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_default_acl_d.h"
51 : #include "catalog/pg_largeobject_d.h"
52 : #include "catalog/pg_proc_d.h"
53 : #include "catalog/pg_publication_d.h"
54 : #include "catalog/pg_subscription_d.h"
55 : #include "catalog/pg_type_d.h"
56 : #include "common/connect.h"
57 : #include "common/int.h"
58 : #include "common/relpath.h"
59 : #include "common/shortest_dec.h"
60 : #include "compress_io.h"
61 : #include "dumputils.h"
62 : #include "fe_utils/option_utils.h"
63 : #include "fe_utils/string_utils.h"
64 : #include "filter.h"
65 : #include "getopt_long.h"
66 : #include "libpq/libpq-fs.h"
67 : #include "parallel.h"
68 : #include "pg_backup_db.h"
69 : #include "pg_backup_utils.h"
70 : #include "pg_dump.h"
71 : #include "storage/block.h"
72 :
73 : typedef struct
74 : {
75 : Oid roleoid; /* role's OID */
76 : const char *rolename; /* role's name */
77 : } RoleNameItem;
78 :
79 : typedef struct
80 : {
81 : const char *descr; /* comment for an object */
82 : Oid classoid; /* object class (catalog OID) */
83 : Oid objoid; /* object OID */
84 : int objsubid; /* subobject (table column #) */
85 : } CommentItem;
86 :
87 : typedef struct
88 : {
89 : const char *provider; /* label provider of this security label */
90 : const char *label; /* security label for an object */
91 : Oid classoid; /* object class (catalog OID) */
92 : Oid objoid; /* object OID */
93 : int objsubid; /* subobject (table column #) */
94 : } SecLabelItem;
95 :
96 : typedef struct
97 : {
98 : Oid oid; /* object OID */
99 : char relkind; /* object kind */
100 : RelFileNumber relfilenumber; /* object filenode */
101 : Oid toast_oid; /* toast table OID */
102 : RelFileNumber toast_relfilenumber; /* toast table filenode */
103 : Oid toast_index_oid; /* toast table index OID */
104 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
105 : } BinaryUpgradeClassOidItem;
106 :
107 : /* sequence types */
108 : typedef enum SeqType
109 : {
110 : SEQTYPE_SMALLINT,
111 : SEQTYPE_INTEGER,
112 : SEQTYPE_BIGINT,
113 : } SeqType;
114 :
115 : static const char *const SeqTypeNames[] =
116 : {
117 : [SEQTYPE_SMALLINT] = "smallint",
118 : [SEQTYPE_INTEGER] = "integer",
119 : [SEQTYPE_BIGINT] = "bigint",
120 : };
121 :
122 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
123 : "array length mismatch");
124 :
125 : typedef struct
126 : {
127 : Oid oid; /* sequence OID */
128 : SeqType seqtype; /* data type of sequence */
129 : bool cycled; /* whether sequence cycles */
130 : int64 minv; /* minimum value */
131 : int64 maxv; /* maximum value */
132 : int64 startv; /* start value */
133 : int64 incby; /* increment value */
134 : int64 cache; /* cache size */
135 : int64 last_value; /* last value of sequence */
136 : bool is_called; /* whether nextval advances before returning */
137 : } SequenceItem;
138 :
139 : typedef enum OidOptions
140 : {
141 : zeroIsError = 1,
142 : zeroAsStar = 2,
143 : zeroAsNone = 4,
144 : } OidOptions;
145 :
146 : /* global decls */
147 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
148 :
149 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
150 :
151 : /* The specified names/patterns should to match at least one entity */
152 : static int strict_names = 0;
153 :
154 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
155 :
156 : /*
157 : * Object inclusion/exclusion lists
158 : *
159 : * The string lists record the patterns given by command-line switches,
160 : * which we then convert to lists of OIDs of matching objects.
161 : */
162 : static SimpleStringList schema_include_patterns = {NULL, NULL};
163 : static SimpleOidList schema_include_oids = {NULL, NULL};
164 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
165 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
166 :
167 : static SimpleStringList table_include_patterns = {NULL, NULL};
168 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
169 : static SimpleOidList table_include_oids = {NULL, NULL};
170 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
171 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
172 : static SimpleOidList table_exclude_oids = {NULL, NULL};
173 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
174 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
175 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
176 :
177 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
178 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
179 :
180 : static SimpleStringList extension_include_patterns = {NULL, NULL};
181 : static SimpleOidList extension_include_oids = {NULL, NULL};
182 :
183 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
184 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
185 :
186 : static const CatalogId nilCatalogId = {0, 0};
187 :
188 : /* override for standard extra_float_digits setting */
189 : static bool have_extra_float_digits = false;
190 : static int extra_float_digits;
191 :
192 : /* sorted table of role names */
193 : static RoleNameItem *rolenames = NULL;
194 : static int nrolenames = 0;
195 :
196 : /* sorted table of comments */
197 : static CommentItem *comments = NULL;
198 : static int ncomments = 0;
199 :
200 : /* sorted table of security labels */
201 : static SecLabelItem *seclabels = NULL;
202 : static int nseclabels = 0;
203 :
204 : /* sorted table of pg_class information for binary upgrade */
205 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
206 : static int nbinaryUpgradeClassOids = 0;
207 :
208 : /* sorted table of sequences */
209 : static SequenceItem *sequences = NULL;
210 : static int nsequences = 0;
211 :
212 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
213 : #define MAX_ATTR_STATS_RELS 64
214 :
215 : /*
216 : * The default number of rows per INSERT when
217 : * --inserts is specified without --rows-per-insert
218 : */
219 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
220 :
221 : /*
222 : * Maximum number of large objects to group into a single ArchiveEntry.
223 : * At some point we might want to make this user-controllable, but for now
224 : * a hard-wired setting will suffice.
225 : */
226 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
227 :
228 : /*
229 : * Macro for producing quoted, schema-qualified name of a dumpable object.
230 : */
231 : #define fmtQualifiedDumpable(obj) \
232 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
233 : (obj)->dobj.name)
234 :
235 : static void help(const char *progname);
236 : static void setup_connection(Archive *AH,
237 : const char *dumpencoding, const char *dumpsnapshot,
238 : char *use_role);
239 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
240 : static void expand_schema_name_patterns(Archive *fout,
241 : SimpleStringList *patterns,
242 : SimpleOidList *oids,
243 : bool strict_names);
244 : static void expand_extension_name_patterns(Archive *fout,
245 : SimpleStringList *patterns,
246 : SimpleOidList *oids,
247 : bool strict_names);
248 : static void expand_foreign_server_name_patterns(Archive *fout,
249 : SimpleStringList *patterns,
250 : SimpleOidList *oids);
251 : static void expand_table_name_patterns(Archive *fout,
252 : SimpleStringList *patterns,
253 : SimpleOidList *oids,
254 : bool strict_names,
255 : bool with_child_tables);
256 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
257 : const char *pattern);
258 :
259 : static NamespaceInfo *findNamespace(Oid nsoid);
260 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
261 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
262 : static const char *getRoleName(const char *roleoid_str);
263 : static void collectRoleNames(Archive *fout);
264 : static void getAdditionalACLs(Archive *fout);
265 : static void dumpCommentExtended(Archive *fout, const char *type,
266 : const char *name, const char *namespace,
267 : const char *owner, CatalogId catalogId,
268 : int subid, DumpId dumpId,
269 : const char *initdb_comment);
270 : static inline void dumpComment(Archive *fout, const char *type,
271 : const char *name, const char *namespace,
272 : const char *owner, CatalogId catalogId,
273 : int subid, DumpId dumpId);
274 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
275 : static void collectComments(Archive *fout);
276 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
277 : const char *namespace, const char *owner,
278 : CatalogId catalogId, int subid, DumpId dumpId);
279 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
280 : static void collectSecLabels(Archive *fout);
281 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
282 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
283 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
284 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
285 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
286 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
287 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
288 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
289 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
290 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
291 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
292 : PGresult *res);
293 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
294 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
295 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
296 : static void dumpCast(Archive *fout, const CastInfo *cast);
297 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
298 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
299 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
300 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
301 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
302 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
303 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
304 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
305 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
306 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
307 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
308 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
309 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
310 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
311 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
312 : static void collectSequences(Archive *fout);
313 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
314 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
315 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
316 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
317 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
318 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
319 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
320 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
321 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
322 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
323 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
324 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
325 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
326 : static void dumpUserMappings(Archive *fout,
327 : const char *servername, const char *namespace,
328 : const char *owner, CatalogId catalogId, DumpId dumpId);
329 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
330 :
331 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
332 : const char *type, const char *name, const char *subname,
333 : const char *nspname, const char *tag, const char *owner,
334 : const DumpableAcl *dacl);
335 :
336 : static void getDependencies(Archive *fout);
337 : static void BuildArchiveDependencies(Archive *fout);
338 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
339 : DumpId **dependencies, int *nDeps, int *allocDeps);
340 :
341 : static DumpableObject *createBoundaryObjects(void);
342 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
343 : DumpableObject *boundaryObjs);
344 :
345 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
346 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
347 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
348 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
349 : static void buildMatViewRefreshDependencies(Archive *fout);
350 : static void getTableDataFKConstraints(void);
351 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
352 : TableInfo *tbinfo, int j,
353 : int i_notnull_name, int i_notnull_invalidoid,
354 : int i_notnull_noinherit,
355 : int i_notnull_islocal,
356 : PQExpBuffer *invalidnotnulloids);
357 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
358 : bool is_agg);
359 : static char *format_function_signature(Archive *fout,
360 : const FuncInfo *finfo, bool honor_quotes);
361 : static char *convertRegProcReference(const char *proc);
362 : static char *getFormattedOperatorName(const char *oproid);
363 : static char *convertTSFunction(Archive *fout, Oid funcOid);
364 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
365 : static void getLOs(Archive *fout);
366 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
367 : static int dumpLOs(Archive *fout, const void *arg);
368 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
369 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
370 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
371 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
372 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
373 : static void dumpDatabase(Archive *fout);
374 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
375 : const char *dbname, Oid dboid);
376 : static void dumpEncoding(Archive *AH);
377 : static void dumpStdStrings(Archive *AH);
378 : static void dumpSearchPath(Archive *AH);
379 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
380 : PQExpBuffer upgrade_buffer,
381 : Oid pg_type_oid,
382 : bool force_array_type,
383 : bool include_multirange_type);
384 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
385 : PQExpBuffer upgrade_buffer,
386 : const TableInfo *tbinfo);
387 : static void collectBinaryUpgradeClassOids(Archive *fout);
388 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
389 : PQExpBuffer upgrade_buffer,
390 : Oid pg_class_oid);
391 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
392 : const DumpableObject *dobj,
393 : const char *objtype,
394 : const char *objname,
395 : const char *objnamespace);
396 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
397 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
398 : static bool nonemptyReloptions(const char *reloptions);
399 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
400 : const char *prefix, Archive *fout);
401 : static char *get_synchronized_snapshot(Archive *fout);
402 : static void set_restrict_relation_kind(Archive *AH, const char *value);
403 : static void setupDumpWorker(Archive *AH);
404 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
405 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
406 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
407 :
408 :
409 : int
410 696 : main(int argc, char **argv)
411 : {
412 : int c;
413 696 : const char *filename = NULL;
414 696 : const char *format = "p";
415 : TableInfo *tblinfo;
416 : int numTables;
417 : DumpableObject **dobjs;
418 : int numObjs;
419 : DumpableObject *boundaryObjs;
420 : int i;
421 : int optindex;
422 : RestoreOptions *ropt;
423 : Archive *fout; /* the script file */
424 696 : bool g_verbose = false;
425 696 : const char *dumpencoding = NULL;
426 696 : const char *dumpsnapshot = NULL;
427 696 : char *use_role = NULL;
428 696 : int numWorkers = 1;
429 696 : int plainText = 0;
430 696 : ArchiveFormat archiveFormat = archUnknown;
431 : ArchiveMode archiveMode;
432 696 : pg_compress_specification compression_spec = {0};
433 696 : char *compression_detail = NULL;
434 696 : char *compression_algorithm_str = "none";
435 696 : char *error_detail = NULL;
436 696 : bool user_compression_defined = false;
437 696 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
438 696 : bool data_only = false;
439 696 : bool schema_only = false;
440 696 : bool statistics_only = false;
441 696 : bool with_data = false;
442 696 : bool with_schema = false;
443 696 : bool with_statistics = false;
444 696 : bool no_data = false;
445 696 : bool no_schema = false;
446 696 : bool no_statistics = false;
447 :
448 : static DumpOptions dopt;
449 :
450 : static struct option long_options[] = {
451 : {"data-only", no_argument, NULL, 'a'},
452 : {"blobs", no_argument, NULL, 'b'},
453 : {"large-objects", no_argument, NULL, 'b'},
454 : {"no-blobs", no_argument, NULL, 'B'},
455 : {"no-large-objects", no_argument, NULL, 'B'},
456 : {"clean", no_argument, NULL, 'c'},
457 : {"create", no_argument, NULL, 'C'},
458 : {"dbname", required_argument, NULL, 'd'},
459 : {"extension", required_argument, NULL, 'e'},
460 : {"file", required_argument, NULL, 'f'},
461 : {"format", required_argument, NULL, 'F'},
462 : {"host", required_argument, NULL, 'h'},
463 : {"jobs", 1, NULL, 'j'},
464 : {"no-reconnect", no_argument, NULL, 'R'},
465 : {"no-owner", no_argument, NULL, 'O'},
466 : {"port", required_argument, NULL, 'p'},
467 : {"schema", required_argument, NULL, 'n'},
468 : {"exclude-schema", required_argument, NULL, 'N'},
469 : {"schema-only", no_argument, NULL, 's'},
470 : {"superuser", required_argument, NULL, 'S'},
471 : {"table", required_argument, NULL, 't'},
472 : {"exclude-table", required_argument, NULL, 'T'},
473 : {"no-password", no_argument, NULL, 'w'},
474 : {"password", no_argument, NULL, 'W'},
475 : {"username", required_argument, NULL, 'U'},
476 : {"verbose", no_argument, NULL, 'v'},
477 : {"no-privileges", no_argument, NULL, 'x'},
478 : {"no-acl", no_argument, NULL, 'x'},
479 : {"compress", required_argument, NULL, 'Z'},
480 : {"encoding", required_argument, NULL, 'E'},
481 : {"help", no_argument, NULL, '?'},
482 : {"version", no_argument, NULL, 'V'},
483 :
484 : /*
485 : * the following options don't have an equivalent short option letter
486 : */
487 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
488 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
489 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
490 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
491 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
492 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
493 : {"exclude-table-data", required_argument, NULL, 4},
494 : {"extra-float-digits", required_argument, NULL, 8},
495 : {"if-exists", no_argument, &dopt.if_exists, 1},
496 : {"inserts", no_argument, NULL, 9},
497 : {"lock-wait-timeout", required_argument, NULL, 2},
498 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
499 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
500 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
501 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
502 : {"role", required_argument, NULL, 3},
503 : {"section", required_argument, NULL, 5},
504 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
505 : {"snapshot", required_argument, NULL, 6},
506 : {"statistics-only", no_argument, NULL, 18},
507 : {"strict-names", no_argument, &strict_names, 1},
508 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
509 : {"no-comments", no_argument, &dopt.no_comments, 1},
510 : {"no-data", no_argument, NULL, 19},
511 : {"no-policies", no_argument, &dopt.no_policies, 1},
512 : {"no-publications", no_argument, &dopt.no_publications, 1},
513 : {"no-schema", no_argument, NULL, 20},
514 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
515 : {"no-statistics", no_argument, NULL, 21},
516 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
517 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
518 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
519 : {"no-sync", no_argument, NULL, 7},
520 : {"with-data", no_argument, NULL, 22},
521 : {"with-schema", no_argument, NULL, 23},
522 : {"with-statistics", no_argument, NULL, 24},
523 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
524 : {"rows-per-insert", required_argument, NULL, 10},
525 : {"include-foreign-data", required_argument, NULL, 11},
526 : {"table-and-children", required_argument, NULL, 12},
527 : {"exclude-table-and-children", required_argument, NULL, 13},
528 : {"exclude-table-data-and-children", required_argument, NULL, 14},
529 : {"sync-method", required_argument, NULL, 15},
530 : {"filter", required_argument, NULL, 16},
531 : {"exclude-extension", required_argument, NULL, 17},
532 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
533 :
534 : {NULL, 0, NULL, 0}
535 : };
536 :
537 696 : pg_logging_init(argv[0]);
538 696 : pg_logging_set_level(PG_LOG_WARNING);
539 696 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
540 :
541 : /*
542 : * Initialize what we need for parallel execution, especially for thread
543 : * support on Windows.
544 : */
545 696 : init_parallel_dump_utils();
546 :
547 696 : progname = get_progname(argv[0]);
548 :
549 696 : if (argc > 1)
550 : {
551 696 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
552 : {
553 2 : help(progname);
554 2 : exit_nicely(0);
555 : }
556 694 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
557 : {
558 138 : puts("pg_dump (PostgreSQL) " PG_VERSION);
559 138 : exit_nicely(0);
560 : }
561 : }
562 :
563 556 : InitDumpOptions(&dopt);
564 :
565 2780 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
566 2780 : long_options, &optindex)) != -1)
567 : {
568 2240 : switch (c)
569 : {
570 18 : case 'a': /* Dump data only */
571 18 : data_only = true;
572 18 : break;
573 :
574 2 : case 'b': /* Dump LOs */
575 2 : dopt.outputLOs = true;
576 2 : break;
577 :
578 4 : case 'B': /* Don't dump LOs */
579 4 : dopt.dontOutputLOs = true;
580 4 : break;
581 :
582 12 : case 'c': /* clean (i.e., drop) schema prior to create */
583 12 : dopt.outputClean = 1;
584 12 : break;
585 :
586 140 : case 'C': /* Create DB */
587 140 : dopt.outputCreateDB = 1;
588 140 : break;
589 :
590 16 : case 'd': /* database name */
591 16 : dopt.cparams.dbname = pg_strdup(optarg);
592 16 : break;
593 :
594 8 : case 'e': /* include extension(s) */
595 8 : simple_string_list_append(&extension_include_patterns, optarg);
596 8 : dopt.include_everything = false;
597 8 : break;
598 :
599 4 : case 'E': /* Dump encoding */
600 4 : dumpencoding = pg_strdup(optarg);
601 4 : break;
602 :
603 466 : case 'f':
604 466 : filename = pg_strdup(optarg);
605 466 : break;
606 :
607 314 : case 'F':
608 314 : format = pg_strdup(optarg);
609 314 : break;
610 :
611 58 : case 'h': /* server host */
612 58 : dopt.cparams.pghost = pg_strdup(optarg);
613 58 : break;
614 :
615 24 : case 'j': /* number of dump jobs */
616 24 : if (!option_parse_int(optarg, "-j/--jobs", 1,
617 : PG_MAX_JOBS,
618 : &numWorkers))
619 2 : exit_nicely(1);
620 22 : break;
621 :
622 34 : case 'n': /* include schema(s) */
623 34 : simple_string_list_append(&schema_include_patterns, optarg);
624 34 : dopt.include_everything = false;
625 34 : break;
626 :
627 2 : case 'N': /* exclude schema(s) */
628 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
629 2 : break;
630 :
631 4 : case 'O': /* Don't reconnect to match owner */
632 4 : dopt.outputNoOwner = 1;
633 4 : break;
634 :
635 134 : case 'p': /* server port */
636 134 : dopt.cparams.pgport = pg_strdup(optarg);
637 134 : break;
638 :
639 4 : case 'R':
640 : /* no-op, still accepted for backwards compatibility */
641 4 : break;
642 :
643 14 : case 's': /* dump schema only */
644 14 : schema_only = true;
645 14 : break;
646 :
647 2 : case 'S': /* Username for superuser in plain text output */
648 2 : dopt.outputSuperuser = pg_strdup(optarg);
649 2 : break;
650 :
651 16 : case 't': /* include table(s) */
652 16 : simple_string_list_append(&table_include_patterns, optarg);
653 16 : dopt.include_everything = false;
654 16 : break;
655 :
656 8 : case 'T': /* exclude table(s) */
657 8 : simple_string_list_append(&table_exclude_patterns, optarg);
658 8 : break;
659 :
660 62 : case 'U':
661 62 : dopt.cparams.username = pg_strdup(optarg);
662 62 : break;
663 :
664 12 : case 'v': /* verbose */
665 12 : g_verbose = true;
666 12 : pg_logging_increase_verbosity();
667 12 : break;
668 :
669 2 : case 'w':
670 2 : dopt.cparams.promptPassword = TRI_NO;
671 2 : break;
672 :
673 0 : case 'W':
674 0 : dopt.cparams.promptPassword = TRI_YES;
675 0 : break;
676 :
677 4 : case 'x': /* skip ACL dump */
678 4 : dopt.aclsSkip = true;
679 4 : break;
680 :
681 24 : case 'Z': /* Compression */
682 24 : parse_compress_options(optarg, &compression_algorithm_str,
683 : &compression_detail);
684 24 : user_compression_defined = true;
685 24 : break;
686 :
687 226 : case 0:
688 : /* This covers the long options. */
689 226 : break;
690 :
691 4 : case 2: /* lock-wait-timeout */
692 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
693 4 : break;
694 :
695 6 : case 3: /* SET ROLE */
696 6 : use_role = pg_strdup(optarg);
697 6 : break;
698 :
699 2 : case 4: /* exclude table(s) data */
700 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
701 2 : break;
702 :
703 12 : case 5: /* section */
704 12 : set_dump_section(optarg, &dopt.dumpSections);
705 12 : break;
706 :
707 0 : case 6: /* snapshot */
708 0 : dumpsnapshot = pg_strdup(optarg);
709 0 : break;
710 :
711 274 : case 7: /* no-sync */
712 274 : dosync = false;
713 274 : break;
714 :
715 2 : case 8:
716 2 : have_extra_float_digits = true;
717 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
718 : &extra_float_digits))
719 2 : exit_nicely(1);
720 0 : break;
721 :
722 4 : case 9: /* inserts */
723 :
724 : /*
725 : * dump_inserts also stores --rows-per-insert, careful not to
726 : * overwrite that.
727 : */
728 4 : if (dopt.dump_inserts == 0)
729 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
730 4 : break;
731 :
732 4 : case 10: /* rows per insert */
733 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
734 : &dopt.dump_inserts))
735 2 : exit_nicely(1);
736 2 : break;
737 :
738 8 : case 11: /* include foreign data */
739 8 : simple_string_list_append(&foreign_servers_include_patterns,
740 : optarg);
741 8 : break;
742 :
743 2 : case 12: /* include table(s) and their children */
744 2 : simple_string_list_append(&table_include_patterns_and_children,
745 : optarg);
746 2 : dopt.include_everything = false;
747 2 : break;
748 :
749 2 : case 13: /* exclude table(s) and their children */
750 2 : simple_string_list_append(&table_exclude_patterns_and_children,
751 : optarg);
752 2 : break;
753 :
754 2 : case 14: /* exclude data of table(s) and children */
755 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
756 : optarg);
757 2 : break;
758 :
759 0 : case 15:
760 0 : if (!parse_sync_method(optarg, &sync_method))
761 0 : exit_nicely(1);
762 0 : break;
763 :
764 52 : case 16: /* read object filters from file */
765 52 : read_dump_filters(optarg, &dopt);
766 44 : break;
767 :
768 2 : case 17: /* exclude extension(s) */
769 2 : simple_string_list_append(&extension_exclude_patterns,
770 : optarg);
771 2 : break;
772 :
773 8 : case 18:
774 8 : statistics_only = true;
775 8 : break;
776 :
777 62 : case 19:
778 62 : no_data = true;
779 62 : break;
780 :
781 4 : case 20:
782 4 : no_schema = true;
783 4 : break;
784 :
785 16 : case 21:
786 16 : no_statistics = true;
787 16 : break;
788 :
789 0 : case 22:
790 0 : with_data = true;
791 0 : break;
792 :
793 0 : case 23:
794 0 : with_schema = true;
795 0 : break;
796 :
797 158 : case 24:
798 158 : with_statistics = true;
799 158 : break;
800 :
801 2 : default:
802 : /* getopt_long already emitted a complaint */
803 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
804 2 : exit_nicely(1);
805 : }
806 : }
807 :
808 : /*
809 : * Non-option argument specifies database name as long as it wasn't
810 : * already specified with -d / --dbname
811 : */
812 540 : if (optind < argc && dopt.cparams.dbname == NULL)
813 464 : dopt.cparams.dbname = argv[optind++];
814 :
815 : /* Complain if any arguments remain */
816 540 : if (optind < argc)
817 : {
818 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
819 : argv[optind]);
820 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
821 2 : exit_nicely(1);
822 : }
823 :
824 : /* --column-inserts implies --inserts */
825 538 : if (dopt.column_inserts && dopt.dump_inserts == 0)
826 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
827 :
828 : /* reject conflicting "-only" options */
829 538 : if (data_only && schema_only)
830 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
831 536 : if (schema_only && statistics_only)
832 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
833 534 : if (data_only && statistics_only)
834 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
835 :
836 : /* reject conflicting "-only" and "no-" options */
837 532 : if (data_only && no_data)
838 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
839 532 : if (schema_only && no_schema)
840 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
841 532 : if (statistics_only && no_statistics)
842 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
843 :
844 : /* reject conflicting "with-" and "no-" options */
845 530 : if (with_data && no_data)
846 0 : pg_fatal("options --with-data and --no-data cannot be used together");
847 530 : if (with_schema && no_schema)
848 0 : pg_fatal("options --with-schema and --no-schema cannot be used together");
849 530 : if (with_statistics && no_statistics)
850 0 : pg_fatal("options --with-statistics and --no-statistics cannot be used together");
851 :
852 530 : if (schema_only && foreign_servers_include_patterns.head != NULL)
853 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
854 :
855 528 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
856 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
857 :
858 526 : if (data_only && dopt.outputClean)
859 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
860 :
861 524 : if (dopt.if_exists && !dopt.outputClean)
862 2 : pg_fatal("option --if-exists requires option -c/--clean");
863 :
864 : /*
865 : * Set derivative flags. An "-only" option may be overridden by an
866 : * explicit "with-" option; e.g. "--schema-only --with-statistics" will
867 : * include schema and statistics. Other ambiguous or nonsensical
868 : * combinations, e.g. "--schema-only --no-schema", will have already
869 : * caused an error in one of the checks above.
870 : */
871 522 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
872 1044 : (data_only || with_data)) && !no_data;
873 522 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
874 1044 : (schema_only || with_schema)) && !no_schema;
875 522 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
876 1044 : (statistics_only || with_statistics)) && !no_statistics;
877 :
878 :
879 : /*
880 : * --inserts are already implied above if --column-inserts or
881 : * --rows-per-insert were specified.
882 : */
883 522 : if (dopt.do_nothing && dopt.dump_inserts == 0)
884 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
885 :
886 : /* Identify archive format to emit */
887 520 : archiveFormat = parseArchiveFormat(format, &archiveMode);
888 :
889 : /* archiveFormat specific setup */
890 518 : if (archiveFormat == archNull)
891 310 : plainText = 1;
892 :
893 : /*
894 : * Custom and directory formats are compressed by default with gzip when
895 : * available, not the others. If gzip is not available, no compression is
896 : * done by default.
897 : */
898 518 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
899 186 : !user_compression_defined)
900 : {
901 : #ifdef HAVE_LIBZ
902 176 : compression_algorithm_str = "gzip";
903 : #else
904 : compression_algorithm_str = "none";
905 : #endif
906 : }
907 :
908 : /*
909 : * Compression options
910 : */
911 518 : if (!parse_compress_algorithm(compression_algorithm_str,
912 : &compression_algorithm))
913 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
914 : compression_algorithm_str);
915 :
916 516 : parse_compress_specification(compression_algorithm, compression_detail,
917 : &compression_spec);
918 516 : error_detail = validate_compress_specification(&compression_spec);
919 516 : if (error_detail != NULL)
920 6 : pg_fatal("invalid compression specification: %s",
921 : error_detail);
922 :
923 510 : error_detail = supports_compression(compression_spec);
924 510 : if (error_detail != NULL)
925 0 : pg_fatal("%s", error_detail);
926 :
927 : /*
928 : * Disable support for zstd workers for now - these are based on
929 : * threading, and it's unclear how it interacts with parallel dumps on
930 : * platforms where that relies on threads too (e.g. Windows).
931 : */
932 510 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
933 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
934 : "workers");
935 :
936 : /*
937 : * If emitting an archive format, we always want to emit a DATABASE item,
938 : * in case --create is specified at pg_restore time.
939 : */
940 510 : if (!plainText)
941 208 : dopt.outputCreateDB = 1;
942 :
943 : /* Parallel backup only in the directory archive format so far */
944 510 : if (archiveFormat != archDirectory && numWorkers > 1)
945 2 : pg_fatal("parallel backup only supported by the directory format");
946 :
947 : /* Open the output file */
948 508 : fout = CreateArchive(filename, archiveFormat, compression_spec,
949 : dosync, archiveMode, setupDumpWorker, sync_method);
950 :
951 : /* Make dump options accessible right away */
952 506 : SetArchiveOptions(fout, &dopt, NULL);
953 :
954 : /* Register the cleanup hook */
955 506 : on_exit_close_archive(fout);
956 :
957 : /* Let the archiver know how noisy to be */
958 506 : fout->verbose = g_verbose;
959 :
960 :
961 : /*
962 : * We allow the server to be back to 9.2, and up to any minor release of
963 : * our own major version. (See also version check in pg_dumpall.c.)
964 : */
965 506 : fout->minRemoteVersion = 90200;
966 506 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
967 :
968 506 : fout->numWorkers = numWorkers;
969 :
970 : /*
971 : * Open the database using the Archiver, so it knows about it. Errors mean
972 : * death.
973 : */
974 506 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
975 502 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
976 :
977 : /*
978 : * On hot standbys, never try to dump unlogged table data, since it will
979 : * just throw an error.
980 : */
981 502 : if (fout->isStandby)
982 8 : dopt.no_unlogged_table_data = true;
983 :
984 : /*
985 : * Find the last built-in OID, if needed (prior to 8.1)
986 : *
987 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
988 : */
989 502 : g_last_builtin_oid = FirstNormalObjectId - 1;
990 :
991 502 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
992 :
993 : /* Expand schema selection patterns into OID lists */
994 502 : if (schema_include_patterns.head != NULL)
995 : {
996 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
997 : &schema_include_oids,
998 : strict_names);
999 24 : if (schema_include_oids.head == NULL)
1000 2 : pg_fatal("no matching schemas were found");
1001 : }
1002 488 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1003 : &schema_exclude_oids,
1004 : false);
1005 : /* non-matching exclusion patterns aren't an error */
1006 :
1007 : /* Expand table selection patterns into OID lists */
1008 488 : expand_table_name_patterns(fout, &table_include_patterns,
1009 : &table_include_oids,
1010 : strict_names, false);
1011 478 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1012 : &table_include_oids,
1013 : strict_names, true);
1014 478 : if ((table_include_patterns.head != NULL ||
1015 456 : table_include_patterns_and_children.head != NULL) &&
1016 26 : table_include_oids.head == NULL)
1017 4 : pg_fatal("no matching tables were found");
1018 :
1019 474 : expand_table_name_patterns(fout, &table_exclude_patterns,
1020 : &table_exclude_oids,
1021 : false, false);
1022 474 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1023 : &table_exclude_oids,
1024 : false, true);
1025 :
1026 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1027 : &tabledata_exclude_oids,
1028 : false, false);
1029 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1030 : &tabledata_exclude_oids,
1031 : false, true);
1032 :
1033 474 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1034 : &foreign_servers_include_oids);
1035 :
1036 : /* non-matching exclusion patterns aren't an error */
1037 :
1038 : /* Expand extension selection patterns into OID lists */
1039 472 : if (extension_include_patterns.head != NULL)
1040 : {
1041 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1042 : &extension_include_oids,
1043 : strict_names);
1044 10 : if (extension_include_oids.head == NULL)
1045 2 : pg_fatal("no matching extensions were found");
1046 : }
1047 470 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1048 : &extension_exclude_oids,
1049 : false);
1050 : /* non-matching exclusion patterns aren't an error */
1051 :
1052 : /*
1053 : * Dumping LOs is the default for dumps where an inclusion switch is not
1054 : * used (an "include everything" dump). -B can be used to exclude LOs
1055 : * from those dumps. -b can be used to include LOs even when an inclusion
1056 : * switch is used.
1057 : *
1058 : * -s means "schema only" and LOs are data, not schema, so we never
1059 : * include LOs when -s is used.
1060 : */
1061 470 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1062 348 : dopt.outputLOs = true;
1063 :
1064 : /*
1065 : * Collect role names so we can map object owner OIDs to names.
1066 : */
1067 470 : collectRoleNames(fout);
1068 :
1069 : /*
1070 : * Now scan the database and create DumpableObject structs for all the
1071 : * objects we intend to dump.
1072 : */
1073 470 : tblinfo = getSchemaData(fout, &numTables);
1074 :
1075 468 : if (dopt.dumpData)
1076 : {
1077 396 : getTableData(&dopt, tblinfo, numTables, 0);
1078 396 : buildMatViewRefreshDependencies(fout);
1079 396 : if (!dopt.dumpSchema)
1080 14 : getTableDataFKConstraints();
1081 : }
1082 :
1083 468 : if (!dopt.dumpData && dopt.sequence_data)
1084 56 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1085 :
1086 : /*
1087 : * In binary-upgrade mode, we do not have to worry about the actual LO
1088 : * data or the associated metadata that resides in the pg_largeobject and
1089 : * pg_largeobject_metadata tables, respectively.
1090 : *
1091 : * However, we do need to collect LO information as there may be comments
1092 : * or other information on LOs that we do need to dump out.
1093 : */
1094 468 : if (dopt.outputLOs || dopt.binary_upgrade)
1095 410 : getLOs(fout);
1096 :
1097 : /*
1098 : * Collect dependency data to assist in ordering the objects.
1099 : */
1100 468 : getDependencies(fout);
1101 :
1102 : /*
1103 : * Collect ACLs, comments, and security labels, if wanted.
1104 : */
1105 468 : if (!dopt.aclsSkip)
1106 464 : getAdditionalACLs(fout);
1107 468 : if (!dopt.no_comments)
1108 468 : collectComments(fout);
1109 468 : if (!dopt.no_security_labels)
1110 468 : collectSecLabels(fout);
1111 :
1112 : /* For binary upgrade mode, collect required pg_class information. */
1113 468 : if (dopt.binary_upgrade)
1114 62 : collectBinaryUpgradeClassOids(fout);
1115 :
1116 : /* Collect sequence information. */
1117 468 : collectSequences(fout);
1118 :
1119 : /* Lastly, create dummy objects to represent the section boundaries */
1120 468 : boundaryObjs = createBoundaryObjects();
1121 :
1122 : /* Get pointers to all the known DumpableObjects */
1123 468 : getDumpableObjects(&dobjs, &numObjs);
1124 :
1125 : /*
1126 : * Add dummy dependencies to enforce the dump section ordering.
1127 : */
1128 468 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1129 :
1130 : /*
1131 : * Sort the objects into a safe dump order (no forward references).
1132 : *
1133 : * We rely on dependency information to help us determine a safe order, so
1134 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1135 : * ensure that logically identical schemas will dump identically.
1136 : */
1137 468 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1138 :
1139 468 : sortDumpableObjects(dobjs, numObjs,
1140 468 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1141 :
1142 : /*
1143 : * Create archive TOC entries for all the objects to be dumped, in a safe
1144 : * order.
1145 : */
1146 :
1147 : /*
1148 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1149 : */
1150 468 : dumpEncoding(fout);
1151 468 : dumpStdStrings(fout);
1152 468 : dumpSearchPath(fout);
1153 :
1154 : /* The database items are always next, unless we don't want them at all */
1155 468 : if (dopt.outputCreateDB)
1156 264 : dumpDatabase(fout);
1157 :
1158 : /* Now the rearrangeable objects. */
1159 1733366 : for (i = 0; i < numObjs; i++)
1160 1732898 : dumpDumpableObject(fout, dobjs[i]);
1161 :
1162 : /*
1163 : * Set up options info to ensure we dump what we want.
1164 : */
1165 468 : ropt = NewRestoreOptions();
1166 468 : ropt->filename = filename;
1167 :
1168 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1169 468 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1170 468 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1171 468 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1172 468 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1173 468 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1174 468 : ropt->dropSchema = dopt.outputClean;
1175 468 : ropt->dumpData = dopt.dumpData;
1176 468 : ropt->dumpSchema = dopt.dumpSchema;
1177 468 : ropt->dumpStatistics = dopt.dumpStatistics;
1178 468 : ropt->if_exists = dopt.if_exists;
1179 468 : ropt->column_inserts = dopt.column_inserts;
1180 468 : ropt->dumpSections = dopt.dumpSections;
1181 468 : ropt->aclsSkip = dopt.aclsSkip;
1182 468 : ropt->superuser = dopt.outputSuperuser;
1183 468 : ropt->createDB = dopt.outputCreateDB;
1184 468 : ropt->noOwner = dopt.outputNoOwner;
1185 468 : ropt->noTableAm = dopt.outputNoTableAm;
1186 468 : ropt->noTablespace = dopt.outputNoTablespaces;
1187 468 : ropt->disable_triggers = dopt.disable_triggers;
1188 468 : ropt->use_setsessauth = dopt.use_setsessauth;
1189 468 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1190 468 : ropt->dump_inserts = dopt.dump_inserts;
1191 468 : ropt->no_comments = dopt.no_comments;
1192 468 : ropt->no_policies = dopt.no_policies;
1193 468 : ropt->no_publications = dopt.no_publications;
1194 468 : ropt->no_security_labels = dopt.no_security_labels;
1195 468 : ropt->no_subscriptions = dopt.no_subscriptions;
1196 468 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1197 468 : ropt->include_everything = dopt.include_everything;
1198 468 : ropt->enable_row_security = dopt.enable_row_security;
1199 468 : ropt->sequence_data = dopt.sequence_data;
1200 468 : ropt->binary_upgrade = dopt.binary_upgrade;
1201 :
1202 468 : ropt->compression_spec = compression_spec;
1203 :
1204 468 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1205 :
1206 468 : SetArchiveOptions(fout, &dopt, ropt);
1207 :
1208 : /* Mark which entries should be output */
1209 468 : ProcessArchiveRestoreOptions(fout);
1210 :
1211 : /*
1212 : * The archive's TOC entries are now marked as to which ones will actually
1213 : * be output, so we can set up their dependency lists properly. This isn't
1214 : * necessary for plain-text output, though.
1215 : */
1216 468 : if (!plainText)
1217 206 : BuildArchiveDependencies(fout);
1218 :
1219 : /*
1220 : * And finally we can do the actual output.
1221 : *
1222 : * Note: for non-plain-text output formats, the output file is written
1223 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1224 : * right now.
1225 : */
1226 468 : if (plainText)
1227 262 : RestoreArchive(fout, false);
1228 :
1229 466 : CloseArchive(fout);
1230 :
1231 466 : exit_nicely(0);
1232 : }
1233 :
1234 :
1235 : static void
1236 2 : help(const char *progname)
1237 : {
1238 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1239 2 : printf(_("Usage:\n"));
1240 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1241 :
1242 2 : printf(_("\nGeneral options:\n"));
1243 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1244 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1245 : " plain text (default))\n"));
1246 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1247 2 : printf(_(" -v, --verbose verbose mode\n"));
1248 2 : printf(_(" -V, --version output version information, then exit\n"));
1249 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1250 : " compress as specified\n"));
1251 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1252 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1253 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1254 2 : printf(_(" -?, --help show this help, then exit\n"));
1255 :
1256 2 : printf(_("\nOptions controlling the output content:\n"));
1257 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1258 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1259 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1260 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1261 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1262 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1263 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1264 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1265 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1266 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1267 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1268 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1269 : " plain-text format\n"));
1270 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1271 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1272 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1273 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1274 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1275 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1276 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1277 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1278 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1279 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1280 : " access to)\n"));
1281 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1282 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1283 : " do NOT dump the specified table(s), including\n"
1284 : " child and partition tables\n"));
1285 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1286 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1287 : " do NOT dump data for the specified table(s),\n"
1288 : " including child and partition tables\n"));
1289 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1290 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1291 : " based on expressions in FILENAME\n"));
1292 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1293 2 : printf(_(" --include-foreign-data=PATTERN\n"
1294 : " include data of foreign tables on foreign\n"
1295 : " servers matching PATTERN\n"));
1296 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1297 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1298 2 : printf(_(" --no-comments do not dump comment commands\n"));
1299 2 : printf(_(" --no-data do not dump data\n"));
1300 2 : printf(_(" --no-policies do not dump row security policies\n"));
1301 2 : printf(_(" --no-publications do not dump publications\n"));
1302 2 : printf(_(" --no-schema do not dump schema\n"));
1303 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1304 2 : printf(_(" --no-statistics do not dump statistics\n"));
1305 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1306 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1307 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1308 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1309 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1310 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1311 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1312 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1313 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1314 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1315 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1316 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1317 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1318 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1319 : " match at least one entity each\n"));
1320 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1321 : " child and partition tables\n"));
1322 2 : printf(_(" --use-set-session-authorization\n"
1323 : " use SET SESSION AUTHORIZATION commands instead of\n"
1324 : " ALTER OWNER commands to set ownership\n"));
1325 2 : printf(_(" --with-data dump the data\n"));
1326 2 : printf(_(" --with-schema dump the schema\n"));
1327 2 : printf(_(" --with-statistics dump the statistics\n"));
1328 :
1329 2 : printf(_("\nConnection options:\n"));
1330 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1331 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1332 2 : printf(_(" -p, --port=PORT database server port number\n"));
1333 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1334 2 : printf(_(" -w, --no-password never prompt for password\n"));
1335 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1336 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1337 :
1338 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1339 : "variable value is used.\n\n"));
1340 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1341 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1342 2 : }
1343 :
1344 : static void
1345 538 : setup_connection(Archive *AH, const char *dumpencoding,
1346 : const char *dumpsnapshot, char *use_role)
1347 : {
1348 538 : DumpOptions *dopt = AH->dopt;
1349 538 : PGconn *conn = GetConnection(AH);
1350 : const char *std_strings;
1351 :
1352 538 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1353 :
1354 : /*
1355 : * Set the client encoding if requested.
1356 : */
1357 538 : if (dumpencoding)
1358 : {
1359 40 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1360 0 : pg_fatal("invalid client encoding \"%s\" specified",
1361 : dumpencoding);
1362 : }
1363 :
1364 : /*
1365 : * Get the active encoding and the standard_conforming_strings setting, so
1366 : * we know how to escape strings.
1367 : */
1368 538 : AH->encoding = PQclientEncoding(conn);
1369 538 : setFmtEncoding(AH->encoding);
1370 :
1371 538 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1372 538 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1373 :
1374 : /*
1375 : * Set the role if requested. In a parallel dump worker, we'll be passed
1376 : * use_role == NULL, but AH->use_role is already set (if user specified it
1377 : * originally) and we should use that.
1378 : */
1379 538 : if (!use_role && AH->use_role)
1380 4 : use_role = AH->use_role;
1381 :
1382 : /* Set the role if requested */
1383 538 : if (use_role)
1384 : {
1385 10 : PQExpBuffer query = createPQExpBuffer();
1386 :
1387 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1388 10 : ExecuteSqlStatement(AH, query->data);
1389 10 : destroyPQExpBuffer(query);
1390 :
1391 : /* save it for possible later use by parallel workers */
1392 10 : if (!AH->use_role)
1393 6 : AH->use_role = pg_strdup(use_role);
1394 : }
1395 :
1396 : /* Set the datestyle to ISO to ensure the dump's portability */
1397 538 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1398 :
1399 : /* Likewise, avoid using sql_standard intervalstyle */
1400 538 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1401 :
1402 : /*
1403 : * Use an explicitly specified extra_float_digits if it has been provided.
1404 : * Otherwise, set extra_float_digits so that we can dump float data
1405 : * exactly (given correctly implemented float I/O code, anyway).
1406 : */
1407 538 : if (have_extra_float_digits)
1408 : {
1409 0 : PQExpBuffer q = createPQExpBuffer();
1410 :
1411 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1412 : extra_float_digits);
1413 0 : ExecuteSqlStatement(AH, q->data);
1414 0 : destroyPQExpBuffer(q);
1415 : }
1416 : else
1417 538 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1418 :
1419 : /*
1420 : * Disable synchronized scanning, to prevent unpredictable changes in row
1421 : * ordering across a dump and reload.
1422 : */
1423 538 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1424 :
1425 : /*
1426 : * Disable timeouts if supported.
1427 : */
1428 538 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1429 538 : if (AH->remoteVersion >= 90300)
1430 538 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1431 538 : if (AH->remoteVersion >= 90600)
1432 538 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1433 538 : if (AH->remoteVersion >= 170000)
1434 538 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1435 :
1436 : /*
1437 : * Quote all identifiers, if requested.
1438 : */
1439 538 : if (quote_all_identifiers)
1440 58 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1441 :
1442 : /*
1443 : * Adjust row-security mode, if supported.
1444 : */
1445 538 : if (AH->remoteVersion >= 90500)
1446 : {
1447 538 : if (dopt->enable_row_security)
1448 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1449 : else
1450 538 : ExecuteSqlStatement(AH, "SET row_security = off");
1451 : }
1452 :
1453 : /*
1454 : * For security reasons, we restrict the expansion of non-system views and
1455 : * access to foreign tables during the pg_dump process. This restriction
1456 : * is adjusted when dumping foreign table data.
1457 : */
1458 538 : set_restrict_relation_kind(AH, "view, foreign-table");
1459 :
1460 : /*
1461 : * Initialize prepared-query state to "nothing prepared". We do this here
1462 : * so that a parallel dump worker will have its own state.
1463 : */
1464 538 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1465 :
1466 : /*
1467 : * Start transaction-snapshot mode transaction to dump consistent data.
1468 : */
1469 538 : ExecuteSqlStatement(AH, "BEGIN");
1470 :
1471 : /*
1472 : * To support the combination of serializable_deferrable with the jobs
1473 : * option we use REPEATABLE READ for the worker connections that are
1474 : * passed a snapshot. As long as the snapshot is acquired in a
1475 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1476 : * REPEATABLE READ transaction provides the appropriate integrity
1477 : * guarantees. This is a kluge, but safe for back-patching.
1478 : */
1479 538 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1480 0 : ExecuteSqlStatement(AH,
1481 : "SET TRANSACTION ISOLATION LEVEL "
1482 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1483 : else
1484 538 : ExecuteSqlStatement(AH,
1485 : "SET TRANSACTION ISOLATION LEVEL "
1486 : "REPEATABLE READ, READ ONLY");
1487 :
1488 : /*
1489 : * If user specified a snapshot to use, select that. In a parallel dump
1490 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1491 : * is already set (if the server can handle it) and we should use that.
1492 : */
1493 538 : if (dumpsnapshot)
1494 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1495 :
1496 538 : if (AH->sync_snapshot_id)
1497 : {
1498 36 : PQExpBuffer query = createPQExpBuffer();
1499 :
1500 36 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1501 36 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1502 36 : ExecuteSqlStatement(AH, query->data);
1503 36 : destroyPQExpBuffer(query);
1504 : }
1505 502 : else if (AH->numWorkers > 1)
1506 : {
1507 18 : if (AH->isStandby && AH->remoteVersion < 100000)
1508 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1509 18 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1510 : }
1511 538 : }
1512 :
1513 : /* Set up connection for a parallel worker process */
1514 : static void
1515 36 : setupDumpWorker(Archive *AH)
1516 : {
1517 : /*
1518 : * We want to re-select all the same values the leader connection is
1519 : * using. We'll have inherited directly-usable values in
1520 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1521 : * inherited encoding value back to a string to pass to setup_connection.
1522 : */
1523 36 : setup_connection(AH,
1524 : pg_encoding_to_char(AH->encoding),
1525 : NULL,
1526 : NULL);
1527 36 : }
1528 :
1529 : static char *
1530 18 : get_synchronized_snapshot(Archive *fout)
1531 : {
1532 18 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1533 : char *result;
1534 : PGresult *res;
1535 :
1536 18 : res = ExecuteSqlQueryForSingleRow(fout, query);
1537 18 : result = pg_strdup(PQgetvalue(res, 0, 0));
1538 18 : PQclear(res);
1539 :
1540 18 : return result;
1541 : }
1542 :
1543 : static ArchiveFormat
1544 520 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1545 : {
1546 : ArchiveFormat archiveFormat;
1547 :
1548 520 : *mode = archModeWrite;
1549 :
1550 520 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1551 : {
1552 : /* This is used by pg_dumpall, and is not documented */
1553 86 : archiveFormat = archNull;
1554 86 : *mode = archModeAppend;
1555 : }
1556 434 : else if (pg_strcasecmp(format, "c") == 0)
1557 0 : archiveFormat = archCustom;
1558 434 : else if (pg_strcasecmp(format, "custom") == 0)
1559 88 : archiveFormat = archCustom;
1560 346 : else if (pg_strcasecmp(format, "d") == 0)
1561 2 : archiveFormat = archDirectory;
1562 344 : else if (pg_strcasecmp(format, "directory") == 0)
1563 96 : archiveFormat = archDirectory;
1564 248 : else if (pg_strcasecmp(format, "p") == 0)
1565 218 : archiveFormat = archNull;
1566 30 : else if (pg_strcasecmp(format, "plain") == 0)
1567 6 : archiveFormat = archNull;
1568 24 : else if (pg_strcasecmp(format, "t") == 0)
1569 0 : archiveFormat = archTar;
1570 24 : else if (pg_strcasecmp(format, "tar") == 0)
1571 22 : archiveFormat = archTar;
1572 : else
1573 2 : pg_fatal("invalid output format \"%s\" specified", format);
1574 518 : return archiveFormat;
1575 : }
1576 :
1577 : /*
1578 : * Find the OIDs of all schemas matching the given list of patterns,
1579 : * and append them to the given OID list.
1580 : */
1581 : static void
1582 524 : expand_schema_name_patterns(Archive *fout,
1583 : SimpleStringList *patterns,
1584 : SimpleOidList *oids,
1585 : bool strict_names)
1586 : {
1587 : PQExpBuffer query;
1588 : PGresult *res;
1589 : SimpleStringListCell *cell;
1590 : int i;
1591 :
1592 524 : if (patterns->head == NULL)
1593 482 : return; /* nothing to do */
1594 :
1595 42 : query = createPQExpBuffer();
1596 :
1597 : /*
1598 : * The loop below runs multiple SELECTs might sometimes result in
1599 : * duplicate entries in the OID list, but we don't care.
1600 : */
1601 :
1602 72 : for (cell = patterns->head; cell; cell = cell->next)
1603 : {
1604 : PQExpBufferData dbbuf;
1605 : int dotcnt;
1606 :
1607 42 : appendPQExpBufferStr(query,
1608 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1609 42 : initPQExpBuffer(&dbbuf);
1610 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1611 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1612 : &dotcnt);
1613 42 : if (dotcnt > 1)
1614 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1615 : cell->val);
1616 38 : else if (dotcnt == 1)
1617 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1618 32 : termPQExpBuffer(&dbbuf);
1619 :
1620 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1621 32 : if (strict_names && PQntuples(res) == 0)
1622 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1623 :
1624 58 : for (i = 0; i < PQntuples(res); i++)
1625 : {
1626 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1627 : }
1628 :
1629 30 : PQclear(res);
1630 30 : resetPQExpBuffer(query);
1631 : }
1632 :
1633 30 : destroyPQExpBuffer(query);
1634 : }
1635 :
1636 : /*
1637 : * Find the OIDs of all extensions matching the given list of patterns,
1638 : * and append them to the given OID list.
1639 : */
1640 : static void
1641 480 : expand_extension_name_patterns(Archive *fout,
1642 : SimpleStringList *patterns,
1643 : SimpleOidList *oids,
1644 : bool strict_names)
1645 : {
1646 : PQExpBuffer query;
1647 : PGresult *res;
1648 : SimpleStringListCell *cell;
1649 : int i;
1650 :
1651 480 : if (patterns->head == NULL)
1652 466 : return; /* nothing to do */
1653 :
1654 14 : query = createPQExpBuffer();
1655 :
1656 : /*
1657 : * The loop below runs multiple SELECTs might sometimes result in
1658 : * duplicate entries in the OID list, but we don't care.
1659 : */
1660 28 : for (cell = patterns->head; cell; cell = cell->next)
1661 : {
1662 : int dotcnt;
1663 :
1664 14 : appendPQExpBufferStr(query,
1665 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1666 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1667 : false, NULL, "e.extname", NULL, NULL, NULL,
1668 : &dotcnt);
1669 14 : if (dotcnt > 0)
1670 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1671 : cell->val);
1672 :
1673 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1674 14 : if (strict_names && PQntuples(res) == 0)
1675 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1676 :
1677 26 : for (i = 0; i < PQntuples(res); i++)
1678 : {
1679 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1680 : }
1681 :
1682 14 : PQclear(res);
1683 14 : resetPQExpBuffer(query);
1684 : }
1685 :
1686 14 : destroyPQExpBuffer(query);
1687 : }
1688 :
1689 : /*
1690 : * Find the OIDs of all foreign servers matching the given list of patterns,
1691 : * and append them to the given OID list.
1692 : */
1693 : static void
1694 474 : expand_foreign_server_name_patterns(Archive *fout,
1695 : SimpleStringList *patterns,
1696 : SimpleOidList *oids)
1697 : {
1698 : PQExpBuffer query;
1699 : PGresult *res;
1700 : SimpleStringListCell *cell;
1701 : int i;
1702 :
1703 474 : if (patterns->head == NULL)
1704 468 : return; /* nothing to do */
1705 :
1706 6 : query = createPQExpBuffer();
1707 :
1708 : /*
1709 : * The loop below runs multiple SELECTs might sometimes result in
1710 : * duplicate entries in the OID list, but we don't care.
1711 : */
1712 :
1713 10 : for (cell = patterns->head; cell; cell = cell->next)
1714 : {
1715 : int dotcnt;
1716 :
1717 6 : appendPQExpBufferStr(query,
1718 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1719 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1720 : false, NULL, "s.srvname", NULL, NULL, NULL,
1721 : &dotcnt);
1722 6 : if (dotcnt > 0)
1723 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1724 : cell->val);
1725 :
1726 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1727 6 : if (PQntuples(res) == 0)
1728 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1729 :
1730 8 : for (i = 0; i < PQntuples(res); i++)
1731 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1732 :
1733 4 : PQclear(res);
1734 4 : resetPQExpBuffer(query);
1735 : }
1736 :
1737 4 : destroyPQExpBuffer(query);
1738 : }
1739 :
1740 : /*
1741 : * Find the OIDs of all tables matching the given list of patterns,
1742 : * and append them to the given OID list. See also expand_dbname_patterns()
1743 : * in pg_dumpall.c
1744 : */
1745 : static void
1746 2862 : expand_table_name_patterns(Archive *fout,
1747 : SimpleStringList *patterns, SimpleOidList *oids,
1748 : bool strict_names, bool with_child_tables)
1749 : {
1750 : PQExpBuffer query;
1751 : PGresult *res;
1752 : SimpleStringListCell *cell;
1753 : int i;
1754 :
1755 2862 : if (patterns->head == NULL)
1756 2804 : return; /* nothing to do */
1757 :
1758 58 : query = createPQExpBuffer();
1759 :
1760 : /*
1761 : * this might sometimes result in duplicate entries in the OID list, but
1762 : * we don't care.
1763 : */
1764 :
1765 118 : for (cell = patterns->head; cell; cell = cell->next)
1766 : {
1767 : PQExpBufferData dbbuf;
1768 : int dotcnt;
1769 :
1770 : /*
1771 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1772 : * would be unnecessary given a pg_table_is_visible() variant taking a
1773 : * search_path argument.
1774 : *
1775 : * For with_child_tables, we start with the basic query's results and
1776 : * recursively search the inheritance tree to add child tables.
1777 : */
1778 70 : if (with_child_tables)
1779 : {
1780 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1781 : }
1782 :
1783 70 : appendPQExpBuffer(query,
1784 : "SELECT c.oid"
1785 : "\nFROM pg_catalog.pg_class c"
1786 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1787 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1788 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1789 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1790 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1791 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1792 : RELKIND_PARTITIONED_TABLE);
1793 70 : initPQExpBuffer(&dbbuf);
1794 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1795 : false, "n.nspname", "c.relname", NULL,
1796 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1797 : &dotcnt);
1798 70 : if (dotcnt > 2)
1799 2 : pg_fatal("improper relation name (too many dotted names): %s",
1800 : cell->val);
1801 68 : else if (dotcnt == 2)
1802 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1803 64 : termPQExpBuffer(&dbbuf);
1804 :
1805 64 : if (with_child_tables)
1806 : {
1807 12 : appendPQExpBufferStr(query, "UNION"
1808 : "\nSELECT i.inhrelid"
1809 : "\nFROM partition_tree p"
1810 : "\n JOIN pg_catalog.pg_inherits i"
1811 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1812 : "\n)"
1813 : "\nSELECT relid FROM partition_tree");
1814 : }
1815 :
1816 64 : ExecuteSqlStatement(fout, "RESET search_path");
1817 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1818 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1819 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1820 64 : if (strict_names && PQntuples(res) == 0)
1821 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1822 :
1823 148 : for (i = 0; i < PQntuples(res); i++)
1824 : {
1825 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1826 : }
1827 :
1828 60 : PQclear(res);
1829 60 : resetPQExpBuffer(query);
1830 : }
1831 :
1832 48 : destroyPQExpBuffer(query);
1833 : }
1834 :
1835 : /*
1836 : * Verifies that the connected database name matches the given database name,
1837 : * and if not, dies with an error about the given pattern.
1838 : *
1839 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1840 : */
1841 : static void
1842 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1843 : {
1844 : const char *db;
1845 :
1846 10 : db = PQdb(conn);
1847 10 : if (db == NULL)
1848 0 : pg_fatal("You are currently not connected to a database.");
1849 :
1850 10 : if (strcmp(db, dbname) != 0)
1851 10 : pg_fatal("cross-database references are not implemented: %s",
1852 : pattern);
1853 0 : }
1854 :
1855 : /*
1856 : * checkExtensionMembership
1857 : * Determine whether object is an extension member, and if so,
1858 : * record an appropriate dependency and set the object's dump flag.
1859 : *
1860 : * It's important to call this for each object that could be an extension
1861 : * member. Generally, we integrate this with determining the object's
1862 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1863 : *
1864 : * Returns true if object is an extension member, else false.
1865 : */
1866 : static bool
1867 1470972 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1868 : {
1869 1470972 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1870 :
1871 1470972 : if (ext == NULL)
1872 1469318 : return false;
1873 :
1874 1654 : dobj->ext_member = true;
1875 :
1876 : /* Record dependency so that getDependencies needn't deal with that */
1877 1654 : addObjectDependency(dobj, ext->dobj.dumpId);
1878 :
1879 : /*
1880 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1881 : * dumped. (Any initial ACLs will be removed later, using data from
1882 : * pg_init_privs, so that we'll dump only the delta from the extension's
1883 : * initial setup.)
1884 : *
1885 : * Prior to 9.6, we do not include any extension member components.
1886 : *
1887 : * In binary upgrades, we still dump all components of the members
1888 : * individually, since the idea is to exactly reproduce the database
1889 : * contents rather than replace the extension contents with something
1890 : * different.
1891 : *
1892 : * Note: it might be interesting someday to implement storage and delta
1893 : * dumping of extension members' RLS policies and/or security labels.
1894 : * However there is a pitfall for RLS policies: trying to dump them
1895 : * requires getting a lock on their tables, and the calling user might not
1896 : * have privileges for that. We need no lock to examine a table's ACLs,
1897 : * so the current feature doesn't have a problem of that sort.
1898 : */
1899 1654 : if (fout->dopt->binary_upgrade)
1900 288 : dobj->dump = ext->dobj.dump;
1901 : else
1902 : {
1903 1366 : if (fout->remoteVersion < 90600)
1904 0 : dobj->dump = DUMP_COMPONENT_NONE;
1905 : else
1906 1366 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1907 : }
1908 :
1909 1654 : return true;
1910 : }
1911 :
1912 : /*
1913 : * selectDumpableNamespace: policy-setting subroutine
1914 : * Mark a namespace as to be dumped or not
1915 : */
1916 : static void
1917 3740 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1918 : {
1919 : /*
1920 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1921 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1922 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1923 : */
1924 3740 : nsinfo->create = true;
1925 :
1926 : /*
1927 : * If specific tables are being dumped, do not dump any complete
1928 : * namespaces. If specific namespaces are being dumped, dump just those
1929 : * namespaces. Otherwise, dump all non-system namespaces.
1930 : */
1931 3740 : if (table_include_oids.head != NULL)
1932 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1933 3640 : else if (schema_include_oids.head != NULL)
1934 358 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1935 358 : simple_oid_list_member(&schema_include_oids,
1936 : nsinfo->dobj.catId.oid) ?
1937 358 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1938 3282 : else if (fout->remoteVersion >= 90600 &&
1939 3282 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1940 : {
1941 : /*
1942 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1943 : * they are interesting (and not the original ACLs which were set at
1944 : * initdb time, see pg_init_privs).
1945 : */
1946 426 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1947 : }
1948 2856 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1949 1254 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1950 : {
1951 : /* Other system schemas don't get dumped */
1952 2028 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1953 : }
1954 828 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1955 : {
1956 : /*
1957 : * The public schema is a strange beast that sits in a sort of
1958 : * no-mans-land between being a system object and a user object.
1959 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1960 : * a comment and an indication of ownership. If the owner is the
1961 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1962 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1963 : */
1964 418 : nsinfo->create = false;
1965 418 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1966 418 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1967 326 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1968 418 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1969 :
1970 : /*
1971 : * Also, make like it has a comment even if it doesn't; this is so
1972 : * that we'll emit a command to drop the comment, if appropriate.
1973 : * (Without this, we'd not call dumpCommentExtended for it.)
1974 : */
1975 418 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1976 : }
1977 : else
1978 410 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1979 :
1980 : /*
1981 : * In any case, a namespace can be excluded by an exclusion switch
1982 : */
1983 5016 : if (nsinfo->dobj.dump_contains &&
1984 1276 : simple_oid_list_member(&schema_exclude_oids,
1985 : nsinfo->dobj.catId.oid))
1986 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1987 :
1988 : /*
1989 : * If the schema belongs to an extension, allow extension membership to
1990 : * override the dump decision for the schema itself. However, this does
1991 : * not change dump_contains, so this won't change what we do with objects
1992 : * within the schema. (If they belong to the extension, they'll get
1993 : * suppressed by it, otherwise not.)
1994 : */
1995 3740 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1996 3740 : }
1997 :
1998 : /*
1999 : * selectDumpableTable: policy-setting subroutine
2000 : * Mark a table as to be dumped or not
2001 : */
2002 : static void
2003 123270 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2004 : {
2005 123270 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2006 450 : return; /* extension membership overrides all else */
2007 :
2008 : /*
2009 : * If specific tables are being dumped, dump just those tables; else, dump
2010 : * according to the parent namespace's dump flag.
2011 : */
2012 122820 : if (table_include_oids.head != NULL)
2013 10276 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2014 : tbinfo->dobj.catId.oid) ?
2015 5138 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2016 : else
2017 117682 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2018 :
2019 : /*
2020 : * In any case, a table can be excluded by an exclusion switch
2021 : */
2022 203548 : if (tbinfo->dobj.dump &&
2023 80728 : simple_oid_list_member(&table_exclude_oids,
2024 : tbinfo->dobj.catId.oid))
2025 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2026 : }
2027 :
2028 : /*
2029 : * selectDumpableType: policy-setting subroutine
2030 : * Mark a type as to be dumped or not
2031 : *
2032 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2033 : * special type code to facilitate sorting into the desired order. (We don't
2034 : * want to consider those to be ordinary types because that would bring tables
2035 : * up into the datatype part of the dump order.) We still set the object's
2036 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2037 : * need it so that casts involving such types will be dumped correctly -- see
2038 : * dumpCast. This means the flag should be set the same as for the underlying
2039 : * object (the table or base type).
2040 : */
2041 : static void
2042 337742 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2043 : {
2044 : /* skip complex types, except for standalone composite types */
2045 337742 : if (OidIsValid(tyinfo->typrelid) &&
2046 121572 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2047 : {
2048 121104 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2049 :
2050 121104 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2051 121104 : if (tytable != NULL)
2052 121104 : tyinfo->dobj.dump = tytable->dobj.dump;
2053 : else
2054 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2055 121104 : return;
2056 : }
2057 :
2058 : /* skip auto-generated array and multirange types */
2059 216638 : if (tyinfo->isArray || tyinfo->isMultirange)
2060 : {
2061 165150 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2062 :
2063 : /*
2064 : * Fall through to set the dump flag; we assume that the subsequent
2065 : * rules will do the same thing as they would for the array's base
2066 : * type or multirange's range type. (We cannot reliably look up the
2067 : * base type here, since getTypes may not have processed it yet.)
2068 : */
2069 : }
2070 :
2071 216638 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2072 300 : return; /* extension membership overrides all else */
2073 :
2074 : /* Dump based on if the contents of the namespace are being dumped */
2075 216338 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2076 : }
2077 :
2078 : /*
2079 : * selectDumpableDefaultACL: policy-setting subroutine
2080 : * Mark a default ACL as to be dumped or not
2081 : *
2082 : * For per-schema default ACLs, dump if the schema is to be dumped.
2083 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2084 : * and aclsSkip are checked separately.
2085 : */
2086 : static void
2087 392 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2088 : {
2089 : /* Default ACLs can't be extension members */
2090 :
2091 392 : if (dinfo->dobj.namespace)
2092 : /* default ACLs are considered part of the namespace */
2093 196 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2094 : else
2095 196 : dinfo->dobj.dump = dopt->include_everything ?
2096 196 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2097 392 : }
2098 :
2099 : /*
2100 : * selectDumpableCast: policy-setting subroutine
2101 : * Mark a cast as to be dumped or not
2102 : *
2103 : * Casts do not belong to any particular namespace (since they haven't got
2104 : * names), nor do they have identifiable owners. To distinguish user-defined
2105 : * casts from built-in ones, we must resort to checking whether the cast's
2106 : * OID is in the range reserved for initdb.
2107 : */
2108 : static void
2109 107390 : selectDumpableCast(CastInfo *cast, Archive *fout)
2110 : {
2111 107390 : if (checkExtensionMembership(&cast->dobj, fout))
2112 0 : return; /* extension membership overrides all else */
2113 :
2114 : /*
2115 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2116 : * support ACLs currently.
2117 : */
2118 107390 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2119 107172 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2120 : else
2121 218 : cast->dobj.dump = fout->dopt->include_everything ?
2122 218 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2123 : }
2124 :
2125 : /*
2126 : * selectDumpableProcLang: policy-setting subroutine
2127 : * Mark a procedural language as to be dumped or not
2128 : *
2129 : * Procedural languages do not belong to any particular namespace. To
2130 : * identify built-in languages, we must resort to checking whether the
2131 : * language's OID is in the range reserved for initdb.
2132 : */
2133 : static void
2134 566 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2135 : {
2136 566 : if (checkExtensionMembership(&plang->dobj, fout))
2137 468 : return; /* extension membership overrides all else */
2138 :
2139 : /*
2140 : * Only include procedural languages when we are dumping everything.
2141 : *
2142 : * For from-initdb procedural languages, only include ACLs, as we do for
2143 : * the pg_catalog namespace. We need this because procedural languages do
2144 : * not live in any namespace.
2145 : */
2146 98 : if (!fout->dopt->include_everything)
2147 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2148 : else
2149 : {
2150 82 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2151 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2152 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2153 : else
2154 82 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2155 : }
2156 : }
2157 :
2158 : /*
2159 : * selectDumpableAccessMethod: policy-setting subroutine
2160 : * Mark an access method as to be dumped or not
2161 : *
2162 : * Access methods do not belong to any particular namespace. To identify
2163 : * built-in access methods, we must resort to checking whether the
2164 : * method's OID is in the range reserved for initdb.
2165 : */
2166 : static void
2167 3542 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2168 : {
2169 3542 : if (checkExtensionMembership(&method->dobj, fout))
2170 50 : return; /* extension membership overrides all else */
2171 :
2172 : /*
2173 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2174 : * they do not support ACLs currently.
2175 : */
2176 3492 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2177 3276 : method->dobj.dump = DUMP_COMPONENT_NONE;
2178 : else
2179 216 : method->dobj.dump = fout->dopt->include_everything ?
2180 216 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2181 : }
2182 :
2183 : /*
2184 : * selectDumpableExtension: policy-setting subroutine
2185 : * Mark an extension as to be dumped or not
2186 : *
2187 : * Built-in extensions should be skipped except for checking ACLs, since we
2188 : * assume those will already be installed in the target database. We identify
2189 : * such extensions by their having OIDs in the range reserved for initdb.
2190 : * We dump all user-added extensions by default. No extensions are dumped
2191 : * if include_everything is false (i.e., a --schema or --table switch was
2192 : * given), except if --extension specifies a list of extensions to dump.
2193 : */
2194 : static void
2195 520 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2196 : {
2197 : /*
2198 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2199 : * change permissions on their member objects, if they wish to, and have
2200 : * those changes preserved.
2201 : */
2202 520 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2203 470 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2204 : else
2205 : {
2206 : /* check if there is a list of extensions to dump */
2207 50 : if (extension_include_oids.head != NULL)
2208 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2209 8 : simple_oid_list_member(&extension_include_oids,
2210 : extinfo->dobj.catId.oid) ?
2211 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2212 : else
2213 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2214 42 : dopt->include_everything ?
2215 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2216 :
2217 : /* check that the extension is not explicitly excluded */
2218 92 : if (extinfo->dobj.dump &&
2219 42 : simple_oid_list_member(&extension_exclude_oids,
2220 : extinfo->dobj.catId.oid))
2221 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2222 : }
2223 520 : }
2224 :
2225 : /*
2226 : * selectDumpablePublicationObject: policy-setting subroutine
2227 : * Mark a publication object as to be dumped or not
2228 : *
2229 : * A publication can have schemas and tables which have schemas, but those are
2230 : * ignored in decision making, because publications are only dumped when we are
2231 : * dumping everything.
2232 : */
2233 : static void
2234 882 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2235 : {
2236 882 : if (checkExtensionMembership(dobj, fout))
2237 0 : return; /* extension membership overrides all else */
2238 :
2239 882 : dobj->dump = fout->dopt->include_everything ?
2240 882 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2241 : }
2242 :
2243 : /*
2244 : * selectDumpableStatisticsObject: policy-setting subroutine
2245 : * Mark an extended statistics object as to be dumped or not
2246 : *
2247 : * We dump an extended statistics object if the schema it's in and the table
2248 : * it's for are being dumped. (This'll need more thought if statistics
2249 : * objects ever support cross-table stats.)
2250 : */
2251 : static void
2252 374 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2253 : {
2254 374 : if (checkExtensionMembership(&sobj->dobj, fout))
2255 0 : return; /* extension membership overrides all else */
2256 :
2257 374 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2258 374 : if (sobj->stattable == NULL ||
2259 374 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2260 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2261 : }
2262 :
2263 : /*
2264 : * selectDumpableObject: policy-setting subroutine
2265 : * Mark a generic dumpable object as to be dumped or not
2266 : *
2267 : * Use this only for object types without a special-case routine above.
2268 : */
2269 : static void
2270 1014570 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2271 : {
2272 1014570 : if (checkExtensionMembership(dobj, fout))
2273 336 : return; /* extension membership overrides all else */
2274 :
2275 : /*
2276 : * Default policy is to dump if parent namespace is dumpable, or for
2277 : * non-namespace-associated items, dump if we're dumping "everything".
2278 : */
2279 1014234 : if (dobj->namespace)
2280 1012896 : dobj->dump = dobj->namespace->dobj.dump_contains;
2281 : else
2282 1338 : dobj->dump = fout->dopt->include_everything ?
2283 1338 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2284 : }
2285 :
2286 : /*
2287 : * Dump a table's contents for loading using the COPY command
2288 : * - this routine is called by the Archiver when it wants the table
2289 : * to be dumped.
2290 : */
2291 : static int
2292 12064 : dumpTableData_copy(Archive *fout, const void *dcontext)
2293 : {
2294 12064 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2295 12064 : TableInfo *tbinfo = tdinfo->tdtable;
2296 12064 : const char *classname = tbinfo->dobj.name;
2297 12064 : PQExpBuffer q = createPQExpBuffer();
2298 :
2299 : /*
2300 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2301 : * which uses it already.
2302 : */
2303 12064 : PQExpBuffer clistBuf = createPQExpBuffer();
2304 12064 : PGconn *conn = GetConnection(fout);
2305 : PGresult *res;
2306 : int ret;
2307 : char *copybuf;
2308 : const char *column_list;
2309 :
2310 12064 : pg_log_info("dumping contents of table \"%s.%s\"",
2311 : tbinfo->dobj.namespace->dobj.name, classname);
2312 :
2313 : /*
2314 : * Specify the column list explicitly so that we have no possibility of
2315 : * retrieving data in the wrong column order. (The default column
2316 : * ordering of COPY will not be what we want in certain corner cases
2317 : * involving ADD COLUMN and inheritance.)
2318 : */
2319 12064 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2320 :
2321 : /*
2322 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2323 : * a filter condition was specified. For other cases a simple COPY
2324 : * suffices.
2325 : */
2326 12064 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2327 : {
2328 : /* Temporary allows to access to foreign tables to dump data */
2329 2 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2330 2 : set_restrict_relation_kind(fout, "view");
2331 :
2332 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2333 : /* klugery to get rid of parens in column list */
2334 2 : if (strlen(column_list) > 2)
2335 : {
2336 2 : appendPQExpBufferStr(q, column_list + 1);
2337 2 : q->data[q->len - 1] = ' ';
2338 : }
2339 : else
2340 0 : appendPQExpBufferStr(q, "* ");
2341 :
2342 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2343 2 : fmtQualifiedDumpable(tbinfo),
2344 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2345 : }
2346 : else
2347 : {
2348 12062 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2349 12062 : fmtQualifiedDumpable(tbinfo),
2350 : column_list);
2351 : }
2352 12064 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2353 12062 : PQclear(res);
2354 12062 : destroyPQExpBuffer(clistBuf);
2355 :
2356 : for (;;)
2357 : {
2358 6135118 : ret = PQgetCopyData(conn, ©buf, 0);
2359 :
2360 6135118 : if (ret < 0)
2361 12062 : break; /* done or error */
2362 :
2363 6123056 : if (copybuf)
2364 : {
2365 6123056 : WriteData(fout, copybuf, ret);
2366 6123056 : PQfreemem(copybuf);
2367 : }
2368 :
2369 : /* ----------
2370 : * THROTTLE:
2371 : *
2372 : * There was considerable discussion in late July, 2000 regarding
2373 : * slowing down pg_dump when backing up large tables. Users with both
2374 : * slow & fast (multi-processor) machines experienced performance
2375 : * degradation when doing a backup.
2376 : *
2377 : * Initial attempts based on sleeping for a number of ms for each ms
2378 : * of work were deemed too complex, then a simple 'sleep in each loop'
2379 : * implementation was suggested. The latter failed because the loop
2380 : * was too tight. Finally, the following was implemented:
2381 : *
2382 : * If throttle is non-zero, then
2383 : * See how long since the last sleep.
2384 : * Work out how long to sleep (based on ratio).
2385 : * If sleep is more than 100ms, then
2386 : * sleep
2387 : * reset timer
2388 : * EndIf
2389 : * EndIf
2390 : *
2391 : * where the throttle value was the number of ms to sleep per ms of
2392 : * work. The calculation was done in each loop.
2393 : *
2394 : * Most of the hard work is done in the backend, and this solution
2395 : * still did not work particularly well: on slow machines, the ratio
2396 : * was 50:1, and on medium paced machines, 1:1, and on fast
2397 : * multi-processor machines, it had little or no effect, for reasons
2398 : * that were unclear.
2399 : *
2400 : * Further discussion ensued, and the proposal was dropped.
2401 : *
2402 : * For those people who want this feature, it can be implemented using
2403 : * gettimeofday in each loop, calculating the time since last sleep,
2404 : * multiplying that by the sleep ratio, then if the result is more
2405 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2406 : * function to sleep for a subsecond period ie.
2407 : *
2408 : * select(0, NULL, NULL, NULL, &tvi);
2409 : *
2410 : * This will return after the interval specified in the structure tvi.
2411 : * Finally, call gettimeofday again to save the 'last sleep time'.
2412 : * ----------
2413 : */
2414 : }
2415 12062 : archprintf(fout, "\\.\n\n\n");
2416 :
2417 12062 : if (ret == -2)
2418 : {
2419 : /* copy data transfer failed */
2420 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2421 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2422 0 : pg_log_error_detail("Command was: %s", q->data);
2423 0 : exit_nicely(1);
2424 : }
2425 :
2426 : /* Check command status and return to normal libpq state */
2427 12062 : res = PQgetResult(conn);
2428 12062 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2429 : {
2430 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2431 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2432 0 : pg_log_error_detail("Command was: %s", q->data);
2433 0 : exit_nicely(1);
2434 : }
2435 12062 : PQclear(res);
2436 :
2437 : /* Do this to ensure we've pumped libpq back to idle state */
2438 12062 : if (PQgetResult(conn) != NULL)
2439 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2440 : classname);
2441 :
2442 12062 : destroyPQExpBuffer(q);
2443 :
2444 : /* Revert back the setting */
2445 12062 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2446 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2447 :
2448 12062 : return 1;
2449 : }
2450 :
2451 : /*
2452 : * Dump table data using INSERT commands.
2453 : *
2454 : * Caution: when we restore from an archive file direct to database, the
2455 : * INSERT commands emitted by this function have to be parsed by
2456 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2457 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2458 : */
2459 : static int
2460 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2461 : {
2462 162 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2463 162 : TableInfo *tbinfo = tdinfo->tdtable;
2464 162 : DumpOptions *dopt = fout->dopt;
2465 162 : PQExpBuffer q = createPQExpBuffer();
2466 162 : PQExpBuffer insertStmt = NULL;
2467 : char *attgenerated;
2468 : PGresult *res;
2469 : int nfields,
2470 : i;
2471 162 : int rows_per_statement = dopt->dump_inserts;
2472 162 : int rows_this_statement = 0;
2473 :
2474 : /* Temporary allows to access to foreign tables to dump data */
2475 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2476 0 : set_restrict_relation_kind(fout, "view");
2477 :
2478 : /*
2479 : * If we're going to emit INSERTs with column names, the most efficient
2480 : * way to deal with generated columns is to exclude them entirely. For
2481 : * INSERTs without column names, we have to emit DEFAULT rather than the
2482 : * actual column value --- but we can save a few cycles by fetching nulls
2483 : * rather than the uninteresting-to-us value.
2484 : */
2485 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2486 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2487 162 : nfields = 0;
2488 506 : for (i = 0; i < tbinfo->numatts; i++)
2489 : {
2490 344 : if (tbinfo->attisdropped[i])
2491 4 : continue;
2492 340 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2493 16 : continue;
2494 324 : if (nfields > 0)
2495 176 : appendPQExpBufferStr(q, ", ");
2496 324 : if (tbinfo->attgenerated[i])
2497 16 : appendPQExpBufferStr(q, "NULL");
2498 : else
2499 308 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2500 324 : attgenerated[nfields] = tbinfo->attgenerated[i];
2501 324 : nfields++;
2502 : }
2503 : /* Servers before 9.4 will complain about zero-column SELECT */
2504 162 : if (nfields == 0)
2505 14 : appendPQExpBufferStr(q, "NULL");
2506 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2507 162 : fmtQualifiedDumpable(tbinfo));
2508 162 : if (tdinfo->filtercond)
2509 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2510 :
2511 162 : ExecuteSqlStatement(fout, q->data);
2512 :
2513 : while (1)
2514 : {
2515 266 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2516 : PGRES_TUPLES_OK);
2517 :
2518 : /* cross-check field count, allowing for dummy NULL if any */
2519 266 : if (nfields != PQnfields(res) &&
2520 20 : !(nfields == 0 && PQnfields(res) == 1))
2521 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2522 : tbinfo->dobj.name);
2523 :
2524 : /*
2525 : * First time through, we build as much of the INSERT statement as
2526 : * possible in "insertStmt", which we can then just print for each
2527 : * statement. If the table happens to have zero dumpable columns then
2528 : * this will be a complete statement, otherwise it will end in
2529 : * "VALUES" and be ready to have the row's column values printed.
2530 : */
2531 266 : if (insertStmt == NULL)
2532 : {
2533 : TableInfo *targettab;
2534 :
2535 162 : insertStmt = createPQExpBuffer();
2536 :
2537 : /*
2538 : * When load-via-partition-root is set or forced, get the root
2539 : * table name for the partition table, so that we can reload data
2540 : * through the root table.
2541 : */
2542 162 : if (tbinfo->ispartition &&
2543 80 : (dopt->load_via_partition_root ||
2544 40 : forcePartitionRootLoad(tbinfo)))
2545 6 : targettab = getRootTableInfo(tbinfo);
2546 : else
2547 156 : targettab = tbinfo;
2548 :
2549 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2550 162 : fmtQualifiedDumpable(targettab));
2551 :
2552 : /* corner case for zero-column table */
2553 162 : if (nfields == 0)
2554 : {
2555 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2556 : }
2557 : else
2558 : {
2559 : /* append the list of column names if required */
2560 148 : if (dopt->column_inserts)
2561 : {
2562 66 : appendPQExpBufferChar(insertStmt, '(');
2563 202 : for (int field = 0; field < nfields; field++)
2564 : {
2565 136 : if (field > 0)
2566 70 : appendPQExpBufferStr(insertStmt, ", ");
2567 136 : appendPQExpBufferStr(insertStmt,
2568 136 : fmtId(PQfname(res, field)));
2569 : }
2570 66 : appendPQExpBufferStr(insertStmt, ") ");
2571 : }
2572 :
2573 148 : if (tbinfo->needs_override)
2574 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2575 :
2576 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2577 : }
2578 : }
2579 :
2580 6808 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2581 : {
2582 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2583 6542 : if (rows_this_statement == 0)
2584 6530 : archputs(insertStmt->data, fout);
2585 :
2586 : /*
2587 : * If it is zero-column table then we've already written the
2588 : * complete statement, which will mean we've disobeyed
2589 : * --rows-per-insert when it's set greater than 1. We do support
2590 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2591 : * UNION ALL ... but that's non-standard so we should avoid it
2592 : * given that using INSERTs is mostly only ever needed for
2593 : * cross-database exports.
2594 : */
2595 6542 : if (nfields == 0)
2596 12 : continue;
2597 :
2598 : /* Emit a row heading */
2599 6530 : if (rows_per_statement == 1)
2600 6512 : archputs(" (", fout);
2601 18 : else if (rows_this_statement > 0)
2602 12 : archputs(",\n\t(", fout);
2603 : else
2604 6 : archputs("\n\t(", fout);
2605 :
2606 19698 : for (int field = 0; field < nfields; field++)
2607 : {
2608 13168 : if (field > 0)
2609 6638 : archputs(", ", fout);
2610 13168 : if (attgenerated[field])
2611 : {
2612 4 : archputs("DEFAULT", fout);
2613 4 : continue;
2614 : }
2615 13164 : if (PQgetisnull(res, tuple, field))
2616 : {
2617 166 : archputs("NULL", fout);
2618 166 : continue;
2619 : }
2620 :
2621 : /* XXX This code is partially duplicated in ruleutils.c */
2622 12998 : switch (PQftype(res, field))
2623 : {
2624 8938 : case INT2OID:
2625 : case INT4OID:
2626 : case INT8OID:
2627 : case OIDOID:
2628 : case FLOAT4OID:
2629 : case FLOAT8OID:
2630 : case NUMERICOID:
2631 : {
2632 : /*
2633 : * These types are printed without quotes unless
2634 : * they contain values that aren't accepted by the
2635 : * scanner unquoted (e.g., 'NaN'). Note that
2636 : * strtod() and friends might accept NaN, so we
2637 : * can't use that to test.
2638 : *
2639 : * In reality we only need to defend against
2640 : * infinity and NaN, so we need not get too crazy
2641 : * about pattern matching here.
2642 : */
2643 8938 : const char *s = PQgetvalue(res, tuple, field);
2644 :
2645 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2646 8934 : archputs(s, fout);
2647 : else
2648 4 : archprintf(fout, "'%s'", s);
2649 : }
2650 8938 : break;
2651 :
2652 4 : case BITOID:
2653 : case VARBITOID:
2654 4 : archprintf(fout, "B'%s'",
2655 : PQgetvalue(res, tuple, field));
2656 4 : break;
2657 :
2658 8 : case BOOLOID:
2659 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2660 4 : archputs("true", fout);
2661 : else
2662 4 : archputs("false", fout);
2663 8 : break;
2664 :
2665 4048 : default:
2666 : /* All other types are printed as string literals. */
2667 4048 : resetPQExpBuffer(q);
2668 4048 : appendStringLiteralAH(q,
2669 : PQgetvalue(res, tuple, field),
2670 : fout);
2671 4048 : archputs(q->data, fout);
2672 4048 : break;
2673 : }
2674 : }
2675 :
2676 : /* Terminate the row ... */
2677 6530 : archputs(")", fout);
2678 :
2679 : /* ... and the statement, if the target no. of rows is reached */
2680 6530 : if (++rows_this_statement >= rows_per_statement)
2681 : {
2682 6516 : if (dopt->do_nothing)
2683 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2684 : else
2685 6516 : archputs(";\n", fout);
2686 : /* Reset the row counter */
2687 6516 : rows_this_statement = 0;
2688 : }
2689 : }
2690 :
2691 266 : if (PQntuples(res) <= 0)
2692 : {
2693 162 : PQclear(res);
2694 162 : break;
2695 : }
2696 104 : PQclear(res);
2697 : }
2698 :
2699 : /* Terminate any statements that didn't make the row count. */
2700 162 : if (rows_this_statement > 0)
2701 : {
2702 2 : if (dopt->do_nothing)
2703 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2704 : else
2705 2 : archputs(";\n", fout);
2706 : }
2707 :
2708 162 : archputs("\n\n", fout);
2709 :
2710 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2711 :
2712 162 : destroyPQExpBuffer(q);
2713 162 : if (insertStmt != NULL)
2714 162 : destroyPQExpBuffer(insertStmt);
2715 162 : free(attgenerated);
2716 :
2717 : /* Revert back the setting */
2718 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2719 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2720 :
2721 162 : return 1;
2722 : }
2723 :
2724 : /*
2725 : * getRootTableInfo:
2726 : * get the root TableInfo for the given partition table.
2727 : */
2728 : static TableInfo *
2729 18 : getRootTableInfo(const TableInfo *tbinfo)
2730 : {
2731 : TableInfo *parentTbinfo;
2732 :
2733 : Assert(tbinfo->ispartition);
2734 : Assert(tbinfo->numParents == 1);
2735 :
2736 18 : parentTbinfo = tbinfo->parents[0];
2737 18 : while (parentTbinfo->ispartition)
2738 : {
2739 : Assert(parentTbinfo->numParents == 1);
2740 0 : parentTbinfo = parentTbinfo->parents[0];
2741 : }
2742 :
2743 18 : return parentTbinfo;
2744 : }
2745 :
2746 : /*
2747 : * forcePartitionRootLoad
2748 : * Check if we must force load_via_partition_root for this partition.
2749 : *
2750 : * This is required if any level of ancestral partitioned table has an
2751 : * unsafe partitioning scheme.
2752 : */
2753 : static bool
2754 3060 : forcePartitionRootLoad(const TableInfo *tbinfo)
2755 : {
2756 : TableInfo *parentTbinfo;
2757 :
2758 : Assert(tbinfo->ispartition);
2759 : Assert(tbinfo->numParents == 1);
2760 :
2761 3060 : parentTbinfo = tbinfo->parents[0];
2762 3060 : if (parentTbinfo->unsafe_partitions)
2763 18 : return true;
2764 3798 : while (parentTbinfo->ispartition)
2765 : {
2766 : Assert(parentTbinfo->numParents == 1);
2767 756 : parentTbinfo = parentTbinfo->parents[0];
2768 756 : if (parentTbinfo->unsafe_partitions)
2769 0 : return true;
2770 : }
2771 :
2772 3042 : return false;
2773 : }
2774 :
2775 : /*
2776 : * dumpTableData -
2777 : * dump the contents of a single table
2778 : *
2779 : * Actually, this just makes an ArchiveEntry for the table contents.
2780 : */
2781 : static void
2782 12386 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2783 : {
2784 12386 : DumpOptions *dopt = fout->dopt;
2785 12386 : TableInfo *tbinfo = tdinfo->tdtable;
2786 12386 : PQExpBuffer copyBuf = createPQExpBuffer();
2787 12386 : PQExpBuffer clistBuf = createPQExpBuffer();
2788 : DataDumperPtr dumpFn;
2789 12386 : char *tdDefn = NULL;
2790 : char *copyStmt;
2791 : const char *copyFrom;
2792 :
2793 : /* We had better have loaded per-column details about this table */
2794 : Assert(tbinfo->interesting);
2795 :
2796 : /*
2797 : * When load-via-partition-root is set or forced, get the root table name
2798 : * for the partition table, so that we can reload data through the root
2799 : * table. Then construct a comment to be inserted into the TOC entry's
2800 : * defn field, so that such cases can be identified reliably.
2801 : */
2802 12386 : if (tbinfo->ispartition &&
2803 6040 : (dopt->load_via_partition_root ||
2804 3020 : forcePartitionRootLoad(tbinfo)))
2805 12 : {
2806 : TableInfo *parentTbinfo;
2807 :
2808 12 : parentTbinfo = getRootTableInfo(tbinfo);
2809 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2810 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2811 : copyFrom);
2812 12 : tdDefn = pg_strdup(copyBuf->data);
2813 : }
2814 : else
2815 12374 : copyFrom = fmtQualifiedDumpable(tbinfo);
2816 :
2817 12386 : if (dopt->dump_inserts == 0)
2818 : {
2819 : /* Dump/restore using COPY */
2820 12224 : dumpFn = dumpTableData_copy;
2821 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2822 12224 : printfPQExpBuffer(copyBuf, "COPY %s ",
2823 : copyFrom);
2824 12224 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2825 : fmtCopyColumnList(tbinfo, clistBuf));
2826 12224 : copyStmt = copyBuf->data;
2827 : }
2828 : else
2829 : {
2830 : /* Restore using INSERT */
2831 162 : dumpFn = dumpTableData_insert;
2832 162 : copyStmt = NULL;
2833 : }
2834 :
2835 : /*
2836 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2837 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2838 : * See comments for BuildArchiveDependencies.
2839 : */
2840 12386 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2841 : {
2842 : TocEntry *te;
2843 :
2844 12386 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2845 12386 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2846 : .namespace = tbinfo->dobj.namespace->dobj.name,
2847 : .owner = tbinfo->rolname,
2848 : .description = "TABLE DATA",
2849 : .section = SECTION_DATA,
2850 : .createStmt = tdDefn,
2851 : .copyStmt = copyStmt,
2852 : .deps = &(tbinfo->dobj.dumpId),
2853 : .nDeps = 1,
2854 : .dumpFn = dumpFn,
2855 : .dumpArg = tdinfo));
2856 :
2857 : /*
2858 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2859 : * and want to order dump jobs by table size. We choose to measure
2860 : * dataLength in table pages (including TOAST pages) during dump, so
2861 : * no scaling is needed.
2862 : *
2863 : * However, relpages is declared as "integer" in pg_class, and hence
2864 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2865 : * Cast so that we get the right interpretation of table sizes
2866 : * exceeding INT_MAX pages.
2867 : */
2868 12386 : te->dataLength = (BlockNumber) tbinfo->relpages;
2869 12386 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2870 :
2871 : /*
2872 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2873 : * and instead we'd better worry about integer overflow. Clamp to
2874 : * INT_MAX if the correct result exceeds that.
2875 : */
2876 : if (sizeof(te->dataLength) == 4 &&
2877 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2878 : te->dataLength < 0))
2879 : te->dataLength = INT_MAX;
2880 : }
2881 :
2882 12386 : destroyPQExpBuffer(copyBuf);
2883 12386 : destroyPQExpBuffer(clistBuf);
2884 12386 : }
2885 :
2886 : /*
2887 : * refreshMatViewData -
2888 : * load or refresh the contents of a single materialized view
2889 : *
2890 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2891 : * statement.
2892 : */
2893 : static void
2894 852 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2895 : {
2896 852 : TableInfo *tbinfo = tdinfo->tdtable;
2897 : PQExpBuffer q;
2898 :
2899 : /* If the materialized view is not flagged as populated, skip this. */
2900 852 : if (!tbinfo->relispopulated)
2901 148 : return;
2902 :
2903 704 : q = createPQExpBuffer();
2904 :
2905 704 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2906 704 : fmtQualifiedDumpable(tbinfo));
2907 :
2908 704 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2909 704 : ArchiveEntry(fout,
2910 : tdinfo->dobj.catId, /* catalog ID */
2911 704 : tdinfo->dobj.dumpId, /* dump ID */
2912 704 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2913 : .namespace = tbinfo->dobj.namespace->dobj.name,
2914 : .owner = tbinfo->rolname,
2915 : .description = "MATERIALIZED VIEW DATA",
2916 : .section = SECTION_POST_DATA,
2917 : .createStmt = q->data,
2918 : .deps = tdinfo->dobj.dependencies,
2919 : .nDeps = tdinfo->dobj.nDeps));
2920 :
2921 704 : destroyPQExpBuffer(q);
2922 : }
2923 :
2924 : /*
2925 : * getTableData -
2926 : * set up dumpable objects representing the contents of tables
2927 : */
2928 : static void
2929 452 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2930 : {
2931 : int i;
2932 :
2933 119778 : for (i = 0; i < numTables; i++)
2934 : {
2935 119326 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2936 1818 : (!relkind || tblinfo[i].relkind == relkind))
2937 17146 : makeTableDataInfo(dopt, &(tblinfo[i]));
2938 : }
2939 452 : }
2940 :
2941 : /*
2942 : * Make a dumpable object for the data of this specific table
2943 : *
2944 : * Note: we make a TableDataInfo if and only if we are going to dump the
2945 : * table data; the "dump" field in such objects isn't very interesting.
2946 : */
2947 : static void
2948 17224 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2949 : {
2950 : TableDataInfo *tdinfo;
2951 :
2952 : /*
2953 : * Nothing to do if we already decided to dump the table. This will
2954 : * happen for "config" tables.
2955 : */
2956 17224 : if (tbinfo->dataObj != NULL)
2957 2 : return;
2958 :
2959 : /* Skip VIEWs (no data to dump) */
2960 17222 : if (tbinfo->relkind == RELKIND_VIEW)
2961 1268 : return;
2962 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2963 15954 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2964 82 : (foreign_servers_include_oids.head == NULL ||
2965 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2966 : tbinfo->foreign_server)))
2967 80 : return;
2968 : /* Skip partitioned tables (data in partitions) */
2969 15874 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2970 1448 : return;
2971 :
2972 : /* Don't dump data in unlogged tables, if so requested */
2973 14426 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2974 136 : dopt->no_unlogged_table_data)
2975 36 : return;
2976 :
2977 : /* Check that the data is not explicitly excluded */
2978 14390 : if (simple_oid_list_member(&tabledata_exclude_oids,
2979 : tbinfo->dobj.catId.oid))
2980 16 : return;
2981 :
2982 : /* OK, let's dump it */
2983 14374 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2984 :
2985 14374 : if (tbinfo->relkind == RELKIND_MATVIEW)
2986 852 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2987 13522 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2988 1136 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2989 : else
2990 12386 : tdinfo->dobj.objType = DO_TABLE_DATA;
2991 :
2992 : /*
2993 : * Note: use tableoid 0 so that this object won't be mistaken for
2994 : * something that pg_depend entries apply to.
2995 : */
2996 14374 : tdinfo->dobj.catId.tableoid = 0;
2997 14374 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2998 14374 : AssignDumpId(&tdinfo->dobj);
2999 14374 : tdinfo->dobj.name = tbinfo->dobj.name;
3000 14374 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3001 14374 : tdinfo->tdtable = tbinfo;
3002 14374 : tdinfo->filtercond = NULL; /* might get set later */
3003 14374 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3004 :
3005 : /* A TableDataInfo contains data, of course */
3006 14374 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3007 :
3008 14374 : tbinfo->dataObj = tdinfo;
3009 :
3010 : /*
3011 : * Materialized view statistics must be restored after the data, because
3012 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3013 : *
3014 : * The dependency is added here because the statistics objects are created
3015 : * first.
3016 : */
3017 14374 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3018 : {
3019 640 : tbinfo->stats->section = SECTION_POST_DATA;
3020 640 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3021 : }
3022 :
3023 : /* Make sure that we'll collect per-column info for this table. */
3024 14374 : tbinfo->interesting = true;
3025 : }
3026 :
3027 : /*
3028 : * The refresh for a materialized view must be dependent on the refresh for
3029 : * any materialized view that this one is dependent on.
3030 : *
3031 : * This must be called after all the objects are created, but before they are
3032 : * sorted.
3033 : */
3034 : static void
3035 396 : buildMatViewRefreshDependencies(Archive *fout)
3036 : {
3037 : PQExpBuffer query;
3038 : PGresult *res;
3039 : int ntups,
3040 : i;
3041 : int i_classid,
3042 : i_objid,
3043 : i_refobjid;
3044 :
3045 : /* No Mat Views before 9.3. */
3046 396 : if (fout->remoteVersion < 90300)
3047 0 : return;
3048 :
3049 396 : query = createPQExpBuffer();
3050 :
3051 396 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3052 : "( "
3053 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3054 : "FROM pg_depend d1 "
3055 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3056 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3057 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3058 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3059 : "AND d2.objid = r1.oid "
3060 : "AND d2.refobjid <> d1.objid "
3061 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3062 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3063 : CppAsString2(RELKIND_VIEW) ") "
3064 : "WHERE d1.classid = 'pg_class'::regclass "
3065 : "UNION "
3066 : "SELECT w.objid, d3.refobjid, c3.relkind "
3067 : "FROM w "
3068 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3069 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3070 : "AND d3.objid = r3.oid "
3071 : "AND d3.refobjid <> w.refobjid "
3072 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3073 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3074 : CppAsString2(RELKIND_VIEW) ") "
3075 : ") "
3076 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3077 : "FROM w "
3078 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3079 :
3080 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3081 :
3082 396 : ntups = PQntuples(res);
3083 :
3084 396 : i_classid = PQfnumber(res, "classid");
3085 396 : i_objid = PQfnumber(res, "objid");
3086 396 : i_refobjid = PQfnumber(res, "refobjid");
3087 :
3088 978 : for (i = 0; i < ntups; i++)
3089 : {
3090 : CatalogId objId;
3091 : CatalogId refobjId;
3092 : DumpableObject *dobj;
3093 : DumpableObject *refdobj;
3094 : TableInfo *tbinfo;
3095 : TableInfo *reftbinfo;
3096 :
3097 582 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3098 582 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3099 582 : refobjId.tableoid = objId.tableoid;
3100 582 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3101 :
3102 582 : dobj = findObjectByCatalogId(objId);
3103 582 : if (dobj == NULL)
3104 96 : continue;
3105 :
3106 : Assert(dobj->objType == DO_TABLE);
3107 582 : tbinfo = (TableInfo *) dobj;
3108 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3109 582 : dobj = (DumpableObject *) tbinfo->dataObj;
3110 582 : if (dobj == NULL)
3111 96 : continue;
3112 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3113 :
3114 486 : refdobj = findObjectByCatalogId(refobjId);
3115 486 : if (refdobj == NULL)
3116 0 : continue;
3117 :
3118 : Assert(refdobj->objType == DO_TABLE);
3119 486 : reftbinfo = (TableInfo *) refdobj;
3120 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3121 486 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3122 486 : if (refdobj == NULL)
3123 0 : continue;
3124 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3125 :
3126 486 : addObjectDependency(dobj, refdobj->dumpId);
3127 :
3128 486 : if (!reftbinfo->relispopulated)
3129 74 : tbinfo->relispopulated = false;
3130 : }
3131 :
3132 396 : PQclear(res);
3133 :
3134 396 : destroyPQExpBuffer(query);
3135 : }
3136 :
3137 : /*
3138 : * getTableDataFKConstraints -
3139 : * add dump-order dependencies reflecting foreign key constraints
3140 : *
3141 : * This code is executed only in a data-only dump --- in schema+data dumps
3142 : * we handle foreign key issues by not creating the FK constraints until
3143 : * after the data is loaded. In a data-only dump, however, we want to
3144 : * order the table data objects in such a way that a table's referenced
3145 : * tables are restored first. (In the presence of circular references or
3146 : * self-references this may be impossible; we'll detect and complain about
3147 : * that during the dependency sorting step.)
3148 : */
3149 : static void
3150 14 : getTableDataFKConstraints(void)
3151 : {
3152 : DumpableObject **dobjs;
3153 : int numObjs;
3154 : int i;
3155 :
3156 : /* Search through all the dumpable objects for FK constraints */
3157 14 : getDumpableObjects(&dobjs, &numObjs);
3158 51242 : for (i = 0; i < numObjs; i++)
3159 : {
3160 51228 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3161 : {
3162 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3163 : TableInfo *ftable;
3164 :
3165 : /* Not interesting unless both tables are to be dumped */
3166 16 : if (cinfo->contable == NULL ||
3167 16 : cinfo->contable->dataObj == NULL)
3168 8 : continue;
3169 8 : ftable = findTableByOid(cinfo->confrelid);
3170 8 : if (ftable == NULL ||
3171 8 : ftable->dataObj == NULL)
3172 0 : continue;
3173 :
3174 : /*
3175 : * Okay, make referencing table's TABLE_DATA object depend on the
3176 : * referenced table's TABLE_DATA object.
3177 : */
3178 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3179 8 : ftable->dataObj->dobj.dumpId);
3180 : }
3181 : }
3182 14 : free(dobjs);
3183 14 : }
3184 :
3185 :
3186 : /*
3187 : * dumpDatabase:
3188 : * dump the database definition
3189 : */
3190 : static void
3191 264 : dumpDatabase(Archive *fout)
3192 : {
3193 264 : DumpOptions *dopt = fout->dopt;
3194 264 : PQExpBuffer dbQry = createPQExpBuffer();
3195 264 : PQExpBuffer delQry = createPQExpBuffer();
3196 264 : PQExpBuffer creaQry = createPQExpBuffer();
3197 264 : PQExpBuffer labelq = createPQExpBuffer();
3198 264 : PGconn *conn = GetConnection(fout);
3199 : PGresult *res;
3200 : int i_tableoid,
3201 : i_oid,
3202 : i_datname,
3203 : i_datdba,
3204 : i_encoding,
3205 : i_datlocprovider,
3206 : i_collate,
3207 : i_ctype,
3208 : i_datlocale,
3209 : i_daticurules,
3210 : i_frozenxid,
3211 : i_minmxid,
3212 : i_datacl,
3213 : i_acldefault,
3214 : i_datistemplate,
3215 : i_datconnlimit,
3216 : i_datcollversion,
3217 : i_tablespace;
3218 : CatalogId dbCatId;
3219 : DumpId dbDumpId;
3220 : DumpableAcl dbdacl;
3221 : const char *datname,
3222 : *dba,
3223 : *encoding,
3224 : *datlocprovider,
3225 : *collate,
3226 : *ctype,
3227 : *locale,
3228 : *icurules,
3229 : *datistemplate,
3230 : *datconnlimit,
3231 : *tablespace;
3232 : uint32 frozenxid,
3233 : minmxid;
3234 : char *qdatname;
3235 :
3236 264 : pg_log_info("saving database definition");
3237 :
3238 : /*
3239 : * Fetch the database-level properties for this database.
3240 : */
3241 264 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3242 : "datdba, "
3243 : "pg_encoding_to_char(encoding) AS encoding, "
3244 : "datcollate, datctype, datfrozenxid, "
3245 : "datacl, acldefault('d', datdba) AS acldefault, "
3246 : "datistemplate, datconnlimit, ");
3247 264 : if (fout->remoteVersion >= 90300)
3248 264 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3249 : else
3250 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3251 264 : if (fout->remoteVersion >= 170000)
3252 264 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3253 0 : else if (fout->remoteVersion >= 150000)
3254 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3255 : else
3256 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3257 264 : if (fout->remoteVersion >= 160000)
3258 264 : appendPQExpBufferStr(dbQry, "daticurules, ");
3259 : else
3260 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3261 264 : appendPQExpBufferStr(dbQry,
3262 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3263 : "shobj_description(oid, 'pg_database') AS description "
3264 : "FROM pg_database "
3265 : "WHERE datname = current_database()");
3266 :
3267 264 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3268 :
3269 264 : i_tableoid = PQfnumber(res, "tableoid");
3270 264 : i_oid = PQfnumber(res, "oid");
3271 264 : i_datname = PQfnumber(res, "datname");
3272 264 : i_datdba = PQfnumber(res, "datdba");
3273 264 : i_encoding = PQfnumber(res, "encoding");
3274 264 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3275 264 : i_collate = PQfnumber(res, "datcollate");
3276 264 : i_ctype = PQfnumber(res, "datctype");
3277 264 : i_datlocale = PQfnumber(res, "datlocale");
3278 264 : i_daticurules = PQfnumber(res, "daticurules");
3279 264 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3280 264 : i_minmxid = PQfnumber(res, "datminmxid");
3281 264 : i_datacl = PQfnumber(res, "datacl");
3282 264 : i_acldefault = PQfnumber(res, "acldefault");
3283 264 : i_datistemplate = PQfnumber(res, "datistemplate");
3284 264 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3285 264 : i_datcollversion = PQfnumber(res, "datcollversion");
3286 264 : i_tablespace = PQfnumber(res, "tablespace");
3287 :
3288 264 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3289 264 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3290 264 : datname = PQgetvalue(res, 0, i_datname);
3291 264 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3292 264 : encoding = PQgetvalue(res, 0, i_encoding);
3293 264 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3294 264 : collate = PQgetvalue(res, 0, i_collate);
3295 264 : ctype = PQgetvalue(res, 0, i_ctype);
3296 264 : if (!PQgetisnull(res, 0, i_datlocale))
3297 30 : locale = PQgetvalue(res, 0, i_datlocale);
3298 : else
3299 234 : locale = NULL;
3300 264 : if (!PQgetisnull(res, 0, i_daticurules))
3301 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3302 : else
3303 264 : icurules = NULL;
3304 264 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3305 264 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3306 264 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3307 264 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3308 264 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3309 264 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3310 264 : tablespace = PQgetvalue(res, 0, i_tablespace);
3311 :
3312 264 : qdatname = pg_strdup(fmtId(datname));
3313 :
3314 : /*
3315 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3316 : * to preserve that), as well as the encoding, locale, and tablespace
3317 : * since those can't be altered later. Other DB properties are left to
3318 : * the DATABASE PROPERTIES entry, so that they can be applied after
3319 : * reconnecting to the target DB.
3320 : *
3321 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3322 : * shown it to be faster. When the server is in binary upgrade mode, it
3323 : * will also skip the checkpoints this strategy ordinarily performs.
3324 : */
3325 264 : if (dopt->binary_upgrade)
3326 : {
3327 60 : appendPQExpBuffer(creaQry,
3328 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3329 : "OID = %u STRATEGY = FILE_COPY",
3330 : qdatname, dbCatId.oid);
3331 : }
3332 : else
3333 : {
3334 204 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3335 : qdatname);
3336 : }
3337 264 : if (strlen(encoding) > 0)
3338 : {
3339 264 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3340 264 : appendStringLiteralAH(creaQry, encoding, fout);
3341 : }
3342 :
3343 264 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3344 264 : if (datlocprovider[0] == 'b')
3345 30 : appendPQExpBufferStr(creaQry, "builtin");
3346 234 : else if (datlocprovider[0] == 'c')
3347 234 : appendPQExpBufferStr(creaQry, "libc");
3348 0 : else if (datlocprovider[0] == 'i')
3349 0 : appendPQExpBufferStr(creaQry, "icu");
3350 : else
3351 0 : pg_fatal("unrecognized locale provider: %s",
3352 : datlocprovider);
3353 :
3354 264 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3355 : {
3356 264 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3357 264 : appendStringLiteralAH(creaQry, collate, fout);
3358 : }
3359 : else
3360 : {
3361 0 : if (strlen(collate) > 0)
3362 : {
3363 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3364 0 : appendStringLiteralAH(creaQry, collate, fout);
3365 : }
3366 0 : if (strlen(ctype) > 0)
3367 : {
3368 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3369 0 : appendStringLiteralAH(creaQry, ctype, fout);
3370 : }
3371 : }
3372 264 : if (locale)
3373 : {
3374 30 : if (datlocprovider[0] == 'b')
3375 30 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3376 : else
3377 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3378 :
3379 30 : appendStringLiteralAH(creaQry, locale, fout);
3380 : }
3381 :
3382 264 : if (icurules)
3383 : {
3384 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3385 0 : appendStringLiteralAH(creaQry, icurules, fout);
3386 : }
3387 :
3388 : /*
3389 : * For binary upgrade, carry over the collation version. For normal
3390 : * dump/restore, omit the version, so that it is computed upon restore.
3391 : */
3392 264 : if (dopt->binary_upgrade)
3393 : {
3394 60 : if (!PQgetisnull(res, 0, i_datcollversion))
3395 : {
3396 60 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3397 60 : appendStringLiteralAH(creaQry,
3398 : PQgetvalue(res, 0, i_datcollversion),
3399 : fout);
3400 : }
3401 : }
3402 :
3403 : /*
3404 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3405 : * thing; the decision whether to specify a tablespace should be left till
3406 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3407 : * label the DATABASE entry with the tablespace and let the normal
3408 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3409 : * attention to default_tablespace, so that won't work.
3410 : */
3411 264 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3412 0 : !dopt->outputNoTablespaces)
3413 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3414 : fmtId(tablespace));
3415 264 : appendPQExpBufferStr(creaQry, ";\n");
3416 :
3417 264 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3418 : qdatname);
3419 :
3420 264 : dbDumpId = createDumpId();
3421 :
3422 264 : ArchiveEntry(fout,
3423 : dbCatId, /* catalog ID */
3424 : dbDumpId, /* dump ID */
3425 264 : ARCHIVE_OPTS(.tag = datname,
3426 : .owner = dba,
3427 : .description = "DATABASE",
3428 : .section = SECTION_PRE_DATA,
3429 : .createStmt = creaQry->data,
3430 : .dropStmt = delQry->data));
3431 :
3432 : /* Compute correct tag for archive entry */
3433 264 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3434 :
3435 : /* Dump DB comment if any */
3436 : {
3437 : /*
3438 : * 8.2 and up keep comments on shared objects in a shared table, so we
3439 : * cannot use the dumpComment() code used for other database objects.
3440 : * Be careful that the ArchiveEntry parameters match that function.
3441 : */
3442 264 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3443 :
3444 264 : if (comment && *comment && !dopt->no_comments)
3445 : {
3446 102 : resetPQExpBuffer(dbQry);
3447 :
3448 : /*
3449 : * Generates warning when loaded into a differently-named
3450 : * database.
3451 : */
3452 102 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3453 102 : appendStringLiteralAH(dbQry, comment, fout);
3454 102 : appendPQExpBufferStr(dbQry, ";\n");
3455 :
3456 102 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3457 102 : ARCHIVE_OPTS(.tag = labelq->data,
3458 : .owner = dba,
3459 : .description = "COMMENT",
3460 : .section = SECTION_NONE,
3461 : .createStmt = dbQry->data,
3462 : .deps = &dbDumpId,
3463 : .nDeps = 1));
3464 : }
3465 : }
3466 :
3467 : /* Dump DB security label, if enabled */
3468 264 : if (!dopt->no_security_labels)
3469 : {
3470 : PGresult *shres;
3471 : PQExpBuffer seclabelQry;
3472 :
3473 264 : seclabelQry = createPQExpBuffer();
3474 :
3475 264 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3476 264 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3477 264 : resetPQExpBuffer(seclabelQry);
3478 264 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3479 264 : if (seclabelQry->len > 0)
3480 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3481 0 : ARCHIVE_OPTS(.tag = labelq->data,
3482 : .owner = dba,
3483 : .description = "SECURITY LABEL",
3484 : .section = SECTION_NONE,
3485 : .createStmt = seclabelQry->data,
3486 : .deps = &dbDumpId,
3487 : .nDeps = 1));
3488 264 : destroyPQExpBuffer(seclabelQry);
3489 264 : PQclear(shres);
3490 : }
3491 :
3492 : /*
3493 : * Dump ACL if any. Note that we do not support initial privileges
3494 : * (pg_init_privs) on databases.
3495 : */
3496 264 : dbdacl.privtype = 0;
3497 264 : dbdacl.initprivs = NULL;
3498 :
3499 264 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3500 : qdatname, NULL, NULL,
3501 : NULL, dba, &dbdacl);
3502 :
3503 : /*
3504 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3505 : * non-default database-level properties. (The reason this must be
3506 : * separate is that we cannot put any additional commands into the TOC
3507 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3508 : * in an implicit transaction block, and the backend won't allow CREATE
3509 : * DATABASE in that context.)
3510 : */
3511 264 : resetPQExpBuffer(creaQry);
3512 264 : resetPQExpBuffer(delQry);
3513 :
3514 264 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3515 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3516 : qdatname, datconnlimit);
3517 :
3518 264 : if (strcmp(datistemplate, "t") == 0)
3519 : {
3520 34 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3521 : qdatname);
3522 :
3523 : /*
3524 : * The backend won't accept DROP DATABASE on a template database. We
3525 : * can deal with that by removing the template marking before the DROP
3526 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3527 : * since no such command is currently supported, fake it with a direct
3528 : * UPDATE on pg_database.
3529 : */
3530 34 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3531 : "SET datistemplate = false WHERE datname = ");
3532 34 : appendStringLiteralAH(delQry, datname, fout);
3533 34 : appendPQExpBufferStr(delQry, ";\n");
3534 : }
3535 :
3536 : /*
3537 : * We do not restore pg_database.dathasloginevt because it is set
3538 : * automatically on login event trigger creation.
3539 : */
3540 :
3541 : /* Add database-specific SET options */
3542 264 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3543 :
3544 : /*
3545 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3546 : * entry, too, for lack of a better place.
3547 : */
3548 264 : if (dopt->binary_upgrade)
3549 : {
3550 60 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3551 60 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3552 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3553 : "WHERE datname = ",
3554 : frozenxid, minmxid);
3555 60 : appendStringLiteralAH(creaQry, datname, fout);
3556 60 : appendPQExpBufferStr(creaQry, ";\n");
3557 : }
3558 :
3559 264 : if (creaQry->len > 0)
3560 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3561 84 : ARCHIVE_OPTS(.tag = datname,
3562 : .owner = dba,
3563 : .description = "DATABASE PROPERTIES",
3564 : .section = SECTION_PRE_DATA,
3565 : .createStmt = creaQry->data,
3566 : .dropStmt = delQry->data,
3567 : .deps = &dbDumpId));
3568 :
3569 : /*
3570 : * pg_largeobject comes from the old system intact, so set its
3571 : * relfrozenxids, relminmxids and relfilenode.
3572 : */
3573 264 : if (dopt->binary_upgrade)
3574 : {
3575 : PGresult *lo_res;
3576 60 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3577 60 : PQExpBuffer loOutQry = createPQExpBuffer();
3578 60 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3579 : int ii_relfrozenxid,
3580 : ii_relfilenode,
3581 : ii_oid,
3582 : ii_relminmxid;
3583 :
3584 : /*
3585 : * pg_largeobject
3586 : */
3587 60 : if (fout->remoteVersion >= 90300)
3588 60 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3589 : "FROM pg_catalog.pg_class\n"
3590 : "WHERE oid IN (%u, %u);\n",
3591 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3592 : else
3593 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3594 : "FROM pg_catalog.pg_class\n"
3595 : "WHERE oid IN (%u, %u);\n",
3596 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3597 :
3598 60 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3599 :
3600 60 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3601 60 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3602 60 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3603 60 : ii_oid = PQfnumber(lo_res, "oid");
3604 :
3605 60 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3606 60 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3607 180 : for (int i = 0; i < PQntuples(lo_res); ++i)
3608 : {
3609 : Oid oid;
3610 : RelFileNumber relfilenumber;
3611 :
3612 120 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3613 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3614 : "WHERE oid = %u;\n",
3615 120 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3616 120 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3617 120 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3618 :
3619 120 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3620 120 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3621 :
3622 120 : if (oid == LargeObjectRelationId)
3623 60 : appendPQExpBuffer(loOutQry,
3624 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3625 : relfilenumber);
3626 60 : else if (oid == LargeObjectLOidPNIndexId)
3627 60 : appendPQExpBuffer(loOutQry,
3628 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3629 : relfilenumber);
3630 : }
3631 :
3632 60 : appendPQExpBufferStr(loOutQry,
3633 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3634 60 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3635 :
3636 60 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3637 60 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3638 : .description = "pg_largeobject",
3639 : .section = SECTION_PRE_DATA,
3640 : .createStmt = loOutQry->data));
3641 :
3642 60 : PQclear(lo_res);
3643 :
3644 60 : destroyPQExpBuffer(loFrozenQry);
3645 60 : destroyPQExpBuffer(loHorizonQry);
3646 60 : destroyPQExpBuffer(loOutQry);
3647 : }
3648 :
3649 264 : PQclear(res);
3650 :
3651 264 : free(qdatname);
3652 264 : destroyPQExpBuffer(dbQry);
3653 264 : destroyPQExpBuffer(delQry);
3654 264 : destroyPQExpBuffer(creaQry);
3655 264 : destroyPQExpBuffer(labelq);
3656 264 : }
3657 :
3658 : /*
3659 : * Collect any database-specific or role-and-database-specific SET options
3660 : * for this database, and append them to outbuf.
3661 : */
3662 : static void
3663 264 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3664 : const char *dbname, Oid dboid)
3665 : {
3666 264 : PGconn *conn = GetConnection(AH);
3667 264 : PQExpBuffer buf = createPQExpBuffer();
3668 : PGresult *res;
3669 :
3670 : /* First collect database-specific options */
3671 264 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3672 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3673 : dboid);
3674 :
3675 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3676 :
3677 336 : for (int i = 0; i < PQntuples(res); i++)
3678 72 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3679 : "DATABASE", dbname, NULL, NULL,
3680 : outbuf);
3681 :
3682 264 : PQclear(res);
3683 :
3684 : /* Now look for role-and-database-specific options */
3685 264 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3686 : "FROM pg_db_role_setting s, pg_roles r "
3687 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3688 : dboid);
3689 :
3690 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3691 :
3692 264 : for (int i = 0; i < PQntuples(res); i++)
3693 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3694 0 : "ROLE", PQgetvalue(res, i, 0),
3695 : "DATABASE", dbname,
3696 : outbuf);
3697 :
3698 264 : PQclear(res);
3699 :
3700 264 : destroyPQExpBuffer(buf);
3701 264 : }
3702 :
3703 : /*
3704 : * dumpEncoding: put the correct encoding into the archive
3705 : */
3706 : static void
3707 468 : dumpEncoding(Archive *AH)
3708 : {
3709 468 : const char *encname = pg_encoding_to_char(AH->encoding);
3710 468 : PQExpBuffer qry = createPQExpBuffer();
3711 :
3712 468 : pg_log_info("saving encoding = %s", encname);
3713 :
3714 468 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3715 468 : appendStringLiteralAH(qry, encname, AH);
3716 468 : appendPQExpBufferStr(qry, ";\n");
3717 :
3718 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3719 468 : ARCHIVE_OPTS(.tag = "ENCODING",
3720 : .description = "ENCODING",
3721 : .section = SECTION_PRE_DATA,
3722 : .createStmt = qry->data));
3723 :
3724 468 : destroyPQExpBuffer(qry);
3725 468 : }
3726 :
3727 :
3728 : /*
3729 : * dumpStdStrings: put the correct escape string behavior into the archive
3730 : */
3731 : static void
3732 468 : dumpStdStrings(Archive *AH)
3733 : {
3734 468 : const char *stdstrings = AH->std_strings ? "on" : "off";
3735 468 : PQExpBuffer qry = createPQExpBuffer();
3736 :
3737 468 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3738 : stdstrings);
3739 :
3740 468 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3741 : stdstrings);
3742 :
3743 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3744 468 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3745 : .description = "STDSTRINGS",
3746 : .section = SECTION_PRE_DATA,
3747 : .createStmt = qry->data));
3748 :
3749 468 : destroyPQExpBuffer(qry);
3750 468 : }
3751 :
3752 : /*
3753 : * dumpSearchPath: record the active search_path in the archive
3754 : */
3755 : static void
3756 468 : dumpSearchPath(Archive *AH)
3757 : {
3758 468 : PQExpBuffer qry = createPQExpBuffer();
3759 468 : PQExpBuffer path = createPQExpBuffer();
3760 : PGresult *res;
3761 468 : char **schemanames = NULL;
3762 468 : int nschemanames = 0;
3763 : int i;
3764 :
3765 : /*
3766 : * We use the result of current_schemas(), not the search_path GUC,
3767 : * because that might contain wildcards such as "$user", which won't
3768 : * necessarily have the same value during restore. Also, this way avoids
3769 : * listing schemas that may appear in search_path but not actually exist,
3770 : * which seems like a prudent exclusion.
3771 : */
3772 468 : res = ExecuteSqlQueryForSingleRow(AH,
3773 : "SELECT pg_catalog.current_schemas(false)");
3774 :
3775 468 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3776 0 : pg_fatal("could not parse result of current_schemas()");
3777 :
3778 : /*
3779 : * We use set_config(), not a simple "SET search_path" command, because
3780 : * the latter has less-clean behavior if the search path is empty. While
3781 : * that's likely to get fixed at some point, it seems like a good idea to
3782 : * be as backwards-compatible as possible in what we put into archives.
3783 : */
3784 468 : for (i = 0; i < nschemanames; i++)
3785 : {
3786 0 : if (i > 0)
3787 0 : appendPQExpBufferStr(path, ", ");
3788 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3789 : }
3790 :
3791 468 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3792 468 : appendStringLiteralAH(qry, path->data, AH);
3793 468 : appendPQExpBufferStr(qry, ", false);\n");
3794 :
3795 468 : pg_log_info("saving \"search_path = %s\"", path->data);
3796 :
3797 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3798 468 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3799 : .description = "SEARCHPATH",
3800 : .section = SECTION_PRE_DATA,
3801 : .createStmt = qry->data));
3802 :
3803 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3804 468 : AH->searchpath = pg_strdup(qry->data);
3805 :
3806 468 : free(schemanames);
3807 468 : PQclear(res);
3808 468 : destroyPQExpBuffer(qry);
3809 468 : destroyPQExpBuffer(path);
3810 468 : }
3811 :
3812 :
3813 : /*
3814 : * getLOs:
3815 : * Collect schema-level data about large objects
3816 : */
3817 : static void
3818 410 : getLOs(Archive *fout)
3819 : {
3820 410 : DumpOptions *dopt = fout->dopt;
3821 410 : PQExpBuffer loQry = createPQExpBuffer();
3822 : PGresult *res;
3823 : int ntups;
3824 : int i;
3825 : int n;
3826 : int i_oid;
3827 : int i_lomowner;
3828 : int i_lomacl;
3829 : int i_acldefault;
3830 :
3831 410 : pg_log_info("reading large objects");
3832 :
3833 : /*
3834 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3835 : * with the same owner/ACL appear together.
3836 : */
3837 410 : appendPQExpBufferStr(loQry,
3838 : "SELECT oid, lomowner, lomacl, "
3839 : "acldefault('L', lomowner) AS acldefault "
3840 : "FROM pg_largeobject_metadata "
3841 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3842 :
3843 410 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3844 :
3845 410 : i_oid = PQfnumber(res, "oid");
3846 410 : i_lomowner = PQfnumber(res, "lomowner");
3847 410 : i_lomacl = PQfnumber(res, "lomacl");
3848 410 : i_acldefault = PQfnumber(res, "acldefault");
3849 :
3850 410 : ntups = PQntuples(res);
3851 :
3852 : /*
3853 : * Group the blobs into suitably-sized groups that have the same owner and
3854 : * ACL setting, and build a metadata and a data DumpableObject for each
3855 : * group. (If we supported initprivs for blobs, we'd have to insist that
3856 : * groups also share initprivs settings, since the DumpableObject only has
3857 : * room for one.) i is the index of the first tuple in the current group,
3858 : * and n is the number of tuples we include in the group.
3859 : */
3860 574 : for (i = 0; i < ntups; i += n)
3861 : {
3862 164 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3863 164 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3864 164 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3865 : LoInfo *loinfo;
3866 : DumpableObject *lodata;
3867 : char namebuf[64];
3868 :
3869 : /* Scan to find first tuple not to be included in group */
3870 164 : n = 1;
3871 196 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3872 : {
3873 106 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3874 106 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3875 : break;
3876 32 : n++;
3877 : }
3878 :
3879 : /* Build the metadata DumpableObject */
3880 164 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3881 :
3882 164 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3883 164 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3884 164 : loinfo->dobj.catId.oid = thisoid;
3885 164 : AssignDumpId(&loinfo->dobj);
3886 :
3887 164 : if (n > 1)
3888 16 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3889 16 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3890 : else
3891 148 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3892 164 : loinfo->dobj.name = pg_strdup(namebuf);
3893 164 : loinfo->dacl.acl = pg_strdup(thisacl);
3894 164 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3895 164 : loinfo->dacl.privtype = 0;
3896 164 : loinfo->dacl.initprivs = NULL;
3897 164 : loinfo->rolname = getRoleName(thisowner);
3898 164 : loinfo->numlos = n;
3899 164 : loinfo->looids[0] = thisoid;
3900 : /* Collect OIDs of the remaining blobs in this group */
3901 196 : for (int k = 1; k < n; k++)
3902 : {
3903 : CatalogId extraID;
3904 :
3905 32 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3906 :
3907 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3908 32 : extraID.tableoid = LargeObjectRelationId;
3909 32 : extraID.oid = loinfo->looids[k];
3910 32 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3911 : }
3912 :
3913 : /* LOs have data */
3914 164 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3915 :
3916 : /* Mark whether LO group has a non-empty ACL */
3917 164 : if (!PQgetisnull(res, i, i_lomacl))
3918 74 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3919 :
3920 : /*
3921 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3922 : * as it will be copied by pg_upgrade, which simply copies the
3923 : * pg_largeobject table. We *do* however dump out anything but the
3924 : * data, as pg_upgrade copies just pg_largeobject, but not
3925 : * pg_largeobject_metadata, after the dump is restored.
3926 : */
3927 164 : if (dopt->binary_upgrade)
3928 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3929 :
3930 : /*
3931 : * Create a "BLOBS" data item for the group, too. This is just a
3932 : * placeholder for sorting; it carries no data now.
3933 : */
3934 164 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3935 164 : lodata->objType = DO_LARGE_OBJECT_DATA;
3936 164 : lodata->catId = nilCatalogId;
3937 164 : AssignDumpId(lodata);
3938 164 : lodata->name = pg_strdup(namebuf);
3939 164 : lodata->components |= DUMP_COMPONENT_DATA;
3940 : /* Set up explicit dependency from data to metadata */
3941 164 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3942 164 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3943 164 : lodata->nDeps = lodata->allocDeps = 1;
3944 : }
3945 :
3946 410 : PQclear(res);
3947 410 : destroyPQExpBuffer(loQry);
3948 410 : }
3949 :
3950 : /*
3951 : * dumpLO
3952 : *
3953 : * dump the definition (metadata) of the given large object group
3954 : */
3955 : static void
3956 164 : dumpLO(Archive *fout, const LoInfo *loinfo)
3957 : {
3958 164 : PQExpBuffer cquery = createPQExpBuffer();
3959 :
3960 : /*
3961 : * The "definition" is just a newline-separated list of OIDs. We need to
3962 : * put something into the dropStmt too, but it can just be a comment.
3963 : */
3964 360 : for (int i = 0; i < loinfo->numlos; i++)
3965 196 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3966 :
3967 164 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3968 164 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3969 164 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3970 : .owner = loinfo->rolname,
3971 : .description = "BLOB METADATA",
3972 : .section = SECTION_DATA,
3973 : .createStmt = cquery->data,
3974 : .dropStmt = "-- dummy"));
3975 :
3976 : /*
3977 : * Dump per-blob comments and seclabels if any. We assume these are rare
3978 : * enough that it's okay to generate retail TOC entries for them.
3979 : */
3980 164 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3981 : DUMP_COMPONENT_SECLABEL))
3982 : {
3983 212 : for (int i = 0; i < loinfo->numlos; i++)
3984 : {
3985 : CatalogId catId;
3986 : char namebuf[32];
3987 :
3988 : /* Build identifying info for this blob */
3989 122 : catId.tableoid = loinfo->dobj.catId.tableoid;
3990 122 : catId.oid = loinfo->looids[i];
3991 122 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3992 :
3993 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3994 122 : dumpComment(fout, "LARGE OBJECT", namebuf,
3995 122 : NULL, loinfo->rolname,
3996 122 : catId, 0, loinfo->dobj.dumpId);
3997 :
3998 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3999 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4000 0 : NULL, loinfo->rolname,
4001 0 : catId, 0, loinfo->dobj.dumpId);
4002 : }
4003 : }
4004 :
4005 : /*
4006 : * Dump the ACLs if any (remember that all blobs in the group will have
4007 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4008 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4009 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4010 : * string to emit a mutated version for each blob.
4011 : */
4012 164 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4013 : {
4014 : char namebuf[32];
4015 :
4016 : /* Build identifying info for the first blob */
4017 74 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4018 :
4019 74 : if (loinfo->numlos > 1)
4020 : {
4021 : char tagbuf[64];
4022 :
4023 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4024 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4025 :
4026 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4027 : "LARGE OBJECT", namebuf, NULL, NULL,
4028 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4029 : }
4030 : else
4031 : {
4032 74 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4033 : "LARGE OBJECT", namebuf, NULL, NULL,
4034 74 : NULL, loinfo->rolname, &loinfo->dacl);
4035 : }
4036 : }
4037 :
4038 164 : destroyPQExpBuffer(cquery);
4039 164 : }
4040 :
4041 : /*
4042 : * dumpLOs:
4043 : * dump the data contents of the large objects in the given group
4044 : */
4045 : static int
4046 150 : dumpLOs(Archive *fout, const void *arg)
4047 : {
4048 150 : const LoInfo *loinfo = (const LoInfo *) arg;
4049 150 : PGconn *conn = GetConnection(fout);
4050 : char buf[LOBBUFSIZE];
4051 :
4052 150 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4053 :
4054 328 : for (int i = 0; i < loinfo->numlos; i++)
4055 : {
4056 178 : Oid loOid = loinfo->looids[i];
4057 : int loFd;
4058 : int cnt;
4059 :
4060 : /* Open the LO */
4061 178 : loFd = lo_open(conn, loOid, INV_READ);
4062 178 : if (loFd == -1)
4063 0 : pg_fatal("could not open large object %u: %s",
4064 : loOid, PQerrorMessage(conn));
4065 :
4066 178 : StartLO(fout, loOid);
4067 :
4068 : /* Now read it in chunks, sending data to archive */
4069 : do
4070 : {
4071 274 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4072 274 : if (cnt < 0)
4073 0 : pg_fatal("error reading large object %u: %s",
4074 : loOid, PQerrorMessage(conn));
4075 :
4076 274 : WriteData(fout, buf, cnt);
4077 274 : } while (cnt > 0);
4078 :
4079 178 : lo_close(conn, loFd);
4080 :
4081 178 : EndLO(fout, loOid);
4082 : }
4083 :
4084 150 : return 1;
4085 : }
4086 :
4087 : /*
4088 : * getPolicies
4089 : * get information about all RLS policies on dumpable tables.
4090 : */
4091 : void
4092 468 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4093 : {
4094 468 : DumpOptions *dopt = fout->dopt;
4095 : PQExpBuffer query;
4096 : PQExpBuffer tbloids;
4097 : PGresult *res;
4098 : PolicyInfo *polinfo;
4099 : int i_oid;
4100 : int i_tableoid;
4101 : int i_polrelid;
4102 : int i_polname;
4103 : int i_polcmd;
4104 : int i_polpermissive;
4105 : int i_polroles;
4106 : int i_polqual;
4107 : int i_polwithcheck;
4108 : int i,
4109 : j,
4110 : ntups;
4111 :
4112 : /* No policies before 9.5 */
4113 468 : if (fout->remoteVersion < 90500)
4114 0 : return;
4115 :
4116 : /* Skip if --no-policies was specified */
4117 468 : if (dopt->no_policies)
4118 2 : return;
4119 :
4120 466 : query = createPQExpBuffer();
4121 466 : tbloids = createPQExpBuffer();
4122 :
4123 : /*
4124 : * Identify tables of interest, and check which ones have RLS enabled.
4125 : */
4126 466 : appendPQExpBufferChar(tbloids, '{');
4127 123134 : for (i = 0; i < numTables; i++)
4128 : {
4129 122668 : TableInfo *tbinfo = &tblinfo[i];
4130 :
4131 : /* Ignore row security on tables not to be dumped */
4132 122668 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4133 103500 : continue;
4134 :
4135 : /* It can't have RLS or policies if it's not a table */
4136 19168 : if (tbinfo->relkind != RELKIND_RELATION &&
4137 5200 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4138 3554 : continue;
4139 :
4140 : /* Add it to the list of table OIDs to be probed below */
4141 15614 : if (tbloids->len > 1) /* do we have more than the '{'? */
4142 15302 : appendPQExpBufferChar(tbloids, ',');
4143 15614 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4144 :
4145 : /* Is RLS enabled? (That's separate from whether it has policies) */
4146 15614 : if (tbinfo->rowsec)
4147 : {
4148 132 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4149 :
4150 : /*
4151 : * We represent RLS being enabled on a table by creating a
4152 : * PolicyInfo object with null polname.
4153 : *
4154 : * Note: use tableoid 0 so that this object won't be mistaken for
4155 : * something that pg_depend entries apply to.
4156 : */
4157 132 : polinfo = pg_malloc(sizeof(PolicyInfo));
4158 132 : polinfo->dobj.objType = DO_POLICY;
4159 132 : polinfo->dobj.catId.tableoid = 0;
4160 132 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4161 132 : AssignDumpId(&polinfo->dobj);
4162 132 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4163 132 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4164 132 : polinfo->poltable = tbinfo;
4165 132 : polinfo->polname = NULL;
4166 132 : polinfo->polcmd = '\0';
4167 132 : polinfo->polpermissive = 0;
4168 132 : polinfo->polroles = NULL;
4169 132 : polinfo->polqual = NULL;
4170 132 : polinfo->polwithcheck = NULL;
4171 : }
4172 : }
4173 466 : appendPQExpBufferChar(tbloids, '}');
4174 :
4175 : /*
4176 : * Now, read all RLS policies belonging to the tables of interest, and
4177 : * create PolicyInfo objects for them. (Note that we must filter the
4178 : * results server-side not locally, because we dare not apply pg_get_expr
4179 : * to tables we don't have lock on.)
4180 : */
4181 466 : pg_log_info("reading row-level security policies");
4182 :
4183 466 : printfPQExpBuffer(query,
4184 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4185 466 : if (fout->remoteVersion >= 100000)
4186 466 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4187 : else
4188 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4189 466 : appendPQExpBuffer(query,
4190 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4191 : " 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, "
4192 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4193 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4194 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4195 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4196 : tbloids->data);
4197 :
4198 466 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4199 :
4200 466 : ntups = PQntuples(res);
4201 466 : if (ntups > 0)
4202 : {
4203 100 : i_oid = PQfnumber(res, "oid");
4204 100 : i_tableoid = PQfnumber(res, "tableoid");
4205 100 : i_polrelid = PQfnumber(res, "polrelid");
4206 100 : i_polname = PQfnumber(res, "polname");
4207 100 : i_polcmd = PQfnumber(res, "polcmd");
4208 100 : i_polpermissive = PQfnumber(res, "polpermissive");
4209 100 : i_polroles = PQfnumber(res, "polroles");
4210 100 : i_polqual = PQfnumber(res, "polqual");
4211 100 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4212 :
4213 100 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4214 :
4215 748 : for (j = 0; j < ntups; j++)
4216 : {
4217 648 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4218 648 : TableInfo *tbinfo = findTableByOid(polrelid);
4219 :
4220 648 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4221 :
4222 648 : polinfo[j].dobj.objType = DO_POLICY;
4223 648 : polinfo[j].dobj.catId.tableoid =
4224 648 : atooid(PQgetvalue(res, j, i_tableoid));
4225 648 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4226 648 : AssignDumpId(&polinfo[j].dobj);
4227 648 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4228 648 : polinfo[j].poltable = tbinfo;
4229 648 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4230 648 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4231 :
4232 648 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4233 648 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4234 :
4235 648 : if (PQgetisnull(res, j, i_polroles))
4236 312 : polinfo[j].polroles = NULL;
4237 : else
4238 336 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4239 :
4240 648 : if (PQgetisnull(res, j, i_polqual))
4241 84 : polinfo[j].polqual = NULL;
4242 : else
4243 564 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4244 :
4245 648 : if (PQgetisnull(res, j, i_polwithcheck))
4246 348 : polinfo[j].polwithcheck = NULL;
4247 : else
4248 300 : polinfo[j].polwithcheck
4249 300 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4250 : }
4251 : }
4252 :
4253 466 : PQclear(res);
4254 :
4255 466 : destroyPQExpBuffer(query);
4256 466 : destroyPQExpBuffer(tbloids);
4257 : }
4258 :
4259 : /*
4260 : * dumpPolicy
4261 : * dump the definition of the given policy
4262 : */
4263 : static void
4264 780 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4265 : {
4266 780 : DumpOptions *dopt = fout->dopt;
4267 780 : TableInfo *tbinfo = polinfo->poltable;
4268 : PQExpBuffer query;
4269 : PQExpBuffer delqry;
4270 : PQExpBuffer polprefix;
4271 : char *qtabname;
4272 : const char *cmd;
4273 : char *tag;
4274 :
4275 : /* Do nothing if not dumping schema */
4276 780 : if (!dopt->dumpSchema)
4277 98 : return;
4278 :
4279 : /*
4280 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4281 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4282 : * ROW LEVEL SECURITY.
4283 : */
4284 682 : if (polinfo->polname == NULL)
4285 : {
4286 118 : query = createPQExpBuffer();
4287 :
4288 118 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4289 118 : fmtQualifiedDumpable(tbinfo));
4290 :
4291 : /*
4292 : * We must emit the ROW SECURITY object's dependency on its table
4293 : * explicitly, because it will not match anything in pg_depend (unlike
4294 : * the case for other PolicyInfo objects).
4295 : */
4296 118 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4297 118 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4298 118 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4299 : .namespace = polinfo->dobj.namespace->dobj.name,
4300 : .owner = tbinfo->rolname,
4301 : .description = "ROW SECURITY",
4302 : .section = SECTION_POST_DATA,
4303 : .createStmt = query->data,
4304 : .deps = &(tbinfo->dobj.dumpId),
4305 : .nDeps = 1));
4306 :
4307 118 : destroyPQExpBuffer(query);
4308 118 : return;
4309 : }
4310 :
4311 564 : if (polinfo->polcmd == '*')
4312 188 : cmd = "";
4313 376 : else if (polinfo->polcmd == 'r')
4314 102 : cmd = " FOR SELECT";
4315 274 : else if (polinfo->polcmd == 'a')
4316 70 : cmd = " FOR INSERT";
4317 204 : else if (polinfo->polcmd == 'w')
4318 102 : cmd = " FOR UPDATE";
4319 102 : else if (polinfo->polcmd == 'd')
4320 102 : cmd = " FOR DELETE";
4321 : else
4322 0 : pg_fatal("unexpected policy command type: %c",
4323 : polinfo->polcmd);
4324 :
4325 564 : query = createPQExpBuffer();
4326 564 : delqry = createPQExpBuffer();
4327 564 : polprefix = createPQExpBuffer();
4328 :
4329 564 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4330 :
4331 564 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4332 :
4333 564 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4334 564 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4335 :
4336 564 : if (polinfo->polroles != NULL)
4337 280 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4338 :
4339 564 : if (polinfo->polqual != NULL)
4340 494 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4341 :
4342 564 : if (polinfo->polwithcheck != NULL)
4343 258 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4344 :
4345 564 : appendPQExpBufferStr(query, ";\n");
4346 :
4347 564 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4348 564 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4349 :
4350 564 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4351 564 : fmtId(polinfo->polname));
4352 :
4353 564 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4354 :
4355 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4356 564 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4357 564 : ARCHIVE_OPTS(.tag = tag,
4358 : .namespace = polinfo->dobj.namespace->dobj.name,
4359 : .owner = tbinfo->rolname,
4360 : .description = "POLICY",
4361 : .section = SECTION_POST_DATA,
4362 : .createStmt = query->data,
4363 : .dropStmt = delqry->data));
4364 :
4365 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4366 0 : dumpComment(fout, polprefix->data, qtabname,
4367 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4368 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4369 :
4370 564 : free(tag);
4371 564 : destroyPQExpBuffer(query);
4372 564 : destroyPQExpBuffer(delqry);
4373 564 : destroyPQExpBuffer(polprefix);
4374 564 : free(qtabname);
4375 : }
4376 :
4377 : /*
4378 : * getPublications
4379 : * get information about publications
4380 : */
4381 : void
4382 468 : getPublications(Archive *fout)
4383 : {
4384 468 : DumpOptions *dopt = fout->dopt;
4385 : PQExpBuffer query;
4386 : PGresult *res;
4387 : PublicationInfo *pubinfo;
4388 : int i_tableoid;
4389 : int i_oid;
4390 : int i_pubname;
4391 : int i_pubowner;
4392 : int i_puballtables;
4393 : int i_pubinsert;
4394 : int i_pubupdate;
4395 : int i_pubdelete;
4396 : int i_pubtruncate;
4397 : int i_pubviaroot;
4398 : int i_pubgencols;
4399 : int i,
4400 : ntups;
4401 :
4402 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4403 0 : return;
4404 :
4405 468 : query = createPQExpBuffer();
4406 :
4407 : /* Get the publications. */
4408 468 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4409 : "p.pubowner, p.puballtables, p.pubinsert, "
4410 : "p.pubupdate, p.pubdelete, ");
4411 :
4412 468 : if (fout->remoteVersion >= 110000)
4413 468 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4414 : else
4415 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4416 :
4417 468 : if (fout->remoteVersion >= 130000)
4418 468 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4419 : else
4420 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4421 :
4422 468 : if (fout->remoteVersion >= 180000)
4423 468 : appendPQExpBufferStr(query, "p.pubgencols ");
4424 : else
4425 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4426 :
4427 468 : appendPQExpBufferStr(query, "FROM pg_publication p");
4428 :
4429 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4430 :
4431 468 : ntups = PQntuples(res);
4432 :
4433 468 : if (ntups == 0)
4434 368 : goto cleanup;
4435 :
4436 100 : i_tableoid = PQfnumber(res, "tableoid");
4437 100 : i_oid = PQfnumber(res, "oid");
4438 100 : i_pubname = PQfnumber(res, "pubname");
4439 100 : i_pubowner = PQfnumber(res, "pubowner");
4440 100 : i_puballtables = PQfnumber(res, "puballtables");
4441 100 : i_pubinsert = PQfnumber(res, "pubinsert");
4442 100 : i_pubupdate = PQfnumber(res, "pubupdate");
4443 100 : i_pubdelete = PQfnumber(res, "pubdelete");
4444 100 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4445 100 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4446 100 : i_pubgencols = PQfnumber(res, "pubgencols");
4447 :
4448 100 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4449 :
4450 592 : for (i = 0; i < ntups; i++)
4451 : {
4452 492 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4453 492 : pubinfo[i].dobj.catId.tableoid =
4454 492 : atooid(PQgetvalue(res, i, i_tableoid));
4455 492 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4456 492 : AssignDumpId(&pubinfo[i].dobj);
4457 492 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4458 492 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4459 492 : pubinfo[i].puballtables =
4460 492 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4461 492 : pubinfo[i].pubinsert =
4462 492 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4463 492 : pubinfo[i].pubupdate =
4464 492 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4465 492 : pubinfo[i].pubdelete =
4466 492 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4467 492 : pubinfo[i].pubtruncate =
4468 492 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4469 492 : pubinfo[i].pubviaroot =
4470 492 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4471 492 : pubinfo[i].pubgencols_type =
4472 492 : *(PQgetvalue(res, i, i_pubgencols));
4473 :
4474 : /* Decide whether we want to dump it */
4475 492 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4476 : }
4477 :
4478 100 : cleanup:
4479 468 : PQclear(res);
4480 :
4481 468 : destroyPQExpBuffer(query);
4482 : }
4483 :
4484 : /*
4485 : * dumpPublication
4486 : * dump the definition of the given publication
4487 : */
4488 : static void
4489 412 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4490 : {
4491 412 : DumpOptions *dopt = fout->dopt;
4492 : PQExpBuffer delq;
4493 : PQExpBuffer query;
4494 : char *qpubname;
4495 412 : bool first = true;
4496 :
4497 : /* Do nothing if not dumping schema */
4498 412 : if (!dopt->dumpSchema)
4499 60 : return;
4500 :
4501 352 : delq = createPQExpBuffer();
4502 352 : query = createPQExpBuffer();
4503 :
4504 352 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4505 :
4506 352 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4507 : qpubname);
4508 :
4509 352 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4510 : qpubname);
4511 :
4512 352 : if (pubinfo->puballtables)
4513 72 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4514 :
4515 352 : appendPQExpBufferStr(query, " WITH (publish = '");
4516 352 : if (pubinfo->pubinsert)
4517 : {
4518 282 : appendPQExpBufferStr(query, "insert");
4519 282 : first = false;
4520 : }
4521 :
4522 352 : if (pubinfo->pubupdate)
4523 : {
4524 282 : if (!first)
4525 282 : appendPQExpBufferStr(query, ", ");
4526 :
4527 282 : appendPQExpBufferStr(query, "update");
4528 282 : first = false;
4529 : }
4530 :
4531 352 : if (pubinfo->pubdelete)
4532 : {
4533 282 : if (!first)
4534 282 : appendPQExpBufferStr(query, ", ");
4535 :
4536 282 : appendPQExpBufferStr(query, "delete");
4537 282 : first = false;
4538 : }
4539 :
4540 352 : if (pubinfo->pubtruncate)
4541 : {
4542 282 : if (!first)
4543 282 : appendPQExpBufferStr(query, ", ");
4544 :
4545 282 : appendPQExpBufferStr(query, "truncate");
4546 282 : first = false;
4547 : }
4548 :
4549 352 : appendPQExpBufferChar(query, '\'');
4550 :
4551 352 : if (pubinfo->pubviaroot)
4552 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4553 :
4554 352 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4555 70 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4556 :
4557 352 : appendPQExpBufferStr(query, ");\n");
4558 :
4559 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4560 352 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4561 352 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4562 : .owner = pubinfo->rolname,
4563 : .description = "PUBLICATION",
4564 : .section = SECTION_POST_DATA,
4565 : .createStmt = query->data,
4566 : .dropStmt = delq->data));
4567 :
4568 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4569 70 : dumpComment(fout, "PUBLICATION", qpubname,
4570 70 : NULL, pubinfo->rolname,
4571 70 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4572 :
4573 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4574 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4575 0 : NULL, pubinfo->rolname,
4576 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4577 :
4578 352 : destroyPQExpBuffer(delq);
4579 352 : destroyPQExpBuffer(query);
4580 352 : free(qpubname);
4581 : }
4582 :
4583 : /*
4584 : * getPublicationNamespaces
4585 : * get information about publication membership for dumpable schemas.
4586 : */
4587 : void
4588 468 : getPublicationNamespaces(Archive *fout)
4589 : {
4590 : PQExpBuffer query;
4591 : PGresult *res;
4592 : PublicationSchemaInfo *pubsinfo;
4593 468 : DumpOptions *dopt = fout->dopt;
4594 : int i_tableoid;
4595 : int i_oid;
4596 : int i_pnpubid;
4597 : int i_pnnspid;
4598 : int i,
4599 : j,
4600 : ntups;
4601 :
4602 468 : if (dopt->no_publications || fout->remoteVersion < 150000)
4603 0 : return;
4604 :
4605 468 : query = createPQExpBuffer();
4606 :
4607 : /* Collect all publication membership info. */
4608 468 : appendPQExpBufferStr(query,
4609 : "SELECT tableoid, oid, pnpubid, pnnspid "
4610 : "FROM pg_catalog.pg_publication_namespace");
4611 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4612 :
4613 468 : ntups = PQntuples(res);
4614 :
4615 468 : i_tableoid = PQfnumber(res, "tableoid");
4616 468 : i_oid = PQfnumber(res, "oid");
4617 468 : i_pnpubid = PQfnumber(res, "pnpubid");
4618 468 : i_pnnspid = PQfnumber(res, "pnnspid");
4619 :
4620 : /* this allocation may be more than we need */
4621 468 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4622 468 : j = 0;
4623 :
4624 664 : for (i = 0; i < ntups; i++)
4625 : {
4626 196 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4627 196 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4628 : PublicationInfo *pubinfo;
4629 : NamespaceInfo *nspinfo;
4630 :
4631 : /*
4632 : * Ignore any entries for which we aren't interested in either the
4633 : * publication or the rel.
4634 : */
4635 196 : pubinfo = findPublicationByOid(pnpubid);
4636 196 : if (pubinfo == NULL)
4637 0 : continue;
4638 196 : nspinfo = findNamespaceByOid(pnnspid);
4639 196 : if (nspinfo == NULL)
4640 0 : continue;
4641 :
4642 : /* OK, make a DumpableObject for this relationship */
4643 196 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4644 196 : pubsinfo[j].dobj.catId.tableoid =
4645 196 : atooid(PQgetvalue(res, i, i_tableoid));
4646 196 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4647 196 : AssignDumpId(&pubsinfo[j].dobj);
4648 196 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4649 196 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4650 196 : pubsinfo[j].publication = pubinfo;
4651 196 : pubsinfo[j].pubschema = nspinfo;
4652 :
4653 : /* Decide whether we want to dump it */
4654 196 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4655 :
4656 196 : j++;
4657 : }
4658 :
4659 468 : PQclear(res);
4660 468 : destroyPQExpBuffer(query);
4661 : }
4662 :
4663 : /*
4664 : * getPublicationTables
4665 : * get information about publication membership for dumpable tables.
4666 : */
4667 : void
4668 468 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4669 : {
4670 : PQExpBuffer query;
4671 : PGresult *res;
4672 : PublicationRelInfo *pubrinfo;
4673 468 : DumpOptions *dopt = fout->dopt;
4674 : int i_tableoid;
4675 : int i_oid;
4676 : int i_prpubid;
4677 : int i_prrelid;
4678 : int i_prrelqual;
4679 : int i_prattrs;
4680 : int i,
4681 : j,
4682 : ntups;
4683 :
4684 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4685 0 : return;
4686 :
4687 468 : query = createPQExpBuffer();
4688 :
4689 : /* Collect all publication membership info. */
4690 468 : if (fout->remoteVersion >= 150000)
4691 468 : appendPQExpBufferStr(query,
4692 : "SELECT tableoid, oid, prpubid, prrelid, "
4693 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4694 : "(CASE\n"
4695 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4696 : " (SELECT array_agg(attname)\n"
4697 : " FROM\n"
4698 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4699 : " pg_catalog.pg_attribute\n"
4700 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4701 : " ELSE NULL END) prattrs "
4702 : "FROM pg_catalog.pg_publication_rel pr");
4703 : else
4704 0 : appendPQExpBufferStr(query,
4705 : "SELECT tableoid, oid, prpubid, prrelid, "
4706 : "NULL AS prrelqual, NULL AS prattrs "
4707 : "FROM pg_catalog.pg_publication_rel");
4708 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4709 :
4710 468 : ntups = PQntuples(res);
4711 :
4712 468 : i_tableoid = PQfnumber(res, "tableoid");
4713 468 : i_oid = PQfnumber(res, "oid");
4714 468 : i_prpubid = PQfnumber(res, "prpubid");
4715 468 : i_prrelid = PQfnumber(res, "prrelid");
4716 468 : i_prrelqual = PQfnumber(res, "prrelqual");
4717 468 : i_prattrs = PQfnumber(res, "prattrs");
4718 :
4719 : /* this allocation may be more than we need */
4720 468 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4721 468 : j = 0;
4722 :
4723 1154 : for (i = 0; i < ntups; i++)
4724 : {
4725 686 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4726 686 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4727 : PublicationInfo *pubinfo;
4728 : TableInfo *tbinfo;
4729 :
4730 : /*
4731 : * Ignore any entries for which we aren't interested in either the
4732 : * publication or the rel.
4733 : */
4734 686 : pubinfo = findPublicationByOid(prpubid);
4735 686 : if (pubinfo == NULL)
4736 0 : continue;
4737 686 : tbinfo = findTableByOid(prrelid);
4738 686 : if (tbinfo == NULL)
4739 0 : continue;
4740 :
4741 : /* OK, make a DumpableObject for this relationship */
4742 686 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4743 686 : pubrinfo[j].dobj.catId.tableoid =
4744 686 : atooid(PQgetvalue(res, i, i_tableoid));
4745 686 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4746 686 : AssignDumpId(&pubrinfo[j].dobj);
4747 686 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4748 686 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4749 686 : pubrinfo[j].publication = pubinfo;
4750 686 : pubrinfo[j].pubtable = tbinfo;
4751 686 : if (PQgetisnull(res, i, i_prrelqual))
4752 392 : pubrinfo[j].pubrelqual = NULL;
4753 : else
4754 294 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4755 :
4756 686 : if (!PQgetisnull(res, i, i_prattrs))
4757 : {
4758 : char **attnames;
4759 : int nattnames;
4760 : PQExpBuffer attribs;
4761 :
4762 196 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4763 : &attnames, &nattnames))
4764 0 : pg_fatal("could not parse %s array", "prattrs");
4765 196 : attribs = createPQExpBuffer();
4766 588 : for (int k = 0; k < nattnames; k++)
4767 : {
4768 392 : if (k > 0)
4769 196 : appendPQExpBufferStr(attribs, ", ");
4770 :
4771 392 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4772 : }
4773 196 : pubrinfo[j].pubrattrs = attribs->data;
4774 196 : free(attribs); /* but not attribs->data */
4775 196 : free(attnames);
4776 : }
4777 : else
4778 490 : pubrinfo[j].pubrattrs = NULL;
4779 :
4780 : /* Decide whether we want to dump it */
4781 686 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4782 :
4783 686 : j++;
4784 : }
4785 :
4786 468 : PQclear(res);
4787 468 : destroyPQExpBuffer(query);
4788 : }
4789 :
4790 : /*
4791 : * dumpPublicationNamespace
4792 : * dump the definition of the given publication schema mapping.
4793 : */
4794 : static void
4795 164 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4796 : {
4797 164 : DumpOptions *dopt = fout->dopt;
4798 164 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4799 164 : PublicationInfo *pubinfo = pubsinfo->publication;
4800 : PQExpBuffer query;
4801 : char *tag;
4802 :
4803 : /* Do nothing if not dumping schema */
4804 164 : if (!dopt->dumpSchema)
4805 24 : return;
4806 :
4807 140 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4808 :
4809 140 : query = createPQExpBuffer();
4810 :
4811 140 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4812 140 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4813 :
4814 : /*
4815 : * There is no point in creating drop query as the drop is done by schema
4816 : * drop.
4817 : */
4818 140 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4819 140 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4820 140 : ARCHIVE_OPTS(.tag = tag,
4821 : .namespace = schemainfo->dobj.name,
4822 : .owner = pubinfo->rolname,
4823 : .description = "PUBLICATION TABLES IN SCHEMA",
4824 : .section = SECTION_POST_DATA,
4825 : .createStmt = query->data));
4826 :
4827 : /* These objects can't currently have comments or seclabels */
4828 :
4829 140 : free(tag);
4830 140 : destroyPQExpBuffer(query);
4831 : }
4832 :
4833 : /*
4834 : * dumpPublicationTable
4835 : * dump the definition of the given publication table mapping
4836 : */
4837 : static void
4838 574 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4839 : {
4840 574 : DumpOptions *dopt = fout->dopt;
4841 574 : PublicationInfo *pubinfo = pubrinfo->publication;
4842 574 : TableInfo *tbinfo = pubrinfo->pubtable;
4843 : PQExpBuffer query;
4844 : char *tag;
4845 :
4846 : /* Do nothing if not dumping schema */
4847 574 : if (!dopt->dumpSchema)
4848 84 : return;
4849 :
4850 490 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4851 :
4852 490 : query = createPQExpBuffer();
4853 :
4854 490 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4855 490 : fmtId(pubinfo->dobj.name));
4856 490 : appendPQExpBuffer(query, " %s",
4857 490 : fmtQualifiedDumpable(tbinfo));
4858 :
4859 490 : if (pubrinfo->pubrattrs)
4860 140 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4861 :
4862 490 : if (pubrinfo->pubrelqual)
4863 : {
4864 : /*
4865 : * It's necessary to add parentheses around the expression because
4866 : * pg_get_expr won't supply the parentheses for things like WHERE
4867 : * TRUE.
4868 : */
4869 210 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4870 : }
4871 490 : appendPQExpBufferStr(query, ";\n");
4872 :
4873 : /*
4874 : * There is no point in creating a drop query as the drop is done by table
4875 : * drop. (If you think to change this, see also _printTocEntry().)
4876 : * Although this object doesn't really have ownership as such, set the
4877 : * owner field anyway to ensure that the command is run by the correct
4878 : * role at restore time.
4879 : */
4880 490 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4881 490 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4882 490 : ARCHIVE_OPTS(.tag = tag,
4883 : .namespace = tbinfo->dobj.namespace->dobj.name,
4884 : .owner = pubinfo->rolname,
4885 : .description = "PUBLICATION TABLE",
4886 : .section = SECTION_POST_DATA,
4887 : .createStmt = query->data));
4888 :
4889 : /* These objects can't currently have comments or seclabels */
4890 :
4891 490 : free(tag);
4892 490 : destroyPQExpBuffer(query);
4893 : }
4894 :
4895 : /*
4896 : * Is the currently connected user a superuser?
4897 : */
4898 : static bool
4899 468 : is_superuser(Archive *fout)
4900 : {
4901 468 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4902 : const char *val;
4903 :
4904 468 : val = PQparameterStatus(AH->connection, "is_superuser");
4905 :
4906 468 : if (val && strcmp(val, "on") == 0)
4907 462 : return true;
4908 :
4909 6 : return false;
4910 : }
4911 :
4912 : /*
4913 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4914 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4915 : * the setting query is effective only where available.
4916 : */
4917 : static void
4918 540 : set_restrict_relation_kind(Archive *AH, const char *value)
4919 : {
4920 540 : PQExpBuffer query = createPQExpBuffer();
4921 : PGresult *res;
4922 :
4923 540 : appendPQExpBuffer(query,
4924 : "SELECT set_config(name, '%s', false) "
4925 : "FROM pg_settings "
4926 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4927 : value);
4928 540 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4929 :
4930 540 : PQclear(res);
4931 540 : destroyPQExpBuffer(query);
4932 540 : }
4933 :
4934 : /*
4935 : * getSubscriptions
4936 : * get information about subscriptions
4937 : */
4938 : void
4939 468 : getSubscriptions(Archive *fout)
4940 : {
4941 468 : DumpOptions *dopt = fout->dopt;
4942 : PQExpBuffer query;
4943 : PGresult *res;
4944 : SubscriptionInfo *subinfo;
4945 : int i_tableoid;
4946 : int i_oid;
4947 : int i_subname;
4948 : int i_subowner;
4949 : int i_subbinary;
4950 : int i_substream;
4951 : int i_subtwophasestate;
4952 : int i_subdisableonerr;
4953 : int i_subpasswordrequired;
4954 : int i_subrunasowner;
4955 : int i_subconninfo;
4956 : int i_subslotname;
4957 : int i_subsynccommit;
4958 : int i_subpublications;
4959 : int i_suborigin;
4960 : int i_suboriginremotelsn;
4961 : int i_subenabled;
4962 : int i_subfailover;
4963 : int i,
4964 : ntups;
4965 :
4966 468 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4967 0 : return;
4968 :
4969 468 : if (!is_superuser(fout))
4970 : {
4971 : int n;
4972 :
4973 6 : res = ExecuteSqlQuery(fout,
4974 : "SELECT count(*) FROM pg_subscription "
4975 : "WHERE subdbid = (SELECT oid FROM pg_database"
4976 : " WHERE datname = current_database())",
4977 : PGRES_TUPLES_OK);
4978 6 : n = atoi(PQgetvalue(res, 0, 0));
4979 6 : if (n > 0)
4980 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4981 6 : PQclear(res);
4982 6 : return;
4983 : }
4984 :
4985 462 : query = createPQExpBuffer();
4986 :
4987 : /* Get the subscriptions in current database. */
4988 462 : appendPQExpBufferStr(query,
4989 : "SELECT s.tableoid, s.oid, s.subname,\n"
4990 : " s.subowner,\n"
4991 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4992 : " s.subpublications,\n");
4993 :
4994 462 : if (fout->remoteVersion >= 140000)
4995 462 : appendPQExpBufferStr(query, " s.subbinary,\n");
4996 : else
4997 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4998 :
4999 462 : if (fout->remoteVersion >= 140000)
5000 462 : appendPQExpBufferStr(query, " s.substream,\n");
5001 : else
5002 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5003 :
5004 462 : if (fout->remoteVersion >= 150000)
5005 462 : appendPQExpBufferStr(query,
5006 : " s.subtwophasestate,\n"
5007 : " s.subdisableonerr,\n");
5008 : else
5009 0 : appendPQExpBuffer(query,
5010 : " '%c' AS subtwophasestate,\n"
5011 : " false AS subdisableonerr,\n",
5012 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5013 :
5014 462 : if (fout->remoteVersion >= 160000)
5015 462 : appendPQExpBufferStr(query,
5016 : " s.subpasswordrequired,\n"
5017 : " s.subrunasowner,\n"
5018 : " s.suborigin,\n");
5019 : else
5020 0 : appendPQExpBuffer(query,
5021 : " 't' AS subpasswordrequired,\n"
5022 : " 't' AS subrunasowner,\n"
5023 : " '%s' AS suborigin,\n",
5024 : LOGICALREP_ORIGIN_ANY);
5025 :
5026 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5027 62 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5028 : " s.subenabled,\n");
5029 : else
5030 400 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5031 : " false AS subenabled,\n");
5032 :
5033 462 : if (fout->remoteVersion >= 170000)
5034 462 : appendPQExpBufferStr(query,
5035 : " s.subfailover\n");
5036 : else
5037 0 : appendPQExpBufferStr(query,
5038 : " false AS subfailover\n");
5039 :
5040 462 : appendPQExpBufferStr(query,
5041 : "FROM pg_subscription s\n");
5042 :
5043 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5044 62 : appendPQExpBufferStr(query,
5045 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5046 : " ON o.external_id = 'pg_' || s.oid::text \n");
5047 :
5048 462 : appendPQExpBufferStr(query,
5049 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5050 : " WHERE datname = current_database())");
5051 :
5052 462 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5053 :
5054 462 : ntups = PQntuples(res);
5055 :
5056 : /*
5057 : * Get subscription fields. We don't include subskiplsn in the dump as
5058 : * after restoring the dump this value may no longer be relevant.
5059 : */
5060 462 : i_tableoid = PQfnumber(res, "tableoid");
5061 462 : i_oid = PQfnumber(res, "oid");
5062 462 : i_subname = PQfnumber(res, "subname");
5063 462 : i_subowner = PQfnumber(res, "subowner");
5064 462 : i_subenabled = PQfnumber(res, "subenabled");
5065 462 : i_subbinary = PQfnumber(res, "subbinary");
5066 462 : i_substream = PQfnumber(res, "substream");
5067 462 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5068 462 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5069 462 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5070 462 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5071 462 : i_subfailover = PQfnumber(res, "subfailover");
5072 462 : i_subconninfo = PQfnumber(res, "subconninfo");
5073 462 : i_subslotname = PQfnumber(res, "subslotname");
5074 462 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5075 462 : i_subpublications = PQfnumber(res, "subpublications");
5076 462 : i_suborigin = PQfnumber(res, "suborigin");
5077 462 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5078 :
5079 462 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5080 :
5081 748 : for (i = 0; i < ntups; i++)
5082 : {
5083 286 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5084 286 : subinfo[i].dobj.catId.tableoid =
5085 286 : atooid(PQgetvalue(res, i, i_tableoid));
5086 286 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5087 286 : AssignDumpId(&subinfo[i].dobj);
5088 286 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5089 286 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5090 :
5091 286 : subinfo[i].subenabled =
5092 286 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5093 286 : subinfo[i].subbinary =
5094 286 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5095 286 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5096 286 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5097 286 : subinfo[i].subdisableonerr =
5098 286 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5099 286 : subinfo[i].subpasswordrequired =
5100 286 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5101 286 : subinfo[i].subrunasowner =
5102 286 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5103 286 : subinfo[i].subfailover =
5104 286 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5105 572 : subinfo[i].subconninfo =
5106 286 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5107 286 : if (PQgetisnull(res, i, i_subslotname))
5108 0 : subinfo[i].subslotname = NULL;
5109 : else
5110 286 : subinfo[i].subslotname =
5111 286 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5112 572 : subinfo[i].subsynccommit =
5113 286 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5114 572 : subinfo[i].subpublications =
5115 286 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5116 286 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5117 286 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5118 284 : subinfo[i].suboriginremotelsn = NULL;
5119 : else
5120 2 : subinfo[i].suboriginremotelsn =
5121 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5122 :
5123 : /* Decide whether we want to dump it */
5124 286 : selectDumpableObject(&(subinfo[i].dobj), fout);
5125 : }
5126 462 : PQclear(res);
5127 :
5128 462 : destroyPQExpBuffer(query);
5129 : }
5130 :
5131 : /*
5132 : * getSubscriptionTables
5133 : * Get information about subscription membership for dumpable tables. This
5134 : * will be used only in binary-upgrade mode for PG17 or later versions.
5135 : */
5136 : void
5137 468 : getSubscriptionTables(Archive *fout)
5138 : {
5139 468 : DumpOptions *dopt = fout->dopt;
5140 468 : SubscriptionInfo *subinfo = NULL;
5141 : SubRelInfo *subrinfo;
5142 : PGresult *res;
5143 : int i_srsubid;
5144 : int i_srrelid;
5145 : int i_srsubstate;
5146 : int i_srsublsn;
5147 : int ntups;
5148 468 : Oid last_srsubid = InvalidOid;
5149 :
5150 468 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5151 62 : fout->remoteVersion < 170000)
5152 406 : return;
5153 :
5154 62 : res = ExecuteSqlQuery(fout,
5155 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5156 : "FROM pg_catalog.pg_subscription_rel "
5157 : "ORDER BY srsubid",
5158 : PGRES_TUPLES_OK);
5159 62 : ntups = PQntuples(res);
5160 62 : if (ntups == 0)
5161 60 : goto cleanup;
5162 :
5163 : /* Get pg_subscription_rel attributes */
5164 2 : i_srsubid = PQfnumber(res, "srsubid");
5165 2 : i_srrelid = PQfnumber(res, "srrelid");
5166 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5167 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5168 :
5169 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5170 6 : for (int i = 0; i < ntups; i++)
5171 : {
5172 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5173 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5174 : TableInfo *tblinfo;
5175 :
5176 : /*
5177 : * If we switched to a new subscription, check if the subscription
5178 : * exists.
5179 : */
5180 4 : if (cur_srsubid != last_srsubid)
5181 : {
5182 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5183 4 : if (subinfo == NULL)
5184 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5185 :
5186 4 : last_srsubid = cur_srsubid;
5187 : }
5188 :
5189 4 : tblinfo = findTableByOid(relid);
5190 4 : if (tblinfo == NULL)
5191 0 : pg_fatal("failed sanity check, table with OID %u not found",
5192 : relid);
5193 :
5194 : /* OK, make a DumpableObject for this relationship */
5195 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5196 4 : subrinfo[i].dobj.catId.tableoid = relid;
5197 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5198 4 : AssignDumpId(&subrinfo[i].dobj);
5199 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5200 4 : subrinfo[i].tblinfo = tblinfo;
5201 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5202 4 : if (PQgetisnull(res, i, i_srsublsn))
5203 2 : subrinfo[i].srsublsn = NULL;
5204 : else
5205 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5206 :
5207 4 : subrinfo[i].subinfo = subinfo;
5208 :
5209 : /* Decide whether we want to dump it */
5210 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5211 : }
5212 :
5213 2 : cleanup:
5214 62 : PQclear(res);
5215 : }
5216 :
5217 : /*
5218 : * dumpSubscriptionTable
5219 : * Dump the definition of the given subscription table mapping. This will be
5220 : * used only in binary-upgrade mode for PG17 or later versions.
5221 : */
5222 : static void
5223 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5224 : {
5225 4 : DumpOptions *dopt = fout->dopt;
5226 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5227 : PQExpBuffer query;
5228 : char *tag;
5229 :
5230 : /* Do nothing if not dumping schema */
5231 4 : if (!dopt->dumpSchema)
5232 0 : return;
5233 :
5234 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5235 :
5236 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5237 :
5238 4 : query = createPQExpBuffer();
5239 :
5240 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5241 : {
5242 : /*
5243 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5244 : * to pg_subscription_rel table. This will be used only in
5245 : * binary-upgrade mode.
5246 : */
5247 4 : appendPQExpBufferStr(query,
5248 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5249 4 : appendPQExpBufferStr(query,
5250 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5251 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5252 4 : appendPQExpBuffer(query,
5253 : ", %u, '%c'",
5254 4 : subrinfo->tblinfo->dobj.catId.oid,
5255 4 : subrinfo->srsubstate);
5256 :
5257 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5258 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5259 : else
5260 2 : appendPQExpBufferStr(query, ", NULL");
5261 :
5262 4 : appendPQExpBufferStr(query, ");\n");
5263 : }
5264 :
5265 : /*
5266 : * There is no point in creating a drop query as the drop is done by table
5267 : * drop. (If you think to change this, see also _printTocEntry().)
5268 : * Although this object doesn't really have ownership as such, set the
5269 : * owner field anyway to ensure that the command is run by the correct
5270 : * role at restore time.
5271 : */
5272 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5273 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5274 4 : ARCHIVE_OPTS(.tag = tag,
5275 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5276 : .owner = subinfo->rolname,
5277 : .description = "SUBSCRIPTION TABLE",
5278 : .section = SECTION_POST_DATA,
5279 : .createStmt = query->data));
5280 :
5281 : /* These objects can't currently have comments or seclabels */
5282 :
5283 4 : free(tag);
5284 4 : destroyPQExpBuffer(query);
5285 : }
5286 :
5287 : /*
5288 : * dumpSubscription
5289 : * dump the definition of the given subscription
5290 : */
5291 : static void
5292 250 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5293 : {
5294 250 : DumpOptions *dopt = fout->dopt;
5295 : PQExpBuffer delq;
5296 : PQExpBuffer query;
5297 : PQExpBuffer publications;
5298 : char *qsubname;
5299 250 : char **pubnames = NULL;
5300 250 : int npubnames = 0;
5301 : int i;
5302 :
5303 : /* Do nothing if not dumping schema */
5304 250 : if (!dopt->dumpSchema)
5305 36 : return;
5306 :
5307 214 : delq = createPQExpBuffer();
5308 214 : query = createPQExpBuffer();
5309 :
5310 214 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5311 :
5312 214 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5313 : qsubname);
5314 :
5315 214 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5316 : qsubname);
5317 214 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5318 :
5319 : /* Build list of quoted publications and append them to query. */
5320 214 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5321 0 : pg_fatal("could not parse %s array", "subpublications");
5322 :
5323 214 : publications = createPQExpBuffer();
5324 428 : for (i = 0; i < npubnames; i++)
5325 : {
5326 214 : if (i > 0)
5327 0 : appendPQExpBufferStr(publications, ", ");
5328 :
5329 214 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5330 : }
5331 :
5332 214 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5333 214 : if (subinfo->subslotname)
5334 214 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5335 : else
5336 0 : appendPQExpBufferStr(query, "NONE");
5337 :
5338 214 : if (subinfo->subbinary)
5339 0 : appendPQExpBufferStr(query, ", binary = true");
5340 :
5341 214 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5342 70 : appendPQExpBufferStr(query, ", streaming = on");
5343 144 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5344 74 : appendPQExpBufferStr(query, ", streaming = parallel");
5345 : else
5346 70 : appendPQExpBufferStr(query, ", streaming = off");
5347 :
5348 214 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5349 0 : appendPQExpBufferStr(query, ", two_phase = on");
5350 :
5351 214 : if (subinfo->subdisableonerr)
5352 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5353 :
5354 214 : if (!subinfo->subpasswordrequired)
5355 0 : appendPQExpBufferStr(query, ", password_required = false");
5356 :
5357 214 : if (subinfo->subrunasowner)
5358 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5359 :
5360 214 : if (subinfo->subfailover)
5361 2 : appendPQExpBufferStr(query, ", failover = true");
5362 :
5363 214 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5364 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5365 :
5366 214 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5367 70 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5368 :
5369 214 : appendPQExpBufferStr(query, ");\n");
5370 :
5371 : /*
5372 : * In binary-upgrade mode, we allow the replication to continue after the
5373 : * upgrade.
5374 : */
5375 214 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5376 : {
5377 10 : if (subinfo->suboriginremotelsn)
5378 : {
5379 : /*
5380 : * Preserve the remote_lsn for the subscriber's replication
5381 : * origin. This value is required to start the replication from
5382 : * the position before the upgrade. This value will be stale if
5383 : * the publisher gets upgraded before the subscriber node.
5384 : * However, this shouldn't be a problem as the upgrade of the
5385 : * publisher ensures that all the transactions were replicated
5386 : * before upgrading it.
5387 : */
5388 2 : appendPQExpBufferStr(query,
5389 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5390 2 : appendPQExpBufferStr(query,
5391 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5392 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5393 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5394 : }
5395 :
5396 10 : if (subinfo->subenabled)
5397 : {
5398 : /*
5399 : * Enable the subscription to allow the replication to continue
5400 : * after the upgrade.
5401 : */
5402 2 : appendPQExpBufferStr(query,
5403 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5404 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5405 : }
5406 : }
5407 :
5408 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5409 214 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5410 214 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5411 : .owner = subinfo->rolname,
5412 : .description = "SUBSCRIPTION",
5413 : .section = SECTION_POST_DATA,
5414 : .createStmt = query->data,
5415 : .dropStmt = delq->data));
5416 :
5417 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5418 70 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5419 70 : NULL, subinfo->rolname,
5420 70 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5421 :
5422 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5423 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5424 0 : NULL, subinfo->rolname,
5425 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5426 :
5427 214 : destroyPQExpBuffer(publications);
5428 214 : free(pubnames);
5429 :
5430 214 : destroyPQExpBuffer(delq);
5431 214 : destroyPQExpBuffer(query);
5432 214 : free(qsubname);
5433 : }
5434 :
5435 : /*
5436 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5437 : * the object needs.
5438 : */
5439 : static void
5440 14218 : append_depends_on_extension(Archive *fout,
5441 : PQExpBuffer create,
5442 : const DumpableObject *dobj,
5443 : const char *catalog,
5444 : const char *keyword,
5445 : const char *objname)
5446 : {
5447 14218 : if (dobj->depends_on_ext)
5448 : {
5449 : char *nm;
5450 : PGresult *res;
5451 : PQExpBuffer query;
5452 : int ntups;
5453 : int i_extname;
5454 : int i;
5455 :
5456 : /* dodge fmtId() non-reentrancy */
5457 84 : nm = pg_strdup(objname);
5458 :
5459 84 : query = createPQExpBuffer();
5460 84 : appendPQExpBuffer(query,
5461 : "SELECT e.extname "
5462 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5463 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5464 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5465 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5466 : catalog,
5467 84 : dobj->catId.oid);
5468 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5469 84 : ntups = PQntuples(res);
5470 84 : i_extname = PQfnumber(res, "extname");
5471 168 : for (i = 0; i < ntups; i++)
5472 : {
5473 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5474 : keyword, nm,
5475 84 : fmtId(PQgetvalue(res, i, i_extname)));
5476 : }
5477 :
5478 84 : PQclear(res);
5479 84 : destroyPQExpBuffer(query);
5480 84 : pg_free(nm);
5481 : }
5482 14218 : }
5483 :
5484 : static Oid
5485 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5486 : {
5487 : /*
5488 : * If the old version didn't assign an array type, but the new version
5489 : * does, we must select an unused type OID to assign. This currently only
5490 : * happens for domains, when upgrading pre-v11 to v11 and up.
5491 : *
5492 : * Note: local state here is kind of ugly, but we must have some, since we
5493 : * mustn't choose the same unused OID more than once.
5494 : */
5495 : static Oid next_possible_free_oid = FirstNormalObjectId;
5496 : PGresult *res;
5497 : bool is_dup;
5498 :
5499 : do
5500 : {
5501 0 : ++next_possible_free_oid;
5502 0 : printfPQExpBuffer(upgrade_query,
5503 : "SELECT EXISTS(SELECT 1 "
5504 : "FROM pg_catalog.pg_type "
5505 : "WHERE oid = '%u'::pg_catalog.oid);",
5506 : next_possible_free_oid);
5507 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5508 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5509 0 : PQclear(res);
5510 0 : } while (is_dup);
5511 :
5512 0 : return next_possible_free_oid;
5513 : }
5514 :
5515 : static void
5516 1838 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5517 : PQExpBuffer upgrade_buffer,
5518 : Oid pg_type_oid,
5519 : bool force_array_type,
5520 : bool include_multirange_type)
5521 : {
5522 1838 : PQExpBuffer upgrade_query = createPQExpBuffer();
5523 : PGresult *res;
5524 : Oid pg_type_array_oid;
5525 : Oid pg_type_multirange_oid;
5526 : Oid pg_type_multirange_array_oid;
5527 : TypeInfo *tinfo;
5528 :
5529 1838 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5530 1838 : appendPQExpBuffer(upgrade_buffer,
5531 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5532 : pg_type_oid);
5533 :
5534 1838 : tinfo = findTypeByOid(pg_type_oid);
5535 1838 : if (tinfo)
5536 1838 : pg_type_array_oid = tinfo->typarray;
5537 : else
5538 0 : pg_type_array_oid = InvalidOid;
5539 :
5540 1838 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5541 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5542 :
5543 1838 : if (OidIsValid(pg_type_array_oid))
5544 : {
5545 1834 : appendPQExpBufferStr(upgrade_buffer,
5546 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5547 1834 : appendPQExpBuffer(upgrade_buffer,
5548 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5549 : pg_type_array_oid);
5550 : }
5551 :
5552 : /*
5553 : * Pre-set the multirange type oid and its own array type oid.
5554 : */
5555 1838 : if (include_multirange_type)
5556 : {
5557 16 : if (fout->remoteVersion >= 140000)
5558 : {
5559 16 : printfPQExpBuffer(upgrade_query,
5560 : "SELECT t.oid, t.typarray "
5561 : "FROM pg_catalog.pg_type t "
5562 : "JOIN pg_catalog.pg_range r "
5563 : "ON t.oid = r.rngmultitypid "
5564 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5565 : pg_type_oid);
5566 :
5567 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5568 :
5569 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5570 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5571 :
5572 16 : PQclear(res);
5573 : }
5574 : else
5575 : {
5576 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5577 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5578 : }
5579 :
5580 16 : appendPQExpBufferStr(upgrade_buffer,
5581 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5582 16 : appendPQExpBuffer(upgrade_buffer,
5583 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5584 : pg_type_multirange_oid);
5585 16 : appendPQExpBufferStr(upgrade_buffer,
5586 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5587 16 : appendPQExpBuffer(upgrade_buffer,
5588 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5589 : pg_type_multirange_array_oid);
5590 : }
5591 :
5592 1838 : destroyPQExpBuffer(upgrade_query);
5593 1838 : }
5594 :
5595 : static void
5596 1692 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5597 : PQExpBuffer upgrade_buffer,
5598 : const TableInfo *tbinfo)
5599 : {
5600 1692 : Oid pg_type_oid = tbinfo->reltype;
5601 :
5602 1692 : if (OidIsValid(pg_type_oid))
5603 1692 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5604 : pg_type_oid, false, false);
5605 1692 : }
5606 :
5607 : /*
5608 : * bsearch() comparator for BinaryUpgradeClassOidItem
5609 : */
5610 : static int
5611 24204 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5612 : {
5613 24204 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5614 24204 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5615 :
5616 24204 : return pg_cmp_u32(v1.oid, v2.oid);
5617 : }
5618 :
5619 : /*
5620 : * collectBinaryUpgradeClassOids
5621 : *
5622 : * Construct a table of pg_class information required for
5623 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5624 : * lookup.
5625 : */
5626 : static void
5627 62 : collectBinaryUpgradeClassOids(Archive *fout)
5628 : {
5629 : PGresult *res;
5630 : const char *query;
5631 :
5632 62 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5633 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5634 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5635 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5636 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5637 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5638 : "ORDER BY c.oid;";
5639 :
5640 62 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5641 :
5642 62 : nbinaryUpgradeClassOids = PQntuples(res);
5643 62 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5644 62 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5645 :
5646 29354 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5647 : {
5648 29292 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5649 29292 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5650 29292 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5651 29292 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5652 29292 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5653 29292 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5654 29292 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5655 : }
5656 :
5657 62 : PQclear(res);
5658 62 : }
5659 :
5660 : static void
5661 2458 : binary_upgrade_set_pg_class_oids(Archive *fout,
5662 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5663 : {
5664 2458 : BinaryUpgradeClassOidItem key = {0};
5665 : BinaryUpgradeClassOidItem *entry;
5666 :
5667 : Assert(binaryUpgradeClassOids);
5668 :
5669 : /*
5670 : * Preserve the OID and relfilenumber of the table, table's index, table's
5671 : * toast table and toast table's index if any.
5672 : *
5673 : * One complexity is that the current table definition might not require
5674 : * the creation of a TOAST table, but the old database might have a TOAST
5675 : * table that was created earlier, before some wide columns were dropped.
5676 : * By setting the TOAST oid we force creation of the TOAST heap and index
5677 : * by the new backend, so we can copy the files during binary upgrade
5678 : * without worrying about this case.
5679 : */
5680 2458 : key.oid = pg_class_oid;
5681 2458 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5682 : sizeof(BinaryUpgradeClassOidItem),
5683 : BinaryUpgradeClassOidItemCmp);
5684 :
5685 2458 : appendPQExpBufferStr(upgrade_buffer,
5686 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5687 :
5688 2458 : if (entry->relkind != RELKIND_INDEX &&
5689 1910 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5690 : {
5691 1860 : appendPQExpBuffer(upgrade_buffer,
5692 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5693 : pg_class_oid);
5694 :
5695 : /*
5696 : * Not every relation has storage. Also, in a pre-v12 database,
5697 : * partitioned tables have a relfilenumber, which should not be
5698 : * preserved when upgrading.
5699 : */
5700 1860 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5701 1538 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5702 1538 : appendPQExpBuffer(upgrade_buffer,
5703 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5704 : entry->relfilenumber);
5705 :
5706 : /*
5707 : * In a pre-v12 database, partitioned tables might be marked as having
5708 : * toast tables, but we should ignore them if so.
5709 : */
5710 1860 : if (OidIsValid(entry->toast_oid) &&
5711 552 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5712 : {
5713 552 : appendPQExpBuffer(upgrade_buffer,
5714 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5715 : entry->toast_oid);
5716 552 : appendPQExpBuffer(upgrade_buffer,
5717 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5718 : entry->toast_relfilenumber);
5719 :
5720 : /* every toast table has an index */
5721 552 : appendPQExpBuffer(upgrade_buffer,
5722 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5723 : entry->toast_index_oid);
5724 552 : appendPQExpBuffer(upgrade_buffer,
5725 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5726 : entry->toast_index_relfilenumber);
5727 : }
5728 : }
5729 : else
5730 : {
5731 : /* Preserve the OID and relfilenumber of the index */
5732 598 : appendPQExpBuffer(upgrade_buffer,
5733 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5734 : pg_class_oid);
5735 598 : appendPQExpBuffer(upgrade_buffer,
5736 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5737 : entry->relfilenumber);
5738 : }
5739 :
5740 2458 : appendPQExpBufferChar(upgrade_buffer, '\n');
5741 2458 : }
5742 :
5743 : /*
5744 : * If the DumpableObject is a member of an extension, add a suitable
5745 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5746 : *
5747 : * For somewhat historical reasons, objname should already be quoted,
5748 : * but not objnamespace (if any).
5749 : */
5750 : static void
5751 2922 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5752 : const DumpableObject *dobj,
5753 : const char *objtype,
5754 : const char *objname,
5755 : const char *objnamespace)
5756 : {
5757 2922 : DumpableObject *extobj = NULL;
5758 : int i;
5759 :
5760 2922 : if (!dobj->ext_member)
5761 2890 : return;
5762 :
5763 : /*
5764 : * Find the parent extension. We could avoid this search if we wanted to
5765 : * add a link field to DumpableObject, but the space costs of that would
5766 : * be considerable. We assume that member objects could only have a
5767 : * direct dependency on their own extension, not any others.
5768 : */
5769 32 : for (i = 0; i < dobj->nDeps; i++)
5770 : {
5771 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5772 32 : if (extobj && extobj->objType == DO_EXTENSION)
5773 32 : break;
5774 0 : extobj = NULL;
5775 : }
5776 32 : if (extobj == NULL)
5777 0 : pg_fatal("could not find parent extension for %s %s",
5778 : objtype, objname);
5779 :
5780 32 : appendPQExpBufferStr(upgrade_buffer,
5781 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5782 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5783 32 : fmtId(extobj->name),
5784 : objtype);
5785 32 : if (objnamespace && *objnamespace)
5786 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5787 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5788 : }
5789 :
5790 : /*
5791 : * getNamespaces:
5792 : * get information about all namespaces in the system catalogs
5793 : */
5794 : void
5795 470 : getNamespaces(Archive *fout)
5796 : {
5797 : PGresult *res;
5798 : int ntups;
5799 : int i;
5800 : PQExpBuffer query;
5801 : NamespaceInfo *nsinfo;
5802 : int i_tableoid;
5803 : int i_oid;
5804 : int i_nspname;
5805 : int i_nspowner;
5806 : int i_nspacl;
5807 : int i_acldefault;
5808 :
5809 470 : query = createPQExpBuffer();
5810 :
5811 : /*
5812 : * we fetch all namespaces including system ones, so that every object we
5813 : * read in can be linked to a containing namespace.
5814 : */
5815 470 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5816 : "n.nspowner, "
5817 : "n.nspacl, "
5818 : "acldefault('n', n.nspowner) AS acldefault "
5819 : "FROM pg_namespace n");
5820 :
5821 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5822 :
5823 470 : ntups = PQntuples(res);
5824 :
5825 470 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5826 :
5827 470 : i_tableoid = PQfnumber(res, "tableoid");
5828 470 : i_oid = PQfnumber(res, "oid");
5829 470 : i_nspname = PQfnumber(res, "nspname");
5830 470 : i_nspowner = PQfnumber(res, "nspowner");
5831 470 : i_nspacl = PQfnumber(res, "nspacl");
5832 470 : i_acldefault = PQfnumber(res, "acldefault");
5833 :
5834 4210 : for (i = 0; i < ntups; i++)
5835 : {
5836 : const char *nspowner;
5837 :
5838 3740 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5839 3740 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5840 3740 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5841 3740 : AssignDumpId(&nsinfo[i].dobj);
5842 3740 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5843 3740 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5844 3740 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5845 3740 : nsinfo[i].dacl.privtype = 0;
5846 3740 : nsinfo[i].dacl.initprivs = NULL;
5847 3740 : nspowner = PQgetvalue(res, i, i_nspowner);
5848 3740 : nsinfo[i].nspowner = atooid(nspowner);
5849 3740 : nsinfo[i].rolname = getRoleName(nspowner);
5850 :
5851 : /* Decide whether to dump this namespace */
5852 3740 : selectDumpableNamespace(&nsinfo[i], fout);
5853 :
5854 : /* Mark whether namespace has an ACL */
5855 3740 : if (!PQgetisnull(res, i, i_nspacl))
5856 1572 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5857 :
5858 : /*
5859 : * We ignore any pg_init_privs.initprivs entry for the public schema
5860 : * and assume a predetermined default, for several reasons. First,
5861 : * dropping and recreating the schema removes its pg_init_privs entry,
5862 : * but an empty destination database starts with this ACL nonetheless.
5863 : * Second, we support dump/reload of public schema ownership changes.
5864 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5865 : * initprivs continues to reflect the initial owner. Hence,
5866 : * synthesize the value that nspacl will have after the restore's
5867 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5868 : * match the source's ACL, even if the latter was an initdb-default
5869 : * ACL, which changed in v15. An upgrade pulls in changes to most
5870 : * system object ACLs that the DBA had not customized. We've made the
5871 : * public schema depart from that, because changing its ACL so easily
5872 : * breaks applications.
5873 : */
5874 3740 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5875 : {
5876 462 : PQExpBuffer aclarray = createPQExpBuffer();
5877 462 : PQExpBuffer aclitem = createPQExpBuffer();
5878 :
5879 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5880 462 : appendPQExpBufferChar(aclarray, '{');
5881 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5882 462 : appendPQExpBufferStr(aclitem, "=UC/");
5883 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5884 462 : appendPGArray(aclarray, aclitem->data);
5885 462 : resetPQExpBuffer(aclitem);
5886 462 : appendPQExpBufferStr(aclitem, "=U/");
5887 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5888 462 : appendPGArray(aclarray, aclitem->data);
5889 462 : appendPQExpBufferChar(aclarray, '}');
5890 :
5891 462 : nsinfo[i].dacl.privtype = 'i';
5892 462 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5893 462 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5894 :
5895 462 : destroyPQExpBuffer(aclarray);
5896 462 : destroyPQExpBuffer(aclitem);
5897 : }
5898 : }
5899 :
5900 470 : PQclear(res);
5901 470 : destroyPQExpBuffer(query);
5902 470 : }
5903 :
5904 : /*
5905 : * findNamespace:
5906 : * given a namespace OID, look up the info read by getNamespaces
5907 : */
5908 : static NamespaceInfo *
5909 1475284 : findNamespace(Oid nsoid)
5910 : {
5911 : NamespaceInfo *nsinfo;
5912 :
5913 1475284 : nsinfo = findNamespaceByOid(nsoid);
5914 1475284 : if (nsinfo == NULL)
5915 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5916 1475284 : return nsinfo;
5917 : }
5918 :
5919 : /*
5920 : * getExtensions:
5921 : * read all extensions in the system catalogs and return them in the
5922 : * ExtensionInfo* structure
5923 : *
5924 : * numExtensions is set to the number of extensions read in
5925 : */
5926 : ExtensionInfo *
5927 470 : getExtensions(Archive *fout, int *numExtensions)
5928 : {
5929 470 : DumpOptions *dopt = fout->dopt;
5930 : PGresult *res;
5931 : int ntups;
5932 : int i;
5933 : PQExpBuffer query;
5934 470 : ExtensionInfo *extinfo = NULL;
5935 : int i_tableoid;
5936 : int i_oid;
5937 : int i_extname;
5938 : int i_nspname;
5939 : int i_extrelocatable;
5940 : int i_extversion;
5941 : int i_extconfig;
5942 : int i_extcondition;
5943 :
5944 470 : query = createPQExpBuffer();
5945 :
5946 470 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5947 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5948 : "FROM pg_extension x "
5949 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5950 :
5951 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5952 :
5953 470 : ntups = PQntuples(res);
5954 470 : if (ntups == 0)
5955 0 : goto cleanup;
5956 :
5957 470 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5958 :
5959 470 : i_tableoid = PQfnumber(res, "tableoid");
5960 470 : i_oid = PQfnumber(res, "oid");
5961 470 : i_extname = PQfnumber(res, "extname");
5962 470 : i_nspname = PQfnumber(res, "nspname");
5963 470 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5964 470 : i_extversion = PQfnumber(res, "extversion");
5965 470 : i_extconfig = PQfnumber(res, "extconfig");
5966 470 : i_extcondition = PQfnumber(res, "extcondition");
5967 :
5968 990 : for (i = 0; i < ntups; i++)
5969 : {
5970 520 : extinfo[i].dobj.objType = DO_EXTENSION;
5971 520 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5972 520 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5973 520 : AssignDumpId(&extinfo[i].dobj);
5974 520 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5975 520 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5976 520 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5977 520 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5978 520 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5979 520 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5980 :
5981 : /* Decide whether we want to dump it */
5982 520 : selectDumpableExtension(&(extinfo[i]), dopt);
5983 : }
5984 :
5985 470 : cleanup:
5986 470 : PQclear(res);
5987 470 : destroyPQExpBuffer(query);
5988 :
5989 470 : *numExtensions = ntups;
5990 :
5991 470 : return extinfo;
5992 : }
5993 :
5994 : /*
5995 : * getTypes:
5996 : * get information about all types in the system catalogs
5997 : *
5998 : * NB: this must run after getFuncs() because we assume we can do
5999 : * findFuncByOid().
6000 : */
6001 : void
6002 468 : getTypes(Archive *fout)
6003 : {
6004 : PGresult *res;
6005 : int ntups;
6006 : int i;
6007 468 : PQExpBuffer query = createPQExpBuffer();
6008 : TypeInfo *tyinfo;
6009 : ShellTypeInfo *stinfo;
6010 : int i_tableoid;
6011 : int i_oid;
6012 : int i_typname;
6013 : int i_typnamespace;
6014 : int i_typacl;
6015 : int i_acldefault;
6016 : int i_typowner;
6017 : int i_typelem;
6018 : int i_typrelid;
6019 : int i_typrelkind;
6020 : int i_typtype;
6021 : int i_typisdefined;
6022 : int i_isarray;
6023 : int i_typarray;
6024 :
6025 : /*
6026 : * we include even the built-in types because those may be used as array
6027 : * elements by user-defined types
6028 : *
6029 : * we filter out the built-in types when we dump out the types
6030 : *
6031 : * same approach for undefined (shell) types and array types
6032 : *
6033 : * Note: as of 8.3 we can reliably detect whether a type is an
6034 : * auto-generated array type by checking the element type's typarray.
6035 : * (Before that the test is capable of generating false positives.) We
6036 : * still check for name beginning with '_', though, so as to avoid the
6037 : * cost of the subselect probe for all standard types. This would have to
6038 : * be revisited if the backend ever allows renaming of array types.
6039 : */
6040 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6041 : "typnamespace, typacl, "
6042 : "acldefault('T', typowner) AS acldefault, "
6043 : "typowner, "
6044 : "typelem, typrelid, typarray, "
6045 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6046 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6047 : "typtype, typisdefined, "
6048 : "typname[0] = '_' AND typelem != 0 AND "
6049 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6050 : "FROM pg_type");
6051 :
6052 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6053 :
6054 468 : ntups = PQntuples(res);
6055 :
6056 468 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6057 :
6058 468 : i_tableoid = PQfnumber(res, "tableoid");
6059 468 : i_oid = PQfnumber(res, "oid");
6060 468 : i_typname = PQfnumber(res, "typname");
6061 468 : i_typnamespace = PQfnumber(res, "typnamespace");
6062 468 : i_typacl = PQfnumber(res, "typacl");
6063 468 : i_acldefault = PQfnumber(res, "acldefault");
6064 468 : i_typowner = PQfnumber(res, "typowner");
6065 468 : i_typelem = PQfnumber(res, "typelem");
6066 468 : i_typrelid = PQfnumber(res, "typrelid");
6067 468 : i_typrelkind = PQfnumber(res, "typrelkind");
6068 468 : i_typtype = PQfnumber(res, "typtype");
6069 468 : i_typisdefined = PQfnumber(res, "typisdefined");
6070 468 : i_isarray = PQfnumber(res, "isarray");
6071 468 : i_typarray = PQfnumber(res, "typarray");
6072 :
6073 338210 : for (i = 0; i < ntups; i++)
6074 : {
6075 337742 : tyinfo[i].dobj.objType = DO_TYPE;
6076 337742 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6077 337742 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6078 337742 : AssignDumpId(&tyinfo[i].dobj);
6079 337742 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6080 675484 : tyinfo[i].dobj.namespace =
6081 337742 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6082 337742 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6083 337742 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6084 337742 : tyinfo[i].dacl.privtype = 0;
6085 337742 : tyinfo[i].dacl.initprivs = NULL;
6086 337742 : tyinfo[i].ftypname = NULL; /* may get filled later */
6087 337742 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6088 337742 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6089 337742 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6090 337742 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6091 337742 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6092 337742 : tyinfo[i].shellType = NULL;
6093 :
6094 337742 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6095 337624 : tyinfo[i].isDefined = true;
6096 : else
6097 118 : tyinfo[i].isDefined = false;
6098 :
6099 337742 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6100 162026 : tyinfo[i].isArray = true;
6101 : else
6102 175716 : tyinfo[i].isArray = false;
6103 :
6104 337742 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6105 :
6106 337742 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6107 3124 : tyinfo[i].isMultirange = true;
6108 : else
6109 334618 : tyinfo[i].isMultirange = false;
6110 :
6111 : /* Decide whether we want to dump it */
6112 337742 : selectDumpableType(&tyinfo[i], fout);
6113 :
6114 : /* Mark whether type has an ACL */
6115 337742 : if (!PQgetisnull(res, i, i_typacl))
6116 442 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6117 :
6118 : /*
6119 : * If it's a domain, fetch info about its constraints, if any
6120 : */
6121 337742 : tyinfo[i].nDomChecks = 0;
6122 337742 : tyinfo[i].domChecks = NULL;
6123 337742 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6124 40796 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6125 452 : getDomainConstraints(fout, &(tyinfo[i]));
6126 :
6127 : /*
6128 : * If it's a base type, make a DumpableObject representing a shell
6129 : * definition of the type. We will need to dump that ahead of the I/O
6130 : * functions for the type. Similarly, range types need a shell
6131 : * definition in case they have a canonicalize function.
6132 : *
6133 : * Note: the shell type doesn't have a catId. You might think it
6134 : * should copy the base type's catId, but then it might capture the
6135 : * pg_depend entries for the type, which we don't want.
6136 : */
6137 337742 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6138 40796 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6139 19934 : tyinfo[i].typtype == TYPTYPE_RANGE))
6140 : {
6141 21162 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6142 21162 : stinfo->dobj.objType = DO_SHELL_TYPE;
6143 21162 : stinfo->dobj.catId = nilCatalogId;
6144 21162 : AssignDumpId(&stinfo->dobj);
6145 21162 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6146 21162 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6147 21162 : stinfo->baseType = &(tyinfo[i]);
6148 21162 : tyinfo[i].shellType = stinfo;
6149 :
6150 : /*
6151 : * Initially mark the shell type as not to be dumped. We'll only
6152 : * dump it if the I/O or canonicalize functions need to be dumped;
6153 : * this is taken care of while sorting dependencies.
6154 : */
6155 21162 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6156 : }
6157 : }
6158 :
6159 468 : PQclear(res);
6160 :
6161 468 : destroyPQExpBuffer(query);
6162 468 : }
6163 :
6164 : /*
6165 : * getOperators:
6166 : * get information about all operators in the system catalogs
6167 : */
6168 : void
6169 468 : getOperators(Archive *fout)
6170 : {
6171 : PGresult *res;
6172 : int ntups;
6173 : int i;
6174 468 : PQExpBuffer query = createPQExpBuffer();
6175 : OprInfo *oprinfo;
6176 : int i_tableoid;
6177 : int i_oid;
6178 : int i_oprname;
6179 : int i_oprnamespace;
6180 : int i_oprowner;
6181 : int i_oprkind;
6182 : int i_oprcode;
6183 :
6184 : /*
6185 : * find all operators, including builtin operators; we filter out
6186 : * system-defined operators at dump-out time.
6187 : */
6188 :
6189 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6190 : "oprnamespace, "
6191 : "oprowner, "
6192 : "oprkind, "
6193 : "oprcode::oid AS oprcode "
6194 : "FROM pg_operator");
6195 :
6196 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6197 :
6198 468 : ntups = PQntuples(res);
6199 :
6200 468 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6201 :
6202 468 : i_tableoid = PQfnumber(res, "tableoid");
6203 468 : i_oid = PQfnumber(res, "oid");
6204 468 : i_oprname = PQfnumber(res, "oprname");
6205 468 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6206 468 : i_oprowner = PQfnumber(res, "oprowner");
6207 468 : i_oprkind = PQfnumber(res, "oprkind");
6208 468 : i_oprcode = PQfnumber(res, "oprcode");
6209 :
6210 374774 : for (i = 0; i < ntups; i++)
6211 : {
6212 374306 : oprinfo[i].dobj.objType = DO_OPERATOR;
6213 374306 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6214 374306 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6215 374306 : AssignDumpId(&oprinfo[i].dobj);
6216 374306 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6217 748612 : oprinfo[i].dobj.namespace =
6218 374306 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6219 374306 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6220 374306 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6221 374306 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6222 :
6223 : /* Decide whether we want to dump it */
6224 374306 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6225 : }
6226 :
6227 468 : PQclear(res);
6228 :
6229 468 : destroyPQExpBuffer(query);
6230 468 : }
6231 :
6232 : /*
6233 : * getCollations:
6234 : * get information about all collations in the system catalogs
6235 : */
6236 : void
6237 468 : getCollations(Archive *fout)
6238 : {
6239 : PGresult *res;
6240 : int ntups;
6241 : int i;
6242 : PQExpBuffer query;
6243 : CollInfo *collinfo;
6244 : int i_tableoid;
6245 : int i_oid;
6246 : int i_collname;
6247 : int i_collnamespace;
6248 : int i_collowner;
6249 :
6250 468 : query = createPQExpBuffer();
6251 :
6252 : /*
6253 : * find all collations, including builtin collations; we filter out
6254 : * system-defined collations at dump-out time.
6255 : */
6256 :
6257 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6258 : "collnamespace, "
6259 : "collowner "
6260 : "FROM pg_collation");
6261 :
6262 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6263 :
6264 468 : ntups = PQntuples(res);
6265 :
6266 468 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6267 :
6268 468 : i_tableoid = PQfnumber(res, "tableoid");
6269 468 : i_oid = PQfnumber(res, "oid");
6270 468 : i_collname = PQfnumber(res, "collname");
6271 468 : i_collnamespace = PQfnumber(res, "collnamespace");
6272 468 : i_collowner = PQfnumber(res, "collowner");
6273 :
6274 382612 : for (i = 0; i < ntups; i++)
6275 : {
6276 382144 : collinfo[i].dobj.objType = DO_COLLATION;
6277 382144 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6278 382144 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6279 382144 : AssignDumpId(&collinfo[i].dobj);
6280 382144 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6281 764288 : collinfo[i].dobj.namespace =
6282 382144 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6283 382144 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6284 :
6285 : /* Decide whether we want to dump it */
6286 382144 : selectDumpableObject(&(collinfo[i].dobj), fout);
6287 : }
6288 :
6289 468 : PQclear(res);
6290 :
6291 468 : destroyPQExpBuffer(query);
6292 468 : }
6293 :
6294 : /*
6295 : * getConversions:
6296 : * get information about all conversions in the system catalogs
6297 : */
6298 : void
6299 468 : getConversions(Archive *fout)
6300 : {
6301 : PGresult *res;
6302 : int ntups;
6303 : int i;
6304 : PQExpBuffer query;
6305 : ConvInfo *convinfo;
6306 : int i_tableoid;
6307 : int i_oid;
6308 : int i_conname;
6309 : int i_connamespace;
6310 : int i_conowner;
6311 :
6312 468 : query = createPQExpBuffer();
6313 :
6314 : /*
6315 : * find all conversions, including builtin conversions; we filter out
6316 : * system-defined conversions at dump-out time.
6317 : */
6318 :
6319 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6320 : "connamespace, "
6321 : "conowner "
6322 : "FROM pg_conversion");
6323 :
6324 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6325 :
6326 468 : ntups = PQntuples(res);
6327 :
6328 468 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6329 :
6330 468 : i_tableoid = PQfnumber(res, "tableoid");
6331 468 : i_oid = PQfnumber(res, "oid");
6332 468 : i_conname = PQfnumber(res, "conname");
6333 468 : i_connamespace = PQfnumber(res, "connamespace");
6334 468 : i_conowner = PQfnumber(res, "conowner");
6335 :
6336 60470 : for (i = 0; i < ntups; i++)
6337 : {
6338 60002 : convinfo[i].dobj.objType = DO_CONVERSION;
6339 60002 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6340 60002 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6341 60002 : AssignDumpId(&convinfo[i].dobj);
6342 60002 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6343 120004 : convinfo[i].dobj.namespace =
6344 60002 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6345 60002 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6346 :
6347 : /* Decide whether we want to dump it */
6348 60002 : selectDumpableObject(&(convinfo[i].dobj), fout);
6349 : }
6350 :
6351 468 : PQclear(res);
6352 :
6353 468 : destroyPQExpBuffer(query);
6354 468 : }
6355 :
6356 : /*
6357 : * getAccessMethods:
6358 : * get information about all user-defined access methods
6359 : */
6360 : void
6361 468 : getAccessMethods(Archive *fout)
6362 : {
6363 : PGresult *res;
6364 : int ntups;
6365 : int i;
6366 : PQExpBuffer query;
6367 : AccessMethodInfo *aminfo;
6368 : int i_tableoid;
6369 : int i_oid;
6370 : int i_amname;
6371 : int i_amhandler;
6372 : int i_amtype;
6373 :
6374 : /* Before 9.6, there are no user-defined access methods */
6375 468 : if (fout->remoteVersion < 90600)
6376 0 : return;
6377 :
6378 468 : query = createPQExpBuffer();
6379 :
6380 : /* Select all access methods from pg_am table */
6381 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6382 : "amhandler::pg_catalog.regproc AS amhandler "
6383 : "FROM pg_am");
6384 :
6385 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6386 :
6387 468 : ntups = PQntuples(res);
6388 :
6389 468 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6390 :
6391 468 : i_tableoid = PQfnumber(res, "tableoid");
6392 468 : i_oid = PQfnumber(res, "oid");
6393 468 : i_amname = PQfnumber(res, "amname");
6394 468 : i_amhandler = PQfnumber(res, "amhandler");
6395 468 : i_amtype = PQfnumber(res, "amtype");
6396 :
6397 4010 : for (i = 0; i < ntups; i++)
6398 : {
6399 3542 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6400 3542 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6401 3542 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6402 3542 : AssignDumpId(&aminfo[i].dobj);
6403 3542 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6404 3542 : aminfo[i].dobj.namespace = NULL;
6405 3542 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6406 3542 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6407 :
6408 : /* Decide whether we want to dump it */
6409 3542 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6410 : }
6411 :
6412 468 : PQclear(res);
6413 :
6414 468 : destroyPQExpBuffer(query);
6415 : }
6416 :
6417 :
6418 : /*
6419 : * getOpclasses:
6420 : * get information about all opclasses in the system catalogs
6421 : */
6422 : void
6423 468 : getOpclasses(Archive *fout)
6424 : {
6425 : PGresult *res;
6426 : int ntups;
6427 : int i;
6428 468 : PQExpBuffer query = createPQExpBuffer();
6429 : OpclassInfo *opcinfo;
6430 : int i_tableoid;
6431 : int i_oid;
6432 : int i_opcname;
6433 : int i_opcnamespace;
6434 : int i_opcowner;
6435 :
6436 : /*
6437 : * find all opclasses, including builtin opclasses; we filter out
6438 : * system-defined opclasses at dump-out time.
6439 : */
6440 :
6441 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6442 : "opcnamespace, "
6443 : "opcowner "
6444 : "FROM pg_opclass");
6445 :
6446 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6447 :
6448 468 : ntups = PQntuples(res);
6449 :
6450 468 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6451 :
6452 468 : i_tableoid = PQfnumber(res, "tableoid");
6453 468 : i_oid = PQfnumber(res, "oid");
6454 468 : i_opcname = PQfnumber(res, "opcname");
6455 468 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6456 468 : i_opcowner = PQfnumber(res, "opcowner");
6457 :
6458 83658 : for (i = 0; i < ntups; i++)
6459 : {
6460 83190 : opcinfo[i].dobj.objType = DO_OPCLASS;
6461 83190 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6462 83190 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6463 83190 : AssignDumpId(&opcinfo[i].dobj);
6464 83190 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6465 166380 : opcinfo[i].dobj.namespace =
6466 83190 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6467 83190 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6468 :
6469 : /* Decide whether we want to dump it */
6470 83190 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6471 : }
6472 :
6473 468 : PQclear(res);
6474 :
6475 468 : destroyPQExpBuffer(query);
6476 468 : }
6477 :
6478 : /*
6479 : * getOpfamilies:
6480 : * get information about all opfamilies in the system catalogs
6481 : */
6482 : void
6483 468 : getOpfamilies(Archive *fout)
6484 : {
6485 : PGresult *res;
6486 : int ntups;
6487 : int i;
6488 : PQExpBuffer query;
6489 : OpfamilyInfo *opfinfo;
6490 : int i_tableoid;
6491 : int i_oid;
6492 : int i_opfname;
6493 : int i_opfnamespace;
6494 : int i_opfowner;
6495 :
6496 468 : query = createPQExpBuffer();
6497 :
6498 : /*
6499 : * find all opfamilies, including builtin opfamilies; we filter out
6500 : * system-defined opfamilies at dump-out time.
6501 : */
6502 :
6503 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6504 : "opfnamespace, "
6505 : "opfowner "
6506 : "FROM pg_opfamily");
6507 :
6508 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6509 :
6510 468 : ntups = PQntuples(res);
6511 :
6512 468 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6513 :
6514 468 : i_tableoid = PQfnumber(res, "tableoid");
6515 468 : i_oid = PQfnumber(res, "oid");
6516 468 : i_opfname = PQfnumber(res, "opfname");
6517 468 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6518 468 : i_opfowner = PQfnumber(res, "opfowner");
6519 :
6520 69132 : for (i = 0; i < ntups; i++)
6521 : {
6522 68664 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6523 68664 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6524 68664 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6525 68664 : AssignDumpId(&opfinfo[i].dobj);
6526 68664 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6527 137328 : opfinfo[i].dobj.namespace =
6528 68664 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6529 68664 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6530 :
6531 : /* Decide whether we want to dump it */
6532 68664 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6533 : }
6534 :
6535 468 : PQclear(res);
6536 :
6537 468 : destroyPQExpBuffer(query);
6538 468 : }
6539 :
6540 : /*
6541 : * getAggregates:
6542 : * get information about all user-defined aggregates in the system catalogs
6543 : */
6544 : void
6545 468 : getAggregates(Archive *fout)
6546 : {
6547 468 : DumpOptions *dopt = fout->dopt;
6548 : PGresult *res;
6549 : int ntups;
6550 : int i;
6551 468 : PQExpBuffer query = createPQExpBuffer();
6552 : AggInfo *agginfo;
6553 : int i_tableoid;
6554 : int i_oid;
6555 : int i_aggname;
6556 : int i_aggnamespace;
6557 : int i_pronargs;
6558 : int i_proargtypes;
6559 : int i_proowner;
6560 : int i_aggacl;
6561 : int i_acldefault;
6562 :
6563 : /*
6564 : * Find all interesting aggregates. See comment in getFuncs() for the
6565 : * rationale behind the filtering logic.
6566 : */
6567 468 : if (fout->remoteVersion >= 90600)
6568 : {
6569 : const char *agg_check;
6570 :
6571 936 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6572 468 : : "p.proisagg");
6573 :
6574 468 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6575 : "p.proname AS aggname, "
6576 : "p.pronamespace AS aggnamespace, "
6577 : "p.pronargs, p.proargtypes, "
6578 : "p.proowner, "
6579 : "p.proacl AS aggacl, "
6580 : "acldefault('f', p.proowner) AS acldefault "
6581 : "FROM pg_proc p "
6582 : "LEFT JOIN pg_init_privs pip ON "
6583 : "(p.oid = pip.objoid "
6584 : "AND pip.classoid = 'pg_proc'::regclass "
6585 : "AND pip.objsubid = 0) "
6586 : "WHERE %s AND ("
6587 : "p.pronamespace != "
6588 : "(SELECT oid FROM pg_namespace "
6589 : "WHERE nspname = 'pg_catalog') OR "
6590 : "p.proacl IS DISTINCT FROM pip.initprivs",
6591 : agg_check);
6592 468 : if (dopt->binary_upgrade)
6593 62 : appendPQExpBufferStr(query,
6594 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6595 : "classid = 'pg_proc'::regclass AND "
6596 : "objid = p.oid AND "
6597 : "refclassid = 'pg_extension'::regclass AND "
6598 : "deptype = 'e')");
6599 468 : appendPQExpBufferChar(query, ')');
6600 : }
6601 : else
6602 : {
6603 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6604 : "pronamespace AS aggnamespace, "
6605 : "pronargs, proargtypes, "
6606 : "proowner, "
6607 : "proacl AS aggacl, "
6608 : "acldefault('f', proowner) AS acldefault "
6609 : "FROM pg_proc p "
6610 : "WHERE proisagg AND ("
6611 : "pronamespace != "
6612 : "(SELECT oid FROM pg_namespace "
6613 : "WHERE nspname = 'pg_catalog')");
6614 0 : if (dopt->binary_upgrade)
6615 0 : appendPQExpBufferStr(query,
6616 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6617 : "classid = 'pg_proc'::regclass AND "
6618 : "objid = p.oid AND "
6619 : "refclassid = 'pg_extension'::regclass AND "
6620 : "deptype = 'e')");
6621 0 : appendPQExpBufferChar(query, ')');
6622 : }
6623 :
6624 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6625 :
6626 468 : ntups = PQntuples(res);
6627 :
6628 468 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6629 :
6630 468 : i_tableoid = PQfnumber(res, "tableoid");
6631 468 : i_oid = PQfnumber(res, "oid");
6632 468 : i_aggname = PQfnumber(res, "aggname");
6633 468 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6634 468 : i_pronargs = PQfnumber(res, "pronargs");
6635 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6636 468 : i_proowner = PQfnumber(res, "proowner");
6637 468 : i_aggacl = PQfnumber(res, "aggacl");
6638 468 : i_acldefault = PQfnumber(res, "acldefault");
6639 :
6640 1556 : for (i = 0; i < ntups; i++)
6641 : {
6642 1088 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6643 1088 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6644 1088 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6645 1088 : AssignDumpId(&agginfo[i].aggfn.dobj);
6646 1088 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6647 2176 : agginfo[i].aggfn.dobj.namespace =
6648 1088 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6649 1088 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6650 1088 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6651 1088 : agginfo[i].aggfn.dacl.privtype = 0;
6652 1088 : agginfo[i].aggfn.dacl.initprivs = NULL;
6653 1088 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6654 1088 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6655 1088 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6656 1088 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6657 1088 : if (agginfo[i].aggfn.nargs == 0)
6658 160 : agginfo[i].aggfn.argtypes = NULL;
6659 : else
6660 : {
6661 928 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6662 928 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6663 928 : agginfo[i].aggfn.argtypes,
6664 928 : agginfo[i].aggfn.nargs);
6665 : }
6666 1088 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6667 :
6668 : /* Decide whether we want to dump it */
6669 1088 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6670 :
6671 : /* Mark whether aggregate has an ACL */
6672 1088 : if (!PQgetisnull(res, i, i_aggacl))
6673 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6674 : }
6675 :
6676 468 : PQclear(res);
6677 :
6678 468 : destroyPQExpBuffer(query);
6679 468 : }
6680 :
6681 : /*
6682 : * getFuncs:
6683 : * get information about all user-defined functions in the system catalogs
6684 : */
6685 : void
6686 468 : getFuncs(Archive *fout)
6687 : {
6688 468 : DumpOptions *dopt = fout->dopt;
6689 : PGresult *res;
6690 : int ntups;
6691 : int i;
6692 468 : PQExpBuffer query = createPQExpBuffer();
6693 : FuncInfo *finfo;
6694 : int i_tableoid;
6695 : int i_oid;
6696 : int i_proname;
6697 : int i_pronamespace;
6698 : int i_proowner;
6699 : int i_prolang;
6700 : int i_pronargs;
6701 : int i_proargtypes;
6702 : int i_prorettype;
6703 : int i_proacl;
6704 : int i_acldefault;
6705 :
6706 : /*
6707 : * Find all interesting functions. This is a bit complicated:
6708 : *
6709 : * 1. Always exclude aggregates; those are handled elsewhere.
6710 : *
6711 : * 2. Always exclude functions that are internally dependent on something
6712 : * else, since presumably those will be created as a result of creating
6713 : * the something else. This currently acts only to suppress constructor
6714 : * functions for range types. Note this is OK only because the
6715 : * constructors don't have any dependencies the range type doesn't have;
6716 : * otherwise we might not get creation ordering correct.
6717 : *
6718 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6719 : * they're members of extensions and we are in binary-upgrade mode then
6720 : * include them, since we want to dump extension members individually in
6721 : * that mode. Also, if they are used by casts or transforms then we need
6722 : * to gather the information about them, though they won't be dumped if
6723 : * they are built-in. Also, in 9.6 and up, include functions in
6724 : * pg_catalog if they have an ACL different from what's shown in
6725 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6726 : */
6727 468 : if (fout->remoteVersion >= 90600)
6728 : {
6729 : const char *not_agg_check;
6730 :
6731 936 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6732 468 : : "NOT p.proisagg");
6733 :
6734 468 : appendPQExpBuffer(query,
6735 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6736 : "p.pronargs, p.proargtypes, p.prorettype, "
6737 : "p.proacl, "
6738 : "acldefault('f', p.proowner) AS acldefault, "
6739 : "p.pronamespace, "
6740 : "p.proowner "
6741 : "FROM pg_proc p "
6742 : "LEFT JOIN pg_init_privs pip ON "
6743 : "(p.oid = pip.objoid "
6744 : "AND pip.classoid = 'pg_proc'::regclass "
6745 : "AND pip.objsubid = 0) "
6746 : "WHERE %s"
6747 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6748 : "WHERE classid = 'pg_proc'::regclass AND "
6749 : "objid = p.oid AND deptype = 'i')"
6750 : "\n AND ("
6751 : "\n pronamespace != "
6752 : "(SELECT oid FROM pg_namespace "
6753 : "WHERE nspname = 'pg_catalog')"
6754 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6755 : "\n WHERE pg_cast.oid > %u "
6756 : "\n AND p.oid = pg_cast.castfunc)"
6757 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6758 : "\n WHERE pg_transform.oid > %u AND "
6759 : "\n (p.oid = pg_transform.trffromsql"
6760 : "\n OR p.oid = pg_transform.trftosql))",
6761 : not_agg_check,
6762 : g_last_builtin_oid,
6763 : g_last_builtin_oid);
6764 468 : if (dopt->binary_upgrade)
6765 62 : appendPQExpBufferStr(query,
6766 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6767 : "classid = 'pg_proc'::regclass AND "
6768 : "objid = p.oid AND "
6769 : "refclassid = 'pg_extension'::regclass AND "
6770 : "deptype = 'e')");
6771 468 : appendPQExpBufferStr(query,
6772 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6773 468 : appendPQExpBufferChar(query, ')');
6774 : }
6775 : else
6776 : {
6777 0 : appendPQExpBuffer(query,
6778 : "SELECT tableoid, oid, proname, prolang, "
6779 : "pronargs, proargtypes, prorettype, proacl, "
6780 : "acldefault('f', proowner) AS acldefault, "
6781 : "pronamespace, "
6782 : "proowner "
6783 : "FROM pg_proc p "
6784 : "WHERE NOT proisagg"
6785 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6786 : "WHERE classid = 'pg_proc'::regclass AND "
6787 : "objid = p.oid AND deptype = 'i')"
6788 : "\n AND ("
6789 : "\n pronamespace != "
6790 : "(SELECT oid FROM pg_namespace "
6791 : "WHERE nspname = 'pg_catalog')"
6792 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6793 : "\n WHERE pg_cast.oid > '%u'::oid"
6794 : "\n AND p.oid = pg_cast.castfunc)",
6795 : g_last_builtin_oid);
6796 :
6797 0 : if (fout->remoteVersion >= 90500)
6798 0 : appendPQExpBuffer(query,
6799 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6800 : "\n WHERE pg_transform.oid > '%u'::oid"
6801 : "\n AND (p.oid = pg_transform.trffromsql"
6802 : "\n OR p.oid = pg_transform.trftosql))",
6803 : g_last_builtin_oid);
6804 :
6805 0 : if (dopt->binary_upgrade)
6806 0 : appendPQExpBufferStr(query,
6807 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6808 : "classid = 'pg_proc'::regclass AND "
6809 : "objid = p.oid AND "
6810 : "refclassid = 'pg_extension'::regclass AND "
6811 : "deptype = 'e')");
6812 0 : appendPQExpBufferChar(query, ')');
6813 : }
6814 :
6815 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6816 :
6817 468 : ntups = PQntuples(res);
6818 :
6819 468 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6820 :
6821 468 : i_tableoid = PQfnumber(res, "tableoid");
6822 468 : i_oid = PQfnumber(res, "oid");
6823 468 : i_proname = PQfnumber(res, "proname");
6824 468 : i_pronamespace = PQfnumber(res, "pronamespace");
6825 468 : i_proowner = PQfnumber(res, "proowner");
6826 468 : i_prolang = PQfnumber(res, "prolang");
6827 468 : i_pronargs = PQfnumber(res, "pronargs");
6828 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6829 468 : i_prorettype = PQfnumber(res, "prorettype");
6830 468 : i_proacl = PQfnumber(res, "proacl");
6831 468 : i_acldefault = PQfnumber(res, "acldefault");
6832 :
6833 12766 : for (i = 0; i < ntups; i++)
6834 : {
6835 12298 : finfo[i].dobj.objType = DO_FUNC;
6836 12298 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6837 12298 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6838 12298 : AssignDumpId(&finfo[i].dobj);
6839 12298 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6840 24596 : finfo[i].dobj.namespace =
6841 12298 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6842 12298 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6843 12298 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6844 12298 : finfo[i].dacl.privtype = 0;
6845 12298 : finfo[i].dacl.initprivs = NULL;
6846 12298 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6847 12298 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6848 12298 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6849 12298 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6850 12298 : if (finfo[i].nargs == 0)
6851 2768 : finfo[i].argtypes = NULL;
6852 : else
6853 : {
6854 9530 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6855 9530 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6856 9530 : finfo[i].argtypes, finfo[i].nargs);
6857 : }
6858 12298 : finfo[i].postponed_def = false; /* might get set during sort */
6859 :
6860 : /* Decide whether we want to dump it */
6861 12298 : selectDumpableObject(&(finfo[i].dobj), fout);
6862 :
6863 : /* Mark whether function has an ACL */
6864 12298 : if (!PQgetisnull(res, i, i_proacl))
6865 310 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6866 : }
6867 :
6868 468 : PQclear(res);
6869 :
6870 468 : destroyPQExpBuffer(query);
6871 468 : }
6872 :
6873 : /*
6874 : * getRelationStatistics
6875 : * register the statistics object as a dependent of the relation.
6876 : *
6877 : * reltuples is passed as a string to avoid complexities in converting from/to
6878 : * floating point.
6879 : */
6880 : static RelStatsInfo *
6881 26334 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
6882 : char *reltuples, int32 relallvisible,
6883 : int32 relallfrozen, char relkind,
6884 : char **indAttNames, int nindAttNames)
6885 : {
6886 26334 : if (!fout->dopt->dumpStatistics)
6887 18786 : return NULL;
6888 :
6889 7548 : if ((relkind == RELKIND_RELATION) ||
6890 3272 : (relkind == RELKIND_PARTITIONED_TABLE) ||
6891 2002 : (relkind == RELKIND_INDEX) ||
6892 1314 : (relkind == RELKIND_PARTITIONED_INDEX) ||
6893 : (relkind == RELKIND_MATVIEW))
6894 : {
6895 6970 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
6896 6970 : DumpableObject *dobj = &info->dobj;
6897 :
6898 6970 : dobj->objType = DO_REL_STATS;
6899 6970 : dobj->catId.tableoid = 0;
6900 6970 : dobj->catId.oid = 0;
6901 6970 : AssignDumpId(dobj);
6902 6970 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
6903 6970 : dobj->dependencies[0] = rel->dumpId;
6904 6970 : dobj->nDeps = 1;
6905 6970 : dobj->allocDeps = 1;
6906 6970 : dobj->components |= DUMP_COMPONENT_STATISTICS;
6907 6970 : dobj->name = pg_strdup(rel->name);
6908 6970 : dobj->namespace = rel->namespace;
6909 6970 : info->relpages = relpages;
6910 6970 : info->reltuples = pstrdup(reltuples);
6911 6970 : info->relallvisible = relallvisible;
6912 6970 : info->relallfrozen = relallfrozen;
6913 6970 : info->relkind = relkind;
6914 6970 : info->indAttNames = indAttNames;
6915 6970 : info->nindAttNames = nindAttNames;
6916 :
6917 : /*
6918 : * Ordinarily, stats go in SECTION_DATA for tables and
6919 : * SECTION_POST_DATA for indexes.
6920 : *
6921 : * However, the section may be updated later for materialized view
6922 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
6923 : * the stats, so the stats must be restored after the data. Also, the
6924 : * materialized view definition may be postponed to SECTION_POST_DATA
6925 : * (see repairMatViewBoundaryMultiLoop()).
6926 : */
6927 6970 : switch (info->relkind)
6928 : {
6929 5012 : case RELKIND_RELATION:
6930 : case RELKIND_PARTITIONED_TABLE:
6931 : case RELKIND_MATVIEW:
6932 5012 : info->section = SECTION_DATA;
6933 5012 : break;
6934 1958 : case RELKIND_INDEX:
6935 : case RELKIND_PARTITIONED_INDEX:
6936 1958 : info->section = SECTION_POST_DATA;
6937 1958 : break;
6938 0 : default:
6939 0 : pg_fatal("cannot dump statistics for relation kind '%c'",
6940 : info->relkind);
6941 : }
6942 :
6943 6970 : return info;
6944 : }
6945 578 : return NULL;
6946 : }
6947 :
6948 : /*
6949 : * getTables
6950 : * read all the tables (no indexes) in the system catalogs,
6951 : * and return them as an array of TableInfo structures
6952 : *
6953 : * *numTables is set to the number of tables read in
6954 : */
6955 : TableInfo *
6956 470 : getTables(Archive *fout, int *numTables)
6957 : {
6958 470 : DumpOptions *dopt = fout->dopt;
6959 : PGresult *res;
6960 : int ntups;
6961 : int i;
6962 470 : PQExpBuffer query = createPQExpBuffer();
6963 : TableInfo *tblinfo;
6964 : int i_reltableoid;
6965 : int i_reloid;
6966 : int i_relname;
6967 : int i_relnamespace;
6968 : int i_relkind;
6969 : int i_reltype;
6970 : int i_relowner;
6971 : int i_relchecks;
6972 : int i_relhasindex;
6973 : int i_relhasrules;
6974 : int i_relpages;
6975 : int i_reltuples;
6976 : int i_relallvisible;
6977 : int i_relallfrozen;
6978 : int i_toastpages;
6979 : int i_owning_tab;
6980 : int i_owning_col;
6981 : int i_reltablespace;
6982 : int i_relhasoids;
6983 : int i_relhastriggers;
6984 : int i_relpersistence;
6985 : int i_relispopulated;
6986 : int i_relreplident;
6987 : int i_relrowsec;
6988 : int i_relforcerowsec;
6989 : int i_relfrozenxid;
6990 : int i_toastfrozenxid;
6991 : int i_toastoid;
6992 : int i_relminmxid;
6993 : int i_toastminmxid;
6994 : int i_reloptions;
6995 : int i_checkoption;
6996 : int i_toastreloptions;
6997 : int i_reloftype;
6998 : int i_foreignserver;
6999 : int i_amname;
7000 : int i_is_identity_sequence;
7001 : int i_relacl;
7002 : int i_acldefault;
7003 : int i_ispartition;
7004 :
7005 : /*
7006 : * Find all the tables and table-like objects.
7007 : *
7008 : * We must fetch all tables in this phase because otherwise we cannot
7009 : * correctly identify inherited columns, owned sequences, etc.
7010 : *
7011 : * We include system catalogs, so that we can work if a user table is
7012 : * defined to inherit from a system catalog (pretty weird, but...)
7013 : *
7014 : * Note: in this phase we should collect only a minimal amount of
7015 : * information about each table, basically just enough to decide if it is
7016 : * interesting. In particular, since we do not yet have lock on any user
7017 : * table, we MUST NOT invoke any server-side data collection functions
7018 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7019 : * wrong answers if any concurrent DDL is happening.
7020 : */
7021 :
7022 470 : appendPQExpBufferStr(query,
7023 : "SELECT c.tableoid, c.oid, c.relname, "
7024 : "c.relnamespace, c.relkind, c.reltype, "
7025 : "c.relowner, "
7026 : "c.relchecks, "
7027 : "c.relhasindex, c.relhasrules, c.relpages, "
7028 : "c.reltuples, c.relallvisible, ");
7029 :
7030 470 : if (fout->remoteVersion >= 180000)
7031 470 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7032 : else
7033 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7034 :
7035 470 : appendPQExpBufferStr(query,
7036 : "c.relhastriggers, c.relpersistence, "
7037 : "c.reloftype, "
7038 : "c.relacl, "
7039 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7040 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7041 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7042 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7043 : "ELSE 0 END AS foreignserver, "
7044 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7045 : "tc.oid AS toid, "
7046 : "tc.relpages AS toastpages, "
7047 : "tc.reloptions AS toast_reloptions, "
7048 : "d.refobjid AS owning_tab, "
7049 : "d.refobjsubid AS owning_col, "
7050 : "tsp.spcname AS reltablespace, ");
7051 :
7052 470 : if (fout->remoteVersion >= 120000)
7053 470 : appendPQExpBufferStr(query,
7054 : "false AS relhasoids, ");
7055 : else
7056 0 : appendPQExpBufferStr(query,
7057 : "c.relhasoids, ");
7058 :
7059 470 : if (fout->remoteVersion >= 90300)
7060 470 : appendPQExpBufferStr(query,
7061 : "c.relispopulated, ");
7062 : else
7063 0 : appendPQExpBufferStr(query,
7064 : "'t' as relispopulated, ");
7065 :
7066 470 : if (fout->remoteVersion >= 90400)
7067 470 : appendPQExpBufferStr(query,
7068 : "c.relreplident, ");
7069 : else
7070 0 : appendPQExpBufferStr(query,
7071 : "'d' AS relreplident, ");
7072 :
7073 470 : if (fout->remoteVersion >= 90500)
7074 470 : appendPQExpBufferStr(query,
7075 : "c.relrowsecurity, c.relforcerowsecurity, ");
7076 : else
7077 0 : appendPQExpBufferStr(query,
7078 : "false AS relrowsecurity, "
7079 : "false AS relforcerowsecurity, ");
7080 :
7081 470 : if (fout->remoteVersion >= 90300)
7082 470 : appendPQExpBufferStr(query,
7083 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7084 : else
7085 0 : appendPQExpBufferStr(query,
7086 : "0 AS relminmxid, 0 AS tminmxid, ");
7087 :
7088 470 : if (fout->remoteVersion >= 90300)
7089 470 : appendPQExpBufferStr(query,
7090 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7091 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7092 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7093 : else
7094 0 : appendPQExpBufferStr(query,
7095 : "c.reloptions, NULL AS checkoption, ");
7096 :
7097 470 : if (fout->remoteVersion >= 90600)
7098 470 : appendPQExpBufferStr(query,
7099 : "am.amname, ");
7100 : else
7101 0 : appendPQExpBufferStr(query,
7102 : "NULL AS amname, ");
7103 :
7104 470 : if (fout->remoteVersion >= 90600)
7105 470 : appendPQExpBufferStr(query,
7106 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7107 : else
7108 0 : appendPQExpBufferStr(query,
7109 : "false AS is_identity_sequence, ");
7110 :
7111 470 : if (fout->remoteVersion >= 100000)
7112 470 : appendPQExpBufferStr(query,
7113 : "c.relispartition AS ispartition ");
7114 : else
7115 0 : appendPQExpBufferStr(query,
7116 : "false AS ispartition ");
7117 :
7118 : /*
7119 : * Left join to pg_depend to pick up dependency info linking sequences to
7120 : * their owning column, if any (note this dependency is AUTO except for
7121 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7122 : * collect the spcname.
7123 : */
7124 470 : appendPQExpBufferStr(query,
7125 : "\nFROM pg_class c\n"
7126 : "LEFT JOIN pg_depend d ON "
7127 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7128 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7129 : "d.objsubid = 0 AND "
7130 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7131 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7132 :
7133 : /*
7134 : * In 9.6 and up, left join to pg_am to pick up the amname.
7135 : */
7136 470 : if (fout->remoteVersion >= 90600)
7137 470 : appendPQExpBufferStr(query,
7138 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7139 :
7140 : /*
7141 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7142 : * that versions 10 and 11 have them, but later versions do not, so
7143 : * emitting them causes the upgrade to fail.
7144 : */
7145 470 : appendPQExpBufferStr(query,
7146 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7147 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7148 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7149 :
7150 : /*
7151 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7152 : * relkinds are possible in older servers, but it's not worth the trouble
7153 : * to emit a version-dependent list.
7154 : *
7155 : * Composite-type table entries won't be dumped as such, but we have to
7156 : * make a DumpableObject for them so that we can track dependencies of the
7157 : * composite type (pg_depend entries for columns of the composite type
7158 : * link to the pg_class entry not the pg_type entry).
7159 : */
7160 470 : appendPQExpBufferStr(query,
7161 : "WHERE c.relkind IN ("
7162 : CppAsString2(RELKIND_RELATION) ", "
7163 : CppAsString2(RELKIND_SEQUENCE) ", "
7164 : CppAsString2(RELKIND_VIEW) ", "
7165 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7166 : CppAsString2(RELKIND_MATVIEW) ", "
7167 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7168 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7169 : "ORDER BY c.oid");
7170 :
7171 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7172 :
7173 470 : ntups = PQntuples(res);
7174 :
7175 470 : *numTables = ntups;
7176 :
7177 : /*
7178 : * Extract data from result and lock dumpable tables. We do the locking
7179 : * before anything else, to minimize the window wherein a table could
7180 : * disappear under us.
7181 : *
7182 : * Note that we have to save info about all tables here, even when dumping
7183 : * only one, because we don't yet know which tables might be inheritance
7184 : * ancestors of the target table.
7185 : */
7186 470 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7187 :
7188 470 : i_reltableoid = PQfnumber(res, "tableoid");
7189 470 : i_reloid = PQfnumber(res, "oid");
7190 470 : i_relname = PQfnumber(res, "relname");
7191 470 : i_relnamespace = PQfnumber(res, "relnamespace");
7192 470 : i_relkind = PQfnumber(res, "relkind");
7193 470 : i_reltype = PQfnumber(res, "reltype");
7194 470 : i_relowner = PQfnumber(res, "relowner");
7195 470 : i_relchecks = PQfnumber(res, "relchecks");
7196 470 : i_relhasindex = PQfnumber(res, "relhasindex");
7197 470 : i_relhasrules = PQfnumber(res, "relhasrules");
7198 470 : i_relpages = PQfnumber(res, "relpages");
7199 470 : i_reltuples = PQfnumber(res, "reltuples");
7200 470 : i_relallvisible = PQfnumber(res, "relallvisible");
7201 470 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7202 470 : i_toastpages = PQfnumber(res, "toastpages");
7203 470 : i_owning_tab = PQfnumber(res, "owning_tab");
7204 470 : i_owning_col = PQfnumber(res, "owning_col");
7205 470 : i_reltablespace = PQfnumber(res, "reltablespace");
7206 470 : i_relhasoids = PQfnumber(res, "relhasoids");
7207 470 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7208 470 : i_relpersistence = PQfnumber(res, "relpersistence");
7209 470 : i_relispopulated = PQfnumber(res, "relispopulated");
7210 470 : i_relreplident = PQfnumber(res, "relreplident");
7211 470 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7212 470 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7213 470 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7214 470 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7215 470 : i_toastoid = PQfnumber(res, "toid");
7216 470 : i_relminmxid = PQfnumber(res, "relminmxid");
7217 470 : i_toastminmxid = PQfnumber(res, "tminmxid");
7218 470 : i_reloptions = PQfnumber(res, "reloptions");
7219 470 : i_checkoption = PQfnumber(res, "checkoption");
7220 470 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7221 470 : i_reloftype = PQfnumber(res, "reloftype");
7222 470 : i_foreignserver = PQfnumber(res, "foreignserver");
7223 470 : i_amname = PQfnumber(res, "amname");
7224 470 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7225 470 : i_relacl = PQfnumber(res, "relacl");
7226 470 : i_acldefault = PQfnumber(res, "acldefault");
7227 470 : i_ispartition = PQfnumber(res, "ispartition");
7228 :
7229 470 : if (dopt->lockWaitTimeout)
7230 : {
7231 : /*
7232 : * Arrange to fail instead of waiting forever for a table lock.
7233 : *
7234 : * NB: this coding assumes that the only queries issued within the
7235 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7236 : * applied to other things too.
7237 : */
7238 4 : resetPQExpBuffer(query);
7239 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7240 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7241 4 : ExecuteSqlStatement(fout, query->data);
7242 : }
7243 :
7244 470 : resetPQExpBuffer(query);
7245 :
7246 124210 : for (i = 0; i < ntups; i++)
7247 : {
7248 123740 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7249 123740 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7250 :
7251 123740 : tblinfo[i].dobj.objType = DO_TABLE;
7252 123740 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7253 123740 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7254 123740 : AssignDumpId(&tblinfo[i].dobj);
7255 123740 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7256 247480 : tblinfo[i].dobj.namespace =
7257 123740 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7258 123740 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7259 123740 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7260 123740 : tblinfo[i].dacl.privtype = 0;
7261 123740 : tblinfo[i].dacl.initprivs = NULL;
7262 123740 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7263 123740 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7264 123740 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7265 123740 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7266 123740 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7267 123740 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7268 123740 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7269 123740 : if (PQgetisnull(res, i, i_toastpages))
7270 98762 : tblinfo[i].toastpages = 0;
7271 : else
7272 24978 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7273 123740 : if (PQgetisnull(res, i, i_owning_tab))
7274 : {
7275 122670 : tblinfo[i].owning_tab = InvalidOid;
7276 122670 : tblinfo[i].owning_col = 0;
7277 : }
7278 : else
7279 : {
7280 1070 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7281 1070 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7282 : }
7283 123740 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7284 123740 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7285 123740 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7286 123740 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7287 123740 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7288 123740 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7289 123740 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7290 123740 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7291 123740 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7292 123740 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7293 123740 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7294 123740 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7295 123740 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7296 123740 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7297 123740 : if (PQgetisnull(res, i, i_checkoption))
7298 123640 : tblinfo[i].checkoption = NULL;
7299 : else
7300 100 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7301 123740 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7302 123740 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7303 123740 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7304 123740 : if (PQgetisnull(res, i, i_amname))
7305 73434 : tblinfo[i].amname = NULL;
7306 : else
7307 50306 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7308 123740 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7309 123740 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7310 :
7311 : /* other fields were zeroed above */
7312 :
7313 : /*
7314 : * Decide whether we want to dump this table.
7315 : */
7316 123740 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7317 470 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7318 : else
7319 123270 : selectDumpableTable(&tblinfo[i], fout);
7320 :
7321 : /*
7322 : * Now, consider the table "interesting" if we need to dump its
7323 : * definition, data or its statistics. Later on, we'll skip a lot of
7324 : * data collection for uninteresting tables.
7325 : *
7326 : * Note: the "interesting" flag will also be set by flagInhTables for
7327 : * parents of interesting tables, so that we collect necessary
7328 : * inheritance info even when the parents are not themselves being
7329 : * dumped. This is the main reason why we need an "interesting" flag
7330 : * that's separate from the components-to-dump bitmask.
7331 : */
7332 123740 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7333 : (DUMP_COMPONENT_DEFINITION |
7334 : DUMP_COMPONENT_DATA |
7335 123740 : DUMP_COMPONENT_STATISTICS)) != 0;
7336 :
7337 123740 : tblinfo[i].dummy_view = false; /* might get set during sort */
7338 123740 : tblinfo[i].postponed_def = false; /* might get set during sort */
7339 :
7340 : /* Tables have data */
7341 123740 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7342 :
7343 : /* Mark whether table has an ACL */
7344 123740 : if (!PQgetisnull(res, i, i_relacl))
7345 98086 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7346 123740 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7347 :
7348 : /* Add statistics */
7349 123740 : if (tblinfo[i].interesting)
7350 : {
7351 : RelStatsInfo *stats;
7352 :
7353 38756 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7354 19378 : tblinfo[i].relpages,
7355 : PQgetvalue(res, i, i_reltuples),
7356 : relallvisible, relallfrozen,
7357 19378 : tblinfo[i].relkind, NULL, 0);
7358 19378 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7359 988 : tblinfo[i].stats = stats;
7360 : }
7361 :
7362 : /*
7363 : * Read-lock target tables to make sure they aren't DROPPED or altered
7364 : * in schema before we get around to dumping them.
7365 : *
7366 : * Note that we don't explicitly lock parents of the target tables; we
7367 : * assume our lock on the child is enough to prevent schema
7368 : * alterations to parent tables.
7369 : *
7370 : * NOTE: it'd be kinda nice to lock other relations too, not only
7371 : * plain or partitioned tables, but the backend doesn't presently
7372 : * allow that.
7373 : *
7374 : * We only need to lock the table for certain components; see
7375 : * pg_dump.h
7376 : */
7377 123740 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7378 19378 : (tblinfo[i].relkind == RELKIND_RELATION ||
7379 5266 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7380 : {
7381 : /*
7382 : * Tables are locked in batches. When dumping from a remote
7383 : * server this can save a significant amount of time by reducing
7384 : * the number of round trips.
7385 : */
7386 15770 : if (query->len == 0)
7387 316 : appendPQExpBuffer(query, "LOCK TABLE %s",
7388 316 : fmtQualifiedDumpable(&tblinfo[i]));
7389 : else
7390 : {
7391 15454 : appendPQExpBuffer(query, ", %s",
7392 15454 : fmtQualifiedDumpable(&tblinfo[i]));
7393 :
7394 : /* Arbitrarily end a batch when query length reaches 100K. */
7395 15454 : if (query->len >= 100000)
7396 : {
7397 : /* Lock another batch of tables. */
7398 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7399 0 : ExecuteSqlStatement(fout, query->data);
7400 0 : resetPQExpBuffer(query);
7401 : }
7402 : }
7403 : }
7404 : }
7405 :
7406 470 : if (query->len != 0)
7407 : {
7408 : /* Lock the tables in the last batch. */
7409 316 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7410 316 : ExecuteSqlStatement(fout, query->data);
7411 : }
7412 :
7413 468 : if (dopt->lockWaitTimeout)
7414 : {
7415 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7416 : }
7417 :
7418 468 : PQclear(res);
7419 :
7420 468 : destroyPQExpBuffer(query);
7421 :
7422 468 : return tblinfo;
7423 : }
7424 :
7425 : /*
7426 : * getOwnedSeqs
7427 : * identify owned sequences and mark them as dumpable if owning table is
7428 : *
7429 : * We used to do this in getTables(), but it's better to do it after the
7430 : * index used by findTableByOid() has been set up.
7431 : */
7432 : void
7433 468 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7434 : {
7435 : int i;
7436 :
7437 : /*
7438 : * Force sequences that are "owned" by table columns to be dumped whenever
7439 : * their owning table is being dumped.
7440 : */
7441 123672 : for (i = 0; i < numTables; i++)
7442 : {
7443 123204 : TableInfo *seqinfo = &tblinfo[i];
7444 : TableInfo *owning_tab;
7445 :
7446 123204 : if (!OidIsValid(seqinfo->owning_tab))
7447 122140 : continue; /* not an owned sequence */
7448 :
7449 1064 : owning_tab = findTableByOid(seqinfo->owning_tab);
7450 1064 : if (owning_tab == NULL)
7451 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7452 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7453 :
7454 : /*
7455 : * For an identity sequence, dump exactly the same components for the
7456 : * sequence as for the owning table. This is important because we
7457 : * treat the identity sequence as an integral part of the table. For
7458 : * example, there is not any DDL command that allows creation of such
7459 : * a sequence independently of the table.
7460 : *
7461 : * For other owned sequences such as serial sequences, we need to dump
7462 : * the components that are being dumped for the table and any
7463 : * components that the sequence is explicitly marked with.
7464 : *
7465 : * We can't simply use the set of components which are being dumped
7466 : * for the table as the table might be in an extension (and only the
7467 : * non-extension components, eg: ACLs if changed, security labels, and
7468 : * policies, are being dumped) while the sequence is not (and
7469 : * therefore the definition and other components should also be
7470 : * dumped).
7471 : *
7472 : * If the sequence is part of the extension then it should be properly
7473 : * marked by checkExtensionMembership() and this will be a no-op as
7474 : * the table will be equivalently marked.
7475 : */
7476 1064 : if (seqinfo->is_identity_sequence)
7477 538 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7478 : else
7479 526 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7480 :
7481 : /* Make sure that necessary data is available if we're dumping it */
7482 1064 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7483 : {
7484 872 : seqinfo->interesting = true;
7485 872 : owning_tab->interesting = true;
7486 : }
7487 : }
7488 468 : }
7489 :
7490 : /*
7491 : * getInherits
7492 : * read all the inheritance information
7493 : * from the system catalogs return them in the InhInfo* structure
7494 : *
7495 : * numInherits is set to the number of pairs read in
7496 : */
7497 : InhInfo *
7498 468 : getInherits(Archive *fout, int *numInherits)
7499 : {
7500 : PGresult *res;
7501 : int ntups;
7502 : int i;
7503 468 : PQExpBuffer query = createPQExpBuffer();
7504 : InhInfo *inhinfo;
7505 :
7506 : int i_inhrelid;
7507 : int i_inhparent;
7508 :
7509 : /* find all the inheritance information */
7510 468 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7511 :
7512 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7513 :
7514 468 : ntups = PQntuples(res);
7515 :
7516 468 : *numInherits = ntups;
7517 :
7518 468 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7519 :
7520 468 : i_inhrelid = PQfnumber(res, "inhrelid");
7521 468 : i_inhparent = PQfnumber(res, "inhparent");
7522 :
7523 9066 : for (i = 0; i < ntups; i++)
7524 : {
7525 8598 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7526 8598 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7527 : }
7528 :
7529 468 : PQclear(res);
7530 :
7531 468 : destroyPQExpBuffer(query);
7532 :
7533 468 : return inhinfo;
7534 : }
7535 :
7536 : /*
7537 : * getPartitioningInfo
7538 : * get information about partitioning
7539 : *
7540 : * For the most part, we only collect partitioning info about tables we
7541 : * intend to dump. However, this function has to consider all partitioned
7542 : * tables in the database, because we need to know about parents of partitions
7543 : * we are going to dump even if the parents themselves won't be dumped.
7544 : *
7545 : * Specifically, what we need to know is whether each partitioned table
7546 : * has an "unsafe" partitioning scheme that requires us to force
7547 : * load-via-partition-root mode for its children. Currently the only case
7548 : * for which we force that is hash partitioning on enum columns, since the
7549 : * hash codes depend on enum value OIDs which won't be replicated across
7550 : * dump-and-reload. There are other cases in which load-via-partition-root
7551 : * might be necessary, but we expect users to cope with them.
7552 : */
7553 : void
7554 468 : getPartitioningInfo(Archive *fout)
7555 : {
7556 : PQExpBuffer query;
7557 : PGresult *res;
7558 : int ntups;
7559 :
7560 : /* hash partitioning didn't exist before v11 */
7561 468 : if (fout->remoteVersion < 110000)
7562 0 : return;
7563 : /* needn't bother if not dumping data */
7564 468 : if (!fout->dopt->dumpData)
7565 72 : return;
7566 :
7567 396 : query = createPQExpBuffer();
7568 :
7569 : /*
7570 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7571 : * appears among the partition opclasses. We needn't check partstrat.
7572 : *
7573 : * Note that this query may well retrieve info about tables we aren't
7574 : * going to dump and hence have no lock on. That's okay since we need not
7575 : * invoke any unsafe server-side functions.
7576 : */
7577 396 : appendPQExpBufferStr(query,
7578 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7579 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7580 : "ON c.opcmethod = a.oid\n"
7581 : "WHERE opcname = 'enum_ops' "
7582 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7583 : "AND amname = 'hash') = ANY(partclass)");
7584 :
7585 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7586 :
7587 396 : ntups = PQntuples(res);
7588 :
7589 400 : for (int i = 0; i < ntups; i++)
7590 : {
7591 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7592 : TableInfo *tbinfo;
7593 :
7594 4 : tbinfo = findTableByOid(tabrelid);
7595 4 : if (tbinfo == NULL)
7596 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7597 : tabrelid);
7598 4 : tbinfo->unsafe_partitions = true;
7599 : }
7600 :
7601 396 : PQclear(res);
7602 :
7603 396 : destroyPQExpBuffer(query);
7604 : }
7605 :
7606 : /*
7607 : * getIndexes
7608 : * get information about every index on a dumpable table
7609 : *
7610 : * Note: index data is not returned directly to the caller, but it
7611 : * does get entered into the DumpableObject tables.
7612 : */
7613 : void
7614 468 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7615 : {
7616 468 : PQExpBuffer query = createPQExpBuffer();
7617 468 : PQExpBuffer tbloids = createPQExpBuffer();
7618 : PGresult *res;
7619 : int ntups;
7620 : int curtblindx;
7621 : IndxInfo *indxinfo;
7622 : int i_tableoid,
7623 : i_oid,
7624 : i_indrelid,
7625 : i_indexname,
7626 : i_relpages,
7627 : i_reltuples,
7628 : i_relallvisible,
7629 : i_relallfrozen,
7630 : i_parentidx,
7631 : i_indexdef,
7632 : i_indnkeyatts,
7633 : i_indnatts,
7634 : i_indkey,
7635 : i_indisclustered,
7636 : i_indisreplident,
7637 : i_indnullsnotdistinct,
7638 : i_contype,
7639 : i_conname,
7640 : i_condeferrable,
7641 : i_condeferred,
7642 : i_conperiod,
7643 : i_contableoid,
7644 : i_conoid,
7645 : i_condef,
7646 : i_indattnames,
7647 : i_tablespace,
7648 : i_indreloptions,
7649 : i_indstatcols,
7650 : i_indstatvals;
7651 :
7652 : /*
7653 : * We want to perform just one query against pg_index. However, we
7654 : * mustn't try to select every row of the catalog and then sort it out on
7655 : * the client side, because some of the server-side functions we need
7656 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7657 : * build an array of the OIDs of tables we care about (and now have lock
7658 : * on!), and use a WHERE clause to constrain which rows are selected.
7659 : */
7660 468 : appendPQExpBufferChar(tbloids, '{');
7661 123672 : for (int i = 0; i < numTables; i++)
7662 : {
7663 123204 : TableInfo *tbinfo = &tblinfo[i];
7664 :
7665 123204 : if (!tbinfo->hasindex)
7666 86990 : continue;
7667 :
7668 : /*
7669 : * We can ignore indexes of uninteresting tables.
7670 : */
7671 36214 : if (!tbinfo->interesting)
7672 30714 : continue;
7673 :
7674 : /* OK, we need info for this table */
7675 5500 : if (tbloids->len > 1) /* do we have more than the '{'? */
7676 5330 : appendPQExpBufferChar(tbloids, ',');
7677 5500 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7678 : }
7679 468 : appendPQExpBufferChar(tbloids, '}');
7680 :
7681 468 : appendPQExpBufferStr(query,
7682 : "SELECT t.tableoid, t.oid, i.indrelid, "
7683 : "t.relname AS indexname, "
7684 : "t.relpages, t.reltuples, t.relallvisible, ");
7685 :
7686 468 : if (fout->remoteVersion >= 180000)
7687 468 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7688 : else
7689 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7690 :
7691 468 : appendPQExpBufferStr(query,
7692 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7693 : "i.indkey, i.indisclustered, "
7694 : "c.contype, c.conname, "
7695 : "c.condeferrable, c.condeferred, "
7696 : "c.tableoid AS contableoid, "
7697 : "c.oid AS conoid, "
7698 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7699 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7700 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7701 : " FROM pg_catalog.pg_attribute "
7702 : " WHERE attrelid = i.indexrelid) "
7703 : "ELSE NULL END AS indattnames, "
7704 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7705 : "t.reloptions AS indreloptions, ");
7706 :
7707 :
7708 468 : if (fout->remoteVersion >= 90400)
7709 468 : appendPQExpBufferStr(query,
7710 : "i.indisreplident, ");
7711 : else
7712 0 : appendPQExpBufferStr(query,
7713 : "false AS indisreplident, ");
7714 :
7715 468 : if (fout->remoteVersion >= 110000)
7716 468 : appendPQExpBufferStr(query,
7717 : "inh.inhparent AS parentidx, "
7718 : "i.indnkeyatts AS indnkeyatts, "
7719 : "i.indnatts AS indnatts, "
7720 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7721 : " FROM pg_catalog.pg_attribute "
7722 : " WHERE attrelid = i.indexrelid AND "
7723 : " attstattarget >= 0) AS indstatcols, "
7724 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7725 : " FROM pg_catalog.pg_attribute "
7726 : " WHERE attrelid = i.indexrelid AND "
7727 : " attstattarget >= 0) AS indstatvals, ");
7728 : else
7729 0 : appendPQExpBufferStr(query,
7730 : "0 AS parentidx, "
7731 : "i.indnatts AS indnkeyatts, "
7732 : "i.indnatts AS indnatts, "
7733 : "'' AS indstatcols, "
7734 : "'' AS indstatvals, ");
7735 :
7736 468 : if (fout->remoteVersion >= 150000)
7737 468 : appendPQExpBufferStr(query,
7738 : "i.indnullsnotdistinct, ");
7739 : else
7740 0 : appendPQExpBufferStr(query,
7741 : "false AS indnullsnotdistinct, ");
7742 :
7743 468 : if (fout->remoteVersion >= 180000)
7744 468 : appendPQExpBufferStr(query,
7745 : "c.conperiod ");
7746 : else
7747 0 : appendPQExpBufferStr(query,
7748 : "NULL AS conperiod ");
7749 :
7750 : /*
7751 : * The point of the messy-looking outer join is to find a constraint that
7752 : * is related by an internal dependency link to the index. If we find one,
7753 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7754 : * index won't have more than one internal dependency.
7755 : *
7756 : * Note: the check on conrelid is redundant, but useful because that
7757 : * column is indexed while conindid is not.
7758 : */
7759 468 : if (fout->remoteVersion >= 110000)
7760 : {
7761 468 : appendPQExpBuffer(query,
7762 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7763 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7764 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7765 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7766 : "LEFT JOIN pg_catalog.pg_constraint c "
7767 : "ON (i.indrelid = c.conrelid AND "
7768 : "i.indexrelid = c.conindid AND "
7769 : "c.contype IN ('p','u','x')) "
7770 : "LEFT JOIN pg_catalog.pg_inherits inh "
7771 : "ON (inh.inhrelid = indexrelid) "
7772 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7773 : "AND i.indisready "
7774 : "ORDER BY i.indrelid, indexname",
7775 : tbloids->data);
7776 : }
7777 : else
7778 : {
7779 : /*
7780 : * the test on indisready is necessary in 9.2, and harmless in
7781 : * earlier/later versions
7782 : */
7783 0 : appendPQExpBuffer(query,
7784 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7785 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7786 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7787 : "LEFT JOIN pg_catalog.pg_constraint c "
7788 : "ON (i.indrelid = c.conrelid AND "
7789 : "i.indexrelid = c.conindid AND "
7790 : "c.contype IN ('p','u','x')) "
7791 : "WHERE i.indisvalid AND i.indisready "
7792 : "ORDER BY i.indrelid, indexname",
7793 : tbloids->data);
7794 : }
7795 :
7796 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7797 :
7798 468 : ntups = PQntuples(res);
7799 :
7800 468 : i_tableoid = PQfnumber(res, "tableoid");
7801 468 : i_oid = PQfnumber(res, "oid");
7802 468 : i_indrelid = PQfnumber(res, "indrelid");
7803 468 : i_indexname = PQfnumber(res, "indexname");
7804 468 : i_relpages = PQfnumber(res, "relpages");
7805 468 : i_reltuples = PQfnumber(res, "reltuples");
7806 468 : i_relallvisible = PQfnumber(res, "relallvisible");
7807 468 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7808 468 : i_parentidx = PQfnumber(res, "parentidx");
7809 468 : i_indexdef = PQfnumber(res, "indexdef");
7810 468 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7811 468 : i_indnatts = PQfnumber(res, "indnatts");
7812 468 : i_indkey = PQfnumber(res, "indkey");
7813 468 : i_indisclustered = PQfnumber(res, "indisclustered");
7814 468 : i_indisreplident = PQfnumber(res, "indisreplident");
7815 468 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7816 468 : i_contype = PQfnumber(res, "contype");
7817 468 : i_conname = PQfnumber(res, "conname");
7818 468 : i_condeferrable = PQfnumber(res, "condeferrable");
7819 468 : i_condeferred = PQfnumber(res, "condeferred");
7820 468 : i_conperiod = PQfnumber(res, "conperiod");
7821 468 : i_contableoid = PQfnumber(res, "contableoid");
7822 468 : i_conoid = PQfnumber(res, "conoid");
7823 468 : i_condef = PQfnumber(res, "condef");
7824 468 : i_indattnames = PQfnumber(res, "indattnames");
7825 468 : i_tablespace = PQfnumber(res, "tablespace");
7826 468 : i_indreloptions = PQfnumber(res, "indreloptions");
7827 468 : i_indstatcols = PQfnumber(res, "indstatcols");
7828 468 : i_indstatvals = PQfnumber(res, "indstatvals");
7829 :
7830 468 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7831 :
7832 : /*
7833 : * Outer loop iterates once per table, not once per row. Incrementing of
7834 : * j is handled by the inner loop.
7835 : */
7836 468 : curtblindx = -1;
7837 5908 : for (int j = 0; j < ntups;)
7838 : {
7839 5440 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7840 5440 : TableInfo *tbinfo = NULL;
7841 5440 : char **indAttNames = NULL;
7842 5440 : int nindAttNames = 0;
7843 : int numinds;
7844 :
7845 : /* Count rows for this table */
7846 6956 : for (numinds = 1; numinds < ntups - j; numinds++)
7847 6786 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7848 5270 : break;
7849 :
7850 : /*
7851 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7852 : * order.
7853 : */
7854 54258 : while (++curtblindx < numTables)
7855 : {
7856 54258 : tbinfo = &tblinfo[curtblindx];
7857 54258 : if (tbinfo->dobj.catId.oid == indrelid)
7858 5440 : break;
7859 : }
7860 5440 : if (curtblindx >= numTables)
7861 0 : pg_fatal("unrecognized table OID %u", indrelid);
7862 : /* cross-check that we only got requested tables */
7863 5440 : if (!tbinfo->hasindex ||
7864 5440 : !tbinfo->interesting)
7865 0 : pg_fatal("unexpected index data for table \"%s\"",
7866 : tbinfo->dobj.name);
7867 :
7868 : /* Save data for this table */
7869 5440 : tbinfo->indexes = indxinfo + j;
7870 5440 : tbinfo->numIndexes = numinds;
7871 :
7872 12396 : for (int c = 0; c < numinds; c++, j++)
7873 : {
7874 : char contype;
7875 : char indexkind;
7876 : RelStatsInfo *relstats;
7877 6956 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
7878 6956 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
7879 6956 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
7880 :
7881 6956 : indxinfo[j].dobj.objType = DO_INDEX;
7882 6956 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7883 6956 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7884 6956 : AssignDumpId(&indxinfo[j].dobj);
7885 6956 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7886 6956 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7887 6956 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7888 6956 : indxinfo[j].indextable = tbinfo;
7889 6956 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7890 6956 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7891 6956 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7892 6956 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7893 6956 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7894 6956 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7895 6956 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7896 6956 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7897 6956 : parseOidArray(PQgetvalue(res, j, i_indkey),
7898 6956 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7899 6956 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7900 6956 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7901 6956 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7902 6956 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7903 6956 : indxinfo[j].partattaches = (SimplePtrList)
7904 : {
7905 : NULL, NULL
7906 : };
7907 :
7908 6956 : if (indxinfo[j].parentidx == 0)
7909 5476 : indexkind = RELKIND_INDEX;
7910 : else
7911 1480 : indexkind = RELKIND_PARTITIONED_INDEX;
7912 :
7913 6956 : if (!PQgetisnull(res, j, i_indattnames))
7914 : {
7915 392 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
7916 : &indAttNames, &nindAttNames))
7917 0 : pg_fatal("could not parse %s array", "indattnames");
7918 : }
7919 :
7920 6956 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
7921 : PQgetvalue(res, j, i_reltuples),
7922 : relallvisible, relallfrozen, indexkind,
7923 : indAttNames, nindAttNames);
7924 :
7925 6956 : contype = *(PQgetvalue(res, j, i_contype));
7926 6956 : if (contype == 'p' || contype == 'u' || contype == 'x')
7927 3864 : {
7928 : /*
7929 : * If we found a constraint matching the index, create an
7930 : * entry for it.
7931 : */
7932 : ConstraintInfo *constrinfo;
7933 :
7934 3864 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7935 3864 : constrinfo->dobj.objType = DO_CONSTRAINT;
7936 3864 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7937 3864 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7938 3864 : AssignDumpId(&constrinfo->dobj);
7939 3864 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7940 3864 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7941 3864 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7942 3864 : constrinfo->contable = tbinfo;
7943 3864 : constrinfo->condomain = NULL;
7944 3864 : constrinfo->contype = contype;
7945 3864 : if (contype == 'x')
7946 32 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7947 : else
7948 3832 : constrinfo->condef = NULL;
7949 3864 : constrinfo->confrelid = InvalidOid;
7950 3864 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7951 3864 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7952 3864 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7953 3864 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7954 3864 : constrinfo->conislocal = true;
7955 3864 : constrinfo->separate = true;
7956 :
7957 3864 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7958 3864 : if (relstats != NULL)
7959 1144 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
7960 : }
7961 : else
7962 : {
7963 : /* Plain secondary index */
7964 3092 : indxinfo[j].indexconstraint = 0;
7965 : }
7966 : }
7967 : }
7968 :
7969 468 : PQclear(res);
7970 :
7971 468 : destroyPQExpBuffer(query);
7972 468 : destroyPQExpBuffer(tbloids);
7973 468 : }
7974 :
7975 : /*
7976 : * getExtendedStatistics
7977 : * get information about extended-statistics objects.
7978 : *
7979 : * Note: extended statistics data is not returned directly to the caller, but
7980 : * it does get entered into the DumpableObject tables.
7981 : */
7982 : void
7983 468 : getExtendedStatistics(Archive *fout)
7984 : {
7985 : PQExpBuffer query;
7986 : PGresult *res;
7987 : StatsExtInfo *statsextinfo;
7988 : int ntups;
7989 : int i_tableoid;
7990 : int i_oid;
7991 : int i_stxname;
7992 : int i_stxnamespace;
7993 : int i_stxowner;
7994 : int i_stxrelid;
7995 : int i_stattarget;
7996 : int i;
7997 :
7998 : /* Extended statistics were new in v10 */
7999 468 : if (fout->remoteVersion < 100000)
8000 0 : return;
8001 :
8002 468 : query = createPQExpBuffer();
8003 :
8004 468 : if (fout->remoteVersion < 130000)
8005 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8006 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8007 : "FROM pg_catalog.pg_statistic_ext");
8008 : else
8009 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8010 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8011 : "FROM pg_catalog.pg_statistic_ext");
8012 :
8013 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8014 :
8015 468 : ntups = PQntuples(res);
8016 :
8017 468 : i_tableoid = PQfnumber(res, "tableoid");
8018 468 : i_oid = PQfnumber(res, "oid");
8019 468 : i_stxname = PQfnumber(res, "stxname");
8020 468 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8021 468 : i_stxowner = PQfnumber(res, "stxowner");
8022 468 : i_stxrelid = PQfnumber(res, "stxrelid");
8023 468 : i_stattarget = PQfnumber(res, "stxstattarget");
8024 :
8025 468 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8026 :
8027 842 : for (i = 0; i < ntups; i++)
8028 : {
8029 374 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8030 374 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8031 374 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8032 374 : AssignDumpId(&statsextinfo[i].dobj);
8033 374 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8034 748 : statsextinfo[i].dobj.namespace =
8035 374 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8036 374 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8037 748 : statsextinfo[i].stattable =
8038 374 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8039 374 : if (PQgetisnull(res, i, i_stattarget))
8040 276 : statsextinfo[i].stattarget = -1;
8041 : else
8042 98 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8043 :
8044 : /* Decide whether we want to dump it */
8045 374 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8046 : }
8047 :
8048 468 : PQclear(res);
8049 468 : destroyPQExpBuffer(query);
8050 : }
8051 :
8052 : /*
8053 : * getConstraints
8054 : *
8055 : * Get info about constraints on dumpable tables.
8056 : *
8057 : * Currently handles foreign keys only.
8058 : * Unique and primary key constraints are handled with indexes,
8059 : * while check constraints are processed in getTableAttrs().
8060 : */
8061 : void
8062 468 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8063 : {
8064 468 : PQExpBuffer query = createPQExpBuffer();
8065 468 : PQExpBuffer tbloids = createPQExpBuffer();
8066 : PGresult *res;
8067 : int ntups;
8068 : int curtblindx;
8069 468 : TableInfo *tbinfo = NULL;
8070 : ConstraintInfo *constrinfo;
8071 : int i_contableoid,
8072 : i_conoid,
8073 : i_conrelid,
8074 : i_conname,
8075 : i_confrelid,
8076 : i_conindid,
8077 : i_condef;
8078 :
8079 : /*
8080 : * We want to perform just one query against pg_constraint. However, we
8081 : * mustn't try to select every row of the catalog and then sort it out on
8082 : * the client side, because some of the server-side functions we need
8083 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8084 : * build an array of the OIDs of tables we care about (and now have lock
8085 : * on!), and use a WHERE clause to constrain which rows are selected.
8086 : */
8087 468 : appendPQExpBufferChar(tbloids, '{');
8088 123672 : for (int i = 0; i < numTables; i++)
8089 : {
8090 123204 : TableInfo *tinfo = &tblinfo[i];
8091 :
8092 123204 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8093 103928 : continue;
8094 :
8095 : /* OK, we need info for this table */
8096 19276 : if (tbloids->len > 1) /* do we have more than the '{'? */
8097 18958 : appendPQExpBufferChar(tbloids, ',');
8098 19276 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8099 : }
8100 468 : appendPQExpBufferChar(tbloids, '}');
8101 :
8102 468 : appendPQExpBufferStr(query,
8103 : "SELECT c.tableoid, c.oid, "
8104 : "conrelid, conname, confrelid, ");
8105 468 : if (fout->remoteVersion >= 110000)
8106 468 : appendPQExpBufferStr(query, "conindid, ");
8107 : else
8108 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8109 468 : appendPQExpBuffer(query,
8110 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8111 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8112 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8113 : "WHERE contype = 'f' ",
8114 : tbloids->data);
8115 468 : if (fout->remoteVersion >= 110000)
8116 468 : appendPQExpBufferStr(query,
8117 : "AND conparentid = 0 ");
8118 468 : appendPQExpBufferStr(query,
8119 : "ORDER BY conrelid, conname");
8120 :
8121 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8122 :
8123 468 : ntups = PQntuples(res);
8124 :
8125 468 : i_contableoid = PQfnumber(res, "tableoid");
8126 468 : i_conoid = PQfnumber(res, "oid");
8127 468 : i_conrelid = PQfnumber(res, "conrelid");
8128 468 : i_conname = PQfnumber(res, "conname");
8129 468 : i_confrelid = PQfnumber(res, "confrelid");
8130 468 : i_conindid = PQfnumber(res, "conindid");
8131 468 : i_condef = PQfnumber(res, "condef");
8132 :
8133 468 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8134 :
8135 468 : curtblindx = -1;
8136 940 : for (int j = 0; j < ntups; j++)
8137 : {
8138 472 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8139 : TableInfo *reftable;
8140 :
8141 : /*
8142 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8143 : * order.
8144 : */
8145 472 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8146 : {
8147 34210 : while (++curtblindx < numTables)
8148 : {
8149 34210 : tbinfo = &tblinfo[curtblindx];
8150 34210 : if (tbinfo->dobj.catId.oid == conrelid)
8151 440 : break;
8152 : }
8153 440 : if (curtblindx >= numTables)
8154 0 : pg_fatal("unrecognized table OID %u", conrelid);
8155 : }
8156 :
8157 472 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8158 472 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8159 472 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8160 472 : AssignDumpId(&constrinfo[j].dobj);
8161 472 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8162 472 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8163 472 : constrinfo[j].contable = tbinfo;
8164 472 : constrinfo[j].condomain = NULL;
8165 472 : constrinfo[j].contype = 'f';
8166 472 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8167 472 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8168 472 : constrinfo[j].conindex = 0;
8169 472 : constrinfo[j].condeferrable = false;
8170 472 : constrinfo[j].condeferred = false;
8171 472 : constrinfo[j].conislocal = true;
8172 472 : constrinfo[j].separate = true;
8173 :
8174 : /*
8175 : * Restoring an FK that points to a partitioned table requires that
8176 : * all partition indexes have been attached beforehand. Ensure that
8177 : * happens by making the constraint depend on each index partition
8178 : * attach object.
8179 : */
8180 472 : reftable = findTableByOid(constrinfo[j].confrelid);
8181 472 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8182 : {
8183 64 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8184 :
8185 64 : if (indexOid != InvalidOid)
8186 : {
8187 64 : for (int k = 0; k < reftable->numIndexes; k++)
8188 : {
8189 : IndxInfo *refidx;
8190 :
8191 : /* not our index? */
8192 64 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8193 0 : continue;
8194 :
8195 64 : refidx = &reftable->indexes[k];
8196 64 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8197 64 : break;
8198 : }
8199 : }
8200 : }
8201 : }
8202 :
8203 468 : PQclear(res);
8204 :
8205 468 : destroyPQExpBuffer(query);
8206 468 : destroyPQExpBuffer(tbloids);
8207 468 : }
8208 :
8209 : /*
8210 : * addConstrChildIdxDeps
8211 : *
8212 : * Recursive subroutine for getConstraints
8213 : *
8214 : * Given an object representing a foreign key constraint and an index on the
8215 : * partitioned table it references, mark the constraint object as dependent
8216 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8217 : * drilling down to their partitions if any. This ensures that the FK is not
8218 : * restored until the index is fully marked valid.
8219 : */
8220 : static void
8221 144 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8222 : {
8223 : SimplePtrListCell *cell;
8224 :
8225 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8226 :
8227 496 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8228 : {
8229 352 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8230 :
8231 352 : addObjectDependency(dobj, attach->dobj.dumpId);
8232 :
8233 352 : if (attach->partitionIdx->partattaches.head != NULL)
8234 80 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8235 : }
8236 144 : }
8237 :
8238 : /*
8239 : * getDomainConstraints
8240 : *
8241 : * Get info about constraints on a domain.
8242 : */
8243 : static void
8244 452 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8245 : {
8246 : int i;
8247 : ConstraintInfo *constrinfo;
8248 452 : PQExpBuffer query = createPQExpBuffer();
8249 : PGresult *res;
8250 : int i_tableoid,
8251 : i_oid,
8252 : i_conname,
8253 : i_consrc;
8254 : int ntups;
8255 :
8256 452 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8257 : {
8258 : /* Set up query for constraint-specific details */
8259 100 : appendPQExpBufferStr(query,
8260 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8261 : "SELECT tableoid, oid, conname, "
8262 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8263 : "convalidated "
8264 : "FROM pg_catalog.pg_constraint "
8265 : "WHERE contypid = $1 AND contype = 'c' "
8266 : "ORDER BY conname");
8267 :
8268 100 : ExecuteSqlStatement(fout, query->data);
8269 :
8270 100 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8271 : }
8272 :
8273 452 : printfPQExpBuffer(query,
8274 : "EXECUTE getDomainConstraints('%u')",
8275 : tyinfo->dobj.catId.oid);
8276 :
8277 452 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8278 :
8279 452 : ntups = PQntuples(res);
8280 :
8281 452 : i_tableoid = PQfnumber(res, "tableoid");
8282 452 : i_oid = PQfnumber(res, "oid");
8283 452 : i_conname = PQfnumber(res, "conname");
8284 452 : i_consrc = PQfnumber(res, "consrc");
8285 :
8286 452 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8287 :
8288 452 : tyinfo->nDomChecks = ntups;
8289 452 : tyinfo->domChecks = constrinfo;
8290 :
8291 760 : for (i = 0; i < ntups; i++)
8292 : {
8293 308 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
8294 :
8295 308 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
8296 308 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8297 308 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8298 308 : AssignDumpId(&constrinfo[i].dobj);
8299 308 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8300 308 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8301 308 : constrinfo[i].contable = NULL;
8302 308 : constrinfo[i].condomain = tyinfo;
8303 308 : constrinfo[i].contype = 'c';
8304 308 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8305 308 : constrinfo[i].confrelid = InvalidOid;
8306 308 : constrinfo[i].conindex = 0;
8307 308 : constrinfo[i].condeferrable = false;
8308 308 : constrinfo[i].condeferred = false;
8309 308 : constrinfo[i].conislocal = true;
8310 :
8311 308 : constrinfo[i].separate = !validated;
8312 :
8313 : /*
8314 : * Make the domain depend on the constraint, ensuring it won't be
8315 : * output till any constraint dependencies are OK. If the constraint
8316 : * has not been validated, it's going to be dumped after the domain
8317 : * anyway, so this doesn't matter.
8318 : */
8319 308 : if (validated)
8320 308 : addObjectDependency(&tyinfo->dobj,
8321 308 : constrinfo[i].dobj.dumpId);
8322 : }
8323 :
8324 452 : PQclear(res);
8325 :
8326 452 : destroyPQExpBuffer(query);
8327 452 : }
8328 :
8329 : /*
8330 : * getRules
8331 : * get basic information about every rule in the system
8332 : */
8333 : void
8334 468 : getRules(Archive *fout)
8335 : {
8336 : PGresult *res;
8337 : int ntups;
8338 : int i;
8339 468 : PQExpBuffer query = createPQExpBuffer();
8340 : RuleInfo *ruleinfo;
8341 : int i_tableoid;
8342 : int i_oid;
8343 : int i_rulename;
8344 : int i_ruletable;
8345 : int i_ev_type;
8346 : int i_is_instead;
8347 : int i_ev_enabled;
8348 :
8349 468 : appendPQExpBufferStr(query, "SELECT "
8350 : "tableoid, oid, rulename, "
8351 : "ev_class AS ruletable, ev_type, is_instead, "
8352 : "ev_enabled "
8353 : "FROM pg_rewrite "
8354 : "ORDER BY oid");
8355 :
8356 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8357 :
8358 468 : ntups = PQntuples(res);
8359 :
8360 468 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8361 :
8362 468 : i_tableoid = PQfnumber(res, "tableoid");
8363 468 : i_oid = PQfnumber(res, "oid");
8364 468 : i_rulename = PQfnumber(res, "rulename");
8365 468 : i_ruletable = PQfnumber(res, "ruletable");
8366 468 : i_ev_type = PQfnumber(res, "ev_type");
8367 468 : i_is_instead = PQfnumber(res, "is_instead");
8368 468 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8369 :
8370 72302 : for (i = 0; i < ntups; i++)
8371 : {
8372 : Oid ruletableoid;
8373 :
8374 71834 : ruleinfo[i].dobj.objType = DO_RULE;
8375 71834 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8376 71834 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8377 71834 : AssignDumpId(&ruleinfo[i].dobj);
8378 71834 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8379 71834 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8380 71834 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8381 71834 : if (ruleinfo[i].ruletable == NULL)
8382 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8383 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8384 71834 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8385 71834 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8386 71834 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8387 71834 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8388 71834 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8389 71834 : if (ruleinfo[i].ruletable)
8390 : {
8391 : /*
8392 : * If the table is a view or materialized view, force its ON
8393 : * SELECT rule to be sorted before the view itself --- this
8394 : * ensures that any dependencies for the rule affect the table's
8395 : * positioning. Other rules are forced to appear after their
8396 : * table.
8397 : */
8398 71834 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8399 1800 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8400 71174 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8401 : {
8402 70118 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8403 70118 : ruleinfo[i].dobj.dumpId);
8404 : /* We'll merge the rule into CREATE VIEW, if possible */
8405 70118 : ruleinfo[i].separate = false;
8406 : }
8407 : else
8408 : {
8409 1716 : addObjectDependency(&ruleinfo[i].dobj,
8410 1716 : ruleinfo[i].ruletable->dobj.dumpId);
8411 1716 : ruleinfo[i].separate = true;
8412 : }
8413 : }
8414 : else
8415 0 : ruleinfo[i].separate = true;
8416 : }
8417 :
8418 468 : PQclear(res);
8419 :
8420 468 : destroyPQExpBuffer(query);
8421 468 : }
8422 :
8423 : /*
8424 : * getTriggers
8425 : * get information about every trigger on a dumpable table
8426 : *
8427 : * Note: trigger data is not returned directly to the caller, but it
8428 : * does get entered into the DumpableObject tables.
8429 : */
8430 : void
8431 468 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8432 : {
8433 468 : PQExpBuffer query = createPQExpBuffer();
8434 468 : PQExpBuffer tbloids = createPQExpBuffer();
8435 : PGresult *res;
8436 : int ntups;
8437 : int curtblindx;
8438 : TriggerInfo *tginfo;
8439 : int i_tableoid,
8440 : i_oid,
8441 : i_tgrelid,
8442 : i_tgname,
8443 : i_tgenabled,
8444 : i_tgispartition,
8445 : i_tgdef;
8446 :
8447 : /*
8448 : * We want to perform just one query against pg_trigger. However, we
8449 : * mustn't try to select every row of the catalog and then sort it out on
8450 : * the client side, because some of the server-side functions we need
8451 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8452 : * build an array of the OIDs of tables we care about (and now have lock
8453 : * on!), and use a WHERE clause to constrain which rows are selected.
8454 : */
8455 468 : appendPQExpBufferChar(tbloids, '{');
8456 123672 : for (int i = 0; i < numTables; i++)
8457 : {
8458 123204 : TableInfo *tbinfo = &tblinfo[i];
8459 :
8460 123204 : if (!tbinfo->hastriggers ||
8461 2898 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8462 120830 : continue;
8463 :
8464 : /* OK, we need info for this table */
8465 2374 : if (tbloids->len > 1) /* do we have more than the '{'? */
8466 2258 : appendPQExpBufferChar(tbloids, ',');
8467 2374 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8468 : }
8469 468 : appendPQExpBufferChar(tbloids, '}');
8470 :
8471 468 : if (fout->remoteVersion >= 150000)
8472 : {
8473 : /*
8474 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8475 : * result in non-forward-compatible dumps of WHEN clauses due to
8476 : * under-parenthesization.
8477 : *
8478 : * NB: We need to see partition triggers in case the tgenabled flag
8479 : * has been changed from the parent.
8480 : */
8481 468 : appendPQExpBuffer(query,
8482 : "SELECT t.tgrelid, t.tgname, "
8483 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8484 : "t.tgenabled, t.tableoid, t.oid, "
8485 : "t.tgparentid <> 0 AS tgispartition\n"
8486 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8487 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8488 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8489 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8490 : "OR t.tgenabled != u.tgenabled) "
8491 : "ORDER BY t.tgrelid, t.tgname",
8492 : tbloids->data);
8493 : }
8494 0 : else if (fout->remoteVersion >= 130000)
8495 : {
8496 : /*
8497 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8498 : * result in non-forward-compatible dumps of WHEN clauses due to
8499 : * under-parenthesization.
8500 : *
8501 : * NB: We need to see tgisinternal triggers in partitions, in case the
8502 : * tgenabled flag has been changed from the parent.
8503 : */
8504 0 : appendPQExpBuffer(query,
8505 : "SELECT t.tgrelid, t.tgname, "
8506 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8507 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8508 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8509 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8510 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8511 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8512 : "ORDER BY t.tgrelid, t.tgname",
8513 : tbloids->data);
8514 : }
8515 0 : else if (fout->remoteVersion >= 110000)
8516 : {
8517 : /*
8518 : * NB: We need to see tgisinternal triggers in partitions, in case the
8519 : * tgenabled flag has been changed from the parent. No tgparentid in
8520 : * version 11-12, so we have to match them via pg_depend.
8521 : *
8522 : * See above about pretty=true in pg_get_triggerdef.
8523 : */
8524 0 : appendPQExpBuffer(query,
8525 : "SELECT t.tgrelid, t.tgname, "
8526 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8527 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8528 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8529 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8530 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8531 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8532 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8533 : " d.objid = t.oid "
8534 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8535 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8536 : "ORDER BY t.tgrelid, t.tgname",
8537 : tbloids->data);
8538 : }
8539 : else
8540 : {
8541 : /* See above about pretty=true in pg_get_triggerdef */
8542 0 : appendPQExpBuffer(query,
8543 : "SELECT t.tgrelid, t.tgname, "
8544 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8545 : "t.tgenabled, false as tgispartition, "
8546 : "t.tableoid, t.oid "
8547 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8548 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8549 : "WHERE NOT tgisinternal "
8550 : "ORDER BY t.tgrelid, t.tgname",
8551 : tbloids->data);
8552 : }
8553 :
8554 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8555 :
8556 468 : ntups = PQntuples(res);
8557 :
8558 468 : i_tableoid = PQfnumber(res, "tableoid");
8559 468 : i_oid = PQfnumber(res, "oid");
8560 468 : i_tgrelid = PQfnumber(res, "tgrelid");
8561 468 : i_tgname = PQfnumber(res, "tgname");
8562 468 : i_tgenabled = PQfnumber(res, "tgenabled");
8563 468 : i_tgispartition = PQfnumber(res, "tgispartition");
8564 468 : i_tgdef = PQfnumber(res, "tgdef");
8565 :
8566 468 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8567 :
8568 : /*
8569 : * Outer loop iterates once per table, not once per row. Incrementing of
8570 : * j is handled by the inner loop.
8571 : */
8572 468 : curtblindx = -1;
8573 1252 : for (int j = 0; j < ntups;)
8574 : {
8575 784 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8576 784 : TableInfo *tbinfo = NULL;
8577 : int numtrigs;
8578 :
8579 : /* Count rows for this table */
8580 1476 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8581 1360 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8582 668 : break;
8583 :
8584 : /*
8585 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8586 : * order.
8587 : */
8588 39594 : while (++curtblindx < numTables)
8589 : {
8590 39594 : tbinfo = &tblinfo[curtblindx];
8591 39594 : if (tbinfo->dobj.catId.oid == tgrelid)
8592 784 : break;
8593 : }
8594 784 : if (curtblindx >= numTables)
8595 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8596 :
8597 : /* Save data for this table */
8598 784 : tbinfo->triggers = tginfo + j;
8599 784 : tbinfo->numTriggers = numtrigs;
8600 :
8601 2260 : for (int c = 0; c < numtrigs; c++, j++)
8602 : {
8603 1476 : tginfo[j].dobj.objType = DO_TRIGGER;
8604 1476 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8605 1476 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8606 1476 : AssignDumpId(&tginfo[j].dobj);
8607 1476 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8608 1476 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8609 1476 : tginfo[j].tgtable = tbinfo;
8610 1476 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8611 1476 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8612 1476 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8613 : }
8614 : }
8615 :
8616 468 : PQclear(res);
8617 :
8618 468 : destroyPQExpBuffer(query);
8619 468 : destroyPQExpBuffer(tbloids);
8620 468 : }
8621 :
8622 : /*
8623 : * getEventTriggers
8624 : * get information about event triggers
8625 : */
8626 : void
8627 468 : getEventTriggers(Archive *fout)
8628 : {
8629 : int i;
8630 : PQExpBuffer query;
8631 : PGresult *res;
8632 : EventTriggerInfo *evtinfo;
8633 : int i_tableoid,
8634 : i_oid,
8635 : i_evtname,
8636 : i_evtevent,
8637 : i_evtowner,
8638 : i_evttags,
8639 : i_evtfname,
8640 : i_evtenabled;
8641 : int ntups;
8642 :
8643 : /* Before 9.3, there are no event triggers */
8644 468 : if (fout->remoteVersion < 90300)
8645 0 : return;
8646 :
8647 468 : query = createPQExpBuffer();
8648 :
8649 468 : appendPQExpBufferStr(query,
8650 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8651 : "evtevent, evtowner, "
8652 : "array_to_string(array("
8653 : "select quote_literal(x) "
8654 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8655 : "e.evtfoid::regproc as evtfname "
8656 : "FROM pg_event_trigger e "
8657 : "ORDER BY e.oid");
8658 :
8659 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8660 :
8661 468 : ntups = PQntuples(res);
8662 :
8663 468 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8664 :
8665 468 : i_tableoid = PQfnumber(res, "tableoid");
8666 468 : i_oid = PQfnumber(res, "oid");
8667 468 : i_evtname = PQfnumber(res, "evtname");
8668 468 : i_evtevent = PQfnumber(res, "evtevent");
8669 468 : i_evtowner = PQfnumber(res, "evtowner");
8670 468 : i_evttags = PQfnumber(res, "evttags");
8671 468 : i_evtfname = PQfnumber(res, "evtfname");
8672 468 : i_evtenabled = PQfnumber(res, "evtenabled");
8673 :
8674 586 : for (i = 0; i < ntups; i++)
8675 : {
8676 118 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8677 118 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8678 118 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8679 118 : AssignDumpId(&evtinfo[i].dobj);
8680 118 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8681 118 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8682 118 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8683 118 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8684 118 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8685 118 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8686 118 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8687 :
8688 : /* Decide whether we want to dump it */
8689 118 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8690 : }
8691 :
8692 468 : PQclear(res);
8693 :
8694 468 : destroyPQExpBuffer(query);
8695 : }
8696 :
8697 : /*
8698 : * getProcLangs
8699 : * get basic information about every procedural language in the system
8700 : *
8701 : * NB: this must run after getFuncs() because we assume we can do
8702 : * findFuncByOid().
8703 : */
8704 : void
8705 468 : getProcLangs(Archive *fout)
8706 : {
8707 : PGresult *res;
8708 : int ntups;
8709 : int i;
8710 468 : PQExpBuffer query = createPQExpBuffer();
8711 : ProcLangInfo *planginfo;
8712 : int i_tableoid;
8713 : int i_oid;
8714 : int i_lanname;
8715 : int i_lanpltrusted;
8716 : int i_lanplcallfoid;
8717 : int i_laninline;
8718 : int i_lanvalidator;
8719 : int i_lanacl;
8720 : int i_acldefault;
8721 : int i_lanowner;
8722 :
8723 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8724 : "lanname, lanpltrusted, lanplcallfoid, "
8725 : "laninline, lanvalidator, "
8726 : "lanacl, "
8727 : "acldefault('l', lanowner) AS acldefault, "
8728 : "lanowner "
8729 : "FROM pg_language "
8730 : "WHERE lanispl "
8731 : "ORDER BY oid");
8732 :
8733 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8734 :
8735 468 : ntups = PQntuples(res);
8736 :
8737 468 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8738 :
8739 468 : i_tableoid = PQfnumber(res, "tableoid");
8740 468 : i_oid = PQfnumber(res, "oid");
8741 468 : i_lanname = PQfnumber(res, "lanname");
8742 468 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8743 468 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8744 468 : i_laninline = PQfnumber(res, "laninline");
8745 468 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8746 468 : i_lanacl = PQfnumber(res, "lanacl");
8747 468 : i_acldefault = PQfnumber(res, "acldefault");
8748 468 : i_lanowner = PQfnumber(res, "lanowner");
8749 :
8750 1034 : for (i = 0; i < ntups; i++)
8751 : {
8752 566 : planginfo[i].dobj.objType = DO_PROCLANG;
8753 566 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8754 566 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8755 566 : AssignDumpId(&planginfo[i].dobj);
8756 :
8757 566 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8758 566 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8759 566 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8760 566 : planginfo[i].dacl.privtype = 0;
8761 566 : planginfo[i].dacl.initprivs = NULL;
8762 566 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8763 566 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8764 566 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8765 566 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8766 566 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8767 :
8768 : /* Decide whether we want to dump it */
8769 566 : selectDumpableProcLang(&(planginfo[i]), fout);
8770 :
8771 : /* Mark whether language has an ACL */
8772 566 : if (!PQgetisnull(res, i, i_lanacl))
8773 98 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8774 : }
8775 :
8776 468 : PQclear(res);
8777 :
8778 468 : destroyPQExpBuffer(query);
8779 468 : }
8780 :
8781 : /*
8782 : * getCasts
8783 : * get basic information about most casts in the system
8784 : *
8785 : * Skip casts from a range to its multirange, since we'll create those
8786 : * automatically.
8787 : */
8788 : void
8789 468 : getCasts(Archive *fout)
8790 : {
8791 : PGresult *res;
8792 : int ntups;
8793 : int i;
8794 468 : PQExpBuffer query = createPQExpBuffer();
8795 : CastInfo *castinfo;
8796 : int i_tableoid;
8797 : int i_oid;
8798 : int i_castsource;
8799 : int i_casttarget;
8800 : int i_castfunc;
8801 : int i_castcontext;
8802 : int i_castmethod;
8803 :
8804 468 : if (fout->remoteVersion >= 140000)
8805 : {
8806 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8807 : "castsource, casttarget, castfunc, castcontext, "
8808 : "castmethod "
8809 : "FROM pg_cast c "
8810 : "WHERE NOT EXISTS ( "
8811 : "SELECT 1 FROM pg_range r "
8812 : "WHERE c.castsource = r.rngtypid "
8813 : "AND c.casttarget = r.rngmultitypid "
8814 : ") "
8815 : "ORDER BY 3,4");
8816 : }
8817 : else
8818 : {
8819 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8820 : "castsource, casttarget, castfunc, castcontext, "
8821 : "castmethod "
8822 : "FROM pg_cast ORDER BY 3,4");
8823 : }
8824 :
8825 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8826 :
8827 468 : ntups = PQntuples(res);
8828 :
8829 468 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8830 :
8831 468 : i_tableoid = PQfnumber(res, "tableoid");
8832 468 : i_oid = PQfnumber(res, "oid");
8833 468 : i_castsource = PQfnumber(res, "castsource");
8834 468 : i_casttarget = PQfnumber(res, "casttarget");
8835 468 : i_castfunc = PQfnumber(res, "castfunc");
8836 468 : i_castcontext = PQfnumber(res, "castcontext");
8837 468 : i_castmethod = PQfnumber(res, "castmethod");
8838 :
8839 107858 : for (i = 0; i < ntups; i++)
8840 : {
8841 : PQExpBufferData namebuf;
8842 : TypeInfo *sTypeInfo;
8843 : TypeInfo *tTypeInfo;
8844 :
8845 107390 : castinfo[i].dobj.objType = DO_CAST;
8846 107390 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8847 107390 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8848 107390 : AssignDumpId(&castinfo[i].dobj);
8849 107390 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8850 107390 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8851 107390 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8852 107390 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8853 107390 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8854 :
8855 : /*
8856 : * Try to name cast as concatenation of typnames. This is only used
8857 : * for purposes of sorting. If we fail to find either type, the name
8858 : * will be an empty string.
8859 : */
8860 107390 : initPQExpBuffer(&namebuf);
8861 107390 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8862 107390 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8863 107390 : if (sTypeInfo && tTypeInfo)
8864 107390 : appendPQExpBuffer(&namebuf, "%s %s",
8865 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8866 107390 : castinfo[i].dobj.name = namebuf.data;
8867 :
8868 : /* Decide whether we want to dump it */
8869 107390 : selectDumpableCast(&(castinfo[i]), fout);
8870 : }
8871 :
8872 468 : PQclear(res);
8873 :
8874 468 : destroyPQExpBuffer(query);
8875 468 : }
8876 :
8877 : static char *
8878 204 : get_language_name(Archive *fout, Oid langid)
8879 : {
8880 : PQExpBuffer query;
8881 : PGresult *res;
8882 : char *lanname;
8883 :
8884 204 : query = createPQExpBuffer();
8885 204 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8886 204 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8887 204 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8888 204 : destroyPQExpBuffer(query);
8889 204 : PQclear(res);
8890 :
8891 204 : return lanname;
8892 : }
8893 :
8894 : /*
8895 : * getTransforms
8896 : * get basic information about every transform in the system
8897 : */
8898 : void
8899 468 : getTransforms(Archive *fout)
8900 : {
8901 : PGresult *res;
8902 : int ntups;
8903 : int i;
8904 : PQExpBuffer query;
8905 : TransformInfo *transforminfo;
8906 : int i_tableoid;
8907 : int i_oid;
8908 : int i_trftype;
8909 : int i_trflang;
8910 : int i_trffromsql;
8911 : int i_trftosql;
8912 :
8913 : /* Transforms didn't exist pre-9.5 */
8914 468 : if (fout->remoteVersion < 90500)
8915 0 : return;
8916 :
8917 468 : query = createPQExpBuffer();
8918 :
8919 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8920 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8921 : "FROM pg_transform "
8922 : "ORDER BY 3,4");
8923 :
8924 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8925 :
8926 468 : ntups = PQntuples(res);
8927 :
8928 468 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8929 :
8930 468 : i_tableoid = PQfnumber(res, "tableoid");
8931 468 : i_oid = PQfnumber(res, "oid");
8932 468 : i_trftype = PQfnumber(res, "trftype");
8933 468 : i_trflang = PQfnumber(res, "trflang");
8934 468 : i_trffromsql = PQfnumber(res, "trffromsql");
8935 468 : i_trftosql = PQfnumber(res, "trftosql");
8936 :
8937 586 : for (i = 0; i < ntups; i++)
8938 : {
8939 : PQExpBufferData namebuf;
8940 : TypeInfo *typeInfo;
8941 : char *lanname;
8942 :
8943 118 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8944 118 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8945 118 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8946 118 : AssignDumpId(&transforminfo[i].dobj);
8947 118 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8948 118 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8949 118 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8950 118 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8951 :
8952 : /*
8953 : * Try to name transform as concatenation of type and language name.
8954 : * This is only used for purposes of sorting. If we fail to find
8955 : * either, the name will be an empty string.
8956 : */
8957 118 : initPQExpBuffer(&namebuf);
8958 118 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8959 118 : lanname = get_language_name(fout, transforminfo[i].trflang);
8960 118 : if (typeInfo && lanname)
8961 118 : appendPQExpBuffer(&namebuf, "%s %s",
8962 : typeInfo->dobj.name, lanname);
8963 118 : transforminfo[i].dobj.name = namebuf.data;
8964 118 : free(lanname);
8965 :
8966 : /* Decide whether we want to dump it */
8967 118 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8968 : }
8969 :
8970 468 : PQclear(res);
8971 :
8972 468 : destroyPQExpBuffer(query);
8973 : }
8974 :
8975 : /*
8976 : * getTableAttrs -
8977 : * for each interesting table, read info about its attributes
8978 : * (names, types, default values, CHECK constraints, etc)
8979 : *
8980 : * modifies tblinfo
8981 : */
8982 : void
8983 468 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8984 : {
8985 468 : DumpOptions *dopt = fout->dopt;
8986 468 : PQExpBuffer q = createPQExpBuffer();
8987 468 : PQExpBuffer tbloids = createPQExpBuffer();
8988 468 : PQExpBuffer checkoids = createPQExpBuffer();
8989 468 : PQExpBuffer invalidnotnulloids = NULL;
8990 : PGresult *res;
8991 : int ntups;
8992 : int curtblindx;
8993 : int i_attrelid;
8994 : int i_attnum;
8995 : int i_attname;
8996 : int i_atttypname;
8997 : int i_attstattarget;
8998 : int i_attstorage;
8999 : int i_typstorage;
9000 : int i_attidentity;
9001 : int i_attgenerated;
9002 : int i_attisdropped;
9003 : int i_attlen;
9004 : int i_attalign;
9005 : int i_attislocal;
9006 : int i_notnull_name;
9007 : int i_notnull_noinherit;
9008 : int i_notnull_islocal;
9009 : int i_notnull_invalidoid;
9010 : int i_attoptions;
9011 : int i_attcollation;
9012 : int i_attcompression;
9013 : int i_attfdwoptions;
9014 : int i_attmissingval;
9015 : int i_atthasdef;
9016 :
9017 : /*
9018 : * We want to perform just one query against pg_attribute, and then just
9019 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9020 : * (for CHECK constraints and for NOT NULL constraints). However, we
9021 : * mustn't try to select every row of those catalogs and then sort it out
9022 : * on the client side, because some of the server-side functions we need
9023 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9024 : * build an array of the OIDs of tables we care about (and now have lock
9025 : * on!), and use a WHERE clause to constrain which rows are selected.
9026 : */
9027 468 : appendPQExpBufferChar(tbloids, '{');
9028 468 : appendPQExpBufferChar(checkoids, '{');
9029 123672 : for (int i = 0; i < numTables; i++)
9030 : {
9031 123204 : TableInfo *tbinfo = &tblinfo[i];
9032 :
9033 : /* Don't bother to collect info for sequences */
9034 123204 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9035 1632 : continue;
9036 :
9037 : /* Don't bother with uninteresting tables, either */
9038 121572 : if (!tbinfo->interesting)
9039 103358 : continue;
9040 :
9041 : /* OK, we need info for this table */
9042 18214 : if (tbloids->len > 1) /* do we have more than the '{'? */
9043 17892 : appendPQExpBufferChar(tbloids, ',');
9044 18214 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9045 :
9046 18214 : if (tbinfo->ncheck > 0)
9047 : {
9048 : /* Also make a list of the ones with check constraints */
9049 1362 : if (checkoids->len > 1) /* do we have more than the '{'? */
9050 1210 : appendPQExpBufferChar(checkoids, ',');
9051 1362 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9052 : }
9053 : }
9054 468 : appendPQExpBufferChar(tbloids, '}');
9055 468 : appendPQExpBufferChar(checkoids, '}');
9056 :
9057 : /*
9058 : * Find all the user attributes and their types.
9059 : *
9060 : * Since we only want to dump COLLATE clauses for attributes whose
9061 : * collation is different from their type's default, we use a CASE here to
9062 : * suppress uninteresting attcollations cheaply.
9063 : */
9064 468 : appendPQExpBufferStr(q,
9065 : "SELECT\n"
9066 : "a.attrelid,\n"
9067 : "a.attnum,\n"
9068 : "a.attname,\n"
9069 : "a.attstattarget,\n"
9070 : "a.attstorage,\n"
9071 : "t.typstorage,\n"
9072 : "a.atthasdef,\n"
9073 : "a.attisdropped,\n"
9074 : "a.attlen,\n"
9075 : "a.attalign,\n"
9076 : "a.attislocal,\n"
9077 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9078 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9079 : "CASE WHEN a.attcollation <> t.typcollation "
9080 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9081 : "pg_catalog.array_to_string(ARRAY("
9082 : "SELECT pg_catalog.quote_ident(option_name) || "
9083 : "' ' || pg_catalog.quote_literal(option_value) "
9084 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9085 : "ORDER BY option_name"
9086 : "), E',\n ') AS attfdwoptions,\n");
9087 :
9088 : /*
9089 : * Find out any NOT NULL markings for each column. In 18 and up we read
9090 : * pg_constraint to obtain the constraint name. notnull_noinherit is set
9091 : * according to the NO INHERIT property. For versions prior to 18, we
9092 : * store an empty string as the name when a constraint is marked as
9093 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9094 : * without a name); also, such cases are never NO INHERIT.
9095 : *
9096 : * For invalid constraints, we need to store their OIDs for processing
9097 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9098 : * is invalid, and NULL otherwise.
9099 : *
9100 : * We track in notnull_islocal whether the constraint was defined directly
9101 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9102 : * might modify this later; that routine is also in charge of determining
9103 : * the correct inhcount.
9104 : */
9105 468 : if (fout->remoteVersion >= 180000)
9106 468 : appendPQExpBufferStr(q,
9107 : "co.conname AS notnull_name,\n"
9108 : "CASE WHEN NOT co.convalidated THEN co.oid "
9109 : "ELSE NULL END AS notnull_invalidoid,\n"
9110 : "co.connoinherit AS notnull_noinherit,\n"
9111 : "co.conislocal AS notnull_islocal,\n");
9112 : else
9113 0 : appendPQExpBufferStr(q,
9114 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9115 : "NULL AS notnull_invalidoid,\n"
9116 : "false AS notnull_noinherit,\n"
9117 : "a.attislocal AS notnull_islocal,\n");
9118 :
9119 468 : if (fout->remoteVersion >= 140000)
9120 468 : appendPQExpBufferStr(q,
9121 : "a.attcompression AS attcompression,\n");
9122 : else
9123 0 : appendPQExpBufferStr(q,
9124 : "'' AS attcompression,\n");
9125 :
9126 468 : if (fout->remoteVersion >= 100000)
9127 468 : appendPQExpBufferStr(q,
9128 : "a.attidentity,\n");
9129 : else
9130 0 : appendPQExpBufferStr(q,
9131 : "'' AS attidentity,\n");
9132 :
9133 468 : if (fout->remoteVersion >= 110000)
9134 468 : appendPQExpBufferStr(q,
9135 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9136 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9137 : else
9138 0 : appendPQExpBufferStr(q,
9139 : "NULL AS attmissingval,\n");
9140 :
9141 468 : if (fout->remoteVersion >= 120000)
9142 468 : appendPQExpBufferStr(q,
9143 : "a.attgenerated\n");
9144 : else
9145 0 : appendPQExpBufferStr(q,
9146 : "'' AS attgenerated\n");
9147 :
9148 : /* need left join to pg_type to not fail on dropped columns ... */
9149 468 : appendPQExpBuffer(q,
9150 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9151 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9152 : "LEFT JOIN pg_catalog.pg_type t "
9153 : "ON (a.atttypid = t.oid)\n",
9154 : tbloids->data);
9155 :
9156 : /*
9157 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9158 : * entries. Also, we need to know if the NOT NULL for each column is
9159 : * backing a primary key.
9160 : */
9161 468 : if (fout->remoteVersion >= 180000)
9162 468 : appendPQExpBufferStr(q,
9163 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9164 : "(a.attrelid = co.conrelid\n"
9165 : " AND co.contype = 'n' AND "
9166 : "co.conkey = array[a.attnum])\n");
9167 :
9168 468 : appendPQExpBufferStr(q,
9169 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9170 : "ORDER BY a.attrelid, a.attnum");
9171 :
9172 468 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9173 :
9174 468 : ntups = PQntuples(res);
9175 :
9176 468 : i_attrelid = PQfnumber(res, "attrelid");
9177 468 : i_attnum = PQfnumber(res, "attnum");
9178 468 : i_attname = PQfnumber(res, "attname");
9179 468 : i_atttypname = PQfnumber(res, "atttypname");
9180 468 : i_attstattarget = PQfnumber(res, "attstattarget");
9181 468 : i_attstorage = PQfnumber(res, "attstorage");
9182 468 : i_typstorage = PQfnumber(res, "typstorage");
9183 468 : i_attidentity = PQfnumber(res, "attidentity");
9184 468 : i_attgenerated = PQfnumber(res, "attgenerated");
9185 468 : i_attisdropped = PQfnumber(res, "attisdropped");
9186 468 : i_attlen = PQfnumber(res, "attlen");
9187 468 : i_attalign = PQfnumber(res, "attalign");
9188 468 : i_attislocal = PQfnumber(res, "attislocal");
9189 468 : i_notnull_name = PQfnumber(res, "notnull_name");
9190 468 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9191 468 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9192 468 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9193 468 : i_attoptions = PQfnumber(res, "attoptions");
9194 468 : i_attcollation = PQfnumber(res, "attcollation");
9195 468 : i_attcompression = PQfnumber(res, "attcompression");
9196 468 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9197 468 : i_attmissingval = PQfnumber(res, "attmissingval");
9198 468 : i_atthasdef = PQfnumber(res, "atthasdef");
9199 :
9200 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9201 468 : resetPQExpBuffer(tbloids);
9202 468 : appendPQExpBufferChar(tbloids, '{');
9203 :
9204 : /*
9205 : * Outer loop iterates once per table, not once per row. Incrementing of
9206 : * r is handled by the inner loop.
9207 : */
9208 468 : curtblindx = -1;
9209 18356 : for (int r = 0; r < ntups;)
9210 : {
9211 17888 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9212 17888 : TableInfo *tbinfo = NULL;
9213 : int numatts;
9214 : bool hasdefaults;
9215 :
9216 : /* Count rows for this table */
9217 68028 : for (numatts = 1; numatts < ntups - r; numatts++)
9218 67712 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9219 17572 : break;
9220 :
9221 : /*
9222 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9223 : * order.
9224 : */
9225 86482 : while (++curtblindx < numTables)
9226 : {
9227 86482 : tbinfo = &tblinfo[curtblindx];
9228 86482 : if (tbinfo->dobj.catId.oid == attrelid)
9229 17888 : break;
9230 : }
9231 17888 : if (curtblindx >= numTables)
9232 0 : pg_fatal("unrecognized table OID %u", attrelid);
9233 : /* cross-check that we only got requested tables */
9234 17888 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9235 17888 : !tbinfo->interesting)
9236 0 : pg_fatal("unexpected column data for table \"%s\"",
9237 : tbinfo->dobj.name);
9238 :
9239 : /* Save data for this table */
9240 17888 : tbinfo->numatts = numatts;
9241 17888 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9242 17888 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9243 17888 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9244 17888 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9245 17888 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9246 17888 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9247 17888 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9248 17888 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9249 17888 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9250 17888 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9251 17888 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9252 17888 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9253 17888 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9254 17888 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9255 17888 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9256 17888 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9257 17888 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9258 17888 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9259 17888 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9260 17888 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9261 17888 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9262 17888 : hasdefaults = false;
9263 :
9264 85916 : for (int j = 0; j < numatts; j++, r++)
9265 : {
9266 68028 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9267 0 : pg_fatal("invalid column numbering in table \"%s\"",
9268 : tbinfo->dobj.name);
9269 68028 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9270 68028 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9271 68028 : if (PQgetisnull(res, r, i_attstattarget))
9272 67940 : tbinfo->attstattarget[j] = -1;
9273 : else
9274 88 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9275 68028 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9276 68028 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9277 68028 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9278 68028 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9279 68028 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9280 68028 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9281 68028 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9282 68028 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9283 68028 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9284 :
9285 : /* Handle not-null constraint name and flags */
9286 68028 : determineNotNullFlags(fout, res, r,
9287 : tbinfo, j,
9288 : i_notnull_name,
9289 : i_notnull_invalidoid,
9290 : i_notnull_noinherit,
9291 : i_notnull_islocal,
9292 : &invalidnotnulloids);
9293 :
9294 68028 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9295 68028 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9296 68028 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9297 68028 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9298 68028 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9299 68028 : tbinfo->attrdefs[j] = NULL; /* fix below */
9300 68028 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9301 3404 : hasdefaults = true;
9302 : }
9303 :
9304 17888 : if (hasdefaults)
9305 : {
9306 : /* Collect OIDs of interesting tables that have defaults */
9307 2582 : if (tbloids->len > 1) /* do we have more than the '{'? */
9308 2432 : appendPQExpBufferChar(tbloids, ',');
9309 2582 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9310 : }
9311 : }
9312 :
9313 : /* If invalidnotnulloids has any data, finalize it */
9314 468 : if (invalidnotnulloids != NULL)
9315 100 : appendPQExpBufferChar(invalidnotnulloids, '}');
9316 :
9317 468 : PQclear(res);
9318 :
9319 : /*
9320 : * Now get info about column defaults. This is skipped for a data-only
9321 : * dump, as it is only needed for table schemas.
9322 : */
9323 468 : if (dopt->dumpSchema && tbloids->len > 1)
9324 : {
9325 : AttrDefInfo *attrdefs;
9326 : int numDefaults;
9327 134 : TableInfo *tbinfo = NULL;
9328 :
9329 134 : pg_log_info("finding table default expressions");
9330 :
9331 134 : appendPQExpBufferChar(tbloids, '}');
9332 :
9333 134 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9334 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9335 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9336 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9337 : "ORDER BY a.adrelid, a.adnum",
9338 : tbloids->data);
9339 :
9340 134 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9341 :
9342 134 : numDefaults = PQntuples(res);
9343 134 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9344 :
9345 134 : curtblindx = -1;
9346 3342 : for (int j = 0; j < numDefaults; j++)
9347 : {
9348 3208 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9349 3208 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9350 3208 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9351 3208 : int adnum = atoi(PQgetvalue(res, j, 3));
9352 3208 : char *adsrc = PQgetvalue(res, j, 4);
9353 :
9354 : /*
9355 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9356 : * OID order.
9357 : */
9358 3208 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9359 : {
9360 45518 : while (++curtblindx < numTables)
9361 : {
9362 45518 : tbinfo = &tblinfo[curtblindx];
9363 45518 : if (tbinfo->dobj.catId.oid == adrelid)
9364 2446 : break;
9365 : }
9366 2446 : if (curtblindx >= numTables)
9367 0 : pg_fatal("unrecognized table OID %u", adrelid);
9368 : }
9369 :
9370 3208 : if (adnum <= 0 || adnum > tbinfo->numatts)
9371 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9372 : adnum, tbinfo->dobj.name);
9373 :
9374 : /*
9375 : * dropped columns shouldn't have defaults, but just in case,
9376 : * ignore 'em
9377 : */
9378 3208 : if (tbinfo->attisdropped[adnum - 1])
9379 0 : continue;
9380 :
9381 3208 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9382 3208 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9383 3208 : attrdefs[j].dobj.catId.oid = adoid;
9384 3208 : AssignDumpId(&attrdefs[j].dobj);
9385 3208 : attrdefs[j].adtable = tbinfo;
9386 3208 : attrdefs[j].adnum = adnum;
9387 3208 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9388 :
9389 3208 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9390 3208 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9391 :
9392 3208 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9393 :
9394 : /*
9395 : * Figure out whether the default/generation expression should be
9396 : * dumped as part of the main CREATE TABLE (or similar) command or
9397 : * as a separate ALTER TABLE (or similar) command. The preference
9398 : * is to put it into the CREATE command, but in some cases that's
9399 : * not possible.
9400 : */
9401 3208 : if (tbinfo->attgenerated[adnum - 1])
9402 : {
9403 : /*
9404 : * Column generation expressions cannot be dumped separately,
9405 : * because there is no syntax for it. By setting separate to
9406 : * false here we prevent the "default" from being processed as
9407 : * its own dumpable object. Later, flagInhAttrs() will mark
9408 : * it as not to be dumped at all, if possible (that is, if it
9409 : * can be inherited from a parent).
9410 : */
9411 1840 : attrdefs[j].separate = false;
9412 : }
9413 1368 : else if (tbinfo->relkind == RELKIND_VIEW)
9414 : {
9415 : /*
9416 : * Defaults on a VIEW must always be dumped as separate ALTER
9417 : * TABLE commands.
9418 : */
9419 72 : attrdefs[j].separate = true;
9420 : }
9421 1296 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9422 : {
9423 : /* column will be suppressed, print default separately */
9424 14 : attrdefs[j].separate = true;
9425 : }
9426 : else
9427 : {
9428 1282 : attrdefs[j].separate = false;
9429 : }
9430 :
9431 3208 : if (!attrdefs[j].separate)
9432 : {
9433 : /*
9434 : * Mark the default as needing to appear before the table, so
9435 : * that any dependencies it has must be emitted before the
9436 : * CREATE TABLE. If this is not possible, we'll change to
9437 : * "separate" mode while sorting dependencies.
9438 : */
9439 3122 : addObjectDependency(&tbinfo->dobj,
9440 3122 : attrdefs[j].dobj.dumpId);
9441 : }
9442 :
9443 3208 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9444 : }
9445 :
9446 134 : PQclear(res);
9447 : }
9448 :
9449 : /*
9450 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9451 : * data-only dump, as it is only needed for table schemas.
9452 : */
9453 468 : if (dopt->dumpSchema && invalidnotnulloids)
9454 : {
9455 : ConstraintInfo *constrs;
9456 : int numConstrs;
9457 : int i_tableoid;
9458 : int i_oid;
9459 : int i_conrelid;
9460 : int i_conname;
9461 : int i_consrc;
9462 : int i_conislocal;
9463 :
9464 88 : pg_log_info("finding invalid not null constraints");
9465 :
9466 88 : resetPQExpBuffer(q);
9467 88 : appendPQExpBuffer(q,
9468 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9469 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9470 : "conislocal, convalidated "
9471 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9472 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9473 : "ORDER BY c.conrelid, c.conname",
9474 88 : invalidnotnulloids->data);
9475 :
9476 88 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9477 :
9478 88 : numConstrs = PQntuples(res);
9479 88 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9480 :
9481 88 : i_tableoid = PQfnumber(res, "tableoid");
9482 88 : i_oid = PQfnumber(res, "oid");
9483 88 : i_conrelid = PQfnumber(res, "conrelid");
9484 88 : i_conname = PQfnumber(res, "conname");
9485 88 : i_consrc = PQfnumber(res, "consrc");
9486 88 : i_conislocal = PQfnumber(res, "conislocal");
9487 :
9488 : /* As above, this loop iterates once per table, not once per row */
9489 88 : curtblindx = -1;
9490 272 : for (int j = 0; j < numConstrs;)
9491 : {
9492 184 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9493 184 : TableInfo *tbinfo = NULL;
9494 : int numcons;
9495 :
9496 : /* Count rows for this table */
9497 184 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9498 96 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9499 96 : break;
9500 :
9501 : /*
9502 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9503 : * OID order.
9504 : */
9505 31782 : while (++curtblindx < numTables)
9506 : {
9507 31782 : tbinfo = &tblinfo[curtblindx];
9508 31782 : if (tbinfo->dobj.catId.oid == conrelid)
9509 184 : break;
9510 : }
9511 184 : if (curtblindx >= numTables)
9512 0 : pg_fatal("unrecognized table OID %u", conrelid);
9513 :
9514 368 : for (int c = 0; c < numcons; c++, j++)
9515 : {
9516 184 : constrs[j].dobj.objType = DO_CONSTRAINT;
9517 184 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9518 184 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9519 184 : AssignDumpId(&constrs[j].dobj);
9520 184 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9521 184 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9522 184 : constrs[j].contable = tbinfo;
9523 184 : constrs[j].condomain = NULL;
9524 184 : constrs[j].contype = 'n';
9525 184 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9526 184 : constrs[j].confrelid = InvalidOid;
9527 184 : constrs[j].conindex = 0;
9528 184 : constrs[j].condeferrable = false;
9529 184 : constrs[j].condeferred = false;
9530 184 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9531 :
9532 : /*
9533 : * All invalid not-null constraints must be dumped separately,
9534 : * because CREATE TABLE would not create them as invalid, and
9535 : * also because they must be created after potentially
9536 : * violating data has been loaded.
9537 : */
9538 184 : constrs[j].separate = true;
9539 :
9540 184 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9541 : }
9542 : }
9543 88 : PQclear(res);
9544 : }
9545 :
9546 : /*
9547 : * Get info about table CHECK constraints. This is skipped for a
9548 : * data-only dump, as it is only needed for table schemas.
9549 : */
9550 468 : if (dopt->dumpSchema && checkoids->len > 2)
9551 : {
9552 : ConstraintInfo *constrs;
9553 : int numConstrs;
9554 : int i_tableoid;
9555 : int i_oid;
9556 : int i_conrelid;
9557 : int i_conname;
9558 : int i_consrc;
9559 : int i_conislocal;
9560 : int i_convalidated;
9561 :
9562 136 : pg_log_info("finding table check constraints");
9563 :
9564 136 : resetPQExpBuffer(q);
9565 136 : appendPQExpBuffer(q,
9566 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9567 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9568 : "conislocal, convalidated "
9569 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9570 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9571 : "WHERE contype = 'c' "
9572 : "ORDER BY c.conrelid, c.conname",
9573 : checkoids->data);
9574 :
9575 136 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9576 :
9577 136 : numConstrs = PQntuples(res);
9578 136 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9579 :
9580 136 : i_tableoid = PQfnumber(res, "tableoid");
9581 136 : i_oid = PQfnumber(res, "oid");
9582 136 : i_conrelid = PQfnumber(res, "conrelid");
9583 136 : i_conname = PQfnumber(res, "conname");
9584 136 : i_consrc = PQfnumber(res, "consrc");
9585 136 : i_conislocal = PQfnumber(res, "conislocal");
9586 136 : i_convalidated = PQfnumber(res, "convalidated");
9587 :
9588 : /* As above, this loop iterates once per table, not once per row */
9589 136 : curtblindx = -1;
9590 1396 : for (int j = 0; j < numConstrs;)
9591 : {
9592 1260 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9593 1260 : TableInfo *tbinfo = NULL;
9594 : int numcons;
9595 :
9596 : /* Count rows for this table */
9597 1630 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9598 1494 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9599 1124 : break;
9600 :
9601 : /*
9602 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9603 : * OID order.
9604 : */
9605 43404 : while (++curtblindx < numTables)
9606 : {
9607 43404 : tbinfo = &tblinfo[curtblindx];
9608 43404 : if (tbinfo->dobj.catId.oid == conrelid)
9609 1260 : break;
9610 : }
9611 1260 : if (curtblindx >= numTables)
9612 0 : pg_fatal("unrecognized table OID %u", conrelid);
9613 :
9614 1260 : if (numcons != tbinfo->ncheck)
9615 : {
9616 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9617 : "expected %d check constraints on table \"%s\" but found %d",
9618 : tbinfo->ncheck),
9619 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9620 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9621 0 : exit_nicely(1);
9622 : }
9623 :
9624 1260 : tbinfo->checkexprs = constrs + j;
9625 :
9626 2890 : for (int c = 0; c < numcons; c++, j++)
9627 : {
9628 1630 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9629 :
9630 1630 : constrs[j].dobj.objType = DO_CONSTRAINT;
9631 1630 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9632 1630 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9633 1630 : AssignDumpId(&constrs[j].dobj);
9634 1630 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9635 1630 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9636 1630 : constrs[j].contable = tbinfo;
9637 1630 : constrs[j].condomain = NULL;
9638 1630 : constrs[j].contype = 'c';
9639 1630 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9640 1630 : constrs[j].confrelid = InvalidOid;
9641 1630 : constrs[j].conindex = 0;
9642 1630 : constrs[j].condeferrable = false;
9643 1630 : constrs[j].condeferred = false;
9644 1630 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9645 :
9646 : /*
9647 : * An unvalidated constraint needs to be dumped separately, so
9648 : * that potentially-violating existing data is loaded before
9649 : * the constraint.
9650 : */
9651 1630 : constrs[j].separate = !validated;
9652 :
9653 1630 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9654 :
9655 : /*
9656 : * Mark the constraint as needing to appear before the table
9657 : * --- this is so that any other dependencies of the
9658 : * constraint will be emitted before we try to create the
9659 : * table. If the constraint is to be dumped separately, it
9660 : * will be dumped after data is loaded anyway, so don't do it.
9661 : * (There's an automatic dependency in the opposite direction
9662 : * anyway, so don't need to add one manually here.)
9663 : */
9664 1630 : if (!constrs[j].separate)
9665 1422 : addObjectDependency(&tbinfo->dobj,
9666 1422 : constrs[j].dobj.dumpId);
9667 :
9668 : /*
9669 : * We will detect later whether the constraint must be split
9670 : * out from the table definition.
9671 : */
9672 : }
9673 : }
9674 :
9675 136 : PQclear(res);
9676 : }
9677 :
9678 468 : destroyPQExpBuffer(q);
9679 468 : destroyPQExpBuffer(tbloids);
9680 468 : destroyPQExpBuffer(checkoids);
9681 468 : }
9682 :
9683 : /*
9684 : * Based on the getTableAttrs query's row corresponding to one column, set
9685 : * the name and flags to handle a not-null constraint for that column in
9686 : * the tbinfo struct.
9687 : *
9688 : * Result row 'r' is for tbinfo's attribute 'j'.
9689 : *
9690 : * There are four possibilities:
9691 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9692 : * (the constraint name) remains NULL.
9693 : * 2) The column has a constraint with no name (this is the case when
9694 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9695 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9696 : * 3) The column has an invalid not-null constraint. This must be treated
9697 : * as a separate object (because it must be created after the table data
9698 : * is loaded). So we add its OID to invalidnotnulloids for processing
9699 : * elsewhere and do nothing further with it here. We distinguish this
9700 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9701 : * value, which is the constraint OID. Valid constraints have a null OID.
9702 : * 4) The column has a constraint with a known name; in that case
9703 : * notnull_constrs carries that name and dumpTableSchema will print
9704 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9705 : * (table_column_not_null), there's no need to print that name in the dump,
9706 : * so notnull_constrs is set to the empty string and it behaves as case 2.
9707 : *
9708 : * In a child table that inherits from a parent already containing NOT NULL
9709 : * constraints and the columns in the child don't have their own NOT NULL
9710 : * declarations, we suppress printing constraints in the child: the
9711 : * constraints are acquired at the point where the child is attached to the
9712 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9713 : * set not here but in flagInhAttrs. That flag is also used when the
9714 : * constraint was validated in a child but all its parent have it as NOT
9715 : * VALID.
9716 : *
9717 : * Any of these constraints might have the NO INHERIT bit. If so we set
9718 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9719 : *
9720 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9721 : * to do the right thing in all but the trivial case. However, the downside
9722 : * of getting it wrong is simply that the name is printed rather than
9723 : * suppressed, so it's not a big deal.
9724 : *
9725 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9726 : * constraints are found, it is initialized and filled with the array of
9727 : * OIDs of such constraints, for later processing.
9728 : */
9729 : static void
9730 68028 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9731 : TableInfo *tbinfo, int j,
9732 : int i_notnull_name,
9733 : int i_notnull_invalidoid,
9734 : int i_notnull_noinherit,
9735 : int i_notnull_islocal,
9736 : PQExpBuffer *invalidnotnulloids)
9737 : {
9738 68028 : DumpOptions *dopt = fout->dopt;
9739 :
9740 : /*
9741 : * If this not-null constraint is not valid, list its OID in
9742 : * invalidnotnulloids and do nothing further. It'll be processed
9743 : * elsewhere later.
9744 : *
9745 : * Because invalid not-null constraints are rare, we don't want to malloc
9746 : * invalidnotnulloids until we're sure we're going it need it, which
9747 : * happens here.
9748 : */
9749 68028 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
9750 : {
9751 196 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9752 :
9753 196 : if (*invalidnotnulloids == NULL)
9754 : {
9755 100 : *invalidnotnulloids = createPQExpBuffer();
9756 100 : appendPQExpBufferChar(*invalidnotnulloids, '{');
9757 100 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
9758 : }
9759 : else
9760 96 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
9761 :
9762 : /*
9763 : * Track when a parent constraint is invalid for the cases where a
9764 : * child constraint has been validated independenly.
9765 : */
9766 196 : tbinfo->notnull_invalid[j] = true;
9767 :
9768 : /* nothing else to do */
9769 196 : tbinfo->notnull_constrs[j] = NULL;
9770 196 : return;
9771 : }
9772 :
9773 : /*
9774 : * notnull_noinh is straight from the query result. notnull_islocal also,
9775 : * though flagInhAttrs may change that one later.
9776 : */
9777 67832 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9778 67832 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9779 67832 : tbinfo->notnull_invalid[j] = false;
9780 :
9781 : /*
9782 : * Determine a constraint name to use. If the column is not marked not-
9783 : * null, we set NULL which cues ... to do nothing. An empty string says
9784 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9785 : * use.
9786 : */
9787 67832 : if (fout->remoteVersion < 180000)
9788 : {
9789 : /*
9790 : * < 18 doesn't have not-null names, so an unnamed constraint is
9791 : * sufficient.
9792 : */
9793 0 : if (PQgetisnull(res, r, i_notnull_name))
9794 0 : tbinfo->notnull_constrs[j] = NULL;
9795 : else
9796 0 : tbinfo->notnull_constrs[j] = "";
9797 : }
9798 : else
9799 : {
9800 67832 : if (PQgetisnull(res, r, i_notnull_name))
9801 60960 : tbinfo->notnull_constrs[j] = NULL;
9802 : else
9803 : {
9804 : /*
9805 : * In binary upgrade of inheritance child tables, must have a
9806 : * constraint name that we can UPDATE later.
9807 : */
9808 6872 : if (dopt->binary_upgrade &&
9809 576 : !tbinfo->ispartition &&
9810 420 : !tbinfo->notnull_islocal)
9811 : {
9812 0 : tbinfo->notnull_constrs[j] =
9813 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9814 : }
9815 : else
9816 : {
9817 : char *default_name;
9818 :
9819 : /* XXX should match ChooseConstraintName better */
9820 6872 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9821 6872 : tbinfo->attnames[j]);
9822 6872 : if (strcmp(default_name,
9823 6872 : PQgetvalue(res, r, i_notnull_name)) == 0)
9824 4452 : tbinfo->notnull_constrs[j] = "";
9825 : else
9826 : {
9827 2420 : tbinfo->notnull_constrs[j] =
9828 2420 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9829 : }
9830 6872 : free(default_name);
9831 : }
9832 : }
9833 : }
9834 : }
9835 :
9836 : /*
9837 : * Test whether a column should be printed as part of table's CREATE TABLE.
9838 : * Column number is zero-based.
9839 : *
9840 : * Normally this is always true, but it's false for dropped columns, as well
9841 : * as those that were inherited without any local definition. (If we print
9842 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9843 : * For partitions, it's always true, because we want the partitions to be
9844 : * created independently and ATTACH PARTITION used afterwards.
9845 : *
9846 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9847 : * attisdropped state later, so as to keep control of the physical column
9848 : * order.
9849 : *
9850 : * This function exists because there are scattered nonobvious places that
9851 : * must be kept in sync with this decision.
9852 : */
9853 : bool
9854 117168 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9855 : {
9856 117168 : if (dopt->binary_upgrade)
9857 12308 : return true;
9858 104860 : if (tbinfo->attisdropped[colno])
9859 2132 : return false;
9860 102728 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9861 : }
9862 :
9863 :
9864 : /*
9865 : * getTSParsers:
9866 : * get information about all text search parsers in the system catalogs
9867 : */
9868 : void
9869 468 : getTSParsers(Archive *fout)
9870 : {
9871 : PGresult *res;
9872 : int ntups;
9873 : int i;
9874 : PQExpBuffer query;
9875 : TSParserInfo *prsinfo;
9876 : int i_tableoid;
9877 : int i_oid;
9878 : int i_prsname;
9879 : int i_prsnamespace;
9880 : int i_prsstart;
9881 : int i_prstoken;
9882 : int i_prsend;
9883 : int i_prsheadline;
9884 : int i_prslextype;
9885 :
9886 468 : query = createPQExpBuffer();
9887 :
9888 : /*
9889 : * find all text search objects, including builtin ones; we filter out
9890 : * system-defined objects at dump-out time.
9891 : */
9892 :
9893 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9894 : "prsstart::oid, prstoken::oid, "
9895 : "prsend::oid, prsheadline::oid, prslextype::oid "
9896 : "FROM pg_ts_parser");
9897 :
9898 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9899 :
9900 468 : ntups = PQntuples(res);
9901 :
9902 468 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9903 :
9904 468 : i_tableoid = PQfnumber(res, "tableoid");
9905 468 : i_oid = PQfnumber(res, "oid");
9906 468 : i_prsname = PQfnumber(res, "prsname");
9907 468 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9908 468 : i_prsstart = PQfnumber(res, "prsstart");
9909 468 : i_prstoken = PQfnumber(res, "prstoken");
9910 468 : i_prsend = PQfnumber(res, "prsend");
9911 468 : i_prsheadline = PQfnumber(res, "prsheadline");
9912 468 : i_prslextype = PQfnumber(res, "prslextype");
9913 :
9914 1034 : for (i = 0; i < ntups; i++)
9915 : {
9916 566 : prsinfo[i].dobj.objType = DO_TSPARSER;
9917 566 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9918 566 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9919 566 : AssignDumpId(&prsinfo[i].dobj);
9920 566 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9921 1132 : prsinfo[i].dobj.namespace =
9922 566 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9923 566 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9924 566 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9925 566 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9926 566 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9927 566 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9928 :
9929 : /* Decide whether we want to dump it */
9930 566 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9931 : }
9932 :
9933 468 : PQclear(res);
9934 :
9935 468 : destroyPQExpBuffer(query);
9936 468 : }
9937 :
9938 : /*
9939 : * getTSDictionaries:
9940 : * get information about all text search dictionaries in the system catalogs
9941 : */
9942 : void
9943 468 : getTSDictionaries(Archive *fout)
9944 : {
9945 : PGresult *res;
9946 : int ntups;
9947 : int i;
9948 : PQExpBuffer query;
9949 : TSDictInfo *dictinfo;
9950 : int i_tableoid;
9951 : int i_oid;
9952 : int i_dictname;
9953 : int i_dictnamespace;
9954 : int i_dictowner;
9955 : int i_dicttemplate;
9956 : int i_dictinitoption;
9957 :
9958 468 : query = createPQExpBuffer();
9959 :
9960 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9961 : "dictnamespace, dictowner, "
9962 : "dicttemplate, dictinitoption "
9963 : "FROM pg_ts_dict");
9964 :
9965 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9966 :
9967 468 : ntups = PQntuples(res);
9968 :
9969 468 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9970 :
9971 468 : i_tableoid = PQfnumber(res, "tableoid");
9972 468 : i_oid = PQfnumber(res, "oid");
9973 468 : i_dictname = PQfnumber(res, "dictname");
9974 468 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9975 468 : i_dictowner = PQfnumber(res, "dictowner");
9976 468 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9977 468 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9978 :
9979 14786 : for (i = 0; i < ntups; i++)
9980 : {
9981 14318 : dictinfo[i].dobj.objType = DO_TSDICT;
9982 14318 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9983 14318 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9984 14318 : AssignDumpId(&dictinfo[i].dobj);
9985 14318 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9986 28636 : dictinfo[i].dobj.namespace =
9987 14318 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9988 14318 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9989 14318 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9990 14318 : if (PQgetisnull(res, i, i_dictinitoption))
9991 566 : dictinfo[i].dictinitoption = NULL;
9992 : else
9993 13752 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9994 :
9995 : /* Decide whether we want to dump it */
9996 14318 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9997 : }
9998 :
9999 468 : PQclear(res);
10000 :
10001 468 : destroyPQExpBuffer(query);
10002 468 : }
10003 :
10004 : /*
10005 : * getTSTemplates:
10006 : * get information about all text search templates in the system catalogs
10007 : */
10008 : void
10009 468 : getTSTemplates(Archive *fout)
10010 : {
10011 : PGresult *res;
10012 : int ntups;
10013 : int i;
10014 : PQExpBuffer query;
10015 : TSTemplateInfo *tmplinfo;
10016 : int i_tableoid;
10017 : int i_oid;
10018 : int i_tmplname;
10019 : int i_tmplnamespace;
10020 : int i_tmplinit;
10021 : int i_tmpllexize;
10022 :
10023 468 : query = createPQExpBuffer();
10024 :
10025 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10026 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10027 : "FROM pg_ts_template");
10028 :
10029 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10030 :
10031 468 : ntups = PQntuples(res);
10032 :
10033 468 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10034 :
10035 468 : i_tableoid = PQfnumber(res, "tableoid");
10036 468 : i_oid = PQfnumber(res, "oid");
10037 468 : i_tmplname = PQfnumber(res, "tmplname");
10038 468 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10039 468 : i_tmplinit = PQfnumber(res, "tmplinit");
10040 468 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10041 :
10042 2906 : for (i = 0; i < ntups; i++)
10043 : {
10044 2438 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10045 2438 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10046 2438 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10047 2438 : AssignDumpId(&tmplinfo[i].dobj);
10048 2438 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10049 4876 : tmplinfo[i].dobj.namespace =
10050 2438 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10051 2438 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10052 2438 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10053 :
10054 : /* Decide whether we want to dump it */
10055 2438 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10056 : }
10057 :
10058 468 : PQclear(res);
10059 :
10060 468 : destroyPQExpBuffer(query);
10061 468 : }
10062 :
10063 : /*
10064 : * getTSConfigurations:
10065 : * get information about all text search configurations
10066 : */
10067 : void
10068 468 : getTSConfigurations(Archive *fout)
10069 : {
10070 : PGresult *res;
10071 : int ntups;
10072 : int i;
10073 : PQExpBuffer query;
10074 : TSConfigInfo *cfginfo;
10075 : int i_tableoid;
10076 : int i_oid;
10077 : int i_cfgname;
10078 : int i_cfgnamespace;
10079 : int i_cfgowner;
10080 : int i_cfgparser;
10081 :
10082 468 : query = createPQExpBuffer();
10083 :
10084 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10085 : "cfgnamespace, cfgowner, cfgparser "
10086 : "FROM pg_ts_config");
10087 :
10088 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10089 :
10090 468 : ntups = PQntuples(res);
10091 :
10092 468 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10093 :
10094 468 : i_tableoid = PQfnumber(res, "tableoid");
10095 468 : i_oid = PQfnumber(res, "oid");
10096 468 : i_cfgname = PQfnumber(res, "cfgname");
10097 468 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10098 468 : i_cfgowner = PQfnumber(res, "cfgowner");
10099 468 : i_cfgparser = PQfnumber(res, "cfgparser");
10100 :
10101 14686 : for (i = 0; i < ntups; i++)
10102 : {
10103 14218 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10104 14218 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10105 14218 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10106 14218 : AssignDumpId(&cfginfo[i].dobj);
10107 14218 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10108 28436 : cfginfo[i].dobj.namespace =
10109 14218 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10110 14218 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10111 14218 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10112 :
10113 : /* Decide whether we want to dump it */
10114 14218 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10115 : }
10116 :
10117 468 : PQclear(res);
10118 :
10119 468 : destroyPQExpBuffer(query);
10120 468 : }
10121 :
10122 : /*
10123 : * getForeignDataWrappers:
10124 : * get information about all foreign-data wrappers in the system catalogs
10125 : */
10126 : void
10127 468 : getForeignDataWrappers(Archive *fout)
10128 : {
10129 : PGresult *res;
10130 : int ntups;
10131 : int i;
10132 : PQExpBuffer query;
10133 : FdwInfo *fdwinfo;
10134 : int i_tableoid;
10135 : int i_oid;
10136 : int i_fdwname;
10137 : int i_fdwowner;
10138 : int i_fdwhandler;
10139 : int i_fdwvalidator;
10140 : int i_fdwacl;
10141 : int i_acldefault;
10142 : int i_fdwoptions;
10143 :
10144 468 : query = createPQExpBuffer();
10145 :
10146 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10147 : "fdwowner, "
10148 : "fdwhandler::pg_catalog.regproc, "
10149 : "fdwvalidator::pg_catalog.regproc, "
10150 : "fdwacl, "
10151 : "acldefault('F', fdwowner) AS acldefault, "
10152 : "array_to_string(ARRAY("
10153 : "SELECT quote_ident(option_name) || ' ' || "
10154 : "quote_literal(option_value) "
10155 : "FROM pg_options_to_table(fdwoptions) "
10156 : "ORDER BY option_name"
10157 : "), E',\n ') AS fdwoptions "
10158 : "FROM pg_foreign_data_wrapper");
10159 :
10160 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10161 :
10162 468 : ntups = PQntuples(res);
10163 :
10164 468 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10165 :
10166 468 : i_tableoid = PQfnumber(res, "tableoid");
10167 468 : i_oid = PQfnumber(res, "oid");
10168 468 : i_fdwname = PQfnumber(res, "fdwname");
10169 468 : i_fdwowner = PQfnumber(res, "fdwowner");
10170 468 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10171 468 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10172 468 : i_fdwacl = PQfnumber(res, "fdwacl");
10173 468 : i_acldefault = PQfnumber(res, "acldefault");
10174 468 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10175 :
10176 624 : for (i = 0; i < ntups; i++)
10177 : {
10178 156 : fdwinfo[i].dobj.objType = DO_FDW;
10179 156 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10180 156 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10181 156 : AssignDumpId(&fdwinfo[i].dobj);
10182 156 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10183 156 : fdwinfo[i].dobj.namespace = NULL;
10184 156 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10185 156 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10186 156 : fdwinfo[i].dacl.privtype = 0;
10187 156 : fdwinfo[i].dacl.initprivs = NULL;
10188 156 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10189 156 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10190 156 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10191 156 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10192 :
10193 : /* Decide whether we want to dump it */
10194 156 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10195 :
10196 : /* Mark whether FDW has an ACL */
10197 156 : if (!PQgetisnull(res, i, i_fdwacl))
10198 98 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10199 : }
10200 :
10201 468 : PQclear(res);
10202 :
10203 468 : destroyPQExpBuffer(query);
10204 468 : }
10205 :
10206 : /*
10207 : * getForeignServers:
10208 : * get information about all foreign servers in the system catalogs
10209 : */
10210 : void
10211 468 : getForeignServers(Archive *fout)
10212 : {
10213 : PGresult *res;
10214 : int ntups;
10215 : int i;
10216 : PQExpBuffer query;
10217 : ForeignServerInfo *srvinfo;
10218 : int i_tableoid;
10219 : int i_oid;
10220 : int i_srvname;
10221 : int i_srvowner;
10222 : int i_srvfdw;
10223 : int i_srvtype;
10224 : int i_srvversion;
10225 : int i_srvacl;
10226 : int i_acldefault;
10227 : int i_srvoptions;
10228 :
10229 468 : query = createPQExpBuffer();
10230 :
10231 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10232 : "srvowner, "
10233 : "srvfdw, srvtype, srvversion, srvacl, "
10234 : "acldefault('S', srvowner) AS acldefault, "
10235 : "array_to_string(ARRAY("
10236 : "SELECT quote_ident(option_name) || ' ' || "
10237 : "quote_literal(option_value) "
10238 : "FROM pg_options_to_table(srvoptions) "
10239 : "ORDER BY option_name"
10240 : "), E',\n ') AS srvoptions "
10241 : "FROM pg_foreign_server");
10242 :
10243 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10244 :
10245 468 : ntups = PQntuples(res);
10246 :
10247 468 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10248 :
10249 468 : i_tableoid = PQfnumber(res, "tableoid");
10250 468 : i_oid = PQfnumber(res, "oid");
10251 468 : i_srvname = PQfnumber(res, "srvname");
10252 468 : i_srvowner = PQfnumber(res, "srvowner");
10253 468 : i_srvfdw = PQfnumber(res, "srvfdw");
10254 468 : i_srvtype = PQfnumber(res, "srvtype");
10255 468 : i_srvversion = PQfnumber(res, "srvversion");
10256 468 : i_srvacl = PQfnumber(res, "srvacl");
10257 468 : i_acldefault = PQfnumber(res, "acldefault");
10258 468 : i_srvoptions = PQfnumber(res, "srvoptions");
10259 :
10260 632 : for (i = 0; i < ntups; i++)
10261 : {
10262 164 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10263 164 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10264 164 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10265 164 : AssignDumpId(&srvinfo[i].dobj);
10266 164 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10267 164 : srvinfo[i].dobj.namespace = NULL;
10268 164 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10269 164 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10270 164 : srvinfo[i].dacl.privtype = 0;
10271 164 : srvinfo[i].dacl.initprivs = NULL;
10272 164 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10273 164 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10274 164 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10275 164 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10276 164 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10277 :
10278 : /* Decide whether we want to dump it */
10279 164 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10280 :
10281 : /* Servers have user mappings */
10282 164 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10283 :
10284 : /* Mark whether server has an ACL */
10285 164 : if (!PQgetisnull(res, i, i_srvacl))
10286 98 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10287 : }
10288 :
10289 468 : PQclear(res);
10290 :
10291 468 : destroyPQExpBuffer(query);
10292 468 : }
10293 :
10294 : /*
10295 : * getDefaultACLs:
10296 : * get information about all default ACL information in the system catalogs
10297 : */
10298 : void
10299 468 : getDefaultACLs(Archive *fout)
10300 : {
10301 468 : DumpOptions *dopt = fout->dopt;
10302 : DefaultACLInfo *daclinfo;
10303 : PQExpBuffer query;
10304 : PGresult *res;
10305 : int i_oid;
10306 : int i_tableoid;
10307 : int i_defaclrole;
10308 : int i_defaclnamespace;
10309 : int i_defaclobjtype;
10310 : int i_defaclacl;
10311 : int i_acldefault;
10312 : int i,
10313 : ntups;
10314 :
10315 468 : query = createPQExpBuffer();
10316 :
10317 : /*
10318 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10319 : * ACL for their object type. We should dump them as deltas from the
10320 : * default ACL, since that will be used as a starting point for
10321 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10322 : * non-global entries can only add privileges not revoke them. We must
10323 : * dump those as-is (i.e., as deltas from an empty ACL).
10324 : *
10325 : * We can use defaclobjtype as the object type for acldefault(), except
10326 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10327 : * 's'.
10328 : */
10329 468 : appendPQExpBufferStr(query,
10330 : "SELECT oid, tableoid, "
10331 : "defaclrole, "
10332 : "defaclnamespace, "
10333 : "defaclobjtype, "
10334 : "defaclacl, "
10335 : "CASE WHEN defaclnamespace = 0 THEN "
10336 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10337 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10338 : "defaclrole) ELSE '{}' END AS acldefault "
10339 : "FROM pg_default_acl");
10340 :
10341 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10342 :
10343 468 : ntups = PQntuples(res);
10344 :
10345 468 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10346 :
10347 468 : i_oid = PQfnumber(res, "oid");
10348 468 : i_tableoid = PQfnumber(res, "tableoid");
10349 468 : i_defaclrole = PQfnumber(res, "defaclrole");
10350 468 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10351 468 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10352 468 : i_defaclacl = PQfnumber(res, "defaclacl");
10353 468 : i_acldefault = PQfnumber(res, "acldefault");
10354 :
10355 860 : for (i = 0; i < ntups; i++)
10356 : {
10357 392 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10358 :
10359 392 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10360 392 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10361 392 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10362 392 : AssignDumpId(&daclinfo[i].dobj);
10363 : /* cheesy ... is it worth coming up with a better object name? */
10364 392 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10365 :
10366 392 : if (nspid != InvalidOid)
10367 196 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10368 : else
10369 196 : daclinfo[i].dobj.namespace = NULL;
10370 :
10371 392 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10372 392 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10373 392 : daclinfo[i].dacl.privtype = 0;
10374 392 : daclinfo[i].dacl.initprivs = NULL;
10375 392 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10376 392 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10377 :
10378 : /* Default ACLs are ACLs, of course */
10379 392 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10380 :
10381 : /* Decide whether we want to dump it */
10382 392 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10383 : }
10384 :
10385 468 : PQclear(res);
10386 :
10387 468 : destroyPQExpBuffer(query);
10388 468 : }
10389 :
10390 : /*
10391 : * getRoleName -- look up the name of a role, given its OID
10392 : *
10393 : * In current usage, we don't expect failures, so error out for a bad OID.
10394 : */
10395 : static const char *
10396 1478426 : getRoleName(const char *roleoid_str)
10397 : {
10398 1478426 : Oid roleoid = atooid(roleoid_str);
10399 :
10400 : /*
10401 : * Do binary search to find the appropriate item.
10402 : */
10403 1478426 : if (nrolenames > 0)
10404 : {
10405 1478426 : RoleNameItem *low = &rolenames[0];
10406 1478426 : RoleNameItem *high = &rolenames[nrolenames - 1];
10407 :
10408 5913856 : while (low <= high)
10409 : {
10410 5913856 : RoleNameItem *middle = low + (high - low) / 2;
10411 :
10412 5913856 : if (roleoid < middle->roleoid)
10413 4432780 : high = middle - 1;
10414 1481076 : else if (roleoid > middle->roleoid)
10415 2650 : low = middle + 1;
10416 : else
10417 1478426 : return middle->rolename; /* found a match */
10418 : }
10419 : }
10420 :
10421 0 : pg_fatal("role with OID %u does not exist", roleoid);
10422 : return NULL; /* keep compiler quiet */
10423 : }
10424 :
10425 : /*
10426 : * collectRoleNames --
10427 : *
10428 : * Construct a table of all known roles.
10429 : * The table is sorted by OID for speed in lookup.
10430 : */
10431 : static void
10432 470 : collectRoleNames(Archive *fout)
10433 : {
10434 : PGresult *res;
10435 : const char *query;
10436 : int i;
10437 :
10438 470 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10439 :
10440 470 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10441 :
10442 470 : nrolenames = PQntuples(res);
10443 :
10444 470 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10445 :
10446 10180 : for (i = 0; i < nrolenames; i++)
10447 : {
10448 9710 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10449 9710 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10450 : }
10451 :
10452 470 : PQclear(res);
10453 470 : }
10454 :
10455 : /*
10456 : * getAdditionalACLs
10457 : *
10458 : * We have now created all the DumpableObjects, and collected the ACL data
10459 : * that appears in the directly-associated catalog entries. However, there's
10460 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10461 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10462 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10463 : * Also, in versions having the pg_init_privs catalog, read that and load the
10464 : * information into the relevant DumpableObjects.
10465 : */
10466 : static void
10467 464 : getAdditionalACLs(Archive *fout)
10468 : {
10469 464 : PQExpBuffer query = createPQExpBuffer();
10470 : PGresult *res;
10471 : int ntups,
10472 : i;
10473 :
10474 : /* Check for per-column ACLs */
10475 464 : appendPQExpBufferStr(query,
10476 : "SELECT DISTINCT attrelid FROM pg_attribute "
10477 : "WHERE attacl IS NOT NULL");
10478 :
10479 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10480 :
10481 464 : ntups = PQntuples(res);
10482 1304 : for (i = 0; i < ntups; i++)
10483 : {
10484 840 : Oid relid = atooid(PQgetvalue(res, i, 0));
10485 : TableInfo *tblinfo;
10486 :
10487 840 : tblinfo = findTableByOid(relid);
10488 : /* OK to ignore tables we haven't got a DumpableObject for */
10489 840 : if (tblinfo)
10490 : {
10491 840 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10492 840 : tblinfo->hascolumnACLs = true;
10493 : }
10494 : }
10495 464 : PQclear(res);
10496 :
10497 : /* Fetch initial-privileges data */
10498 464 : if (fout->remoteVersion >= 90600)
10499 : {
10500 464 : printfPQExpBuffer(query,
10501 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10502 : "FROM pg_init_privs");
10503 :
10504 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10505 :
10506 464 : ntups = PQntuples(res);
10507 107336 : for (i = 0; i < ntups; i++)
10508 : {
10509 106872 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10510 106872 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10511 106872 : int objsubid = atoi(PQgetvalue(res, i, 2));
10512 106872 : char privtype = *(PQgetvalue(res, i, 3));
10513 106872 : char *initprivs = PQgetvalue(res, i, 4);
10514 : CatalogId objId;
10515 : DumpableObject *dobj;
10516 :
10517 106872 : objId.tableoid = classoid;
10518 106872 : objId.oid = objoid;
10519 106872 : dobj = findObjectByCatalogId(objId);
10520 : /* OK to ignore entries we haven't got a DumpableObject for */
10521 106872 : if (dobj)
10522 : {
10523 : /* Cope with sub-object initprivs */
10524 76344 : if (objsubid != 0)
10525 : {
10526 7936 : if (dobj->objType == DO_TABLE)
10527 : {
10528 : /* For a column initprivs, set the table's ACL flags */
10529 7936 : dobj->components |= DUMP_COMPONENT_ACL;
10530 7936 : ((TableInfo *) dobj)->hascolumnACLs = true;
10531 : }
10532 : else
10533 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10534 : classoid, objoid, objsubid);
10535 8392 : continue;
10536 : }
10537 :
10538 : /*
10539 : * We ignore any pg_init_privs.initprivs entry for the public
10540 : * schema, as explained in getNamespaces().
10541 : */
10542 68408 : if (dobj->objType == DO_NAMESPACE &&
10543 920 : strcmp(dobj->name, "public") == 0)
10544 456 : continue;
10545 :
10546 : /* Else it had better be of a type we think has ACLs */
10547 67952 : if (dobj->objType == DO_NAMESPACE ||
10548 67488 : dobj->objType == DO_TYPE ||
10549 67440 : dobj->objType == DO_FUNC ||
10550 67248 : dobj->objType == DO_AGG ||
10551 67200 : dobj->objType == DO_TABLE ||
10552 0 : dobj->objType == DO_PROCLANG ||
10553 0 : dobj->objType == DO_FDW ||
10554 0 : dobj->objType == DO_FOREIGN_SERVER)
10555 67952 : {
10556 67952 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10557 :
10558 67952 : daobj->dacl.privtype = privtype;
10559 67952 : daobj->dacl.initprivs = pstrdup(initprivs);
10560 : }
10561 : else
10562 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10563 : classoid, objoid, objsubid);
10564 : }
10565 : }
10566 464 : PQclear(res);
10567 : }
10568 :
10569 464 : destroyPQExpBuffer(query);
10570 464 : }
10571 :
10572 : /*
10573 : * dumpCommentExtended --
10574 : *
10575 : * This routine is used to dump any comments associated with the
10576 : * object handed to this routine. The routine takes the object type
10577 : * and object name (ready to print, except for schema decoration), plus
10578 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10579 : * plus catalog ID and subid which are the lookup key for pg_description,
10580 : * plus the dump ID for the object (for setting a dependency).
10581 : * If a matching pg_description entry is found, it is dumped.
10582 : *
10583 : * Note: in some cases, such as comments for triggers and rules, the "type"
10584 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10585 : * but it doesn't seem worth complicating the API for all callers to make
10586 : * it cleaner.
10587 : *
10588 : * Note: although this routine takes a dumpId for dependency purposes,
10589 : * that purpose is just to mark the dependency in the emitted dump file
10590 : * for possible future use by pg_restore. We do NOT use it for determining
10591 : * ordering of the comment in the dump file, because this routine is called
10592 : * after dependency sorting occurs. This routine should be called just after
10593 : * calling ArchiveEntry() for the specified object.
10594 : */
10595 : static void
10596 13000 : dumpCommentExtended(Archive *fout, const char *type,
10597 : const char *name, const char *namespace,
10598 : const char *owner, CatalogId catalogId,
10599 : int subid, DumpId dumpId,
10600 : const char *initdb_comment)
10601 : {
10602 13000 : DumpOptions *dopt = fout->dopt;
10603 : CommentItem *comments;
10604 : int ncomments;
10605 :
10606 : /* do nothing, if --no-comments is supplied */
10607 13000 : if (dopt->no_comments)
10608 0 : return;
10609 :
10610 : /* Comments are schema not data ... except LO comments are data */
10611 13000 : if (strcmp(type, "LARGE OBJECT") != 0)
10612 : {
10613 12878 : if (!dopt->dumpSchema)
10614 0 : return;
10615 : }
10616 : else
10617 : {
10618 : /* We do dump LO comments in binary-upgrade mode */
10619 122 : if (!dopt->dumpData && !dopt->binary_upgrade)
10620 0 : return;
10621 : }
10622 :
10623 : /* Search for comments associated with catalogId, using table */
10624 13000 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10625 : &comments);
10626 :
10627 : /* Is there one matching the subid? */
10628 13000 : while (ncomments > 0)
10629 : {
10630 12904 : if (comments->objsubid == subid)
10631 12904 : break;
10632 0 : comments++;
10633 0 : ncomments--;
10634 : }
10635 :
10636 13000 : if (initdb_comment != NULL)
10637 : {
10638 : static CommentItem empty_comment = {.descr = ""};
10639 :
10640 : /*
10641 : * initdb creates this object with a comment. Skip dumping the
10642 : * initdb-provided comment, which would complicate matters for
10643 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10644 : * comment, replicate that.
10645 : */
10646 340 : if (ncomments == 0)
10647 : {
10648 8 : comments = &empty_comment;
10649 8 : ncomments = 1;
10650 : }
10651 332 : else if (strcmp(comments->descr, initdb_comment) == 0)
10652 332 : ncomments = 0;
10653 : }
10654 :
10655 : /* If a comment exists, build COMMENT ON statement */
10656 13000 : if (ncomments > 0)
10657 : {
10658 12580 : PQExpBuffer query = createPQExpBuffer();
10659 12580 : PQExpBuffer tag = createPQExpBuffer();
10660 :
10661 12580 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10662 12580 : if (namespace && *namespace)
10663 12222 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10664 12580 : appendPQExpBuffer(query, "%s IS ", name);
10665 12580 : appendStringLiteralAH(query, comments->descr, fout);
10666 12580 : appendPQExpBufferStr(query, ";\n");
10667 :
10668 12580 : appendPQExpBuffer(tag, "%s %s", type, name);
10669 :
10670 : /*
10671 : * We mark comments as SECTION_NONE because they really belong in the
10672 : * same section as their parent, whether that is pre-data or
10673 : * post-data.
10674 : */
10675 12580 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10676 12580 : ARCHIVE_OPTS(.tag = tag->data,
10677 : .namespace = namespace,
10678 : .owner = owner,
10679 : .description = "COMMENT",
10680 : .section = SECTION_NONE,
10681 : .createStmt = query->data,
10682 : .deps = &dumpId,
10683 : .nDeps = 1));
10684 :
10685 12580 : destroyPQExpBuffer(query);
10686 12580 : destroyPQExpBuffer(tag);
10687 : }
10688 : }
10689 :
10690 : /*
10691 : * dumpComment --
10692 : *
10693 : * Typical simplification of the above function.
10694 : */
10695 : static inline void
10696 12592 : dumpComment(Archive *fout, const char *type,
10697 : const char *name, const char *namespace,
10698 : const char *owner, CatalogId catalogId,
10699 : int subid, DumpId dumpId)
10700 : {
10701 12592 : dumpCommentExtended(fout, type, name, namespace, owner,
10702 : catalogId, subid, dumpId, NULL);
10703 12592 : }
10704 :
10705 : /*
10706 : * appendNamedArgument --
10707 : *
10708 : * Convenience routine for constructing parameters of the form:
10709 : * 'paraname', 'value'::type
10710 : */
10711 : static void
10712 11288 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10713 : const char *argtype, const char *argval)
10714 : {
10715 11288 : appendPQExpBufferStr(out, ",\n\t");
10716 :
10717 11288 : appendStringLiteralAH(out, argname, fout);
10718 11288 : appendPQExpBufferStr(out, ", ");
10719 :
10720 11288 : appendStringLiteralAH(out, argval, fout);
10721 11288 : appendPQExpBuffer(out, "::%s", argtype);
10722 11288 : }
10723 :
10724 : /*
10725 : * fetchAttributeStats --
10726 : *
10727 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10728 : */
10729 : static PGresult *
10730 2242 : fetchAttributeStats(Archive *fout)
10731 : {
10732 2242 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10733 2242 : PQExpBuffer nspnames = createPQExpBuffer();
10734 2242 : PQExpBuffer relnames = createPQExpBuffer();
10735 2242 : int count = 0;
10736 2242 : PGresult *res = NULL;
10737 : static TocEntry *te;
10738 : static bool restarted;
10739 2242 : int max_rels = MAX_ATTR_STATS_RELS;
10740 :
10741 : /*
10742 : * Our query for retrieving statistics for multiple relations uses WITH
10743 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10744 : * in v9.4. For older versions, we resort to gathering statistics for a
10745 : * single relation at a time.
10746 : */
10747 2242 : if (fout->remoteVersion < 90400)
10748 0 : max_rels = 1;
10749 :
10750 : /* If we're just starting, set our TOC pointer. */
10751 2242 : if (!te)
10752 106 : te = AH->toc->next;
10753 :
10754 : /*
10755 : * We can't easily avoid a second TOC scan for the tar format because it
10756 : * writes restore.sql separately, which means we must execute the queries
10757 : * twice. This feels risky, but there is no known reason it should
10758 : * generate different output than the first pass. Even if it does, the
10759 : * worst-case scenario is that restore.sql might have different statistics
10760 : * data than the archive.
10761 : */
10762 2242 : if (!restarted && te == AH->toc && AH->format == archTar)
10763 : {
10764 2 : te = AH->toc->next;
10765 2 : restarted = true;
10766 : }
10767 :
10768 2242 : appendPQExpBufferChar(nspnames, '{');
10769 2242 : appendPQExpBufferChar(relnames, '{');
10770 :
10771 : /*
10772 : * Scan the TOC for the next set of relevant stats entries. We assume
10773 : * that statistics are dumped in the order they are listed in the TOC.
10774 : * This is perhaps not the sturdiest assumption, so we verify it matches
10775 : * reality in dumpRelationStats_dumper().
10776 : */
10777 33150 : for (; te != AH->toc && count < max_rels; te = te->next)
10778 : {
10779 30908 : if ((te->reqs & REQ_STATS) != 0 &&
10780 6834 : strcmp(te->desc, "STATISTICS DATA") == 0)
10781 : {
10782 6834 : appendPGArray(nspnames, te->namespace);
10783 6834 : appendPGArray(relnames, te->tag);
10784 6834 : count++;
10785 : }
10786 : }
10787 :
10788 2242 : appendPQExpBufferChar(nspnames, '}');
10789 2242 : appendPQExpBufferChar(relnames, '}');
10790 :
10791 : /* Execute the query for the next batch of relations. */
10792 2242 : if (count > 0)
10793 : {
10794 200 : PQExpBuffer query = createPQExpBuffer();
10795 :
10796 200 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
10797 200 : appendStringLiteralAH(query, nspnames->data, fout);
10798 200 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
10799 200 : appendStringLiteralAH(query, relnames->data, fout);
10800 200 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
10801 200 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10802 200 : destroyPQExpBuffer(query);
10803 : }
10804 :
10805 2242 : destroyPQExpBuffer(nspnames);
10806 2242 : destroyPQExpBuffer(relnames);
10807 2242 : return res;
10808 : }
10809 :
10810 : /*
10811 : * dumpRelationStats_dumper --
10812 : *
10813 : * Generate command to import stats into the relation on the new database.
10814 : * This routine is called by the Archiver when it wants the statistics to be
10815 : * dumped.
10816 : */
10817 : static char *
10818 6834 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
10819 : {
10820 6834 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
10821 : static PGresult *res;
10822 : static int rownum;
10823 : PQExpBuffer query;
10824 : PQExpBufferData out_data;
10825 6834 : PQExpBuffer out = &out_data;
10826 : int i_schemaname;
10827 : int i_tablename;
10828 : int i_attname;
10829 : int i_inherited;
10830 : int i_null_frac;
10831 : int i_avg_width;
10832 : int i_n_distinct;
10833 : int i_most_common_vals;
10834 : int i_most_common_freqs;
10835 : int i_histogram_bounds;
10836 : int i_correlation;
10837 : int i_most_common_elems;
10838 : int i_most_common_elem_freqs;
10839 : int i_elem_count_histogram;
10840 : int i_range_length_histogram;
10841 : int i_range_empty_frac;
10842 : int i_range_bounds_histogram;
10843 : static TocEntry *expected_te;
10844 :
10845 : /*
10846 : * fetchAttributeStats() assumes that the statistics are dumped in the
10847 : * order they are listed in the TOC. We verify that here for safety.
10848 : */
10849 6834 : if (!expected_te)
10850 106 : expected_te = ((ArchiveHandle *) fout)->toc;
10851 :
10852 6834 : expected_te = expected_te->next;
10853 27218 : while ((expected_te->reqs & REQ_STATS) == 0 ||
10854 6834 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
10855 20384 : expected_te = expected_te->next;
10856 :
10857 6834 : if (te != expected_te)
10858 0 : pg_fatal("stats dumped out of order (current: %d %s %s) (expected: %d %s %s)",
10859 : te->dumpId, te->desc, te->tag,
10860 : expected_te->dumpId, expected_te->desc, expected_te->tag);
10861 :
10862 6834 : query = createPQExpBuffer();
10863 6834 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
10864 : {
10865 106 : appendPQExpBufferStr(query,
10866 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
10867 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
10868 : "s.null_frac, s.avg_width, s.n_distinct, "
10869 : "s.most_common_vals, s.most_common_freqs, "
10870 : "s.histogram_bounds, s.correlation, "
10871 : "s.most_common_elems, s.most_common_elem_freqs, "
10872 : "s.elem_count_histogram, ");
10873 :
10874 106 : if (fout->remoteVersion >= 170000)
10875 106 : appendPQExpBufferStr(query,
10876 : "s.range_length_histogram, "
10877 : "s.range_empty_frac, "
10878 : "s.range_bounds_histogram ");
10879 : else
10880 0 : appendPQExpBufferStr(query,
10881 : "NULL AS range_length_histogram,"
10882 : "NULL AS range_empty_frac,"
10883 : "NULL AS range_bounds_histogram ");
10884 :
10885 : /*
10886 : * The results must be in the order of the relations supplied in the
10887 : * parameters to ensure we remain in sync as we walk through the TOC.
10888 : * The redundant filter clause on s.tablename = ANY(...) seems
10889 : * sufficient to convince the planner to use
10890 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
10891 : * This may not work for all versions.
10892 : *
10893 : * Our query for retrieving statistics for multiple relations uses
10894 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
10895 : * introduced in v9.4. For older versions, we resort to gathering
10896 : * statistics for a single relation at a time.
10897 : */
10898 106 : if (fout->remoteVersion >= 90400)
10899 106 : appendPQExpBufferStr(query,
10900 : "FROM pg_catalog.pg_stats s "
10901 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
10902 : "ON s.schemaname = u.schemaname "
10903 : "AND s.tablename = u.tablename "
10904 : "WHERE s.tablename = ANY($2) "
10905 : "ORDER BY u.ord, s.attname, s.inherited");
10906 : else
10907 0 : appendPQExpBufferStr(query,
10908 : "FROM pg_catalog.pg_stats s "
10909 : "WHERE s.schemaname = $1[1] "
10910 : "AND s.tablename = $2[1] "
10911 : "ORDER BY s.attname, s.inherited");
10912 :
10913 106 : ExecuteSqlStatement(fout, query->data);
10914 :
10915 106 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
10916 106 : resetPQExpBuffer(query);
10917 : }
10918 :
10919 6834 : initPQExpBuffer(out);
10920 :
10921 : /* restore relation stats */
10922 6834 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
10923 6834 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
10924 : fout->remoteVersion);
10925 6834 : appendPQExpBufferStr(out, "\t'schemaname', ");
10926 6834 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
10927 6834 : appendPQExpBufferStr(out, ",\n");
10928 6834 : appendPQExpBufferStr(out, "\t'relname', ");
10929 6834 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
10930 6834 : appendPQExpBufferStr(out, ",\n");
10931 6834 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
10932 :
10933 : /*
10934 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
10935 : * the relation is empty, or it could mean that it hadn't yet been
10936 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
10937 : * This ambiguity allegedly can cause the planner to choose inefficient
10938 : * plans after restoring to v18 or newer. To deal with this, let's just
10939 : * set reltuples to -1 in that case.
10940 : */
10941 6834 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
10942 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
10943 : else
10944 6834 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
10945 :
10946 6834 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
10947 6834 : rsinfo->relallvisible);
10948 :
10949 6834 : if (fout->remoteVersion >= 180000)
10950 6834 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
10951 :
10952 6834 : appendPQExpBufferStr(out, "\n);\n");
10953 :
10954 : /* Fetch the next batch of attribute statistics if needed. */
10955 6834 : if (rownum >= PQntuples(res))
10956 : {
10957 2242 : PQclear(res);
10958 2242 : res = fetchAttributeStats(fout);
10959 2242 : rownum = 0;
10960 : }
10961 :
10962 6834 : i_schemaname = PQfnumber(res, "schemaname");
10963 6834 : i_tablename = PQfnumber(res, "tablename");
10964 6834 : i_attname = PQfnumber(res, "attname");
10965 6834 : i_inherited = PQfnumber(res, "inherited");
10966 6834 : i_null_frac = PQfnumber(res, "null_frac");
10967 6834 : i_avg_width = PQfnumber(res, "avg_width");
10968 6834 : i_n_distinct = PQfnumber(res, "n_distinct");
10969 6834 : i_most_common_vals = PQfnumber(res, "most_common_vals");
10970 6834 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
10971 6834 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
10972 6834 : i_correlation = PQfnumber(res, "correlation");
10973 6834 : i_most_common_elems = PQfnumber(res, "most_common_elems");
10974 6834 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
10975 6834 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
10976 6834 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
10977 6834 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
10978 6834 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
10979 :
10980 : /* restore attribute stats */
10981 8532 : for (; rownum < PQntuples(res); rownum++)
10982 : {
10983 : const char *attname;
10984 :
10985 : /* Stop if the next stat row in our cache isn't for this relation. */
10986 6290 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
10987 1698 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
10988 : break;
10989 :
10990 1698 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
10991 1698 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
10992 : fout->remoteVersion);
10993 1698 : appendPQExpBufferStr(out, "\t'schemaname', ");
10994 1698 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
10995 1698 : appendPQExpBufferStr(out, ",\n\t'relname', ");
10996 1698 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
10997 :
10998 1698 : if (PQgetisnull(res, rownum, i_attname))
10999 0 : pg_fatal("attname cannot be NULL");
11000 1698 : attname = PQgetvalue(res, rownum, i_attname);
11001 :
11002 : /*
11003 : * Indexes look up attname in indAttNames to derive attnum, all others
11004 : * use attname directly. We must specify attnum for indexes, since
11005 : * their attnames are not necessarily stable across dump/reload.
11006 : */
11007 1698 : if (rsinfo->nindAttNames == 0)
11008 : {
11009 1616 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11010 1616 : appendStringLiteralAH(out, attname, fout);
11011 : }
11012 : else
11013 : {
11014 82 : bool found = false;
11015 :
11016 152 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11017 : {
11018 152 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11019 : {
11020 82 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11021 : i + 1);
11022 82 : found = true;
11023 82 : break;
11024 : }
11025 : }
11026 :
11027 82 : if (!found)
11028 0 : pg_fatal("could not find index attname \"%s\"", attname);
11029 : }
11030 :
11031 1698 : if (!PQgetisnull(res, rownum, i_inherited))
11032 1698 : appendNamedArgument(out, fout, "inherited", "boolean",
11033 1698 : PQgetvalue(res, rownum, i_inherited));
11034 1698 : if (!PQgetisnull(res, rownum, i_null_frac))
11035 1698 : appendNamedArgument(out, fout, "null_frac", "real",
11036 1698 : PQgetvalue(res, rownum, i_null_frac));
11037 1698 : if (!PQgetisnull(res, rownum, i_avg_width))
11038 1698 : appendNamedArgument(out, fout, "avg_width", "integer",
11039 1698 : PQgetvalue(res, rownum, i_avg_width));
11040 1698 : if (!PQgetisnull(res, rownum, i_n_distinct))
11041 1698 : appendNamedArgument(out, fout, "n_distinct", "real",
11042 1698 : PQgetvalue(res, rownum, i_n_distinct));
11043 1698 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11044 874 : appendNamedArgument(out, fout, "most_common_vals", "text",
11045 874 : PQgetvalue(res, rownum, i_most_common_vals));
11046 1698 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11047 874 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11048 874 : PQgetvalue(res, rownum, i_most_common_freqs));
11049 1698 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11050 1060 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11051 1060 : PQgetvalue(res, rownum, i_histogram_bounds));
11052 1698 : if (!PQgetisnull(res, rownum, i_correlation))
11053 1618 : appendNamedArgument(out, fout, "correlation", "real",
11054 1618 : PQgetvalue(res, rownum, i_correlation));
11055 1698 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11056 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11057 16 : PQgetvalue(res, rownum, i_most_common_elems));
11058 1698 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11059 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11060 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11061 1698 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11062 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11063 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11064 1698 : if (fout->remoteVersion >= 170000)
11065 : {
11066 1698 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11067 8 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11068 8 : PQgetvalue(res, rownum, i_range_length_histogram));
11069 1698 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11070 8 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11071 8 : PQgetvalue(res, rownum, i_range_empty_frac));
11072 1698 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11073 8 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11074 8 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11075 : }
11076 1698 : appendPQExpBufferStr(out, "\n);\n");
11077 : }
11078 :
11079 6834 : destroyPQExpBuffer(query);
11080 6834 : return out->data;
11081 : }
11082 :
11083 : /*
11084 : * dumpRelationStats --
11085 : *
11086 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11087 : * care of gathering the statistics and generating the restore commands when
11088 : * they are needed.
11089 : */
11090 : static void
11091 6970 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11092 : {
11093 6970 : const DumpableObject *dobj = &rsinfo->dobj;
11094 :
11095 : /* nothing to do if we are not dumping statistics */
11096 6970 : if (!fout->dopt->dumpStatistics)
11097 0 : return;
11098 :
11099 6970 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11100 6970 : ARCHIVE_OPTS(.tag = dobj->name,
11101 : .namespace = dobj->namespace->dobj.name,
11102 : .description = "STATISTICS DATA",
11103 : .section = rsinfo->section,
11104 : .defnFn = dumpRelationStats_dumper,
11105 : .defnArg = rsinfo,
11106 : .deps = dobj->dependencies,
11107 : .nDeps = dobj->nDeps));
11108 : }
11109 :
11110 : /*
11111 : * dumpTableComment --
11112 : *
11113 : * As above, but dump comments for both the specified table (or view)
11114 : * and its columns.
11115 : */
11116 : static void
11117 176 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11118 : const char *reltypename)
11119 : {
11120 176 : DumpOptions *dopt = fout->dopt;
11121 : CommentItem *comments;
11122 : int ncomments;
11123 : PQExpBuffer query;
11124 : PQExpBuffer tag;
11125 :
11126 : /* do nothing, if --no-comments is supplied */
11127 176 : if (dopt->no_comments)
11128 0 : return;
11129 :
11130 : /* Comments are SCHEMA not data */
11131 176 : if (!dopt->dumpSchema)
11132 0 : return;
11133 :
11134 : /* Search for comments associated with relation, using table */
11135 176 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11136 176 : tbinfo->dobj.catId.oid,
11137 : &comments);
11138 :
11139 : /* If comments exist, build COMMENT ON statements */
11140 176 : if (ncomments <= 0)
11141 0 : return;
11142 :
11143 176 : query = createPQExpBuffer();
11144 176 : tag = createPQExpBuffer();
11145 :
11146 496 : while (ncomments > 0)
11147 : {
11148 320 : const char *descr = comments->descr;
11149 320 : int objsubid = comments->objsubid;
11150 :
11151 320 : if (objsubid == 0)
11152 : {
11153 72 : resetPQExpBuffer(tag);
11154 72 : appendPQExpBuffer(tag, "%s %s", reltypename,
11155 72 : fmtId(tbinfo->dobj.name));
11156 :
11157 72 : resetPQExpBuffer(query);
11158 72 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11159 72 : fmtQualifiedDumpable(tbinfo));
11160 72 : appendStringLiteralAH(query, descr, fout);
11161 72 : appendPQExpBufferStr(query, ";\n");
11162 :
11163 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11164 72 : ARCHIVE_OPTS(.tag = tag->data,
11165 : .namespace = tbinfo->dobj.namespace->dobj.name,
11166 : .owner = tbinfo->rolname,
11167 : .description = "COMMENT",
11168 : .section = SECTION_NONE,
11169 : .createStmt = query->data,
11170 : .deps = &(tbinfo->dobj.dumpId),
11171 : .nDeps = 1));
11172 : }
11173 248 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11174 : {
11175 248 : resetPQExpBuffer(tag);
11176 248 : appendPQExpBuffer(tag, "COLUMN %s.",
11177 248 : fmtId(tbinfo->dobj.name));
11178 248 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11179 :
11180 248 : resetPQExpBuffer(query);
11181 248 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11182 248 : fmtQualifiedDumpable(tbinfo));
11183 248 : appendPQExpBuffer(query, "%s IS ",
11184 248 : fmtId(tbinfo->attnames[objsubid - 1]));
11185 248 : appendStringLiteralAH(query, descr, fout);
11186 248 : appendPQExpBufferStr(query, ";\n");
11187 :
11188 248 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11189 248 : ARCHIVE_OPTS(.tag = tag->data,
11190 : .namespace = tbinfo->dobj.namespace->dobj.name,
11191 : .owner = tbinfo->rolname,
11192 : .description = "COMMENT",
11193 : .section = SECTION_NONE,
11194 : .createStmt = query->data,
11195 : .deps = &(tbinfo->dobj.dumpId),
11196 : .nDeps = 1));
11197 : }
11198 :
11199 320 : comments++;
11200 320 : ncomments--;
11201 : }
11202 :
11203 176 : destroyPQExpBuffer(query);
11204 176 : destroyPQExpBuffer(tag);
11205 : }
11206 :
11207 : /*
11208 : * findComments --
11209 : *
11210 : * Find the comment(s), if any, associated with the given object. All the
11211 : * objsubid values associated with the given classoid/objoid are found with
11212 : * one search.
11213 : */
11214 : static int
11215 13248 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11216 : {
11217 13248 : CommentItem *middle = NULL;
11218 : CommentItem *low;
11219 : CommentItem *high;
11220 : int nmatch;
11221 :
11222 : /*
11223 : * Do binary search to find some item matching the object.
11224 : */
11225 13248 : low = &comments[0];
11226 13248 : high = &comments[ncomments - 1];
11227 131376 : while (low <= high)
11228 : {
11229 131280 : middle = low + (high - low) / 2;
11230 :
11231 131280 : if (classoid < middle->classoid)
11232 14814 : high = middle - 1;
11233 116466 : else if (classoid > middle->classoid)
11234 14608 : low = middle + 1;
11235 101858 : else if (objoid < middle->objoid)
11236 43192 : high = middle - 1;
11237 58666 : else if (objoid > middle->objoid)
11238 45514 : low = middle + 1;
11239 : else
11240 13152 : break; /* found a match */
11241 : }
11242 :
11243 13248 : if (low > high) /* no matches */
11244 : {
11245 96 : *items = NULL;
11246 96 : return 0;
11247 : }
11248 :
11249 : /*
11250 : * Now determine how many items match the object. The search loop
11251 : * invariant still holds: only items between low and high inclusive could
11252 : * match.
11253 : */
11254 13152 : nmatch = 1;
11255 13152 : while (middle > low)
11256 : {
11257 6316 : if (classoid != middle[-1].classoid ||
11258 6124 : objoid != middle[-1].objoid)
11259 : break;
11260 0 : middle--;
11261 0 : nmatch++;
11262 : }
11263 :
11264 13152 : *items = middle;
11265 :
11266 13152 : middle += nmatch;
11267 13296 : while (middle <= high)
11268 : {
11269 7156 : if (classoid != middle->classoid ||
11270 6336 : objoid != middle->objoid)
11271 : break;
11272 144 : middle++;
11273 144 : nmatch++;
11274 : }
11275 :
11276 13152 : return nmatch;
11277 : }
11278 :
11279 : /*
11280 : * collectComments --
11281 : *
11282 : * Construct a table of all comments available for database objects;
11283 : * also set the has-comment component flag for each relevant object.
11284 : *
11285 : * We used to do per-object queries for the comments, but it's much faster
11286 : * to pull them all over at once, and on most databases the memory cost
11287 : * isn't high.
11288 : *
11289 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11290 : */
11291 : static void
11292 468 : collectComments(Archive *fout)
11293 : {
11294 : PGresult *res;
11295 : PQExpBuffer query;
11296 : int i_description;
11297 : int i_classoid;
11298 : int i_objoid;
11299 : int i_objsubid;
11300 : int ntups;
11301 : int i;
11302 : DumpableObject *dobj;
11303 :
11304 468 : query = createPQExpBuffer();
11305 :
11306 468 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11307 : "FROM pg_catalog.pg_description "
11308 : "ORDER BY classoid, objoid, objsubid");
11309 :
11310 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11311 :
11312 : /* Construct lookup table containing OIDs in numeric form */
11313 :
11314 468 : i_description = PQfnumber(res, "description");
11315 468 : i_classoid = PQfnumber(res, "classoid");
11316 468 : i_objoid = PQfnumber(res, "objoid");
11317 468 : i_objsubid = PQfnumber(res, "objsubid");
11318 :
11319 468 : ntups = PQntuples(res);
11320 :
11321 468 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11322 468 : ncomments = 0;
11323 468 : dobj = NULL;
11324 :
11325 2488750 : for (i = 0; i < ntups; i++)
11326 : {
11327 : CatalogId objId;
11328 : int subid;
11329 :
11330 2488282 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11331 2488282 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11332 2488282 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11333 :
11334 : /* We needn't remember comments that don't match any dumpable object */
11335 2488282 : if (dobj == NULL ||
11336 897030 : dobj->catId.tableoid != objId.tableoid ||
11337 891350 : dobj->catId.oid != objId.oid)
11338 2488086 : dobj = findObjectByCatalogId(objId);
11339 2488282 : if (dobj == NULL)
11340 1590800 : continue;
11341 :
11342 : /*
11343 : * Comments on columns of composite types are linked to the type's
11344 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11345 : * in the type's own DumpableObject.
11346 : */
11347 897482 : if (subid != 0 && dobj->objType == DO_TABLE &&
11348 432 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11349 98 : {
11350 : TypeInfo *cTypeInfo;
11351 :
11352 98 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11353 98 : if (cTypeInfo)
11354 98 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11355 : }
11356 : else
11357 897384 : dobj->components |= DUMP_COMPONENT_COMMENT;
11358 :
11359 897482 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11360 897482 : comments[ncomments].classoid = objId.tableoid;
11361 897482 : comments[ncomments].objoid = objId.oid;
11362 897482 : comments[ncomments].objsubid = subid;
11363 897482 : ncomments++;
11364 : }
11365 :
11366 468 : PQclear(res);
11367 468 : destroyPQExpBuffer(query);
11368 468 : }
11369 :
11370 : /*
11371 : * dumpDumpableObject
11372 : *
11373 : * This routine and its subsidiaries are responsible for creating
11374 : * ArchiveEntries (TOC objects) for each object to be dumped.
11375 : */
11376 : static void
11377 1732898 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11378 : {
11379 : /*
11380 : * Clear any dump-request bits for components that don't exist for this
11381 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11382 : * request for every kind of object.)
11383 : */
11384 1732898 : dobj->dump &= dobj->components;
11385 :
11386 : /* Now, short-circuit if there's nothing to be done here. */
11387 1732898 : if (dobj->dump == 0)
11388 1536726 : return;
11389 :
11390 196172 : switch (dobj->objType)
11391 : {
11392 1218 : case DO_NAMESPACE:
11393 1218 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11394 1218 : break;
11395 38 : case DO_EXTENSION:
11396 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11397 38 : break;
11398 2148 : case DO_TYPE:
11399 2148 : dumpType(fout, (const TypeInfo *) dobj);
11400 2148 : break;
11401 196 : case DO_SHELL_TYPE:
11402 196 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11403 196 : break;
11404 5376 : case DO_FUNC:
11405 5376 : dumpFunc(fout, (const FuncInfo *) dobj);
11406 5376 : break;
11407 874 : case DO_AGG:
11408 874 : dumpAgg(fout, (const AggInfo *) dobj);
11409 874 : break;
11410 5098 : case DO_OPERATOR:
11411 5098 : dumpOpr(fout, (const OprInfo *) dobj);
11412 5098 : break;
11413 182 : case DO_ACCESS_METHOD:
11414 182 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11415 182 : break;
11416 1362 : case DO_OPCLASS:
11417 1362 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11418 1362 : break;
11419 1156 : case DO_OPFAMILY:
11420 1156 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11421 1156 : break;
11422 5108 : case DO_COLLATION:
11423 5108 : dumpCollation(fout, (const CollInfo *) dobj);
11424 5108 : break;
11425 852 : case DO_CONVERSION:
11426 852 : dumpConversion(fout, (const ConvInfo *) dobj);
11427 852 : break;
11428 80656 : case DO_TABLE:
11429 80656 : dumpTable(fout, (const TableInfo *) dobj);
11430 80656 : break;
11431 3900 : case DO_TABLE_ATTACH:
11432 3900 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11433 3900 : break;
11434 2900 : case DO_ATTRDEF:
11435 2900 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11436 2900 : break;
11437 6938 : case DO_INDEX:
11438 6938 : dumpIndex(fout, (const IndxInfo *) dobj);
11439 6938 : break;
11440 1480 : case DO_INDEX_ATTACH:
11441 1480 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11442 1480 : break;
11443 314 : case DO_STATSEXT:
11444 314 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11445 314 : break;
11446 852 : case DO_REFRESH_MATVIEW:
11447 852 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11448 852 : break;
11449 2984 : case DO_RULE:
11450 2984 : dumpRule(fout, (const RuleInfo *) dobj);
11451 2984 : break;
11452 1476 : case DO_TRIGGER:
11453 1476 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11454 1476 : break;
11455 98 : case DO_EVENT_TRIGGER:
11456 98 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11457 98 : break;
11458 5898 : case DO_CONSTRAINT:
11459 5898 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11460 5898 : break;
11461 472 : case DO_FK_CONSTRAINT:
11462 472 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11463 472 : break;
11464 180 : case DO_PROCLANG:
11465 180 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11466 180 : break;
11467 178 : case DO_CAST:
11468 178 : dumpCast(fout, (const CastInfo *) dobj);
11469 178 : break;
11470 98 : case DO_TRANSFORM:
11471 98 : dumpTransform(fout, (const TransformInfo *) dobj);
11472 98 : break;
11473 1136 : case DO_SEQUENCE_SET:
11474 1136 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11475 1136 : break;
11476 12386 : case DO_TABLE_DATA:
11477 12386 : dumpTableData(fout, (const TableDataInfo *) dobj);
11478 12386 : break;
11479 38684 : case DO_DUMMY_TYPE:
11480 : /* table rowtypes and array types are never dumped separately */
11481 38684 : break;
11482 90 : case DO_TSPARSER:
11483 90 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11484 90 : break;
11485 408 : case DO_TSDICT:
11486 408 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11487 408 : break;
11488 114 : case DO_TSTEMPLATE:
11489 114 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11490 114 : break;
11491 328 : case DO_TSCONFIG:
11492 328 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11493 328 : break;
11494 118 : case DO_FDW:
11495 118 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11496 118 : break;
11497 126 : case DO_FOREIGN_SERVER:
11498 126 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11499 126 : break;
11500 332 : case DO_DEFAULT_ACL:
11501 332 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11502 332 : break;
11503 164 : case DO_LARGE_OBJECT:
11504 164 : dumpLO(fout, (const LoInfo *) dobj);
11505 164 : break;
11506 164 : case DO_LARGE_OBJECT_DATA:
11507 164 : if (dobj->dump & DUMP_COMPONENT_DATA)
11508 : {
11509 : LoInfo *loinfo;
11510 : TocEntry *te;
11511 :
11512 164 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11513 164 : if (loinfo == NULL)
11514 0 : pg_fatal("missing metadata for large objects \"%s\"",
11515 : dobj->name);
11516 :
11517 164 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11518 164 : ARCHIVE_OPTS(.tag = dobj->name,
11519 : .owner = loinfo->rolname,
11520 : .description = "BLOBS",
11521 : .section = SECTION_DATA,
11522 : .deps = dobj->dependencies,
11523 : .nDeps = dobj->nDeps,
11524 : .dumpFn = dumpLOs,
11525 : .dumpArg = loinfo));
11526 :
11527 : /*
11528 : * Set the TocEntry's dataLength in case we are doing a
11529 : * parallel dump and want to order dump jobs by table size.
11530 : * (We need some size estimate for every TocEntry with a
11531 : * DataDumper function.) We don't currently have any cheap
11532 : * way to estimate the size of LOs, but fortunately it doesn't
11533 : * matter too much as long as we get large batches of LOs
11534 : * processed reasonably early. Assume 8K per blob.
11535 : */
11536 164 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11537 : }
11538 164 : break;
11539 780 : case DO_POLICY:
11540 780 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11541 780 : break;
11542 412 : case DO_PUBLICATION:
11543 412 : dumpPublication(fout, (const PublicationInfo *) dobj);
11544 412 : break;
11545 574 : case DO_PUBLICATION_REL:
11546 574 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11547 574 : break;
11548 164 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11549 164 : dumpPublicationNamespace(fout,
11550 : (const PublicationSchemaInfo *) dobj);
11551 164 : break;
11552 250 : case DO_SUBSCRIPTION:
11553 250 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11554 250 : break;
11555 4 : case DO_SUBSCRIPTION_REL:
11556 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11557 4 : break;
11558 6970 : case DO_REL_STATS:
11559 6970 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11560 6970 : break;
11561 936 : case DO_PRE_DATA_BOUNDARY:
11562 : case DO_POST_DATA_BOUNDARY:
11563 : /* never dumped, nothing to do */
11564 936 : break;
11565 : }
11566 : }
11567 :
11568 : /*
11569 : * dumpNamespace
11570 : * writes out to fout the queries to recreate a user-defined namespace
11571 : */
11572 : static void
11573 1218 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11574 : {
11575 1218 : DumpOptions *dopt = fout->dopt;
11576 : PQExpBuffer q;
11577 : PQExpBuffer delq;
11578 : char *qnspname;
11579 :
11580 : /* Do nothing if not dumping schema */
11581 1218 : if (!dopt->dumpSchema)
11582 56 : return;
11583 :
11584 1162 : q = createPQExpBuffer();
11585 1162 : delq = createPQExpBuffer();
11586 :
11587 1162 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11588 :
11589 1162 : if (nspinfo->create)
11590 : {
11591 764 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11592 764 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11593 : }
11594 : else
11595 : {
11596 : /* see selectDumpableNamespace() */
11597 398 : appendPQExpBufferStr(delq,
11598 : "-- *not* dropping schema, since initdb creates it\n");
11599 398 : appendPQExpBufferStr(q,
11600 : "-- *not* creating schema, since initdb creates it\n");
11601 : }
11602 :
11603 1162 : if (dopt->binary_upgrade)
11604 152 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11605 : "SCHEMA", qnspname, NULL);
11606 :
11607 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11608 434 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11609 434 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11610 : .owner = nspinfo->rolname,
11611 : .description = "SCHEMA",
11612 : .section = SECTION_PRE_DATA,
11613 : .createStmt = q->data,
11614 : .dropStmt = delq->data));
11615 :
11616 : /* Dump Schema Comments and Security Labels */
11617 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11618 : {
11619 408 : const char *initdb_comment = NULL;
11620 :
11621 408 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11622 340 : initdb_comment = "standard public schema";
11623 408 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11624 408 : NULL, nspinfo->rolname,
11625 408 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11626 : initdb_comment);
11627 : }
11628 :
11629 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11630 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11631 0 : NULL, nspinfo->rolname,
11632 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11633 :
11634 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11635 936 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11636 : qnspname, NULL, NULL,
11637 936 : NULL, nspinfo->rolname, &nspinfo->dacl);
11638 :
11639 1162 : free(qnspname);
11640 :
11641 1162 : destroyPQExpBuffer(q);
11642 1162 : destroyPQExpBuffer(delq);
11643 : }
11644 :
11645 : /*
11646 : * dumpExtension
11647 : * writes out to fout the queries to recreate an extension
11648 : */
11649 : static void
11650 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11651 : {
11652 38 : DumpOptions *dopt = fout->dopt;
11653 : PQExpBuffer q;
11654 : PQExpBuffer delq;
11655 : char *qextname;
11656 :
11657 : /* Do nothing if not dumping schema */
11658 38 : if (!dopt->dumpSchema)
11659 2 : return;
11660 :
11661 36 : q = createPQExpBuffer();
11662 36 : delq = createPQExpBuffer();
11663 :
11664 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11665 :
11666 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11667 :
11668 36 : if (!dopt->binary_upgrade)
11669 : {
11670 : /*
11671 : * In a regular dump, we simply create the extension, intentionally
11672 : * not specifying a version, so that the destination installation's
11673 : * default version is used.
11674 : *
11675 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11676 : * types; but there are various scenarios in which it's convenient to
11677 : * manually create the desired extension before restoring, so we
11678 : * prefer to allow it to exist already.
11679 : */
11680 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11681 34 : qextname, fmtId(extinfo->namespace));
11682 : }
11683 : else
11684 : {
11685 : /*
11686 : * In binary-upgrade mode, it's critical to reproduce the state of the
11687 : * database exactly, so our procedure is to create an empty extension,
11688 : * restore all the contained objects normally, and add them to the
11689 : * extension one by one. This function performs just the first of
11690 : * those steps. binary_upgrade_extension_member() takes care of
11691 : * adding member objects as they're created.
11692 : */
11693 : int i;
11694 : int n;
11695 :
11696 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11697 :
11698 : /*
11699 : * We unconditionally create the extension, so we must drop it if it
11700 : * exists. This could happen if the user deleted 'plpgsql' and then
11701 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11702 : */
11703 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11704 :
11705 2 : appendPQExpBufferStr(q,
11706 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11707 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11708 2 : appendPQExpBufferStr(q, ", ");
11709 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11710 2 : appendPQExpBufferStr(q, ", ");
11711 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11712 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11713 2 : appendPQExpBufferStr(q, ", ");
11714 :
11715 : /*
11716 : * Note that we're pushing extconfig (an OID array) back into
11717 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11718 : * preserved in binary upgrade.
11719 : */
11720 2 : if (strlen(extinfo->extconfig) > 2)
11721 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11722 : else
11723 0 : appendPQExpBufferStr(q, "NULL");
11724 2 : appendPQExpBufferStr(q, ", ");
11725 2 : if (strlen(extinfo->extcondition) > 2)
11726 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11727 : else
11728 0 : appendPQExpBufferStr(q, "NULL");
11729 2 : appendPQExpBufferStr(q, ", ");
11730 2 : appendPQExpBufferStr(q, "ARRAY[");
11731 2 : n = 0;
11732 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11733 : {
11734 : DumpableObject *extobj;
11735 :
11736 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11737 2 : if (extobj && extobj->objType == DO_EXTENSION)
11738 : {
11739 0 : if (n++ > 0)
11740 0 : appendPQExpBufferChar(q, ',');
11741 0 : appendStringLiteralAH(q, extobj->name, fout);
11742 : }
11743 : }
11744 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11745 2 : appendPQExpBufferStr(q, ");\n");
11746 : }
11747 :
11748 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11749 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11750 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11751 : .description = "EXTENSION",
11752 : .section = SECTION_PRE_DATA,
11753 : .createStmt = q->data,
11754 : .dropStmt = delq->data));
11755 :
11756 : /* Dump Extension Comments and Security Labels */
11757 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11758 36 : dumpComment(fout, "EXTENSION", qextname,
11759 : NULL, "",
11760 36 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11761 :
11762 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11763 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11764 : NULL, "",
11765 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11766 :
11767 36 : free(qextname);
11768 :
11769 36 : destroyPQExpBuffer(q);
11770 36 : destroyPQExpBuffer(delq);
11771 : }
11772 :
11773 : /*
11774 : * dumpType
11775 : * writes out to fout the queries to recreate a user-defined type
11776 : */
11777 : static void
11778 2148 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11779 : {
11780 2148 : DumpOptions *dopt = fout->dopt;
11781 :
11782 : /* Do nothing if not dumping schema */
11783 2148 : if (!dopt->dumpSchema)
11784 86 : return;
11785 :
11786 : /* Dump out in proper style */
11787 2062 : if (tyinfo->typtype == TYPTYPE_BASE)
11788 604 : dumpBaseType(fout, tyinfo);
11789 1458 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11790 440 : dumpDomain(fout, tyinfo);
11791 1018 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11792 364 : dumpCompositeType(fout, tyinfo);
11793 654 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11794 140 : dumpEnumType(fout, tyinfo);
11795 514 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11796 276 : dumpRangeType(fout, tyinfo);
11797 238 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11798 88 : dumpUndefinedType(fout, tyinfo);
11799 : else
11800 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11801 : tyinfo->dobj.name);
11802 : }
11803 :
11804 : /*
11805 : * dumpEnumType
11806 : * writes out to fout the queries to recreate a user-defined enum type
11807 : */
11808 : static void
11809 140 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11810 : {
11811 140 : DumpOptions *dopt = fout->dopt;
11812 140 : PQExpBuffer q = createPQExpBuffer();
11813 140 : PQExpBuffer delq = createPQExpBuffer();
11814 140 : PQExpBuffer query = createPQExpBuffer();
11815 : PGresult *res;
11816 : int num,
11817 : i;
11818 : Oid enum_oid;
11819 : char *qtypname;
11820 : char *qualtypname;
11821 : char *label;
11822 : int i_enumlabel;
11823 : int i_oid;
11824 :
11825 140 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11826 : {
11827 : /* Set up query for enum-specific details */
11828 92 : appendPQExpBufferStr(query,
11829 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11830 : "SELECT oid, enumlabel "
11831 : "FROM pg_catalog.pg_enum "
11832 : "WHERE enumtypid = $1 "
11833 : "ORDER BY enumsortorder");
11834 :
11835 92 : ExecuteSqlStatement(fout, query->data);
11836 :
11837 92 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11838 : }
11839 :
11840 140 : printfPQExpBuffer(query,
11841 : "EXECUTE dumpEnumType('%u')",
11842 140 : tyinfo->dobj.catId.oid);
11843 :
11844 140 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11845 :
11846 140 : num = PQntuples(res);
11847 :
11848 140 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11849 140 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11850 :
11851 : /*
11852 : * CASCADE shouldn't be required here as for normal types since the I/O
11853 : * functions are generic and do not get dropped.
11854 : */
11855 140 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11856 :
11857 140 : if (dopt->binary_upgrade)
11858 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11859 10 : tyinfo->dobj.catId.oid,
11860 : false, false);
11861 :
11862 140 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11863 : qualtypname);
11864 :
11865 140 : if (!dopt->binary_upgrade)
11866 : {
11867 130 : i_enumlabel = PQfnumber(res, "enumlabel");
11868 :
11869 : /* Labels with server-assigned oids */
11870 1080 : for (i = 0; i < num; i++)
11871 : {
11872 950 : label = PQgetvalue(res, i, i_enumlabel);
11873 950 : if (i > 0)
11874 820 : appendPQExpBufferChar(q, ',');
11875 950 : appendPQExpBufferStr(q, "\n ");
11876 950 : appendStringLiteralAH(q, label, fout);
11877 : }
11878 : }
11879 :
11880 140 : appendPQExpBufferStr(q, "\n);\n");
11881 :
11882 140 : if (dopt->binary_upgrade)
11883 : {
11884 10 : i_oid = PQfnumber(res, "oid");
11885 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11886 :
11887 : /* Labels with dump-assigned (preserved) oids */
11888 116 : for (i = 0; i < num; i++)
11889 : {
11890 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11891 106 : label = PQgetvalue(res, i, i_enumlabel);
11892 :
11893 106 : if (i == 0)
11894 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11895 106 : appendPQExpBuffer(q,
11896 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11897 : enum_oid);
11898 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11899 106 : appendStringLiteralAH(q, label, fout);
11900 106 : appendPQExpBufferStr(q, ";\n\n");
11901 : }
11902 : }
11903 :
11904 140 : if (dopt->binary_upgrade)
11905 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11906 : "TYPE", qtypname,
11907 10 : tyinfo->dobj.namespace->dobj.name);
11908 :
11909 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11910 140 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11911 140 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11912 : .namespace = tyinfo->dobj.namespace->dobj.name,
11913 : .owner = tyinfo->rolname,
11914 : .description = "TYPE",
11915 : .section = SECTION_PRE_DATA,
11916 : .createStmt = q->data,
11917 : .dropStmt = delq->data));
11918 :
11919 : /* Dump Type Comments and Security Labels */
11920 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11921 72 : dumpComment(fout, "TYPE", qtypname,
11922 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11923 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11924 :
11925 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11926 0 : dumpSecLabel(fout, "TYPE", qtypname,
11927 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11928 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11929 :
11930 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11931 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11932 : qtypname, NULL,
11933 72 : tyinfo->dobj.namespace->dobj.name,
11934 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
11935 :
11936 140 : PQclear(res);
11937 140 : destroyPQExpBuffer(q);
11938 140 : destroyPQExpBuffer(delq);
11939 140 : destroyPQExpBuffer(query);
11940 140 : free(qtypname);
11941 140 : free(qualtypname);
11942 140 : }
11943 :
11944 : /*
11945 : * dumpRangeType
11946 : * writes out to fout the queries to recreate a user-defined range type
11947 : */
11948 : static void
11949 276 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11950 : {
11951 276 : DumpOptions *dopt = fout->dopt;
11952 276 : PQExpBuffer q = createPQExpBuffer();
11953 276 : PQExpBuffer delq = createPQExpBuffer();
11954 276 : PQExpBuffer query = createPQExpBuffer();
11955 : PGresult *res;
11956 : Oid collationOid;
11957 : char *qtypname;
11958 : char *qualtypname;
11959 : char *procname;
11960 :
11961 276 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11962 : {
11963 : /* Set up query for range-specific details */
11964 94 : appendPQExpBufferStr(query,
11965 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11966 :
11967 94 : appendPQExpBufferStr(query,
11968 : "SELECT ");
11969 :
11970 94 : if (fout->remoteVersion >= 140000)
11971 94 : appendPQExpBufferStr(query,
11972 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11973 : else
11974 0 : appendPQExpBufferStr(query,
11975 : "NULL AS rngmultitype, ");
11976 :
11977 94 : appendPQExpBufferStr(query,
11978 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11979 : "opc.opcname AS opcname, "
11980 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11981 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11982 : "opc.opcdefault, "
11983 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11984 : " ELSE rngcollation END AS collation, "
11985 : "rngcanonical, rngsubdiff "
11986 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11987 : " pg_catalog.pg_opclass opc "
11988 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11989 : "rngtypid = $1");
11990 :
11991 94 : ExecuteSqlStatement(fout, query->data);
11992 :
11993 94 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11994 : }
11995 :
11996 276 : printfPQExpBuffer(query,
11997 : "EXECUTE dumpRangeType('%u')",
11998 276 : tyinfo->dobj.catId.oid);
11999 :
12000 276 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12001 :
12002 276 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12003 276 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12004 :
12005 : /*
12006 : * CASCADE shouldn't be required here as for normal types since the I/O
12007 : * functions are generic and do not get dropped.
12008 : */
12009 276 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12010 :
12011 276 : if (dopt->binary_upgrade)
12012 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12013 16 : tyinfo->dobj.catId.oid,
12014 : false, true);
12015 :
12016 276 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12017 : qualtypname);
12018 :
12019 276 : appendPQExpBuffer(q, "\n subtype = %s",
12020 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12021 :
12022 276 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12023 276 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12024 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12025 :
12026 : /* print subtype_opclass only if not default for subtype */
12027 276 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12028 : {
12029 72 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12030 72 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12031 :
12032 72 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12033 : fmtId(nspname));
12034 72 : appendPQExpBufferStr(q, fmtId(opcname));
12035 : }
12036 :
12037 276 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12038 276 : if (OidIsValid(collationOid))
12039 : {
12040 88 : CollInfo *coll = findCollationByOid(collationOid);
12041 :
12042 88 : if (coll)
12043 88 : appendPQExpBuffer(q, ",\n collation = %s",
12044 88 : fmtQualifiedDumpable(coll));
12045 : }
12046 :
12047 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12048 276 : if (strcmp(procname, "-") != 0)
12049 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12050 :
12051 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12052 276 : if (strcmp(procname, "-") != 0)
12053 52 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12054 :
12055 276 : appendPQExpBufferStr(q, "\n);\n");
12056 :
12057 276 : if (dopt->binary_upgrade)
12058 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12059 : "TYPE", qtypname,
12060 16 : tyinfo->dobj.namespace->dobj.name);
12061 :
12062 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12063 276 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12064 276 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12065 : .namespace = tyinfo->dobj.namespace->dobj.name,
12066 : .owner = tyinfo->rolname,
12067 : .description = "TYPE",
12068 : .section = SECTION_PRE_DATA,
12069 : .createStmt = q->data,
12070 : .dropStmt = delq->data));
12071 :
12072 : /* Dump Type Comments and Security Labels */
12073 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12074 108 : dumpComment(fout, "TYPE", qtypname,
12075 108 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12076 108 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12077 :
12078 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12079 0 : dumpSecLabel(fout, "TYPE", qtypname,
12080 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12081 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12082 :
12083 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12084 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12085 : qtypname, NULL,
12086 72 : tyinfo->dobj.namespace->dobj.name,
12087 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12088 :
12089 276 : PQclear(res);
12090 276 : destroyPQExpBuffer(q);
12091 276 : destroyPQExpBuffer(delq);
12092 276 : destroyPQExpBuffer(query);
12093 276 : free(qtypname);
12094 276 : free(qualtypname);
12095 276 : }
12096 :
12097 : /*
12098 : * dumpUndefinedType
12099 : * writes out to fout the queries to recreate a !typisdefined type
12100 : *
12101 : * This is a shell type, but we use different terminology to distinguish
12102 : * this case from where we have to emit a shell type definition to break
12103 : * circular dependencies. An undefined type shouldn't ever have anything
12104 : * depending on it.
12105 : */
12106 : static void
12107 88 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12108 : {
12109 88 : DumpOptions *dopt = fout->dopt;
12110 88 : PQExpBuffer q = createPQExpBuffer();
12111 88 : PQExpBuffer delq = createPQExpBuffer();
12112 : char *qtypname;
12113 : char *qualtypname;
12114 :
12115 88 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12116 88 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12117 :
12118 88 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12119 :
12120 88 : if (dopt->binary_upgrade)
12121 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12122 4 : tyinfo->dobj.catId.oid,
12123 : false, false);
12124 :
12125 88 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12126 : qualtypname);
12127 :
12128 88 : if (dopt->binary_upgrade)
12129 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12130 : "TYPE", qtypname,
12131 4 : tyinfo->dobj.namespace->dobj.name);
12132 :
12133 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12134 88 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12135 88 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12136 : .namespace = tyinfo->dobj.namespace->dobj.name,
12137 : .owner = tyinfo->rolname,
12138 : .description = "TYPE",
12139 : .section = SECTION_PRE_DATA,
12140 : .createStmt = q->data,
12141 : .dropStmt = delq->data));
12142 :
12143 : /* Dump Type Comments and Security Labels */
12144 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12145 72 : dumpComment(fout, "TYPE", qtypname,
12146 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12147 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12148 :
12149 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12150 0 : dumpSecLabel(fout, "TYPE", qtypname,
12151 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12152 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12153 :
12154 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12155 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12156 : qtypname, NULL,
12157 0 : tyinfo->dobj.namespace->dobj.name,
12158 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12159 :
12160 88 : destroyPQExpBuffer(q);
12161 88 : destroyPQExpBuffer(delq);
12162 88 : free(qtypname);
12163 88 : free(qualtypname);
12164 88 : }
12165 :
12166 : /*
12167 : * dumpBaseType
12168 : * writes out to fout the queries to recreate a user-defined base type
12169 : */
12170 : static void
12171 604 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12172 : {
12173 604 : DumpOptions *dopt = fout->dopt;
12174 604 : PQExpBuffer q = createPQExpBuffer();
12175 604 : PQExpBuffer delq = createPQExpBuffer();
12176 604 : PQExpBuffer query = createPQExpBuffer();
12177 : PGresult *res;
12178 : char *qtypname;
12179 : char *qualtypname;
12180 : char *typlen;
12181 : char *typinput;
12182 : char *typoutput;
12183 : char *typreceive;
12184 : char *typsend;
12185 : char *typmodin;
12186 : char *typmodout;
12187 : char *typanalyze;
12188 : char *typsubscript;
12189 : Oid typreceiveoid;
12190 : Oid typsendoid;
12191 : Oid typmodinoid;
12192 : Oid typmodoutoid;
12193 : Oid typanalyzeoid;
12194 : Oid typsubscriptoid;
12195 : char *typcategory;
12196 : char *typispreferred;
12197 : char *typdelim;
12198 : char *typbyval;
12199 : char *typalign;
12200 : char *typstorage;
12201 : char *typcollatable;
12202 : char *typdefault;
12203 604 : bool typdefault_is_literal = false;
12204 :
12205 604 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12206 : {
12207 : /* Set up query for type-specific details */
12208 94 : appendPQExpBufferStr(query,
12209 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12210 : "SELECT typlen, "
12211 : "typinput, typoutput, typreceive, typsend, "
12212 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12213 : "typsend::pg_catalog.oid AS typsendoid, "
12214 : "typanalyze, "
12215 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12216 : "typdelim, typbyval, typalign, typstorage, "
12217 : "typmodin, typmodout, "
12218 : "typmodin::pg_catalog.oid AS typmodinoid, "
12219 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12220 : "typcategory, typispreferred, "
12221 : "(typcollation <> 0) AS typcollatable, "
12222 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12223 :
12224 94 : if (fout->remoteVersion >= 140000)
12225 94 : appendPQExpBufferStr(query,
12226 : "typsubscript, "
12227 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12228 : else
12229 0 : appendPQExpBufferStr(query,
12230 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12231 :
12232 94 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12233 : "WHERE oid = $1");
12234 :
12235 94 : ExecuteSqlStatement(fout, query->data);
12236 :
12237 94 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12238 : }
12239 :
12240 604 : printfPQExpBuffer(query,
12241 : "EXECUTE dumpBaseType('%u')",
12242 604 : tyinfo->dobj.catId.oid);
12243 :
12244 604 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12245 :
12246 604 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12247 604 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12248 604 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12249 604 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12250 604 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12251 604 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12252 604 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12253 604 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12254 604 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12255 604 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12256 604 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12257 604 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12258 604 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12259 604 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12260 604 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12261 604 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12262 604 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12263 604 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12264 604 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12265 604 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12266 604 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12267 604 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12268 604 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12269 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12270 604 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12271 : {
12272 104 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12273 104 : typdefault_is_literal = true; /* it needs quotes */
12274 : }
12275 : else
12276 500 : typdefault = NULL;
12277 :
12278 604 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12279 604 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12280 :
12281 : /*
12282 : * The reason we include CASCADE is that the circular dependency between
12283 : * the type and its I/O functions makes it impossible to drop the type any
12284 : * other way.
12285 : */
12286 604 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12287 :
12288 : /*
12289 : * We might already have a shell type, but setting pg_type_oid is
12290 : * harmless, and in any case we'd better set the array type OID.
12291 : */
12292 604 : if (dopt->binary_upgrade)
12293 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12294 16 : tyinfo->dobj.catId.oid,
12295 : false, false);
12296 :
12297 604 : appendPQExpBuffer(q,
12298 : "CREATE TYPE %s (\n"
12299 : " INTERNALLENGTH = %s",
12300 : qualtypname,
12301 604 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12302 :
12303 : /* regproc result is sufficiently quoted already */
12304 604 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12305 604 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12306 604 : if (OidIsValid(typreceiveoid))
12307 408 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12308 604 : if (OidIsValid(typsendoid))
12309 408 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12310 604 : if (OidIsValid(typmodinoid))
12311 76 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12312 604 : if (OidIsValid(typmodoutoid))
12313 76 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12314 604 : if (OidIsValid(typanalyzeoid))
12315 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12316 :
12317 604 : if (strcmp(typcollatable, "t") == 0)
12318 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12319 :
12320 604 : if (typdefault != NULL)
12321 : {
12322 104 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12323 104 : if (typdefault_is_literal)
12324 104 : appendStringLiteralAH(q, typdefault, fout);
12325 : else
12326 0 : appendPQExpBufferStr(q, typdefault);
12327 : }
12328 :
12329 604 : if (OidIsValid(typsubscriptoid))
12330 64 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12331 :
12332 604 : if (OidIsValid(tyinfo->typelem))
12333 58 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12334 58 : getFormattedTypeName(fout, tyinfo->typelem,
12335 : zeroIsError));
12336 :
12337 604 : if (strcmp(typcategory, "U") != 0)
12338 : {
12339 316 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12340 316 : appendStringLiteralAH(q, typcategory, fout);
12341 : }
12342 :
12343 604 : if (strcmp(typispreferred, "t") == 0)
12344 64 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12345 :
12346 604 : if (typdelim && strcmp(typdelim, ",") != 0)
12347 : {
12348 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12349 6 : appendStringLiteralAH(q, typdelim, fout);
12350 : }
12351 :
12352 604 : if (*typalign == TYPALIGN_CHAR)
12353 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12354 580 : else if (*typalign == TYPALIGN_SHORT)
12355 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12356 568 : else if (*typalign == TYPALIGN_INT)
12357 400 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12358 168 : else if (*typalign == TYPALIGN_DOUBLE)
12359 168 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12360 :
12361 604 : if (*typstorage == TYPSTORAGE_PLAIN)
12362 454 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12363 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12364 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12365 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12366 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12367 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12368 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12369 :
12370 604 : if (strcmp(typbyval, "t") == 0)
12371 288 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12372 :
12373 604 : appendPQExpBufferStr(q, "\n);\n");
12374 :
12375 604 : if (dopt->binary_upgrade)
12376 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12377 : "TYPE", qtypname,
12378 16 : tyinfo->dobj.namespace->dobj.name);
12379 :
12380 604 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12381 604 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12382 604 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12383 : .namespace = tyinfo->dobj.namespace->dobj.name,
12384 : .owner = tyinfo->rolname,
12385 : .description = "TYPE",
12386 : .section = SECTION_PRE_DATA,
12387 : .createStmt = q->data,
12388 : .dropStmt = delq->data));
12389 :
12390 : /* Dump Type Comments and Security Labels */
12391 604 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12392 492 : dumpComment(fout, "TYPE", qtypname,
12393 492 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12394 492 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12395 :
12396 604 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12397 0 : dumpSecLabel(fout, "TYPE", qtypname,
12398 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12399 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12400 :
12401 604 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12402 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12403 : qtypname, NULL,
12404 72 : tyinfo->dobj.namespace->dobj.name,
12405 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12406 :
12407 604 : PQclear(res);
12408 604 : destroyPQExpBuffer(q);
12409 604 : destroyPQExpBuffer(delq);
12410 604 : destroyPQExpBuffer(query);
12411 604 : free(qtypname);
12412 604 : free(qualtypname);
12413 604 : }
12414 :
12415 : /*
12416 : * dumpDomain
12417 : * writes out to fout the queries to recreate a user-defined domain
12418 : */
12419 : static void
12420 440 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12421 : {
12422 440 : DumpOptions *dopt = fout->dopt;
12423 440 : PQExpBuffer q = createPQExpBuffer();
12424 440 : PQExpBuffer delq = createPQExpBuffer();
12425 440 : PQExpBuffer query = createPQExpBuffer();
12426 : PGresult *res;
12427 : int i;
12428 : char *qtypname;
12429 : char *qualtypname;
12430 : char *typnotnull;
12431 : char *typdefn;
12432 : char *typdefault;
12433 : Oid typcollation;
12434 440 : bool typdefault_is_literal = false;
12435 :
12436 440 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12437 : {
12438 : /* Set up query for domain-specific details */
12439 88 : appendPQExpBufferStr(query,
12440 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12441 :
12442 88 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12443 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12444 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12445 : "t.typdefault, "
12446 : "CASE WHEN t.typcollation <> u.typcollation "
12447 : "THEN t.typcollation ELSE 0 END AS typcollation "
12448 : "FROM pg_catalog.pg_type t "
12449 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12450 : "WHERE t.oid = $1");
12451 :
12452 88 : ExecuteSqlStatement(fout, query->data);
12453 :
12454 88 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12455 : }
12456 :
12457 440 : printfPQExpBuffer(query,
12458 : "EXECUTE dumpDomain('%u')",
12459 440 : tyinfo->dobj.catId.oid);
12460 :
12461 440 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12462 :
12463 440 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12464 440 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12465 440 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12466 88 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12467 352 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12468 : {
12469 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12470 0 : typdefault_is_literal = true; /* it needs quotes */
12471 : }
12472 : else
12473 352 : typdefault = NULL;
12474 440 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12475 :
12476 440 : if (dopt->binary_upgrade)
12477 48 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12478 48 : tyinfo->dobj.catId.oid,
12479 : true, /* force array type */
12480 : false); /* force multirange type */
12481 :
12482 440 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12483 440 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12484 :
12485 440 : appendPQExpBuffer(q,
12486 : "CREATE DOMAIN %s AS %s",
12487 : qualtypname,
12488 : typdefn);
12489 :
12490 : /* Print collation only if different from base type's collation */
12491 440 : if (OidIsValid(typcollation))
12492 : {
12493 : CollInfo *coll;
12494 :
12495 72 : coll = findCollationByOid(typcollation);
12496 72 : if (coll)
12497 72 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12498 : }
12499 :
12500 440 : if (typnotnull[0] == 't')
12501 48 : appendPQExpBufferStr(q, " NOT NULL");
12502 :
12503 440 : if (typdefault != NULL)
12504 : {
12505 88 : appendPQExpBufferStr(q, " DEFAULT ");
12506 88 : if (typdefault_is_literal)
12507 0 : appendStringLiteralAH(q, typdefault, fout);
12508 : else
12509 88 : appendPQExpBufferStr(q, typdefault);
12510 : }
12511 :
12512 440 : PQclear(res);
12513 :
12514 : /*
12515 : * Add any CHECK constraints for the domain
12516 : */
12517 736 : for (i = 0; i < tyinfo->nDomChecks; i++)
12518 : {
12519 296 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12520 :
12521 296 : if (!domcheck->separate)
12522 296 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12523 296 : fmtId(domcheck->dobj.name), domcheck->condef);
12524 : }
12525 :
12526 440 : appendPQExpBufferStr(q, ";\n");
12527 :
12528 440 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12529 :
12530 440 : if (dopt->binary_upgrade)
12531 48 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12532 : "DOMAIN", qtypname,
12533 48 : tyinfo->dobj.namespace->dobj.name);
12534 :
12535 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12536 440 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12537 440 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12538 : .namespace = tyinfo->dobj.namespace->dobj.name,
12539 : .owner = tyinfo->rolname,
12540 : .description = "DOMAIN",
12541 : .section = SECTION_PRE_DATA,
12542 : .createStmt = q->data,
12543 : .dropStmt = delq->data));
12544 :
12545 : /* Dump Domain Comments and Security Labels */
12546 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12547 0 : dumpComment(fout, "DOMAIN", qtypname,
12548 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12549 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12550 :
12551 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12552 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12553 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12554 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12555 :
12556 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12557 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12558 : qtypname, NULL,
12559 72 : tyinfo->dobj.namespace->dobj.name,
12560 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12561 :
12562 : /* Dump any per-constraint comments */
12563 736 : for (i = 0; i < tyinfo->nDomChecks; i++)
12564 : {
12565 296 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12566 296 : PQExpBuffer conprefix = createPQExpBuffer();
12567 :
12568 296 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12569 296 : fmtId(domcheck->dobj.name));
12570 :
12571 296 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12572 72 : dumpComment(fout, conprefix->data, qtypname,
12573 72 : tyinfo->dobj.namespace->dobj.name,
12574 72 : tyinfo->rolname,
12575 72 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12576 :
12577 296 : destroyPQExpBuffer(conprefix);
12578 : }
12579 :
12580 440 : destroyPQExpBuffer(q);
12581 440 : destroyPQExpBuffer(delq);
12582 440 : destroyPQExpBuffer(query);
12583 440 : free(qtypname);
12584 440 : free(qualtypname);
12585 440 : }
12586 :
12587 : /*
12588 : * dumpCompositeType
12589 : * writes out to fout the queries to recreate a user-defined stand-alone
12590 : * composite type
12591 : */
12592 : static void
12593 364 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12594 : {
12595 364 : DumpOptions *dopt = fout->dopt;
12596 364 : PQExpBuffer q = createPQExpBuffer();
12597 364 : PQExpBuffer dropped = createPQExpBuffer();
12598 364 : PQExpBuffer delq = createPQExpBuffer();
12599 364 : PQExpBuffer query = createPQExpBuffer();
12600 : PGresult *res;
12601 : char *qtypname;
12602 : char *qualtypname;
12603 : int ntups;
12604 : int i_attname;
12605 : int i_atttypdefn;
12606 : int i_attlen;
12607 : int i_attalign;
12608 : int i_attisdropped;
12609 : int i_attcollation;
12610 : int i;
12611 : int actual_atts;
12612 :
12613 364 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12614 : {
12615 : /*
12616 : * Set up query for type-specific details.
12617 : *
12618 : * Since we only want to dump COLLATE clauses for attributes whose
12619 : * collation is different from their type's default, we use a CASE
12620 : * here to suppress uninteresting attcollations cheaply. atttypid
12621 : * will be 0 for dropped columns; collation does not matter for those.
12622 : */
12623 124 : appendPQExpBufferStr(query,
12624 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12625 : "SELECT a.attname, a.attnum, "
12626 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12627 : "a.attlen, a.attalign, a.attisdropped, "
12628 : "CASE WHEN a.attcollation <> at.typcollation "
12629 : "THEN a.attcollation ELSE 0 END AS attcollation "
12630 : "FROM pg_catalog.pg_type ct "
12631 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12632 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12633 : "WHERE ct.oid = $1 "
12634 : "ORDER BY a.attnum");
12635 :
12636 124 : ExecuteSqlStatement(fout, query->data);
12637 :
12638 124 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12639 : }
12640 :
12641 364 : printfPQExpBuffer(query,
12642 : "EXECUTE dumpCompositeType('%u')",
12643 364 : tyinfo->dobj.catId.oid);
12644 :
12645 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12646 :
12647 364 : ntups = PQntuples(res);
12648 :
12649 364 : i_attname = PQfnumber(res, "attname");
12650 364 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12651 364 : i_attlen = PQfnumber(res, "attlen");
12652 364 : i_attalign = PQfnumber(res, "attalign");
12653 364 : i_attisdropped = PQfnumber(res, "attisdropped");
12654 364 : i_attcollation = PQfnumber(res, "attcollation");
12655 :
12656 364 : if (dopt->binary_upgrade)
12657 : {
12658 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12659 36 : tyinfo->dobj.catId.oid,
12660 : false, false);
12661 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12662 : }
12663 :
12664 364 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12665 364 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12666 :
12667 364 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12668 : qualtypname);
12669 :
12670 364 : actual_atts = 0;
12671 1180 : for (i = 0; i < ntups; i++)
12672 : {
12673 : char *attname;
12674 : char *atttypdefn;
12675 : char *attlen;
12676 : char *attalign;
12677 : bool attisdropped;
12678 : Oid attcollation;
12679 :
12680 816 : attname = PQgetvalue(res, i, i_attname);
12681 816 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12682 816 : attlen = PQgetvalue(res, i, i_attlen);
12683 816 : attalign = PQgetvalue(res, i, i_attalign);
12684 816 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12685 816 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12686 :
12687 816 : if (attisdropped && !dopt->binary_upgrade)
12688 24 : continue;
12689 :
12690 : /* Format properly if not first attr */
12691 792 : if (actual_atts++ > 0)
12692 428 : appendPQExpBufferChar(q, ',');
12693 792 : appendPQExpBufferStr(q, "\n\t");
12694 :
12695 792 : if (!attisdropped)
12696 : {
12697 788 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12698 :
12699 : /* Add collation if not default for the column type */
12700 788 : if (OidIsValid(attcollation))
12701 : {
12702 : CollInfo *coll;
12703 :
12704 0 : coll = findCollationByOid(attcollation);
12705 0 : if (coll)
12706 0 : appendPQExpBuffer(q, " COLLATE %s",
12707 0 : fmtQualifiedDumpable(coll));
12708 : }
12709 : }
12710 : else
12711 : {
12712 : /*
12713 : * This is a dropped attribute and we're in binary_upgrade mode.
12714 : * Insert a placeholder for it in the CREATE TYPE command, and set
12715 : * length and alignment with direct UPDATE to the catalogs
12716 : * afterwards. See similar code in dumpTableSchema().
12717 : */
12718 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12719 :
12720 : /* stash separately for insertion after the CREATE TYPE */
12721 4 : appendPQExpBufferStr(dropped,
12722 : "\n-- For binary upgrade, recreate dropped column.\n");
12723 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12724 : "SET attlen = %s, "
12725 : "attalign = '%s', attbyval = false\n"
12726 : "WHERE attname = ", attlen, attalign);
12727 4 : appendStringLiteralAH(dropped, attname, fout);
12728 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12729 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12730 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12731 :
12732 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12733 : qualtypname);
12734 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12735 : fmtId(attname));
12736 : }
12737 : }
12738 364 : appendPQExpBufferStr(q, "\n);\n");
12739 364 : appendPQExpBufferStr(q, dropped->data);
12740 :
12741 364 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12742 :
12743 364 : if (dopt->binary_upgrade)
12744 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12745 : "TYPE", qtypname,
12746 36 : tyinfo->dobj.namespace->dobj.name);
12747 :
12748 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12749 330 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12750 330 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12751 : .namespace = tyinfo->dobj.namespace->dobj.name,
12752 : .owner = tyinfo->rolname,
12753 : .description = "TYPE",
12754 : .section = SECTION_PRE_DATA,
12755 : .createStmt = q->data,
12756 : .dropStmt = delq->data));
12757 :
12758 :
12759 : /* Dump Type Comments and Security Labels */
12760 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12761 72 : dumpComment(fout, "TYPE", qtypname,
12762 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12763 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12764 :
12765 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12766 0 : dumpSecLabel(fout, "TYPE", qtypname,
12767 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12768 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12769 :
12770 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12771 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12772 : qtypname, NULL,
12773 36 : tyinfo->dobj.namespace->dobj.name,
12774 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
12775 :
12776 : /* Dump any per-column comments */
12777 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12778 72 : dumpCompositeTypeColComments(fout, tyinfo, res);
12779 :
12780 364 : PQclear(res);
12781 364 : destroyPQExpBuffer(q);
12782 364 : destroyPQExpBuffer(dropped);
12783 364 : destroyPQExpBuffer(delq);
12784 364 : destroyPQExpBuffer(query);
12785 364 : free(qtypname);
12786 364 : free(qualtypname);
12787 364 : }
12788 :
12789 : /*
12790 : * dumpCompositeTypeColComments
12791 : * writes out to fout the queries to recreate comments on the columns of
12792 : * a user-defined stand-alone composite type.
12793 : *
12794 : * The caller has already made a query to collect the names and attnums
12795 : * of the type's columns, so we just pass that result into here rather
12796 : * than reading them again.
12797 : */
12798 : static void
12799 72 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12800 : PGresult *res)
12801 : {
12802 : CommentItem *comments;
12803 : int ncomments;
12804 : PQExpBuffer query;
12805 : PQExpBuffer target;
12806 : int i;
12807 : int ntups;
12808 : int i_attname;
12809 : int i_attnum;
12810 : int i_attisdropped;
12811 :
12812 : /* do nothing, if --no-comments is supplied */
12813 72 : if (fout->dopt->no_comments)
12814 0 : return;
12815 :
12816 : /* Search for comments associated with type's pg_class OID */
12817 72 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12818 : &comments);
12819 :
12820 : /* If no comments exist, we're done */
12821 72 : if (ncomments <= 0)
12822 0 : return;
12823 :
12824 : /* Build COMMENT ON statements */
12825 72 : query = createPQExpBuffer();
12826 72 : target = createPQExpBuffer();
12827 :
12828 72 : ntups = PQntuples(res);
12829 72 : i_attnum = PQfnumber(res, "attnum");
12830 72 : i_attname = PQfnumber(res, "attname");
12831 72 : i_attisdropped = PQfnumber(res, "attisdropped");
12832 144 : while (ncomments > 0)
12833 : {
12834 : const char *attname;
12835 :
12836 72 : attname = NULL;
12837 72 : for (i = 0; i < ntups; i++)
12838 : {
12839 72 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12840 72 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12841 : {
12842 72 : attname = PQgetvalue(res, i, i_attname);
12843 72 : break;
12844 : }
12845 : }
12846 72 : if (attname) /* just in case we don't find it */
12847 : {
12848 72 : const char *descr = comments->descr;
12849 :
12850 72 : resetPQExpBuffer(target);
12851 72 : appendPQExpBuffer(target, "COLUMN %s.",
12852 72 : fmtId(tyinfo->dobj.name));
12853 72 : appendPQExpBufferStr(target, fmtId(attname));
12854 :
12855 72 : resetPQExpBuffer(query);
12856 72 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12857 72 : fmtQualifiedDumpable(tyinfo));
12858 72 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12859 72 : appendStringLiteralAH(query, descr, fout);
12860 72 : appendPQExpBufferStr(query, ";\n");
12861 :
12862 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12863 72 : ARCHIVE_OPTS(.tag = target->data,
12864 : .namespace = tyinfo->dobj.namespace->dobj.name,
12865 : .owner = tyinfo->rolname,
12866 : .description = "COMMENT",
12867 : .section = SECTION_NONE,
12868 : .createStmt = query->data,
12869 : .deps = &(tyinfo->dobj.dumpId),
12870 : .nDeps = 1));
12871 : }
12872 :
12873 72 : comments++;
12874 72 : ncomments--;
12875 : }
12876 :
12877 72 : destroyPQExpBuffer(query);
12878 72 : destroyPQExpBuffer(target);
12879 : }
12880 :
12881 : /*
12882 : * dumpShellType
12883 : * writes out to fout the queries to create a shell type
12884 : *
12885 : * We dump a shell definition in advance of the I/O functions for the type.
12886 : */
12887 : static void
12888 196 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12889 : {
12890 196 : DumpOptions *dopt = fout->dopt;
12891 : PQExpBuffer q;
12892 :
12893 : /* Do nothing if not dumping schema */
12894 196 : if (!dopt->dumpSchema)
12895 12 : return;
12896 :
12897 184 : q = createPQExpBuffer();
12898 :
12899 : /*
12900 : * Note the lack of a DROP command for the shell type; any required DROP
12901 : * is driven off the base type entry, instead. This interacts with
12902 : * _printTocEntry()'s use of the presence of a DROP command to decide
12903 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12904 : * the shell type's owner immediately on creation; that should happen only
12905 : * after it's filled in, otherwise the backend complains.
12906 : */
12907 :
12908 184 : if (dopt->binary_upgrade)
12909 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12910 16 : stinfo->baseType->dobj.catId.oid,
12911 : false, false);
12912 :
12913 184 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12914 184 : fmtQualifiedDumpable(stinfo));
12915 :
12916 184 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12917 184 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12918 184 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12919 : .namespace = stinfo->dobj.namespace->dobj.name,
12920 : .owner = stinfo->baseType->rolname,
12921 : .description = "SHELL TYPE",
12922 : .section = SECTION_PRE_DATA,
12923 : .createStmt = q->data));
12924 :
12925 184 : destroyPQExpBuffer(q);
12926 : }
12927 :
12928 : /*
12929 : * dumpProcLang
12930 : * writes out to fout the queries to recreate a user-defined
12931 : * procedural language
12932 : */
12933 : static void
12934 180 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12935 : {
12936 180 : DumpOptions *dopt = fout->dopt;
12937 : PQExpBuffer defqry;
12938 : PQExpBuffer delqry;
12939 : bool useParams;
12940 : char *qlanname;
12941 : FuncInfo *funcInfo;
12942 180 : FuncInfo *inlineInfo = NULL;
12943 180 : FuncInfo *validatorInfo = NULL;
12944 :
12945 : /* Do nothing if not dumping schema */
12946 180 : if (!dopt->dumpSchema)
12947 26 : return;
12948 :
12949 : /*
12950 : * Try to find the support function(s). It is not an error if we don't
12951 : * find them --- if the functions are in the pg_catalog schema, as is
12952 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12953 : * we will emit a parameterless CREATE LANGUAGE command, which will
12954 : * require PL template knowledge in the backend to reload.)
12955 : */
12956 :
12957 154 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12958 154 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12959 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12960 :
12961 154 : if (OidIsValid(plang->laninline))
12962 : {
12963 84 : inlineInfo = findFuncByOid(plang->laninline);
12964 84 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12965 2 : inlineInfo = NULL;
12966 : }
12967 :
12968 154 : if (OidIsValid(plang->lanvalidator))
12969 : {
12970 84 : validatorInfo = findFuncByOid(plang->lanvalidator);
12971 84 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12972 2 : validatorInfo = NULL;
12973 : }
12974 :
12975 : /*
12976 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12977 : * parameters. Otherwise, we'll write a parameterless command, which will
12978 : * be interpreted as CREATE EXTENSION.
12979 : */
12980 68 : useParams = (funcInfo != NULL &&
12981 290 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12982 68 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12983 :
12984 154 : defqry = createPQExpBuffer();
12985 154 : delqry = createPQExpBuffer();
12986 :
12987 154 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12988 :
12989 154 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12990 : qlanname);
12991 :
12992 154 : if (useParams)
12993 : {
12994 68 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12995 68 : plang->lanpltrusted ? "TRUSTED " : "",
12996 : qlanname);
12997 68 : appendPQExpBuffer(defqry, " HANDLER %s",
12998 68 : fmtQualifiedDumpable(funcInfo));
12999 68 : if (OidIsValid(plang->laninline))
13000 0 : appendPQExpBuffer(defqry, " INLINE %s",
13001 0 : fmtQualifiedDumpable(inlineInfo));
13002 68 : if (OidIsValid(plang->lanvalidator))
13003 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13004 0 : fmtQualifiedDumpable(validatorInfo));
13005 : }
13006 : else
13007 : {
13008 : /*
13009 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13010 : * command will not fail if the language is preinstalled in the target
13011 : * database.
13012 : *
13013 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13014 : * EXISTS; perhaps we should emit that instead? But it might just add
13015 : * confusion.
13016 : */
13017 86 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13018 : qlanname);
13019 : }
13020 154 : appendPQExpBufferStr(defqry, ";\n");
13021 :
13022 154 : if (dopt->binary_upgrade)
13023 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13024 : "LANGUAGE", qlanname, NULL);
13025 :
13026 154 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13027 70 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13028 70 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13029 : .owner = plang->lanowner,
13030 : .description = "PROCEDURAL LANGUAGE",
13031 : .section = SECTION_PRE_DATA,
13032 : .createStmt = defqry->data,
13033 : .dropStmt = delqry->data,
13034 : ));
13035 :
13036 : /* Dump Proc Lang Comments and Security Labels */
13037 154 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13038 0 : dumpComment(fout, "LANGUAGE", qlanname,
13039 0 : NULL, plang->lanowner,
13040 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13041 :
13042 154 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13043 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13044 0 : NULL, plang->lanowner,
13045 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13046 :
13047 154 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13048 84 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13049 : qlanname, NULL, NULL,
13050 84 : NULL, plang->lanowner, &plang->dacl);
13051 :
13052 154 : free(qlanname);
13053 :
13054 154 : destroyPQExpBuffer(defqry);
13055 154 : destroyPQExpBuffer(delqry);
13056 : }
13057 :
13058 : /*
13059 : * format_function_arguments: generate function name and argument list
13060 : *
13061 : * This is used when we can rely on pg_get_function_arguments to format
13062 : * the argument list. Note, however, that pg_get_function_arguments
13063 : * does not special-case zero-argument aggregates.
13064 : */
13065 : static char *
13066 12224 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13067 : {
13068 : PQExpBufferData fn;
13069 :
13070 12224 : initPQExpBuffer(&fn);
13071 12224 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13072 12224 : if (is_agg && finfo->nargs == 0)
13073 256 : appendPQExpBufferStr(&fn, "(*)");
13074 : else
13075 11968 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13076 12224 : return fn.data;
13077 : }
13078 :
13079 : /*
13080 : * format_function_signature: generate function name and argument list
13081 : *
13082 : * Only a minimal list of input argument types is generated; this is
13083 : * sufficient to reference the function, but not to define it.
13084 : *
13085 : * If honor_quotes is false then the function name is never quoted.
13086 : * This is appropriate for use in TOC tags, but not in SQL commands.
13087 : */
13088 : static char *
13089 6370 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13090 : {
13091 : PQExpBufferData fn;
13092 : int j;
13093 :
13094 6370 : initPQExpBuffer(&fn);
13095 6370 : if (honor_quotes)
13096 1118 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13097 : else
13098 5252 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13099 11838 : for (j = 0; j < finfo->nargs; j++)
13100 : {
13101 5468 : if (j > 0)
13102 1348 : appendPQExpBufferStr(&fn, ", ");
13103 :
13104 5468 : appendPQExpBufferStr(&fn,
13105 5468 : getFormattedTypeName(fout, finfo->argtypes[j],
13106 : zeroIsError));
13107 : }
13108 6370 : appendPQExpBufferChar(&fn, ')');
13109 6370 : return fn.data;
13110 : }
13111 :
13112 :
13113 : /*
13114 : * dumpFunc:
13115 : * dump out one function
13116 : */
13117 : static void
13118 5376 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13119 : {
13120 5376 : DumpOptions *dopt = fout->dopt;
13121 : PQExpBuffer query;
13122 : PQExpBuffer q;
13123 : PQExpBuffer delqry;
13124 : PQExpBuffer asPart;
13125 : PGresult *res;
13126 : char *funcsig; /* identity signature */
13127 5376 : char *funcfullsig = NULL; /* full signature */
13128 : char *funcsig_tag;
13129 : char *qual_funcsig;
13130 : char *proretset;
13131 : char *prosrc;
13132 : char *probin;
13133 : char *prosqlbody;
13134 : char *funcargs;
13135 : char *funciargs;
13136 : char *funcresult;
13137 : char *protrftypes;
13138 : char *prokind;
13139 : char *provolatile;
13140 : char *proisstrict;
13141 : char *prosecdef;
13142 : char *proleakproof;
13143 : char *proconfig;
13144 : char *procost;
13145 : char *prorows;
13146 : char *prosupport;
13147 : char *proparallel;
13148 : char *lanname;
13149 5376 : char **configitems = NULL;
13150 5376 : int nconfigitems = 0;
13151 : const char *keyword;
13152 :
13153 : /* Do nothing if not dumping schema */
13154 5376 : if (!dopt->dumpSchema)
13155 124 : return;
13156 :
13157 5252 : query = createPQExpBuffer();
13158 5252 : q = createPQExpBuffer();
13159 5252 : delqry = createPQExpBuffer();
13160 5252 : asPart = createPQExpBuffer();
13161 :
13162 5252 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13163 : {
13164 : /* Set up query for function-specific details */
13165 150 : appendPQExpBufferStr(query,
13166 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13167 :
13168 150 : appendPQExpBufferStr(query,
13169 : "SELECT\n"
13170 : "proretset,\n"
13171 : "prosrc,\n"
13172 : "probin,\n"
13173 : "provolatile,\n"
13174 : "proisstrict,\n"
13175 : "prosecdef,\n"
13176 : "lanname,\n"
13177 : "proconfig,\n"
13178 : "procost,\n"
13179 : "prorows,\n"
13180 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13181 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13182 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13183 : "proleakproof,\n");
13184 :
13185 150 : if (fout->remoteVersion >= 90500)
13186 150 : appendPQExpBufferStr(query,
13187 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13188 : else
13189 0 : appendPQExpBufferStr(query,
13190 : "NULL AS protrftypes,\n");
13191 :
13192 150 : if (fout->remoteVersion >= 90600)
13193 150 : appendPQExpBufferStr(query,
13194 : "proparallel,\n");
13195 : else
13196 0 : appendPQExpBufferStr(query,
13197 : "'u' AS proparallel,\n");
13198 :
13199 150 : if (fout->remoteVersion >= 110000)
13200 150 : appendPQExpBufferStr(query,
13201 : "prokind,\n");
13202 : else
13203 0 : appendPQExpBufferStr(query,
13204 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13205 :
13206 150 : if (fout->remoteVersion >= 120000)
13207 150 : appendPQExpBufferStr(query,
13208 : "prosupport,\n");
13209 : else
13210 0 : appendPQExpBufferStr(query,
13211 : "'-' AS prosupport,\n");
13212 :
13213 150 : if (fout->remoteVersion >= 140000)
13214 150 : appendPQExpBufferStr(query,
13215 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13216 : else
13217 0 : appendPQExpBufferStr(query,
13218 : "NULL AS prosqlbody\n");
13219 :
13220 150 : appendPQExpBufferStr(query,
13221 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13222 : "WHERE p.oid = $1 "
13223 : "AND l.oid = p.prolang");
13224 :
13225 150 : ExecuteSqlStatement(fout, query->data);
13226 :
13227 150 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13228 : }
13229 :
13230 5252 : printfPQExpBuffer(query,
13231 : "EXECUTE dumpFunc('%u')",
13232 5252 : finfo->dobj.catId.oid);
13233 :
13234 5252 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13235 :
13236 5252 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13237 5252 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13238 : {
13239 5130 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13240 5130 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13241 5130 : prosqlbody = NULL;
13242 : }
13243 : else
13244 : {
13245 122 : prosrc = NULL;
13246 122 : probin = NULL;
13247 122 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13248 : }
13249 5252 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13250 5252 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13251 5252 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13252 5252 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13253 5252 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13254 5252 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13255 5252 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13256 5252 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13257 5252 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13258 5252 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13259 5252 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13260 5252 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13261 5252 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13262 5252 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13263 5252 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13264 :
13265 : /*
13266 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13267 : * is used.
13268 : */
13269 5252 : if (prosqlbody)
13270 : {
13271 122 : appendPQExpBufferStr(asPart, prosqlbody);
13272 : }
13273 5130 : else if (probin[0] != '\0')
13274 : {
13275 392 : appendPQExpBufferStr(asPart, "AS ");
13276 392 : appendStringLiteralAH(asPart, probin, fout);
13277 392 : if (prosrc[0] != '\0')
13278 : {
13279 392 : appendPQExpBufferStr(asPart, ", ");
13280 :
13281 : /*
13282 : * where we have bin, use dollar quoting if allowed and src
13283 : * contains quote or backslash; else use regular quoting.
13284 : */
13285 392 : if (dopt->disable_dollar_quoting ||
13286 392 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13287 392 : appendStringLiteralAH(asPart, prosrc, fout);
13288 : else
13289 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13290 : }
13291 : }
13292 : else
13293 : {
13294 4738 : appendPQExpBufferStr(asPart, "AS ");
13295 : /* with no bin, dollar quote src unconditionally if allowed */
13296 4738 : if (dopt->disable_dollar_quoting)
13297 0 : appendStringLiteralAH(asPart, prosrc, fout);
13298 : else
13299 4738 : appendStringLiteralDQ(asPart, prosrc, NULL);
13300 : }
13301 :
13302 5252 : if (*proconfig)
13303 : {
13304 48 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13305 0 : pg_fatal("could not parse %s array", "proconfig");
13306 : }
13307 : else
13308 : {
13309 5204 : configitems = NULL;
13310 5204 : nconfigitems = 0;
13311 : }
13312 :
13313 5252 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13314 5252 : funcsig = format_function_arguments(finfo, funciargs, false);
13315 :
13316 5252 : funcsig_tag = format_function_signature(fout, finfo, false);
13317 :
13318 5252 : qual_funcsig = psprintf("%s.%s",
13319 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13320 : funcsig);
13321 :
13322 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13323 264 : keyword = "PROCEDURE";
13324 : else
13325 4988 : keyword = "FUNCTION"; /* works for window functions too */
13326 :
13327 5252 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13328 : keyword, qual_funcsig);
13329 :
13330 10504 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13331 : keyword,
13332 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13333 : funcfullsig ? funcfullsig :
13334 : funcsig);
13335 :
13336 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13337 : /* no result type to output */ ;
13338 4988 : else if (funcresult)
13339 4988 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13340 : else
13341 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13342 0 : (proretset[0] == 't') ? "SETOF " : "",
13343 0 : getFormattedTypeName(fout, finfo->prorettype,
13344 : zeroIsError));
13345 :
13346 5252 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13347 :
13348 5252 : if (*protrftypes)
13349 : {
13350 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13351 : int i;
13352 :
13353 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13354 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13355 0 : for (i = 0; typeids[i]; i++)
13356 : {
13357 0 : if (i != 0)
13358 0 : appendPQExpBufferStr(q, ", ");
13359 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13360 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13361 : }
13362 :
13363 0 : free(typeids);
13364 : }
13365 :
13366 5252 : if (prokind[0] == PROKIND_WINDOW)
13367 16 : appendPQExpBufferStr(q, " WINDOW");
13368 :
13369 5252 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13370 : {
13371 1054 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13372 988 : appendPQExpBufferStr(q, " IMMUTABLE");
13373 66 : else if (provolatile[0] == PROVOLATILE_STABLE)
13374 66 : appendPQExpBufferStr(q, " STABLE");
13375 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13376 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13377 : finfo->dobj.name);
13378 : }
13379 :
13380 5252 : if (proisstrict[0] == 't')
13381 1030 : appendPQExpBufferStr(q, " STRICT");
13382 :
13383 5252 : if (prosecdef[0] == 't')
13384 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13385 :
13386 5252 : if (proleakproof[0] == 't')
13387 32 : appendPQExpBufferStr(q, " LEAKPROOF");
13388 :
13389 : /*
13390 : * COST and ROWS are emitted only if present and not default, so as not to
13391 : * break backwards-compatibility of the dump without need. Keep this code
13392 : * in sync with the defaults in functioncmds.c.
13393 : */
13394 5252 : if (strcmp(procost, "0") != 0)
13395 : {
13396 5252 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13397 : {
13398 : /* default cost is 1 */
13399 996 : if (strcmp(procost, "1") != 0)
13400 0 : appendPQExpBuffer(q, " COST %s", procost);
13401 : }
13402 : else
13403 : {
13404 : /* default cost is 100 */
13405 4256 : if (strcmp(procost, "100") != 0)
13406 18 : appendPQExpBuffer(q, " COST %s", procost);
13407 : }
13408 : }
13409 5252 : if (proretset[0] == 't' &&
13410 568 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13411 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13412 :
13413 5252 : if (strcmp(prosupport, "-") != 0)
13414 : {
13415 : /* We rely on regprocout to provide quoting and qualification */
13416 104 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13417 : }
13418 :
13419 5252 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13420 : {
13421 302 : if (proparallel[0] == PROPARALLEL_SAFE)
13422 286 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13423 16 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13424 16 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13425 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13426 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13427 : finfo->dobj.name);
13428 : }
13429 :
13430 5364 : for (int i = 0; i < nconfigitems; i++)
13431 : {
13432 : /* we feel free to scribble on configitems[] here */
13433 112 : char *configitem = configitems[i];
13434 : char *pos;
13435 :
13436 112 : pos = strchr(configitem, '=');
13437 112 : if (pos == NULL)
13438 0 : continue;
13439 112 : *pos++ = '\0';
13440 112 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13441 :
13442 : /*
13443 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13444 : * by flatten_set_variable_args() before they were put into the
13445 : * proconfig array. However, because the quoting rules used there
13446 : * aren't exactly like SQL's, we have to break the list value apart
13447 : * and then quote the elements as string literals. (The elements may
13448 : * be double-quoted as-is, but we can't just feed them to the SQL
13449 : * parser; it would do the wrong thing with elements that are
13450 : * zero-length or longer than NAMEDATALEN.)
13451 : *
13452 : * Variables that are not so marked should just be emitted as simple
13453 : * string literals. If the variable is not known to
13454 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13455 : * to use GUC_LIST_QUOTE for extension variables.
13456 : */
13457 112 : if (variable_is_guc_list_quote(configitem))
13458 : {
13459 : char **namelist;
13460 : char **nameptr;
13461 :
13462 : /* Parse string into list of identifiers */
13463 : /* this shouldn't fail really */
13464 32 : if (SplitGUCList(pos, ',', &namelist))
13465 : {
13466 112 : for (nameptr = namelist; *nameptr; nameptr++)
13467 : {
13468 80 : if (nameptr != namelist)
13469 48 : appendPQExpBufferStr(q, ", ");
13470 80 : appendStringLiteralAH(q, *nameptr, fout);
13471 : }
13472 : }
13473 32 : pg_free(namelist);
13474 : }
13475 : else
13476 80 : appendStringLiteralAH(q, pos, fout);
13477 : }
13478 :
13479 5252 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13480 :
13481 5252 : append_depends_on_extension(fout, q, &finfo->dobj,
13482 : "pg_catalog.pg_proc", keyword,
13483 : qual_funcsig);
13484 :
13485 5252 : if (dopt->binary_upgrade)
13486 576 : binary_upgrade_extension_member(q, &finfo->dobj,
13487 : keyword, funcsig,
13488 576 : finfo->dobj.namespace->dobj.name);
13489 :
13490 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13491 5044 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13492 5044 : ARCHIVE_OPTS(.tag = funcsig_tag,
13493 : .namespace = finfo->dobj.namespace->dobj.name,
13494 : .owner = finfo->rolname,
13495 : .description = keyword,
13496 : .section = finfo->postponed_def ?
13497 : SECTION_POST_DATA : SECTION_PRE_DATA,
13498 : .createStmt = q->data,
13499 : .dropStmt = delqry->data));
13500 :
13501 : /* Dump Function Comments and Security Labels */
13502 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13503 18 : dumpComment(fout, keyword, funcsig,
13504 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13505 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13506 :
13507 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13508 0 : dumpSecLabel(fout, keyword, funcsig,
13509 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13510 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13511 :
13512 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13513 230 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13514 : funcsig, NULL,
13515 230 : finfo->dobj.namespace->dobj.name,
13516 230 : NULL, finfo->rolname, &finfo->dacl);
13517 :
13518 5252 : PQclear(res);
13519 :
13520 5252 : destroyPQExpBuffer(query);
13521 5252 : destroyPQExpBuffer(q);
13522 5252 : destroyPQExpBuffer(delqry);
13523 5252 : destroyPQExpBuffer(asPart);
13524 5252 : free(funcsig);
13525 5252 : free(funcfullsig);
13526 5252 : free(funcsig_tag);
13527 5252 : free(qual_funcsig);
13528 5252 : free(configitems);
13529 : }
13530 :
13531 :
13532 : /*
13533 : * Dump a user-defined cast
13534 : */
13535 : static void
13536 178 : dumpCast(Archive *fout, const CastInfo *cast)
13537 : {
13538 178 : DumpOptions *dopt = fout->dopt;
13539 : PQExpBuffer defqry;
13540 : PQExpBuffer delqry;
13541 : PQExpBuffer labelq;
13542 : PQExpBuffer castargs;
13543 178 : FuncInfo *funcInfo = NULL;
13544 : const char *sourceType;
13545 : const char *targetType;
13546 :
13547 : /* Do nothing if not dumping schema */
13548 178 : if (!dopt->dumpSchema)
13549 12 : return;
13550 :
13551 : /* Cannot dump if we don't have the cast function's info */
13552 166 : if (OidIsValid(cast->castfunc))
13553 : {
13554 86 : funcInfo = findFuncByOid(cast->castfunc);
13555 86 : if (funcInfo == NULL)
13556 0 : pg_fatal("could not find function definition for function with OID %u",
13557 : cast->castfunc);
13558 : }
13559 :
13560 166 : defqry = createPQExpBuffer();
13561 166 : delqry = createPQExpBuffer();
13562 166 : labelq = createPQExpBuffer();
13563 166 : castargs = createPQExpBuffer();
13564 :
13565 166 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13566 166 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13567 166 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13568 : sourceType, targetType);
13569 :
13570 166 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13571 : sourceType, targetType);
13572 :
13573 166 : switch (cast->castmethod)
13574 : {
13575 80 : case COERCION_METHOD_BINARY:
13576 80 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13577 80 : break;
13578 0 : case COERCION_METHOD_INOUT:
13579 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13580 0 : break;
13581 86 : case COERCION_METHOD_FUNCTION:
13582 86 : if (funcInfo)
13583 : {
13584 86 : char *fsig = format_function_signature(fout, funcInfo, true);
13585 :
13586 : /*
13587 : * Always qualify the function name (format_function_signature
13588 : * won't qualify it).
13589 : */
13590 86 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13591 86 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13592 86 : free(fsig);
13593 : }
13594 : else
13595 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13596 86 : break;
13597 0 : default:
13598 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13599 : }
13600 :
13601 166 : if (cast->castcontext == 'a')
13602 70 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13603 96 : else if (cast->castcontext == 'i')
13604 32 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13605 166 : appendPQExpBufferStr(defqry, ";\n");
13606 :
13607 166 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13608 : sourceType, targetType);
13609 :
13610 166 : appendPQExpBuffer(castargs, "(%s AS %s)",
13611 : sourceType, targetType);
13612 :
13613 166 : if (dopt->binary_upgrade)
13614 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13615 14 : "CAST", castargs->data, NULL);
13616 :
13617 166 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13618 166 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13619 166 : ARCHIVE_OPTS(.tag = labelq->data,
13620 : .description = "CAST",
13621 : .section = SECTION_PRE_DATA,
13622 : .createStmt = defqry->data,
13623 : .dropStmt = delqry->data));
13624 :
13625 : /* Dump Cast Comments */
13626 166 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13627 0 : dumpComment(fout, "CAST", castargs->data,
13628 : NULL, "",
13629 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13630 :
13631 166 : destroyPQExpBuffer(defqry);
13632 166 : destroyPQExpBuffer(delqry);
13633 166 : destroyPQExpBuffer(labelq);
13634 166 : destroyPQExpBuffer(castargs);
13635 : }
13636 :
13637 : /*
13638 : * Dump a transform
13639 : */
13640 : static void
13641 98 : dumpTransform(Archive *fout, const TransformInfo *transform)
13642 : {
13643 98 : DumpOptions *dopt = fout->dopt;
13644 : PQExpBuffer defqry;
13645 : PQExpBuffer delqry;
13646 : PQExpBuffer labelq;
13647 : PQExpBuffer transformargs;
13648 98 : FuncInfo *fromsqlFuncInfo = NULL;
13649 98 : FuncInfo *tosqlFuncInfo = NULL;
13650 : char *lanname;
13651 : const char *transformType;
13652 :
13653 : /* Do nothing if not dumping schema */
13654 98 : if (!dopt->dumpSchema)
13655 12 : return;
13656 :
13657 : /* Cannot dump if we don't have the transform functions' info */
13658 86 : if (OidIsValid(transform->trffromsql))
13659 : {
13660 86 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13661 86 : if (fromsqlFuncInfo == NULL)
13662 0 : pg_fatal("could not find function definition for function with OID %u",
13663 : transform->trffromsql);
13664 : }
13665 86 : if (OidIsValid(transform->trftosql))
13666 : {
13667 86 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13668 86 : if (tosqlFuncInfo == NULL)
13669 0 : pg_fatal("could not find function definition for function with OID %u",
13670 : transform->trftosql);
13671 : }
13672 :
13673 86 : defqry = createPQExpBuffer();
13674 86 : delqry = createPQExpBuffer();
13675 86 : labelq = createPQExpBuffer();
13676 86 : transformargs = createPQExpBuffer();
13677 :
13678 86 : lanname = get_language_name(fout, transform->trflang);
13679 86 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13680 :
13681 86 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13682 : transformType, lanname);
13683 :
13684 86 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13685 : transformType, lanname);
13686 :
13687 86 : if (!transform->trffromsql && !transform->trftosql)
13688 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13689 :
13690 86 : if (transform->trffromsql)
13691 : {
13692 86 : if (fromsqlFuncInfo)
13693 : {
13694 86 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13695 :
13696 : /*
13697 : * Always qualify the function name (format_function_signature
13698 : * won't qualify it).
13699 : */
13700 86 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13701 86 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13702 86 : free(fsig);
13703 : }
13704 : else
13705 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13706 : }
13707 :
13708 86 : if (transform->trftosql)
13709 : {
13710 86 : if (transform->trffromsql)
13711 86 : appendPQExpBufferStr(defqry, ", ");
13712 :
13713 86 : if (tosqlFuncInfo)
13714 : {
13715 86 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13716 :
13717 : /*
13718 : * Always qualify the function name (format_function_signature
13719 : * won't qualify it).
13720 : */
13721 86 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13722 86 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13723 86 : free(fsig);
13724 : }
13725 : else
13726 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13727 : }
13728 :
13729 86 : appendPQExpBufferStr(defqry, ");\n");
13730 :
13731 86 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13732 : transformType, lanname);
13733 :
13734 86 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13735 : transformType, lanname);
13736 :
13737 86 : if (dopt->binary_upgrade)
13738 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13739 4 : "TRANSFORM", transformargs->data, NULL);
13740 :
13741 86 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13742 86 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13743 86 : ARCHIVE_OPTS(.tag = labelq->data,
13744 : .description = "TRANSFORM",
13745 : .section = SECTION_PRE_DATA,
13746 : .createStmt = defqry->data,
13747 : .dropStmt = delqry->data,
13748 : .deps = transform->dobj.dependencies,
13749 : .nDeps = transform->dobj.nDeps));
13750 :
13751 : /* Dump Transform Comments */
13752 86 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13753 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13754 : NULL, "",
13755 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
13756 :
13757 86 : free(lanname);
13758 86 : destroyPQExpBuffer(defqry);
13759 86 : destroyPQExpBuffer(delqry);
13760 86 : destroyPQExpBuffer(labelq);
13761 86 : destroyPQExpBuffer(transformargs);
13762 : }
13763 :
13764 :
13765 : /*
13766 : * dumpOpr
13767 : * write out a single operator definition
13768 : */
13769 : static void
13770 5098 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
13771 : {
13772 5098 : DumpOptions *dopt = fout->dopt;
13773 : PQExpBuffer query;
13774 : PQExpBuffer q;
13775 : PQExpBuffer delq;
13776 : PQExpBuffer oprid;
13777 : PQExpBuffer details;
13778 : PGresult *res;
13779 : int i_oprkind;
13780 : int i_oprcode;
13781 : int i_oprleft;
13782 : int i_oprright;
13783 : int i_oprcom;
13784 : int i_oprnegate;
13785 : int i_oprrest;
13786 : int i_oprjoin;
13787 : int i_oprcanmerge;
13788 : int i_oprcanhash;
13789 : char *oprkind;
13790 : char *oprcode;
13791 : char *oprleft;
13792 : char *oprright;
13793 : char *oprcom;
13794 : char *oprnegate;
13795 : char *oprrest;
13796 : char *oprjoin;
13797 : char *oprcanmerge;
13798 : char *oprcanhash;
13799 : char *oprregproc;
13800 : char *oprref;
13801 :
13802 : /* Do nothing if not dumping schema */
13803 5098 : if (!dopt->dumpSchema)
13804 12 : return;
13805 :
13806 : /*
13807 : * some operators are invalid because they were the result of user
13808 : * defining operators before commutators exist
13809 : */
13810 5086 : if (!OidIsValid(oprinfo->oprcode))
13811 44 : return;
13812 :
13813 5042 : query = createPQExpBuffer();
13814 5042 : q = createPQExpBuffer();
13815 5042 : delq = createPQExpBuffer();
13816 5042 : oprid = createPQExpBuffer();
13817 5042 : details = createPQExpBuffer();
13818 :
13819 5042 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13820 : {
13821 : /* Set up query for operator-specific details */
13822 94 : appendPQExpBufferStr(query,
13823 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13824 : "SELECT oprkind, "
13825 : "oprcode::pg_catalog.regprocedure, "
13826 : "oprleft::pg_catalog.regtype, "
13827 : "oprright::pg_catalog.regtype, "
13828 : "oprcom, "
13829 : "oprnegate, "
13830 : "oprrest::pg_catalog.regprocedure, "
13831 : "oprjoin::pg_catalog.regprocedure, "
13832 : "oprcanmerge, oprcanhash "
13833 : "FROM pg_catalog.pg_operator "
13834 : "WHERE oid = $1");
13835 :
13836 94 : ExecuteSqlStatement(fout, query->data);
13837 :
13838 94 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13839 : }
13840 :
13841 5042 : printfPQExpBuffer(query,
13842 : "EXECUTE dumpOpr('%u')",
13843 5042 : oprinfo->dobj.catId.oid);
13844 :
13845 5042 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13846 :
13847 5042 : i_oprkind = PQfnumber(res, "oprkind");
13848 5042 : i_oprcode = PQfnumber(res, "oprcode");
13849 5042 : i_oprleft = PQfnumber(res, "oprleft");
13850 5042 : i_oprright = PQfnumber(res, "oprright");
13851 5042 : i_oprcom = PQfnumber(res, "oprcom");
13852 5042 : i_oprnegate = PQfnumber(res, "oprnegate");
13853 5042 : i_oprrest = PQfnumber(res, "oprrest");
13854 5042 : i_oprjoin = PQfnumber(res, "oprjoin");
13855 5042 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13856 5042 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13857 :
13858 5042 : oprkind = PQgetvalue(res, 0, i_oprkind);
13859 5042 : oprcode = PQgetvalue(res, 0, i_oprcode);
13860 5042 : oprleft = PQgetvalue(res, 0, i_oprleft);
13861 5042 : oprright = PQgetvalue(res, 0, i_oprright);
13862 5042 : oprcom = PQgetvalue(res, 0, i_oprcom);
13863 5042 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13864 5042 : oprrest = PQgetvalue(res, 0, i_oprrest);
13865 5042 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13866 5042 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13867 5042 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13868 :
13869 : /* In PG14 upwards postfix operator support does not exist anymore. */
13870 5042 : if (strcmp(oprkind, "r") == 0)
13871 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13872 : oprcode);
13873 :
13874 5042 : oprregproc = convertRegProcReference(oprcode);
13875 5042 : if (oprregproc)
13876 : {
13877 5042 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13878 5042 : free(oprregproc);
13879 : }
13880 :
13881 5042 : appendPQExpBuffer(oprid, "%s (",
13882 5042 : oprinfo->dobj.name);
13883 :
13884 : /*
13885 : * right unary means there's a left arg and left unary means there's a
13886 : * right arg. (Although the "r" case is dead code for PG14 and later,
13887 : * continue to support it in case we're dumping from an old server.)
13888 : */
13889 5042 : if (strcmp(oprkind, "r") == 0 ||
13890 5042 : strcmp(oprkind, "b") == 0)
13891 : {
13892 4732 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13893 4732 : appendPQExpBufferStr(oprid, oprleft);
13894 : }
13895 : else
13896 310 : appendPQExpBufferStr(oprid, "NONE");
13897 :
13898 5042 : if (strcmp(oprkind, "l") == 0 ||
13899 4732 : strcmp(oprkind, "b") == 0)
13900 : {
13901 5042 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13902 5042 : appendPQExpBuffer(oprid, ", %s)", oprright);
13903 : }
13904 : else
13905 0 : appendPQExpBufferStr(oprid, ", NONE)");
13906 :
13907 5042 : oprref = getFormattedOperatorName(oprcom);
13908 5042 : if (oprref)
13909 : {
13910 3346 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13911 3346 : free(oprref);
13912 : }
13913 :
13914 5042 : oprref = getFormattedOperatorName(oprnegate);
13915 5042 : if (oprref)
13916 : {
13917 2332 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13918 2332 : free(oprref);
13919 : }
13920 :
13921 5042 : if (strcmp(oprcanmerge, "t") == 0)
13922 394 : appendPQExpBufferStr(details, ",\n MERGES");
13923 :
13924 5042 : if (strcmp(oprcanhash, "t") == 0)
13925 276 : appendPQExpBufferStr(details, ",\n HASHES");
13926 :
13927 5042 : oprregproc = convertRegProcReference(oprrest);
13928 5042 : if (oprregproc)
13929 : {
13930 3052 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13931 3052 : free(oprregproc);
13932 : }
13933 :
13934 5042 : oprregproc = convertRegProcReference(oprjoin);
13935 5042 : if (oprregproc)
13936 : {
13937 3052 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13938 3052 : free(oprregproc);
13939 : }
13940 :
13941 5042 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13942 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
13943 : oprid->data);
13944 :
13945 5042 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13946 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
13947 5042 : oprinfo->dobj.name, details->data);
13948 :
13949 5042 : if (dopt->binary_upgrade)
13950 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13951 24 : "OPERATOR", oprid->data,
13952 24 : oprinfo->dobj.namespace->dobj.name);
13953 :
13954 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13955 5042 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13956 5042 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13957 : .namespace = oprinfo->dobj.namespace->dobj.name,
13958 : .owner = oprinfo->rolname,
13959 : .description = "OPERATOR",
13960 : .section = SECTION_PRE_DATA,
13961 : .createStmt = q->data,
13962 : .dropStmt = delq->data));
13963 :
13964 : /* Dump Operator Comments */
13965 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13966 4794 : dumpComment(fout, "OPERATOR", oprid->data,
13967 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13968 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13969 :
13970 5042 : PQclear(res);
13971 :
13972 5042 : destroyPQExpBuffer(query);
13973 5042 : destroyPQExpBuffer(q);
13974 5042 : destroyPQExpBuffer(delq);
13975 5042 : destroyPQExpBuffer(oprid);
13976 5042 : destroyPQExpBuffer(details);
13977 : }
13978 :
13979 : /*
13980 : * Convert a function reference obtained from pg_operator
13981 : *
13982 : * Returns allocated string of what to print, or NULL if function references
13983 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13984 : *
13985 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13986 : * part.
13987 : */
13988 : static char *
13989 15126 : convertRegProcReference(const char *proc)
13990 : {
13991 : char *name;
13992 : char *paren;
13993 : bool inquote;
13994 :
13995 : /* In all cases "-" means a null reference */
13996 15126 : if (strcmp(proc, "-") == 0)
13997 3980 : return NULL;
13998 :
13999 11146 : name = pg_strdup(proc);
14000 : /* find non-double-quoted left paren */
14001 11146 : inquote = false;
14002 134312 : for (paren = name; *paren; paren++)
14003 : {
14004 134312 : if (*paren == '(' && !inquote)
14005 : {
14006 11146 : *paren = '\0';
14007 11146 : break;
14008 : }
14009 123166 : if (*paren == '"')
14010 100 : inquote = !inquote;
14011 : }
14012 11146 : return name;
14013 : }
14014 :
14015 : /*
14016 : * getFormattedOperatorName - retrieve the operator name for the
14017 : * given operator OID (presented in string form).
14018 : *
14019 : * Returns an allocated string, or NULL if the given OID is invalid.
14020 : * Caller is responsible for free'ing result string.
14021 : *
14022 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14023 : * useful in commands where the operator's argument types can be inferred from
14024 : * context. We always schema-qualify the name, though. The predecessor to
14025 : * this code tried to skip the schema qualification if possible, but that led
14026 : * to wrong results in corner cases, such as if an operator and its negator
14027 : * are in different schemas.
14028 : */
14029 : static char *
14030 10944 : getFormattedOperatorName(const char *oproid)
14031 : {
14032 : OprInfo *oprInfo;
14033 :
14034 : /* In all cases "0" means a null reference */
14035 10944 : if (strcmp(oproid, "0") == 0)
14036 5266 : return NULL;
14037 :
14038 5678 : oprInfo = findOprByOid(atooid(oproid));
14039 5678 : if (oprInfo == NULL)
14040 : {
14041 0 : pg_log_warning("could not find operator with OID %s",
14042 : oproid);
14043 0 : return NULL;
14044 : }
14045 :
14046 5678 : return psprintf("OPERATOR(%s.%s)",
14047 5678 : fmtId(oprInfo->dobj.namespace->dobj.name),
14048 : oprInfo->dobj.name);
14049 : }
14050 :
14051 : /*
14052 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14053 : *
14054 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14055 : * argument lists of these functions are predetermined. Note that the
14056 : * caller should ensure we are in the proper schema, because the results
14057 : * are search path dependent!
14058 : */
14059 : static char *
14060 450 : convertTSFunction(Archive *fout, Oid funcOid)
14061 : {
14062 : char *result;
14063 : char query[128];
14064 : PGresult *res;
14065 :
14066 450 : snprintf(query, sizeof(query),
14067 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14068 450 : res = ExecuteSqlQueryForSingleRow(fout, query);
14069 :
14070 450 : result = pg_strdup(PQgetvalue(res, 0, 0));
14071 :
14072 450 : PQclear(res);
14073 :
14074 450 : return result;
14075 : }
14076 :
14077 : /*
14078 : * dumpAccessMethod
14079 : * write out a single access method definition
14080 : */
14081 : static void
14082 182 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14083 : {
14084 182 : DumpOptions *dopt = fout->dopt;
14085 : PQExpBuffer q;
14086 : PQExpBuffer delq;
14087 : char *qamname;
14088 :
14089 : /* Do nothing if not dumping schema */
14090 182 : if (!dopt->dumpSchema)
14091 24 : return;
14092 :
14093 158 : q = createPQExpBuffer();
14094 158 : delq = createPQExpBuffer();
14095 :
14096 158 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14097 :
14098 158 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14099 :
14100 158 : switch (aminfo->amtype)
14101 : {
14102 72 : case AMTYPE_INDEX:
14103 72 : appendPQExpBufferStr(q, "TYPE INDEX ");
14104 72 : break;
14105 86 : case AMTYPE_TABLE:
14106 86 : appendPQExpBufferStr(q, "TYPE TABLE ");
14107 86 : break;
14108 0 : default:
14109 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14110 : aminfo->amtype, qamname);
14111 0 : destroyPQExpBuffer(q);
14112 0 : destroyPQExpBuffer(delq);
14113 0 : free(qamname);
14114 0 : return;
14115 : }
14116 :
14117 158 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14118 :
14119 158 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14120 : qamname);
14121 :
14122 158 : if (dopt->binary_upgrade)
14123 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14124 : "ACCESS METHOD", qamname, NULL);
14125 :
14126 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14127 158 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14128 158 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14129 : .description = "ACCESS METHOD",
14130 : .section = SECTION_PRE_DATA,
14131 : .createStmt = q->data,
14132 : .dropStmt = delq->data));
14133 :
14134 : /* Dump Access Method Comments */
14135 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14136 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14137 : NULL, "",
14138 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14139 :
14140 158 : destroyPQExpBuffer(q);
14141 158 : destroyPQExpBuffer(delq);
14142 158 : free(qamname);
14143 : }
14144 :
14145 : /*
14146 : * dumpOpclass
14147 : * write out a single operator class definition
14148 : */
14149 : static void
14150 1362 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14151 : {
14152 1362 : DumpOptions *dopt = fout->dopt;
14153 : PQExpBuffer query;
14154 : PQExpBuffer q;
14155 : PQExpBuffer delq;
14156 : PQExpBuffer nameusing;
14157 : PGresult *res;
14158 : int ntups;
14159 : int i_opcintype;
14160 : int i_opckeytype;
14161 : int i_opcdefault;
14162 : int i_opcfamily;
14163 : int i_opcfamilyname;
14164 : int i_opcfamilynsp;
14165 : int i_amname;
14166 : int i_amopstrategy;
14167 : int i_amopopr;
14168 : int i_sortfamily;
14169 : int i_sortfamilynsp;
14170 : int i_amprocnum;
14171 : int i_amproc;
14172 : int i_amproclefttype;
14173 : int i_amprocrighttype;
14174 : char *opcintype;
14175 : char *opckeytype;
14176 : char *opcdefault;
14177 : char *opcfamily;
14178 : char *opcfamilyname;
14179 : char *opcfamilynsp;
14180 : char *amname;
14181 : char *amopstrategy;
14182 : char *amopopr;
14183 : char *sortfamily;
14184 : char *sortfamilynsp;
14185 : char *amprocnum;
14186 : char *amproc;
14187 : char *amproclefttype;
14188 : char *amprocrighttype;
14189 : bool needComma;
14190 : int i;
14191 :
14192 : /* Do nothing if not dumping schema */
14193 1362 : if (!dopt->dumpSchema)
14194 36 : return;
14195 :
14196 1326 : query = createPQExpBuffer();
14197 1326 : q = createPQExpBuffer();
14198 1326 : delq = createPQExpBuffer();
14199 1326 : nameusing = createPQExpBuffer();
14200 :
14201 : /* Get additional fields from the pg_opclass row */
14202 1326 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14203 : "opckeytype::pg_catalog.regtype, "
14204 : "opcdefault, opcfamily, "
14205 : "opfname AS opcfamilyname, "
14206 : "nspname AS opcfamilynsp, "
14207 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14208 : "FROM pg_catalog.pg_opclass c "
14209 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14210 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14211 : "WHERE c.oid = '%u'::pg_catalog.oid",
14212 1326 : opcinfo->dobj.catId.oid);
14213 :
14214 1326 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14215 :
14216 1326 : i_opcintype = PQfnumber(res, "opcintype");
14217 1326 : i_opckeytype = PQfnumber(res, "opckeytype");
14218 1326 : i_opcdefault = PQfnumber(res, "opcdefault");
14219 1326 : i_opcfamily = PQfnumber(res, "opcfamily");
14220 1326 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14221 1326 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14222 1326 : i_amname = PQfnumber(res, "amname");
14223 :
14224 : /* opcintype may still be needed after we PQclear res */
14225 1326 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14226 1326 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14227 1326 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14228 : /* opcfamily will still be needed after we PQclear res */
14229 1326 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14230 1326 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14231 1326 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14232 : /* amname will still be needed after we PQclear res */
14233 1326 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14234 :
14235 1326 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14236 1326 : fmtQualifiedDumpable(opcinfo));
14237 1326 : appendPQExpBuffer(delq, " USING %s;\n",
14238 : fmtId(amname));
14239 :
14240 : /* Build the fixed portion of the CREATE command */
14241 1326 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14242 1326 : fmtQualifiedDumpable(opcinfo));
14243 1326 : if (strcmp(opcdefault, "t") == 0)
14244 714 : appendPQExpBufferStr(q, "DEFAULT ");
14245 1326 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14246 : opcintype,
14247 : fmtId(amname));
14248 1326 : if (strlen(opcfamilyname) > 0)
14249 : {
14250 1326 : appendPQExpBufferStr(q, " FAMILY ");
14251 1326 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14252 1326 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14253 : }
14254 1326 : appendPQExpBufferStr(q, " AS\n ");
14255 :
14256 1326 : needComma = false;
14257 :
14258 1326 : if (strcmp(opckeytype, "-") != 0)
14259 : {
14260 504 : appendPQExpBuffer(q, "STORAGE %s",
14261 : opckeytype);
14262 504 : needComma = true;
14263 : }
14264 :
14265 1326 : PQclear(res);
14266 :
14267 : /*
14268 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14269 : *
14270 : * Print only those opfamily members that are tied to the opclass by
14271 : * pg_depend entries.
14272 : */
14273 1326 : resetPQExpBuffer(query);
14274 1326 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14275 : "amopopr::pg_catalog.regoperator, "
14276 : "opfname AS sortfamily, "
14277 : "nspname AS sortfamilynsp "
14278 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14279 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14280 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14281 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14282 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14283 : "AND refobjid = '%u'::pg_catalog.oid "
14284 : "AND amopfamily = '%s'::pg_catalog.oid "
14285 : "ORDER BY amopstrategy",
14286 1326 : opcinfo->dobj.catId.oid,
14287 : opcfamily);
14288 :
14289 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14290 :
14291 1326 : ntups = PQntuples(res);
14292 :
14293 1326 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14294 1326 : i_amopopr = PQfnumber(res, "amopopr");
14295 1326 : i_sortfamily = PQfnumber(res, "sortfamily");
14296 1326 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14297 :
14298 1790 : for (i = 0; i < ntups; i++)
14299 : {
14300 464 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14301 464 : amopopr = PQgetvalue(res, i, i_amopopr);
14302 464 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14303 464 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14304 :
14305 464 : if (needComma)
14306 288 : appendPQExpBufferStr(q, " ,\n ");
14307 :
14308 464 : appendPQExpBuffer(q, "OPERATOR %s %s",
14309 : amopstrategy, amopopr);
14310 :
14311 464 : if (strlen(sortfamily) > 0)
14312 : {
14313 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14314 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14315 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14316 : }
14317 :
14318 464 : needComma = true;
14319 : }
14320 :
14321 1326 : PQclear(res);
14322 :
14323 : /*
14324 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14325 : *
14326 : * Print only those opfamily members that are tied to the opclass by
14327 : * pg_depend entries.
14328 : *
14329 : * We print the amproclefttype/amprocrighttype even though in most cases
14330 : * the backend could deduce the right values, because of the corner case
14331 : * of a btree sort support function for a cross-type comparison.
14332 : */
14333 1326 : resetPQExpBuffer(query);
14334 :
14335 1326 : appendPQExpBuffer(query, "SELECT amprocnum, "
14336 : "amproc::pg_catalog.regprocedure, "
14337 : "amproclefttype::pg_catalog.regtype, "
14338 : "amprocrighttype::pg_catalog.regtype "
14339 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14340 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14341 : "AND refobjid = '%u'::pg_catalog.oid "
14342 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14343 : "AND objid = ap.oid "
14344 : "ORDER BY amprocnum",
14345 1326 : opcinfo->dobj.catId.oid);
14346 :
14347 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14348 :
14349 1326 : ntups = PQntuples(res);
14350 :
14351 1326 : i_amprocnum = PQfnumber(res, "amprocnum");
14352 1326 : i_amproc = PQfnumber(res, "amproc");
14353 1326 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14354 1326 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14355 :
14356 1398 : for (i = 0; i < ntups; i++)
14357 : {
14358 72 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14359 72 : amproc = PQgetvalue(res, i, i_amproc);
14360 72 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14361 72 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14362 :
14363 72 : if (needComma)
14364 72 : appendPQExpBufferStr(q, " ,\n ");
14365 :
14366 72 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14367 :
14368 72 : if (*amproclefttype && *amprocrighttype)
14369 72 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14370 :
14371 72 : appendPQExpBuffer(q, " %s", amproc);
14372 :
14373 72 : needComma = true;
14374 : }
14375 :
14376 1326 : PQclear(res);
14377 :
14378 : /*
14379 : * If needComma is still false it means we haven't added anything after
14380 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14381 : * clause with the same datatype. This isn't sanctioned by the
14382 : * documentation, but actually DefineOpClass will treat it as a no-op.
14383 : */
14384 1326 : if (!needComma)
14385 646 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14386 :
14387 1326 : appendPQExpBufferStr(q, ";\n");
14388 :
14389 1326 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14390 1326 : appendPQExpBuffer(nameusing, " USING %s",
14391 : fmtId(amname));
14392 :
14393 1326 : if (dopt->binary_upgrade)
14394 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14395 12 : "OPERATOR CLASS", nameusing->data,
14396 12 : opcinfo->dobj.namespace->dobj.name);
14397 :
14398 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14399 1326 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14400 1326 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14401 : .namespace = opcinfo->dobj.namespace->dobj.name,
14402 : .owner = opcinfo->rolname,
14403 : .description = "OPERATOR CLASS",
14404 : .section = SECTION_PRE_DATA,
14405 : .createStmt = q->data,
14406 : .dropStmt = delq->data));
14407 :
14408 : /* Dump Operator Class Comments */
14409 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14410 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14411 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14412 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14413 :
14414 1326 : free(opcintype);
14415 1326 : free(opcfamily);
14416 1326 : free(amname);
14417 1326 : destroyPQExpBuffer(query);
14418 1326 : destroyPQExpBuffer(q);
14419 1326 : destroyPQExpBuffer(delq);
14420 1326 : destroyPQExpBuffer(nameusing);
14421 : }
14422 :
14423 : /*
14424 : * dumpOpfamily
14425 : * write out a single operator family definition
14426 : *
14427 : * Note: this also dumps any "loose" operator members that aren't bound to a
14428 : * specific opclass within the opfamily.
14429 : */
14430 : static void
14431 1156 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14432 : {
14433 1156 : DumpOptions *dopt = fout->dopt;
14434 : PQExpBuffer query;
14435 : PQExpBuffer q;
14436 : PQExpBuffer delq;
14437 : PQExpBuffer nameusing;
14438 : PGresult *res;
14439 : PGresult *res_ops;
14440 : PGresult *res_procs;
14441 : int ntups;
14442 : int i_amname;
14443 : int i_amopstrategy;
14444 : int i_amopopr;
14445 : int i_sortfamily;
14446 : int i_sortfamilynsp;
14447 : int i_amprocnum;
14448 : int i_amproc;
14449 : int i_amproclefttype;
14450 : int i_amprocrighttype;
14451 : char *amname;
14452 : char *amopstrategy;
14453 : char *amopopr;
14454 : char *sortfamily;
14455 : char *sortfamilynsp;
14456 : char *amprocnum;
14457 : char *amproc;
14458 : char *amproclefttype;
14459 : char *amprocrighttype;
14460 : bool needComma;
14461 : int i;
14462 :
14463 : /* Do nothing if not dumping schema */
14464 1156 : if (!dopt->dumpSchema)
14465 24 : return;
14466 :
14467 1132 : query = createPQExpBuffer();
14468 1132 : q = createPQExpBuffer();
14469 1132 : delq = createPQExpBuffer();
14470 1132 : nameusing = createPQExpBuffer();
14471 :
14472 : /*
14473 : * Fetch only those opfamily members that are tied directly to the
14474 : * opfamily by pg_depend entries.
14475 : */
14476 1132 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14477 : "amopopr::pg_catalog.regoperator, "
14478 : "opfname AS sortfamily, "
14479 : "nspname AS sortfamilynsp "
14480 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14481 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14482 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14483 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14484 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14485 : "AND refobjid = '%u'::pg_catalog.oid "
14486 : "AND amopfamily = '%u'::pg_catalog.oid "
14487 : "ORDER BY amopstrategy",
14488 1132 : opfinfo->dobj.catId.oid,
14489 1132 : opfinfo->dobj.catId.oid);
14490 :
14491 1132 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14492 :
14493 1132 : resetPQExpBuffer(query);
14494 :
14495 1132 : appendPQExpBuffer(query, "SELECT amprocnum, "
14496 : "amproc::pg_catalog.regprocedure, "
14497 : "amproclefttype::pg_catalog.regtype, "
14498 : "amprocrighttype::pg_catalog.regtype "
14499 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14500 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14501 : "AND refobjid = '%u'::pg_catalog.oid "
14502 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14503 : "AND objid = ap.oid "
14504 : "ORDER BY amprocnum",
14505 1132 : opfinfo->dobj.catId.oid);
14506 :
14507 1132 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14508 :
14509 : /* Get additional fields from the pg_opfamily row */
14510 1132 : resetPQExpBuffer(query);
14511 :
14512 1132 : appendPQExpBuffer(query, "SELECT "
14513 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14514 : "FROM pg_catalog.pg_opfamily "
14515 : "WHERE oid = '%u'::pg_catalog.oid",
14516 1132 : opfinfo->dobj.catId.oid);
14517 :
14518 1132 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14519 :
14520 1132 : i_amname = PQfnumber(res, "amname");
14521 :
14522 : /* amname will still be needed after we PQclear res */
14523 1132 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14524 :
14525 1132 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14526 1132 : fmtQualifiedDumpable(opfinfo));
14527 1132 : appendPQExpBuffer(delq, " USING %s;\n",
14528 : fmtId(amname));
14529 :
14530 : /* Build the fixed portion of the CREATE command */
14531 1132 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14532 1132 : fmtQualifiedDumpable(opfinfo));
14533 1132 : appendPQExpBuffer(q, " USING %s;\n",
14534 : fmtId(amname));
14535 :
14536 1132 : PQclear(res);
14537 :
14538 : /* Do we need an ALTER to add loose members? */
14539 1132 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14540 : {
14541 120 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14542 120 : fmtQualifiedDumpable(opfinfo));
14543 120 : appendPQExpBuffer(q, " USING %s ADD\n ",
14544 : fmtId(amname));
14545 :
14546 120 : needComma = false;
14547 :
14548 : /*
14549 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14550 : */
14551 120 : ntups = PQntuples(res_ops);
14552 :
14553 120 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14554 120 : i_amopopr = PQfnumber(res_ops, "amopopr");
14555 120 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14556 120 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14557 :
14558 480 : for (i = 0; i < ntups; i++)
14559 : {
14560 360 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14561 360 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14562 360 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14563 360 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14564 :
14565 360 : if (needComma)
14566 288 : appendPQExpBufferStr(q, " ,\n ");
14567 :
14568 360 : appendPQExpBuffer(q, "OPERATOR %s %s",
14569 : amopstrategy, amopopr);
14570 :
14571 360 : if (strlen(sortfamily) > 0)
14572 : {
14573 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14574 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14575 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14576 : }
14577 :
14578 360 : needComma = true;
14579 : }
14580 :
14581 : /*
14582 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14583 : */
14584 120 : ntups = PQntuples(res_procs);
14585 :
14586 120 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14587 120 : i_amproc = PQfnumber(res_procs, "amproc");
14588 120 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14589 120 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14590 :
14591 528 : for (i = 0; i < ntups; i++)
14592 : {
14593 408 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14594 408 : amproc = PQgetvalue(res_procs, i, i_amproc);
14595 408 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14596 408 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14597 :
14598 408 : if (needComma)
14599 360 : appendPQExpBufferStr(q, " ,\n ");
14600 :
14601 408 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14602 : amprocnum, amproclefttype, amprocrighttype,
14603 : amproc);
14604 :
14605 408 : needComma = true;
14606 : }
14607 :
14608 120 : appendPQExpBufferStr(q, ";\n");
14609 : }
14610 :
14611 1132 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14612 1132 : appendPQExpBuffer(nameusing, " USING %s",
14613 : fmtId(amname));
14614 :
14615 1132 : if (dopt->binary_upgrade)
14616 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14617 18 : "OPERATOR FAMILY", nameusing->data,
14618 18 : opfinfo->dobj.namespace->dobj.name);
14619 :
14620 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 1132 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14622 1132 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14623 : .namespace = opfinfo->dobj.namespace->dobj.name,
14624 : .owner = opfinfo->rolname,
14625 : .description = "OPERATOR FAMILY",
14626 : .section = SECTION_PRE_DATA,
14627 : .createStmt = q->data,
14628 : .dropStmt = delq->data));
14629 :
14630 : /* Dump Operator Family Comments */
14631 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14632 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14633 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14634 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14635 :
14636 1132 : free(amname);
14637 1132 : PQclear(res_ops);
14638 1132 : PQclear(res_procs);
14639 1132 : destroyPQExpBuffer(query);
14640 1132 : destroyPQExpBuffer(q);
14641 1132 : destroyPQExpBuffer(delq);
14642 1132 : destroyPQExpBuffer(nameusing);
14643 : }
14644 :
14645 : /*
14646 : * dumpCollation
14647 : * write out a single collation definition
14648 : */
14649 : static void
14650 5108 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14651 : {
14652 5108 : DumpOptions *dopt = fout->dopt;
14653 : PQExpBuffer query;
14654 : PQExpBuffer q;
14655 : PQExpBuffer delq;
14656 : char *qcollname;
14657 : PGresult *res;
14658 : int i_collprovider;
14659 : int i_collisdeterministic;
14660 : int i_collcollate;
14661 : int i_collctype;
14662 : int i_colllocale;
14663 : int i_collicurules;
14664 : const char *collprovider;
14665 : const char *collcollate;
14666 : const char *collctype;
14667 : const char *colllocale;
14668 : const char *collicurules;
14669 :
14670 : /* Do nothing if not dumping schema */
14671 5108 : if (!dopt->dumpSchema)
14672 24 : return;
14673 :
14674 5084 : query = createPQExpBuffer();
14675 5084 : q = createPQExpBuffer();
14676 5084 : delq = createPQExpBuffer();
14677 :
14678 5084 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14679 :
14680 : /* Get collation-specific details */
14681 5084 : appendPQExpBufferStr(query, "SELECT ");
14682 :
14683 5084 : if (fout->remoteVersion >= 100000)
14684 5084 : appendPQExpBufferStr(query,
14685 : "collprovider, "
14686 : "collversion, ");
14687 : else
14688 0 : appendPQExpBufferStr(query,
14689 : "'c' AS collprovider, "
14690 : "NULL AS collversion, ");
14691 :
14692 5084 : if (fout->remoteVersion >= 120000)
14693 5084 : appendPQExpBufferStr(query,
14694 : "collisdeterministic, ");
14695 : else
14696 0 : appendPQExpBufferStr(query,
14697 : "true AS collisdeterministic, ");
14698 :
14699 5084 : if (fout->remoteVersion >= 170000)
14700 5084 : appendPQExpBufferStr(query,
14701 : "colllocale, ");
14702 0 : else if (fout->remoteVersion >= 150000)
14703 0 : appendPQExpBufferStr(query,
14704 : "colliculocale AS colllocale, ");
14705 : else
14706 0 : appendPQExpBufferStr(query,
14707 : "NULL AS colllocale, ");
14708 :
14709 5084 : if (fout->remoteVersion >= 160000)
14710 5084 : appendPQExpBufferStr(query,
14711 : "collicurules, ");
14712 : else
14713 0 : appendPQExpBufferStr(query,
14714 : "NULL AS collicurules, ");
14715 :
14716 5084 : appendPQExpBuffer(query,
14717 : "collcollate, "
14718 : "collctype "
14719 : "FROM pg_catalog.pg_collation c "
14720 : "WHERE c.oid = '%u'::pg_catalog.oid",
14721 5084 : collinfo->dobj.catId.oid);
14722 :
14723 5084 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14724 :
14725 5084 : i_collprovider = PQfnumber(res, "collprovider");
14726 5084 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14727 5084 : i_collcollate = PQfnumber(res, "collcollate");
14728 5084 : i_collctype = PQfnumber(res, "collctype");
14729 5084 : i_colllocale = PQfnumber(res, "colllocale");
14730 5084 : i_collicurules = PQfnumber(res, "collicurules");
14731 :
14732 5084 : collprovider = PQgetvalue(res, 0, i_collprovider);
14733 :
14734 5084 : if (!PQgetisnull(res, 0, i_collcollate))
14735 100 : collcollate = PQgetvalue(res, 0, i_collcollate);
14736 : else
14737 4984 : collcollate = NULL;
14738 :
14739 5084 : if (!PQgetisnull(res, 0, i_collctype))
14740 100 : collctype = PQgetvalue(res, 0, i_collctype);
14741 : else
14742 4984 : collctype = NULL;
14743 :
14744 : /*
14745 : * Before version 15, collcollate and collctype were of type NAME and
14746 : * non-nullable. Treat empty strings as NULL for consistency.
14747 : */
14748 5084 : if (fout->remoteVersion < 150000)
14749 : {
14750 0 : if (collcollate[0] == '\0')
14751 0 : collcollate = NULL;
14752 0 : if (collctype[0] == '\0')
14753 0 : collctype = NULL;
14754 : }
14755 :
14756 5084 : if (!PQgetisnull(res, 0, i_colllocale))
14757 4978 : colllocale = PQgetvalue(res, 0, i_colllocale);
14758 : else
14759 106 : colllocale = NULL;
14760 :
14761 5084 : if (!PQgetisnull(res, 0, i_collicurules))
14762 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
14763 : else
14764 5084 : collicurules = NULL;
14765 :
14766 5084 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
14767 5084 : fmtQualifiedDumpable(collinfo));
14768 :
14769 5084 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
14770 5084 : fmtQualifiedDumpable(collinfo));
14771 :
14772 5084 : appendPQExpBufferStr(q, "provider = ");
14773 5084 : if (collprovider[0] == 'b')
14774 50 : appendPQExpBufferStr(q, "builtin");
14775 5034 : else if (collprovider[0] == 'c')
14776 100 : appendPQExpBufferStr(q, "libc");
14777 4934 : else if (collprovider[0] == 'i')
14778 4928 : appendPQExpBufferStr(q, "icu");
14779 6 : else if (collprovider[0] == 'd')
14780 : /* to allow dumping pg_catalog; not accepted on input */
14781 6 : appendPQExpBufferStr(q, "default");
14782 : else
14783 0 : pg_fatal("unrecognized collation provider: %s",
14784 : collprovider);
14785 :
14786 5084 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14787 0 : appendPQExpBufferStr(q, ", deterministic = false");
14788 :
14789 5084 : if (collprovider[0] == 'd')
14790 : {
14791 6 : if (collcollate || collctype || colllocale || collicurules)
14792 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14793 :
14794 : /* no locale -- the default collation cannot be reloaded anyway */
14795 : }
14796 5078 : else if (collprovider[0] == 'b')
14797 : {
14798 50 : if (collcollate || collctype || !colllocale || collicurules)
14799 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14800 :
14801 50 : appendPQExpBufferStr(q, ", locale = ");
14802 50 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14803 : fout);
14804 : }
14805 5028 : else if (collprovider[0] == 'i')
14806 : {
14807 4928 : if (fout->remoteVersion >= 150000)
14808 : {
14809 4928 : if (collcollate || collctype || !colllocale)
14810 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14811 :
14812 4928 : appendPQExpBufferStr(q, ", locale = ");
14813 4928 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14814 : fout);
14815 : }
14816 : else
14817 : {
14818 0 : if (!collcollate || !collctype || colllocale ||
14819 0 : strcmp(collcollate, collctype) != 0)
14820 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14821 :
14822 0 : appendPQExpBufferStr(q, ", locale = ");
14823 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14824 : }
14825 :
14826 4928 : if (collicurules)
14827 : {
14828 0 : appendPQExpBufferStr(q, ", rules = ");
14829 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14830 : }
14831 : }
14832 100 : else if (collprovider[0] == 'c')
14833 : {
14834 100 : if (colllocale || collicurules || !collcollate || !collctype)
14835 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14836 :
14837 100 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14838 : {
14839 100 : appendPQExpBufferStr(q, ", locale = ");
14840 100 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14841 : }
14842 : else
14843 : {
14844 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14845 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14846 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14847 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14848 : }
14849 : }
14850 : else
14851 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14852 :
14853 : /*
14854 : * For binary upgrade, carry over the collation version. For normal
14855 : * dump/restore, omit the version, so that it is computed upon restore.
14856 : */
14857 5084 : if (dopt->binary_upgrade)
14858 : {
14859 : int i_collversion;
14860 :
14861 10 : i_collversion = PQfnumber(res, "collversion");
14862 10 : if (!PQgetisnull(res, 0, i_collversion))
14863 : {
14864 8 : appendPQExpBufferStr(q, ", version = ");
14865 8 : appendStringLiteralAH(q,
14866 : PQgetvalue(res, 0, i_collversion),
14867 : fout);
14868 : }
14869 : }
14870 :
14871 5084 : appendPQExpBufferStr(q, ");\n");
14872 :
14873 5084 : if (dopt->binary_upgrade)
14874 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
14875 : "COLLATION", qcollname,
14876 10 : collinfo->dobj.namespace->dobj.name);
14877 :
14878 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14879 5084 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14880 5084 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14881 : .namespace = collinfo->dobj.namespace->dobj.name,
14882 : .owner = collinfo->rolname,
14883 : .description = "COLLATION",
14884 : .section = SECTION_PRE_DATA,
14885 : .createStmt = q->data,
14886 : .dropStmt = delq->data));
14887 :
14888 : /* Dump Collation Comments */
14889 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14890 4870 : dumpComment(fout, "COLLATION", qcollname,
14891 4870 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14892 4870 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14893 :
14894 5084 : PQclear(res);
14895 :
14896 5084 : destroyPQExpBuffer(query);
14897 5084 : destroyPQExpBuffer(q);
14898 5084 : destroyPQExpBuffer(delq);
14899 5084 : free(qcollname);
14900 : }
14901 :
14902 : /*
14903 : * dumpConversion
14904 : * write out a single conversion definition
14905 : */
14906 : static void
14907 852 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14908 : {
14909 852 : DumpOptions *dopt = fout->dopt;
14910 : PQExpBuffer query;
14911 : PQExpBuffer q;
14912 : PQExpBuffer delq;
14913 : char *qconvname;
14914 : PGresult *res;
14915 : int i_conforencoding;
14916 : int i_contoencoding;
14917 : int i_conproc;
14918 : int i_condefault;
14919 : const char *conforencoding;
14920 : const char *contoencoding;
14921 : const char *conproc;
14922 : bool condefault;
14923 :
14924 : /* Do nothing if not dumping schema */
14925 852 : if (!dopt->dumpSchema)
14926 12 : return;
14927 :
14928 840 : query = createPQExpBuffer();
14929 840 : q = createPQExpBuffer();
14930 840 : delq = createPQExpBuffer();
14931 :
14932 840 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14933 :
14934 : /* Get conversion-specific details */
14935 840 : appendPQExpBuffer(query, "SELECT "
14936 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14937 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14938 : "conproc, condefault "
14939 : "FROM pg_catalog.pg_conversion c "
14940 : "WHERE c.oid = '%u'::pg_catalog.oid",
14941 840 : convinfo->dobj.catId.oid);
14942 :
14943 840 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14944 :
14945 840 : i_conforencoding = PQfnumber(res, "conforencoding");
14946 840 : i_contoencoding = PQfnumber(res, "contoencoding");
14947 840 : i_conproc = PQfnumber(res, "conproc");
14948 840 : i_condefault = PQfnumber(res, "condefault");
14949 :
14950 840 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14951 840 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14952 840 : conproc = PQgetvalue(res, 0, i_conproc);
14953 840 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14954 :
14955 840 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14956 840 : fmtQualifiedDumpable(convinfo));
14957 :
14958 840 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14959 : (condefault) ? "DEFAULT " : "",
14960 840 : fmtQualifiedDumpable(convinfo));
14961 840 : appendStringLiteralAH(q, conforencoding, fout);
14962 840 : appendPQExpBufferStr(q, " TO ");
14963 840 : appendStringLiteralAH(q, contoencoding, fout);
14964 : /* regproc output is already sufficiently quoted */
14965 840 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14966 :
14967 840 : if (dopt->binary_upgrade)
14968 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14969 : "CONVERSION", qconvname,
14970 2 : convinfo->dobj.namespace->dobj.name);
14971 :
14972 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14973 840 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14974 840 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14975 : .namespace = convinfo->dobj.namespace->dobj.name,
14976 : .owner = convinfo->rolname,
14977 : .description = "CONVERSION",
14978 : .section = SECTION_PRE_DATA,
14979 : .createStmt = q->data,
14980 : .dropStmt = delq->data));
14981 :
14982 : /* Dump Conversion Comments */
14983 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14984 840 : dumpComment(fout, "CONVERSION", qconvname,
14985 840 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14986 840 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14987 :
14988 840 : PQclear(res);
14989 :
14990 840 : destroyPQExpBuffer(query);
14991 840 : destroyPQExpBuffer(q);
14992 840 : destroyPQExpBuffer(delq);
14993 840 : free(qconvname);
14994 : }
14995 :
14996 : /*
14997 : * format_aggregate_signature: generate aggregate name and argument list
14998 : *
14999 : * The argument type names are qualified if needed. The aggregate name
15000 : * is never qualified.
15001 : */
15002 : static char *
15003 860 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15004 : {
15005 : PQExpBufferData buf;
15006 : int j;
15007 :
15008 860 : initPQExpBuffer(&buf);
15009 860 : if (honor_quotes)
15010 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15011 : else
15012 860 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15013 :
15014 860 : if (agginfo->aggfn.nargs == 0)
15015 128 : appendPQExpBufferStr(&buf, "(*)");
15016 : else
15017 : {
15018 732 : appendPQExpBufferChar(&buf, '(');
15019 1608 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15020 876 : appendPQExpBuffer(&buf, "%s%s",
15021 : (j > 0) ? ", " : "",
15022 : getFormattedTypeName(fout,
15023 876 : agginfo->aggfn.argtypes[j],
15024 : zeroIsError));
15025 732 : appendPQExpBufferChar(&buf, ')');
15026 : }
15027 860 : return buf.data;
15028 : }
15029 :
15030 : /*
15031 : * dumpAgg
15032 : * write out a single aggregate definition
15033 : */
15034 : static void
15035 874 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15036 : {
15037 874 : DumpOptions *dopt = fout->dopt;
15038 : PQExpBuffer query;
15039 : PQExpBuffer q;
15040 : PQExpBuffer delq;
15041 : PQExpBuffer details;
15042 : char *aggsig; /* identity signature */
15043 874 : char *aggfullsig = NULL; /* full signature */
15044 : char *aggsig_tag;
15045 : PGresult *res;
15046 : int i_agginitval;
15047 : int i_aggminitval;
15048 : const char *aggtransfn;
15049 : const char *aggfinalfn;
15050 : const char *aggcombinefn;
15051 : const char *aggserialfn;
15052 : const char *aggdeserialfn;
15053 : const char *aggmtransfn;
15054 : const char *aggminvtransfn;
15055 : const char *aggmfinalfn;
15056 : bool aggfinalextra;
15057 : bool aggmfinalextra;
15058 : char aggfinalmodify;
15059 : char aggmfinalmodify;
15060 : const char *aggsortop;
15061 : char *aggsortconvop;
15062 : char aggkind;
15063 : const char *aggtranstype;
15064 : const char *aggtransspace;
15065 : const char *aggmtranstype;
15066 : const char *aggmtransspace;
15067 : const char *agginitval;
15068 : const char *aggminitval;
15069 : const char *proparallel;
15070 : char defaultfinalmodify;
15071 :
15072 : /* Do nothing if not dumping schema */
15073 874 : if (!dopt->dumpSchema)
15074 14 : return;
15075 :
15076 860 : query = createPQExpBuffer();
15077 860 : q = createPQExpBuffer();
15078 860 : delq = createPQExpBuffer();
15079 860 : details = createPQExpBuffer();
15080 :
15081 860 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15082 : {
15083 : /* Set up query for aggregate-specific details */
15084 124 : appendPQExpBufferStr(query,
15085 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15086 :
15087 124 : appendPQExpBufferStr(query,
15088 : "SELECT "
15089 : "aggtransfn,\n"
15090 : "aggfinalfn,\n"
15091 : "aggtranstype::pg_catalog.regtype,\n"
15092 : "agginitval,\n"
15093 : "aggsortop,\n"
15094 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15095 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15096 :
15097 124 : if (fout->remoteVersion >= 90400)
15098 124 : appendPQExpBufferStr(query,
15099 : "aggkind,\n"
15100 : "aggmtransfn,\n"
15101 : "aggminvtransfn,\n"
15102 : "aggmfinalfn,\n"
15103 : "aggmtranstype::pg_catalog.regtype,\n"
15104 : "aggfinalextra,\n"
15105 : "aggmfinalextra,\n"
15106 : "aggtransspace,\n"
15107 : "aggmtransspace,\n"
15108 : "aggminitval,\n");
15109 : else
15110 0 : appendPQExpBufferStr(query,
15111 : "'n' AS aggkind,\n"
15112 : "'-' AS aggmtransfn,\n"
15113 : "'-' AS aggminvtransfn,\n"
15114 : "'-' AS aggmfinalfn,\n"
15115 : "0 AS aggmtranstype,\n"
15116 : "false AS aggfinalextra,\n"
15117 : "false AS aggmfinalextra,\n"
15118 : "0 AS aggtransspace,\n"
15119 : "0 AS aggmtransspace,\n"
15120 : "NULL AS aggminitval,\n");
15121 :
15122 124 : if (fout->remoteVersion >= 90600)
15123 124 : appendPQExpBufferStr(query,
15124 : "aggcombinefn,\n"
15125 : "aggserialfn,\n"
15126 : "aggdeserialfn,\n"
15127 : "proparallel,\n");
15128 : else
15129 0 : appendPQExpBufferStr(query,
15130 : "'-' AS aggcombinefn,\n"
15131 : "'-' AS aggserialfn,\n"
15132 : "'-' AS aggdeserialfn,\n"
15133 : "'u' AS proparallel,\n");
15134 :
15135 124 : if (fout->remoteVersion >= 110000)
15136 124 : appendPQExpBufferStr(query,
15137 : "aggfinalmodify,\n"
15138 : "aggmfinalmodify\n");
15139 : else
15140 0 : appendPQExpBufferStr(query,
15141 : "'0' AS aggfinalmodify,\n"
15142 : "'0' AS aggmfinalmodify\n");
15143 :
15144 124 : appendPQExpBufferStr(query,
15145 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15146 : "WHERE a.aggfnoid = p.oid "
15147 : "AND p.oid = $1");
15148 :
15149 124 : ExecuteSqlStatement(fout, query->data);
15150 :
15151 124 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15152 : }
15153 :
15154 860 : printfPQExpBuffer(query,
15155 : "EXECUTE dumpAgg('%u')",
15156 860 : agginfo->aggfn.dobj.catId.oid);
15157 :
15158 860 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15159 :
15160 860 : i_agginitval = PQfnumber(res, "agginitval");
15161 860 : i_aggminitval = PQfnumber(res, "aggminitval");
15162 :
15163 860 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15164 860 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15165 860 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15166 860 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15167 860 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15168 860 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15169 860 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15170 860 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15171 860 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15172 860 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15173 860 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15174 860 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15175 860 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15176 860 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15177 860 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15178 860 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15179 860 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15180 860 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15181 860 : agginitval = PQgetvalue(res, 0, i_agginitval);
15182 860 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15183 860 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15184 :
15185 : {
15186 : char *funcargs;
15187 : char *funciargs;
15188 :
15189 860 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15190 860 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15191 860 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15192 860 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15193 : }
15194 :
15195 860 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15196 :
15197 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15198 860 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15199 : /* replace omitted flags for old versions */
15200 860 : if (aggfinalmodify == '0')
15201 0 : aggfinalmodify = defaultfinalmodify;
15202 860 : if (aggmfinalmodify == '0')
15203 0 : aggmfinalmodify = defaultfinalmodify;
15204 :
15205 : /* regproc and regtype output is already sufficiently quoted */
15206 860 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15207 : aggtransfn, aggtranstype);
15208 :
15209 860 : if (strcmp(aggtransspace, "0") != 0)
15210 : {
15211 16 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15212 : aggtransspace);
15213 : }
15214 :
15215 860 : if (!PQgetisnull(res, 0, i_agginitval))
15216 : {
15217 632 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15218 632 : appendStringLiteralAH(details, agginitval, fout);
15219 : }
15220 :
15221 860 : if (strcmp(aggfinalfn, "-") != 0)
15222 : {
15223 392 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15224 : aggfinalfn);
15225 392 : if (aggfinalextra)
15226 32 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15227 392 : if (aggfinalmodify != defaultfinalmodify)
15228 : {
15229 72 : switch (aggfinalmodify)
15230 : {
15231 0 : case AGGMODIFY_READ_ONLY:
15232 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15233 0 : break;
15234 72 : case AGGMODIFY_SHAREABLE:
15235 72 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15236 72 : break;
15237 0 : case AGGMODIFY_READ_WRITE:
15238 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15239 0 : break;
15240 0 : default:
15241 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15242 : agginfo->aggfn.dobj.name);
15243 : break;
15244 : }
15245 : }
15246 : }
15247 :
15248 860 : if (strcmp(aggcombinefn, "-") != 0)
15249 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15250 :
15251 860 : if (strcmp(aggserialfn, "-") != 0)
15252 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15253 :
15254 860 : if (strcmp(aggdeserialfn, "-") != 0)
15255 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15256 :
15257 860 : if (strcmp(aggmtransfn, "-") != 0)
15258 : {
15259 96 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15260 : aggmtransfn,
15261 : aggminvtransfn,
15262 : aggmtranstype);
15263 : }
15264 :
15265 860 : if (strcmp(aggmtransspace, "0") != 0)
15266 : {
15267 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15268 : aggmtransspace);
15269 : }
15270 :
15271 860 : if (!PQgetisnull(res, 0, i_aggminitval))
15272 : {
15273 32 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15274 32 : appendStringLiteralAH(details, aggminitval, fout);
15275 : }
15276 :
15277 860 : if (strcmp(aggmfinalfn, "-") != 0)
15278 : {
15279 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15280 : aggmfinalfn);
15281 0 : if (aggmfinalextra)
15282 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15283 0 : if (aggmfinalmodify != defaultfinalmodify)
15284 : {
15285 0 : switch (aggmfinalmodify)
15286 : {
15287 0 : case AGGMODIFY_READ_ONLY:
15288 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15289 0 : break;
15290 0 : case AGGMODIFY_SHAREABLE:
15291 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15292 0 : break;
15293 0 : case AGGMODIFY_READ_WRITE:
15294 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15295 0 : break;
15296 0 : default:
15297 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15298 : agginfo->aggfn.dobj.name);
15299 : break;
15300 : }
15301 : }
15302 : }
15303 :
15304 860 : aggsortconvop = getFormattedOperatorName(aggsortop);
15305 860 : if (aggsortconvop)
15306 : {
15307 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15308 : aggsortconvop);
15309 0 : free(aggsortconvop);
15310 : }
15311 :
15312 860 : if (aggkind == AGGKIND_HYPOTHETICAL)
15313 16 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15314 :
15315 860 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15316 : {
15317 16 : if (proparallel[0] == PROPARALLEL_SAFE)
15318 16 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15319 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15320 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15321 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15322 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15323 : agginfo->aggfn.dobj.name);
15324 : }
15325 :
15326 860 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15327 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15328 : aggsig);
15329 :
15330 1720 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15331 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15332 : aggfullsig ? aggfullsig : aggsig, details->data);
15333 :
15334 860 : if (dopt->binary_upgrade)
15335 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15336 : "AGGREGATE", aggsig,
15337 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15338 :
15339 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15340 826 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15341 826 : agginfo->aggfn.dobj.dumpId,
15342 826 : ARCHIVE_OPTS(.tag = aggsig_tag,
15343 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15344 : .owner = agginfo->aggfn.rolname,
15345 : .description = "AGGREGATE",
15346 : .section = SECTION_PRE_DATA,
15347 : .createStmt = q->data,
15348 : .dropStmt = delq->data));
15349 :
15350 : /* Dump Aggregate Comments */
15351 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15352 32 : dumpComment(fout, "AGGREGATE", aggsig,
15353 32 : agginfo->aggfn.dobj.namespace->dobj.name,
15354 32 : agginfo->aggfn.rolname,
15355 32 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15356 :
15357 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15358 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15359 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15360 0 : agginfo->aggfn.rolname,
15361 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15362 :
15363 : /*
15364 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15365 : * command look like a function's GRANT; in particular this affects the
15366 : * syntax for zero-argument aggregates and ordered-set aggregates.
15367 : */
15368 860 : free(aggsig);
15369 :
15370 860 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15371 :
15372 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15373 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15374 : "FUNCTION", aggsig, NULL,
15375 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15376 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15377 :
15378 860 : free(aggsig);
15379 860 : free(aggfullsig);
15380 860 : free(aggsig_tag);
15381 :
15382 860 : PQclear(res);
15383 :
15384 860 : destroyPQExpBuffer(query);
15385 860 : destroyPQExpBuffer(q);
15386 860 : destroyPQExpBuffer(delq);
15387 860 : destroyPQExpBuffer(details);
15388 : }
15389 :
15390 : /*
15391 : * dumpTSParser
15392 : * write out a single text search parser
15393 : */
15394 : static void
15395 90 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15396 : {
15397 90 : DumpOptions *dopt = fout->dopt;
15398 : PQExpBuffer q;
15399 : PQExpBuffer delq;
15400 : char *qprsname;
15401 :
15402 : /* Do nothing if not dumping schema */
15403 90 : if (!dopt->dumpSchema)
15404 12 : return;
15405 :
15406 78 : q = createPQExpBuffer();
15407 78 : delq = createPQExpBuffer();
15408 :
15409 78 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15410 :
15411 78 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15412 78 : fmtQualifiedDumpable(prsinfo));
15413 :
15414 78 : appendPQExpBuffer(q, " START = %s,\n",
15415 78 : convertTSFunction(fout, prsinfo->prsstart));
15416 78 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15417 78 : convertTSFunction(fout, prsinfo->prstoken));
15418 78 : appendPQExpBuffer(q, " END = %s,\n",
15419 78 : convertTSFunction(fout, prsinfo->prsend));
15420 78 : if (prsinfo->prsheadline != InvalidOid)
15421 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15422 6 : convertTSFunction(fout, prsinfo->prsheadline));
15423 78 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15424 78 : convertTSFunction(fout, prsinfo->prslextype));
15425 :
15426 78 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15427 78 : fmtQualifiedDumpable(prsinfo));
15428 :
15429 78 : if (dopt->binary_upgrade)
15430 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15431 : "TEXT SEARCH PARSER", qprsname,
15432 2 : prsinfo->dobj.namespace->dobj.name);
15433 :
15434 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15435 78 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15436 78 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15437 : .namespace = prsinfo->dobj.namespace->dobj.name,
15438 : .description = "TEXT SEARCH PARSER",
15439 : .section = SECTION_PRE_DATA,
15440 : .createStmt = q->data,
15441 : .dropStmt = delq->data));
15442 :
15443 : /* Dump Parser Comments */
15444 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15445 78 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15446 78 : prsinfo->dobj.namespace->dobj.name, "",
15447 78 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15448 :
15449 78 : destroyPQExpBuffer(q);
15450 78 : destroyPQExpBuffer(delq);
15451 78 : free(qprsname);
15452 : }
15453 :
15454 : /*
15455 : * dumpTSDictionary
15456 : * write out a single text search dictionary
15457 : */
15458 : static void
15459 408 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15460 : {
15461 408 : DumpOptions *dopt = fout->dopt;
15462 : PQExpBuffer q;
15463 : PQExpBuffer delq;
15464 : PQExpBuffer query;
15465 : char *qdictname;
15466 : PGresult *res;
15467 : char *nspname;
15468 : char *tmplname;
15469 :
15470 : /* Do nothing if not dumping schema */
15471 408 : if (!dopt->dumpSchema)
15472 12 : return;
15473 :
15474 396 : q = createPQExpBuffer();
15475 396 : delq = createPQExpBuffer();
15476 396 : query = createPQExpBuffer();
15477 :
15478 396 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15479 :
15480 : /* Fetch name and namespace of the dictionary's template */
15481 396 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15482 : "FROM pg_ts_template p, pg_namespace n "
15483 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15484 396 : dictinfo->dicttemplate);
15485 396 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15486 396 : nspname = PQgetvalue(res, 0, 0);
15487 396 : tmplname = PQgetvalue(res, 0, 1);
15488 :
15489 396 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15490 396 : fmtQualifiedDumpable(dictinfo));
15491 :
15492 396 : appendPQExpBufferStr(q, " TEMPLATE = ");
15493 396 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15494 396 : appendPQExpBufferStr(q, fmtId(tmplname));
15495 :
15496 396 : PQclear(res);
15497 :
15498 : /* the dictinitoption can be dumped straight into the command */
15499 396 : if (dictinfo->dictinitoption)
15500 318 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15501 :
15502 396 : appendPQExpBufferStr(q, " );\n");
15503 :
15504 396 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15505 396 : fmtQualifiedDumpable(dictinfo));
15506 :
15507 396 : if (dopt->binary_upgrade)
15508 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15509 : "TEXT SEARCH DICTIONARY", qdictname,
15510 20 : dictinfo->dobj.namespace->dobj.name);
15511 :
15512 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15513 396 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15514 396 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15515 : .namespace = dictinfo->dobj.namespace->dobj.name,
15516 : .owner = dictinfo->rolname,
15517 : .description = "TEXT SEARCH DICTIONARY",
15518 : .section = SECTION_PRE_DATA,
15519 : .createStmt = q->data,
15520 : .dropStmt = delq->data));
15521 :
15522 : /* Dump Dictionary Comments */
15523 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15524 252 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15525 252 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15526 252 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15527 :
15528 396 : destroyPQExpBuffer(q);
15529 396 : destroyPQExpBuffer(delq);
15530 396 : destroyPQExpBuffer(query);
15531 396 : free(qdictname);
15532 : }
15533 :
15534 : /*
15535 : * dumpTSTemplate
15536 : * write out a single text search template
15537 : */
15538 : static void
15539 114 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15540 : {
15541 114 : DumpOptions *dopt = fout->dopt;
15542 : PQExpBuffer q;
15543 : PQExpBuffer delq;
15544 : char *qtmplname;
15545 :
15546 : /* Do nothing if not dumping schema */
15547 114 : if (!dopt->dumpSchema)
15548 12 : return;
15549 :
15550 102 : q = createPQExpBuffer();
15551 102 : delq = createPQExpBuffer();
15552 :
15553 102 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15554 :
15555 102 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15556 102 : fmtQualifiedDumpable(tmplinfo));
15557 :
15558 102 : if (tmplinfo->tmplinit != InvalidOid)
15559 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15560 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15561 102 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15562 102 : convertTSFunction(fout, tmplinfo->tmpllexize));
15563 :
15564 102 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15565 102 : fmtQualifiedDumpable(tmplinfo));
15566 :
15567 102 : if (dopt->binary_upgrade)
15568 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15569 : "TEXT SEARCH TEMPLATE", qtmplname,
15570 2 : tmplinfo->dobj.namespace->dobj.name);
15571 :
15572 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15573 102 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15574 102 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15575 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15576 : .description = "TEXT SEARCH TEMPLATE",
15577 : .section = SECTION_PRE_DATA,
15578 : .createStmt = q->data,
15579 : .dropStmt = delq->data));
15580 :
15581 : /* Dump Template Comments */
15582 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15583 102 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15584 102 : tmplinfo->dobj.namespace->dobj.name, "",
15585 102 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15586 :
15587 102 : destroyPQExpBuffer(q);
15588 102 : destroyPQExpBuffer(delq);
15589 102 : free(qtmplname);
15590 : }
15591 :
15592 : /*
15593 : * dumpTSConfig
15594 : * write out a single text search configuration
15595 : */
15596 : static void
15597 328 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15598 : {
15599 328 : DumpOptions *dopt = fout->dopt;
15600 : PQExpBuffer q;
15601 : PQExpBuffer delq;
15602 : PQExpBuffer query;
15603 : char *qcfgname;
15604 : PGresult *res;
15605 : char *nspname;
15606 : char *prsname;
15607 : int ntups,
15608 : i;
15609 : int i_tokenname;
15610 : int i_dictname;
15611 :
15612 : /* Do nothing if not dumping schema */
15613 328 : if (!dopt->dumpSchema)
15614 12 : return;
15615 :
15616 316 : q = createPQExpBuffer();
15617 316 : delq = createPQExpBuffer();
15618 316 : query = createPQExpBuffer();
15619 :
15620 316 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15621 :
15622 : /* Fetch name and namespace of the config's parser */
15623 316 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15624 : "FROM pg_ts_parser p, pg_namespace n "
15625 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15626 316 : cfginfo->cfgparser);
15627 316 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15628 316 : nspname = PQgetvalue(res, 0, 0);
15629 316 : prsname = PQgetvalue(res, 0, 1);
15630 :
15631 316 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15632 316 : fmtQualifiedDumpable(cfginfo));
15633 :
15634 316 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15635 316 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15636 :
15637 316 : PQclear(res);
15638 :
15639 316 : resetPQExpBuffer(query);
15640 316 : appendPQExpBuffer(query,
15641 : "SELECT\n"
15642 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15643 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15644 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15645 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15646 : "WHERE m.mapcfg = '%u'\n"
15647 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15648 316 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15649 :
15650 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15651 316 : ntups = PQntuples(res);
15652 :
15653 316 : i_tokenname = PQfnumber(res, "tokenname");
15654 316 : i_dictname = PQfnumber(res, "dictname");
15655 :
15656 6752 : for (i = 0; i < ntups; i++)
15657 : {
15658 6436 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15659 6436 : char *dictname = PQgetvalue(res, i, i_dictname);
15660 :
15661 6436 : if (i == 0 ||
15662 6120 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15663 : {
15664 : /* starting a new token type, so start a new command */
15665 6004 : if (i > 0)
15666 5688 : appendPQExpBufferStr(q, ";\n");
15667 6004 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15668 6004 : fmtQualifiedDumpable(cfginfo));
15669 : /* tokenname needs quoting, dictname does NOT */
15670 6004 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15671 : fmtId(tokenname), dictname);
15672 : }
15673 : else
15674 432 : appendPQExpBuffer(q, ", %s", dictname);
15675 : }
15676 :
15677 316 : if (ntups > 0)
15678 316 : appendPQExpBufferStr(q, ";\n");
15679 :
15680 316 : PQclear(res);
15681 :
15682 316 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15683 316 : fmtQualifiedDumpable(cfginfo));
15684 :
15685 316 : if (dopt->binary_upgrade)
15686 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15687 : "TEXT SEARCH CONFIGURATION", qcfgname,
15688 10 : cfginfo->dobj.namespace->dobj.name);
15689 :
15690 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15691 316 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15692 316 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15693 : .namespace = cfginfo->dobj.namespace->dobj.name,
15694 : .owner = cfginfo->rolname,
15695 : .description = "TEXT SEARCH CONFIGURATION",
15696 : .section = SECTION_PRE_DATA,
15697 : .createStmt = q->data,
15698 : .dropStmt = delq->data));
15699 :
15700 : /* Dump Configuration Comments */
15701 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15702 252 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15703 252 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15704 252 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15705 :
15706 316 : destroyPQExpBuffer(q);
15707 316 : destroyPQExpBuffer(delq);
15708 316 : destroyPQExpBuffer(query);
15709 316 : free(qcfgname);
15710 : }
15711 :
15712 : /*
15713 : * dumpForeignDataWrapper
15714 : * write out a single foreign-data wrapper definition
15715 : */
15716 : static void
15717 118 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15718 : {
15719 118 : DumpOptions *dopt = fout->dopt;
15720 : PQExpBuffer q;
15721 : PQExpBuffer delq;
15722 : char *qfdwname;
15723 :
15724 : /* Do nothing if not dumping schema */
15725 118 : if (!dopt->dumpSchema)
15726 14 : return;
15727 :
15728 104 : q = createPQExpBuffer();
15729 104 : delq = createPQExpBuffer();
15730 :
15731 104 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15732 :
15733 104 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15734 : qfdwname);
15735 :
15736 104 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15737 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15738 :
15739 104 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15740 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15741 :
15742 104 : if (strlen(fdwinfo->fdwoptions) > 0)
15743 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15744 :
15745 104 : appendPQExpBufferStr(q, ";\n");
15746 :
15747 104 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15748 : qfdwname);
15749 :
15750 104 : if (dopt->binary_upgrade)
15751 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15752 : "FOREIGN DATA WRAPPER", qfdwname,
15753 : NULL);
15754 :
15755 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15756 104 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15757 104 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15758 : .owner = fdwinfo->rolname,
15759 : .description = "FOREIGN DATA WRAPPER",
15760 : .section = SECTION_PRE_DATA,
15761 : .createStmt = q->data,
15762 : .dropStmt = delq->data));
15763 :
15764 : /* Dump Foreign Data Wrapper Comments */
15765 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15766 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
15767 0 : NULL, fdwinfo->rolname,
15768 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
15769 :
15770 : /* Handle the ACL */
15771 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
15772 70 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15773 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15774 70 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15775 :
15776 104 : free(qfdwname);
15777 :
15778 104 : destroyPQExpBuffer(q);
15779 104 : destroyPQExpBuffer(delq);
15780 : }
15781 :
15782 : /*
15783 : * dumpForeignServer
15784 : * write out a foreign server definition
15785 : */
15786 : static void
15787 126 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15788 : {
15789 126 : DumpOptions *dopt = fout->dopt;
15790 : PQExpBuffer q;
15791 : PQExpBuffer delq;
15792 : PQExpBuffer query;
15793 : PGresult *res;
15794 : char *qsrvname;
15795 : char *fdwname;
15796 :
15797 : /* Do nothing if not dumping schema */
15798 126 : if (!dopt->dumpSchema)
15799 18 : return;
15800 :
15801 108 : q = createPQExpBuffer();
15802 108 : delq = createPQExpBuffer();
15803 108 : query = createPQExpBuffer();
15804 :
15805 108 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15806 :
15807 : /* look up the foreign-data wrapper */
15808 108 : appendPQExpBuffer(query, "SELECT fdwname "
15809 : "FROM pg_foreign_data_wrapper w "
15810 : "WHERE w.oid = '%u'",
15811 108 : srvinfo->srvfdw);
15812 108 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15813 108 : fdwname = PQgetvalue(res, 0, 0);
15814 :
15815 108 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15816 108 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15817 : {
15818 0 : appendPQExpBufferStr(q, " TYPE ");
15819 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15820 : }
15821 108 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15822 : {
15823 0 : appendPQExpBufferStr(q, " VERSION ");
15824 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15825 : }
15826 :
15827 108 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15828 108 : appendPQExpBufferStr(q, fmtId(fdwname));
15829 :
15830 108 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15831 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15832 :
15833 108 : appendPQExpBufferStr(q, ";\n");
15834 :
15835 108 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15836 : qsrvname);
15837 :
15838 108 : if (dopt->binary_upgrade)
15839 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15840 : "SERVER", qsrvname, NULL);
15841 :
15842 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15843 108 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15844 108 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15845 : .owner = srvinfo->rolname,
15846 : .description = "SERVER",
15847 : .section = SECTION_PRE_DATA,
15848 : .createStmt = q->data,
15849 : .dropStmt = delq->data));
15850 :
15851 : /* Dump Foreign Server Comments */
15852 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15853 0 : dumpComment(fout, "SERVER", qsrvname,
15854 0 : NULL, srvinfo->rolname,
15855 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15856 :
15857 : /* Handle the ACL */
15858 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15859 70 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15860 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15861 70 : NULL, srvinfo->rolname, &srvinfo->dacl);
15862 :
15863 : /* Dump user mappings */
15864 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15865 108 : dumpUserMappings(fout,
15866 108 : srvinfo->dobj.name, NULL,
15867 108 : srvinfo->rolname,
15868 108 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15869 :
15870 108 : PQclear(res);
15871 :
15872 108 : free(qsrvname);
15873 :
15874 108 : destroyPQExpBuffer(q);
15875 108 : destroyPQExpBuffer(delq);
15876 108 : destroyPQExpBuffer(query);
15877 : }
15878 :
15879 : /*
15880 : * dumpUserMappings
15881 : *
15882 : * This routine is used to dump any user mappings associated with the
15883 : * server handed to this routine. Should be called after ArchiveEntry()
15884 : * for the server.
15885 : */
15886 : static void
15887 108 : dumpUserMappings(Archive *fout,
15888 : const char *servername, const char *namespace,
15889 : const char *owner,
15890 : CatalogId catalogId, DumpId dumpId)
15891 : {
15892 : PQExpBuffer q;
15893 : PQExpBuffer delq;
15894 : PQExpBuffer query;
15895 : PQExpBuffer tag;
15896 : PGresult *res;
15897 : int ntups;
15898 : int i_usename;
15899 : int i_umoptions;
15900 : int i;
15901 :
15902 108 : q = createPQExpBuffer();
15903 108 : tag = createPQExpBuffer();
15904 108 : delq = createPQExpBuffer();
15905 108 : query = createPQExpBuffer();
15906 :
15907 : /*
15908 : * We read from the publicly accessible view pg_user_mappings, so as not
15909 : * to fail if run by a non-superuser. Note that the view will show
15910 : * umoptions as null if the user hasn't got privileges for the associated
15911 : * server; this means that pg_dump will dump such a mapping, but with no
15912 : * OPTIONS clause. A possible alternative is to skip such mappings
15913 : * altogether, but it's not clear that that's an improvement.
15914 : */
15915 108 : appendPQExpBuffer(query,
15916 : "SELECT usename, "
15917 : "array_to_string(ARRAY("
15918 : "SELECT quote_ident(option_name) || ' ' || "
15919 : "quote_literal(option_value) "
15920 : "FROM pg_options_to_table(umoptions) "
15921 : "ORDER BY option_name"
15922 : "), E',\n ') AS umoptions "
15923 : "FROM pg_user_mappings "
15924 : "WHERE srvid = '%u' "
15925 : "ORDER BY usename",
15926 : catalogId.oid);
15927 :
15928 108 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15929 :
15930 108 : ntups = PQntuples(res);
15931 108 : i_usename = PQfnumber(res, "usename");
15932 108 : i_umoptions = PQfnumber(res, "umoptions");
15933 :
15934 178 : for (i = 0; i < ntups; i++)
15935 : {
15936 : char *usename;
15937 : char *umoptions;
15938 :
15939 70 : usename = PQgetvalue(res, i, i_usename);
15940 70 : umoptions = PQgetvalue(res, i, i_umoptions);
15941 :
15942 70 : resetPQExpBuffer(q);
15943 70 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15944 70 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15945 :
15946 70 : if (umoptions && strlen(umoptions) > 0)
15947 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15948 :
15949 70 : appendPQExpBufferStr(q, ";\n");
15950 :
15951 70 : resetPQExpBuffer(delq);
15952 70 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15953 70 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15954 :
15955 70 : resetPQExpBuffer(tag);
15956 70 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15957 : usename, servername);
15958 :
15959 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15960 70 : ARCHIVE_OPTS(.tag = tag->data,
15961 : .namespace = namespace,
15962 : .owner = owner,
15963 : .description = "USER MAPPING",
15964 : .section = SECTION_PRE_DATA,
15965 : .createStmt = q->data,
15966 : .dropStmt = delq->data));
15967 : }
15968 :
15969 108 : PQclear(res);
15970 :
15971 108 : destroyPQExpBuffer(query);
15972 108 : destroyPQExpBuffer(delq);
15973 108 : destroyPQExpBuffer(tag);
15974 108 : destroyPQExpBuffer(q);
15975 108 : }
15976 :
15977 : /*
15978 : * Write out default privileges information
15979 : */
15980 : static void
15981 332 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15982 : {
15983 332 : DumpOptions *dopt = fout->dopt;
15984 : PQExpBuffer q;
15985 : PQExpBuffer tag;
15986 : const char *type;
15987 :
15988 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
15989 332 : if (!dopt->dumpSchema || dopt->aclsSkip)
15990 56 : return;
15991 :
15992 276 : q = createPQExpBuffer();
15993 276 : tag = createPQExpBuffer();
15994 :
15995 276 : switch (daclinfo->defaclobjtype)
15996 : {
15997 138 : case DEFACLOBJ_RELATION:
15998 138 : type = "TABLES";
15999 138 : break;
16000 0 : case DEFACLOBJ_SEQUENCE:
16001 0 : type = "SEQUENCES";
16002 0 : break;
16003 138 : case DEFACLOBJ_FUNCTION:
16004 138 : type = "FUNCTIONS";
16005 138 : break;
16006 0 : case DEFACLOBJ_TYPE:
16007 0 : type = "TYPES";
16008 0 : break;
16009 0 : case DEFACLOBJ_NAMESPACE:
16010 0 : type = "SCHEMAS";
16011 0 : break;
16012 0 : case DEFACLOBJ_LARGEOBJECT:
16013 0 : type = "LARGE OBJECTS";
16014 0 : break;
16015 0 : default:
16016 : /* shouldn't get here */
16017 0 : pg_fatal("unrecognized object type in default privileges: %d",
16018 : (int) daclinfo->defaclobjtype);
16019 : type = ""; /* keep compiler quiet */
16020 : }
16021 :
16022 276 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16023 :
16024 : /* build the actual command(s) for this tuple */
16025 276 : if (!buildDefaultACLCommands(type,
16026 276 : daclinfo->dobj.namespace != NULL ?
16027 140 : daclinfo->dobj.namespace->dobj.name : NULL,
16028 276 : daclinfo->dacl.acl,
16029 276 : daclinfo->dacl.acldefault,
16030 276 : daclinfo->defaclrole,
16031 : fout->remoteVersion,
16032 : q))
16033 0 : pg_fatal("could not parse default ACL list (%s)",
16034 : daclinfo->dacl.acl);
16035 :
16036 276 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16037 276 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16038 276 : ARCHIVE_OPTS(.tag = tag->data,
16039 : .namespace = daclinfo->dobj.namespace ?
16040 : daclinfo->dobj.namespace->dobj.name : NULL,
16041 : .owner = daclinfo->defaclrole,
16042 : .description = "DEFAULT ACL",
16043 : .section = SECTION_POST_DATA,
16044 : .createStmt = q->data));
16045 :
16046 276 : destroyPQExpBuffer(tag);
16047 276 : destroyPQExpBuffer(q);
16048 : }
16049 :
16050 : /*----------
16051 : * Write out grant/revoke information
16052 : *
16053 : * 'objDumpId' is the dump ID of the underlying object.
16054 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16055 : * or InvalidDumpId if there is no need for a second dependency.
16056 : * 'type' must be one of
16057 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16058 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16059 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16060 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16061 : * (Currently we assume that subname is only provided for table columns.)
16062 : * 'nspname' is the namespace the object is in (NULL if none).
16063 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16064 : * to use the default for the object type.
16065 : * 'owner' is the owner, NULL if there is no owner (for languages).
16066 : * 'dacl' is the DumpableAcl struct for the object.
16067 : *
16068 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16069 : * no ACL entry was created.
16070 : *----------
16071 : */
16072 : static DumpId
16073 72052 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16074 : const char *type, const char *name, const char *subname,
16075 : const char *nspname, const char *tag, const char *owner,
16076 : const DumpableAcl *dacl)
16077 : {
16078 72052 : DumpId aclDumpId = InvalidDumpId;
16079 72052 : DumpOptions *dopt = fout->dopt;
16080 72052 : const char *acls = dacl->acl;
16081 72052 : const char *acldefault = dacl->acldefault;
16082 72052 : char privtype = dacl->privtype;
16083 72052 : const char *initprivs = dacl->initprivs;
16084 : const char *baseacls;
16085 : PQExpBuffer sql;
16086 :
16087 : /* Do nothing if ACL dump is not enabled */
16088 72052 : if (dopt->aclsSkip)
16089 644 : return InvalidDumpId;
16090 :
16091 : /* --data-only skips ACLs *except* large object ACLs */
16092 71408 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16093 0 : return InvalidDumpId;
16094 :
16095 71408 : sql = createPQExpBuffer();
16096 :
16097 : /*
16098 : * In binary upgrade mode, we don't run an extension's script but instead
16099 : * dump out the objects independently and then recreate them. To preserve
16100 : * any initial privileges which were set on extension objects, we need to
16101 : * compute the set of GRANT and REVOKE commands necessary to get from the
16102 : * default privileges of an object to its initial privileges as recorded
16103 : * in pg_init_privs.
16104 : *
16105 : * At restore time, we apply these commands after having called
16106 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16107 : * copy the results into pg_init_privs. This is how we preserve the
16108 : * contents of that catalog across binary upgrades.
16109 : */
16110 71408 : if (dopt->binary_upgrade && privtype == 'e' &&
16111 26 : initprivs && *initprivs != '\0')
16112 : {
16113 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16114 26 : if (!buildACLCommands(name, subname, nspname, type,
16115 : initprivs, acldefault, owner,
16116 : "", fout->remoteVersion, sql))
16117 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16118 : initprivs, acldefault, name, type);
16119 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16120 : }
16121 :
16122 : /*
16123 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16124 : * actual current ACL, starting from the initprivs if given, else from the
16125 : * object-type-specific default. Also, while buildACLCommands will assume
16126 : * that a NULL/empty acls string means it needn't do anything, what that
16127 : * actually represents is the object-type-specific default; so we need to
16128 : * substitute the acldefault string to get the right results in that case.
16129 : */
16130 71408 : if (initprivs && *initprivs != '\0')
16131 : {
16132 67330 : baseacls = initprivs;
16133 67330 : if (acls == NULL || *acls == '\0')
16134 34 : acls = acldefault;
16135 : }
16136 : else
16137 4078 : baseacls = acldefault;
16138 :
16139 71408 : if (!buildACLCommands(name, subname, nspname, type,
16140 : acls, baseacls, owner,
16141 : "", fout->remoteVersion, sql))
16142 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16143 : acls, baseacls, name, type);
16144 :
16145 71408 : if (sql->len > 0)
16146 : {
16147 4138 : PQExpBuffer tagbuf = createPQExpBuffer();
16148 : DumpId aclDeps[2];
16149 4138 : int nDeps = 0;
16150 :
16151 4138 : if (tag)
16152 0 : appendPQExpBufferStr(tagbuf, tag);
16153 4138 : else if (subname)
16154 2368 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16155 : else
16156 1770 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16157 :
16158 4138 : aclDeps[nDeps++] = objDumpId;
16159 4138 : if (altDumpId != InvalidDumpId)
16160 2174 : aclDeps[nDeps++] = altDumpId;
16161 :
16162 4138 : aclDumpId = createDumpId();
16163 :
16164 4138 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16165 4138 : ARCHIVE_OPTS(.tag = tagbuf->data,
16166 : .namespace = nspname,
16167 : .owner = owner,
16168 : .description = "ACL",
16169 : .section = SECTION_NONE,
16170 : .createStmt = sql->data,
16171 : .deps = aclDeps,
16172 : .nDeps = nDeps));
16173 :
16174 4138 : destroyPQExpBuffer(tagbuf);
16175 : }
16176 :
16177 71408 : destroyPQExpBuffer(sql);
16178 :
16179 71408 : return aclDumpId;
16180 : }
16181 :
16182 : /*
16183 : * dumpSecLabel
16184 : *
16185 : * This routine is used to dump any security labels associated with the
16186 : * object handed to this routine. The routine takes the object type
16187 : * and object name (ready to print, except for schema decoration), plus
16188 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16189 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16190 : * plus the dump ID for the object (for setting a dependency).
16191 : * If a matching pg_seclabel entry is found, it is dumped.
16192 : *
16193 : * Note: although this routine takes a dumpId for dependency purposes,
16194 : * that purpose is just to mark the dependency in the emitted dump file
16195 : * for possible future use by pg_restore. We do NOT use it for determining
16196 : * ordering of the label in the dump file, because this routine is called
16197 : * after dependency sorting occurs. This routine should be called just after
16198 : * calling ArchiveEntry() for the specified object.
16199 : */
16200 : static void
16201 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16202 : const char *namespace, const char *owner,
16203 : CatalogId catalogId, int subid, DumpId dumpId)
16204 : {
16205 0 : DumpOptions *dopt = fout->dopt;
16206 : SecLabelItem *labels;
16207 : int nlabels;
16208 : int i;
16209 : PQExpBuffer query;
16210 :
16211 : /* do nothing, if --no-security-labels is supplied */
16212 0 : if (dopt->no_security_labels)
16213 0 : return;
16214 :
16215 : /*
16216 : * Security labels are schema not data ... except large object labels are
16217 : * data
16218 : */
16219 0 : if (strcmp(type, "LARGE OBJECT") != 0)
16220 : {
16221 0 : if (!dopt->dumpSchema)
16222 0 : return;
16223 : }
16224 : else
16225 : {
16226 : /* We do dump large object security labels in binary-upgrade mode */
16227 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
16228 0 : return;
16229 : }
16230 :
16231 : /* Search for security labels associated with catalogId, using table */
16232 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16233 :
16234 0 : query = createPQExpBuffer();
16235 :
16236 0 : for (i = 0; i < nlabels; i++)
16237 : {
16238 : /*
16239 : * Ignore label entries for which the subid doesn't match.
16240 : */
16241 0 : if (labels[i].objsubid != subid)
16242 0 : continue;
16243 :
16244 0 : appendPQExpBuffer(query,
16245 : "SECURITY LABEL FOR %s ON %s ",
16246 0 : fmtId(labels[i].provider), type);
16247 0 : if (namespace && *namespace)
16248 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16249 0 : appendPQExpBuffer(query, "%s IS ", name);
16250 0 : appendStringLiteralAH(query, labels[i].label, fout);
16251 0 : appendPQExpBufferStr(query, ";\n");
16252 : }
16253 :
16254 0 : if (query->len > 0)
16255 : {
16256 0 : PQExpBuffer tag = createPQExpBuffer();
16257 :
16258 0 : appendPQExpBuffer(tag, "%s %s", type, name);
16259 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16260 0 : ARCHIVE_OPTS(.tag = tag->data,
16261 : .namespace = namespace,
16262 : .owner = owner,
16263 : .description = "SECURITY LABEL",
16264 : .section = SECTION_NONE,
16265 : .createStmt = query->data,
16266 : .deps = &dumpId,
16267 : .nDeps = 1));
16268 0 : destroyPQExpBuffer(tag);
16269 : }
16270 :
16271 0 : destroyPQExpBuffer(query);
16272 : }
16273 :
16274 : /*
16275 : * dumpTableSecLabel
16276 : *
16277 : * As above, but dump security label for both the specified table (or view)
16278 : * and its columns.
16279 : */
16280 : static void
16281 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16282 : {
16283 0 : DumpOptions *dopt = fout->dopt;
16284 : SecLabelItem *labels;
16285 : int nlabels;
16286 : int i;
16287 : PQExpBuffer query;
16288 : PQExpBuffer target;
16289 :
16290 : /* do nothing, if --no-security-labels is supplied */
16291 0 : if (dopt->no_security_labels)
16292 0 : return;
16293 :
16294 : /* SecLabel are SCHEMA not data */
16295 0 : if (!dopt->dumpSchema)
16296 0 : return;
16297 :
16298 : /* Search for comments associated with relation, using table */
16299 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16300 0 : tbinfo->dobj.catId.oid,
16301 : &labels);
16302 :
16303 : /* If security labels exist, build SECURITY LABEL statements */
16304 0 : if (nlabels <= 0)
16305 0 : return;
16306 :
16307 0 : query = createPQExpBuffer();
16308 0 : target = createPQExpBuffer();
16309 :
16310 0 : for (i = 0; i < nlabels; i++)
16311 : {
16312 : const char *colname;
16313 0 : const char *provider = labels[i].provider;
16314 0 : const char *label = labels[i].label;
16315 0 : int objsubid = labels[i].objsubid;
16316 :
16317 0 : resetPQExpBuffer(target);
16318 0 : if (objsubid == 0)
16319 : {
16320 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16321 0 : fmtQualifiedDumpable(tbinfo));
16322 : }
16323 : else
16324 : {
16325 0 : colname = getAttrName(objsubid, tbinfo);
16326 : /* first fmtXXX result must be consumed before calling again */
16327 0 : appendPQExpBuffer(target, "COLUMN %s",
16328 0 : fmtQualifiedDumpable(tbinfo));
16329 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16330 : }
16331 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16332 : fmtId(provider), target->data);
16333 0 : appendStringLiteralAH(query, label, fout);
16334 0 : appendPQExpBufferStr(query, ";\n");
16335 : }
16336 0 : if (query->len > 0)
16337 : {
16338 0 : resetPQExpBuffer(target);
16339 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16340 0 : fmtId(tbinfo->dobj.name));
16341 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16342 0 : ARCHIVE_OPTS(.tag = target->data,
16343 : .namespace = tbinfo->dobj.namespace->dobj.name,
16344 : .owner = tbinfo->rolname,
16345 : .description = "SECURITY LABEL",
16346 : .section = SECTION_NONE,
16347 : .createStmt = query->data,
16348 : .deps = &(tbinfo->dobj.dumpId),
16349 : .nDeps = 1));
16350 : }
16351 0 : destroyPQExpBuffer(query);
16352 0 : destroyPQExpBuffer(target);
16353 : }
16354 :
16355 : /*
16356 : * findSecLabels
16357 : *
16358 : * Find the security label(s), if any, associated with the given object.
16359 : * All the objsubid values associated with the given classoid/objoid are
16360 : * found with one search.
16361 : */
16362 : static int
16363 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16364 : {
16365 0 : SecLabelItem *middle = NULL;
16366 : SecLabelItem *low;
16367 : SecLabelItem *high;
16368 : int nmatch;
16369 :
16370 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16371 : {
16372 0 : *items = NULL;
16373 0 : return 0;
16374 : }
16375 :
16376 : /*
16377 : * Do binary search to find some item matching the object.
16378 : */
16379 0 : low = &seclabels[0];
16380 0 : high = &seclabels[nseclabels - 1];
16381 0 : while (low <= high)
16382 : {
16383 0 : middle = low + (high - low) / 2;
16384 :
16385 0 : if (classoid < middle->classoid)
16386 0 : high = middle - 1;
16387 0 : else if (classoid > middle->classoid)
16388 0 : low = middle + 1;
16389 0 : else if (objoid < middle->objoid)
16390 0 : high = middle - 1;
16391 0 : else if (objoid > middle->objoid)
16392 0 : low = middle + 1;
16393 : else
16394 0 : break; /* found a match */
16395 : }
16396 :
16397 0 : if (low > high) /* no matches */
16398 : {
16399 0 : *items = NULL;
16400 0 : return 0;
16401 : }
16402 :
16403 : /*
16404 : * Now determine how many items match the object. The search loop
16405 : * invariant still holds: only items between low and high inclusive could
16406 : * match.
16407 : */
16408 0 : nmatch = 1;
16409 0 : while (middle > low)
16410 : {
16411 0 : if (classoid != middle[-1].classoid ||
16412 0 : objoid != middle[-1].objoid)
16413 : break;
16414 0 : middle--;
16415 0 : nmatch++;
16416 : }
16417 :
16418 0 : *items = middle;
16419 :
16420 0 : middle += nmatch;
16421 0 : while (middle <= high)
16422 : {
16423 0 : if (classoid != middle->classoid ||
16424 0 : objoid != middle->objoid)
16425 : break;
16426 0 : middle++;
16427 0 : nmatch++;
16428 : }
16429 :
16430 0 : return nmatch;
16431 : }
16432 :
16433 : /*
16434 : * collectSecLabels
16435 : *
16436 : * Construct a table of all security labels available for database objects;
16437 : * also set the has-seclabel component flag for each relevant object.
16438 : *
16439 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16440 : */
16441 : static void
16442 468 : collectSecLabels(Archive *fout)
16443 : {
16444 : PGresult *res;
16445 : PQExpBuffer query;
16446 : int i_label;
16447 : int i_provider;
16448 : int i_classoid;
16449 : int i_objoid;
16450 : int i_objsubid;
16451 : int ntups;
16452 : int i;
16453 : DumpableObject *dobj;
16454 :
16455 468 : query = createPQExpBuffer();
16456 :
16457 468 : appendPQExpBufferStr(query,
16458 : "SELECT label, provider, classoid, objoid, objsubid "
16459 : "FROM pg_catalog.pg_seclabel "
16460 : "ORDER BY classoid, objoid, objsubid");
16461 :
16462 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16463 :
16464 : /* Construct lookup table containing OIDs in numeric form */
16465 468 : i_label = PQfnumber(res, "label");
16466 468 : i_provider = PQfnumber(res, "provider");
16467 468 : i_classoid = PQfnumber(res, "classoid");
16468 468 : i_objoid = PQfnumber(res, "objoid");
16469 468 : i_objsubid = PQfnumber(res, "objsubid");
16470 :
16471 468 : ntups = PQntuples(res);
16472 :
16473 468 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16474 468 : nseclabels = 0;
16475 468 : dobj = NULL;
16476 :
16477 468 : for (i = 0; i < ntups; i++)
16478 : {
16479 : CatalogId objId;
16480 : int subid;
16481 :
16482 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16483 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16484 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16485 :
16486 : /* We needn't remember labels that don't match any dumpable object */
16487 0 : if (dobj == NULL ||
16488 0 : dobj->catId.tableoid != objId.tableoid ||
16489 0 : dobj->catId.oid != objId.oid)
16490 0 : dobj = findObjectByCatalogId(objId);
16491 0 : if (dobj == NULL)
16492 0 : continue;
16493 :
16494 : /*
16495 : * Labels on columns of composite types are linked to the type's
16496 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16497 : * in the type's own DumpableObject.
16498 : */
16499 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16500 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16501 0 : {
16502 : TypeInfo *cTypeInfo;
16503 :
16504 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16505 0 : if (cTypeInfo)
16506 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16507 : }
16508 : else
16509 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16510 :
16511 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16512 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16513 0 : seclabels[nseclabels].classoid = objId.tableoid;
16514 0 : seclabels[nseclabels].objoid = objId.oid;
16515 0 : seclabels[nseclabels].objsubid = subid;
16516 0 : nseclabels++;
16517 : }
16518 :
16519 468 : PQclear(res);
16520 468 : destroyPQExpBuffer(query);
16521 468 : }
16522 :
16523 : /*
16524 : * dumpTable
16525 : * write out to fout the declarations (not data) of a user-defined table
16526 : */
16527 : static void
16528 80656 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16529 : {
16530 80656 : DumpOptions *dopt = fout->dopt;
16531 80656 : DumpId tableAclDumpId = InvalidDumpId;
16532 : char *namecopy;
16533 :
16534 : /* Do nothing if not dumping schema */
16535 80656 : if (!dopt->dumpSchema)
16536 2984 : return;
16537 :
16538 77672 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16539 : {
16540 18614 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16541 1106 : dumpSequence(fout, tbinfo);
16542 : else
16543 17508 : dumpTableSchema(fout, tbinfo);
16544 : }
16545 :
16546 : /* Handle the ACL here */
16547 77672 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16548 77672 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16549 : {
16550 60626 : const char *objtype =
16551 60626 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16552 :
16553 : tableAclDumpId =
16554 60626 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16555 : objtype, namecopy, NULL,
16556 60626 : tbinfo->dobj.namespace->dobj.name,
16557 60626 : NULL, tbinfo->rolname, &tbinfo->dacl);
16558 : }
16559 :
16560 : /*
16561 : * Handle column ACLs, if any. Note: we pull these with a separate query
16562 : * rather than trying to fetch them during getTableAttrs, so that we won't
16563 : * miss ACLs on system columns. Doing it this way also allows us to dump
16564 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16565 : */
16566 77672 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16567 : {
16568 694 : PQExpBuffer query = createPQExpBuffer();
16569 : PGresult *res;
16570 : int i;
16571 :
16572 694 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16573 : {
16574 : /* Set up query for column ACLs */
16575 416 : appendPQExpBufferStr(query,
16576 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16577 :
16578 416 : if (fout->remoteVersion >= 90600)
16579 : {
16580 : /*
16581 : * In principle we should call acldefault('c', relowner) to
16582 : * get the default ACL for a column. However, we don't
16583 : * currently store the numeric OID of the relowner in
16584 : * TableInfo. We could convert the owner name using regrole,
16585 : * but that creates a risk of failure due to concurrent role
16586 : * renames. Given that the default ACL for columns is empty
16587 : * and is likely to stay that way, it's not worth extra cycles
16588 : * and risk to avoid hard-wiring that knowledge here.
16589 : */
16590 416 : appendPQExpBufferStr(query,
16591 : "SELECT at.attname, "
16592 : "at.attacl, "
16593 : "'{}' AS acldefault, "
16594 : "pip.privtype, pip.initprivs "
16595 : "FROM pg_catalog.pg_attribute at "
16596 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16597 : "(at.attrelid = pip.objoid "
16598 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16599 : "AND at.attnum = pip.objsubid) "
16600 : "WHERE at.attrelid = $1 AND "
16601 : "NOT at.attisdropped "
16602 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16603 : "ORDER BY at.attnum");
16604 : }
16605 : else
16606 : {
16607 0 : appendPQExpBufferStr(query,
16608 : "SELECT attname, attacl, '{}' AS acldefault, "
16609 : "NULL AS privtype, NULL AS initprivs "
16610 : "FROM pg_catalog.pg_attribute "
16611 : "WHERE attrelid = $1 AND NOT attisdropped "
16612 : "AND attacl IS NOT NULL "
16613 : "ORDER BY attnum");
16614 : }
16615 :
16616 416 : ExecuteSqlStatement(fout, query->data);
16617 :
16618 416 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16619 : }
16620 :
16621 694 : printfPQExpBuffer(query,
16622 : "EXECUTE getColumnACLs('%u')",
16623 694 : tbinfo->dobj.catId.oid);
16624 :
16625 694 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16626 :
16627 10032 : for (i = 0; i < PQntuples(res); i++)
16628 : {
16629 9338 : char *attname = PQgetvalue(res, i, 0);
16630 9338 : char *attacl = PQgetvalue(res, i, 1);
16631 9338 : char *acldefault = PQgetvalue(res, i, 2);
16632 9338 : char privtype = *(PQgetvalue(res, i, 3));
16633 9338 : char *initprivs = PQgetvalue(res, i, 4);
16634 : DumpableAcl coldacl;
16635 : char *attnamecopy;
16636 :
16637 9338 : coldacl.acl = attacl;
16638 9338 : coldacl.acldefault = acldefault;
16639 9338 : coldacl.privtype = privtype;
16640 9338 : coldacl.initprivs = initprivs;
16641 9338 : attnamecopy = pg_strdup(fmtId(attname));
16642 :
16643 : /*
16644 : * Column's GRANT type is always TABLE. Each column ACL depends
16645 : * on the table-level ACL, since we can restore column ACLs in
16646 : * parallel but the table-level ACL has to be done first.
16647 : */
16648 9338 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16649 : "TABLE", namecopy, attnamecopy,
16650 9338 : tbinfo->dobj.namespace->dobj.name,
16651 9338 : NULL, tbinfo->rolname, &coldacl);
16652 9338 : free(attnamecopy);
16653 : }
16654 694 : PQclear(res);
16655 694 : destroyPQExpBuffer(query);
16656 : }
16657 :
16658 77672 : free(namecopy);
16659 : }
16660 :
16661 : /*
16662 : * Create the AS clause for a view or materialized view. The semicolon is
16663 : * stripped because a materialized view must add a WITH NO DATA clause.
16664 : *
16665 : * This returns a new buffer which must be freed by the caller.
16666 : */
16667 : static PQExpBuffer
16668 2216 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16669 : {
16670 2216 : PQExpBuffer query = createPQExpBuffer();
16671 2216 : PQExpBuffer result = createPQExpBuffer();
16672 : PGresult *res;
16673 : int len;
16674 :
16675 : /* Fetch the view definition */
16676 2216 : appendPQExpBuffer(query,
16677 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16678 2216 : tbinfo->dobj.catId.oid);
16679 :
16680 2216 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16681 :
16682 2216 : if (PQntuples(res) != 1)
16683 : {
16684 0 : if (PQntuples(res) < 1)
16685 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16686 : tbinfo->dobj.name);
16687 : else
16688 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16689 : tbinfo->dobj.name);
16690 : }
16691 :
16692 2216 : len = PQgetlength(res, 0, 0);
16693 :
16694 2216 : if (len == 0)
16695 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16696 : tbinfo->dobj.name);
16697 :
16698 : /* Strip off the trailing semicolon so that other things may follow. */
16699 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16700 2216 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16701 :
16702 2216 : PQclear(res);
16703 2216 : destroyPQExpBuffer(query);
16704 :
16705 2216 : return result;
16706 : }
16707 :
16708 : /*
16709 : * Create a dummy AS clause for a view. This is used when the real view
16710 : * definition has to be postponed because of circular dependencies.
16711 : * We must duplicate the view's external properties -- column names and types
16712 : * (including collation) -- so that it works for subsequent references.
16713 : *
16714 : * This returns a new buffer which must be freed by the caller.
16715 : */
16716 : static PQExpBuffer
16717 64 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16718 : {
16719 64 : PQExpBuffer result = createPQExpBuffer();
16720 : int j;
16721 :
16722 64 : appendPQExpBufferStr(result, "SELECT");
16723 :
16724 128 : for (j = 0; j < tbinfo->numatts; j++)
16725 : {
16726 64 : if (j > 0)
16727 32 : appendPQExpBufferChar(result, ',');
16728 64 : appendPQExpBufferStr(result, "\n ");
16729 :
16730 64 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16731 :
16732 : /*
16733 : * Must add collation if not default for the type, because CREATE OR
16734 : * REPLACE VIEW won't change it
16735 : */
16736 64 : if (OidIsValid(tbinfo->attcollation[j]))
16737 : {
16738 : CollInfo *coll;
16739 :
16740 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16741 0 : if (coll)
16742 0 : appendPQExpBuffer(result, " COLLATE %s",
16743 0 : fmtQualifiedDumpable(coll));
16744 : }
16745 :
16746 64 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16747 : }
16748 :
16749 64 : return result;
16750 : }
16751 :
16752 : /*
16753 : * dumpTableSchema
16754 : * write the declaration (not data) of one user-defined table or view
16755 : */
16756 : static void
16757 17508 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16758 : {
16759 17508 : DumpOptions *dopt = fout->dopt;
16760 17508 : PQExpBuffer q = createPQExpBuffer();
16761 17508 : PQExpBuffer delq = createPQExpBuffer();
16762 17508 : PQExpBuffer extra = createPQExpBuffer();
16763 : char *qrelname;
16764 : char *qualrelname;
16765 : int numParents;
16766 : TableInfo **parents;
16767 : int actual_atts; /* number of attrs in this CREATE statement */
16768 : const char *reltypename;
16769 : char *storage;
16770 : int j,
16771 : k;
16772 :
16773 : /* We had better have loaded per-column details about this table */
16774 : Assert(tbinfo->interesting);
16775 :
16776 17508 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16777 17508 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16778 :
16779 17508 : if (tbinfo->hasoids)
16780 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16781 : qrelname);
16782 :
16783 17508 : if (dopt->binary_upgrade)
16784 1692 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16785 :
16786 : /* Is it a table or a view? */
16787 17508 : if (tbinfo->relkind == RELKIND_VIEW)
16788 : {
16789 : PQExpBuffer result;
16790 :
16791 : /*
16792 : * Note: keep this code in sync with the is_view case in dumpRule()
16793 : */
16794 :
16795 1368 : reltypename = "VIEW";
16796 :
16797 1368 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16798 :
16799 1368 : if (dopt->binary_upgrade)
16800 104 : binary_upgrade_set_pg_class_oids(fout, q,
16801 104 : tbinfo->dobj.catId.oid);
16802 :
16803 1368 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16804 :
16805 1368 : if (tbinfo->dummy_view)
16806 32 : result = createDummyViewAsClause(fout, tbinfo);
16807 : else
16808 : {
16809 1336 : if (nonemptyReloptions(tbinfo->reloptions))
16810 : {
16811 154 : appendPQExpBufferStr(q, " WITH (");
16812 154 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16813 154 : appendPQExpBufferChar(q, ')');
16814 : }
16815 1336 : result = createViewAsClause(fout, tbinfo);
16816 : }
16817 1368 : appendPQExpBuffer(q, " AS\n%s", result->data);
16818 1368 : destroyPQExpBuffer(result);
16819 :
16820 1368 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16821 72 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16822 1368 : appendPQExpBufferStr(q, ";\n");
16823 : }
16824 : else
16825 : {
16826 16140 : char *partkeydef = NULL;
16827 16140 : char *ftoptions = NULL;
16828 16140 : char *srvname = NULL;
16829 16140 : const char *foreign = "";
16830 :
16831 : /*
16832 : * Set reltypename, and collect any relkind-specific data that we
16833 : * didn't fetch during getTables().
16834 : */
16835 16140 : switch (tbinfo->relkind)
16836 : {
16837 1616 : case RELKIND_PARTITIONED_TABLE:
16838 : {
16839 1616 : PQExpBuffer query = createPQExpBuffer();
16840 : PGresult *res;
16841 :
16842 1616 : reltypename = "TABLE";
16843 :
16844 : /* retrieve partition key definition */
16845 1616 : appendPQExpBuffer(query,
16846 : "SELECT pg_get_partkeydef('%u')",
16847 1616 : tbinfo->dobj.catId.oid);
16848 1616 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16849 1616 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16850 1616 : PQclear(res);
16851 1616 : destroyPQExpBuffer(query);
16852 1616 : break;
16853 : }
16854 76 : case RELKIND_FOREIGN_TABLE:
16855 : {
16856 76 : PQExpBuffer query = createPQExpBuffer();
16857 : PGresult *res;
16858 : int i_srvname;
16859 : int i_ftoptions;
16860 :
16861 76 : reltypename = "FOREIGN TABLE";
16862 :
16863 : /* retrieve name of foreign server and generic options */
16864 76 : appendPQExpBuffer(query,
16865 : "SELECT fs.srvname, "
16866 : "pg_catalog.array_to_string(ARRAY("
16867 : "SELECT pg_catalog.quote_ident(option_name) || "
16868 : "' ' || pg_catalog.quote_literal(option_value) "
16869 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16870 : "ORDER BY option_name"
16871 : "), E',\n ') AS ftoptions "
16872 : "FROM pg_catalog.pg_foreign_table ft "
16873 : "JOIN pg_catalog.pg_foreign_server fs "
16874 : "ON (fs.oid = ft.ftserver) "
16875 : "WHERE ft.ftrelid = '%u'",
16876 76 : tbinfo->dobj.catId.oid);
16877 76 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16878 76 : i_srvname = PQfnumber(res, "srvname");
16879 76 : i_ftoptions = PQfnumber(res, "ftoptions");
16880 76 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16881 76 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16882 76 : PQclear(res);
16883 76 : destroyPQExpBuffer(query);
16884 :
16885 76 : foreign = "FOREIGN ";
16886 76 : break;
16887 : }
16888 848 : case RELKIND_MATVIEW:
16889 848 : reltypename = "MATERIALIZED VIEW";
16890 848 : break;
16891 13600 : default:
16892 13600 : reltypename = "TABLE";
16893 13600 : break;
16894 : }
16895 :
16896 16140 : numParents = tbinfo->numParents;
16897 16140 : parents = tbinfo->parents;
16898 :
16899 16140 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16900 :
16901 16140 : if (dopt->binary_upgrade)
16902 1588 : binary_upgrade_set_pg_class_oids(fout, q,
16903 1588 : tbinfo->dobj.catId.oid);
16904 :
16905 : /*
16906 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16907 : * ignore it when dumping if it was set in this case.
16908 : */
16909 16140 : appendPQExpBuffer(q, "CREATE %s%s %s",
16910 16140 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16911 64 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16912 : "UNLOGGED " : "",
16913 : reltypename,
16914 : qualrelname);
16915 :
16916 : /*
16917 : * Attach to type, if reloftype; except in case of a binary upgrade,
16918 : * we dump the table normally and attach it to the type afterward.
16919 : */
16920 16140 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16921 84 : appendPQExpBuffer(q, " OF %s",
16922 84 : getFormattedTypeName(fout, tbinfo->reloftype,
16923 : zeroIsError));
16924 :
16925 16140 : if (tbinfo->relkind != RELKIND_MATVIEW)
16926 : {
16927 : /* Dump the attributes */
16928 15292 : actual_atts = 0;
16929 73290 : for (j = 0; j < tbinfo->numatts; j++)
16930 : {
16931 : /*
16932 : * Normally, dump if it's locally defined in this table, and
16933 : * not dropped. But for binary upgrade, we'll dump all the
16934 : * columns, and then fix up the dropped and nonlocal cases
16935 : * below.
16936 : */
16937 57998 : if (shouldPrintColumn(dopt, tbinfo, j))
16938 : {
16939 : bool print_default;
16940 : bool print_notnull;
16941 :
16942 : /*
16943 : * Default value --- suppress if to be printed separately
16944 : * or not at all.
16945 : */
16946 113080 : print_default = (tbinfo->attrdefs[j] != NULL &&
16947 57884 : tbinfo->attrdefs[j]->dobj.dump &&
16948 2814 : !tbinfo->attrdefs[j]->separate);
16949 :
16950 : /*
16951 : * Not Null constraint --- print it if it is locally
16952 : * defined, or if binary upgrade. (In the latter case, we
16953 : * reset conislocal below.)
16954 : */
16955 61094 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16956 6024 : (tbinfo->notnull_islocal[j] ||
16957 1640 : dopt->binary_upgrade ||
16958 1472 : tbinfo->ispartition));
16959 :
16960 : /*
16961 : * Skip column if fully defined by reloftype, except in
16962 : * binary upgrade
16963 : */
16964 55070 : if (OidIsValid(tbinfo->reloftype) &&
16965 160 : !print_default && !print_notnull &&
16966 96 : !dopt->binary_upgrade)
16967 84 : continue;
16968 :
16969 : /* Format properly if not first attr */
16970 54986 : if (actual_atts == 0)
16971 14434 : appendPQExpBufferStr(q, " (");
16972 : else
16973 40552 : appendPQExpBufferChar(q, ',');
16974 54986 : appendPQExpBufferStr(q, "\n ");
16975 54986 : actual_atts++;
16976 :
16977 : /* Attribute name */
16978 54986 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16979 :
16980 54986 : if (tbinfo->attisdropped[j])
16981 : {
16982 : /*
16983 : * ALTER TABLE DROP COLUMN clears
16984 : * pg_attribute.atttypid, so we will not have gotten a
16985 : * valid type name; insert INTEGER as a stopgap. We'll
16986 : * clean things up later.
16987 : */
16988 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16989 : /* and skip to the next column */
16990 168 : continue;
16991 : }
16992 :
16993 : /*
16994 : * Attribute type; print it except when creating a typed
16995 : * table ('OF type_name'), but in binary-upgrade mode,
16996 : * print it in that case too.
16997 : */
16998 54818 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16999 : {
17000 54762 : appendPQExpBuffer(q, " %s",
17001 54762 : tbinfo->atttypnames[j]);
17002 : }
17003 :
17004 54818 : if (print_default)
17005 : {
17006 2460 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17007 788 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17008 788 : tbinfo->attrdefs[j]->adef_expr);
17009 1672 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17010 660 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17011 660 : tbinfo->attrdefs[j]->adef_expr);
17012 : else
17013 1012 : appendPQExpBuffer(q, " DEFAULT %s",
17014 1012 : tbinfo->attrdefs[j]->adef_expr);
17015 : }
17016 :
17017 54818 : if (print_notnull)
17018 : {
17019 5954 : if (tbinfo->notnull_constrs[j][0] == '\0')
17020 4236 : appendPQExpBufferStr(q, " NOT NULL");
17021 : else
17022 1718 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17023 1718 : fmtId(tbinfo->notnull_constrs[j]));
17024 :
17025 5954 : if (tbinfo->notnull_noinh[j])
17026 0 : appendPQExpBufferStr(q, " NO INHERIT");
17027 : }
17028 :
17029 : /* Add collation if not default for the type */
17030 54818 : if (OidIsValid(tbinfo->attcollation[j]))
17031 : {
17032 : CollInfo *coll;
17033 :
17034 400 : coll = findCollationByOid(tbinfo->attcollation[j]);
17035 400 : if (coll)
17036 400 : appendPQExpBuffer(q, " COLLATE %s",
17037 400 : fmtQualifiedDumpable(coll));
17038 : }
17039 : }
17040 :
17041 : /*
17042 : * On the other hand, if we choose not to print a column
17043 : * (likely because it is created by inheritance), but the
17044 : * column has a locally-defined not-null constraint, we need
17045 : * to dump the constraint as a standalone object.
17046 : *
17047 : * This syntax isn't SQL-conforming, but if you wanted
17048 : * standard output you wouldn't be creating non-standard
17049 : * objects to begin with.
17050 : */
17051 57746 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17052 2928 : !tbinfo->attisdropped[j] &&
17053 1862 : tbinfo->notnull_constrs[j] != NULL &&
17054 490 : tbinfo->notnull_islocal[j])
17055 : {
17056 : /* Format properly if not first attr */
17057 154 : if (actual_atts == 0)
17058 140 : appendPQExpBufferStr(q, " (");
17059 : else
17060 14 : appendPQExpBufferChar(q, ',');
17061 154 : appendPQExpBufferStr(q, "\n ");
17062 154 : actual_atts++;
17063 :
17064 154 : if (tbinfo->notnull_constrs[j][0] == '\0')
17065 14 : appendPQExpBuffer(q, "NOT NULL %s",
17066 14 : fmtId(tbinfo->attnames[j]));
17067 : else
17068 280 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17069 140 : tbinfo->notnull_constrs[j],
17070 140 : fmtId(tbinfo->attnames[j]));
17071 : }
17072 : }
17073 :
17074 : /*
17075 : * Add non-inherited CHECK constraints, if any.
17076 : *
17077 : * For partitions, we need to include check constraints even if
17078 : * they're not defined locally, because the ALTER TABLE ATTACH
17079 : * PARTITION that we'll emit later expects the constraint to be
17080 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17081 : */
17082 16844 : for (j = 0; j < tbinfo->ncheck; j++)
17083 : {
17084 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17085 :
17086 1552 : if (constr->separate ||
17087 1328 : (!constr->conislocal && !tbinfo->ispartition))
17088 312 : continue;
17089 :
17090 1240 : if (actual_atts == 0)
17091 56 : appendPQExpBufferStr(q, " (\n ");
17092 : else
17093 1184 : appendPQExpBufferStr(q, ",\n ");
17094 :
17095 1240 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17096 1240 : fmtId(constr->dobj.name));
17097 1240 : appendPQExpBufferStr(q, constr->condef);
17098 :
17099 1240 : actual_atts++;
17100 : }
17101 :
17102 15292 : if (actual_atts)
17103 14630 : appendPQExpBufferStr(q, "\n)");
17104 662 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17105 : {
17106 : /*
17107 : * No attributes? we must have a parenthesized attribute list,
17108 : * even though empty, when not using the OF TYPE syntax.
17109 : */
17110 620 : appendPQExpBufferStr(q, " (\n)");
17111 : }
17112 :
17113 : /*
17114 : * Emit the INHERITS clause (not for partitions), except in
17115 : * binary-upgrade mode.
17116 : */
17117 15292 : if (numParents > 0 && !tbinfo->ispartition &&
17118 1342 : !dopt->binary_upgrade)
17119 : {
17120 1218 : appendPQExpBufferStr(q, "\nINHERITS (");
17121 2632 : for (k = 0; k < numParents; k++)
17122 : {
17123 1414 : TableInfo *parentRel = parents[k];
17124 :
17125 1414 : if (k > 0)
17126 196 : appendPQExpBufferStr(q, ", ");
17127 1414 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17128 : }
17129 1218 : appendPQExpBufferChar(q, ')');
17130 : }
17131 :
17132 15292 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17133 1616 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17134 :
17135 15292 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17136 76 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17137 : }
17138 :
17139 31856 : if (nonemptyReloptions(tbinfo->reloptions) ||
17140 15716 : nonemptyReloptions(tbinfo->toast_reloptions))
17141 : {
17142 424 : bool addcomma = false;
17143 :
17144 424 : appendPQExpBufferStr(q, "\nWITH (");
17145 424 : if (nonemptyReloptions(tbinfo->reloptions))
17146 : {
17147 424 : addcomma = true;
17148 424 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17149 : }
17150 424 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17151 : {
17152 16 : if (addcomma)
17153 16 : appendPQExpBufferStr(q, ", ");
17154 16 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17155 : fout);
17156 : }
17157 424 : appendPQExpBufferChar(q, ')');
17158 : }
17159 :
17160 : /* Dump generic options if any */
17161 16140 : if (ftoptions && ftoptions[0])
17162 72 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17163 :
17164 : /*
17165 : * For materialized views, create the AS clause just like a view. At
17166 : * this point, we always mark the view as not populated.
17167 : */
17168 16140 : if (tbinfo->relkind == RELKIND_MATVIEW)
17169 : {
17170 : PQExpBuffer result;
17171 :
17172 848 : result = createViewAsClause(fout, tbinfo);
17173 848 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17174 : result->data);
17175 848 : destroyPQExpBuffer(result);
17176 : }
17177 : else
17178 15292 : appendPQExpBufferStr(q, ";\n");
17179 :
17180 : /* Materialized views can depend on extensions */
17181 16140 : if (tbinfo->relkind == RELKIND_MATVIEW)
17182 848 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17183 : "pg_catalog.pg_class",
17184 : "MATERIALIZED VIEW",
17185 : qualrelname);
17186 :
17187 : /*
17188 : * in binary upgrade mode, update the catalog with any missing values
17189 : * that might be present.
17190 : */
17191 16140 : if (dopt->binary_upgrade)
17192 : {
17193 7808 : for (j = 0; j < tbinfo->numatts; j++)
17194 : {
17195 6220 : if (tbinfo->attmissingval[j][0] != '\0')
17196 : {
17197 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17198 4 : appendPQExpBufferStr(q,
17199 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17200 4 : appendStringLiteralAH(q, qualrelname, fout);
17201 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17202 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17203 4 : appendPQExpBufferChar(q, ',');
17204 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17205 4 : appendPQExpBufferStr(q, ");\n\n");
17206 : }
17207 : }
17208 : }
17209 :
17210 : /*
17211 : * To create binary-compatible heap files, we have to ensure the same
17212 : * physical column order, including dropped columns, as in the
17213 : * original. Therefore, we create dropped columns above and drop them
17214 : * here, also updating their attlen/attalign values so that the
17215 : * dropped column can be skipped properly. (We do not bother with
17216 : * restoring the original attbyval setting.) Also, inheritance
17217 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17218 : * using an INHERITS clause --- the latter would possibly mess up the
17219 : * column order. That also means we have to take care about setting
17220 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17221 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17222 : *
17223 : * We process foreign and partitioned tables here, even though they
17224 : * lack heap storage, because they can participate in inheritance
17225 : * relationships and we want this stuff to be consistent across the
17226 : * inheritance tree. We can exclude indexes, toast tables, sequences
17227 : * and matviews, even though they have storage, because we don't
17228 : * support altering or dropping columns in them, nor can they be part
17229 : * of inheritance trees.
17230 : */
17231 16140 : if (dopt->binary_upgrade &&
17232 1588 : (tbinfo->relkind == RELKIND_RELATION ||
17233 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17234 216 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17235 : {
17236 : bool firstitem;
17237 : bool firstitem_extra;
17238 :
17239 : /*
17240 : * Drop any dropped columns. Merge the pg_attribute manipulations
17241 : * into a single SQL command, so that we don't cause repeated
17242 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17243 : * relcache bloat while dropping N columns.
17244 : */
17245 1552 : resetPQExpBuffer(extra);
17246 1552 : firstitem = true;
17247 7728 : for (j = 0; j < tbinfo->numatts; j++)
17248 : {
17249 6176 : if (tbinfo->attisdropped[j])
17250 : {
17251 168 : if (firstitem)
17252 : {
17253 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17254 : "UPDATE pg_catalog.pg_attribute\n"
17255 : "SET attlen = v.dlen, "
17256 : "attalign = v.dalign, "
17257 : "attbyval = false\n"
17258 : "FROM (VALUES ");
17259 76 : firstitem = false;
17260 : }
17261 : else
17262 92 : appendPQExpBufferStr(q, ",\n ");
17263 168 : appendPQExpBufferChar(q, '(');
17264 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17265 168 : appendPQExpBuffer(q, ", %d, '%c')",
17266 168 : tbinfo->attlen[j],
17267 168 : tbinfo->attalign[j]);
17268 : /* The ALTER ... DROP COLUMN commands must come after */
17269 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17270 : foreign, qualrelname);
17271 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17272 168 : fmtId(tbinfo->attnames[j]));
17273 : }
17274 : }
17275 1552 : if (!firstitem)
17276 : {
17277 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17278 : "WHERE attrelid = ");
17279 76 : appendStringLiteralAH(q, qualrelname, fout);
17280 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17281 : " AND attname = v.dname;\n");
17282 : /* Now we can issue the actual DROP COLUMN commands */
17283 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17284 : }
17285 :
17286 : /*
17287 : * Fix up inherited columns. As above, do the pg_attribute
17288 : * manipulations in a single SQL command.
17289 : */
17290 1552 : firstitem = true;
17291 7728 : for (j = 0; j < tbinfo->numatts; j++)
17292 : {
17293 6176 : if (!tbinfo->attisdropped[j] &&
17294 6008 : !tbinfo->attislocal[j])
17295 : {
17296 1200 : if (firstitem)
17297 : {
17298 526 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17299 526 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17300 : "SET attislocal = false\n"
17301 : "WHERE attrelid = ");
17302 526 : appendStringLiteralAH(q, qualrelname, fout);
17303 526 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17304 : " AND attname IN (");
17305 526 : firstitem = false;
17306 : }
17307 : else
17308 674 : appendPQExpBufferStr(q, ", ");
17309 1200 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17310 : }
17311 : }
17312 1552 : if (!firstitem)
17313 526 : appendPQExpBufferStr(q, ");\n");
17314 :
17315 : /*
17316 : * Fix up not-null constraints that come from inheritance. As
17317 : * above, do the pg_constraint manipulations in a single SQL
17318 : * command. (Actually, two in special cases, if we're doing an
17319 : * upgrade from < 18).
17320 : */
17321 1552 : firstitem = true;
17322 1552 : firstitem_extra = true;
17323 1552 : resetPQExpBuffer(extra);
17324 7728 : for (j = 0; j < tbinfo->numatts; j++)
17325 : {
17326 : /*
17327 : * If a not-null constraint comes from inheritance, reset
17328 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17329 : * below. Special hack: in versions < 18, columns with no
17330 : * local definition need their constraint to be matched by
17331 : * column number in conkeys instead of by constraint name,
17332 : * because the latter is not available. (We distinguish the
17333 : * case because the constraint name is the empty string.)
17334 : */
17335 6176 : if (tbinfo->notnull_constrs[j] != NULL &&
17336 576 : !tbinfo->notnull_islocal[j])
17337 : {
17338 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17339 : {
17340 142 : if (firstitem)
17341 : {
17342 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17343 : "SET conislocal = false\n"
17344 : "WHERE contype = 'n' AND conrelid = ");
17345 122 : appendStringLiteralAH(q, qualrelname, fout);
17346 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17347 : "conname IN (");
17348 122 : firstitem = false;
17349 : }
17350 : else
17351 20 : appendPQExpBufferStr(q, ", ");
17352 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17353 : }
17354 : else
17355 : {
17356 26 : if (firstitem_extra)
17357 : {
17358 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17359 : "SET conislocal = false\n"
17360 : "WHERE contype = 'n' AND conrelid = ");
17361 26 : appendStringLiteralAH(extra, qualrelname, fout);
17362 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17363 : "conkey IN (");
17364 26 : firstitem_extra = false;
17365 : }
17366 : else
17367 0 : appendPQExpBufferStr(extra, ", ");
17368 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17369 : }
17370 : }
17371 : }
17372 1552 : if (!firstitem)
17373 122 : appendPQExpBufferStr(q, ");\n");
17374 1552 : if (!firstitem_extra)
17375 26 : appendPQExpBufferStr(extra, ");\n");
17376 :
17377 1552 : if (extra->len > 0)
17378 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17379 :
17380 : /*
17381 : * Add inherited CHECK constraints, if any.
17382 : *
17383 : * For partitions, they were already dumped, and conislocal
17384 : * doesn't need fixing.
17385 : *
17386 : * As above, issue only one direct manipulation of pg_constraint.
17387 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17388 : * commands into one as well, refrain for now due to concern about
17389 : * possible backend memory bloat if there are many such
17390 : * constraints.
17391 : */
17392 1552 : resetPQExpBuffer(extra);
17393 1552 : firstitem = true;
17394 1680 : for (k = 0; k < tbinfo->ncheck; k++)
17395 : {
17396 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17397 :
17398 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17399 124 : continue;
17400 :
17401 4 : if (firstitem)
17402 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17403 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17404 : foreign, qualrelname,
17405 4 : fmtId(constr->dobj.name),
17406 : constr->condef);
17407 : /* Update pg_constraint after all the ALTER TABLEs */
17408 4 : if (firstitem)
17409 : {
17410 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17411 : "SET conislocal = false\n"
17412 : "WHERE contype = 'c' AND conrelid = ");
17413 4 : appendStringLiteralAH(extra, qualrelname, fout);
17414 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17415 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17416 4 : firstitem = false;
17417 : }
17418 : else
17419 0 : appendPQExpBufferStr(extra, ", ");
17420 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17421 : }
17422 1552 : if (!firstitem)
17423 : {
17424 4 : appendPQExpBufferStr(extra, ");\n");
17425 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17426 : }
17427 :
17428 1552 : if (numParents > 0 && !tbinfo->ispartition)
17429 : {
17430 124 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17431 268 : for (k = 0; k < numParents; k++)
17432 : {
17433 144 : TableInfo *parentRel = parents[k];
17434 :
17435 144 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17436 : qualrelname,
17437 144 : fmtQualifiedDumpable(parentRel));
17438 : }
17439 : }
17440 :
17441 1552 : if (OidIsValid(tbinfo->reloftype))
17442 : {
17443 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17444 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17445 : qualrelname,
17446 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17447 : zeroIsError));
17448 : }
17449 : }
17450 :
17451 : /*
17452 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17453 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17454 : * TOAST tables semi-independently, here we see them only as children
17455 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17456 : * child toast table is handled below.)
17457 : */
17458 16140 : if (dopt->binary_upgrade &&
17459 1588 : (tbinfo->relkind == RELKIND_RELATION ||
17460 218 : tbinfo->relkind == RELKIND_MATVIEW))
17461 : {
17462 1406 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17463 1406 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17464 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17465 : "WHERE oid = ",
17466 1406 : tbinfo->frozenxid, tbinfo->minmxid);
17467 1406 : appendStringLiteralAH(q, qualrelname, fout);
17468 1406 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17469 :
17470 1406 : if (tbinfo->toast_oid)
17471 : {
17472 : /*
17473 : * The toast table will have the same OID at restore, so we
17474 : * can safely target it by OID.
17475 : */
17476 552 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17477 552 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17478 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17479 : "WHERE oid = '%u';\n",
17480 552 : tbinfo->toast_frozenxid,
17481 552 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17482 : }
17483 : }
17484 :
17485 : /*
17486 : * In binary_upgrade mode, restore matviews' populated status by
17487 : * poking pg_class directly. This is pretty ugly, but we can't use
17488 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17489 : * matview is not populated even though this matview is; in any case,
17490 : * we want to transfer the matview's heap storage, not run REFRESH.
17491 : */
17492 16140 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17493 36 : tbinfo->relispopulated)
17494 : {
17495 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17496 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17497 : "SET relispopulated = 't'\n"
17498 : "WHERE oid = ");
17499 32 : appendStringLiteralAH(q, qualrelname, fout);
17500 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17501 : }
17502 :
17503 : /*
17504 : * Dump additional per-column properties that we can't handle in the
17505 : * main CREATE TABLE command.
17506 : */
17507 75162 : for (j = 0; j < tbinfo->numatts; j++)
17508 : {
17509 : /* None of this applies to dropped columns */
17510 59022 : if (tbinfo->attisdropped[j])
17511 1234 : continue;
17512 :
17513 : /*
17514 : * Dump per-column statistics information. We only issue an ALTER
17515 : * TABLE statement if the attstattarget entry for this column is
17516 : * not the default value.
17517 : */
17518 57788 : if (tbinfo->attstattarget[j] >= 0)
17519 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17520 : foreign, qualrelname,
17521 72 : fmtId(tbinfo->attnames[j]),
17522 72 : tbinfo->attstattarget[j]);
17523 :
17524 : /*
17525 : * Dump per-column storage information. The statement is only
17526 : * dumped if the storage has been changed from the type's default.
17527 : */
17528 57788 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17529 : {
17530 192 : switch (tbinfo->attstorage[j])
17531 : {
17532 32 : case TYPSTORAGE_PLAIN:
17533 32 : storage = "PLAIN";
17534 32 : break;
17535 88 : case TYPSTORAGE_EXTERNAL:
17536 88 : storage = "EXTERNAL";
17537 88 : break;
17538 0 : case TYPSTORAGE_EXTENDED:
17539 0 : storage = "EXTENDED";
17540 0 : break;
17541 72 : case TYPSTORAGE_MAIN:
17542 72 : storage = "MAIN";
17543 72 : break;
17544 0 : default:
17545 0 : storage = NULL;
17546 : }
17547 :
17548 : /*
17549 : * Only dump the statement if it's a storage type we recognize
17550 : */
17551 192 : if (storage != NULL)
17552 192 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17553 : foreign, qualrelname,
17554 192 : fmtId(tbinfo->attnames[j]),
17555 : storage);
17556 : }
17557 :
17558 : /*
17559 : * Dump per-column compression, if it's been set.
17560 : */
17561 57788 : if (!dopt->no_toast_compression)
17562 : {
17563 : const char *cmname;
17564 :
17565 57598 : switch (tbinfo->attcompression[j])
17566 : {
17567 150 : case 'p':
17568 150 : cmname = "pglz";
17569 150 : break;
17570 236 : case 'l':
17571 236 : cmname = "lz4";
17572 236 : break;
17573 57212 : default:
17574 57212 : cmname = NULL;
17575 57212 : break;
17576 : }
17577 :
17578 57598 : if (cmname != NULL)
17579 386 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17580 : foreign, qualrelname,
17581 386 : fmtId(tbinfo->attnames[j]),
17582 : cmname);
17583 : }
17584 :
17585 : /*
17586 : * Dump per-column attributes.
17587 : */
17588 57788 : if (tbinfo->attoptions[j][0] != '\0')
17589 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17590 : foreign, qualrelname,
17591 72 : fmtId(tbinfo->attnames[j]),
17592 72 : tbinfo->attoptions[j]);
17593 :
17594 : /*
17595 : * Dump per-column fdw options.
17596 : */
17597 57788 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17598 76 : tbinfo->attfdwoptions[j][0] != '\0')
17599 72 : appendPQExpBuffer(q,
17600 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17601 : " %s\n"
17602 : ");\n",
17603 : qualrelname,
17604 72 : fmtId(tbinfo->attnames[j]),
17605 72 : tbinfo->attfdwoptions[j]);
17606 : } /* end loop over columns */
17607 :
17608 16140 : free(partkeydef);
17609 16140 : free(ftoptions);
17610 16140 : free(srvname);
17611 : }
17612 :
17613 : /*
17614 : * dump properties we only have ALTER TABLE syntax for
17615 : */
17616 17508 : if ((tbinfo->relkind == RELKIND_RELATION ||
17617 3908 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17618 2292 : tbinfo->relkind == RELKIND_MATVIEW) &&
17619 16064 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17620 : {
17621 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17622 : {
17623 : /* nothing to do, will be set when the index is dumped */
17624 : }
17625 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17626 : {
17627 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17628 : qualrelname);
17629 : }
17630 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17631 : {
17632 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17633 : qualrelname);
17634 : }
17635 : }
17636 :
17637 17508 : if (tbinfo->forcerowsec)
17638 16 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17639 : qualrelname);
17640 :
17641 17508 : if (dopt->binary_upgrade)
17642 1692 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17643 : reltypename, qrelname,
17644 1692 : tbinfo->dobj.namespace->dobj.name);
17645 :
17646 17508 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17647 : {
17648 17508 : char *tablespace = NULL;
17649 17508 : char *tableam = NULL;
17650 :
17651 : /*
17652 : * _selectTablespace() relies on tablespace-enabled objects in the
17653 : * default tablespace to have a tablespace of "" (empty string) versus
17654 : * non-tablespace-enabled objects to have a tablespace of NULL.
17655 : * getTables() sets tbinfo->reltablespace to "" for the default
17656 : * tablespace (not NULL).
17657 : */
17658 17508 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17659 16064 : tablespace = tbinfo->reltablespace;
17660 :
17661 17508 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17662 3060 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17663 16064 : tableam = tbinfo->amname;
17664 :
17665 17508 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17666 17508 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17667 : .namespace = tbinfo->dobj.namespace->dobj.name,
17668 : .tablespace = tablespace,
17669 : .tableam = tableam,
17670 : .relkind = tbinfo->relkind,
17671 : .owner = tbinfo->rolname,
17672 : .description = reltypename,
17673 : .section = tbinfo->postponed_def ?
17674 : SECTION_POST_DATA : SECTION_PRE_DATA,
17675 : .createStmt = q->data,
17676 : .dropStmt = delq->data));
17677 : }
17678 :
17679 : /* Dump Table Comments */
17680 17508 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17681 176 : dumpTableComment(fout, tbinfo, reltypename);
17682 :
17683 : /* Dump Table Security Labels */
17684 17508 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17685 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17686 :
17687 : /* Dump comments on inlined table constraints */
17688 19060 : for (j = 0; j < tbinfo->ncheck; j++)
17689 : {
17690 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17691 :
17692 1552 : if (constr->separate || !constr->conislocal)
17693 624 : continue;
17694 :
17695 928 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17696 88 : dumpTableConstraintComment(fout, constr);
17697 : }
17698 :
17699 17508 : destroyPQExpBuffer(q);
17700 17508 : destroyPQExpBuffer(delq);
17701 17508 : destroyPQExpBuffer(extra);
17702 17508 : free(qrelname);
17703 17508 : free(qualrelname);
17704 17508 : }
17705 :
17706 : /*
17707 : * dumpTableAttach
17708 : * write to fout the commands to attach a child partition
17709 : *
17710 : * Child partitions are always made by creating them separately
17711 : * and then using ATTACH PARTITION, rather than using
17712 : * CREATE TABLE ... PARTITION OF. This is important for preserving
17713 : * any possible discrepancy in column layout, to allow assigning the
17714 : * correct tablespace if different, and so that it's possible to restore
17715 : * a partition without restoring its parent. (You'll get an error from
17716 : * the ATTACH PARTITION command, but that can be ignored, or skipped
17717 : * using "pg_restore -L" if you prefer.) The last point motivates
17718 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
17719 : * rather than emitting it within the child partition's ArchiveEntry.
17720 : */
17721 : static void
17722 3900 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
17723 : {
17724 3900 : DumpOptions *dopt = fout->dopt;
17725 : PQExpBuffer q;
17726 : PGresult *res;
17727 : char *partbound;
17728 :
17729 : /* Do nothing if not dumping schema */
17730 3900 : if (!dopt->dumpSchema)
17731 84 : return;
17732 :
17733 3816 : q = createPQExpBuffer();
17734 :
17735 3816 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
17736 : {
17737 : /* Set up query for partbound details */
17738 100 : appendPQExpBufferStr(q,
17739 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
17740 :
17741 100 : appendPQExpBufferStr(q,
17742 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
17743 : "FROM pg_class c "
17744 : "WHERE c.oid = $1");
17745 :
17746 100 : ExecuteSqlStatement(fout, q->data);
17747 :
17748 100 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
17749 : }
17750 :
17751 3816 : printfPQExpBuffer(q,
17752 : "EXECUTE dumpTableAttach('%u')",
17753 3816 : attachinfo->partitionTbl->dobj.catId.oid);
17754 :
17755 3816 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
17756 3816 : partbound = PQgetvalue(res, 0, 0);
17757 :
17758 : /* Perform ALTER TABLE on the parent */
17759 3816 : printfPQExpBuffer(q,
17760 : "ALTER TABLE ONLY %s ",
17761 3816 : fmtQualifiedDumpable(attachinfo->parentTbl));
17762 3816 : appendPQExpBuffer(q,
17763 : "ATTACH PARTITION %s %s;\n",
17764 3816 : fmtQualifiedDumpable(attachinfo->partitionTbl),
17765 : partbound);
17766 :
17767 : /*
17768 : * There is no point in creating a drop query as the drop is done by table
17769 : * drop. (If you think to change this, see also _printTocEntry().)
17770 : * Although this object doesn't really have ownership as such, set the
17771 : * owner field anyway to ensure that the command is run by the correct
17772 : * role at restore time.
17773 : */
17774 3816 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17775 3816 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17776 : .namespace = attachinfo->dobj.namespace->dobj.name,
17777 : .owner = attachinfo->partitionTbl->rolname,
17778 : .description = "TABLE ATTACH",
17779 : .section = SECTION_PRE_DATA,
17780 : .createStmt = q->data));
17781 :
17782 3816 : PQclear(res);
17783 3816 : destroyPQExpBuffer(q);
17784 : }
17785 :
17786 : /*
17787 : * dumpAttrDef --- dump an attribute's default-value declaration
17788 : */
17789 : static void
17790 2900 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
17791 : {
17792 2900 : DumpOptions *dopt = fout->dopt;
17793 2900 : TableInfo *tbinfo = adinfo->adtable;
17794 2900 : int adnum = adinfo->adnum;
17795 : PQExpBuffer q;
17796 : PQExpBuffer delq;
17797 : char *qualrelname;
17798 : char *tag;
17799 : char *foreign;
17800 :
17801 : /* Do nothing if not dumping schema */
17802 2900 : if (!dopt->dumpSchema)
17803 0 : return;
17804 :
17805 : /* Skip if not "separate"; it was dumped in the table's definition */
17806 2900 : if (!adinfo->separate)
17807 2460 : return;
17808 :
17809 440 : q = createPQExpBuffer();
17810 440 : delq = createPQExpBuffer();
17811 :
17812 440 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17813 :
17814 440 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17815 :
17816 440 : appendPQExpBuffer(q,
17817 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17818 440 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17819 440 : adinfo->adef_expr);
17820 :
17821 440 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17822 : foreign, qualrelname,
17823 440 : fmtId(tbinfo->attnames[adnum - 1]));
17824 :
17825 440 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17826 :
17827 440 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17828 440 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17829 440 : ARCHIVE_OPTS(.tag = tag,
17830 : .namespace = tbinfo->dobj.namespace->dobj.name,
17831 : .owner = tbinfo->rolname,
17832 : .description = "DEFAULT",
17833 : .section = SECTION_PRE_DATA,
17834 : .createStmt = q->data,
17835 : .dropStmt = delq->data));
17836 :
17837 440 : free(tag);
17838 440 : destroyPQExpBuffer(q);
17839 440 : destroyPQExpBuffer(delq);
17840 440 : free(qualrelname);
17841 : }
17842 :
17843 : /*
17844 : * getAttrName: extract the correct name for an attribute
17845 : *
17846 : * The array tblInfo->attnames[] only provides names of user attributes;
17847 : * if a system attribute number is supplied, we have to fake it.
17848 : * We also do a little bit of bounds checking for safety's sake.
17849 : */
17850 : static const char *
17851 5188 : getAttrName(int attrnum, const TableInfo *tblInfo)
17852 : {
17853 5188 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
17854 5188 : return tblInfo->attnames[attrnum - 1];
17855 0 : switch (attrnum)
17856 : {
17857 0 : case SelfItemPointerAttributeNumber:
17858 0 : return "ctid";
17859 0 : case MinTransactionIdAttributeNumber:
17860 0 : return "xmin";
17861 0 : case MinCommandIdAttributeNumber:
17862 0 : return "cmin";
17863 0 : case MaxTransactionIdAttributeNumber:
17864 0 : return "xmax";
17865 0 : case MaxCommandIdAttributeNumber:
17866 0 : return "cmax";
17867 0 : case TableOidAttributeNumber:
17868 0 : return "tableoid";
17869 : }
17870 0 : pg_fatal("invalid column number %d for table \"%s\"",
17871 : attrnum, tblInfo->dobj.name);
17872 : return NULL; /* keep compiler quiet */
17873 : }
17874 :
17875 : /*
17876 : * dumpIndex
17877 : * write out to fout a user-defined index
17878 : */
17879 : static void
17880 6938 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17881 : {
17882 6938 : DumpOptions *dopt = fout->dopt;
17883 6938 : TableInfo *tbinfo = indxinfo->indextable;
17884 6938 : bool is_constraint = (indxinfo->indexconstraint != 0);
17885 : PQExpBuffer q;
17886 : PQExpBuffer delq;
17887 : char *qindxname;
17888 : char *qqindxname;
17889 :
17890 : /* Do nothing if not dumping schema */
17891 6938 : if (!dopt->dumpSchema)
17892 234 : return;
17893 :
17894 6704 : q = createPQExpBuffer();
17895 6704 : delq = createPQExpBuffer();
17896 :
17897 6704 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17898 6704 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17899 :
17900 : /*
17901 : * If there's an associated constraint, don't dump the index per se, but
17902 : * do dump any comment for it. (This is safe because dependency ordering
17903 : * will have ensured the constraint is emitted first.) Note that the
17904 : * emitted comment has to be shown as depending on the constraint, not the
17905 : * index, in such cases.
17906 : */
17907 6704 : if (!is_constraint)
17908 : {
17909 2998 : char *indstatcols = indxinfo->indstatcols;
17910 2998 : char *indstatvals = indxinfo->indstatvals;
17911 2998 : char **indstatcolsarray = NULL;
17912 2998 : char **indstatvalsarray = NULL;
17913 2998 : int nstatcols = 0;
17914 2998 : int nstatvals = 0;
17915 :
17916 2998 : if (dopt->binary_upgrade)
17917 310 : binary_upgrade_set_pg_class_oids(fout, q,
17918 310 : indxinfo->dobj.catId.oid);
17919 :
17920 : /* Plain secondary index */
17921 2998 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17922 :
17923 : /*
17924 : * Append ALTER TABLE commands as needed to set properties that we
17925 : * only have ALTER TABLE syntax for. Keep this in sync with the
17926 : * similar code in dumpConstraint!
17927 : */
17928 :
17929 : /* If the index is clustered, we need to record that. */
17930 2998 : if (indxinfo->indisclustered)
17931 : {
17932 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17933 0 : fmtQualifiedDumpable(tbinfo));
17934 : /* index name is not qualified in this syntax */
17935 0 : appendPQExpBuffer(q, " ON %s;\n",
17936 : qindxname);
17937 : }
17938 :
17939 : /*
17940 : * If the index has any statistics on some of its columns, generate
17941 : * the associated ALTER INDEX queries.
17942 : */
17943 2998 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17944 : {
17945 : int j;
17946 :
17947 72 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17948 0 : pg_fatal("could not parse index statistic columns");
17949 72 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17950 0 : pg_fatal("could not parse index statistic values");
17951 72 : if (nstatcols != nstatvals)
17952 0 : pg_fatal("mismatched number of columns and values for index statistics");
17953 :
17954 216 : for (j = 0; j < nstatcols; j++)
17955 : {
17956 144 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17957 :
17958 : /*
17959 : * Note that this is a column number, so no quotes should be
17960 : * used.
17961 : */
17962 144 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
17963 144 : indstatcolsarray[j]);
17964 144 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17965 144 : indstatvalsarray[j]);
17966 : }
17967 : }
17968 :
17969 : /* Indexes can depend on extensions */
17970 2998 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17971 : "pg_catalog.pg_class",
17972 : "INDEX", qqindxname);
17973 :
17974 : /* If the index defines identity, we need to record that. */
17975 2998 : if (indxinfo->indisreplident)
17976 : {
17977 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17978 0 : fmtQualifiedDumpable(tbinfo));
17979 : /* index name is not qualified in this syntax */
17980 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17981 : qindxname);
17982 : }
17983 :
17984 : /*
17985 : * If this index is a member of a partitioned index, the backend will
17986 : * not allow us to drop it separately, so don't try. It will go away
17987 : * automatically when we drop either the index's table or the
17988 : * partitioned index. (If, in a selective restore with --clean, we
17989 : * drop neither of those, then this index will not be dropped either.
17990 : * But that's fine, and even if you think it's not, the backend won't
17991 : * let us do differently.)
17992 : */
17993 2998 : if (indxinfo->parentidx == 0)
17994 2542 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17995 :
17996 2998 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17997 2998 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17998 2998 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17999 : .namespace = tbinfo->dobj.namespace->dobj.name,
18000 : .tablespace = indxinfo->tablespace,
18001 : .owner = tbinfo->rolname,
18002 : .description = "INDEX",
18003 : .section = SECTION_POST_DATA,
18004 : .createStmt = q->data,
18005 : .dropStmt = delq->data));
18006 :
18007 2998 : free(indstatcolsarray);
18008 2998 : free(indstatvalsarray);
18009 : }
18010 :
18011 : /* Dump Index Comments */
18012 6704 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18013 48 : dumpComment(fout, "INDEX", qindxname,
18014 48 : tbinfo->dobj.namespace->dobj.name,
18015 : tbinfo->rolname,
18016 : indxinfo->dobj.catId, 0,
18017 : is_constraint ? indxinfo->indexconstraint :
18018 : indxinfo->dobj.dumpId);
18019 :
18020 6704 : destroyPQExpBuffer(q);
18021 6704 : destroyPQExpBuffer(delq);
18022 6704 : free(qindxname);
18023 6704 : free(qqindxname);
18024 : }
18025 :
18026 : /*
18027 : * dumpIndexAttach
18028 : * write out to fout a partitioned-index attachment clause
18029 : */
18030 : static void
18031 1480 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18032 : {
18033 : /* Do nothing if not dumping schema */
18034 1480 : if (!fout->dopt->dumpSchema)
18035 96 : return;
18036 :
18037 1384 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18038 : {
18039 1384 : PQExpBuffer q = createPQExpBuffer();
18040 :
18041 1384 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18042 1384 : fmtQualifiedDumpable(attachinfo->parentIdx));
18043 1384 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18044 1384 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18045 :
18046 : /*
18047 : * There is no need for a dropStmt since the drop is done implicitly
18048 : * when we drop either the index's table or the partitioned index.
18049 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18050 : * there's no way to do it anyway. (If you think to change this,
18051 : * consider also what to do with --if-exists.)
18052 : *
18053 : * Although this object doesn't really have ownership as such, set the
18054 : * owner field anyway to ensure that the command is run by the correct
18055 : * role at restore time.
18056 : */
18057 1384 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18058 1384 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18059 : .namespace = attachinfo->dobj.namespace->dobj.name,
18060 : .owner = attachinfo->parentIdx->indextable->rolname,
18061 : .description = "INDEX ATTACH",
18062 : .section = SECTION_POST_DATA,
18063 : .createStmt = q->data));
18064 :
18065 1384 : destroyPQExpBuffer(q);
18066 : }
18067 : }
18068 :
18069 : /*
18070 : * dumpStatisticsExt
18071 : * write out to fout an extended statistics object
18072 : */
18073 : static void
18074 314 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18075 : {
18076 314 : DumpOptions *dopt = fout->dopt;
18077 : PQExpBuffer q;
18078 : PQExpBuffer delq;
18079 : PQExpBuffer query;
18080 : char *qstatsextname;
18081 : PGresult *res;
18082 : char *stxdef;
18083 :
18084 : /* Do nothing if not dumping schema */
18085 314 : if (!dopt->dumpSchema)
18086 36 : return;
18087 :
18088 278 : q = createPQExpBuffer();
18089 278 : delq = createPQExpBuffer();
18090 278 : query = createPQExpBuffer();
18091 :
18092 278 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18093 :
18094 278 : appendPQExpBuffer(query, "SELECT "
18095 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18096 278 : statsextinfo->dobj.catId.oid);
18097 :
18098 278 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18099 :
18100 278 : stxdef = PQgetvalue(res, 0, 0);
18101 :
18102 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18103 278 : appendPQExpBuffer(q, "%s;\n", stxdef);
18104 :
18105 : /*
18106 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18107 : * for this statistics object is not the default value.
18108 : */
18109 278 : if (statsextinfo->stattarget >= 0)
18110 : {
18111 72 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18112 72 : fmtQualifiedDumpable(statsextinfo));
18113 72 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18114 72 : statsextinfo->stattarget);
18115 : }
18116 :
18117 278 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18118 278 : fmtQualifiedDumpable(statsextinfo));
18119 :
18120 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18121 278 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18122 278 : statsextinfo->dobj.dumpId,
18123 278 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18124 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18125 : .owner = statsextinfo->rolname,
18126 : .description = "STATISTICS",
18127 : .section = SECTION_POST_DATA,
18128 : .createStmt = q->data,
18129 : .dropStmt = delq->data));
18130 :
18131 : /* Dump Statistics Comments */
18132 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18133 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18134 0 : statsextinfo->dobj.namespace->dobj.name,
18135 0 : statsextinfo->rolname,
18136 : statsextinfo->dobj.catId, 0,
18137 0 : statsextinfo->dobj.dumpId);
18138 :
18139 278 : PQclear(res);
18140 278 : destroyPQExpBuffer(q);
18141 278 : destroyPQExpBuffer(delq);
18142 278 : destroyPQExpBuffer(query);
18143 278 : free(qstatsextname);
18144 : }
18145 :
18146 : /*
18147 : * dumpConstraint
18148 : * write out to fout a user-defined constraint
18149 : */
18150 : static void
18151 6370 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18152 : {
18153 6370 : DumpOptions *dopt = fout->dopt;
18154 6370 : TableInfo *tbinfo = coninfo->contable;
18155 : PQExpBuffer q;
18156 : PQExpBuffer delq;
18157 6370 : char *tag = NULL;
18158 : char *foreign;
18159 :
18160 : /* Do nothing if not dumping schema */
18161 6370 : if (!dopt->dumpSchema)
18162 184 : return;
18163 :
18164 6186 : q = createPQExpBuffer();
18165 6186 : delq = createPQExpBuffer();
18166 :
18167 12076 : foreign = tbinfo &&
18168 6186 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18169 :
18170 6186 : if (coninfo->contype == 'p' ||
18171 3020 : coninfo->contype == 'u' ||
18172 2512 : coninfo->contype == 'x')
18173 3706 : {
18174 : /* Index-related constraint */
18175 : IndxInfo *indxinfo;
18176 : int k;
18177 :
18178 3706 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18179 :
18180 3706 : if (indxinfo == NULL)
18181 0 : pg_fatal("missing index for constraint \"%s\"",
18182 : coninfo->dobj.name);
18183 :
18184 3706 : if (dopt->binary_upgrade)
18185 288 : binary_upgrade_set_pg_class_oids(fout, q,
18186 : indxinfo->dobj.catId.oid);
18187 :
18188 3706 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18189 3706 : fmtQualifiedDumpable(tbinfo));
18190 3706 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18191 3706 : fmtId(coninfo->dobj.name));
18192 :
18193 3706 : if (coninfo->condef)
18194 : {
18195 : /* pg_get_constraintdef should have provided everything */
18196 32 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18197 : }
18198 : else
18199 : {
18200 3674 : appendPQExpBufferStr(q,
18201 3674 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18202 :
18203 : /*
18204 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18205 : * indexes. Being able to create this was fixed, but we need to
18206 : * make the index distinct in order to be able to restore the
18207 : * dump.
18208 : */
18209 3674 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18210 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18211 3674 : appendPQExpBufferStr(q, " (");
18212 8734 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18213 : {
18214 5060 : int indkey = (int) indxinfo->indkeys[k];
18215 : const char *attname;
18216 :
18217 5060 : if (indkey == InvalidAttrNumber)
18218 0 : break;
18219 5060 : attname = getAttrName(indkey, tbinfo);
18220 :
18221 5060 : appendPQExpBuffer(q, "%s%s",
18222 : (k == 0) ? "" : ", ",
18223 : fmtId(attname));
18224 : }
18225 3674 : if (coninfo->conperiod)
18226 272 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18227 :
18228 3674 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18229 64 : appendPQExpBufferStr(q, ") INCLUDE (");
18230 :
18231 3802 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18232 : {
18233 128 : int indkey = (int) indxinfo->indkeys[k];
18234 : const char *attname;
18235 :
18236 128 : if (indkey == InvalidAttrNumber)
18237 0 : break;
18238 128 : attname = getAttrName(indkey, tbinfo);
18239 :
18240 256 : appendPQExpBuffer(q, "%s%s",
18241 128 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18242 : fmtId(attname));
18243 : }
18244 :
18245 3674 : appendPQExpBufferChar(q, ')');
18246 :
18247 3674 : if (nonemptyReloptions(indxinfo->indreloptions))
18248 : {
18249 0 : appendPQExpBufferStr(q, " WITH (");
18250 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18251 0 : appendPQExpBufferChar(q, ')');
18252 : }
18253 :
18254 3674 : if (coninfo->condeferrable)
18255 : {
18256 80 : appendPQExpBufferStr(q, " DEFERRABLE");
18257 80 : if (coninfo->condeferred)
18258 48 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18259 : }
18260 :
18261 3674 : appendPQExpBufferStr(q, ";\n");
18262 : }
18263 :
18264 : /*
18265 : * Append ALTER TABLE commands as needed to set properties that we
18266 : * only have ALTER TABLE syntax for. Keep this in sync with the
18267 : * similar code in dumpIndex!
18268 : */
18269 :
18270 : /* If the index is clustered, we need to record that. */
18271 3706 : if (indxinfo->indisclustered)
18272 : {
18273 72 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18274 72 : fmtQualifiedDumpable(tbinfo));
18275 : /* index name is not qualified in this syntax */
18276 72 : appendPQExpBuffer(q, " ON %s;\n",
18277 72 : fmtId(indxinfo->dobj.name));
18278 : }
18279 :
18280 : /* If the index defines identity, we need to record that. */
18281 3706 : if (indxinfo->indisreplident)
18282 : {
18283 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18284 0 : fmtQualifiedDumpable(tbinfo));
18285 : /* index name is not qualified in this syntax */
18286 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18287 0 : fmtId(indxinfo->dobj.name));
18288 : }
18289 :
18290 : /* Indexes can depend on extensions */
18291 3706 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18292 : "pg_catalog.pg_class", "INDEX",
18293 3706 : fmtQualifiedDumpable(indxinfo));
18294 :
18295 3706 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18296 3706 : fmtQualifiedDumpable(tbinfo));
18297 3706 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18298 3706 : fmtId(coninfo->dobj.name));
18299 :
18300 3706 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18301 :
18302 3706 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18303 3706 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18304 3706 : ARCHIVE_OPTS(.tag = tag,
18305 : .namespace = tbinfo->dobj.namespace->dobj.name,
18306 : .tablespace = indxinfo->tablespace,
18307 : .owner = tbinfo->rolname,
18308 : .description = "CONSTRAINT",
18309 : .section = SECTION_POST_DATA,
18310 : .createStmt = q->data,
18311 : .dropStmt = delq->data));
18312 : }
18313 2480 : else if (coninfo->contype == 'f')
18314 : {
18315 : char *only;
18316 :
18317 : /*
18318 : * Foreign keys on partitioned tables are always declared as
18319 : * inheriting to partitions; for all other cases, emit them as
18320 : * applying ONLY directly to the named table, because that's how they
18321 : * work for regular inherited tables.
18322 : */
18323 448 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18324 :
18325 : /*
18326 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18327 : * current table data is not processed
18328 : */
18329 448 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18330 448 : only, fmtQualifiedDumpable(tbinfo));
18331 448 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18332 448 : fmtId(coninfo->dobj.name),
18333 448 : coninfo->condef);
18334 :
18335 448 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18336 448 : only, fmtQualifiedDumpable(tbinfo));
18337 448 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18338 448 : fmtId(coninfo->dobj.name));
18339 :
18340 448 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18341 :
18342 448 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18343 448 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18344 448 : ARCHIVE_OPTS(.tag = tag,
18345 : .namespace = tbinfo->dobj.namespace->dobj.name,
18346 : .owner = tbinfo->rolname,
18347 : .description = "FK CONSTRAINT",
18348 : .section = SECTION_POST_DATA,
18349 : .createStmt = q->data,
18350 : .dropStmt = delq->data));
18351 : }
18352 2032 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18353 : {
18354 : /* CHECK or invalid not-null constraint on a table */
18355 :
18356 : /* Ignore if not to be dumped separately, or if it was inherited */
18357 1736 : if (coninfo->separate && coninfo->conislocal)
18358 : {
18359 : const char *keyword;
18360 :
18361 312 : if (coninfo->contype == 'c')
18362 144 : keyword = "CHECK CONSTRAINT";
18363 : else
18364 168 : keyword = "CONSTRAINT";
18365 :
18366 : /* not ONLY since we want it to propagate to children */
18367 312 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18368 312 : fmtQualifiedDumpable(tbinfo));
18369 312 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18370 312 : fmtId(coninfo->dobj.name),
18371 312 : coninfo->condef);
18372 :
18373 312 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18374 312 : fmtQualifiedDumpable(tbinfo));
18375 312 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18376 312 : fmtId(coninfo->dobj.name));
18377 :
18378 312 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18379 :
18380 312 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18381 312 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18382 312 : ARCHIVE_OPTS(.tag = tag,
18383 : .namespace = tbinfo->dobj.namespace->dobj.name,
18384 : .owner = tbinfo->rolname,
18385 : .description = keyword,
18386 : .section = SECTION_POST_DATA,
18387 : .createStmt = q->data,
18388 : .dropStmt = delq->data));
18389 : }
18390 : }
18391 296 : else if (coninfo->contype == 'c' && tbinfo == NULL)
18392 296 : {
18393 : /* CHECK constraint on a domain */
18394 296 : TypeInfo *tyinfo = coninfo->condomain;
18395 :
18396 : /* Ignore if not to be dumped separately */
18397 296 : if (coninfo->separate)
18398 : {
18399 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18400 0 : fmtQualifiedDumpable(tyinfo));
18401 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18402 0 : fmtId(coninfo->dobj.name),
18403 0 : coninfo->condef);
18404 :
18405 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18406 0 : fmtQualifiedDumpable(tyinfo));
18407 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18408 0 : fmtId(coninfo->dobj.name));
18409 :
18410 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18411 :
18412 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18413 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18414 0 : ARCHIVE_OPTS(.tag = tag,
18415 : .namespace = tyinfo->dobj.namespace->dobj.name,
18416 : .owner = tyinfo->rolname,
18417 : .description = "CHECK CONSTRAINT",
18418 : .section = SECTION_POST_DATA,
18419 : .createStmt = q->data,
18420 : .dropStmt = delq->data));
18421 : }
18422 : }
18423 : else
18424 : {
18425 0 : pg_fatal("unrecognized constraint type: %c",
18426 : coninfo->contype);
18427 : }
18428 :
18429 : /* Dump Constraint Comments --- only works for table constraints */
18430 6186 : if (tbinfo && coninfo->separate &&
18431 4562 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18432 32 : dumpTableConstraintComment(fout, coninfo);
18433 :
18434 6186 : free(tag);
18435 6186 : destroyPQExpBuffer(q);
18436 6186 : destroyPQExpBuffer(delq);
18437 : }
18438 :
18439 : /*
18440 : * dumpTableConstraintComment --- dump a constraint's comment if any
18441 : *
18442 : * This is split out because we need the function in two different places
18443 : * depending on whether the constraint is dumped as part of CREATE TABLE
18444 : * or as a separate ALTER command.
18445 : */
18446 : static void
18447 120 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18448 : {
18449 120 : TableInfo *tbinfo = coninfo->contable;
18450 120 : PQExpBuffer conprefix = createPQExpBuffer();
18451 : char *qtabname;
18452 :
18453 120 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18454 :
18455 120 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18456 120 : fmtId(coninfo->dobj.name));
18457 :
18458 120 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18459 120 : dumpComment(fout, conprefix->data, qtabname,
18460 120 : tbinfo->dobj.namespace->dobj.name,
18461 : tbinfo->rolname,
18462 : coninfo->dobj.catId, 0,
18463 120 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18464 :
18465 120 : destroyPQExpBuffer(conprefix);
18466 120 : free(qtabname);
18467 120 : }
18468 :
18469 : static inline SeqType
18470 1632 : parse_sequence_type(const char *name)
18471 : {
18472 3612 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18473 : {
18474 3612 : if (strcmp(SeqTypeNames[i], name) == 0)
18475 1632 : return (SeqType) i;
18476 : }
18477 :
18478 0 : pg_fatal("unrecognized sequence type: %s", name);
18479 : return (SeqType) 0; /* keep compiler quiet */
18480 : }
18481 :
18482 : /*
18483 : * bsearch() comparator for SequenceItem
18484 : */
18485 : static int
18486 9138 : SequenceItemCmp(const void *p1, const void *p2)
18487 : {
18488 9138 : SequenceItem v1 = *((const SequenceItem *) p1);
18489 9138 : SequenceItem v2 = *((const SequenceItem *) p2);
18490 :
18491 9138 : return pg_cmp_u32(v1.oid, v2.oid);
18492 : }
18493 :
18494 : /*
18495 : * collectSequences
18496 : *
18497 : * Construct a table of sequence information. This table is sorted by OID for
18498 : * speed in lookup.
18499 : */
18500 : static void
18501 468 : collectSequences(Archive *fout)
18502 : {
18503 : PGresult *res;
18504 : const char *query;
18505 :
18506 : /*
18507 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18508 : * some extra effort, we might be able to use the sorted table for those
18509 : * versions, but for now it seems unlikely to be worth it.
18510 : *
18511 : * Since version 18, we can gather the sequence data in this query with
18512 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18513 : */
18514 468 : if (fout->remoteVersion < 100000)
18515 0 : return;
18516 468 : else if (fout->remoteVersion < 180000 ||
18517 468 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18518 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18519 : "seqstart, seqincrement, "
18520 : "seqmax, seqmin, "
18521 : "seqcache, seqcycle, "
18522 : "NULL, 'f' "
18523 : "FROM pg_catalog.pg_sequence "
18524 : "ORDER BY seqrelid";
18525 : else
18526 452 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18527 : "seqstart, seqincrement, "
18528 : "seqmax, seqmin, "
18529 : "seqcache, seqcycle, "
18530 : "last_value, is_called "
18531 : "FROM pg_catalog.pg_sequence, "
18532 : "pg_get_sequence_data(seqrelid) "
18533 : "ORDER BY seqrelid;";
18534 :
18535 468 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18536 :
18537 468 : nsequences = PQntuples(res);
18538 468 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18539 :
18540 2100 : for (int i = 0; i < nsequences; i++)
18541 : {
18542 1632 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18543 1632 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18544 1632 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18545 1632 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18546 1632 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18547 1632 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18548 1632 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18549 1632 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18550 1632 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18551 1632 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18552 : }
18553 :
18554 468 : PQclear(res);
18555 : }
18556 :
18557 : /*
18558 : * dumpSequence
18559 : * write the declaration (not data) of one user-defined sequence
18560 : */
18561 : static void
18562 1106 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18563 : {
18564 1106 : DumpOptions *dopt = fout->dopt;
18565 : SequenceItem *seq;
18566 : bool is_ascending;
18567 : int64 default_minv,
18568 : default_maxv;
18569 1106 : PQExpBuffer query = createPQExpBuffer();
18570 1106 : PQExpBuffer delqry = createPQExpBuffer();
18571 : char *qseqname;
18572 1106 : TableInfo *owning_tab = NULL;
18573 :
18574 1106 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18575 :
18576 : /*
18577 : * For versions >= 10, the sequence information is gathered in a sorted
18578 : * table before any calls to dumpSequence(). See collectSequences() for
18579 : * more information.
18580 : */
18581 1106 : if (fout->remoteVersion >= 100000)
18582 : {
18583 1106 : SequenceItem key = {0};
18584 :
18585 : Assert(sequences);
18586 :
18587 1106 : key.oid = tbinfo->dobj.catId.oid;
18588 1106 : seq = bsearch(&key, sequences, nsequences,
18589 : sizeof(SequenceItem), SequenceItemCmp);
18590 : }
18591 : else
18592 : {
18593 : PGresult *res;
18594 :
18595 : /*
18596 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18597 : *
18598 : * Note: it might seem that 'bigint' potentially needs to be
18599 : * schema-qualified, but actually that's a keyword.
18600 : */
18601 0 : appendPQExpBuffer(query,
18602 : "SELECT 'bigint' AS sequence_type, "
18603 : "start_value, increment_by, max_value, min_value, "
18604 : "cache_value, is_cycled FROM %s",
18605 0 : fmtQualifiedDumpable(tbinfo));
18606 :
18607 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18608 :
18609 0 : if (PQntuples(res) != 1)
18610 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18611 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18612 : PQntuples(res)),
18613 : tbinfo->dobj.name, PQntuples(res));
18614 :
18615 0 : seq = pg_malloc0(sizeof(SequenceItem));
18616 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18617 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18618 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18619 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18620 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18621 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18622 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18623 :
18624 0 : PQclear(res);
18625 : }
18626 :
18627 : /* Calculate default limits for a sequence of this type */
18628 1106 : is_ascending = (seq->incby >= 0);
18629 1106 : if (seq->seqtype == SEQTYPE_SMALLINT)
18630 : {
18631 80 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18632 80 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18633 : }
18634 1026 : else if (seq->seqtype == SEQTYPE_INTEGER)
18635 : {
18636 814 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18637 814 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18638 : }
18639 212 : else if (seq->seqtype == SEQTYPE_BIGINT)
18640 : {
18641 212 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18642 212 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18643 : }
18644 : else
18645 : {
18646 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18647 : default_minv = default_maxv = 0; /* keep compiler quiet */
18648 : }
18649 :
18650 : /*
18651 : * Identity sequences are not to be dropped separately.
18652 : */
18653 1106 : if (!tbinfo->is_identity_sequence)
18654 : {
18655 682 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18656 682 : fmtQualifiedDumpable(tbinfo));
18657 : }
18658 :
18659 1106 : resetPQExpBuffer(query);
18660 :
18661 1106 : if (dopt->binary_upgrade)
18662 : {
18663 132 : binary_upgrade_set_pg_class_oids(fout, query,
18664 132 : tbinfo->dobj.catId.oid);
18665 :
18666 : /*
18667 : * In older PG versions a sequence will have a pg_type entry, but v14
18668 : * and up don't use that, so don't attempt to preserve the type OID.
18669 : */
18670 : }
18671 :
18672 1106 : if (tbinfo->is_identity_sequence)
18673 : {
18674 424 : owning_tab = findTableByOid(tbinfo->owning_tab);
18675 :
18676 424 : appendPQExpBuffer(query,
18677 : "ALTER TABLE %s ",
18678 424 : fmtQualifiedDumpable(owning_tab));
18679 424 : appendPQExpBuffer(query,
18680 : "ALTER COLUMN %s ADD GENERATED ",
18681 424 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18682 424 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18683 296 : appendPQExpBufferStr(query, "ALWAYS");
18684 128 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18685 128 : appendPQExpBufferStr(query, "BY DEFAULT");
18686 424 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
18687 424 : fmtQualifiedDumpable(tbinfo));
18688 :
18689 : /*
18690 : * Emit persistence option only if it's different from the owning
18691 : * table's. This avoids using this new syntax unnecessarily.
18692 : */
18693 424 : if (tbinfo->relpersistence != owning_tab->relpersistence)
18694 32 : appendPQExpBuffer(query, " %s\n",
18695 32 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18696 : "UNLOGGED" : "LOGGED");
18697 : }
18698 : else
18699 : {
18700 682 : appendPQExpBuffer(query,
18701 : "CREATE %sSEQUENCE %s\n",
18702 682 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18703 : "UNLOGGED " : "",
18704 682 : fmtQualifiedDumpable(tbinfo));
18705 :
18706 682 : if (seq->seqtype != SEQTYPE_BIGINT)
18707 518 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
18708 : }
18709 :
18710 1106 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
18711 :
18712 1106 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
18713 :
18714 1106 : if (seq->minv != default_minv)
18715 48 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
18716 : else
18717 1058 : appendPQExpBufferStr(query, " NO MINVALUE\n");
18718 :
18719 1106 : if (seq->maxv != default_maxv)
18720 48 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
18721 : else
18722 1058 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
18723 :
18724 1106 : appendPQExpBuffer(query,
18725 : " CACHE " INT64_FORMAT "%s",
18726 1106 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
18727 :
18728 1106 : if (tbinfo->is_identity_sequence)
18729 424 : appendPQExpBufferStr(query, "\n);\n");
18730 : else
18731 682 : appendPQExpBufferStr(query, ";\n");
18732 :
18733 : /* binary_upgrade: no need to clear TOAST table oid */
18734 :
18735 1106 : if (dopt->binary_upgrade)
18736 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
18737 : "SEQUENCE", qseqname,
18738 132 : tbinfo->dobj.namespace->dobj.name);
18739 :
18740 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18741 1106 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18742 1106 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18743 : .namespace = tbinfo->dobj.namespace->dobj.name,
18744 : .owner = tbinfo->rolname,
18745 : .description = "SEQUENCE",
18746 : .section = SECTION_PRE_DATA,
18747 : .createStmt = query->data,
18748 : .dropStmt = delqry->data));
18749 :
18750 : /*
18751 : * If the sequence is owned by a table column, emit the ALTER for it as a
18752 : * separate TOC entry immediately following the sequence's own entry. It's
18753 : * OK to do this rather than using full sorting logic, because the
18754 : * dependency that tells us it's owned will have forced the table to be
18755 : * created first. We can't just include the ALTER in the TOC entry
18756 : * because it will fail if we haven't reassigned the sequence owner to
18757 : * match the table's owner.
18758 : *
18759 : * We need not schema-qualify the table reference because both sequence
18760 : * and table must be in the same schema.
18761 : */
18762 1106 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
18763 : {
18764 374 : owning_tab = findTableByOid(tbinfo->owning_tab);
18765 :
18766 374 : if (owning_tab == NULL)
18767 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
18768 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
18769 :
18770 374 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
18771 : {
18772 370 : resetPQExpBuffer(query);
18773 370 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
18774 370 : fmtQualifiedDumpable(tbinfo));
18775 370 : appendPQExpBuffer(query, " OWNED BY %s",
18776 370 : fmtQualifiedDumpable(owning_tab));
18777 370 : appendPQExpBuffer(query, ".%s;\n",
18778 370 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18779 :
18780 370 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18781 370 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18782 370 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18783 : .namespace = tbinfo->dobj.namespace->dobj.name,
18784 : .owner = tbinfo->rolname,
18785 : .description = "SEQUENCE OWNED BY",
18786 : .section = SECTION_PRE_DATA,
18787 : .createStmt = query->data,
18788 : .deps = &(tbinfo->dobj.dumpId),
18789 : .nDeps = 1));
18790 : }
18791 : }
18792 :
18793 : /* Dump Sequence Comments and Security Labels */
18794 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18795 0 : dumpComment(fout, "SEQUENCE", qseqname,
18796 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18797 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18798 :
18799 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18800 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
18801 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18802 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18803 :
18804 1106 : if (fout->remoteVersion < 100000)
18805 0 : pg_free(seq);
18806 1106 : destroyPQExpBuffer(query);
18807 1106 : destroyPQExpBuffer(delqry);
18808 1106 : free(qseqname);
18809 1106 : }
18810 :
18811 : /*
18812 : * dumpSequenceData
18813 : * write the data of one user-defined sequence
18814 : */
18815 : static void
18816 1136 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
18817 : {
18818 1136 : TableInfo *tbinfo = tdinfo->tdtable;
18819 : int64 last;
18820 : bool called;
18821 1136 : PQExpBuffer query = createPQExpBuffer();
18822 :
18823 : /*
18824 : * For versions >= 18, the sequence information is gathered in the sorted
18825 : * array before any calls to dumpSequenceData(). See collectSequences()
18826 : * for more information.
18827 : *
18828 : * For older versions, we have to query the sequence relations
18829 : * individually.
18830 : */
18831 1136 : if (fout->remoteVersion < 180000)
18832 : {
18833 : PGresult *res;
18834 :
18835 0 : appendPQExpBuffer(query,
18836 : "SELECT last_value, is_called FROM %s",
18837 0 : fmtQualifiedDumpable(tbinfo));
18838 :
18839 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18840 :
18841 0 : if (PQntuples(res) != 1)
18842 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18843 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18844 : PQntuples(res)),
18845 : tbinfo->dobj.name, PQntuples(res));
18846 :
18847 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18848 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18849 :
18850 0 : PQclear(res);
18851 : }
18852 : else
18853 : {
18854 1136 : SequenceItem key = {0};
18855 : SequenceItem *entry;
18856 :
18857 : Assert(sequences);
18858 : Assert(tbinfo->dobj.catId.oid);
18859 :
18860 1136 : key.oid = tbinfo->dobj.catId.oid;
18861 1136 : entry = bsearch(&key, sequences, nsequences,
18862 : sizeof(SequenceItem), SequenceItemCmp);
18863 :
18864 1136 : last = entry->last_value;
18865 1136 : called = entry->is_called;
18866 : }
18867 :
18868 1136 : resetPQExpBuffer(query);
18869 1136 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18870 1136 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18871 1136 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18872 : last, (called ? "true" : "false"));
18873 :
18874 1136 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18875 1136 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18876 1136 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18877 : .namespace = tbinfo->dobj.namespace->dobj.name,
18878 : .owner = tbinfo->rolname,
18879 : .description = "SEQUENCE SET",
18880 : .section = SECTION_DATA,
18881 : .createStmt = query->data,
18882 : .deps = &(tbinfo->dobj.dumpId),
18883 : .nDeps = 1));
18884 :
18885 1136 : destroyPQExpBuffer(query);
18886 1136 : }
18887 :
18888 : /*
18889 : * dumpTrigger
18890 : * write the declaration of one user-defined table trigger
18891 : */
18892 : static void
18893 1476 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18894 : {
18895 1476 : DumpOptions *dopt = fout->dopt;
18896 1476 : TableInfo *tbinfo = tginfo->tgtable;
18897 : PQExpBuffer query;
18898 : PQExpBuffer delqry;
18899 : PQExpBuffer trigprefix;
18900 : PQExpBuffer trigidentity;
18901 : char *qtabname;
18902 : char *tag;
18903 :
18904 : /* Do nothing if not dumping schema */
18905 1476 : if (!dopt->dumpSchema)
18906 62 : return;
18907 :
18908 1414 : query = createPQExpBuffer();
18909 1414 : delqry = createPQExpBuffer();
18910 1414 : trigprefix = createPQExpBuffer();
18911 1414 : trigidentity = createPQExpBuffer();
18912 :
18913 1414 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18914 :
18915 1414 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18916 1414 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18917 :
18918 1414 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18919 1414 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18920 :
18921 : /* Triggers can depend on extensions */
18922 1414 : append_depends_on_extension(fout, query, &tginfo->dobj,
18923 : "pg_catalog.pg_trigger", "TRIGGER",
18924 1414 : trigidentity->data);
18925 :
18926 1414 : if (tginfo->tgispartition)
18927 : {
18928 : Assert(tbinfo->ispartition);
18929 :
18930 : /*
18931 : * Partition triggers only appear here because their 'tgenabled' flag
18932 : * differs from its parent's. The trigger is created already, so
18933 : * remove the CREATE and replace it with an ALTER. (Clear out the
18934 : * DROP query too, so that pg_dump --create does not cause errors.)
18935 : */
18936 254 : resetPQExpBuffer(query);
18937 254 : resetPQExpBuffer(delqry);
18938 254 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18939 254 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18940 254 : fmtQualifiedDumpable(tbinfo));
18941 254 : switch (tginfo->tgenabled)
18942 : {
18943 90 : case 'f':
18944 : case 'D':
18945 90 : appendPQExpBufferStr(query, "DISABLE");
18946 90 : break;
18947 0 : case 't':
18948 : case 'O':
18949 0 : appendPQExpBufferStr(query, "ENABLE");
18950 0 : break;
18951 74 : case 'R':
18952 74 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18953 74 : break;
18954 90 : case 'A':
18955 90 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18956 90 : break;
18957 : }
18958 254 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18959 254 : fmtId(tginfo->dobj.name));
18960 : }
18961 1160 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
18962 : {
18963 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18964 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18965 0 : fmtQualifiedDumpable(tbinfo));
18966 0 : switch (tginfo->tgenabled)
18967 : {
18968 0 : case 'D':
18969 : case 'f':
18970 0 : appendPQExpBufferStr(query, "DISABLE");
18971 0 : break;
18972 0 : case 'A':
18973 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18974 0 : break;
18975 0 : case 'R':
18976 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18977 0 : break;
18978 0 : default:
18979 0 : appendPQExpBufferStr(query, "ENABLE");
18980 0 : break;
18981 : }
18982 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18983 0 : fmtId(tginfo->dobj.name));
18984 : }
18985 :
18986 1414 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
18987 1414 : fmtId(tginfo->dobj.name));
18988 :
18989 1414 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
18990 :
18991 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18992 1414 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
18993 1414 : ARCHIVE_OPTS(.tag = tag,
18994 : .namespace = tbinfo->dobj.namespace->dobj.name,
18995 : .owner = tbinfo->rolname,
18996 : .description = "TRIGGER",
18997 : .section = SECTION_POST_DATA,
18998 : .createStmt = query->data,
18999 : .dropStmt = delqry->data));
19000 :
19001 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19002 0 : dumpComment(fout, trigprefix->data, qtabname,
19003 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19004 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19005 :
19006 1414 : free(tag);
19007 1414 : destroyPQExpBuffer(query);
19008 1414 : destroyPQExpBuffer(delqry);
19009 1414 : destroyPQExpBuffer(trigprefix);
19010 1414 : destroyPQExpBuffer(trigidentity);
19011 1414 : free(qtabname);
19012 : }
19013 :
19014 : /*
19015 : * dumpEventTrigger
19016 : * write the declaration of one user-defined event trigger
19017 : */
19018 : static void
19019 98 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19020 : {
19021 98 : DumpOptions *dopt = fout->dopt;
19022 : PQExpBuffer query;
19023 : PQExpBuffer delqry;
19024 : char *qevtname;
19025 :
19026 : /* Do nothing if not dumping schema */
19027 98 : if (!dopt->dumpSchema)
19028 12 : return;
19029 :
19030 86 : query = createPQExpBuffer();
19031 86 : delqry = createPQExpBuffer();
19032 :
19033 86 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19034 :
19035 86 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19036 86 : appendPQExpBufferStr(query, qevtname);
19037 86 : appendPQExpBufferStr(query, " ON ");
19038 86 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19039 :
19040 86 : if (strcmp("", evtinfo->evttags) != 0)
19041 : {
19042 16 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19043 16 : appendPQExpBufferStr(query, evtinfo->evttags);
19044 16 : appendPQExpBufferChar(query, ')');
19045 : }
19046 :
19047 86 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19048 86 : appendPQExpBufferStr(query, evtinfo->evtfname);
19049 86 : appendPQExpBufferStr(query, "();\n");
19050 :
19051 86 : if (evtinfo->evtenabled != 'O')
19052 : {
19053 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19054 : qevtname);
19055 0 : switch (evtinfo->evtenabled)
19056 : {
19057 0 : case 'D':
19058 0 : appendPQExpBufferStr(query, "DISABLE");
19059 0 : break;
19060 0 : case 'A':
19061 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19062 0 : break;
19063 0 : case 'R':
19064 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19065 0 : break;
19066 0 : default:
19067 0 : appendPQExpBufferStr(query, "ENABLE");
19068 0 : break;
19069 : }
19070 0 : appendPQExpBufferStr(query, ";\n");
19071 : }
19072 :
19073 86 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19074 : qevtname);
19075 :
19076 86 : if (dopt->binary_upgrade)
19077 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19078 : "EVENT TRIGGER", qevtname, NULL);
19079 :
19080 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19081 86 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19082 86 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19083 : .owner = evtinfo->evtowner,
19084 : .description = "EVENT TRIGGER",
19085 : .section = SECTION_POST_DATA,
19086 : .createStmt = query->data,
19087 : .dropStmt = delqry->data));
19088 :
19089 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19090 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19091 0 : NULL, evtinfo->evtowner,
19092 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19093 :
19094 86 : destroyPQExpBuffer(query);
19095 86 : destroyPQExpBuffer(delqry);
19096 86 : free(qevtname);
19097 : }
19098 :
19099 : /*
19100 : * dumpRule
19101 : * Dump a rule
19102 : */
19103 : static void
19104 2984 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19105 : {
19106 2984 : DumpOptions *dopt = fout->dopt;
19107 2984 : TableInfo *tbinfo = rinfo->ruletable;
19108 : bool is_view;
19109 : PQExpBuffer query;
19110 : PQExpBuffer cmd;
19111 : PQExpBuffer delcmd;
19112 : PQExpBuffer ruleprefix;
19113 : char *qtabname;
19114 : PGresult *res;
19115 : char *tag;
19116 :
19117 : /* Do nothing if not dumping schema */
19118 2984 : if (!dopt->dumpSchema)
19119 132 : return;
19120 :
19121 : /*
19122 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19123 : * we do not want to dump it as a separate object.
19124 : */
19125 2852 : if (!rinfo->separate)
19126 2184 : return;
19127 :
19128 : /*
19129 : * If it's an ON SELECT rule, we want to print it as a view definition,
19130 : * instead of a rule.
19131 : */
19132 668 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19133 :
19134 668 : query = createPQExpBuffer();
19135 668 : cmd = createPQExpBuffer();
19136 668 : delcmd = createPQExpBuffer();
19137 668 : ruleprefix = createPQExpBuffer();
19138 :
19139 668 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19140 :
19141 668 : if (is_view)
19142 : {
19143 : PQExpBuffer result;
19144 :
19145 : /*
19146 : * We need OR REPLACE here because we'll be replacing a dummy view.
19147 : * Otherwise this should look largely like the regular view dump code.
19148 : */
19149 32 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19150 32 : fmtQualifiedDumpable(tbinfo));
19151 32 : if (nonemptyReloptions(tbinfo->reloptions))
19152 : {
19153 0 : appendPQExpBufferStr(cmd, " WITH (");
19154 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19155 0 : appendPQExpBufferChar(cmd, ')');
19156 : }
19157 32 : result = createViewAsClause(fout, tbinfo);
19158 32 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19159 32 : destroyPQExpBuffer(result);
19160 32 : if (tbinfo->checkoption != NULL)
19161 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19162 : tbinfo->checkoption);
19163 32 : appendPQExpBufferStr(cmd, ";\n");
19164 : }
19165 : else
19166 : {
19167 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19168 636 : appendPQExpBuffer(query,
19169 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19170 636 : rinfo->dobj.catId.oid);
19171 :
19172 636 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19173 :
19174 636 : if (PQntuples(res) != 1)
19175 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19176 : rinfo->dobj.name, tbinfo->dobj.name);
19177 :
19178 636 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19179 :
19180 636 : PQclear(res);
19181 : }
19182 :
19183 : /*
19184 : * Add the command to alter the rules replication firing semantics if it
19185 : * differs from the default.
19186 : */
19187 668 : if (rinfo->ev_enabled != 'O')
19188 : {
19189 48 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19190 48 : switch (rinfo->ev_enabled)
19191 : {
19192 0 : case 'A':
19193 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19194 0 : fmtId(rinfo->dobj.name));
19195 0 : break;
19196 0 : case 'R':
19197 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19198 0 : fmtId(rinfo->dobj.name));
19199 0 : break;
19200 48 : case 'D':
19201 48 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19202 48 : fmtId(rinfo->dobj.name));
19203 48 : break;
19204 : }
19205 : }
19206 :
19207 668 : if (is_view)
19208 : {
19209 : /*
19210 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19211 : * REPLACE VIEW to replace the rule with something with minimal
19212 : * dependencies.
19213 : */
19214 : PQExpBuffer result;
19215 :
19216 32 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19217 32 : fmtQualifiedDumpable(tbinfo));
19218 32 : result = createDummyViewAsClause(fout, tbinfo);
19219 32 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19220 32 : destroyPQExpBuffer(result);
19221 : }
19222 : else
19223 : {
19224 636 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19225 636 : fmtId(rinfo->dobj.name));
19226 636 : appendPQExpBuffer(delcmd, "ON %s;\n",
19227 636 : fmtQualifiedDumpable(tbinfo));
19228 : }
19229 :
19230 668 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19231 668 : fmtId(rinfo->dobj.name));
19232 :
19233 668 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19234 :
19235 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19236 668 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19237 668 : ARCHIVE_OPTS(.tag = tag,
19238 : .namespace = tbinfo->dobj.namespace->dobj.name,
19239 : .owner = tbinfo->rolname,
19240 : .description = "RULE",
19241 : .section = SECTION_POST_DATA,
19242 : .createStmt = cmd->data,
19243 : .dropStmt = delcmd->data));
19244 :
19245 : /* Dump rule comments */
19246 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19247 0 : dumpComment(fout, ruleprefix->data, qtabname,
19248 0 : tbinfo->dobj.namespace->dobj.name,
19249 : tbinfo->rolname,
19250 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19251 :
19252 668 : free(tag);
19253 668 : destroyPQExpBuffer(query);
19254 668 : destroyPQExpBuffer(cmd);
19255 668 : destroyPQExpBuffer(delcmd);
19256 668 : destroyPQExpBuffer(ruleprefix);
19257 668 : free(qtabname);
19258 : }
19259 :
19260 : /*
19261 : * getExtensionMembership --- obtain extension membership data
19262 : *
19263 : * We need to identify objects that are extension members as soon as they're
19264 : * loaded, so that we can correctly determine whether they need to be dumped.
19265 : * Generally speaking, extension member objects will get marked as *not* to
19266 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19267 : * command. However, in binary upgrade mode we still need to dump the members
19268 : * individually.
19269 : */
19270 : void
19271 470 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19272 : int numExtensions)
19273 : {
19274 : PQExpBuffer query;
19275 : PGresult *res;
19276 : int ntups,
19277 : i;
19278 : int i_classid,
19279 : i_objid,
19280 : i_refobjid;
19281 : ExtensionInfo *ext;
19282 :
19283 : /* Nothing to do if no extensions */
19284 470 : if (numExtensions == 0)
19285 0 : return;
19286 :
19287 470 : query = createPQExpBuffer();
19288 :
19289 : /* refclassid constraint is redundant but may speed the search */
19290 470 : appendPQExpBufferStr(query, "SELECT "
19291 : "classid, objid, refobjid "
19292 : "FROM pg_depend "
19293 : "WHERE refclassid = 'pg_extension'::regclass "
19294 : "AND deptype = 'e' "
19295 : "ORDER BY 3");
19296 :
19297 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19298 :
19299 470 : ntups = PQntuples(res);
19300 :
19301 470 : i_classid = PQfnumber(res, "classid");
19302 470 : i_objid = PQfnumber(res, "objid");
19303 470 : i_refobjid = PQfnumber(res, "refobjid");
19304 :
19305 : /*
19306 : * Since we ordered the SELECT by referenced ID, we can expect that
19307 : * multiple entries for the same extension will appear together; this
19308 : * saves on searches.
19309 : */
19310 470 : ext = NULL;
19311 :
19312 3550 : for (i = 0; i < ntups; i++)
19313 : {
19314 : CatalogId objId;
19315 : Oid extId;
19316 :
19317 3080 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19318 3080 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19319 3080 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19320 :
19321 3080 : if (ext == NULL ||
19322 2610 : ext->dobj.catId.oid != extId)
19323 520 : ext = findExtensionByOid(extId);
19324 :
19325 3080 : if (ext == NULL)
19326 : {
19327 : /* shouldn't happen */
19328 0 : pg_log_warning("could not find referenced extension %u", extId);
19329 0 : continue;
19330 : }
19331 :
19332 3080 : recordExtensionMembership(objId, ext);
19333 : }
19334 :
19335 470 : PQclear(res);
19336 :
19337 470 : destroyPQExpBuffer(query);
19338 : }
19339 :
19340 : /*
19341 : * processExtensionTables --- deal with extension configuration tables
19342 : *
19343 : * There are two parts to this process:
19344 : *
19345 : * 1. Identify and create dump records for extension configuration tables.
19346 : *
19347 : * Extensions can mark tables as "configuration", which means that the user
19348 : * is able and expected to modify those tables after the extension has been
19349 : * loaded. For these tables, we dump out only the data- the structure is
19350 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19351 : * foreign keys, which brings us to-
19352 : *
19353 : * 2. Record FK dependencies between configuration tables.
19354 : *
19355 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19356 : * the data is loaded, we have to work out what the best order for reloading
19357 : * the data is, to avoid FK violations when the tables are restored. This is
19358 : * not perfect- we can't handle circular dependencies and if any exist they
19359 : * will cause an invalid dump to be produced (though at least all of the data
19360 : * is included for a user to manually restore). This is currently documented
19361 : * but perhaps we can provide a better solution in the future.
19362 : */
19363 : void
19364 468 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19365 : int numExtensions)
19366 : {
19367 468 : DumpOptions *dopt = fout->dopt;
19368 : PQExpBuffer query;
19369 : PGresult *res;
19370 : int ntups,
19371 : i;
19372 : int i_conrelid,
19373 : i_confrelid;
19374 :
19375 : /* Nothing to do if no extensions */
19376 468 : if (numExtensions == 0)
19377 0 : return;
19378 :
19379 : /*
19380 : * Identify extension configuration tables and create TableDataInfo
19381 : * objects for them, ensuring their data will be dumped even though the
19382 : * tables themselves won't be.
19383 : *
19384 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19385 : * user data in a configuration table is treated like schema data. This
19386 : * seems appropriate since system data in a config table would get
19387 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19388 : * list of extensions to be included, none of its data is dumped.
19389 : */
19390 986 : for (i = 0; i < numExtensions; i++)
19391 : {
19392 518 : ExtensionInfo *curext = &(extinfo[i]);
19393 518 : char *extconfig = curext->extconfig;
19394 518 : char *extcondition = curext->extcondition;
19395 518 : char **extconfigarray = NULL;
19396 518 : char **extconditionarray = NULL;
19397 518 : int nconfigitems = 0;
19398 518 : int nconditionitems = 0;
19399 :
19400 : /*
19401 : * Check if this extension is listed as to include in the dump. If
19402 : * not, any table data associated with it is discarded.
19403 : */
19404 518 : if (extension_include_oids.head != NULL &&
19405 16 : !simple_oid_list_member(&extension_include_oids,
19406 : curext->dobj.catId.oid))
19407 12 : continue;
19408 :
19409 : /*
19410 : * Check if this extension is listed as to exclude in the dump. If
19411 : * yes, any table data associated with it is discarded.
19412 : */
19413 518 : if (extension_exclude_oids.head != NULL &&
19414 8 : simple_oid_list_member(&extension_exclude_oids,
19415 : curext->dobj.catId.oid))
19416 4 : continue;
19417 :
19418 506 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19419 : {
19420 : int j;
19421 :
19422 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19423 0 : pg_fatal("could not parse %s array", "extconfig");
19424 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19425 0 : pg_fatal("could not parse %s array", "extcondition");
19426 40 : if (nconfigitems != nconditionitems)
19427 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19428 :
19429 120 : for (j = 0; j < nconfigitems; j++)
19430 : {
19431 : TableInfo *configtbl;
19432 80 : Oid configtbloid = atooid(extconfigarray[j]);
19433 80 : bool dumpobj =
19434 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19435 :
19436 80 : configtbl = findTableByOid(configtbloid);
19437 80 : if (configtbl == NULL)
19438 0 : continue;
19439 :
19440 : /*
19441 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19442 : * unless the table or its schema is explicitly included
19443 : */
19444 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19445 : {
19446 : /* check table explicitly requested */
19447 4 : if (table_include_oids.head != NULL &&
19448 0 : simple_oid_list_member(&table_include_oids,
19449 : configtbloid))
19450 0 : dumpobj = true;
19451 :
19452 : /* check table's schema explicitly requested */
19453 4 : if (configtbl->dobj.namespace->dobj.dump &
19454 : DUMP_COMPONENT_DATA)
19455 4 : dumpobj = true;
19456 : }
19457 :
19458 : /* check table excluded by an exclusion switch */
19459 88 : if (table_exclude_oids.head != NULL &&
19460 8 : simple_oid_list_member(&table_exclude_oids,
19461 : configtbloid))
19462 2 : dumpobj = false;
19463 :
19464 : /* check schema excluded by an exclusion switch */
19465 80 : if (simple_oid_list_member(&schema_exclude_oids,
19466 80 : configtbl->dobj.namespace->dobj.catId.oid))
19467 0 : dumpobj = false;
19468 :
19469 80 : if (dumpobj)
19470 : {
19471 78 : makeTableDataInfo(dopt, configtbl);
19472 78 : if (configtbl->dataObj != NULL)
19473 : {
19474 78 : if (strlen(extconditionarray[j]) > 0)
19475 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19476 : }
19477 : }
19478 : }
19479 : }
19480 506 : if (extconfigarray)
19481 40 : free(extconfigarray);
19482 506 : if (extconditionarray)
19483 40 : free(extconditionarray);
19484 : }
19485 :
19486 : /*
19487 : * Now that all the TableDataInfo objects have been created for all the
19488 : * extensions, check their FK dependencies and register them to try and
19489 : * dump the data out in an order that they can be restored in.
19490 : *
19491 : * Note that this is not a problem for user tables as their FKs are
19492 : * recreated after the data has been loaded.
19493 : */
19494 :
19495 468 : query = createPQExpBuffer();
19496 :
19497 468 : printfPQExpBuffer(query,
19498 : "SELECT conrelid, confrelid "
19499 : "FROM pg_constraint "
19500 : "JOIN pg_depend ON (objid = confrelid) "
19501 : "WHERE contype = 'f' "
19502 : "AND refclassid = 'pg_extension'::regclass "
19503 : "AND classid = 'pg_class'::regclass;");
19504 :
19505 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19506 468 : ntups = PQntuples(res);
19507 :
19508 468 : i_conrelid = PQfnumber(res, "conrelid");
19509 468 : i_confrelid = PQfnumber(res, "confrelid");
19510 :
19511 : /* Now get the dependencies and register them */
19512 468 : for (i = 0; i < ntups; i++)
19513 : {
19514 : Oid conrelid,
19515 : confrelid;
19516 : TableInfo *reftable,
19517 : *contable;
19518 :
19519 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19520 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19521 0 : contable = findTableByOid(conrelid);
19522 0 : reftable = findTableByOid(confrelid);
19523 :
19524 0 : if (reftable == NULL ||
19525 0 : reftable->dataObj == NULL ||
19526 0 : contable == NULL ||
19527 0 : contable->dataObj == NULL)
19528 0 : continue;
19529 :
19530 : /*
19531 : * Make referencing TABLE_DATA object depend on the referenced table's
19532 : * TABLE_DATA object.
19533 : */
19534 0 : addObjectDependency(&contable->dataObj->dobj,
19535 0 : reftable->dataObj->dobj.dumpId);
19536 : }
19537 468 : PQclear(res);
19538 468 : destroyPQExpBuffer(query);
19539 : }
19540 :
19541 : /*
19542 : * getDependencies --- obtain available dependency data
19543 : */
19544 : static void
19545 468 : getDependencies(Archive *fout)
19546 : {
19547 : PQExpBuffer query;
19548 : PGresult *res;
19549 : int ntups,
19550 : i;
19551 : int i_classid,
19552 : i_objid,
19553 : i_refclassid,
19554 : i_refobjid,
19555 : i_deptype;
19556 : DumpableObject *dobj,
19557 : *refdobj;
19558 :
19559 468 : pg_log_info("reading dependency data");
19560 :
19561 468 : query = createPQExpBuffer();
19562 :
19563 : /*
19564 : * Messy query to collect the dependency data we need. Note that we
19565 : * ignore the sub-object column, so that dependencies of or on a column
19566 : * look the same as dependencies of or on a whole table.
19567 : *
19568 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19569 : * already processed by getExtensionMembership.
19570 : */
19571 468 : appendPQExpBufferStr(query, "SELECT "
19572 : "classid, objid, refclassid, refobjid, deptype "
19573 : "FROM pg_depend "
19574 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19575 :
19576 : /*
19577 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19578 : * have to translate their dependencies into dependencies of their parent
19579 : * opfamily. Ignore internal dependencies though, as those will point to
19580 : * their parent opclass, which we needn't consider here (and if we did,
19581 : * it'd just result in circular dependencies). Also, "loose" opfamily
19582 : * entries will have dependencies on their parent opfamily, which we
19583 : * should drop since they'd likewise become useless self-dependencies.
19584 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19585 : */
19586 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19587 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19588 : "FROM pg_depend d, pg_amop o "
19589 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19590 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19591 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19592 :
19593 : /* Likewise for pg_amproc entries */
19594 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19595 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19596 : "FROM pg_depend d, pg_amproc p "
19597 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19598 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19599 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19600 :
19601 : /* Sort the output for efficiency below */
19602 468 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19603 :
19604 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19605 :
19606 468 : ntups = PQntuples(res);
19607 :
19608 468 : i_classid = PQfnumber(res, "classid");
19609 468 : i_objid = PQfnumber(res, "objid");
19610 468 : i_refclassid = PQfnumber(res, "refclassid");
19611 468 : i_refobjid = PQfnumber(res, "refobjid");
19612 468 : i_deptype = PQfnumber(res, "deptype");
19613 :
19614 : /*
19615 : * Since we ordered the SELECT by referencing ID, we can expect that
19616 : * multiple entries for the same object will appear together; this saves
19617 : * on searches.
19618 : */
19619 468 : dobj = NULL;
19620 :
19621 1010552 : for (i = 0; i < ntups; i++)
19622 : {
19623 : CatalogId objId;
19624 : CatalogId refobjId;
19625 : char deptype;
19626 :
19627 1010084 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19628 1010084 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19629 1010084 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19630 1010084 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19631 1010084 : deptype = *(PQgetvalue(res, i, i_deptype));
19632 :
19633 1010084 : if (dobj == NULL ||
19634 948050 : dobj->catId.tableoid != objId.tableoid ||
19635 943010 : dobj->catId.oid != objId.oid)
19636 442650 : dobj = findObjectByCatalogId(objId);
19637 :
19638 : /*
19639 : * Failure to find objects mentioned in pg_depend is not unexpected,
19640 : * since for example we don't collect info about TOAST tables.
19641 : */
19642 1010084 : if (dobj == NULL)
19643 : {
19644 : #ifdef NOT_USED
19645 : pg_log_warning("no referencing object %u %u",
19646 : objId.tableoid, objId.oid);
19647 : #endif
19648 63790 : continue;
19649 : }
19650 :
19651 948518 : refdobj = findObjectByCatalogId(refobjId);
19652 :
19653 948518 : if (refdobj == NULL)
19654 : {
19655 : #ifdef NOT_USED
19656 : pg_log_warning("no referenced object %u %u",
19657 : refobjId.tableoid, refobjId.oid);
19658 : #endif
19659 2224 : continue;
19660 : }
19661 :
19662 : /*
19663 : * For 'x' dependencies, mark the object for later; we still add the
19664 : * normal dependency, for possible ordering purposes. Currently
19665 : * pg_dump_sort.c knows to put extensions ahead of all object types
19666 : * that could possibly depend on them, but this is safer.
19667 : */
19668 946294 : if (deptype == 'x')
19669 88 : dobj->depends_on_ext = true;
19670 :
19671 : /*
19672 : * Ordinarily, table rowtypes have implicit dependencies on their
19673 : * tables. However, for a composite type the implicit dependency goes
19674 : * the other way in pg_depend; which is the right thing for DROP but
19675 : * it doesn't produce the dependency ordering we need. So in that one
19676 : * case, we reverse the direction of the dependency.
19677 : */
19678 946294 : if (deptype == 'i' &&
19679 265448 : dobj->objType == DO_TABLE &&
19680 3248 : refdobj->objType == DO_TYPE)
19681 468 : addObjectDependency(refdobj, dobj->dumpId);
19682 : else
19683 : /* normal case */
19684 945826 : addObjectDependency(dobj, refdobj->dumpId);
19685 : }
19686 :
19687 468 : PQclear(res);
19688 :
19689 468 : destroyPQExpBuffer(query);
19690 468 : }
19691 :
19692 :
19693 : /*
19694 : * createBoundaryObjects - create dummy DumpableObjects to represent
19695 : * dump section boundaries.
19696 : */
19697 : static DumpableObject *
19698 468 : createBoundaryObjects(void)
19699 : {
19700 : DumpableObject *dobjs;
19701 :
19702 468 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
19703 :
19704 468 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
19705 468 : dobjs[0].catId = nilCatalogId;
19706 468 : AssignDumpId(dobjs + 0);
19707 468 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
19708 :
19709 468 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
19710 468 : dobjs[1].catId = nilCatalogId;
19711 468 : AssignDumpId(dobjs + 1);
19712 468 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
19713 :
19714 468 : return dobjs;
19715 : }
19716 :
19717 : /*
19718 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
19719 : * section boundaries.
19720 : */
19721 : static void
19722 468 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
19723 : DumpableObject *boundaryObjs)
19724 : {
19725 468 : DumpableObject *preDataBound = boundaryObjs + 0;
19726 468 : DumpableObject *postDataBound = boundaryObjs + 1;
19727 : int i;
19728 :
19729 1733366 : for (i = 0; i < numObjs; i++)
19730 : {
19731 1732898 : DumpableObject *dobj = dobjs[i];
19732 :
19733 : /*
19734 : * The classification of object types here must match the SECTION_xxx
19735 : * values assigned during subsequent ArchiveEntry calls!
19736 : */
19737 1732898 : switch (dobj->objType)
19738 : {
19739 1618758 : case DO_NAMESPACE:
19740 : case DO_EXTENSION:
19741 : case DO_TYPE:
19742 : case DO_SHELL_TYPE:
19743 : case DO_FUNC:
19744 : case DO_AGG:
19745 : case DO_OPERATOR:
19746 : case DO_ACCESS_METHOD:
19747 : case DO_OPCLASS:
19748 : case DO_OPFAMILY:
19749 : case DO_COLLATION:
19750 : case DO_CONVERSION:
19751 : case DO_TABLE:
19752 : case DO_TABLE_ATTACH:
19753 : case DO_ATTRDEF:
19754 : case DO_PROCLANG:
19755 : case DO_CAST:
19756 : case DO_DUMMY_TYPE:
19757 : case DO_TSPARSER:
19758 : case DO_TSDICT:
19759 : case DO_TSTEMPLATE:
19760 : case DO_TSCONFIG:
19761 : case DO_FDW:
19762 : case DO_FOREIGN_SERVER:
19763 : case DO_TRANSFORM:
19764 : /* Pre-data objects: must come before the pre-data boundary */
19765 1618758 : addObjectDependency(preDataBound, dobj->dumpId);
19766 1618758 : break;
19767 13850 : case DO_TABLE_DATA:
19768 : case DO_SEQUENCE_SET:
19769 : case DO_LARGE_OBJECT:
19770 : case DO_LARGE_OBJECT_DATA:
19771 : /* Data objects: must come between the boundaries */
19772 13850 : addObjectDependency(dobj, preDataBound->dumpId);
19773 13850 : addObjectDependency(postDataBound, dobj->dumpId);
19774 13850 : break;
19775 14092 : case DO_INDEX:
19776 : case DO_INDEX_ATTACH:
19777 : case DO_STATSEXT:
19778 : case DO_REFRESH_MATVIEW:
19779 : case DO_TRIGGER:
19780 : case DO_EVENT_TRIGGER:
19781 : case DO_DEFAULT_ACL:
19782 : case DO_POLICY:
19783 : case DO_PUBLICATION:
19784 : case DO_PUBLICATION_REL:
19785 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
19786 : case DO_SUBSCRIPTION:
19787 : case DO_SUBSCRIPTION_REL:
19788 : /* Post-data objects: must come after the post-data boundary */
19789 14092 : addObjectDependency(dobj, postDataBound->dumpId);
19790 14092 : break;
19791 71834 : case DO_RULE:
19792 : /* Rules are post-data, but only if dumped separately */
19793 71834 : if (((RuleInfo *) dobj)->separate)
19794 1716 : addObjectDependency(dobj, postDataBound->dumpId);
19795 71834 : break;
19796 6458 : case DO_CONSTRAINT:
19797 : case DO_FK_CONSTRAINT:
19798 : /* Constraints are post-data, but only if dumped separately */
19799 6458 : if (((ConstraintInfo *) dobj)->separate)
19800 4728 : addObjectDependency(dobj, postDataBound->dumpId);
19801 6458 : break;
19802 468 : case DO_PRE_DATA_BOUNDARY:
19803 : /* nothing to do */
19804 468 : break;
19805 468 : case DO_POST_DATA_BOUNDARY:
19806 : /* must come after the pre-data boundary */
19807 468 : addObjectDependency(dobj, preDataBound->dumpId);
19808 468 : break;
19809 6970 : case DO_REL_STATS:
19810 : /* stats section varies by parent object type, DATA or POST */
19811 6970 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
19812 : {
19813 4372 : addObjectDependency(dobj, preDataBound->dumpId);
19814 4372 : addObjectDependency(postDataBound, dobj->dumpId);
19815 : }
19816 : else
19817 2598 : addObjectDependency(dobj, postDataBound->dumpId);
19818 6970 : break;
19819 : }
19820 : }
19821 468 : }
19822 :
19823 :
19824 : /*
19825 : * BuildArchiveDependencies - create dependency data for archive TOC entries
19826 : *
19827 : * The raw dependency data obtained by getDependencies() is not terribly
19828 : * useful in an archive dump, because in many cases there are dependency
19829 : * chains linking through objects that don't appear explicitly in the dump.
19830 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
19831 : * will depend on other objects --- but the rule will not appear as a separate
19832 : * object in the dump. We need to adjust the view's dependencies to include
19833 : * whatever the rule depends on that is included in the dump.
19834 : *
19835 : * Just to make things more complicated, there are also "special" dependencies
19836 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
19837 : * not rearrange because pg_restore knows that TABLE DATA only depends on
19838 : * its table. In these cases we must leave the dependencies strictly as-is
19839 : * even if they refer to not-to-be-dumped objects.
19840 : *
19841 : * To handle this, the convention is that "special" dependencies are created
19842 : * during ArchiveEntry calls, and an archive TOC item that has any such
19843 : * entries will not be touched here. Otherwise, we recursively search the
19844 : * DumpableObject data structures to build the correct dependencies for each
19845 : * archive TOC item.
19846 : */
19847 : static void
19848 206 : BuildArchiveDependencies(Archive *fout)
19849 : {
19850 206 : ArchiveHandle *AH = (ArchiveHandle *) fout;
19851 : TocEntry *te;
19852 :
19853 : /* Scan all TOC entries in the archive */
19854 20454 : for (te = AH->toc->next; te != AH->toc; te = te->next)
19855 : {
19856 : DumpableObject *dobj;
19857 : DumpId *dependencies;
19858 : int nDeps;
19859 : int allocDeps;
19860 :
19861 : /* No need to process entries that will not be dumped */
19862 20248 : if (te->reqs == 0)
19863 8964 : continue;
19864 : /* Ignore entries that already have "special" dependencies */
19865 20242 : if (te->nDeps > 0)
19866 7712 : continue;
19867 : /* Otherwise, look up the item's original DumpableObject, if any */
19868 12530 : dobj = findObjectByDumpId(te->dumpId);
19869 12530 : if (dobj == NULL)
19870 978 : continue;
19871 : /* No work if it has no dependencies */
19872 11552 : if (dobj->nDeps <= 0)
19873 268 : continue;
19874 : /* Set up work array */
19875 11284 : allocDeps = 64;
19876 11284 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19877 11284 : nDeps = 0;
19878 : /* Recursively find all dumpable dependencies */
19879 11284 : findDumpableDependencies(AH, dobj,
19880 : &dependencies, &nDeps, &allocDeps);
19881 : /* And save 'em ... */
19882 11284 : if (nDeps > 0)
19883 : {
19884 8000 : dependencies = (DumpId *) pg_realloc(dependencies,
19885 : nDeps * sizeof(DumpId));
19886 8000 : te->dependencies = dependencies;
19887 8000 : te->nDeps = nDeps;
19888 : }
19889 : else
19890 3284 : free(dependencies);
19891 : }
19892 206 : }
19893 :
19894 : /* Recursive search subroutine for BuildArchiveDependencies */
19895 : static void
19896 26684 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19897 : DumpId **dependencies, int *nDeps, int *allocDeps)
19898 : {
19899 : int i;
19900 :
19901 : /*
19902 : * Ignore section boundary objects: if we search through them, we'll
19903 : * report lots of bogus dependencies.
19904 : */
19905 26684 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19906 26644 : dobj->objType == DO_POST_DATA_BOUNDARY)
19907 4304 : return;
19908 :
19909 54644 : for (i = 0; i < dobj->nDeps; i++)
19910 : {
19911 32264 : DumpId depid = dobj->dependencies[i];
19912 :
19913 32264 : if (TocIDRequired(AH, depid) != 0)
19914 : {
19915 : /* Object will be dumped, so just reference it as a dependency */
19916 16864 : if (*nDeps >= *allocDeps)
19917 : {
19918 0 : *allocDeps *= 2;
19919 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
19920 0 : *allocDeps * sizeof(DumpId));
19921 : }
19922 16864 : (*dependencies)[*nDeps] = depid;
19923 16864 : (*nDeps)++;
19924 : }
19925 : else
19926 : {
19927 : /*
19928 : * Object will not be dumped, so recursively consider its deps. We
19929 : * rely on the assumption that sortDumpableObjects already broke
19930 : * any dependency loops, else we might recurse infinitely.
19931 : */
19932 15400 : DumpableObject *otherdobj = findObjectByDumpId(depid);
19933 :
19934 15400 : if (otherdobj)
19935 15400 : findDumpableDependencies(AH, otherdobj,
19936 : dependencies, nDeps, allocDeps);
19937 : }
19938 : }
19939 : }
19940 :
19941 :
19942 : /*
19943 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
19944 : * given type OID.
19945 : *
19946 : * This does not guarantee to schema-qualify the output, so it should not
19947 : * be used to create the target object name for CREATE or ALTER commands.
19948 : *
19949 : * Note that the result is cached and must not be freed by the caller.
19950 : */
19951 : static const char *
19952 6916 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
19953 : {
19954 : TypeInfo *typeInfo;
19955 : char *result;
19956 : PQExpBuffer query;
19957 : PGresult *res;
19958 :
19959 6916 : if (oid == 0)
19960 : {
19961 0 : if ((opts & zeroAsStar) != 0)
19962 0 : return "*";
19963 0 : else if ((opts & zeroAsNone) != 0)
19964 0 : return "NONE";
19965 : }
19966 :
19967 : /* see if we have the result cached in the type's TypeInfo record */
19968 6916 : typeInfo = findTypeByOid(oid);
19969 6916 : if (typeInfo && typeInfo->ftypname)
19970 5680 : return typeInfo->ftypname;
19971 :
19972 1236 : query = createPQExpBuffer();
19973 1236 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
19974 : oid);
19975 :
19976 1236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
19977 :
19978 : /* result of format_type is already quoted */
19979 1236 : result = pg_strdup(PQgetvalue(res, 0, 0));
19980 :
19981 1236 : PQclear(res);
19982 1236 : destroyPQExpBuffer(query);
19983 :
19984 : /*
19985 : * Cache the result for re-use in later requests, if possible. If we
19986 : * don't have a TypeInfo for the type, the string will be leaked once the
19987 : * caller is done with it ... but that case really should not happen, so
19988 : * leaking if it does seems acceptable.
19989 : */
19990 1236 : if (typeInfo)
19991 1236 : typeInfo->ftypname = result;
19992 :
19993 1236 : return result;
19994 : }
19995 :
19996 : /*
19997 : * Return a column list clause for the given relation.
19998 : *
19999 : * Special case: if there are no undropped columns in the relation, return
20000 : * "", not an invalid "()" column list.
20001 : */
20002 : static const char *
20003 24288 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20004 : {
20005 24288 : int numatts = ti->numatts;
20006 24288 : char **attnames = ti->attnames;
20007 24288 : bool *attisdropped = ti->attisdropped;
20008 24288 : char *attgenerated = ti->attgenerated;
20009 : bool needComma;
20010 : int i;
20011 :
20012 24288 : appendPQExpBufferChar(buffer, '(');
20013 24288 : needComma = false;
20014 120664 : for (i = 0; i < numatts; i++)
20015 : {
20016 96376 : if (attisdropped[i])
20017 1744 : continue;
20018 94632 : if (attgenerated[i])
20019 3188 : continue;
20020 91444 : if (needComma)
20021 67688 : appendPQExpBufferStr(buffer, ", ");
20022 91444 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20023 91444 : needComma = true;
20024 : }
20025 :
20026 24288 : if (!needComma)
20027 532 : return ""; /* no undropped columns */
20028 :
20029 23756 : appendPQExpBufferChar(buffer, ')');
20030 23756 : return buffer->data;
20031 : }
20032 :
20033 : /*
20034 : * Check if a reloptions array is nonempty.
20035 : */
20036 : static bool
20037 37746 : nonemptyReloptions(const char *reloptions)
20038 : {
20039 : /* Don't want to print it if it's just "{}" */
20040 37746 : return (reloptions != NULL && strlen(reloptions) > 2);
20041 : }
20042 :
20043 : /*
20044 : * Format a reloptions array and append it to the given buffer.
20045 : *
20046 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20047 : */
20048 : static void
20049 594 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20050 : const char *prefix, Archive *fout)
20051 : {
20052 : bool res;
20053 :
20054 594 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20055 594 : fout->std_strings);
20056 594 : if (!res)
20057 0 : pg_log_warning("could not parse %s array", "reloptions");
20058 594 : }
20059 :
20060 : /*
20061 : * read_dump_filters - retrieve object identifier patterns from file
20062 : *
20063 : * Parse the specified filter file for include and exclude patterns, and add
20064 : * them to the relevant lists. If the filename is "-" then filters will be
20065 : * read from STDIN rather than a file.
20066 : */
20067 : static void
20068 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20069 : {
20070 : FilterStateData fstate;
20071 : char *objname;
20072 : FilterCommandType comtype;
20073 : FilterObjectType objtype;
20074 :
20075 52 : filter_init(&fstate, filename, exit_nicely);
20076 :
20077 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20078 : {
20079 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20080 : {
20081 34 : switch (objtype)
20082 : {
20083 0 : case FILTER_OBJECT_TYPE_NONE:
20084 0 : break;
20085 0 : case FILTER_OBJECT_TYPE_DATABASE:
20086 : case FILTER_OBJECT_TYPE_FUNCTION:
20087 : case FILTER_OBJECT_TYPE_INDEX:
20088 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20089 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20090 : case FILTER_OBJECT_TYPE_TRIGGER:
20091 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20092 : "include",
20093 : filter_object_type_name(objtype));
20094 0 : exit_nicely(1);
20095 : break; /* unreachable */
20096 :
20097 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20098 2 : simple_string_list_append(&extension_include_patterns, objname);
20099 2 : break;
20100 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20101 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20102 2 : break;
20103 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20104 2 : simple_string_list_append(&schema_include_patterns, objname);
20105 2 : dopt->include_everything = false;
20106 2 : break;
20107 26 : case FILTER_OBJECT_TYPE_TABLE:
20108 26 : simple_string_list_append(&table_include_patterns, objname);
20109 26 : dopt->include_everything = false;
20110 26 : break;
20111 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20112 2 : simple_string_list_append(&table_include_patterns_and_children,
20113 : objname);
20114 2 : dopt->include_everything = false;
20115 2 : break;
20116 : }
20117 : }
20118 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20119 : {
20120 18 : switch (objtype)
20121 : {
20122 0 : case FILTER_OBJECT_TYPE_NONE:
20123 0 : break;
20124 2 : case FILTER_OBJECT_TYPE_DATABASE:
20125 : case FILTER_OBJECT_TYPE_FUNCTION:
20126 : case FILTER_OBJECT_TYPE_INDEX:
20127 : case FILTER_OBJECT_TYPE_TRIGGER:
20128 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20129 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20130 : "exclude",
20131 : filter_object_type_name(objtype));
20132 2 : exit_nicely(1);
20133 : break;
20134 :
20135 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20136 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20137 2 : break;
20138 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20139 2 : simple_string_list_append(&tabledata_exclude_patterns,
20140 : objname);
20141 2 : break;
20142 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20143 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20144 : objname);
20145 2 : break;
20146 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20147 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20148 4 : break;
20149 4 : case FILTER_OBJECT_TYPE_TABLE:
20150 4 : simple_string_list_append(&table_exclude_patterns, objname);
20151 4 : break;
20152 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20153 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20154 : objname);
20155 2 : break;
20156 : }
20157 : }
20158 : else
20159 : {
20160 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20161 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20162 : }
20163 :
20164 64 : if (objname)
20165 50 : free(objname);
20166 : }
20167 :
20168 44 : filter_free(&fstate);
20169 44 : }
|