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-2026, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_constraint_d.h"
51 : #include "catalog/pg_default_acl_d.h"
52 : #include "catalog/pg_largeobject_d.h"
53 : #include "catalog/pg_largeobject_metadata_d.h"
54 : #include "catalog/pg_proc_d.h"
55 : #include "catalog/pg_publication_d.h"
56 : #include "catalog/pg_shdepend_d.h"
57 : #include "catalog/pg_subscription_d.h"
58 : #include "catalog/pg_type_d.h"
59 : #include "common/connect.h"
60 : #include "common/int.h"
61 : #include "common/relpath.h"
62 : #include "common/shortest_dec.h"
63 : #include "compress_io.h"
64 : #include "dumputils.h"
65 : #include "fe_utils/option_utils.h"
66 : #include "fe_utils/string_utils.h"
67 : #include "filter.h"
68 : #include "getopt_long.h"
69 : #include "libpq/libpq-fs.h"
70 : #include "parallel.h"
71 : #include "pg_backup_db.h"
72 : #include "pg_backup_utils.h"
73 : #include "pg_dump.h"
74 : #include "storage/block.h"
75 :
76 : typedef struct
77 : {
78 : Oid roleoid; /* role's OID */
79 : const char *rolename; /* role's name */
80 : } RoleNameItem;
81 :
82 : typedef struct
83 : {
84 : const char *descr; /* comment for an object */
85 : Oid classoid; /* object class (catalog OID) */
86 : Oid objoid; /* object OID */
87 : int objsubid; /* subobject (table column #) */
88 : } CommentItem;
89 :
90 : typedef struct
91 : {
92 : const char *provider; /* label provider of this security label */
93 : const char *label; /* security label for an object */
94 : Oid classoid; /* object class (catalog OID) */
95 : Oid objoid; /* object OID */
96 : int objsubid; /* subobject (table column #) */
97 : } SecLabelItem;
98 :
99 : typedef struct
100 : {
101 : Oid oid; /* object OID */
102 : char relkind; /* object kind */
103 : RelFileNumber relfilenumber; /* object filenode */
104 : Oid toast_oid; /* toast table OID */
105 : RelFileNumber toast_relfilenumber; /* toast table filenode */
106 : Oid toast_index_oid; /* toast table index OID */
107 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
108 : } BinaryUpgradeClassOidItem;
109 :
110 : /* sequence types */
111 : typedef enum SeqType
112 : {
113 : SEQTYPE_SMALLINT,
114 : SEQTYPE_INTEGER,
115 : SEQTYPE_BIGINT,
116 : } SeqType;
117 :
118 : static const char *const SeqTypeNames[] =
119 : {
120 : [SEQTYPE_SMALLINT] = "smallint",
121 : [SEQTYPE_INTEGER] = "integer",
122 : [SEQTYPE_BIGINT] = "bigint",
123 : };
124 :
125 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
126 : "array length mismatch");
127 :
128 : typedef struct
129 : {
130 : Oid oid; /* sequence OID */
131 : SeqType seqtype; /* data type of sequence */
132 : bool cycled; /* whether sequence cycles */
133 : int64 minv; /* minimum value */
134 : int64 maxv; /* maximum value */
135 : int64 startv; /* start value */
136 : int64 incby; /* increment value */
137 : int64 cache; /* cache size */
138 : int64 last_value; /* last value of sequence */
139 : bool is_called; /* whether nextval advances before returning */
140 : bool null_seqtuple; /* did pg_get_sequence_data return nulls? */
141 : } SequenceItem;
142 :
143 : typedef enum OidOptions
144 : {
145 : zeroIsError = 1,
146 : zeroAsStar = 2,
147 : zeroAsNone = 4,
148 : } OidOptions;
149 :
150 : /* global decls */
151 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
152 :
153 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
154 :
155 : /* The specified names/patterns should to match at least one entity */
156 : static int strict_names = 0;
157 :
158 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
159 :
160 : /*
161 : * Object inclusion/exclusion lists
162 : *
163 : * The string lists record the patterns given by command-line switches,
164 : * which we then convert to lists of OIDs of matching objects.
165 : */
166 : static SimpleStringList schema_include_patterns = {NULL, NULL};
167 : static SimpleOidList schema_include_oids = {NULL, NULL};
168 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
169 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
170 :
171 : static SimpleStringList table_include_patterns = {NULL, NULL};
172 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
173 : static SimpleOidList table_include_oids = {NULL, NULL};
174 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
175 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
176 : static SimpleOidList table_exclude_oids = {NULL, NULL};
177 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
178 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
179 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
180 :
181 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
182 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
183 :
184 : static SimpleStringList extension_include_patterns = {NULL, NULL};
185 : static SimpleOidList extension_include_oids = {NULL, NULL};
186 :
187 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
188 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
189 :
190 : static const CatalogId nilCatalogId = {0, 0};
191 :
192 : /* override for standard extra_float_digits setting */
193 : static bool have_extra_float_digits = false;
194 : static int extra_float_digits;
195 :
196 : /* sorted table of role names */
197 : static RoleNameItem *rolenames = NULL;
198 : static int nrolenames = 0;
199 :
200 : /* sorted table of comments */
201 : static CommentItem *comments = NULL;
202 : static int ncomments = 0;
203 :
204 : /* sorted table of security labels */
205 : static SecLabelItem *seclabels = NULL;
206 : static int nseclabels = 0;
207 :
208 : /* sorted table of pg_class information for binary upgrade */
209 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
210 : static int nbinaryUpgradeClassOids = 0;
211 :
212 : /* sorted table of sequences */
213 : static SequenceItem *sequences = NULL;
214 : static int nsequences = 0;
215 :
216 : /*
217 : * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
218 : * as a dependency for pg_shdepend and any large object comments/seclabels.
219 : */
220 : static DumpId lo_metadata_dumpId;
221 :
222 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
223 : #define MAX_ATTR_STATS_RELS 64
224 :
225 : /*
226 : * The default number of rows per INSERT when
227 : * --inserts is specified without --rows-per-insert
228 : */
229 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
230 :
231 : /*
232 : * Maximum number of large objects to group into a single ArchiveEntry.
233 : * At some point we might want to make this user-controllable, but for now
234 : * a hard-wired setting will suffice.
235 : */
236 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
237 :
238 : /*
239 : * Macro for producing quoted, schema-qualified name of a dumpable object.
240 : */
241 : #define fmtQualifiedDumpable(obj) \
242 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
243 : (obj)->dobj.name)
244 :
245 : static void help(const char *progname);
246 : static void setup_connection(Archive *AH,
247 : const char *dumpencoding, const char *dumpsnapshot,
248 : char *use_role);
249 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
250 : static void expand_schema_name_patterns(Archive *fout,
251 : SimpleStringList *patterns,
252 : SimpleOidList *oids,
253 : bool strict_names);
254 : static void expand_extension_name_patterns(Archive *fout,
255 : SimpleStringList *patterns,
256 : SimpleOidList *oids,
257 : bool strict_names);
258 : static void expand_foreign_server_name_patterns(Archive *fout,
259 : SimpleStringList *patterns,
260 : SimpleOidList *oids);
261 : static void expand_table_name_patterns(Archive *fout,
262 : SimpleStringList *patterns,
263 : SimpleOidList *oids,
264 : bool strict_names,
265 : bool with_child_tables);
266 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
267 : const char *pattern);
268 :
269 : static NamespaceInfo *findNamespace(Oid nsoid);
270 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
271 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
272 : static const char *getRoleName(const char *roleoid_str);
273 : static void collectRoleNames(Archive *fout);
274 : static void getAdditionalACLs(Archive *fout);
275 : static void dumpCommentExtended(Archive *fout, const char *type,
276 : const char *name, const char *namespace,
277 : const char *owner, CatalogId catalogId,
278 : int subid, DumpId dumpId,
279 : const char *initdb_comment);
280 : static inline void dumpComment(Archive *fout, const char *type,
281 : const char *name, const char *namespace,
282 : const char *owner, CatalogId catalogId,
283 : int subid, DumpId dumpId);
284 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
285 : static void collectComments(Archive *fout);
286 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
287 : const char *namespace, const char *owner,
288 : CatalogId catalogId, int subid, DumpId dumpId);
289 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
290 : static void collectSecLabels(Archive *fout);
291 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
292 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
293 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
294 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
295 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
296 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
297 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
298 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
299 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
300 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
301 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
302 : PGresult *res);
303 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
304 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
305 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
306 : static void dumpCast(Archive *fout, const CastInfo *cast);
307 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
308 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
309 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
310 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
311 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
312 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
313 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
314 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
315 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
316 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
317 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
318 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
319 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
320 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
321 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
322 : static void collectSequences(Archive *fout);
323 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
324 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
325 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
326 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
327 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
328 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
329 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
330 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
331 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
332 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
333 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
334 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
335 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
336 : static void dumpUserMappings(Archive *fout,
337 : const char *servername, const char *namespace,
338 : const char *owner, CatalogId catalogId, DumpId dumpId);
339 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
340 :
341 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
342 : const char *type, const char *name, const char *subname,
343 : const char *nspname, const char *tag, const char *owner,
344 : const DumpableAcl *dacl);
345 :
346 : static void getDependencies(Archive *fout);
347 : static void BuildArchiveDependencies(Archive *fout);
348 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
349 : DumpId **dependencies, int *nDeps, int *allocDeps);
350 :
351 : static DumpableObject *createBoundaryObjects(void);
352 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
353 : DumpableObject *boundaryObjs);
354 :
355 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
356 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
357 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
358 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
359 : static void buildMatViewRefreshDependencies(Archive *fout);
360 : static void getTableDataFKConstraints(void);
361 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
362 : TableInfo *tbinfo, int j,
363 : int i_notnull_name,
364 : int i_notnull_comment,
365 : int i_notnull_invalidoid,
366 : int i_notnull_noinherit,
367 : int i_notnull_islocal,
368 : PQExpBuffer *invalidnotnulloids);
369 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
370 : bool is_agg);
371 : static char *format_function_signature(Archive *fout,
372 : const FuncInfo *finfo, bool honor_quotes);
373 : static char *convertRegProcReference(const char *proc);
374 : static char *getFormattedOperatorName(const char *oproid);
375 : static char *convertTSFunction(Archive *fout, Oid funcOid);
376 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
377 : static void getLOs(Archive *fout);
378 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
379 : static int dumpLOs(Archive *fout, const void *arg);
380 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
381 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
382 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
383 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
384 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
385 : static void dumpDatabase(Archive *fout);
386 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
387 : const char *dbname, Oid dboid);
388 : static void dumpEncoding(Archive *AH);
389 : static void dumpStdStrings(Archive *AH);
390 : static void dumpSearchPath(Archive *AH);
391 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
392 : PQExpBuffer upgrade_buffer,
393 : Oid pg_type_oid,
394 : bool force_array_type,
395 : bool include_multirange_type);
396 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
397 : PQExpBuffer upgrade_buffer,
398 : const TableInfo *tbinfo);
399 : static void collectBinaryUpgradeClassOids(Archive *fout);
400 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
401 : PQExpBuffer upgrade_buffer,
402 : Oid pg_class_oid);
403 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
404 : const DumpableObject *dobj,
405 : const char *objtype,
406 : const char *objname,
407 : const char *objnamespace);
408 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
409 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
410 : static bool nonemptyReloptions(const char *reloptions);
411 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
412 : const char *prefix, Archive *fout);
413 : static char *get_synchronized_snapshot(Archive *fout);
414 : static void set_restrict_relation_kind(Archive *AH, const char *value);
415 : static void setupDumpWorker(Archive *AH);
416 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
417 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
418 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
419 :
420 :
421 : int
422 0 : main(int argc, char **argv)
423 : {
424 0 : int c;
425 0 : const char *filename = NULL;
426 0 : const char *format = "p";
427 0 : TableInfo *tblinfo;
428 0 : int numTables;
429 0 : DumpableObject **dobjs;
430 0 : int numObjs;
431 0 : DumpableObject *boundaryObjs;
432 0 : int i;
433 0 : int optindex;
434 0 : RestoreOptions *ropt;
435 0 : Archive *fout; /* the script file */
436 0 : bool g_verbose = false;
437 0 : const char *dumpencoding = NULL;
438 0 : const char *dumpsnapshot = NULL;
439 0 : char *use_role = NULL;
440 0 : int numWorkers = 1;
441 0 : int plainText = 0;
442 0 : ArchiveFormat archiveFormat = archUnknown;
443 0 : ArchiveMode archiveMode;
444 0 : pg_compress_specification compression_spec = {0};
445 0 : char *compression_detail = NULL;
446 0 : char *compression_algorithm_str = "none";
447 0 : char *error_detail = NULL;
448 0 : bool user_compression_defined = false;
449 0 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
450 0 : bool data_only = false;
451 0 : bool schema_only = false;
452 0 : bool statistics_only = false;
453 0 : bool with_statistics = false;
454 0 : bool no_data = false;
455 0 : bool no_schema = false;
456 0 : bool no_statistics = false;
457 :
458 : static DumpOptions dopt;
459 :
460 : static struct option long_options[] = {
461 : {"data-only", no_argument, NULL, 'a'},
462 : {"blobs", no_argument, NULL, 'b'},
463 : {"large-objects", no_argument, NULL, 'b'},
464 : {"no-blobs", no_argument, NULL, 'B'},
465 : {"no-large-objects", no_argument, NULL, 'B'},
466 : {"clean", no_argument, NULL, 'c'},
467 : {"create", no_argument, NULL, 'C'},
468 : {"dbname", required_argument, NULL, 'd'},
469 : {"extension", required_argument, NULL, 'e'},
470 : {"file", required_argument, NULL, 'f'},
471 : {"format", required_argument, NULL, 'F'},
472 : {"host", required_argument, NULL, 'h'},
473 : {"jobs", 1, NULL, 'j'},
474 : {"no-reconnect", no_argument, NULL, 'R'},
475 : {"no-owner", no_argument, NULL, 'O'},
476 : {"port", required_argument, NULL, 'p'},
477 : {"schema", required_argument, NULL, 'n'},
478 : {"exclude-schema", required_argument, NULL, 'N'},
479 : {"schema-only", no_argument, NULL, 's'},
480 : {"superuser", required_argument, NULL, 'S'},
481 : {"table", required_argument, NULL, 't'},
482 : {"exclude-table", required_argument, NULL, 'T'},
483 : {"no-password", no_argument, NULL, 'w'},
484 : {"password", no_argument, NULL, 'W'},
485 : {"username", required_argument, NULL, 'U'},
486 : {"verbose", no_argument, NULL, 'v'},
487 : {"no-privileges", no_argument, NULL, 'x'},
488 : {"no-acl", no_argument, NULL, 'x'},
489 : {"compress", required_argument, NULL, 'Z'},
490 : {"encoding", required_argument, NULL, 'E'},
491 : {"help", no_argument, NULL, '?'},
492 : {"version", no_argument, NULL, 'V'},
493 :
494 : /*
495 : * the following options don't have an equivalent short option letter
496 : */
497 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
498 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
499 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
500 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
501 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
502 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
503 : {"exclude-table-data", required_argument, NULL, 4},
504 : {"extra-float-digits", required_argument, NULL, 8},
505 : {"if-exists", no_argument, &dopt.if_exists, 1},
506 : {"inserts", no_argument, NULL, 9},
507 : {"lock-wait-timeout", required_argument, NULL, 2},
508 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
509 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
510 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
511 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
512 : {"role", required_argument, NULL, 3},
513 : {"section", required_argument, NULL, 5},
514 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
515 : {"snapshot", required_argument, NULL, 6},
516 : {"statistics", no_argument, NULL, 22},
517 : {"statistics-only", no_argument, NULL, 18},
518 : {"strict-names", no_argument, &strict_names, 1},
519 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
520 : {"no-comments", no_argument, &dopt.no_comments, 1},
521 : {"no-data", no_argument, NULL, 19},
522 : {"no-policies", no_argument, &dopt.no_policies, 1},
523 : {"no-publications", no_argument, &dopt.no_publications, 1},
524 : {"no-schema", no_argument, NULL, 20},
525 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
526 : {"no-statistics", no_argument, NULL, 21},
527 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
528 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
529 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
530 : {"no-sync", no_argument, NULL, 7},
531 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
532 : {"rows-per-insert", required_argument, NULL, 10},
533 : {"include-foreign-data", required_argument, NULL, 11},
534 : {"table-and-children", required_argument, NULL, 12},
535 : {"exclude-table-and-children", required_argument, NULL, 13},
536 : {"exclude-table-data-and-children", required_argument, NULL, 14},
537 : {"sync-method", required_argument, NULL, 15},
538 : {"filter", required_argument, NULL, 16},
539 : {"exclude-extension", required_argument, NULL, 17},
540 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
541 : {"restrict-key", required_argument, NULL, 25},
542 :
543 : {NULL, 0, NULL, 0}
544 : };
545 :
546 0 : pg_logging_init(argv[0]);
547 0 : pg_logging_set_level(PG_LOG_WARNING);
548 0 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
549 :
550 : /*
551 : * Initialize what we need for parallel execution, especially for thread
552 : * support on Windows.
553 : */
554 0 : init_parallel_dump_utils();
555 :
556 0 : progname = get_progname(argv[0]);
557 :
558 0 : if (argc > 1)
559 : {
560 0 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
561 : {
562 0 : help(progname);
563 0 : exit_nicely(0);
564 : }
565 0 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
566 : {
567 0 : puts("pg_dump (PostgreSQL) " PG_VERSION);
568 0 : exit_nicely(0);
569 : }
570 0 : }
571 :
572 0 : InitDumpOptions(&dopt);
573 :
574 0 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
575 0 : long_options, &optindex)) != -1)
576 : {
577 0 : switch (c)
578 : {
579 : case 'a': /* Dump data only */
580 0 : data_only = true;
581 0 : break;
582 :
583 : case 'b': /* Dump LOs */
584 0 : dopt.outputLOs = true;
585 0 : break;
586 :
587 : case 'B': /* Don't dump LOs */
588 0 : dopt.dontOutputLOs = true;
589 0 : break;
590 :
591 : case 'c': /* clean (i.e., drop) schema prior to create */
592 0 : dopt.outputClean = 1;
593 0 : break;
594 :
595 : case 'C': /* Create DB */
596 0 : dopt.outputCreateDB = 1;
597 0 : break;
598 :
599 : case 'd': /* database name */
600 0 : dopt.cparams.dbname = pg_strdup(optarg);
601 0 : break;
602 :
603 : case 'e': /* include extension(s) */
604 0 : simple_string_list_append(&extension_include_patterns, optarg);
605 0 : dopt.include_everything = false;
606 0 : break;
607 :
608 : case 'E': /* Dump encoding */
609 0 : dumpencoding = pg_strdup(optarg);
610 0 : break;
611 :
612 : case 'f':
613 0 : filename = pg_strdup(optarg);
614 0 : break;
615 :
616 : case 'F':
617 0 : format = pg_strdup(optarg);
618 0 : break;
619 :
620 : case 'h': /* server host */
621 0 : dopt.cparams.pghost = pg_strdup(optarg);
622 0 : break;
623 :
624 : case 'j': /* number of dump jobs */
625 0 : if (!option_parse_int(optarg, "-j/--jobs", 1,
626 : PG_MAX_JOBS,
627 : &numWorkers))
628 0 : exit_nicely(1);
629 0 : break;
630 :
631 : case 'n': /* include schema(s) */
632 0 : simple_string_list_append(&schema_include_patterns, optarg);
633 0 : dopt.include_everything = false;
634 0 : break;
635 :
636 : case 'N': /* exclude schema(s) */
637 0 : simple_string_list_append(&schema_exclude_patterns, optarg);
638 0 : break;
639 :
640 : case 'O': /* Don't reconnect to match owner */
641 0 : dopt.outputNoOwner = 1;
642 0 : break;
643 :
644 : case 'p': /* server port */
645 0 : dopt.cparams.pgport = pg_strdup(optarg);
646 0 : break;
647 :
648 : case 'R':
649 : /* no-op, still accepted for backwards compatibility */
650 : break;
651 :
652 : case 's': /* dump schema only */
653 0 : schema_only = true;
654 0 : break;
655 :
656 : case 'S': /* Username for superuser in plain text output */
657 0 : dopt.outputSuperuser = pg_strdup(optarg);
658 0 : break;
659 :
660 : case 't': /* include table(s) */
661 0 : simple_string_list_append(&table_include_patterns, optarg);
662 0 : dopt.include_everything = false;
663 0 : break;
664 :
665 : case 'T': /* exclude table(s) */
666 0 : simple_string_list_append(&table_exclude_patterns, optarg);
667 0 : break;
668 :
669 : case 'U':
670 0 : dopt.cparams.username = pg_strdup(optarg);
671 0 : break;
672 :
673 : case 'v': /* verbose */
674 0 : g_verbose = true;
675 0 : pg_logging_increase_verbosity();
676 0 : break;
677 :
678 : case 'w':
679 0 : dopt.cparams.promptPassword = TRI_NO;
680 0 : break;
681 :
682 : case 'W':
683 0 : dopt.cparams.promptPassword = TRI_YES;
684 0 : break;
685 :
686 : case 'x': /* skip ACL dump */
687 0 : dopt.aclsSkip = true;
688 0 : break;
689 :
690 : case 'Z': /* Compression */
691 0 : parse_compress_options(optarg, &compression_algorithm_str,
692 : &compression_detail);
693 0 : user_compression_defined = true;
694 0 : break;
695 :
696 : case 0:
697 : /* This covers the long options. */
698 : break;
699 :
700 : case 2: /* lock-wait-timeout */
701 0 : dopt.lockWaitTimeout = pg_strdup(optarg);
702 0 : break;
703 :
704 : case 3: /* SET ROLE */
705 0 : use_role = pg_strdup(optarg);
706 0 : break;
707 :
708 : case 4: /* exclude table(s) data */
709 0 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
710 0 : break;
711 :
712 : case 5: /* section */
713 0 : set_dump_section(optarg, &dopt.dumpSections);
714 0 : break;
715 :
716 : case 6: /* snapshot */
717 0 : dumpsnapshot = pg_strdup(optarg);
718 0 : break;
719 :
720 : case 7: /* no-sync */
721 0 : dosync = false;
722 0 : break;
723 :
724 : case 8:
725 0 : have_extra_float_digits = true;
726 0 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
727 : &extra_float_digits))
728 0 : exit_nicely(1);
729 0 : break;
730 :
731 : case 9: /* inserts */
732 :
733 : /*
734 : * dump_inserts also stores --rows-per-insert, careful not to
735 : * overwrite that.
736 : */
737 0 : if (dopt.dump_inserts == 0)
738 0 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
739 0 : break;
740 :
741 : case 10: /* rows per insert */
742 0 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
743 : &dopt.dump_inserts))
744 0 : exit_nicely(1);
745 0 : break;
746 :
747 : case 11: /* include foreign data */
748 0 : simple_string_list_append(&foreign_servers_include_patterns,
749 0 : optarg);
750 0 : break;
751 :
752 : case 12: /* include table(s) and their children */
753 0 : simple_string_list_append(&table_include_patterns_and_children,
754 0 : optarg);
755 0 : dopt.include_everything = false;
756 0 : break;
757 :
758 : case 13: /* exclude table(s) and their children */
759 0 : simple_string_list_append(&table_exclude_patterns_and_children,
760 0 : optarg);
761 0 : break;
762 :
763 : case 14: /* exclude data of table(s) and children */
764 0 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
765 0 : optarg);
766 0 : break;
767 :
768 : case 15:
769 0 : if (!parse_sync_method(optarg, &sync_method))
770 0 : exit_nicely(1);
771 0 : break;
772 :
773 : case 16: /* read object filters from file */
774 0 : read_dump_filters(optarg, &dopt);
775 0 : break;
776 :
777 : case 17: /* exclude extension(s) */
778 0 : simple_string_list_append(&extension_exclude_patterns,
779 0 : optarg);
780 0 : break;
781 :
782 : case 18:
783 0 : statistics_only = true;
784 0 : break;
785 :
786 : case 19:
787 0 : no_data = true;
788 0 : break;
789 :
790 : case 20:
791 0 : no_schema = true;
792 0 : break;
793 :
794 : case 21:
795 0 : no_statistics = true;
796 0 : break;
797 :
798 : case 22:
799 0 : with_statistics = true;
800 0 : break;
801 :
802 : case 25:
803 0 : dopt.restrict_key = pg_strdup(optarg);
804 0 : break;
805 :
806 : default:
807 : /* getopt_long already emitted a complaint */
808 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
809 0 : exit_nicely(1);
810 : }
811 : }
812 :
813 : /*
814 : * Non-option argument specifies database name as long as it wasn't
815 : * already specified with -d / --dbname
816 : */
817 0 : if (optind < argc && dopt.cparams.dbname == NULL)
818 0 : dopt.cparams.dbname = argv[optind++];
819 :
820 : /* Complain if any arguments remain */
821 0 : if (optind < argc)
822 : {
823 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
824 : argv[optind]);
825 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
826 0 : exit_nicely(1);
827 : }
828 :
829 : /* --column-inserts implies --inserts */
830 0 : if (dopt.column_inserts && dopt.dump_inserts == 0)
831 0 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
832 :
833 : /* reject conflicting "-only" options */
834 0 : if (data_only && schema_only)
835 0 : pg_fatal("options %s and %s cannot be used together",
836 : "-s/--schema-only", "-a/--data-only");
837 0 : if (schema_only && statistics_only)
838 0 : pg_fatal("options %s and %s cannot be used together",
839 : "-s/--schema-only", "--statistics-only");
840 0 : if (data_only && statistics_only)
841 0 : pg_fatal("options %s and %s cannot be used together",
842 : "-a/--data-only", "--statistics-only");
843 :
844 : /* reject conflicting "-only" and "no-" options */
845 0 : if (data_only && no_data)
846 0 : pg_fatal("options %s and %s cannot be used together",
847 : "-a/--data-only", "--no-data");
848 0 : if (schema_only && no_schema)
849 0 : pg_fatal("options %s and %s cannot be used together",
850 : "-s/--schema-only", "--no-schema");
851 0 : if (statistics_only && no_statistics)
852 0 : pg_fatal("options %s and %s cannot be used together",
853 : "--statistics-only", "--no-statistics");
854 :
855 : /* reject conflicting "no-" options */
856 0 : if (with_statistics && no_statistics)
857 0 : pg_fatal("options %s and %s cannot be used together",
858 : "--statistics", "--no-statistics");
859 :
860 : /* reject conflicting "-only" options */
861 0 : if (data_only && with_statistics)
862 0 : pg_fatal("options %s and %s cannot be used together",
863 : "-a/--data-only", "--statistics");
864 0 : if (schema_only && with_statistics)
865 0 : pg_fatal("options %s and %s cannot be used together",
866 : "-s/--schema-only", "--statistics");
867 :
868 0 : if (schema_only && foreign_servers_include_patterns.head != NULL)
869 0 : pg_fatal("options %s and %s cannot be used together",
870 : "-s/--schema-only", "--include-foreign-data");
871 :
872 0 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
873 0 : pg_fatal("option %s is not supported with parallel backup",
874 : "--include-foreign-data");
875 :
876 0 : if (data_only && dopt.outputClean)
877 0 : pg_fatal("options %s and %s cannot be used together",
878 : "-c/--clean", "-a/--data-only");
879 :
880 0 : if (dopt.if_exists && !dopt.outputClean)
881 0 : pg_fatal("option %s requires option %s",
882 : "--if-exists", "-c/--clean");
883 :
884 : /*
885 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
886 : * "--schema-only --no-schema", will have already caused an error in one
887 : * of the checks above.
888 : */
889 0 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
890 0 : data_only) && !no_data;
891 0 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
892 0 : schema_only) && !no_schema;
893 0 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
894 0 : (statistics_only || with_statistics)) && !no_statistics;
895 :
896 :
897 : /*
898 : * --inserts are already implied above if --column-inserts or
899 : * --rows-per-insert were specified.
900 : */
901 0 : if (dopt.do_nothing && dopt.dump_inserts == 0)
902 0 : pg_fatal("option %s requires option %s, %s, or %s",
903 : "--on-conflict-do-nothing",
904 : "--inserts", "--rows-per-insert", "--column-inserts");
905 :
906 : /* Identify archive format to emit */
907 0 : archiveFormat = parseArchiveFormat(format, &archiveMode);
908 :
909 : /* archiveFormat specific setup */
910 0 : if (archiveFormat == archNull)
911 : {
912 0 : plainText = 1;
913 :
914 : /*
915 : * If you don't provide a restrict key, one will be appointed for you.
916 : */
917 0 : if (!dopt.restrict_key)
918 0 : dopt.restrict_key = generate_restrict_key();
919 0 : if (!dopt.restrict_key)
920 0 : pg_fatal("could not generate restrict key");
921 0 : if (!valid_restrict_key(dopt.restrict_key))
922 0 : pg_fatal("invalid restrict key");
923 0 : }
924 0 : else if (dopt.restrict_key)
925 0 : pg_fatal("option %s can only be used with %s",
926 : "--restrict-key", "--format=plain");
927 :
928 : /*
929 : * Custom and directory formats are compressed by default with gzip when
930 : * available, not the others. If gzip is not available, no compression is
931 : * done by default.
932 : */
933 0 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
934 0 : !user_compression_defined)
935 : {
936 : #ifdef HAVE_LIBZ
937 0 : compression_algorithm_str = "gzip";
938 : #else
939 : compression_algorithm_str = "none";
940 : #endif
941 0 : }
942 :
943 : /*
944 : * Compression options
945 : */
946 0 : if (!parse_compress_algorithm(compression_algorithm_str,
947 : &compression_algorithm))
948 0 : pg_fatal("unrecognized compression algorithm: \"%s\"",
949 : compression_algorithm_str);
950 :
951 0 : parse_compress_specification(compression_algorithm, compression_detail,
952 : &compression_spec);
953 0 : error_detail = validate_compress_specification(&compression_spec);
954 0 : if (error_detail != NULL)
955 0 : pg_fatal("invalid compression specification: %s",
956 : error_detail);
957 :
958 0 : error_detail = supports_compression(compression_spec);
959 0 : if (error_detail != NULL)
960 0 : pg_fatal("%s", error_detail);
961 :
962 : /*
963 : * Disable support for zstd workers for now - these are based on
964 : * threading, and it's unclear how it interacts with parallel dumps on
965 : * platforms where that relies on threads too (e.g. Windows).
966 : */
967 0 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
968 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
969 : "workers");
970 :
971 : /*
972 : * If emitting an archive format, we always want to emit a DATABASE item,
973 : * in case --create is specified at pg_restore time.
974 : */
975 0 : if (!plainText)
976 0 : dopt.outputCreateDB = 1;
977 :
978 : /* Parallel backup only in the directory archive format so far */
979 0 : if (archiveFormat != archDirectory && numWorkers > 1)
980 0 : pg_fatal("parallel backup only supported by the directory format");
981 :
982 : /* Open the output file */
983 0 : fout = CreateArchive(filename, archiveFormat, compression_spec,
984 0 : dosync, archiveMode, setupDumpWorker, sync_method);
985 :
986 : /* Make dump options accessible right away */
987 0 : SetArchiveOptions(fout, &dopt, NULL);
988 :
989 : /* Register the cleanup hook */
990 0 : on_exit_close_archive(fout);
991 :
992 : /* Let the archiver know how noisy to be */
993 0 : fout->verbose = g_verbose;
994 :
995 :
996 : /*
997 : * We allow the server to be back to 9.2, and up to any minor release of
998 : * our own major version. (See also version check in pg_dumpall.c.)
999 : */
1000 0 : fout->minRemoteVersion = 90200;
1001 0 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
1002 :
1003 0 : fout->numWorkers = numWorkers;
1004 :
1005 : /*
1006 : * Open the database using the Archiver, so it knows about it. Errors mean
1007 : * death.
1008 : */
1009 0 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
1010 0 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
1011 :
1012 : /*
1013 : * On hot standbys, never try to dump unlogged table data, since it will
1014 : * just throw an error.
1015 : */
1016 0 : if (fout->isStandby)
1017 0 : dopt.no_unlogged_table_data = true;
1018 :
1019 : /*
1020 : * Find the last built-in OID, if needed (prior to 8.1)
1021 : *
1022 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1023 : */
1024 0 : g_last_builtin_oid = FirstNormalObjectId - 1;
1025 :
1026 0 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1027 :
1028 : /* Expand schema selection patterns into OID lists */
1029 0 : if (schema_include_patterns.head != NULL)
1030 : {
1031 0 : expand_schema_name_patterns(fout, &schema_include_patterns,
1032 : &schema_include_oids,
1033 0 : strict_names);
1034 0 : if (schema_include_oids.head == NULL)
1035 0 : pg_fatal("no matching schemas were found");
1036 0 : }
1037 0 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1038 : &schema_exclude_oids,
1039 : false);
1040 : /* non-matching exclusion patterns aren't an error */
1041 :
1042 : /* Expand table selection patterns into OID lists */
1043 0 : expand_table_name_patterns(fout, &table_include_patterns,
1044 : &table_include_oids,
1045 0 : strict_names, false);
1046 0 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1047 : &table_include_oids,
1048 0 : strict_names, true);
1049 0 : if ((table_include_patterns.head != NULL ||
1050 0 : table_include_patterns_and_children.head != NULL) &&
1051 0 : table_include_oids.head == NULL)
1052 0 : pg_fatal("no matching tables were found");
1053 :
1054 0 : expand_table_name_patterns(fout, &table_exclude_patterns,
1055 : &table_exclude_oids,
1056 : false, false);
1057 0 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1058 : &table_exclude_oids,
1059 : false, true);
1060 :
1061 0 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1062 : &tabledata_exclude_oids,
1063 : false, false);
1064 0 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1065 : &tabledata_exclude_oids,
1066 : false, true);
1067 :
1068 0 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1069 : &foreign_servers_include_oids);
1070 :
1071 : /* non-matching exclusion patterns aren't an error */
1072 :
1073 : /* Expand extension selection patterns into OID lists */
1074 0 : if (extension_include_patterns.head != NULL)
1075 : {
1076 0 : expand_extension_name_patterns(fout, &extension_include_patterns,
1077 : &extension_include_oids,
1078 0 : strict_names);
1079 0 : if (extension_include_oids.head == NULL)
1080 0 : pg_fatal("no matching extensions were found");
1081 0 : }
1082 0 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1083 : &extension_exclude_oids,
1084 : false);
1085 : /* non-matching exclusion patterns aren't an error */
1086 :
1087 : /*
1088 : * Dumping LOs is the default for dumps where an inclusion switch is not
1089 : * used (an "include everything" dump). -B can be used to exclude LOs
1090 : * from those dumps. -b can be used to include LOs even when an inclusion
1091 : * switch is used.
1092 : *
1093 : * -s means "schema only" and LOs are data, not schema, so we never
1094 : * include LOs when -s is used.
1095 : */
1096 0 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1097 0 : dopt.outputLOs = true;
1098 :
1099 : /*
1100 : * Collect role names so we can map object owner OIDs to names.
1101 : */
1102 0 : collectRoleNames(fout);
1103 :
1104 : /*
1105 : * Now scan the database and create DumpableObject structs for all the
1106 : * objects we intend to dump.
1107 : */
1108 0 : tblinfo = getSchemaData(fout, &numTables);
1109 :
1110 0 : if (dopt.dumpData)
1111 : {
1112 0 : getTableData(&dopt, tblinfo, numTables, 0);
1113 0 : buildMatViewRefreshDependencies(fout);
1114 0 : if (!dopt.dumpSchema)
1115 0 : getTableDataFKConstraints();
1116 0 : }
1117 :
1118 0 : if (!dopt.dumpData && dopt.sequence_data)
1119 0 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1120 :
1121 : /*
1122 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1123 : * associated pg_shdepend rows. This is faster to restore than the
1124 : * equivalent set of large object commands. We can only do this for
1125 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1126 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1127 : */
1128 0 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1129 : {
1130 0 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1131 0 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1132 :
1133 0 : makeTableDataInfo(&dopt, lo_metadata);
1134 0 : makeTableDataInfo(&dopt, shdepend);
1135 :
1136 : /*
1137 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1138 : * pg_shdepend and any large object comments/seclabels.
1139 : */
1140 0 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1141 0 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1142 :
1143 : /*
1144 : * Only dump large object shdepend rows for this database.
1145 : */
1146 0 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1147 : "AND dbid = (SELECT oid FROM pg_database "
1148 : " WHERE datname = current_database())";
1149 :
1150 : /*
1151 : * If upgrading from v16 or newer, only dump large objects with
1152 : * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1153 : * pg_largeobject_metadata's files (which is usually faster) but we
1154 : * still need to dump LOs with comments/seclabels here so that the
1155 : * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1156 : * can't copy/link the files from older versions because aclitem
1157 : * (needed by pg_largeobject_metadata.lomacl) changed its storage
1158 : * format in v16.
1159 : */
1160 0 : if (fout->remoteVersion >= 160000)
1161 0 : lo_metadata->dataObj->filtercond = "WHERE oid IN "
1162 : "(SELECT objoid FROM pg_description "
1163 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1164 : "UNION SELECT objoid FROM pg_seclabel "
1165 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1166 0 : }
1167 :
1168 : /*
1169 : * In binary-upgrade mode, we do not have to worry about the actual LO
1170 : * data or the associated metadata that resides in the pg_largeobject and
1171 : * pg_largeobject_metadata tables, respectively.
1172 : *
1173 : * However, we do need to collect LO information as there may be comments
1174 : * or other information on LOs that we do need to dump out.
1175 : */
1176 0 : if (dopt.outputLOs || dopt.binary_upgrade)
1177 0 : getLOs(fout);
1178 :
1179 : /*
1180 : * Collect dependency data to assist in ordering the objects.
1181 : */
1182 0 : getDependencies(fout);
1183 :
1184 : /*
1185 : * Collect ACLs, comments, and security labels, if wanted.
1186 : */
1187 0 : if (!dopt.aclsSkip)
1188 0 : getAdditionalACLs(fout);
1189 0 : if (!dopt.no_comments)
1190 0 : collectComments(fout);
1191 0 : if (!dopt.no_security_labels)
1192 0 : collectSecLabels(fout);
1193 :
1194 : /* For binary upgrade mode, collect required pg_class information. */
1195 0 : if (dopt.binary_upgrade)
1196 0 : collectBinaryUpgradeClassOids(fout);
1197 :
1198 : /* Collect sequence information. */
1199 0 : collectSequences(fout);
1200 :
1201 : /* Lastly, create dummy objects to represent the section boundaries */
1202 0 : boundaryObjs = createBoundaryObjects();
1203 :
1204 : /* Get pointers to all the known DumpableObjects */
1205 0 : getDumpableObjects(&dobjs, &numObjs);
1206 :
1207 : /*
1208 : * Add dummy dependencies to enforce the dump section ordering.
1209 : */
1210 0 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1211 :
1212 : /*
1213 : * Sort the objects into a safe dump order (no forward references).
1214 : *
1215 : * We rely on dependency information to help us determine a safe order, so
1216 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1217 : * ensure that logically identical schemas will dump identically.
1218 : */
1219 0 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1220 :
1221 0 : sortDumpableObjects(dobjs, numObjs,
1222 0 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1223 :
1224 : /*
1225 : * Create archive TOC entries for all the objects to be dumped, in a safe
1226 : * order.
1227 : */
1228 :
1229 : /*
1230 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1231 : */
1232 0 : dumpEncoding(fout);
1233 0 : dumpStdStrings(fout);
1234 0 : dumpSearchPath(fout);
1235 :
1236 : /* The database items are always next, unless we don't want them at all */
1237 0 : if (dopt.outputCreateDB)
1238 0 : dumpDatabase(fout);
1239 :
1240 : /* Now the rearrangeable objects. */
1241 0 : for (i = 0; i < numObjs; i++)
1242 0 : dumpDumpableObject(fout, dobjs[i]);
1243 :
1244 : /*
1245 : * Set up options info to ensure we dump what we want.
1246 : */
1247 0 : ropt = NewRestoreOptions();
1248 0 : ropt->filename = filename;
1249 :
1250 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1251 0 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1252 0 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1253 0 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1254 0 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1255 0 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1256 0 : ropt->dropSchema = dopt.outputClean;
1257 0 : ropt->dumpData = dopt.dumpData;
1258 0 : ropt->dumpSchema = dopt.dumpSchema;
1259 0 : ropt->dumpStatistics = dopt.dumpStatistics;
1260 0 : ropt->if_exists = dopt.if_exists;
1261 0 : ropt->column_inserts = dopt.column_inserts;
1262 0 : ropt->dumpSections = dopt.dumpSections;
1263 0 : ropt->aclsSkip = dopt.aclsSkip;
1264 0 : ropt->superuser = dopt.outputSuperuser;
1265 0 : ropt->createDB = dopt.outputCreateDB;
1266 0 : ropt->noOwner = dopt.outputNoOwner;
1267 0 : ropt->noTableAm = dopt.outputNoTableAm;
1268 0 : ropt->noTablespace = dopt.outputNoTablespaces;
1269 0 : ropt->disable_triggers = dopt.disable_triggers;
1270 0 : ropt->use_setsessauth = dopt.use_setsessauth;
1271 0 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1272 0 : ropt->dump_inserts = dopt.dump_inserts;
1273 0 : ropt->no_comments = dopt.no_comments;
1274 0 : ropt->no_policies = dopt.no_policies;
1275 0 : ropt->no_publications = dopt.no_publications;
1276 0 : ropt->no_security_labels = dopt.no_security_labels;
1277 0 : ropt->no_subscriptions = dopt.no_subscriptions;
1278 0 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1279 0 : ropt->include_everything = dopt.include_everything;
1280 0 : ropt->enable_row_security = dopt.enable_row_security;
1281 0 : ropt->sequence_data = dopt.sequence_data;
1282 0 : ropt->binary_upgrade = dopt.binary_upgrade;
1283 0 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1284 :
1285 0 : ropt->compression_spec = compression_spec;
1286 :
1287 0 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1288 :
1289 0 : SetArchiveOptions(fout, &dopt, ropt);
1290 :
1291 : /* Mark which entries should be output */
1292 0 : ProcessArchiveRestoreOptions(fout);
1293 :
1294 : /*
1295 : * The archive's TOC entries are now marked as to which ones will actually
1296 : * be output, so we can set up their dependency lists properly. This isn't
1297 : * necessary for plain-text output, though.
1298 : */
1299 0 : if (!plainText)
1300 0 : BuildArchiveDependencies(fout);
1301 :
1302 : /*
1303 : * And finally we can do the actual output.
1304 : *
1305 : * Note: for non-plain-text output formats, the output file is written
1306 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1307 : * right now.
1308 : */
1309 0 : if (plainText)
1310 0 : RestoreArchive(fout);
1311 :
1312 0 : CloseArchive(fout);
1313 :
1314 0 : exit_nicely(0);
1315 : }
1316 :
1317 :
1318 : static void
1319 0 : help(const char *progname)
1320 : {
1321 0 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1322 0 : printf(_("Usage:\n"));
1323 0 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1324 :
1325 0 : printf(_("\nGeneral options:\n"));
1326 0 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1327 0 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1328 : " plain text (default))\n"));
1329 0 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1330 0 : printf(_(" -v, --verbose verbose mode\n"));
1331 0 : printf(_(" -V, --version output version information, then exit\n"));
1332 0 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1333 : " compress as specified\n"));
1334 0 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1335 0 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1336 0 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1337 0 : printf(_(" -?, --help show this help, then exit\n"));
1338 :
1339 0 : printf(_("\nOptions controlling the output content:\n"));
1340 0 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1341 0 : printf(_(" -b, --large-objects include large objects in dump\n"));
1342 0 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1343 0 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1344 0 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1345 0 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1346 0 : printf(_(" -C, --create include commands to create database in dump\n"));
1347 0 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1348 0 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1349 0 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1350 0 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1351 0 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1352 : " plain-text format\n"));
1353 0 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1354 0 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1355 0 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1356 0 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1357 0 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1358 0 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1359 0 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1360 0 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1361 0 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1362 0 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1363 : " access to)\n"));
1364 0 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1365 0 : printf(_(" --exclude-table-and-children=PATTERN\n"
1366 : " do NOT dump the specified table(s), including\n"
1367 : " child and partition tables\n"));
1368 0 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1369 0 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1370 : " do NOT dump data for the specified table(s),\n"
1371 : " including child and partition tables\n"));
1372 0 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1373 0 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1374 : " based on expressions in FILENAME\n"));
1375 0 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1376 0 : printf(_(" --include-foreign-data=PATTERN\n"
1377 : " include data of foreign tables on foreign\n"
1378 : " servers matching PATTERN\n"));
1379 0 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1380 0 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1381 0 : printf(_(" --no-comments do not dump comment commands\n"));
1382 0 : printf(_(" --no-data do not dump data\n"));
1383 0 : printf(_(" --no-policies do not dump row security policies\n"));
1384 0 : printf(_(" --no-publications do not dump publications\n"));
1385 0 : printf(_(" --no-schema do not dump schema\n"));
1386 0 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1387 0 : printf(_(" --no-statistics do not dump statistics\n"));
1388 0 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1389 0 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1390 0 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1391 0 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1392 0 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1393 0 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1394 0 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1395 0 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1396 0 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1397 0 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1398 0 : printf(_(" --sequence-data include sequence data in dump\n"));
1399 0 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1400 0 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1401 0 : printf(_(" --statistics dump the statistics\n"));
1402 0 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1403 0 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1404 : " match at least one entity each\n"));
1405 0 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1406 : " child and partition tables\n"));
1407 0 : printf(_(" --use-set-session-authorization\n"
1408 : " use SET SESSION AUTHORIZATION commands instead of\n"
1409 : " ALTER OWNER commands to set ownership\n"));
1410 :
1411 0 : printf(_("\nConnection options:\n"));
1412 0 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1413 0 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1414 0 : printf(_(" -p, --port=PORT database server port number\n"));
1415 0 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1416 0 : printf(_(" -w, --no-password never prompt for password\n"));
1417 0 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1418 0 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1419 :
1420 0 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1421 : "variable value is used.\n\n"));
1422 0 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1423 0 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1424 0 : }
1425 :
1426 : static void
1427 0 : setup_connection(Archive *AH, const char *dumpencoding,
1428 : const char *dumpsnapshot, char *use_role)
1429 : {
1430 0 : DumpOptions *dopt = AH->dopt;
1431 0 : PGconn *conn = GetConnection(AH);
1432 :
1433 0 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1434 :
1435 : /*
1436 : * Set the client encoding if requested.
1437 : */
1438 0 : if (dumpencoding)
1439 : {
1440 0 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1441 0 : pg_fatal("invalid client encoding \"%s\" specified",
1442 : dumpencoding);
1443 0 : }
1444 :
1445 : /*
1446 : * Force standard_conforming_strings on, just in case we are dumping from
1447 : * an old server that has it disabled. Without this, literals in views,
1448 : * expressions, etc, would be incorrect for modern servers.
1449 : */
1450 0 : ExecuteSqlStatement(AH, "SET standard_conforming_strings = on");
1451 :
1452 : /*
1453 : * And reflect that to AH->std_strings. You might think that we should
1454 : * just delete that variable and the code that checks it, but that would
1455 : * be problematic for pg_restore, which at least for now should still cope
1456 : * with archives containing the other setting (cf. processStdStringsEntry
1457 : * in pg_backup_archiver.c).
1458 : */
1459 0 : AH->std_strings = true;
1460 :
1461 : /*
1462 : * Get the active encoding, so we know how to escape strings.
1463 : */
1464 0 : AH->encoding = PQclientEncoding(conn);
1465 0 : setFmtEncoding(AH->encoding);
1466 :
1467 : /*
1468 : * Set the role if requested. In a parallel dump worker, we'll be passed
1469 : * use_role == NULL, but AH->use_role is already set (if user specified it
1470 : * originally) and we should use that.
1471 : */
1472 0 : if (!use_role && AH->use_role)
1473 0 : use_role = AH->use_role;
1474 :
1475 : /* Set the role if requested */
1476 0 : if (use_role)
1477 : {
1478 0 : PQExpBuffer query = createPQExpBuffer();
1479 :
1480 0 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1481 0 : ExecuteSqlStatement(AH, query->data);
1482 0 : destroyPQExpBuffer(query);
1483 :
1484 : /* save it for possible later use by parallel workers */
1485 0 : if (!AH->use_role)
1486 0 : AH->use_role = pg_strdup(use_role);
1487 0 : }
1488 :
1489 : /* Set the datestyle to ISO to ensure the dump's portability */
1490 0 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1491 :
1492 : /* Likewise, avoid using sql_standard intervalstyle */
1493 0 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1494 :
1495 : /*
1496 : * Use an explicitly specified extra_float_digits if it has been provided.
1497 : * Otherwise, set extra_float_digits so that we can dump float data
1498 : * exactly (given correctly implemented float I/O code, anyway).
1499 : */
1500 0 : if (have_extra_float_digits)
1501 : {
1502 0 : PQExpBuffer q = createPQExpBuffer();
1503 :
1504 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1505 0 : extra_float_digits);
1506 0 : ExecuteSqlStatement(AH, q->data);
1507 0 : destroyPQExpBuffer(q);
1508 0 : }
1509 : else
1510 0 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1511 :
1512 : /*
1513 : * Disable synchronized scanning, to prevent unpredictable changes in row
1514 : * ordering across a dump and reload.
1515 : */
1516 0 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1517 :
1518 : /*
1519 : * Disable timeouts if supported.
1520 : */
1521 0 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1522 0 : if (AH->remoteVersion >= 90300)
1523 0 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1524 0 : if (AH->remoteVersion >= 90600)
1525 0 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1526 0 : if (AH->remoteVersion >= 170000)
1527 0 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1528 :
1529 : /*
1530 : * Quote all identifiers, if requested.
1531 : */
1532 0 : if (quote_all_identifiers)
1533 0 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1534 :
1535 : /*
1536 : * Adjust row-security mode, if supported.
1537 : */
1538 0 : if (AH->remoteVersion >= 90500)
1539 : {
1540 0 : if (dopt->enable_row_security)
1541 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1542 : else
1543 0 : ExecuteSqlStatement(AH, "SET row_security = off");
1544 0 : }
1545 :
1546 : /*
1547 : * For security reasons, we restrict the expansion of non-system views and
1548 : * access to foreign tables during the pg_dump process. This restriction
1549 : * is adjusted when dumping foreign table data.
1550 : */
1551 0 : set_restrict_relation_kind(AH, "view, foreign-table");
1552 :
1553 : /*
1554 : * Initialize prepared-query state to "nothing prepared". We do this here
1555 : * so that a parallel dump worker will have its own state.
1556 : */
1557 0 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1558 :
1559 : /*
1560 : * Start transaction-snapshot mode transaction to dump consistent data.
1561 : */
1562 0 : ExecuteSqlStatement(AH, "BEGIN");
1563 :
1564 : /*
1565 : * To support the combination of serializable_deferrable with the jobs
1566 : * option we use REPEATABLE READ for the worker connections that are
1567 : * passed a snapshot. As long as the snapshot is acquired in a
1568 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1569 : * REPEATABLE READ transaction provides the appropriate integrity
1570 : * guarantees. This is a kluge, but safe for back-patching.
1571 : */
1572 0 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1573 0 : ExecuteSqlStatement(AH,
1574 : "SET TRANSACTION ISOLATION LEVEL "
1575 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1576 : else
1577 0 : ExecuteSqlStatement(AH,
1578 : "SET TRANSACTION ISOLATION LEVEL "
1579 : "REPEATABLE READ, READ ONLY");
1580 :
1581 : /*
1582 : * If user specified a snapshot to use, select that. In a parallel dump
1583 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1584 : * is already set (if the server can handle it) and we should use that.
1585 : */
1586 0 : if (dumpsnapshot)
1587 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1588 :
1589 0 : if (AH->sync_snapshot_id)
1590 : {
1591 0 : PQExpBuffer query = createPQExpBuffer();
1592 :
1593 0 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1594 0 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1595 0 : ExecuteSqlStatement(AH, query->data);
1596 0 : destroyPQExpBuffer(query);
1597 0 : }
1598 0 : else if (AH->numWorkers > 1)
1599 : {
1600 0 : if (AH->isStandby && AH->remoteVersion < 100000)
1601 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1602 0 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1603 0 : }
1604 0 : }
1605 :
1606 : /* Set up connection for a parallel worker process */
1607 : static void
1608 0 : setupDumpWorker(Archive *AH)
1609 : {
1610 : /*
1611 : * We want to re-select all the same values the leader connection is
1612 : * using. We'll have inherited directly-usable values in
1613 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1614 : * inherited encoding value back to a string to pass to setup_connection.
1615 : */
1616 0 : setup_connection(AH,
1617 0 : pg_encoding_to_char(AH->encoding),
1618 : NULL,
1619 : NULL);
1620 0 : }
1621 :
1622 : static char *
1623 0 : get_synchronized_snapshot(Archive *fout)
1624 : {
1625 0 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1626 0 : char *result;
1627 0 : PGresult *res;
1628 :
1629 0 : res = ExecuteSqlQueryForSingleRow(fout, query);
1630 0 : result = pg_strdup(PQgetvalue(res, 0, 0));
1631 0 : PQclear(res);
1632 :
1633 0 : return result;
1634 0 : }
1635 :
1636 : static ArchiveFormat
1637 0 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1638 : {
1639 0 : ArchiveFormat archiveFormat;
1640 :
1641 0 : *mode = archModeWrite;
1642 :
1643 0 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1644 : {
1645 : /* This is used by pg_dumpall, and is not documented */
1646 0 : archiveFormat = archNull;
1647 0 : *mode = archModeAppend;
1648 0 : }
1649 0 : else if (pg_strcasecmp(format, "c") == 0)
1650 0 : archiveFormat = archCustom;
1651 0 : else if (pg_strcasecmp(format, "custom") == 0)
1652 0 : archiveFormat = archCustom;
1653 0 : else if (pg_strcasecmp(format, "d") == 0)
1654 0 : archiveFormat = archDirectory;
1655 0 : else if (pg_strcasecmp(format, "directory") == 0)
1656 0 : archiveFormat = archDirectory;
1657 0 : else if (pg_strcasecmp(format, "p") == 0)
1658 0 : archiveFormat = archNull;
1659 0 : else if (pg_strcasecmp(format, "plain") == 0)
1660 0 : archiveFormat = archNull;
1661 0 : else if (pg_strcasecmp(format, "t") == 0)
1662 0 : archiveFormat = archTar;
1663 0 : else if (pg_strcasecmp(format, "tar") == 0)
1664 0 : archiveFormat = archTar;
1665 : else
1666 0 : pg_fatal("invalid output format \"%s\" specified", format);
1667 0 : return archiveFormat;
1668 0 : }
1669 :
1670 : /*
1671 : * Find the OIDs of all schemas matching the given list of patterns,
1672 : * and append them to the given OID list.
1673 : */
1674 : static void
1675 0 : expand_schema_name_patterns(Archive *fout,
1676 : SimpleStringList *patterns,
1677 : SimpleOidList *oids,
1678 : bool strict_names)
1679 : {
1680 0 : PQExpBuffer query;
1681 0 : PGresult *res;
1682 0 : SimpleStringListCell *cell;
1683 0 : int i;
1684 :
1685 0 : if (patterns->head == NULL)
1686 0 : return; /* nothing to do */
1687 :
1688 0 : query = createPQExpBuffer();
1689 :
1690 : /*
1691 : * The loop below runs multiple SELECTs might sometimes result in
1692 : * duplicate entries in the OID list, but we don't care.
1693 : */
1694 :
1695 0 : for (cell = patterns->head; cell; cell = cell->next)
1696 : {
1697 0 : PQExpBufferData dbbuf;
1698 0 : int dotcnt;
1699 :
1700 0 : appendPQExpBufferStr(query,
1701 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1702 0 : initPQExpBuffer(&dbbuf);
1703 0 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1704 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1705 : &dotcnt);
1706 0 : if (dotcnt > 1)
1707 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1708 : cell->val);
1709 0 : else if (dotcnt == 1)
1710 0 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1711 0 : termPQExpBuffer(&dbbuf);
1712 :
1713 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1714 0 : if (strict_names && PQntuples(res) == 0)
1715 0 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1716 :
1717 0 : for (i = 0; i < PQntuples(res); i++)
1718 : {
1719 0 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1720 0 : }
1721 :
1722 0 : PQclear(res);
1723 0 : resetPQExpBuffer(query);
1724 0 : }
1725 :
1726 0 : destroyPQExpBuffer(query);
1727 0 : }
1728 :
1729 : /*
1730 : * Find the OIDs of all extensions matching the given list of patterns,
1731 : * and append them to the given OID list.
1732 : */
1733 : static void
1734 0 : expand_extension_name_patterns(Archive *fout,
1735 : SimpleStringList *patterns,
1736 : SimpleOidList *oids,
1737 : bool strict_names)
1738 : {
1739 0 : PQExpBuffer query;
1740 0 : PGresult *res;
1741 0 : SimpleStringListCell *cell;
1742 0 : int i;
1743 :
1744 0 : if (patterns->head == NULL)
1745 0 : return; /* nothing to do */
1746 :
1747 0 : query = createPQExpBuffer();
1748 :
1749 : /*
1750 : * The loop below runs multiple SELECTs might sometimes result in
1751 : * duplicate entries in the OID list, but we don't care.
1752 : */
1753 0 : for (cell = patterns->head; cell; cell = cell->next)
1754 : {
1755 0 : int dotcnt;
1756 :
1757 0 : appendPQExpBufferStr(query,
1758 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1759 0 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1760 : false, NULL, "e.extname", NULL, NULL, NULL,
1761 : &dotcnt);
1762 0 : if (dotcnt > 0)
1763 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1764 : cell->val);
1765 :
1766 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1767 0 : if (strict_names && PQntuples(res) == 0)
1768 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1769 :
1770 0 : for (i = 0; i < PQntuples(res); i++)
1771 : {
1772 0 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1773 0 : }
1774 :
1775 0 : PQclear(res);
1776 0 : resetPQExpBuffer(query);
1777 0 : }
1778 :
1779 0 : destroyPQExpBuffer(query);
1780 0 : }
1781 :
1782 : /*
1783 : * Find the OIDs of all foreign servers matching the given list of patterns,
1784 : * and append them to the given OID list.
1785 : */
1786 : static void
1787 0 : expand_foreign_server_name_patterns(Archive *fout,
1788 : SimpleStringList *patterns,
1789 : SimpleOidList *oids)
1790 : {
1791 0 : PQExpBuffer query;
1792 0 : PGresult *res;
1793 0 : SimpleStringListCell *cell;
1794 0 : int i;
1795 :
1796 0 : if (patterns->head == NULL)
1797 0 : return; /* nothing to do */
1798 :
1799 0 : query = createPQExpBuffer();
1800 :
1801 : /*
1802 : * The loop below runs multiple SELECTs might sometimes result in
1803 : * duplicate entries in the OID list, but we don't care.
1804 : */
1805 :
1806 0 : for (cell = patterns->head; cell; cell = cell->next)
1807 : {
1808 0 : int dotcnt;
1809 :
1810 0 : appendPQExpBufferStr(query,
1811 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1812 0 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1813 : false, NULL, "s.srvname", NULL, NULL, NULL,
1814 : &dotcnt);
1815 0 : if (dotcnt > 0)
1816 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1817 : cell->val);
1818 :
1819 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1820 0 : if (PQntuples(res) == 0)
1821 0 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1822 :
1823 0 : for (i = 0; i < PQntuples(res); i++)
1824 0 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1825 :
1826 0 : PQclear(res);
1827 0 : resetPQExpBuffer(query);
1828 0 : }
1829 :
1830 0 : destroyPQExpBuffer(query);
1831 0 : }
1832 :
1833 : /*
1834 : * Find the OIDs of all tables matching the given list of patterns,
1835 : * and append them to the given OID list. See also expand_dbname_patterns()
1836 : * in pg_dumpall.c
1837 : */
1838 : static void
1839 0 : expand_table_name_patterns(Archive *fout,
1840 : SimpleStringList *patterns, SimpleOidList *oids,
1841 : bool strict_names, bool with_child_tables)
1842 : {
1843 0 : PQExpBuffer query;
1844 0 : PGresult *res;
1845 0 : SimpleStringListCell *cell;
1846 0 : int i;
1847 :
1848 0 : if (patterns->head == NULL)
1849 0 : return; /* nothing to do */
1850 :
1851 0 : query = createPQExpBuffer();
1852 :
1853 : /*
1854 : * this might sometimes result in duplicate entries in the OID list, but
1855 : * we don't care.
1856 : */
1857 :
1858 0 : for (cell = patterns->head; cell; cell = cell->next)
1859 : {
1860 0 : PQExpBufferData dbbuf;
1861 0 : int dotcnt;
1862 :
1863 : /*
1864 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1865 : * would be unnecessary given a pg_table_is_visible() variant taking a
1866 : * search_path argument.
1867 : *
1868 : * For with_child_tables, we start with the basic query's results and
1869 : * recursively search the inheritance tree to add child tables.
1870 : */
1871 0 : if (with_child_tables)
1872 : {
1873 0 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1874 0 : }
1875 :
1876 0 : appendPQExpBuffer(query,
1877 : "SELECT c.oid"
1878 : "\nFROM pg_catalog.pg_class c"
1879 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1880 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1881 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1882 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1883 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1884 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1885 : RELKIND_PARTITIONED_TABLE);
1886 0 : initPQExpBuffer(&dbbuf);
1887 0 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1888 : false, "n.nspname", "c.relname", NULL,
1889 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1890 : &dotcnt);
1891 0 : if (dotcnt > 2)
1892 0 : pg_fatal("improper relation name (too many dotted names): %s",
1893 : cell->val);
1894 0 : else if (dotcnt == 2)
1895 0 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1896 0 : termPQExpBuffer(&dbbuf);
1897 :
1898 0 : if (with_child_tables)
1899 : {
1900 0 : appendPQExpBufferStr(query, "UNION"
1901 : "\nSELECT i.inhrelid"
1902 : "\nFROM partition_tree p"
1903 : "\n JOIN pg_catalog.pg_inherits i"
1904 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1905 : "\n)"
1906 : "\nSELECT relid FROM partition_tree");
1907 0 : }
1908 :
1909 0 : ExecuteSqlStatement(fout, "RESET search_path");
1910 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1911 0 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1912 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1913 0 : if (strict_names && PQntuples(res) == 0)
1914 0 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1915 :
1916 0 : for (i = 0; i < PQntuples(res); i++)
1917 : {
1918 0 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1919 0 : }
1920 :
1921 0 : PQclear(res);
1922 0 : resetPQExpBuffer(query);
1923 0 : }
1924 :
1925 0 : destroyPQExpBuffer(query);
1926 0 : }
1927 :
1928 : /*
1929 : * Verifies that the connected database name matches the given database name,
1930 : * and if not, dies with an error about the given pattern.
1931 : *
1932 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1933 : */
1934 : static void
1935 0 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1936 : {
1937 0 : const char *db;
1938 :
1939 0 : db = PQdb(conn);
1940 0 : if (db == NULL)
1941 0 : pg_fatal("You are currently not connected to a database.");
1942 :
1943 0 : if (strcmp(db, dbname) != 0)
1944 0 : pg_fatal("cross-database references are not implemented: %s",
1945 : pattern);
1946 0 : }
1947 :
1948 : /*
1949 : * checkExtensionMembership
1950 : * Determine whether object is an extension member, and if so,
1951 : * record an appropriate dependency and set the object's dump flag.
1952 : *
1953 : * It's important to call this for each object that could be an extension
1954 : * member. Generally, we integrate this with determining the object's
1955 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1956 : *
1957 : * Returns true if object is an extension member, else false.
1958 : */
1959 : static bool
1960 0 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1961 : {
1962 0 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1963 :
1964 0 : if (ext == NULL)
1965 0 : return false;
1966 :
1967 0 : dobj->ext_member = true;
1968 :
1969 : /* Record dependency so that getDependencies needn't deal with that */
1970 0 : addObjectDependency(dobj, ext->dobj.dumpId);
1971 :
1972 : /*
1973 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1974 : * dumped. (Any initial ACLs will be removed later, using data from
1975 : * pg_init_privs, so that we'll dump only the delta from the extension's
1976 : * initial setup.)
1977 : *
1978 : * Prior to 9.6, we do not include any extension member components.
1979 : *
1980 : * In binary upgrades, we still dump all components of the members
1981 : * individually, since the idea is to exactly reproduce the database
1982 : * contents rather than replace the extension contents with something
1983 : * different.
1984 : *
1985 : * Note: it might be interesting someday to implement storage and delta
1986 : * dumping of extension members' RLS policies and/or security labels.
1987 : * However there is a pitfall for RLS policies: trying to dump them
1988 : * requires getting a lock on their tables, and the calling user might not
1989 : * have privileges for that. We need no lock to examine a table's ACLs,
1990 : * so the current feature doesn't have a problem of that sort.
1991 : */
1992 0 : if (fout->dopt->binary_upgrade)
1993 0 : dobj->dump = ext->dobj.dump;
1994 : else
1995 : {
1996 0 : if (fout->remoteVersion < 90600)
1997 0 : dobj->dump = DUMP_COMPONENT_NONE;
1998 : else
1999 0 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
2000 : }
2001 :
2002 0 : return true;
2003 0 : }
2004 :
2005 : /*
2006 : * selectDumpableNamespace: policy-setting subroutine
2007 : * Mark a namespace as to be dumped or not
2008 : */
2009 : static void
2010 0 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
2011 : {
2012 : /*
2013 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
2014 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
2015 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
2016 : */
2017 0 : nsinfo->create = true;
2018 :
2019 : /*
2020 : * If specific tables are being dumped, do not dump any complete
2021 : * namespaces. If specific namespaces are being dumped, dump just those
2022 : * namespaces. Otherwise, dump all non-system namespaces.
2023 : */
2024 0 : if (table_include_oids.head != NULL)
2025 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2026 0 : else if (schema_include_oids.head != NULL)
2027 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2028 0 : simple_oid_list_member(&schema_include_oids,
2029 0 : nsinfo->dobj.catId.oid) ?
2030 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2031 0 : else if (fout->remoteVersion >= 90600 &&
2032 0 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2033 : {
2034 : /*
2035 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2036 : * they are interesting (and not the original ACLs which were set at
2037 : * initdb time, see pg_init_privs).
2038 : */
2039 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2040 0 : }
2041 0 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2042 0 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2043 : {
2044 : /* Other system schemas don't get dumped */
2045 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2046 0 : }
2047 0 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2048 : {
2049 : /*
2050 : * The public schema is a strange beast that sits in a sort of
2051 : * no-mans-land between being a system object and a user object.
2052 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2053 : * a comment and an indication of ownership. If the owner is the
2054 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2055 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2056 : */
2057 0 : nsinfo->create = false;
2058 0 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2059 0 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2060 0 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2061 0 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2062 :
2063 : /*
2064 : * Also, make like it has a comment even if it doesn't; this is so
2065 : * that we'll emit a command to drop the comment, if appropriate.
2066 : * (Without this, we'd not call dumpCommentExtended for it.)
2067 : */
2068 0 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2069 0 : }
2070 : else
2071 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2072 :
2073 : /*
2074 : * In any case, a namespace can be excluded by an exclusion switch
2075 : */
2076 0 : if (nsinfo->dobj.dump_contains &&
2077 0 : simple_oid_list_member(&schema_exclude_oids,
2078 0 : nsinfo->dobj.catId.oid))
2079 0 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2080 :
2081 : /*
2082 : * If the schema belongs to an extension, allow extension membership to
2083 : * override the dump decision for the schema itself. However, this does
2084 : * not change dump_contains, so this won't change what we do with objects
2085 : * within the schema. (If they belong to the extension, they'll get
2086 : * suppressed by it, otherwise not.)
2087 : */
2088 0 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2089 0 : }
2090 :
2091 : /*
2092 : * selectDumpableTable: policy-setting subroutine
2093 : * Mark a table as to be dumped or not
2094 : */
2095 : static void
2096 0 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2097 : {
2098 0 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2099 0 : return; /* extension membership overrides all else */
2100 :
2101 : /*
2102 : * If specific tables are being dumped, dump just those tables; else, dump
2103 : * according to the parent namespace's dump flag.
2104 : */
2105 0 : if (table_include_oids.head != NULL)
2106 0 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2107 0 : tbinfo->dobj.catId.oid) ?
2108 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2109 : else
2110 0 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2111 :
2112 : /*
2113 : * In any case, a table can be excluded by an exclusion switch
2114 : */
2115 0 : if (tbinfo->dobj.dump &&
2116 0 : simple_oid_list_member(&table_exclude_oids,
2117 0 : tbinfo->dobj.catId.oid))
2118 0 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2119 0 : }
2120 :
2121 : /*
2122 : * selectDumpableType: policy-setting subroutine
2123 : * Mark a type as to be dumped or not
2124 : *
2125 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2126 : * special type code to facilitate sorting into the desired order. (We don't
2127 : * want to consider those to be ordinary types because that would bring tables
2128 : * up into the datatype part of the dump order.) We still set the object's
2129 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2130 : * need it so that casts involving such types will be dumped correctly -- see
2131 : * dumpCast. This means the flag should be set the same as for the underlying
2132 : * object (the table or base type).
2133 : */
2134 : static void
2135 0 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2136 : {
2137 : /* skip complex types, except for standalone composite types */
2138 0 : if (OidIsValid(tyinfo->typrelid) &&
2139 0 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2140 : {
2141 0 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2142 :
2143 0 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2144 0 : if (tytable != NULL)
2145 0 : tyinfo->dobj.dump = tytable->dobj.dump;
2146 : else
2147 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2148 : return;
2149 0 : }
2150 :
2151 : /* skip auto-generated array and multirange types */
2152 0 : if (tyinfo->isArray || tyinfo->isMultirange)
2153 : {
2154 0 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2155 :
2156 : /*
2157 : * Fall through to set the dump flag; we assume that the subsequent
2158 : * rules will do the same thing as they would for the array's base
2159 : * type or multirange's range type. (We cannot reliably look up the
2160 : * base type here, since getTypes may not have processed it yet.)
2161 : */
2162 0 : }
2163 :
2164 0 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2165 0 : return; /* extension membership overrides all else */
2166 :
2167 : /* Dump based on if the contents of the namespace are being dumped */
2168 0 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2169 0 : }
2170 :
2171 : /*
2172 : * selectDumpableDefaultACL: policy-setting subroutine
2173 : * Mark a default ACL as to be dumped or not
2174 : *
2175 : * For per-schema default ACLs, dump if the schema is to be dumped.
2176 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2177 : * and aclsSkip are checked separately.
2178 : */
2179 : static void
2180 0 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2181 : {
2182 : /* Default ACLs can't be extension members */
2183 :
2184 0 : if (dinfo->dobj.namespace)
2185 : /* default ACLs are considered part of the namespace */
2186 0 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2187 : else
2188 0 : dinfo->dobj.dump = dopt->include_everything ?
2189 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2190 0 : }
2191 :
2192 : /*
2193 : * selectDumpableCast: policy-setting subroutine
2194 : * Mark a cast as to be dumped or not
2195 : *
2196 : * Casts do not belong to any particular namespace (since they haven't got
2197 : * names), nor do they have identifiable owners. To distinguish user-defined
2198 : * casts from built-in ones, we must resort to checking whether the cast's
2199 : * OID is in the range reserved for initdb.
2200 : */
2201 : static void
2202 0 : selectDumpableCast(CastInfo *cast, Archive *fout)
2203 : {
2204 0 : if (checkExtensionMembership(&cast->dobj, fout))
2205 0 : return; /* extension membership overrides all else */
2206 :
2207 : /*
2208 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2209 : * support ACLs currently.
2210 : */
2211 0 : if (cast->dobj.catId.oid <= g_last_builtin_oid)
2212 0 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2213 : else
2214 0 : cast->dobj.dump = fout->dopt->include_everything ?
2215 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2216 0 : }
2217 :
2218 : /*
2219 : * selectDumpableProcLang: policy-setting subroutine
2220 : * Mark a procedural language as to be dumped or not
2221 : *
2222 : * Procedural languages do not belong to any particular namespace. To
2223 : * identify built-in languages, we must resort to checking whether the
2224 : * language's OID is in the range reserved for initdb.
2225 : */
2226 : static void
2227 0 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2228 : {
2229 0 : if (checkExtensionMembership(&plang->dobj, fout))
2230 0 : return; /* extension membership overrides all else */
2231 :
2232 : /*
2233 : * Only include procedural languages when we are dumping everything.
2234 : *
2235 : * For from-initdb procedural languages, only include ACLs, as we do for
2236 : * the pg_catalog namespace. We need this because procedural languages do
2237 : * not live in any namespace.
2238 : */
2239 0 : if (!fout->dopt->include_everything)
2240 0 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2241 : else
2242 : {
2243 0 : if (plang->dobj.catId.oid <= g_last_builtin_oid)
2244 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2245 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2246 : else
2247 0 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2248 : }
2249 0 : }
2250 :
2251 : /*
2252 : * selectDumpableAccessMethod: policy-setting subroutine
2253 : * Mark an access method as to be dumped or not
2254 : *
2255 : * Access methods do not belong to any particular namespace. To identify
2256 : * built-in access methods, we must resort to checking whether the
2257 : * method's OID is in the range reserved for initdb.
2258 : */
2259 : static void
2260 0 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2261 : {
2262 : /* see getAccessMethods() comment about v9.6. */
2263 0 : if (fout->remoteVersion < 90600)
2264 : {
2265 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2266 0 : return;
2267 : }
2268 :
2269 0 : if (checkExtensionMembership(&method->dobj, fout))
2270 0 : return; /* extension membership overrides all else */
2271 :
2272 : /*
2273 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2274 : * they do not support ACLs currently.
2275 : */
2276 0 : if (method->dobj.catId.oid <= g_last_builtin_oid)
2277 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2278 : else
2279 0 : method->dobj.dump = fout->dopt->include_everything ?
2280 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2281 0 : }
2282 :
2283 : /*
2284 : * selectDumpableExtension: policy-setting subroutine
2285 : * Mark an extension as to be dumped or not
2286 : *
2287 : * Built-in extensions should be skipped except for checking ACLs, since we
2288 : * assume those will already be installed in the target database. We identify
2289 : * such extensions by their having OIDs in the range reserved for initdb.
2290 : * We dump all user-added extensions by default. No extensions are dumped
2291 : * if include_everything is false (i.e., a --schema or --table switch was
2292 : * given), except if --extension specifies a list of extensions to dump.
2293 : */
2294 : static void
2295 0 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2296 : {
2297 : /*
2298 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2299 : * change permissions on their member objects, if they wish to, and have
2300 : * those changes preserved.
2301 : */
2302 0 : if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2303 0 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2304 : else
2305 : {
2306 : /* check if there is a list of extensions to dump */
2307 0 : if (extension_include_oids.head != NULL)
2308 0 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2309 0 : simple_oid_list_member(&extension_include_oids,
2310 0 : extinfo->dobj.catId.oid) ?
2311 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2312 : else
2313 0 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2314 0 : dopt->include_everything ?
2315 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2316 :
2317 : /* check that the extension is not explicitly excluded */
2318 0 : if (extinfo->dobj.dump &&
2319 0 : simple_oid_list_member(&extension_exclude_oids,
2320 0 : extinfo->dobj.catId.oid))
2321 0 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2322 : }
2323 0 : }
2324 :
2325 : /*
2326 : * selectDumpablePublicationObject: policy-setting subroutine
2327 : * Mark a publication object as to be dumped or not
2328 : *
2329 : * A publication can have schemas and tables which have schemas, but those are
2330 : * ignored in decision making, because publications are only dumped when we are
2331 : * dumping everything.
2332 : */
2333 : static void
2334 0 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2335 : {
2336 0 : if (checkExtensionMembership(dobj, fout))
2337 0 : return; /* extension membership overrides all else */
2338 :
2339 0 : dobj->dump = fout->dopt->include_everything ?
2340 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2341 0 : }
2342 :
2343 : /*
2344 : * selectDumpableStatisticsObject: policy-setting subroutine
2345 : * Mark an extended statistics object as to be dumped or not
2346 : *
2347 : * We dump an extended statistics object if the schema it's in and the table
2348 : * it's for are being dumped. (This'll need more thought if statistics
2349 : * objects ever support cross-table stats.)
2350 : */
2351 : static void
2352 0 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2353 : {
2354 0 : if (checkExtensionMembership(&sobj->dobj, fout))
2355 0 : return; /* extension membership overrides all else */
2356 :
2357 0 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2358 0 : if (sobj->stattable == NULL ||
2359 0 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2360 0 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2361 0 : }
2362 :
2363 : /*
2364 : * selectDumpableObject: policy-setting subroutine
2365 : * Mark a generic dumpable object as to be dumped or not
2366 : *
2367 : * Use this only for object types without a special-case routine above.
2368 : */
2369 : static void
2370 0 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2371 : {
2372 0 : if (checkExtensionMembership(dobj, fout))
2373 0 : return; /* extension membership overrides all else */
2374 :
2375 : /*
2376 : * Default policy is to dump if parent namespace is dumpable, or for
2377 : * non-namespace-associated items, dump if we're dumping "everything".
2378 : */
2379 0 : if (dobj->namespace)
2380 0 : dobj->dump = dobj->namespace->dobj.dump_contains;
2381 : else
2382 0 : dobj->dump = fout->dopt->include_everything ?
2383 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2384 0 : }
2385 :
2386 : /*
2387 : * Dump a table's contents for loading using the COPY command
2388 : * - this routine is called by the Archiver when it wants the table
2389 : * to be dumped.
2390 : */
2391 : static int
2392 0 : dumpTableData_copy(Archive *fout, const void *dcontext)
2393 : {
2394 0 : const TableDataInfo *tdinfo = dcontext;
2395 0 : const TableInfo *tbinfo = tdinfo->tdtable;
2396 0 : const char *classname = tbinfo->dobj.name;
2397 0 : PQExpBuffer q = createPQExpBuffer();
2398 :
2399 : /*
2400 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2401 : * which uses it already.
2402 : */
2403 0 : PQExpBuffer clistBuf = createPQExpBuffer();
2404 0 : PGconn *conn = GetConnection(fout);
2405 0 : PGresult *res;
2406 0 : int ret;
2407 0 : char *copybuf;
2408 0 : const char *column_list;
2409 :
2410 0 : pg_log_info("dumping contents of table \"%s.%s\"",
2411 : tbinfo->dobj.namespace->dobj.name, classname);
2412 :
2413 : /*
2414 : * Specify the column list explicitly so that we have no possibility of
2415 : * retrieving data in the wrong column order. (The default column
2416 : * ordering of COPY will not be what we want in certain corner cases
2417 : * involving ADD COLUMN and inheritance.)
2418 : */
2419 0 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2420 :
2421 : /*
2422 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2423 : * a filter condition was specified. For other cases a simple COPY
2424 : * suffices.
2425 : */
2426 0 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2427 : {
2428 : /* Temporary allows to access to foreign tables to dump data */
2429 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2430 0 : set_restrict_relation_kind(fout, "view");
2431 :
2432 0 : appendPQExpBufferStr(q, "COPY (SELECT ");
2433 : /* klugery to get rid of parens in column list */
2434 0 : if (strlen(column_list) > 2)
2435 : {
2436 0 : appendPQExpBufferStr(q, column_list + 1);
2437 0 : q->data[q->len - 1] = ' ';
2438 0 : }
2439 : else
2440 0 : appendPQExpBufferStr(q, "* ");
2441 :
2442 0 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2443 0 : fmtQualifiedDumpable(tbinfo),
2444 0 : tdinfo->filtercond ? tdinfo->filtercond : "");
2445 0 : }
2446 : else
2447 : {
2448 0 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2449 0 : fmtQualifiedDumpable(tbinfo),
2450 0 : column_list);
2451 : }
2452 0 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2453 0 : PQclear(res);
2454 0 : destroyPQExpBuffer(clistBuf);
2455 :
2456 0 : for (;;)
2457 : {
2458 0 : ret = PQgetCopyData(conn, ©buf, 0);
2459 :
2460 0 : if (ret < 0)
2461 0 : break; /* done or error */
2462 :
2463 0 : if (copybuf)
2464 : {
2465 0 : WriteData(fout, copybuf, ret);
2466 0 : PQfreemem(copybuf);
2467 0 : }
2468 :
2469 : /* ----------
2470 : * THROTTLE:
2471 : *
2472 : * There was considerable discussion in late July, 2000 regarding
2473 : * slowing down pg_dump when backing up large tables. Users with both
2474 : * slow & fast (multi-processor) machines experienced performance
2475 : * degradation when doing a backup.
2476 : *
2477 : * Initial attempts based on sleeping for a number of ms for each ms
2478 : * of work were deemed too complex, then a simple 'sleep in each loop'
2479 : * implementation was suggested. The latter failed because the loop
2480 : * was too tight. Finally, the following was implemented:
2481 : *
2482 : * If throttle is non-zero, then
2483 : * See how long since the last sleep.
2484 : * Work out how long to sleep (based on ratio).
2485 : * If sleep is more than 100ms, then
2486 : * sleep
2487 : * reset timer
2488 : * EndIf
2489 : * EndIf
2490 : *
2491 : * where the throttle value was the number of ms to sleep per ms of
2492 : * work. The calculation was done in each loop.
2493 : *
2494 : * Most of the hard work is done in the backend, and this solution
2495 : * still did not work particularly well: on slow machines, the ratio
2496 : * was 50:1, and on medium paced machines, 1:1, and on fast
2497 : * multi-processor machines, it had little or no effect, for reasons
2498 : * that were unclear.
2499 : *
2500 : * Further discussion ensued, and the proposal was dropped.
2501 : *
2502 : * For those people who want this feature, it can be implemented using
2503 : * gettimeofday in each loop, calculating the time since last sleep,
2504 : * multiplying that by the sleep ratio, then if the result is more
2505 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2506 : * function to sleep for a subsecond period ie.
2507 : *
2508 : * select(0, NULL, NULL, NULL, &tvi);
2509 : *
2510 : * This will return after the interval specified in the structure tvi.
2511 : * Finally, call gettimeofday again to save the 'last sleep time'.
2512 : * ----------
2513 : */
2514 : }
2515 0 : archprintf(fout, "\\.\n\n\n");
2516 :
2517 0 : if (ret == -2)
2518 : {
2519 : /* copy data transfer failed */
2520 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2521 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2522 0 : pg_log_error_detail("Command was: %s", q->data);
2523 0 : exit_nicely(1);
2524 : }
2525 :
2526 : /* Check command status and return to normal libpq state */
2527 0 : res = PQgetResult(conn);
2528 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2529 : {
2530 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2531 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2532 0 : pg_log_error_detail("Command was: %s", q->data);
2533 0 : exit_nicely(1);
2534 : }
2535 0 : PQclear(res);
2536 :
2537 : /* Do this to ensure we've pumped libpq back to idle state */
2538 0 : if (PQgetResult(conn) != NULL)
2539 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2540 : classname);
2541 :
2542 0 : destroyPQExpBuffer(q);
2543 :
2544 : /* Revert back the setting */
2545 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2546 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2547 :
2548 0 : return 1;
2549 0 : }
2550 :
2551 : /*
2552 : * Dump table data using INSERT commands.
2553 : *
2554 : * Caution: when we restore from an archive file direct to database, the
2555 : * INSERT commands emitted by this function have to be parsed by
2556 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2557 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2558 : */
2559 : static int
2560 0 : dumpTableData_insert(Archive *fout, const void *dcontext)
2561 : {
2562 0 : const TableDataInfo *tdinfo = dcontext;
2563 0 : const TableInfo *tbinfo = tdinfo->tdtable;
2564 0 : DumpOptions *dopt = fout->dopt;
2565 0 : PQExpBuffer q = createPQExpBuffer();
2566 0 : PQExpBuffer insertStmt = NULL;
2567 0 : char *attgenerated;
2568 0 : PGresult *res;
2569 0 : int nfields,
2570 : i;
2571 0 : int rows_per_statement = dopt->dump_inserts;
2572 0 : int rows_this_statement = 0;
2573 :
2574 : /* Temporary allows to access to foreign tables to dump data */
2575 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2576 0 : set_restrict_relation_kind(fout, "view");
2577 :
2578 : /*
2579 : * If we're going to emit INSERTs with column names, the most efficient
2580 : * way to deal with generated columns is to exclude them entirely. For
2581 : * INSERTs without column names, we have to emit DEFAULT rather than the
2582 : * actual column value --- but we can save a few cycles by fetching nulls
2583 : * rather than the uninteresting-to-us value.
2584 : */
2585 0 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2586 0 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2587 0 : nfields = 0;
2588 0 : for (i = 0; i < tbinfo->numatts; i++)
2589 : {
2590 0 : if (tbinfo->attisdropped[i])
2591 0 : continue;
2592 0 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2593 0 : continue;
2594 0 : if (nfields > 0)
2595 0 : appendPQExpBufferStr(q, ", ");
2596 0 : if (tbinfo->attgenerated[i])
2597 0 : appendPQExpBufferStr(q, "NULL");
2598 : else
2599 0 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2600 0 : attgenerated[nfields] = tbinfo->attgenerated[i];
2601 0 : nfields++;
2602 0 : }
2603 : /* Servers before 9.4 will complain about zero-column SELECT */
2604 0 : if (nfields == 0)
2605 0 : appendPQExpBufferStr(q, "NULL");
2606 0 : appendPQExpBuffer(q, " FROM ONLY %s",
2607 0 : fmtQualifiedDumpable(tbinfo));
2608 0 : if (tdinfo->filtercond)
2609 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2610 :
2611 0 : ExecuteSqlStatement(fout, q->data);
2612 :
2613 0 : while (1)
2614 : {
2615 0 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2616 : PGRES_TUPLES_OK);
2617 :
2618 : /* cross-check field count, allowing for dummy NULL if any */
2619 0 : if (nfields != PQnfields(res) &&
2620 0 : !(nfields == 0 && PQnfields(res) == 1))
2621 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2622 : tbinfo->dobj.name);
2623 :
2624 : /*
2625 : * First time through, we build as much of the INSERT statement as
2626 : * possible in "insertStmt", which we can then just print for each
2627 : * statement. If the table happens to have zero dumpable columns then
2628 : * this will be a complete statement, otherwise it will end in
2629 : * "VALUES" and be ready to have the row's column values printed.
2630 : */
2631 0 : if (insertStmt == NULL)
2632 : {
2633 0 : const TableInfo *targettab;
2634 :
2635 0 : insertStmt = createPQExpBuffer();
2636 :
2637 : /*
2638 : * When load-via-partition-root is set or forced, get the root
2639 : * table name for the partition table, so that we can reload data
2640 : * through the root table.
2641 : */
2642 0 : if (tbinfo->ispartition &&
2643 0 : (dopt->load_via_partition_root ||
2644 0 : forcePartitionRootLoad(tbinfo)))
2645 0 : targettab = getRootTableInfo(tbinfo);
2646 : else
2647 0 : targettab = tbinfo;
2648 :
2649 0 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2650 0 : fmtQualifiedDumpable(targettab));
2651 :
2652 : /* corner case for zero-column table */
2653 0 : if (nfields == 0)
2654 : {
2655 0 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2656 0 : }
2657 : else
2658 : {
2659 : /* append the list of column names if required */
2660 0 : if (dopt->column_inserts)
2661 : {
2662 0 : appendPQExpBufferChar(insertStmt, '(');
2663 0 : for (int field = 0; field < nfields; field++)
2664 : {
2665 0 : if (field > 0)
2666 0 : appendPQExpBufferStr(insertStmt, ", ");
2667 0 : appendPQExpBufferStr(insertStmt,
2668 0 : fmtId(PQfname(res, field)));
2669 0 : }
2670 0 : appendPQExpBufferStr(insertStmt, ") ");
2671 0 : }
2672 :
2673 0 : if (tbinfo->needs_override)
2674 0 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2675 :
2676 0 : appendPQExpBufferStr(insertStmt, "VALUES");
2677 : }
2678 0 : }
2679 :
2680 0 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2681 : {
2682 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2683 0 : if (rows_this_statement == 0)
2684 0 : archputs(insertStmt->data, fout);
2685 :
2686 : /*
2687 : * If it is zero-column table then we've already written the
2688 : * complete statement, which will mean we've disobeyed
2689 : * --rows-per-insert when it's set greater than 1. We do support
2690 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2691 : * UNION ALL ... but that's non-standard so we should avoid it
2692 : * given that using INSERTs is mostly only ever needed for
2693 : * cross-database exports.
2694 : */
2695 0 : if (nfields == 0)
2696 0 : continue;
2697 :
2698 : /* Emit a row heading */
2699 0 : if (rows_per_statement == 1)
2700 0 : archputs(" (", fout);
2701 0 : else if (rows_this_statement > 0)
2702 0 : archputs(",\n\t(", fout);
2703 : else
2704 0 : archputs("\n\t(", fout);
2705 :
2706 0 : for (int field = 0; field < nfields; field++)
2707 : {
2708 0 : if (field > 0)
2709 0 : archputs(", ", fout);
2710 0 : if (attgenerated[field])
2711 : {
2712 0 : archputs("DEFAULT", fout);
2713 0 : continue;
2714 : }
2715 0 : if (PQgetisnull(res, tuple, field))
2716 : {
2717 0 : archputs("NULL", fout);
2718 0 : continue;
2719 : }
2720 :
2721 : /* XXX This code is partially duplicated in ruleutils.c */
2722 0 : switch (PQftype(res, field))
2723 : {
2724 : case INT2OID:
2725 : case INT4OID:
2726 : case INT8OID:
2727 : case OIDOID:
2728 : case FLOAT4OID:
2729 : case FLOAT8OID:
2730 : case NUMERICOID:
2731 : {
2732 : /*
2733 : * These types are printed without quotes unless
2734 : * they contain values that aren't accepted by the
2735 : * scanner unquoted (e.g., 'NaN'). Note that
2736 : * strtod() and friends might accept NaN, so we
2737 : * can't use that to test.
2738 : *
2739 : * In reality we only need to defend against
2740 : * infinity and NaN, so we need not get too crazy
2741 : * about pattern matching here.
2742 : */
2743 0 : const char *s = PQgetvalue(res, tuple, field);
2744 :
2745 0 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2746 0 : archputs(s, fout);
2747 : else
2748 0 : archprintf(fout, "'%s'", s);
2749 0 : }
2750 0 : break;
2751 :
2752 : case BITOID:
2753 : case VARBITOID:
2754 0 : archprintf(fout, "B'%s'",
2755 0 : PQgetvalue(res, tuple, field));
2756 0 : break;
2757 :
2758 : case BOOLOID:
2759 0 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2760 0 : archputs("true", fout);
2761 : else
2762 0 : archputs("false", fout);
2763 0 : break;
2764 :
2765 : default:
2766 : /* All other types are printed as string literals. */
2767 0 : resetPQExpBuffer(q);
2768 0 : appendStringLiteralAH(q,
2769 : PQgetvalue(res, tuple, field),
2770 : fout);
2771 0 : archputs(q->data, fout);
2772 0 : break;
2773 : }
2774 0 : }
2775 :
2776 : /* Terminate the row ... */
2777 0 : archputs(")", fout);
2778 :
2779 : /* ... and the statement, if the target no. of rows is reached */
2780 0 : if (++rows_this_statement >= rows_per_statement)
2781 : {
2782 0 : if (dopt->do_nothing)
2783 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2784 : else
2785 0 : archputs(";\n", fout);
2786 : /* Reset the row counter */
2787 0 : rows_this_statement = 0;
2788 0 : }
2789 0 : }
2790 :
2791 0 : if (PQntuples(res) <= 0)
2792 : {
2793 0 : PQclear(res);
2794 0 : break;
2795 : }
2796 0 : PQclear(res);
2797 : }
2798 :
2799 : /* Terminate any statements that didn't make the row count. */
2800 0 : if (rows_this_statement > 0)
2801 : {
2802 0 : if (dopt->do_nothing)
2803 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2804 : else
2805 0 : archputs(";\n", fout);
2806 0 : }
2807 :
2808 0 : archputs("\n\n", fout);
2809 :
2810 0 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2811 :
2812 0 : destroyPQExpBuffer(q);
2813 0 : if (insertStmt != NULL)
2814 0 : destroyPQExpBuffer(insertStmt);
2815 0 : free(attgenerated);
2816 :
2817 : /* Revert back the setting */
2818 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2819 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2820 :
2821 0 : return 1;
2822 0 : }
2823 :
2824 : /*
2825 : * getRootTableInfo:
2826 : * get the root TableInfo for the given partition table.
2827 : */
2828 : static TableInfo *
2829 0 : getRootTableInfo(const TableInfo *tbinfo)
2830 : {
2831 0 : TableInfo *parentTbinfo;
2832 :
2833 0 : Assert(tbinfo->ispartition);
2834 0 : Assert(tbinfo->numParents == 1);
2835 :
2836 0 : parentTbinfo = tbinfo->parents[0];
2837 0 : while (parentTbinfo->ispartition)
2838 : {
2839 0 : Assert(parentTbinfo->numParents == 1);
2840 0 : parentTbinfo = parentTbinfo->parents[0];
2841 : }
2842 :
2843 0 : return parentTbinfo;
2844 0 : }
2845 :
2846 : /*
2847 : * forcePartitionRootLoad
2848 : * Check if we must force load_via_partition_root for this partition.
2849 : *
2850 : * This is required if any level of ancestral partitioned table has an
2851 : * unsafe partitioning scheme.
2852 : */
2853 : static bool
2854 0 : forcePartitionRootLoad(const TableInfo *tbinfo)
2855 : {
2856 0 : TableInfo *parentTbinfo;
2857 :
2858 0 : Assert(tbinfo->ispartition);
2859 0 : Assert(tbinfo->numParents == 1);
2860 :
2861 0 : parentTbinfo = tbinfo->parents[0];
2862 0 : if (parentTbinfo->unsafe_partitions)
2863 0 : return true;
2864 0 : while (parentTbinfo->ispartition)
2865 : {
2866 0 : Assert(parentTbinfo->numParents == 1);
2867 0 : parentTbinfo = parentTbinfo->parents[0];
2868 0 : if (parentTbinfo->unsafe_partitions)
2869 0 : return true;
2870 : }
2871 :
2872 0 : return false;
2873 0 : }
2874 :
2875 : /*
2876 : * dumpTableData -
2877 : * dump the contents of a single table
2878 : *
2879 : * Actually, this just makes an ArchiveEntry for the table contents.
2880 : */
2881 : static void
2882 0 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2883 : {
2884 0 : DumpOptions *dopt = fout->dopt;
2885 0 : const TableInfo *tbinfo = tdinfo->tdtable;
2886 0 : PQExpBuffer copyBuf = createPQExpBuffer();
2887 0 : PQExpBuffer clistBuf = createPQExpBuffer();
2888 0 : DataDumperPtr dumpFn;
2889 0 : char *tdDefn = NULL;
2890 0 : char *copyStmt;
2891 0 : const char *copyFrom;
2892 :
2893 : /* We had better have loaded per-column details about this table */
2894 0 : Assert(tbinfo->interesting);
2895 :
2896 : /*
2897 : * When load-via-partition-root is set or forced, get the root table name
2898 : * for the partition table, so that we can reload data through the root
2899 : * table. Then construct a comment to be inserted into the TOC entry's
2900 : * defn field, so that such cases can be identified reliably.
2901 : */
2902 0 : if (tbinfo->ispartition &&
2903 0 : (dopt->load_via_partition_root ||
2904 0 : forcePartitionRootLoad(tbinfo)))
2905 : {
2906 0 : const TableInfo *parentTbinfo;
2907 0 : char *sanitized;
2908 :
2909 0 : parentTbinfo = getRootTableInfo(tbinfo);
2910 0 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2911 0 : sanitized = sanitize_line(copyFrom, true);
2912 0 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2913 0 : sanitized);
2914 0 : free(sanitized);
2915 0 : tdDefn = pg_strdup(copyBuf->data);
2916 0 : }
2917 : else
2918 0 : copyFrom = fmtQualifiedDumpable(tbinfo);
2919 :
2920 0 : if (dopt->dump_inserts == 0)
2921 : {
2922 : /* Dump/restore using COPY */
2923 0 : dumpFn = dumpTableData_copy;
2924 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2925 0 : printfPQExpBuffer(copyBuf, "COPY %s ",
2926 0 : copyFrom);
2927 0 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2928 0 : fmtCopyColumnList(tbinfo, clistBuf));
2929 0 : copyStmt = copyBuf->data;
2930 0 : }
2931 : else
2932 : {
2933 : /* Restore using INSERT */
2934 0 : dumpFn = dumpTableData_insert;
2935 0 : copyStmt = NULL;
2936 : }
2937 :
2938 : /*
2939 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2940 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2941 : * See comments for BuildArchiveDependencies.
2942 : */
2943 0 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2944 : {
2945 0 : TocEntry *te;
2946 :
2947 0 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2948 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2949 : .namespace = tbinfo->dobj.namespace->dobj.name,
2950 : .owner = tbinfo->rolname,
2951 : .description = "TABLE DATA",
2952 : .section = SECTION_DATA,
2953 : .createStmt = tdDefn,
2954 : .copyStmt = copyStmt,
2955 : .deps = &(tbinfo->dobj.dumpId),
2956 : .nDeps = 1,
2957 : .dumpFn = dumpFn,
2958 : .dumpArg = tdinfo));
2959 :
2960 : /*
2961 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2962 : * and want to order dump jobs by table size. We choose to measure
2963 : * dataLength in table pages (including TOAST pages) during dump, so
2964 : * no scaling is needed.
2965 : *
2966 : * However, relpages is declared as "integer" in pg_class, and hence
2967 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2968 : * Cast so that we get the right interpretation of table sizes
2969 : * exceeding INT_MAX pages.
2970 : */
2971 0 : te->dataLength = (BlockNumber) tbinfo->relpages;
2972 0 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2973 :
2974 : /*
2975 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2976 : * and instead we'd better worry about integer overflow. Clamp to
2977 : * INT_MAX if the correct result exceeds that.
2978 : */
2979 : if (sizeof(te->dataLength) == 4 &&
2980 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2981 : te->dataLength < 0))
2982 : te->dataLength = INT_MAX;
2983 0 : }
2984 :
2985 0 : destroyPQExpBuffer(copyBuf);
2986 0 : destroyPQExpBuffer(clistBuf);
2987 0 : }
2988 :
2989 : /*
2990 : * refreshMatViewData -
2991 : * load or refresh the contents of a single materialized view
2992 : *
2993 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2994 : * statement.
2995 : */
2996 : static void
2997 0 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2998 : {
2999 0 : TableInfo *tbinfo = tdinfo->tdtable;
3000 0 : PQExpBuffer q;
3001 :
3002 : /* If the materialized view is not flagged as populated, skip this. */
3003 0 : if (!tbinfo->relispopulated)
3004 0 : return;
3005 :
3006 0 : q = createPQExpBuffer();
3007 :
3008 0 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
3009 0 : fmtQualifiedDumpable(tbinfo));
3010 :
3011 0 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
3012 0 : ArchiveEntry(fout,
3013 0 : tdinfo->dobj.catId, /* catalog ID */
3014 0 : tdinfo->dobj.dumpId, /* dump ID */
3015 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
3016 : .namespace = tbinfo->dobj.namespace->dobj.name,
3017 : .owner = tbinfo->rolname,
3018 : .description = "MATERIALIZED VIEW DATA",
3019 : .section = SECTION_POST_DATA,
3020 : .createStmt = q->data,
3021 : .deps = tdinfo->dobj.dependencies,
3022 : .nDeps = tdinfo->dobj.nDeps));
3023 :
3024 0 : destroyPQExpBuffer(q);
3025 0 : }
3026 :
3027 : /*
3028 : * getTableData -
3029 : * set up dumpable objects representing the contents of tables
3030 : */
3031 : static void
3032 0 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3033 : {
3034 0 : int i;
3035 :
3036 0 : for (i = 0; i < numTables; i++)
3037 : {
3038 0 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3039 0 : (!relkind || tblinfo[i].relkind == relkind))
3040 0 : makeTableDataInfo(dopt, &(tblinfo[i]));
3041 0 : }
3042 0 : }
3043 :
3044 : /*
3045 : * Make a dumpable object for the data of this specific table
3046 : *
3047 : * Note: we make a TableDataInfo if and only if we are going to dump the
3048 : * table data; the "dump" field in such objects isn't very interesting.
3049 : */
3050 : static void
3051 0 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3052 : {
3053 0 : TableDataInfo *tdinfo;
3054 :
3055 : /*
3056 : * Nothing to do if we already decided to dump the table. This will
3057 : * happen for "config" tables.
3058 : */
3059 0 : if (tbinfo->dataObj != NULL)
3060 0 : return;
3061 :
3062 : /* Skip VIEWs (no data to dump) */
3063 0 : if (tbinfo->relkind == RELKIND_VIEW)
3064 0 : return;
3065 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3066 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3067 0 : (foreign_servers_include_oids.head == NULL ||
3068 0 : !simple_oid_list_member(&foreign_servers_include_oids,
3069 0 : tbinfo->foreign_server)))
3070 0 : return;
3071 : /* Skip partitioned tables (data in partitions) */
3072 0 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3073 0 : return;
3074 :
3075 : /* Don't dump data in unlogged tables, if so requested */
3076 0 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3077 0 : dopt->no_unlogged_table_data)
3078 0 : return;
3079 :
3080 : /* Check that the data is not explicitly excluded */
3081 0 : if (simple_oid_list_member(&tabledata_exclude_oids,
3082 0 : tbinfo->dobj.catId.oid))
3083 0 : return;
3084 :
3085 : /* OK, let's dump it */
3086 0 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3087 :
3088 0 : if (tbinfo->relkind == RELKIND_MATVIEW)
3089 0 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3090 0 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3091 0 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3092 : else
3093 0 : tdinfo->dobj.objType = DO_TABLE_DATA;
3094 :
3095 : /*
3096 : * Note: use tableoid 0 so that this object won't be mistaken for
3097 : * something that pg_depend entries apply to.
3098 : */
3099 0 : tdinfo->dobj.catId.tableoid = 0;
3100 0 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3101 0 : AssignDumpId(&tdinfo->dobj);
3102 0 : tdinfo->dobj.name = tbinfo->dobj.name;
3103 0 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3104 0 : tdinfo->tdtable = tbinfo;
3105 0 : tdinfo->filtercond = NULL; /* might get set later */
3106 0 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3107 :
3108 : /* A TableDataInfo contains data, of course */
3109 0 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3110 :
3111 0 : tbinfo->dataObj = tdinfo;
3112 :
3113 : /*
3114 : * Materialized view statistics must be restored after the data, because
3115 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3116 : *
3117 : * The dependency is added here because the statistics objects are created
3118 : * first.
3119 : */
3120 0 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3121 : {
3122 0 : tbinfo->stats->section = SECTION_POST_DATA;
3123 0 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3124 0 : }
3125 :
3126 : /* Make sure that we'll collect per-column info for this table. */
3127 0 : tbinfo->interesting = true;
3128 0 : }
3129 :
3130 : /*
3131 : * The refresh for a materialized view must be dependent on the refresh for
3132 : * any materialized view that this one is dependent on.
3133 : *
3134 : * This must be called after all the objects are created, but before they are
3135 : * sorted.
3136 : */
3137 : static void
3138 0 : buildMatViewRefreshDependencies(Archive *fout)
3139 : {
3140 0 : PQExpBuffer query;
3141 0 : PGresult *res;
3142 0 : int ntups,
3143 : i;
3144 0 : int i_classid,
3145 : i_objid,
3146 : i_refobjid;
3147 :
3148 : /* No Mat Views before 9.3. */
3149 0 : if (fout->remoteVersion < 90300)
3150 0 : return;
3151 :
3152 0 : query = createPQExpBuffer();
3153 :
3154 0 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3155 : "( "
3156 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3157 : "FROM pg_depend d1 "
3158 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3159 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3160 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3161 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3162 : "AND d2.objid = r1.oid "
3163 : "AND d2.refobjid <> d1.objid "
3164 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3165 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3166 : CppAsString2(RELKIND_VIEW) ") "
3167 : "WHERE d1.classid = 'pg_class'::regclass "
3168 : "UNION "
3169 : "SELECT w.objid, d3.refobjid, c3.relkind "
3170 : "FROM w "
3171 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3172 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3173 : "AND d3.objid = r3.oid "
3174 : "AND d3.refobjid <> w.refobjid "
3175 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3176 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3177 : CppAsString2(RELKIND_VIEW) ") "
3178 : ") "
3179 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3180 : "FROM w "
3181 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3182 :
3183 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3184 :
3185 0 : ntups = PQntuples(res);
3186 :
3187 0 : i_classid = PQfnumber(res, "classid");
3188 0 : i_objid = PQfnumber(res, "objid");
3189 0 : i_refobjid = PQfnumber(res, "refobjid");
3190 :
3191 0 : for (i = 0; i < ntups; i++)
3192 : {
3193 0 : CatalogId objId;
3194 0 : CatalogId refobjId;
3195 0 : DumpableObject *dobj;
3196 0 : DumpableObject *refdobj;
3197 0 : TableInfo *tbinfo;
3198 0 : TableInfo *reftbinfo;
3199 :
3200 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3201 0 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3202 0 : refobjId.tableoid = objId.tableoid;
3203 0 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3204 :
3205 0 : dobj = findObjectByCatalogId(objId);
3206 0 : if (dobj == NULL)
3207 0 : continue;
3208 :
3209 0 : Assert(dobj->objType == DO_TABLE);
3210 0 : tbinfo = (TableInfo *) dobj;
3211 0 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3212 0 : dobj = (DumpableObject *) tbinfo->dataObj;
3213 0 : if (dobj == NULL)
3214 0 : continue;
3215 0 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3216 :
3217 0 : refdobj = findObjectByCatalogId(refobjId);
3218 0 : if (refdobj == NULL)
3219 0 : continue;
3220 :
3221 0 : Assert(refdobj->objType == DO_TABLE);
3222 0 : reftbinfo = (TableInfo *) refdobj;
3223 0 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3224 0 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3225 0 : if (refdobj == NULL)
3226 0 : continue;
3227 0 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3228 :
3229 0 : addObjectDependency(dobj, refdobj->dumpId);
3230 :
3231 0 : if (!reftbinfo->relispopulated)
3232 0 : tbinfo->relispopulated = false;
3233 0 : }
3234 :
3235 0 : PQclear(res);
3236 :
3237 0 : destroyPQExpBuffer(query);
3238 0 : }
3239 :
3240 : /*
3241 : * getTableDataFKConstraints -
3242 : * add dump-order dependencies reflecting foreign key constraints
3243 : *
3244 : * This code is executed only in a data-only dump --- in schema+data dumps
3245 : * we handle foreign key issues by not creating the FK constraints until
3246 : * after the data is loaded. In a data-only dump, however, we want to
3247 : * order the table data objects in such a way that a table's referenced
3248 : * tables are restored first. (In the presence of circular references or
3249 : * self-references this may be impossible; we'll detect and complain about
3250 : * that during the dependency sorting step.)
3251 : */
3252 : static void
3253 0 : getTableDataFKConstraints(void)
3254 : {
3255 0 : DumpableObject **dobjs;
3256 0 : int numObjs;
3257 0 : int i;
3258 :
3259 : /* Search through all the dumpable objects for FK constraints */
3260 0 : getDumpableObjects(&dobjs, &numObjs);
3261 0 : for (i = 0; i < numObjs; i++)
3262 : {
3263 0 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3264 : {
3265 0 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3266 0 : TableInfo *ftable;
3267 :
3268 : /* Not interesting unless both tables are to be dumped */
3269 0 : if (cinfo->contable == NULL ||
3270 0 : cinfo->contable->dataObj == NULL)
3271 0 : continue;
3272 0 : ftable = findTableByOid(cinfo->confrelid);
3273 0 : if (ftable == NULL ||
3274 0 : ftable->dataObj == NULL)
3275 0 : continue;
3276 :
3277 : /*
3278 : * Okay, make referencing table's TABLE_DATA object depend on the
3279 : * referenced table's TABLE_DATA object.
3280 : */
3281 0 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3282 0 : ftable->dataObj->dobj.dumpId);
3283 0 : }
3284 0 : }
3285 0 : free(dobjs);
3286 0 : }
3287 :
3288 :
3289 : /*
3290 : * dumpDatabase:
3291 : * dump the database definition
3292 : */
3293 : static void
3294 0 : dumpDatabase(Archive *fout)
3295 : {
3296 0 : DumpOptions *dopt = fout->dopt;
3297 0 : PQExpBuffer dbQry = createPQExpBuffer();
3298 0 : PQExpBuffer delQry = createPQExpBuffer();
3299 0 : PQExpBuffer creaQry = createPQExpBuffer();
3300 0 : PQExpBuffer labelq = createPQExpBuffer();
3301 0 : PGconn *conn = GetConnection(fout);
3302 0 : PGresult *res;
3303 0 : int i_tableoid,
3304 : i_oid,
3305 : i_datname,
3306 : i_datdba,
3307 : i_encoding,
3308 : i_datlocprovider,
3309 : i_collate,
3310 : i_ctype,
3311 : i_datlocale,
3312 : i_daticurules,
3313 : i_frozenxid,
3314 : i_minmxid,
3315 : i_datacl,
3316 : i_acldefault,
3317 : i_datistemplate,
3318 : i_datconnlimit,
3319 : i_datcollversion,
3320 : i_tablespace;
3321 0 : CatalogId dbCatId;
3322 0 : DumpId dbDumpId;
3323 0 : DumpableAcl dbdacl;
3324 0 : const char *datname,
3325 : *dba,
3326 : *encoding,
3327 : *datlocprovider,
3328 : *collate,
3329 : *ctype,
3330 : *locale,
3331 : *icurules,
3332 : *datistemplate,
3333 : *datconnlimit,
3334 : *tablespace;
3335 0 : uint32 frozenxid,
3336 : minmxid;
3337 0 : char *qdatname;
3338 :
3339 0 : pg_log_info("saving database definition");
3340 :
3341 : /*
3342 : * Fetch the database-level properties for this database.
3343 : */
3344 0 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3345 : "datdba, "
3346 : "pg_encoding_to_char(encoding) AS encoding, "
3347 : "datcollate, datctype, datfrozenxid, "
3348 : "datacl, acldefault('d', datdba) AS acldefault, "
3349 : "datistemplate, datconnlimit, ");
3350 0 : if (fout->remoteVersion >= 90300)
3351 0 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3352 : else
3353 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3354 0 : if (fout->remoteVersion >= 170000)
3355 0 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3356 0 : else if (fout->remoteVersion >= 150000)
3357 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3358 : else
3359 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3360 0 : if (fout->remoteVersion >= 160000)
3361 0 : appendPQExpBufferStr(dbQry, "daticurules, ");
3362 : else
3363 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3364 0 : appendPQExpBufferStr(dbQry,
3365 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3366 : "shobj_description(oid, 'pg_database') AS description "
3367 : "FROM pg_database "
3368 : "WHERE datname = current_database()");
3369 :
3370 0 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3371 :
3372 0 : i_tableoid = PQfnumber(res, "tableoid");
3373 0 : i_oid = PQfnumber(res, "oid");
3374 0 : i_datname = PQfnumber(res, "datname");
3375 0 : i_datdba = PQfnumber(res, "datdba");
3376 0 : i_encoding = PQfnumber(res, "encoding");
3377 0 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3378 0 : i_collate = PQfnumber(res, "datcollate");
3379 0 : i_ctype = PQfnumber(res, "datctype");
3380 0 : i_datlocale = PQfnumber(res, "datlocale");
3381 0 : i_daticurules = PQfnumber(res, "daticurules");
3382 0 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3383 0 : i_minmxid = PQfnumber(res, "datminmxid");
3384 0 : i_datacl = PQfnumber(res, "datacl");
3385 0 : i_acldefault = PQfnumber(res, "acldefault");
3386 0 : i_datistemplate = PQfnumber(res, "datistemplate");
3387 0 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3388 0 : i_datcollversion = PQfnumber(res, "datcollversion");
3389 0 : i_tablespace = PQfnumber(res, "tablespace");
3390 :
3391 0 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3392 0 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3393 0 : datname = PQgetvalue(res, 0, i_datname);
3394 0 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3395 0 : encoding = PQgetvalue(res, 0, i_encoding);
3396 0 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3397 0 : collate = PQgetvalue(res, 0, i_collate);
3398 0 : ctype = PQgetvalue(res, 0, i_ctype);
3399 0 : if (!PQgetisnull(res, 0, i_datlocale))
3400 0 : locale = PQgetvalue(res, 0, i_datlocale);
3401 : else
3402 0 : locale = NULL;
3403 0 : if (!PQgetisnull(res, 0, i_daticurules))
3404 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3405 : else
3406 0 : icurules = NULL;
3407 0 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3408 0 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3409 0 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3410 0 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3411 0 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3412 0 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3413 0 : tablespace = PQgetvalue(res, 0, i_tablespace);
3414 :
3415 0 : qdatname = pg_strdup(fmtId(datname));
3416 :
3417 : /*
3418 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3419 : * to preserve that), as well as the encoding, locale, and tablespace
3420 : * since those can't be altered later. Other DB properties are left to
3421 : * the DATABASE PROPERTIES entry, so that they can be applied after
3422 : * reconnecting to the target DB.
3423 : *
3424 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3425 : * shown it to be faster. When the server is in binary upgrade mode, it
3426 : * will also skip the checkpoints this strategy ordinarily performs.
3427 : */
3428 0 : if (dopt->binary_upgrade)
3429 : {
3430 0 : appendPQExpBuffer(creaQry,
3431 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3432 : "OID = %u STRATEGY = FILE_COPY",
3433 0 : qdatname, dbCatId.oid);
3434 0 : }
3435 : else
3436 : {
3437 0 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3438 0 : qdatname);
3439 : }
3440 0 : if (strlen(encoding) > 0)
3441 : {
3442 0 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3443 0 : appendStringLiteralAH(creaQry, encoding, fout);
3444 0 : }
3445 :
3446 0 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3447 0 : if (datlocprovider[0] == 'b')
3448 0 : appendPQExpBufferStr(creaQry, "builtin");
3449 0 : else if (datlocprovider[0] == 'c')
3450 0 : appendPQExpBufferStr(creaQry, "libc");
3451 0 : else if (datlocprovider[0] == 'i')
3452 0 : appendPQExpBufferStr(creaQry, "icu");
3453 : else
3454 0 : pg_fatal("unrecognized locale provider: %s",
3455 : datlocprovider);
3456 :
3457 0 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3458 : {
3459 0 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3460 0 : appendStringLiteralAH(creaQry, collate, fout);
3461 0 : }
3462 : else
3463 : {
3464 0 : if (strlen(collate) > 0)
3465 : {
3466 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3467 0 : appendStringLiteralAH(creaQry, collate, fout);
3468 0 : }
3469 0 : if (strlen(ctype) > 0)
3470 : {
3471 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3472 0 : appendStringLiteralAH(creaQry, ctype, fout);
3473 0 : }
3474 : }
3475 0 : if (locale)
3476 : {
3477 0 : if (datlocprovider[0] == 'b')
3478 0 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3479 : else
3480 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3481 :
3482 0 : appendStringLiteralAH(creaQry, locale, fout);
3483 0 : }
3484 :
3485 0 : if (icurules)
3486 : {
3487 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3488 0 : appendStringLiteralAH(creaQry, icurules, fout);
3489 0 : }
3490 :
3491 : /*
3492 : * For binary upgrade, carry over the collation version. For normal
3493 : * dump/restore, omit the version, so that it is computed upon restore.
3494 : */
3495 0 : if (dopt->binary_upgrade)
3496 : {
3497 0 : if (!PQgetisnull(res, 0, i_datcollversion))
3498 : {
3499 0 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3500 0 : appendStringLiteralAH(creaQry,
3501 : PQgetvalue(res, 0, i_datcollversion),
3502 : fout);
3503 0 : }
3504 0 : }
3505 :
3506 : /*
3507 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3508 : * thing; the decision whether to specify a tablespace should be left till
3509 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3510 : * label the DATABASE entry with the tablespace and let the normal
3511 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3512 : * attention to default_tablespace, so that won't work.
3513 : */
3514 0 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3515 0 : !dopt->outputNoTablespaces)
3516 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3517 0 : fmtId(tablespace));
3518 0 : appendPQExpBufferStr(creaQry, ";\n");
3519 :
3520 0 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3521 0 : qdatname);
3522 :
3523 0 : dbDumpId = createDumpId();
3524 :
3525 0 : ArchiveEntry(fout,
3526 : dbCatId, /* catalog ID */
3527 0 : dbDumpId, /* dump ID */
3528 0 : ARCHIVE_OPTS(.tag = datname,
3529 : .owner = dba,
3530 : .description = "DATABASE",
3531 : .section = SECTION_PRE_DATA,
3532 : .createStmt = creaQry->data,
3533 : .dropStmt = delQry->data));
3534 :
3535 : /* Compute correct tag for archive entry */
3536 0 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3537 :
3538 : /* Dump DB comment if any */
3539 : {
3540 : /*
3541 : * 8.2 and up keep comments on shared objects in a shared table, so we
3542 : * cannot use the dumpComment() code used for other database objects.
3543 : * Be careful that the ArchiveEntry parameters match that function.
3544 : */
3545 0 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3546 :
3547 0 : if (comment && *comment && !dopt->no_comments)
3548 : {
3549 0 : resetPQExpBuffer(dbQry);
3550 :
3551 : /*
3552 : * Generates warning when loaded into a differently-named
3553 : * database.
3554 : */
3555 0 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3556 0 : appendStringLiteralAH(dbQry, comment, fout);
3557 0 : appendPQExpBufferStr(dbQry, ";\n");
3558 :
3559 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3560 0 : ARCHIVE_OPTS(.tag = labelq->data,
3561 : .owner = dba,
3562 : .description = "COMMENT",
3563 : .section = SECTION_NONE,
3564 : .createStmt = dbQry->data,
3565 : .deps = &dbDumpId,
3566 : .nDeps = 1));
3567 0 : }
3568 0 : }
3569 :
3570 : /* Dump DB security label, if enabled */
3571 0 : if (!dopt->no_security_labels)
3572 : {
3573 0 : PGresult *shres;
3574 0 : PQExpBuffer seclabelQry;
3575 :
3576 0 : seclabelQry = createPQExpBuffer();
3577 :
3578 0 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3579 0 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3580 0 : resetPQExpBuffer(seclabelQry);
3581 0 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3582 0 : if (seclabelQry->len > 0)
3583 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3584 0 : ARCHIVE_OPTS(.tag = labelq->data,
3585 : .owner = dba,
3586 : .description = "SECURITY LABEL",
3587 : .section = SECTION_NONE,
3588 : .createStmt = seclabelQry->data,
3589 : .deps = &dbDumpId,
3590 : .nDeps = 1));
3591 0 : destroyPQExpBuffer(seclabelQry);
3592 0 : PQclear(shres);
3593 0 : }
3594 :
3595 : /*
3596 : * Dump ACL if any. Note that we do not support initial privileges
3597 : * (pg_init_privs) on databases.
3598 : */
3599 0 : dbdacl.privtype = 0;
3600 0 : dbdacl.initprivs = NULL;
3601 :
3602 0 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3603 0 : qdatname, NULL, NULL,
3604 0 : NULL, dba, &dbdacl);
3605 :
3606 : /*
3607 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3608 : * non-default database-level properties. (The reason this must be
3609 : * separate is that we cannot put any additional commands into the TOC
3610 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3611 : * in an implicit transaction block, and the backend won't allow CREATE
3612 : * DATABASE in that context.)
3613 : */
3614 0 : resetPQExpBuffer(creaQry);
3615 0 : resetPQExpBuffer(delQry);
3616 :
3617 0 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3618 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3619 0 : qdatname, datconnlimit);
3620 :
3621 0 : if (strcmp(datistemplate, "t") == 0)
3622 : {
3623 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3624 0 : qdatname);
3625 :
3626 : /*
3627 : * The backend won't accept DROP DATABASE on a template database. We
3628 : * can deal with that by removing the template marking before the DROP
3629 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3630 : * since no such command is currently supported, fake it with a direct
3631 : * UPDATE on pg_database.
3632 : */
3633 0 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3634 : "SET datistemplate = false WHERE datname = ");
3635 0 : appendStringLiteralAH(delQry, datname, fout);
3636 0 : appendPQExpBufferStr(delQry, ";\n");
3637 0 : }
3638 :
3639 : /*
3640 : * We do not restore pg_database.dathasloginevt because it is set
3641 : * automatically on login event trigger creation.
3642 : */
3643 :
3644 : /* Add database-specific SET options */
3645 0 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3646 :
3647 : /*
3648 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3649 : * entry, too, for lack of a better place.
3650 : */
3651 0 : if (dopt->binary_upgrade)
3652 : {
3653 0 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3654 0 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3655 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3656 : "WHERE datname = ",
3657 0 : frozenxid, minmxid);
3658 0 : appendStringLiteralAH(creaQry, datname, fout);
3659 0 : appendPQExpBufferStr(creaQry, ";\n");
3660 0 : }
3661 :
3662 0 : if (creaQry->len > 0)
3663 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3664 0 : ARCHIVE_OPTS(.tag = datname,
3665 : .owner = dba,
3666 : .description = "DATABASE PROPERTIES",
3667 : .section = SECTION_PRE_DATA,
3668 : .createStmt = creaQry->data,
3669 : .dropStmt = delQry->data,
3670 : .deps = &dbDumpId));
3671 :
3672 : /*
3673 : * pg_largeobject comes from the old system intact, so set its
3674 : * relfrozenxids, relminmxids and relfilenode.
3675 : *
3676 : * pg_largeobject_metadata also comes from the old system intact for
3677 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3678 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3679 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3680 : * changed its storage format in v16.
3681 : */
3682 0 : if (dopt->binary_upgrade)
3683 : {
3684 0 : PGresult *lo_res;
3685 0 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3686 0 : PQExpBuffer loOutQry = createPQExpBuffer();
3687 0 : PQExpBuffer lomOutQry = createPQExpBuffer();
3688 0 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3689 0 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3690 0 : int ii_relfrozenxid,
3691 : ii_relfilenode,
3692 : ii_oid,
3693 : ii_relminmxid;
3694 :
3695 0 : if (fout->remoteVersion >= 90300)
3696 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3697 : "FROM pg_catalog.pg_class\n"
3698 : "WHERE oid IN (%u, %u, %u, %u);\n",
3699 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3700 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3701 : else
3702 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3703 : "FROM pg_catalog.pg_class\n"
3704 : "WHERE oid IN (%u, %u);\n",
3705 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3706 :
3707 0 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3708 :
3709 0 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3710 0 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3711 0 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3712 0 : ii_oid = PQfnumber(lo_res, "oid");
3713 :
3714 0 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3715 0 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3716 0 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3717 0 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3718 0 : for (int i = 0; i < PQntuples(lo_res); ++i)
3719 : {
3720 0 : Oid oid;
3721 0 : RelFileNumber relfilenumber;
3722 0 : PQExpBuffer horizonQry;
3723 0 : PQExpBuffer outQry;
3724 :
3725 0 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3726 0 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3727 :
3728 0 : if (oid == LargeObjectRelationId ||
3729 0 : oid == LargeObjectLOidPNIndexId)
3730 : {
3731 0 : horizonQry = loHorizonQry;
3732 0 : outQry = loOutQry;
3733 0 : }
3734 : else
3735 : {
3736 0 : horizonQry = lomHorizonQry;
3737 0 : outQry = lomOutQry;
3738 : }
3739 :
3740 0 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3741 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3742 : "WHERE oid = %u;\n",
3743 0 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3744 0 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3745 0 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3746 :
3747 0 : if (oid == LargeObjectRelationId ||
3748 0 : oid == LargeObjectMetadataRelationId)
3749 0 : appendPQExpBuffer(outQry,
3750 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3751 0 : relfilenumber);
3752 0 : else if (oid == LargeObjectLOidPNIndexId ||
3753 0 : oid == LargeObjectMetadataOidIndexId)
3754 0 : appendPQExpBuffer(outQry,
3755 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3756 0 : relfilenumber);
3757 0 : }
3758 :
3759 0 : appendPQExpBufferStr(loOutQry,
3760 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3761 0 : appendPQExpBufferStr(lomOutQry,
3762 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3763 :
3764 0 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3765 0 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3766 :
3767 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3768 0 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3769 : .description = "pg_largeobject",
3770 : .section = SECTION_PRE_DATA,
3771 : .createStmt = loOutQry->data));
3772 :
3773 0 : if (fout->remoteVersion >= 160000)
3774 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3775 0 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3776 : .description = "pg_largeobject_metadata",
3777 : .section = SECTION_PRE_DATA,
3778 : .createStmt = lomOutQry->data));
3779 :
3780 0 : PQclear(lo_res);
3781 :
3782 0 : destroyPQExpBuffer(loFrozenQry);
3783 0 : destroyPQExpBuffer(loHorizonQry);
3784 0 : destroyPQExpBuffer(lomHorizonQry);
3785 0 : destroyPQExpBuffer(loOutQry);
3786 0 : destroyPQExpBuffer(lomOutQry);
3787 0 : }
3788 :
3789 0 : PQclear(res);
3790 :
3791 0 : free(qdatname);
3792 0 : destroyPQExpBuffer(dbQry);
3793 0 : destroyPQExpBuffer(delQry);
3794 0 : destroyPQExpBuffer(creaQry);
3795 0 : destroyPQExpBuffer(labelq);
3796 0 : }
3797 :
3798 : /*
3799 : * Collect any database-specific or role-and-database-specific SET options
3800 : * for this database, and append them to outbuf.
3801 : */
3802 : static void
3803 0 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3804 : const char *dbname, Oid dboid)
3805 : {
3806 0 : PGconn *conn = GetConnection(AH);
3807 0 : PQExpBuffer buf = createPQExpBuffer();
3808 0 : PGresult *res;
3809 :
3810 : /* First collect database-specific options */
3811 0 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3812 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3813 0 : dboid);
3814 :
3815 0 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3816 :
3817 0 : for (int i = 0; i < PQntuples(res); i++)
3818 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3819 0 : "DATABASE", dbname, NULL, NULL,
3820 0 : outbuf);
3821 :
3822 0 : PQclear(res);
3823 :
3824 : /* Now look for role-and-database-specific options */
3825 0 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3826 : "FROM pg_db_role_setting s, pg_roles r "
3827 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3828 0 : dboid);
3829 :
3830 0 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3831 :
3832 0 : for (int i = 0; i < PQntuples(res); i++)
3833 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3834 0 : "ROLE", PQgetvalue(res, i, 0),
3835 0 : "DATABASE", dbname,
3836 0 : outbuf);
3837 :
3838 0 : PQclear(res);
3839 :
3840 0 : destroyPQExpBuffer(buf);
3841 0 : }
3842 :
3843 : /*
3844 : * dumpEncoding: put the correct encoding into the archive
3845 : */
3846 : static void
3847 0 : dumpEncoding(Archive *AH)
3848 : {
3849 0 : const char *encname = pg_encoding_to_char(AH->encoding);
3850 0 : PQExpBuffer qry = createPQExpBuffer();
3851 :
3852 0 : pg_log_info("saving encoding = %s", encname);
3853 :
3854 0 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3855 0 : appendStringLiteralAH(qry, encname, AH);
3856 0 : appendPQExpBufferStr(qry, ";\n");
3857 :
3858 0 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3859 0 : ARCHIVE_OPTS(.tag = "ENCODING",
3860 : .description = "ENCODING",
3861 : .section = SECTION_PRE_DATA,
3862 : .createStmt = qry->data));
3863 :
3864 0 : destroyPQExpBuffer(qry);
3865 0 : }
3866 :
3867 :
3868 : /*
3869 : * dumpStdStrings: put the correct escape string behavior into the archive
3870 : */
3871 : static void
3872 0 : dumpStdStrings(Archive *AH)
3873 : {
3874 0 : const char *stdstrings = AH->std_strings ? "on" : "off";
3875 0 : PQExpBuffer qry = createPQExpBuffer();
3876 :
3877 0 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3878 : stdstrings);
3879 :
3880 0 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3881 0 : stdstrings);
3882 :
3883 0 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3884 0 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3885 : .description = "STDSTRINGS",
3886 : .section = SECTION_PRE_DATA,
3887 : .createStmt = qry->data));
3888 :
3889 0 : destroyPQExpBuffer(qry);
3890 0 : }
3891 :
3892 : /*
3893 : * dumpSearchPath: record the active search_path in the archive
3894 : */
3895 : static void
3896 0 : dumpSearchPath(Archive *AH)
3897 : {
3898 0 : PQExpBuffer qry = createPQExpBuffer();
3899 0 : PQExpBuffer path = createPQExpBuffer();
3900 0 : PGresult *res;
3901 0 : char **schemanames = NULL;
3902 0 : int nschemanames = 0;
3903 0 : int i;
3904 :
3905 : /*
3906 : * We use the result of current_schemas(), not the search_path GUC,
3907 : * because that might contain wildcards such as "$user", which won't
3908 : * necessarily have the same value during restore. Also, this way avoids
3909 : * listing schemas that may appear in search_path but not actually exist,
3910 : * which seems like a prudent exclusion.
3911 : */
3912 0 : res = ExecuteSqlQueryForSingleRow(AH,
3913 : "SELECT pg_catalog.current_schemas(false)");
3914 :
3915 0 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3916 0 : pg_fatal("could not parse result of current_schemas()");
3917 :
3918 : /*
3919 : * We use set_config(), not a simple "SET search_path" command, because
3920 : * the latter has less-clean behavior if the search path is empty. While
3921 : * that's likely to get fixed at some point, it seems like a good idea to
3922 : * be as backwards-compatible as possible in what we put into archives.
3923 : */
3924 0 : for (i = 0; i < nschemanames; i++)
3925 : {
3926 0 : if (i > 0)
3927 0 : appendPQExpBufferStr(path, ", ");
3928 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3929 0 : }
3930 :
3931 0 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3932 0 : appendStringLiteralAH(qry, path->data, AH);
3933 0 : appendPQExpBufferStr(qry, ", false);\n");
3934 :
3935 0 : pg_log_info("saving \"search_path = %s\"", path->data);
3936 :
3937 0 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3938 0 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3939 : .description = "SEARCHPATH",
3940 : .section = SECTION_PRE_DATA,
3941 : .createStmt = qry->data));
3942 :
3943 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3944 0 : AH->searchpath = pg_strdup(qry->data);
3945 :
3946 0 : free(schemanames);
3947 0 : PQclear(res);
3948 0 : destroyPQExpBuffer(qry);
3949 0 : destroyPQExpBuffer(path);
3950 0 : }
3951 :
3952 :
3953 : /*
3954 : * getLOs:
3955 : * Collect schema-level data about large objects
3956 : */
3957 : static void
3958 0 : getLOs(Archive *fout)
3959 : {
3960 0 : DumpOptions *dopt = fout->dopt;
3961 0 : PQExpBuffer loQry = createPQExpBuffer();
3962 0 : PGresult *res;
3963 0 : int ntups;
3964 0 : int i;
3965 0 : int n;
3966 0 : int i_oid;
3967 0 : int i_lomowner;
3968 0 : int i_lomacl;
3969 0 : int i_acldefault;
3970 :
3971 0 : pg_log_info("reading large objects");
3972 :
3973 : /*
3974 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3975 : * with the same owner/ACL appear together.
3976 : */
3977 0 : appendPQExpBufferStr(loQry,
3978 : "SELECT oid, lomowner, lomacl, "
3979 : "acldefault('L', lomowner) AS acldefault "
3980 : "FROM pg_largeobject_metadata "
3981 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3982 :
3983 0 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3984 :
3985 0 : i_oid = PQfnumber(res, "oid");
3986 0 : i_lomowner = PQfnumber(res, "lomowner");
3987 0 : i_lomacl = PQfnumber(res, "lomacl");
3988 0 : i_acldefault = PQfnumber(res, "acldefault");
3989 :
3990 0 : ntups = PQntuples(res);
3991 :
3992 : /*
3993 : * Group the blobs into suitably-sized groups that have the same owner and
3994 : * ACL setting, and build a metadata and a data DumpableObject for each
3995 : * group. (If we supported initprivs for blobs, we'd have to insist that
3996 : * groups also share initprivs settings, since the DumpableObject only has
3997 : * room for one.) i is the index of the first tuple in the current group,
3998 : * and n is the number of tuples we include in the group.
3999 : */
4000 0 : for (i = 0; i < ntups; i += n)
4001 : {
4002 0 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
4003 0 : char *thisowner = PQgetvalue(res, i, i_lomowner);
4004 0 : char *thisacl = PQgetvalue(res, i, i_lomacl);
4005 0 : LoInfo *loinfo;
4006 0 : DumpableObject *lodata;
4007 0 : char namebuf[64];
4008 :
4009 : /* Scan to find first tuple not to be included in group */
4010 0 : n = 1;
4011 0 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4012 : {
4013 0 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4014 0 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4015 0 : break;
4016 0 : n++;
4017 : }
4018 :
4019 : /* Build the metadata DumpableObject */
4020 0 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4021 :
4022 0 : loinfo->dobj.objType = DO_LARGE_OBJECT;
4023 0 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4024 0 : loinfo->dobj.catId.oid = thisoid;
4025 0 : AssignDumpId(&loinfo->dobj);
4026 :
4027 0 : if (n > 1)
4028 0 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4029 0 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4030 : else
4031 0 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4032 0 : loinfo->dobj.name = pg_strdup(namebuf);
4033 0 : loinfo->dacl.acl = pg_strdup(thisacl);
4034 0 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4035 0 : loinfo->dacl.privtype = 0;
4036 0 : loinfo->dacl.initprivs = NULL;
4037 0 : loinfo->rolname = getRoleName(thisowner);
4038 0 : loinfo->numlos = n;
4039 0 : loinfo->looids[0] = thisoid;
4040 : /* Collect OIDs of the remaining blobs in this group */
4041 0 : for (int k = 1; k < n; k++)
4042 : {
4043 0 : CatalogId extraID;
4044 :
4045 0 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4046 :
4047 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4048 0 : extraID.tableoid = LargeObjectRelationId;
4049 0 : extraID.oid = loinfo->looids[k];
4050 0 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4051 0 : }
4052 :
4053 : /* LOs have data */
4054 0 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4055 :
4056 : /* Mark whether LO group has a non-empty ACL */
4057 0 : if (!PQgetisnull(res, i, i_lomacl))
4058 0 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4059 :
4060 : /*
4061 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4062 : * as it will be copied by pg_upgrade, which simply copies the
4063 : * pg_largeobject table. We *do* however dump out anything but the
4064 : * data, as pg_upgrade copies just pg_largeobject, but not
4065 : * pg_largeobject_metadata, after the dump is restored. In versions
4066 : * before v12, this is done via proper large object commands. In
4067 : * newer versions, we dump the content of pg_largeobject_metadata and
4068 : * any associated pg_shdepend rows, which is faster to restore. (On
4069 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4070 : * column is hidden and won't be dumped.)
4071 : */
4072 0 : if (dopt->binary_upgrade)
4073 : {
4074 0 : if (fout->remoteVersion >= 120000)
4075 : {
4076 : /*
4077 : * We should've saved pg_largeobject_metadata's dump ID before
4078 : * this point.
4079 : */
4080 0 : Assert(lo_metadata_dumpId);
4081 :
4082 0 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4083 :
4084 : /*
4085 : * Mark the large object as dependent on
4086 : * pg_largeobject_metadata so that any large object
4087 : * comments/seclables are dumped after it.
4088 : */
4089 0 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4090 0 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4091 0 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4092 0 : }
4093 : else
4094 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4095 0 : }
4096 :
4097 : /*
4098 : * Create a "BLOBS" data item for the group, too. This is just a
4099 : * placeholder for sorting; it carries no data now.
4100 : */
4101 0 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4102 0 : lodata->objType = DO_LARGE_OBJECT_DATA;
4103 0 : lodata->catId = nilCatalogId;
4104 0 : AssignDumpId(lodata);
4105 0 : lodata->name = pg_strdup(namebuf);
4106 0 : lodata->components |= DUMP_COMPONENT_DATA;
4107 : /* Set up explicit dependency from data to metadata */
4108 0 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4109 0 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4110 0 : lodata->nDeps = lodata->allocDeps = 1;
4111 0 : }
4112 :
4113 0 : PQclear(res);
4114 0 : destroyPQExpBuffer(loQry);
4115 0 : }
4116 :
4117 : /*
4118 : * dumpLO
4119 : *
4120 : * dump the definition (metadata) of the given large object group
4121 : */
4122 : static void
4123 0 : dumpLO(Archive *fout, const LoInfo *loinfo)
4124 : {
4125 0 : PQExpBuffer cquery = createPQExpBuffer();
4126 :
4127 : /*
4128 : * The "definition" is just a newline-separated list of OIDs. We need to
4129 : * put something into the dropStmt too, but it can just be a comment.
4130 : */
4131 0 : for (int i = 0; i < loinfo->numlos; i++)
4132 0 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4133 :
4134 0 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4135 0 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4136 0 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4137 : .owner = loinfo->rolname,
4138 : .description = "BLOB METADATA",
4139 : .section = SECTION_DATA,
4140 : .createStmt = cquery->data,
4141 : .dropStmt = "-- dummy"));
4142 :
4143 : /*
4144 : * Dump per-blob comments and seclabels if any. We assume these are rare
4145 : * enough that it's okay to generate retail TOC entries for them.
4146 : */
4147 0 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4148 : DUMP_COMPONENT_SECLABEL))
4149 : {
4150 0 : for (int i = 0; i < loinfo->numlos; i++)
4151 : {
4152 0 : CatalogId catId;
4153 0 : char namebuf[32];
4154 :
4155 : /* Build identifying info for this blob */
4156 0 : catId.tableoid = loinfo->dobj.catId.tableoid;
4157 0 : catId.oid = loinfo->looids[i];
4158 0 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4159 :
4160 0 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4161 0 : dumpComment(fout, "LARGE OBJECT", namebuf,
4162 0 : NULL, loinfo->rolname,
4163 0 : catId, 0, loinfo->dobj.dumpId);
4164 :
4165 0 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4166 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4167 0 : NULL, loinfo->rolname,
4168 0 : catId, 0, loinfo->dobj.dumpId);
4169 0 : }
4170 0 : }
4171 :
4172 : /*
4173 : * Dump the ACLs if any (remember that all blobs in the group will have
4174 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4175 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4176 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4177 : * string to emit a mutated version for each blob.
4178 : */
4179 0 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4180 : {
4181 0 : char namebuf[32];
4182 :
4183 : /* Build identifying info for the first blob */
4184 0 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4185 :
4186 0 : if (loinfo->numlos > 1)
4187 : {
4188 0 : char tagbuf[64];
4189 :
4190 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4191 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4192 :
4193 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4194 0 : "LARGE OBJECT", namebuf, NULL, NULL,
4195 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4196 0 : }
4197 : else
4198 : {
4199 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4200 0 : "LARGE OBJECT", namebuf, NULL, NULL,
4201 0 : NULL, loinfo->rolname, &loinfo->dacl);
4202 : }
4203 0 : }
4204 :
4205 0 : destroyPQExpBuffer(cquery);
4206 0 : }
4207 :
4208 : /*
4209 : * dumpLOs:
4210 : * dump the data contents of the large objects in the given group
4211 : */
4212 : static int
4213 0 : dumpLOs(Archive *fout, const void *arg)
4214 : {
4215 0 : const LoInfo *loinfo = (const LoInfo *) arg;
4216 0 : PGconn *conn = GetConnection(fout);
4217 0 : char buf[LOBBUFSIZE];
4218 :
4219 0 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4220 :
4221 0 : for (int i = 0; i < loinfo->numlos; i++)
4222 : {
4223 0 : Oid loOid = loinfo->looids[i];
4224 0 : int loFd;
4225 0 : int cnt;
4226 :
4227 : /* Open the LO */
4228 0 : loFd = lo_open(conn, loOid, INV_READ);
4229 0 : if (loFd == -1)
4230 0 : pg_fatal("could not open large object %u: %s",
4231 : loOid, PQerrorMessage(conn));
4232 :
4233 0 : StartLO(fout, loOid);
4234 :
4235 : /* Now read it in chunks, sending data to archive */
4236 0 : do
4237 : {
4238 0 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4239 0 : if (cnt < 0)
4240 0 : pg_fatal("error reading large object %u: %s",
4241 : loOid, PQerrorMessage(conn));
4242 :
4243 0 : WriteData(fout, buf, cnt);
4244 0 : } while (cnt > 0);
4245 :
4246 0 : lo_close(conn, loFd);
4247 :
4248 0 : EndLO(fout, loOid);
4249 0 : }
4250 :
4251 0 : return 1;
4252 0 : }
4253 :
4254 : /*
4255 : * getPolicies
4256 : * get information about all RLS policies on dumpable tables.
4257 : */
4258 : void
4259 0 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4260 : {
4261 0 : DumpOptions *dopt = fout->dopt;
4262 0 : PQExpBuffer query;
4263 0 : PQExpBuffer tbloids;
4264 0 : PGresult *res;
4265 0 : PolicyInfo *polinfo;
4266 0 : int i_oid;
4267 0 : int i_tableoid;
4268 0 : int i_polrelid;
4269 0 : int i_polname;
4270 0 : int i_polcmd;
4271 0 : int i_polpermissive;
4272 0 : int i_polroles;
4273 0 : int i_polqual;
4274 0 : int i_polwithcheck;
4275 0 : int i,
4276 : j,
4277 : ntups;
4278 :
4279 : /* No policies before 9.5 */
4280 0 : if (fout->remoteVersion < 90500)
4281 0 : return;
4282 :
4283 : /* Skip if --no-policies was specified */
4284 0 : if (dopt->no_policies)
4285 0 : return;
4286 :
4287 0 : query = createPQExpBuffer();
4288 0 : tbloids = createPQExpBuffer();
4289 :
4290 : /*
4291 : * Identify tables of interest, and check which ones have RLS enabled.
4292 : */
4293 0 : appendPQExpBufferChar(tbloids, '{');
4294 0 : for (i = 0; i < numTables; i++)
4295 : {
4296 0 : TableInfo *tbinfo = &tblinfo[i];
4297 :
4298 : /* Ignore row security on tables not to be dumped */
4299 0 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4300 0 : continue;
4301 :
4302 : /* It can't have RLS or policies if it's not a table */
4303 0 : if (tbinfo->relkind != RELKIND_RELATION &&
4304 0 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4305 0 : continue;
4306 :
4307 : /* Add it to the list of table OIDs to be probed below */
4308 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
4309 0 : appendPQExpBufferChar(tbloids, ',');
4310 0 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4311 :
4312 : /* Is RLS enabled? (That's separate from whether it has policies) */
4313 0 : if (tbinfo->rowsec)
4314 : {
4315 0 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4316 :
4317 : /*
4318 : * We represent RLS being enabled on a table by creating a
4319 : * PolicyInfo object with null polname.
4320 : *
4321 : * Note: use tableoid 0 so that this object won't be mistaken for
4322 : * something that pg_depend entries apply to.
4323 : */
4324 0 : polinfo = pg_malloc(sizeof(PolicyInfo));
4325 0 : polinfo->dobj.objType = DO_POLICY;
4326 0 : polinfo->dobj.catId.tableoid = 0;
4327 0 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4328 0 : AssignDumpId(&polinfo->dobj);
4329 0 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4330 0 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4331 0 : polinfo->poltable = tbinfo;
4332 0 : polinfo->polname = NULL;
4333 0 : polinfo->polcmd = '\0';
4334 0 : polinfo->polpermissive = 0;
4335 0 : polinfo->polroles = NULL;
4336 0 : polinfo->polqual = NULL;
4337 0 : polinfo->polwithcheck = NULL;
4338 0 : }
4339 0 : }
4340 0 : appendPQExpBufferChar(tbloids, '}');
4341 :
4342 : /*
4343 : * Now, read all RLS policies belonging to the tables of interest, and
4344 : * create PolicyInfo objects for them. (Note that we must filter the
4345 : * results server-side not locally, because we dare not apply pg_get_expr
4346 : * to tables we don't have lock on.)
4347 : */
4348 0 : pg_log_info("reading row-level security policies");
4349 :
4350 0 : printfPQExpBuffer(query,
4351 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4352 0 : if (fout->remoteVersion >= 100000)
4353 0 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4354 : else
4355 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4356 0 : appendPQExpBuffer(query,
4357 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4358 : " 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, "
4359 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4360 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4361 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4362 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4363 0 : tbloids->data);
4364 :
4365 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4366 :
4367 0 : ntups = PQntuples(res);
4368 0 : if (ntups > 0)
4369 : {
4370 0 : i_oid = PQfnumber(res, "oid");
4371 0 : i_tableoid = PQfnumber(res, "tableoid");
4372 0 : i_polrelid = PQfnumber(res, "polrelid");
4373 0 : i_polname = PQfnumber(res, "polname");
4374 0 : i_polcmd = PQfnumber(res, "polcmd");
4375 0 : i_polpermissive = PQfnumber(res, "polpermissive");
4376 0 : i_polroles = PQfnumber(res, "polroles");
4377 0 : i_polqual = PQfnumber(res, "polqual");
4378 0 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4379 :
4380 0 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4381 :
4382 0 : for (j = 0; j < ntups; j++)
4383 : {
4384 0 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4385 0 : TableInfo *tbinfo = findTableByOid(polrelid);
4386 :
4387 0 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4388 :
4389 0 : polinfo[j].dobj.objType = DO_POLICY;
4390 0 : polinfo[j].dobj.catId.tableoid =
4391 0 : atooid(PQgetvalue(res, j, i_tableoid));
4392 0 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4393 0 : AssignDumpId(&polinfo[j].dobj);
4394 0 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4395 0 : polinfo[j].poltable = tbinfo;
4396 0 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4397 0 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4398 :
4399 0 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4400 0 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4401 :
4402 0 : if (PQgetisnull(res, j, i_polroles))
4403 0 : polinfo[j].polroles = NULL;
4404 : else
4405 0 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4406 :
4407 0 : if (PQgetisnull(res, j, i_polqual))
4408 0 : polinfo[j].polqual = NULL;
4409 : else
4410 0 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4411 :
4412 0 : if (PQgetisnull(res, j, i_polwithcheck))
4413 0 : polinfo[j].polwithcheck = NULL;
4414 : else
4415 0 : polinfo[j].polwithcheck
4416 0 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4417 0 : }
4418 0 : }
4419 :
4420 0 : PQclear(res);
4421 :
4422 0 : destroyPQExpBuffer(query);
4423 0 : destroyPQExpBuffer(tbloids);
4424 0 : }
4425 :
4426 : /*
4427 : * dumpPolicy
4428 : * dump the definition of the given policy
4429 : */
4430 : static void
4431 0 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4432 : {
4433 0 : DumpOptions *dopt = fout->dopt;
4434 0 : TableInfo *tbinfo = polinfo->poltable;
4435 0 : PQExpBuffer query;
4436 0 : PQExpBuffer delqry;
4437 0 : PQExpBuffer polprefix;
4438 0 : char *qtabname;
4439 0 : const char *cmd;
4440 0 : char *tag;
4441 :
4442 : /* Do nothing if not dumping schema */
4443 0 : if (!dopt->dumpSchema)
4444 0 : return;
4445 :
4446 : /*
4447 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4448 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4449 : * ROW LEVEL SECURITY.
4450 : */
4451 0 : if (polinfo->polname == NULL)
4452 : {
4453 0 : query = createPQExpBuffer();
4454 :
4455 0 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4456 0 : fmtQualifiedDumpable(tbinfo));
4457 :
4458 : /*
4459 : * We must emit the ROW SECURITY object's dependency on its table
4460 : * explicitly, because it will not match anything in pg_depend (unlike
4461 : * the case for other PolicyInfo objects).
4462 : */
4463 0 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4464 0 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4465 0 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4466 : .namespace = polinfo->dobj.namespace->dobj.name,
4467 : .owner = tbinfo->rolname,
4468 : .description = "ROW SECURITY",
4469 : .section = SECTION_POST_DATA,
4470 : .createStmt = query->data,
4471 : .deps = &(tbinfo->dobj.dumpId),
4472 : .nDeps = 1));
4473 :
4474 0 : destroyPQExpBuffer(query);
4475 0 : return;
4476 : }
4477 :
4478 0 : if (polinfo->polcmd == '*')
4479 0 : cmd = "";
4480 0 : else if (polinfo->polcmd == 'r')
4481 0 : cmd = " FOR SELECT";
4482 0 : else if (polinfo->polcmd == 'a')
4483 0 : cmd = " FOR INSERT";
4484 0 : else if (polinfo->polcmd == 'w')
4485 0 : cmd = " FOR UPDATE";
4486 0 : else if (polinfo->polcmd == 'd')
4487 0 : cmd = " FOR DELETE";
4488 : else
4489 0 : pg_fatal("unexpected policy command type: %c",
4490 : polinfo->polcmd);
4491 :
4492 0 : query = createPQExpBuffer();
4493 0 : delqry = createPQExpBuffer();
4494 0 : polprefix = createPQExpBuffer();
4495 :
4496 0 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4497 :
4498 0 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4499 :
4500 0 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4501 0 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4502 :
4503 0 : if (polinfo->polroles != NULL)
4504 0 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4505 :
4506 0 : if (polinfo->polqual != NULL)
4507 0 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4508 :
4509 0 : if (polinfo->polwithcheck != NULL)
4510 0 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4511 :
4512 0 : appendPQExpBufferStr(query, ";\n");
4513 :
4514 0 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4515 0 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4516 :
4517 0 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4518 0 : fmtId(polinfo->polname));
4519 :
4520 0 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4521 :
4522 0 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4523 0 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4524 0 : ARCHIVE_OPTS(.tag = tag,
4525 : .namespace = polinfo->dobj.namespace->dobj.name,
4526 : .owner = tbinfo->rolname,
4527 : .description = "POLICY",
4528 : .section = SECTION_POST_DATA,
4529 : .createStmt = query->data,
4530 : .dropStmt = delqry->data));
4531 :
4532 0 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4533 0 : dumpComment(fout, polprefix->data, qtabname,
4534 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4535 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4536 :
4537 0 : free(tag);
4538 0 : destroyPQExpBuffer(query);
4539 0 : destroyPQExpBuffer(delqry);
4540 0 : destroyPQExpBuffer(polprefix);
4541 0 : free(qtabname);
4542 0 : }
4543 :
4544 : /*
4545 : * getPublications
4546 : * get information about publications
4547 : */
4548 : void
4549 0 : getPublications(Archive *fout)
4550 : {
4551 0 : DumpOptions *dopt = fout->dopt;
4552 0 : PQExpBuffer query;
4553 0 : PGresult *res;
4554 0 : PublicationInfo *pubinfo;
4555 0 : int i_tableoid;
4556 0 : int i_oid;
4557 0 : int i_pubname;
4558 0 : int i_pubowner;
4559 0 : int i_puballtables;
4560 0 : int i_puballsequences;
4561 0 : int i_pubinsert;
4562 0 : int i_pubupdate;
4563 0 : int i_pubdelete;
4564 0 : int i_pubtruncate;
4565 0 : int i_pubviaroot;
4566 0 : int i_pubgencols;
4567 0 : int i,
4568 : ntups;
4569 :
4570 0 : if (dopt->no_publications || fout->remoteVersion < 100000)
4571 0 : return;
4572 :
4573 0 : query = createPQExpBuffer();
4574 :
4575 : /* Get the publications. */
4576 0 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4577 : "p.pubowner, p.puballtables, p.pubinsert, "
4578 : "p.pubupdate, p.pubdelete, ");
4579 :
4580 0 : if (fout->remoteVersion >= 110000)
4581 0 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4582 : else
4583 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4584 :
4585 0 : if (fout->remoteVersion >= 130000)
4586 0 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4587 : else
4588 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4589 :
4590 0 : if (fout->remoteVersion >= 180000)
4591 0 : appendPQExpBufferStr(query, "p.pubgencols, ");
4592 : else
4593 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4594 :
4595 0 : if (fout->remoteVersion >= 190000)
4596 0 : appendPQExpBufferStr(query, "p.puballsequences ");
4597 : else
4598 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4599 :
4600 0 : appendPQExpBufferStr(query, "FROM pg_publication p");
4601 :
4602 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4603 :
4604 0 : ntups = PQntuples(res);
4605 :
4606 0 : if (ntups == 0)
4607 0 : goto cleanup;
4608 :
4609 0 : i_tableoid = PQfnumber(res, "tableoid");
4610 0 : i_oid = PQfnumber(res, "oid");
4611 0 : i_pubname = PQfnumber(res, "pubname");
4612 0 : i_pubowner = PQfnumber(res, "pubowner");
4613 0 : i_puballtables = PQfnumber(res, "puballtables");
4614 0 : i_puballsequences = PQfnumber(res, "puballsequences");
4615 0 : i_pubinsert = PQfnumber(res, "pubinsert");
4616 0 : i_pubupdate = PQfnumber(res, "pubupdate");
4617 0 : i_pubdelete = PQfnumber(res, "pubdelete");
4618 0 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4619 0 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4620 0 : i_pubgencols = PQfnumber(res, "pubgencols");
4621 :
4622 0 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4623 :
4624 0 : for (i = 0; i < ntups; i++)
4625 : {
4626 0 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4627 0 : pubinfo[i].dobj.catId.tableoid =
4628 0 : atooid(PQgetvalue(res, i, i_tableoid));
4629 0 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4630 0 : AssignDumpId(&pubinfo[i].dobj);
4631 0 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4632 0 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4633 0 : pubinfo[i].puballtables =
4634 0 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4635 0 : pubinfo[i].puballsequences =
4636 0 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4637 0 : pubinfo[i].pubinsert =
4638 0 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4639 0 : pubinfo[i].pubupdate =
4640 0 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4641 0 : pubinfo[i].pubdelete =
4642 0 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4643 0 : pubinfo[i].pubtruncate =
4644 0 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4645 0 : pubinfo[i].pubviaroot =
4646 0 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4647 0 : pubinfo[i].pubgencols_type =
4648 0 : *(PQgetvalue(res, i, i_pubgencols));
4649 :
4650 : /* Decide whether we want to dump it */
4651 0 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4652 0 : }
4653 :
4654 : cleanup:
4655 0 : PQclear(res);
4656 :
4657 0 : destroyPQExpBuffer(query);
4658 0 : }
4659 :
4660 : /*
4661 : * dumpPublication
4662 : * dump the definition of the given publication
4663 : */
4664 : static void
4665 0 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4666 : {
4667 0 : DumpOptions *dopt = fout->dopt;
4668 0 : PQExpBuffer delq;
4669 0 : PQExpBuffer query;
4670 0 : char *qpubname;
4671 0 : bool first = true;
4672 :
4673 : /* Do nothing if not dumping schema */
4674 0 : if (!dopt->dumpSchema)
4675 0 : return;
4676 :
4677 0 : delq = createPQExpBuffer();
4678 0 : query = createPQExpBuffer();
4679 :
4680 0 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4681 :
4682 0 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4683 0 : qpubname);
4684 :
4685 0 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4686 0 : qpubname);
4687 :
4688 0 : if (pubinfo->puballtables && pubinfo->puballsequences)
4689 0 : appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4690 0 : else if (pubinfo->puballtables)
4691 0 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4692 0 : else if (pubinfo->puballsequences)
4693 0 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4694 :
4695 0 : appendPQExpBufferStr(query, " WITH (publish = '");
4696 0 : if (pubinfo->pubinsert)
4697 : {
4698 0 : appendPQExpBufferStr(query, "insert");
4699 0 : first = false;
4700 0 : }
4701 :
4702 0 : if (pubinfo->pubupdate)
4703 : {
4704 0 : if (!first)
4705 0 : appendPQExpBufferStr(query, ", ");
4706 :
4707 0 : appendPQExpBufferStr(query, "update");
4708 0 : first = false;
4709 0 : }
4710 :
4711 0 : if (pubinfo->pubdelete)
4712 : {
4713 0 : if (!first)
4714 0 : appendPQExpBufferStr(query, ", ");
4715 :
4716 0 : appendPQExpBufferStr(query, "delete");
4717 0 : first = false;
4718 0 : }
4719 :
4720 0 : if (pubinfo->pubtruncate)
4721 : {
4722 0 : if (!first)
4723 0 : appendPQExpBufferStr(query, ", ");
4724 :
4725 0 : appendPQExpBufferStr(query, "truncate");
4726 0 : first = false;
4727 0 : }
4728 :
4729 0 : appendPQExpBufferChar(query, '\'');
4730 :
4731 0 : if (pubinfo->pubviaroot)
4732 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4733 :
4734 0 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4735 0 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4736 :
4737 0 : appendPQExpBufferStr(query, ");\n");
4738 :
4739 0 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4740 0 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4741 0 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4742 : .owner = pubinfo->rolname,
4743 : .description = "PUBLICATION",
4744 : .section = SECTION_POST_DATA,
4745 : .createStmt = query->data,
4746 : .dropStmt = delq->data));
4747 :
4748 0 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4749 0 : dumpComment(fout, "PUBLICATION", qpubname,
4750 0 : NULL, pubinfo->rolname,
4751 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4752 :
4753 0 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4754 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4755 0 : NULL, pubinfo->rolname,
4756 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4757 :
4758 0 : destroyPQExpBuffer(delq);
4759 0 : destroyPQExpBuffer(query);
4760 0 : free(qpubname);
4761 0 : }
4762 :
4763 : /*
4764 : * getPublicationNamespaces
4765 : * get information about publication membership for dumpable schemas.
4766 : */
4767 : void
4768 0 : getPublicationNamespaces(Archive *fout)
4769 : {
4770 0 : PQExpBuffer query;
4771 0 : PGresult *res;
4772 0 : PublicationSchemaInfo *pubsinfo;
4773 0 : DumpOptions *dopt = fout->dopt;
4774 0 : int i_tableoid;
4775 0 : int i_oid;
4776 0 : int i_pnpubid;
4777 0 : int i_pnnspid;
4778 0 : int i,
4779 : j,
4780 : ntups;
4781 :
4782 0 : if (dopt->no_publications || fout->remoteVersion < 150000)
4783 0 : return;
4784 :
4785 0 : query = createPQExpBuffer();
4786 :
4787 : /* Collect all publication membership info. */
4788 0 : appendPQExpBufferStr(query,
4789 : "SELECT tableoid, oid, pnpubid, pnnspid "
4790 : "FROM pg_catalog.pg_publication_namespace");
4791 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4792 :
4793 0 : ntups = PQntuples(res);
4794 :
4795 0 : i_tableoid = PQfnumber(res, "tableoid");
4796 0 : i_oid = PQfnumber(res, "oid");
4797 0 : i_pnpubid = PQfnumber(res, "pnpubid");
4798 0 : i_pnnspid = PQfnumber(res, "pnnspid");
4799 :
4800 : /* this allocation may be more than we need */
4801 0 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4802 0 : j = 0;
4803 :
4804 0 : for (i = 0; i < ntups; i++)
4805 : {
4806 0 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4807 0 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4808 0 : PublicationInfo *pubinfo;
4809 0 : NamespaceInfo *nspinfo;
4810 :
4811 : /*
4812 : * Ignore any entries for which we aren't interested in either the
4813 : * publication or the rel.
4814 : */
4815 0 : pubinfo = findPublicationByOid(pnpubid);
4816 0 : if (pubinfo == NULL)
4817 0 : continue;
4818 0 : nspinfo = findNamespaceByOid(pnnspid);
4819 0 : if (nspinfo == NULL)
4820 0 : continue;
4821 :
4822 : /* OK, make a DumpableObject for this relationship */
4823 0 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4824 0 : pubsinfo[j].dobj.catId.tableoid =
4825 0 : atooid(PQgetvalue(res, i, i_tableoid));
4826 0 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4827 0 : AssignDumpId(&pubsinfo[j].dobj);
4828 0 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4829 0 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4830 0 : pubsinfo[j].publication = pubinfo;
4831 0 : pubsinfo[j].pubschema = nspinfo;
4832 :
4833 : /* Decide whether we want to dump it */
4834 0 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4835 :
4836 0 : j++;
4837 0 : }
4838 :
4839 0 : PQclear(res);
4840 0 : destroyPQExpBuffer(query);
4841 0 : }
4842 :
4843 : /*
4844 : * getPublicationTables
4845 : * get information about publication membership for dumpable tables.
4846 : */
4847 : void
4848 0 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4849 : {
4850 0 : PQExpBuffer query;
4851 0 : PGresult *res;
4852 0 : PublicationRelInfo *pubrinfo;
4853 0 : DumpOptions *dopt = fout->dopt;
4854 0 : int i_tableoid;
4855 0 : int i_oid;
4856 0 : int i_prpubid;
4857 0 : int i_prrelid;
4858 0 : int i_prrelqual;
4859 0 : int i_prattrs;
4860 0 : int i,
4861 : j,
4862 : ntups;
4863 :
4864 0 : if (dopt->no_publications || fout->remoteVersion < 100000)
4865 0 : return;
4866 :
4867 0 : query = createPQExpBuffer();
4868 :
4869 : /* Collect all publication membership info. */
4870 0 : if (fout->remoteVersion >= 150000)
4871 0 : appendPQExpBufferStr(query,
4872 : "SELECT tableoid, oid, prpubid, prrelid, "
4873 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4874 : "(CASE\n"
4875 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4876 : " (SELECT array_agg(attname)\n"
4877 : " FROM\n"
4878 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4879 : " pg_catalog.pg_attribute\n"
4880 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4881 : " ELSE NULL END) prattrs "
4882 : "FROM pg_catalog.pg_publication_rel pr");
4883 : else
4884 0 : appendPQExpBufferStr(query,
4885 : "SELECT tableoid, oid, prpubid, prrelid, "
4886 : "NULL AS prrelqual, NULL AS prattrs "
4887 : "FROM pg_catalog.pg_publication_rel");
4888 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4889 :
4890 0 : ntups = PQntuples(res);
4891 :
4892 0 : i_tableoid = PQfnumber(res, "tableoid");
4893 0 : i_oid = PQfnumber(res, "oid");
4894 0 : i_prpubid = PQfnumber(res, "prpubid");
4895 0 : i_prrelid = PQfnumber(res, "prrelid");
4896 0 : i_prrelqual = PQfnumber(res, "prrelqual");
4897 0 : i_prattrs = PQfnumber(res, "prattrs");
4898 :
4899 : /* this allocation may be more than we need */
4900 0 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4901 0 : j = 0;
4902 :
4903 0 : for (i = 0; i < ntups; i++)
4904 : {
4905 0 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4906 0 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4907 0 : PublicationInfo *pubinfo;
4908 0 : TableInfo *tbinfo;
4909 :
4910 : /*
4911 : * Ignore any entries for which we aren't interested in either the
4912 : * publication or the rel.
4913 : */
4914 0 : pubinfo = findPublicationByOid(prpubid);
4915 0 : if (pubinfo == NULL)
4916 0 : continue;
4917 0 : tbinfo = findTableByOid(prrelid);
4918 0 : if (tbinfo == NULL)
4919 0 : continue;
4920 :
4921 : /* OK, make a DumpableObject for this relationship */
4922 0 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4923 0 : pubrinfo[j].dobj.catId.tableoid =
4924 0 : atooid(PQgetvalue(res, i, i_tableoid));
4925 0 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4926 0 : AssignDumpId(&pubrinfo[j].dobj);
4927 0 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4928 0 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4929 0 : pubrinfo[j].publication = pubinfo;
4930 0 : pubrinfo[j].pubtable = tbinfo;
4931 0 : if (PQgetisnull(res, i, i_prrelqual))
4932 0 : pubrinfo[j].pubrelqual = NULL;
4933 : else
4934 0 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4935 :
4936 0 : if (!PQgetisnull(res, i, i_prattrs))
4937 : {
4938 0 : char **attnames;
4939 0 : int nattnames;
4940 0 : PQExpBuffer attribs;
4941 :
4942 0 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4943 : &attnames, &nattnames))
4944 0 : pg_fatal("could not parse %s array", "prattrs");
4945 0 : attribs = createPQExpBuffer();
4946 0 : for (int k = 0; k < nattnames; k++)
4947 : {
4948 0 : if (k > 0)
4949 0 : appendPQExpBufferStr(attribs, ", ");
4950 :
4951 0 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4952 0 : }
4953 0 : pubrinfo[j].pubrattrs = attribs->data;
4954 0 : free(attribs); /* but not attribs->data */
4955 0 : free(attnames);
4956 0 : }
4957 : else
4958 0 : pubrinfo[j].pubrattrs = NULL;
4959 :
4960 : /* Decide whether we want to dump it */
4961 0 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4962 :
4963 0 : j++;
4964 0 : }
4965 :
4966 0 : PQclear(res);
4967 0 : destroyPQExpBuffer(query);
4968 0 : }
4969 :
4970 : /*
4971 : * dumpPublicationNamespace
4972 : * dump the definition of the given publication schema mapping.
4973 : */
4974 : static void
4975 0 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4976 : {
4977 0 : DumpOptions *dopt = fout->dopt;
4978 0 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4979 0 : PublicationInfo *pubinfo = pubsinfo->publication;
4980 0 : PQExpBuffer query;
4981 0 : char *tag;
4982 :
4983 : /* Do nothing if not dumping schema */
4984 0 : if (!dopt->dumpSchema)
4985 0 : return;
4986 :
4987 0 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4988 :
4989 0 : query = createPQExpBuffer();
4990 :
4991 0 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4992 0 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4993 :
4994 : /*
4995 : * There is no point in creating drop query as the drop is done by schema
4996 : * drop.
4997 : */
4998 0 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4999 0 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
5000 0 : ARCHIVE_OPTS(.tag = tag,
5001 : .namespace = schemainfo->dobj.name,
5002 : .owner = pubinfo->rolname,
5003 : .description = "PUBLICATION TABLES IN SCHEMA",
5004 : .section = SECTION_POST_DATA,
5005 : .createStmt = query->data));
5006 :
5007 : /* These objects can't currently have comments or seclabels */
5008 :
5009 0 : free(tag);
5010 0 : destroyPQExpBuffer(query);
5011 0 : }
5012 :
5013 : /*
5014 : * dumpPublicationTable
5015 : * dump the definition of the given publication table mapping
5016 : */
5017 : static void
5018 0 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
5019 : {
5020 0 : DumpOptions *dopt = fout->dopt;
5021 0 : PublicationInfo *pubinfo = pubrinfo->publication;
5022 0 : TableInfo *tbinfo = pubrinfo->pubtable;
5023 0 : PQExpBuffer query;
5024 0 : char *tag;
5025 :
5026 : /* Do nothing if not dumping schema */
5027 0 : if (!dopt->dumpSchema)
5028 0 : return;
5029 :
5030 0 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5031 :
5032 0 : query = createPQExpBuffer();
5033 :
5034 0 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5035 0 : fmtId(pubinfo->dobj.name));
5036 0 : appendPQExpBuffer(query, " %s",
5037 0 : fmtQualifiedDumpable(tbinfo));
5038 :
5039 0 : if (pubrinfo->pubrattrs)
5040 0 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5041 :
5042 0 : if (pubrinfo->pubrelqual)
5043 : {
5044 : /*
5045 : * It's necessary to add parentheses around the expression because
5046 : * pg_get_expr won't supply the parentheses for things like WHERE
5047 : * TRUE.
5048 : */
5049 0 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5050 0 : }
5051 0 : appendPQExpBufferStr(query, ";\n");
5052 :
5053 : /*
5054 : * There is no point in creating a drop query as the drop is done by table
5055 : * drop. (If you think to change this, see also _printTocEntry().)
5056 : * Although this object doesn't really have ownership as such, set the
5057 : * owner field anyway to ensure that the command is run by the correct
5058 : * role at restore time.
5059 : */
5060 0 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5061 0 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5062 0 : ARCHIVE_OPTS(.tag = tag,
5063 : .namespace = tbinfo->dobj.namespace->dobj.name,
5064 : .owner = pubinfo->rolname,
5065 : .description = "PUBLICATION TABLE",
5066 : .section = SECTION_POST_DATA,
5067 : .createStmt = query->data));
5068 :
5069 : /* These objects can't currently have comments or seclabels */
5070 :
5071 0 : free(tag);
5072 0 : destroyPQExpBuffer(query);
5073 0 : }
5074 :
5075 : /*
5076 : * Is the currently connected user a superuser?
5077 : */
5078 : static bool
5079 0 : is_superuser(Archive *fout)
5080 : {
5081 0 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5082 0 : const char *val;
5083 :
5084 0 : val = PQparameterStatus(AH->connection, "is_superuser");
5085 :
5086 0 : if (val && strcmp(val, "on") == 0)
5087 0 : return true;
5088 :
5089 0 : return false;
5090 0 : }
5091 :
5092 : /*
5093 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5094 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5095 : * the setting query is effective only where available.
5096 : */
5097 : static void
5098 0 : set_restrict_relation_kind(Archive *AH, const char *value)
5099 : {
5100 0 : PQExpBuffer query = createPQExpBuffer();
5101 0 : PGresult *res;
5102 :
5103 0 : appendPQExpBuffer(query,
5104 : "SELECT set_config(name, '%s', false) "
5105 : "FROM pg_settings "
5106 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5107 0 : value);
5108 0 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5109 :
5110 0 : PQclear(res);
5111 0 : destroyPQExpBuffer(query);
5112 0 : }
5113 :
5114 : /*
5115 : * getSubscriptions
5116 : * get information about subscriptions
5117 : */
5118 : void
5119 0 : getSubscriptions(Archive *fout)
5120 : {
5121 0 : DumpOptions *dopt = fout->dopt;
5122 0 : PQExpBuffer query;
5123 0 : PGresult *res;
5124 0 : SubscriptionInfo *subinfo;
5125 0 : int i_tableoid;
5126 0 : int i_oid;
5127 0 : int i_subname;
5128 0 : int i_subowner;
5129 0 : int i_subbinary;
5130 0 : int i_substream;
5131 0 : int i_subtwophasestate;
5132 0 : int i_subdisableonerr;
5133 0 : int i_subpasswordrequired;
5134 0 : int i_subrunasowner;
5135 0 : int i_subconninfo;
5136 0 : int i_subslotname;
5137 0 : int i_subsynccommit;
5138 0 : int i_subpublications;
5139 0 : int i_suborigin;
5140 0 : int i_suboriginremotelsn;
5141 0 : int i_subenabled;
5142 0 : int i_subfailover;
5143 0 : int i_subretaindeadtuples;
5144 0 : int i_submaxretention;
5145 0 : int i,
5146 : ntups;
5147 :
5148 0 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5149 0 : return;
5150 :
5151 0 : if (!is_superuser(fout))
5152 : {
5153 0 : int n;
5154 :
5155 0 : res = ExecuteSqlQuery(fout,
5156 : "SELECT count(*) FROM pg_subscription "
5157 : "WHERE subdbid = (SELECT oid FROM pg_database"
5158 : " WHERE datname = current_database())",
5159 : PGRES_TUPLES_OK);
5160 0 : n = atoi(PQgetvalue(res, 0, 0));
5161 0 : if (n > 0)
5162 0 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5163 0 : PQclear(res);
5164 : return;
5165 0 : }
5166 :
5167 0 : query = createPQExpBuffer();
5168 :
5169 : /* Get the subscriptions in current database. */
5170 0 : appendPQExpBufferStr(query,
5171 : "SELECT s.tableoid, s.oid, s.subname,\n"
5172 : " s.subowner,\n"
5173 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5174 : " s.subpublications,\n");
5175 :
5176 0 : if (fout->remoteVersion >= 140000)
5177 0 : appendPQExpBufferStr(query, " s.subbinary,\n");
5178 : else
5179 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5180 :
5181 0 : if (fout->remoteVersion >= 140000)
5182 0 : appendPQExpBufferStr(query, " s.substream,\n");
5183 : else
5184 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5185 :
5186 0 : if (fout->remoteVersion >= 150000)
5187 0 : appendPQExpBufferStr(query,
5188 : " s.subtwophasestate,\n"
5189 : " s.subdisableonerr,\n");
5190 : else
5191 0 : appendPQExpBuffer(query,
5192 : " '%c' AS subtwophasestate,\n"
5193 : " false AS subdisableonerr,\n",
5194 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5195 :
5196 0 : if (fout->remoteVersion >= 160000)
5197 0 : appendPQExpBufferStr(query,
5198 : " s.subpasswordrequired,\n"
5199 : " s.subrunasowner,\n"
5200 : " s.suborigin,\n");
5201 : else
5202 0 : appendPQExpBuffer(query,
5203 : " 't' AS subpasswordrequired,\n"
5204 : " 't' AS subrunasowner,\n"
5205 : " '%s' AS suborigin,\n",
5206 : LOGICALREP_ORIGIN_ANY);
5207 :
5208 0 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5209 0 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5210 : " s.subenabled,\n");
5211 : else
5212 0 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5213 : " false AS subenabled,\n");
5214 :
5215 0 : if (fout->remoteVersion >= 170000)
5216 0 : appendPQExpBufferStr(query,
5217 : " s.subfailover,\n");
5218 : else
5219 0 : appendPQExpBufferStr(query,
5220 : " false AS subfailover,\n");
5221 :
5222 0 : if (fout->remoteVersion >= 190000)
5223 0 : appendPQExpBufferStr(query,
5224 : " s.subretaindeadtuples,\n");
5225 : else
5226 0 : appendPQExpBufferStr(query,
5227 : " false AS subretaindeadtuples,\n");
5228 :
5229 0 : if (fout->remoteVersion >= 190000)
5230 0 : appendPQExpBufferStr(query,
5231 : " s.submaxretention\n");
5232 : else
5233 0 : appendPQExpBuffer(query,
5234 : " 0 AS submaxretention\n");
5235 :
5236 0 : appendPQExpBufferStr(query,
5237 : "FROM pg_subscription s\n");
5238 :
5239 0 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5240 0 : appendPQExpBufferStr(query,
5241 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5242 : " ON o.external_id = 'pg_' || s.oid::text \n");
5243 :
5244 0 : appendPQExpBufferStr(query,
5245 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5246 : " WHERE datname = current_database())");
5247 :
5248 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5249 :
5250 0 : ntups = PQntuples(res);
5251 :
5252 : /*
5253 : * Get subscription fields. We don't include subskiplsn in the dump as
5254 : * after restoring the dump this value may no longer be relevant.
5255 : */
5256 0 : i_tableoid = PQfnumber(res, "tableoid");
5257 0 : i_oid = PQfnumber(res, "oid");
5258 0 : i_subname = PQfnumber(res, "subname");
5259 0 : i_subowner = PQfnumber(res, "subowner");
5260 0 : i_subenabled = PQfnumber(res, "subenabled");
5261 0 : i_subbinary = PQfnumber(res, "subbinary");
5262 0 : i_substream = PQfnumber(res, "substream");
5263 0 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5264 0 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5265 0 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5266 0 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5267 0 : i_subfailover = PQfnumber(res, "subfailover");
5268 0 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5269 0 : i_submaxretention = PQfnumber(res, "submaxretention");
5270 0 : i_subconninfo = PQfnumber(res, "subconninfo");
5271 0 : i_subslotname = PQfnumber(res, "subslotname");
5272 0 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5273 0 : i_subpublications = PQfnumber(res, "subpublications");
5274 0 : i_suborigin = PQfnumber(res, "suborigin");
5275 0 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5276 :
5277 0 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5278 :
5279 0 : for (i = 0; i < ntups; i++)
5280 : {
5281 0 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5282 0 : subinfo[i].dobj.catId.tableoid =
5283 0 : atooid(PQgetvalue(res, i, i_tableoid));
5284 0 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5285 0 : AssignDumpId(&subinfo[i].dobj);
5286 0 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5287 0 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5288 :
5289 0 : subinfo[i].subenabled =
5290 0 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5291 0 : subinfo[i].subbinary =
5292 0 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5293 0 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5294 0 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5295 0 : subinfo[i].subdisableonerr =
5296 0 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5297 0 : subinfo[i].subpasswordrequired =
5298 0 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5299 0 : subinfo[i].subrunasowner =
5300 0 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5301 0 : subinfo[i].subfailover =
5302 0 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5303 0 : subinfo[i].subretaindeadtuples =
5304 0 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5305 0 : subinfo[i].submaxretention =
5306 0 : atoi(PQgetvalue(res, i, i_submaxretention));
5307 0 : subinfo[i].subconninfo =
5308 0 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5309 0 : if (PQgetisnull(res, i, i_subslotname))
5310 0 : subinfo[i].subslotname = NULL;
5311 : else
5312 0 : subinfo[i].subslotname =
5313 0 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5314 0 : subinfo[i].subsynccommit =
5315 0 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5316 0 : subinfo[i].subpublications =
5317 0 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5318 0 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5319 0 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5320 0 : subinfo[i].suboriginremotelsn = NULL;
5321 : else
5322 0 : subinfo[i].suboriginremotelsn =
5323 0 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5324 :
5325 : /* Decide whether we want to dump it */
5326 0 : selectDumpableObject(&(subinfo[i].dobj), fout);
5327 0 : }
5328 0 : PQclear(res);
5329 :
5330 0 : destroyPQExpBuffer(query);
5331 0 : }
5332 :
5333 : /*
5334 : * getSubscriptionRelations
5335 : * Get information about subscription membership for dumpable relations. This
5336 : * will be used only in binary-upgrade mode for PG17 or later versions.
5337 : */
5338 : void
5339 0 : getSubscriptionRelations(Archive *fout)
5340 : {
5341 0 : DumpOptions *dopt = fout->dopt;
5342 0 : SubscriptionInfo *subinfo = NULL;
5343 0 : SubRelInfo *subrinfo;
5344 0 : PGresult *res;
5345 0 : int i_srsubid;
5346 0 : int i_srrelid;
5347 0 : int i_srsubstate;
5348 0 : int i_srsublsn;
5349 0 : int ntups;
5350 0 : Oid last_srsubid = InvalidOid;
5351 :
5352 0 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5353 0 : fout->remoteVersion < 170000)
5354 0 : return;
5355 :
5356 0 : res = ExecuteSqlQuery(fout,
5357 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5358 : "FROM pg_catalog.pg_subscription_rel "
5359 : "ORDER BY srsubid",
5360 : PGRES_TUPLES_OK);
5361 0 : ntups = PQntuples(res);
5362 0 : if (ntups == 0)
5363 0 : goto cleanup;
5364 :
5365 : /* Get pg_subscription_rel attributes */
5366 0 : i_srsubid = PQfnumber(res, "srsubid");
5367 0 : i_srrelid = PQfnumber(res, "srrelid");
5368 0 : i_srsubstate = PQfnumber(res, "srsubstate");
5369 0 : i_srsublsn = PQfnumber(res, "srsublsn");
5370 :
5371 0 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5372 0 : for (int i = 0; i < ntups; i++)
5373 : {
5374 0 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5375 0 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5376 0 : TableInfo *tblinfo;
5377 :
5378 : /*
5379 : * If we switched to a new subscription, check if the subscription
5380 : * exists.
5381 : */
5382 0 : if (cur_srsubid != last_srsubid)
5383 : {
5384 0 : subinfo = findSubscriptionByOid(cur_srsubid);
5385 0 : if (subinfo == NULL)
5386 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5387 :
5388 0 : last_srsubid = cur_srsubid;
5389 0 : }
5390 :
5391 0 : tblinfo = findTableByOid(relid);
5392 0 : if (tblinfo == NULL)
5393 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5394 : relid);
5395 :
5396 : /* OK, make a DumpableObject for this relationship */
5397 0 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5398 0 : subrinfo[i].dobj.catId.tableoid = relid;
5399 0 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5400 0 : AssignDumpId(&subrinfo[i].dobj);
5401 0 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5402 0 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5403 0 : subrinfo[i].subinfo = subinfo;
5404 0 : subrinfo[i].tblinfo = tblinfo;
5405 0 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5406 0 : if (PQgetisnull(res, i, i_srsublsn))
5407 0 : subrinfo[i].srsublsn = NULL;
5408 : else
5409 0 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5410 :
5411 : /* Decide whether we want to dump it */
5412 0 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5413 0 : }
5414 :
5415 : cleanup:
5416 0 : PQclear(res);
5417 0 : }
5418 :
5419 : /*
5420 : * dumpSubscriptionTable
5421 : * Dump the definition of the given subscription table mapping. This will be
5422 : * used only in binary-upgrade mode for PG17 or later versions.
5423 : */
5424 : static void
5425 0 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5426 : {
5427 0 : DumpOptions *dopt = fout->dopt;
5428 0 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5429 0 : PQExpBuffer query;
5430 0 : char *tag;
5431 :
5432 : /* Do nothing if not dumping schema */
5433 0 : if (!dopt->dumpSchema)
5434 0 : return;
5435 :
5436 0 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5437 :
5438 0 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5439 :
5440 0 : query = createPQExpBuffer();
5441 :
5442 0 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5443 : {
5444 : /*
5445 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5446 : * to pg_subscription_rel table. This will be used only in
5447 : * binary-upgrade mode.
5448 : */
5449 0 : appendPQExpBufferStr(query,
5450 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5451 0 : appendPQExpBufferStr(query,
5452 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5453 0 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5454 0 : appendPQExpBuffer(query,
5455 : ", %u, '%c'",
5456 0 : subrinfo->tblinfo->dobj.catId.oid,
5457 0 : subrinfo->srsubstate);
5458 :
5459 0 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5460 0 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5461 : else
5462 0 : appendPQExpBufferStr(query, ", NULL");
5463 :
5464 0 : appendPQExpBufferStr(query, ");\n");
5465 0 : }
5466 :
5467 : /*
5468 : * There is no point in creating a drop query as the drop is done by table
5469 : * drop. (If you think to change this, see also _printTocEntry().)
5470 : * Although this object doesn't really have ownership as such, set the
5471 : * owner field anyway to ensure that the command is run by the correct
5472 : * role at restore time.
5473 : */
5474 0 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5475 0 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5476 0 : ARCHIVE_OPTS(.tag = tag,
5477 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5478 : .owner = subinfo->rolname,
5479 : .description = "SUBSCRIPTION TABLE",
5480 : .section = SECTION_POST_DATA,
5481 : .createStmt = query->data));
5482 :
5483 : /* These objects can't currently have comments or seclabels */
5484 :
5485 0 : free(tag);
5486 0 : destroyPQExpBuffer(query);
5487 0 : }
5488 :
5489 : /*
5490 : * dumpSubscription
5491 : * dump the definition of the given subscription
5492 : */
5493 : static void
5494 0 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5495 : {
5496 0 : DumpOptions *dopt = fout->dopt;
5497 0 : PQExpBuffer delq;
5498 0 : PQExpBuffer query;
5499 0 : PQExpBuffer publications;
5500 0 : char *qsubname;
5501 0 : char **pubnames = NULL;
5502 0 : int npubnames = 0;
5503 0 : int i;
5504 :
5505 : /* Do nothing if not dumping schema */
5506 0 : if (!dopt->dumpSchema)
5507 0 : return;
5508 :
5509 0 : delq = createPQExpBuffer();
5510 0 : query = createPQExpBuffer();
5511 :
5512 0 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5513 :
5514 0 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5515 0 : qsubname);
5516 :
5517 0 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5518 0 : qsubname);
5519 0 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5520 :
5521 : /* Build list of quoted publications and append them to query. */
5522 0 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5523 0 : pg_fatal("could not parse %s array", "subpublications");
5524 :
5525 0 : publications = createPQExpBuffer();
5526 0 : for (i = 0; i < npubnames; i++)
5527 : {
5528 0 : if (i > 0)
5529 0 : appendPQExpBufferStr(publications, ", ");
5530 :
5531 0 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5532 0 : }
5533 :
5534 0 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5535 0 : if (subinfo->subslotname)
5536 0 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5537 : else
5538 0 : appendPQExpBufferStr(query, "NONE");
5539 :
5540 0 : if (subinfo->subbinary)
5541 0 : appendPQExpBufferStr(query, ", binary = true");
5542 :
5543 0 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5544 0 : appendPQExpBufferStr(query, ", streaming = on");
5545 0 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5546 0 : appendPQExpBufferStr(query, ", streaming = parallel");
5547 : else
5548 0 : appendPQExpBufferStr(query, ", streaming = off");
5549 :
5550 0 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5551 0 : appendPQExpBufferStr(query, ", two_phase = on");
5552 :
5553 0 : if (subinfo->subdisableonerr)
5554 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5555 :
5556 0 : if (!subinfo->subpasswordrequired)
5557 0 : appendPQExpBufferStr(query, ", password_required = false");
5558 :
5559 0 : if (subinfo->subrunasowner)
5560 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5561 :
5562 0 : if (subinfo->subfailover)
5563 0 : appendPQExpBufferStr(query, ", failover = true");
5564 :
5565 0 : if (subinfo->subretaindeadtuples)
5566 0 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5567 :
5568 0 : if (subinfo->submaxretention)
5569 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5570 :
5571 0 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5572 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5573 :
5574 0 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5575 0 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5576 :
5577 0 : appendPQExpBufferStr(query, ");\n");
5578 :
5579 : /*
5580 : * In binary-upgrade mode, we allow the replication to continue after the
5581 : * upgrade.
5582 : */
5583 0 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5584 : {
5585 0 : if (subinfo->suboriginremotelsn)
5586 : {
5587 : /*
5588 : * Preserve the remote_lsn for the subscriber's replication
5589 : * origin. This value is required to start the replication from
5590 : * the position before the upgrade. This value will be stale if
5591 : * the publisher gets upgraded before the subscriber node.
5592 : * However, this shouldn't be a problem as the upgrade of the
5593 : * publisher ensures that all the transactions were replicated
5594 : * before upgrading it.
5595 : */
5596 0 : appendPQExpBufferStr(query,
5597 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5598 0 : appendPQExpBufferStr(query,
5599 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5600 0 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5601 0 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5602 0 : }
5603 :
5604 0 : if (subinfo->subenabled)
5605 : {
5606 : /*
5607 : * Enable the subscription to allow the replication to continue
5608 : * after the upgrade.
5609 : */
5610 0 : appendPQExpBufferStr(query,
5611 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5612 0 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5613 0 : }
5614 0 : }
5615 :
5616 0 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5617 0 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5618 0 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5619 : .owner = subinfo->rolname,
5620 : .description = "SUBSCRIPTION",
5621 : .section = SECTION_POST_DATA,
5622 : .createStmt = query->data,
5623 : .dropStmt = delq->data));
5624 :
5625 0 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5626 0 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5627 0 : NULL, subinfo->rolname,
5628 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5629 :
5630 0 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5631 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5632 0 : NULL, subinfo->rolname,
5633 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5634 :
5635 0 : destroyPQExpBuffer(publications);
5636 0 : free(pubnames);
5637 :
5638 0 : destroyPQExpBuffer(delq);
5639 0 : destroyPQExpBuffer(query);
5640 0 : free(qsubname);
5641 0 : }
5642 :
5643 : /*
5644 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5645 : * the object needs.
5646 : */
5647 : static void
5648 0 : append_depends_on_extension(Archive *fout,
5649 : PQExpBuffer create,
5650 : const DumpableObject *dobj,
5651 : const char *catalog,
5652 : const char *keyword,
5653 : const char *objname)
5654 : {
5655 0 : if (dobj->depends_on_ext)
5656 : {
5657 0 : char *nm;
5658 0 : PGresult *res;
5659 0 : PQExpBuffer query;
5660 0 : int ntups;
5661 0 : int i_extname;
5662 0 : int i;
5663 :
5664 : /* dodge fmtId() non-reentrancy */
5665 0 : nm = pg_strdup(objname);
5666 :
5667 0 : query = createPQExpBuffer();
5668 0 : appendPQExpBuffer(query,
5669 : "SELECT e.extname "
5670 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5671 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5672 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5673 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5674 0 : catalog,
5675 0 : dobj->catId.oid);
5676 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5677 0 : ntups = PQntuples(res);
5678 0 : i_extname = PQfnumber(res, "extname");
5679 0 : for (i = 0; i < ntups; i++)
5680 : {
5681 0 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5682 0 : keyword, nm,
5683 0 : fmtId(PQgetvalue(res, i, i_extname)));
5684 0 : }
5685 :
5686 0 : PQclear(res);
5687 0 : destroyPQExpBuffer(query);
5688 0 : pg_free(nm);
5689 0 : }
5690 0 : }
5691 :
5692 : static Oid
5693 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5694 : {
5695 : /*
5696 : * If the old version didn't assign an array type, but the new version
5697 : * does, we must select an unused type OID to assign. This currently only
5698 : * happens for domains, when upgrading pre-v11 to v11 and up.
5699 : *
5700 : * Note: local state here is kind of ugly, but we must have some, since we
5701 : * mustn't choose the same unused OID more than once.
5702 : */
5703 : static Oid next_possible_free_oid = FirstNormalObjectId;
5704 0 : PGresult *res;
5705 0 : bool is_dup;
5706 :
5707 0 : do
5708 : {
5709 0 : ++next_possible_free_oid;
5710 0 : printfPQExpBuffer(upgrade_query,
5711 : "SELECT EXISTS(SELECT 1 "
5712 : "FROM pg_catalog.pg_type "
5713 : "WHERE oid = '%u'::pg_catalog.oid);",
5714 0 : next_possible_free_oid);
5715 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5716 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5717 0 : PQclear(res);
5718 0 : } while (is_dup);
5719 :
5720 0 : return next_possible_free_oid;
5721 0 : }
5722 :
5723 : static void
5724 0 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5725 : PQExpBuffer upgrade_buffer,
5726 : Oid pg_type_oid,
5727 : bool force_array_type,
5728 : bool include_multirange_type)
5729 : {
5730 0 : PQExpBuffer upgrade_query = createPQExpBuffer();
5731 0 : PGresult *res;
5732 0 : Oid pg_type_array_oid;
5733 0 : Oid pg_type_multirange_oid;
5734 0 : Oid pg_type_multirange_array_oid;
5735 0 : TypeInfo *tinfo;
5736 :
5737 0 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5738 0 : appendPQExpBuffer(upgrade_buffer,
5739 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5740 0 : pg_type_oid);
5741 :
5742 0 : tinfo = findTypeByOid(pg_type_oid);
5743 0 : if (tinfo)
5744 0 : pg_type_array_oid = tinfo->typarray;
5745 : else
5746 0 : pg_type_array_oid = InvalidOid;
5747 :
5748 0 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5749 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5750 :
5751 0 : if (OidIsValid(pg_type_array_oid))
5752 : {
5753 0 : appendPQExpBufferStr(upgrade_buffer,
5754 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5755 0 : appendPQExpBuffer(upgrade_buffer,
5756 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5757 0 : pg_type_array_oid);
5758 0 : }
5759 :
5760 : /*
5761 : * Pre-set the multirange type oid and its own array type oid.
5762 : */
5763 0 : if (include_multirange_type)
5764 : {
5765 0 : if (fout->remoteVersion >= 140000)
5766 : {
5767 0 : printfPQExpBuffer(upgrade_query,
5768 : "SELECT t.oid, t.typarray "
5769 : "FROM pg_catalog.pg_type t "
5770 : "JOIN pg_catalog.pg_range r "
5771 : "ON t.oid = r.rngmultitypid "
5772 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5773 0 : pg_type_oid);
5774 :
5775 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5776 :
5777 0 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5778 0 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5779 :
5780 0 : PQclear(res);
5781 0 : }
5782 : else
5783 : {
5784 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5785 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5786 : }
5787 :
5788 0 : appendPQExpBufferStr(upgrade_buffer,
5789 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5790 0 : appendPQExpBuffer(upgrade_buffer,
5791 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5792 0 : pg_type_multirange_oid);
5793 0 : appendPQExpBufferStr(upgrade_buffer,
5794 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5795 0 : appendPQExpBuffer(upgrade_buffer,
5796 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5797 0 : pg_type_multirange_array_oid);
5798 0 : }
5799 :
5800 0 : destroyPQExpBuffer(upgrade_query);
5801 0 : }
5802 :
5803 : static void
5804 0 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5805 : PQExpBuffer upgrade_buffer,
5806 : const TableInfo *tbinfo)
5807 : {
5808 0 : Oid pg_type_oid = tbinfo->reltype;
5809 :
5810 0 : if (OidIsValid(pg_type_oid))
5811 0 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5812 0 : pg_type_oid, false, false);
5813 0 : }
5814 :
5815 : /*
5816 : * bsearch() comparator for BinaryUpgradeClassOidItem
5817 : */
5818 : static int
5819 0 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5820 : {
5821 0 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5822 0 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5823 :
5824 0 : return pg_cmp_u32(v1.oid, v2.oid);
5825 0 : }
5826 :
5827 : /*
5828 : * collectBinaryUpgradeClassOids
5829 : *
5830 : * Construct a table of pg_class information required for
5831 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5832 : * lookup.
5833 : */
5834 : static void
5835 0 : collectBinaryUpgradeClassOids(Archive *fout)
5836 : {
5837 0 : PGresult *res;
5838 0 : const char *query;
5839 :
5840 0 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5841 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5842 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5843 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5844 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5845 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5846 : "ORDER BY c.oid;";
5847 :
5848 0 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5849 :
5850 0 : nbinaryUpgradeClassOids = PQntuples(res);
5851 0 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5852 0 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5853 :
5854 0 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5855 : {
5856 0 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5857 0 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5858 0 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5859 0 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5860 0 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5861 0 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5862 0 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5863 0 : }
5864 :
5865 0 : PQclear(res);
5866 0 : }
5867 :
5868 : static void
5869 0 : binary_upgrade_set_pg_class_oids(Archive *fout,
5870 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5871 : {
5872 0 : BinaryUpgradeClassOidItem key = {0};
5873 0 : BinaryUpgradeClassOidItem *entry;
5874 :
5875 0 : Assert(binaryUpgradeClassOids);
5876 :
5877 : /*
5878 : * Preserve the OID and relfilenumber of the table, table's index, table's
5879 : * toast table and toast table's index if any.
5880 : *
5881 : * One complexity is that the current table definition might not require
5882 : * the creation of a TOAST table, but the old database might have a TOAST
5883 : * table that was created earlier, before some wide columns were dropped.
5884 : * By setting the TOAST oid we force creation of the TOAST heap and index
5885 : * by the new backend, so we can copy the files during binary upgrade
5886 : * without worrying about this case.
5887 : */
5888 0 : key.oid = pg_class_oid;
5889 0 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5890 : sizeof(BinaryUpgradeClassOidItem),
5891 : BinaryUpgradeClassOidItemCmp);
5892 :
5893 0 : appendPQExpBufferStr(upgrade_buffer,
5894 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5895 :
5896 0 : if (entry->relkind != RELKIND_INDEX &&
5897 0 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5898 : {
5899 0 : appendPQExpBuffer(upgrade_buffer,
5900 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5901 0 : pg_class_oid);
5902 :
5903 : /*
5904 : * Not every relation has storage. Also, in a pre-v12 database,
5905 : * partitioned tables have a relfilenumber, which should not be
5906 : * preserved when upgrading.
5907 : */
5908 0 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5909 0 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5910 0 : appendPQExpBuffer(upgrade_buffer,
5911 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5912 0 : entry->relfilenumber);
5913 :
5914 : /*
5915 : * In a pre-v12 database, partitioned tables might be marked as having
5916 : * toast tables, but we should ignore them if so.
5917 : */
5918 0 : if (OidIsValid(entry->toast_oid) &&
5919 0 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5920 : {
5921 0 : appendPQExpBuffer(upgrade_buffer,
5922 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5923 0 : entry->toast_oid);
5924 0 : appendPQExpBuffer(upgrade_buffer,
5925 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5926 0 : entry->toast_relfilenumber);
5927 :
5928 : /* every toast table has an index */
5929 0 : appendPQExpBuffer(upgrade_buffer,
5930 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5931 0 : entry->toast_index_oid);
5932 0 : appendPQExpBuffer(upgrade_buffer,
5933 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5934 0 : entry->toast_index_relfilenumber);
5935 0 : }
5936 0 : }
5937 : else
5938 : {
5939 : /* Preserve the OID and relfilenumber of the index */
5940 0 : appendPQExpBuffer(upgrade_buffer,
5941 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5942 0 : pg_class_oid);
5943 0 : appendPQExpBuffer(upgrade_buffer,
5944 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5945 0 : entry->relfilenumber);
5946 : }
5947 :
5948 0 : appendPQExpBufferChar(upgrade_buffer, '\n');
5949 0 : }
5950 :
5951 : /*
5952 : * If the DumpableObject is a member of an extension, add a suitable
5953 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5954 : *
5955 : * For somewhat historical reasons, objname should already be quoted,
5956 : * but not objnamespace (if any).
5957 : */
5958 : static void
5959 0 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5960 : const DumpableObject *dobj,
5961 : const char *objtype,
5962 : const char *objname,
5963 : const char *objnamespace)
5964 : {
5965 0 : DumpableObject *extobj = NULL;
5966 0 : int i;
5967 :
5968 0 : if (!dobj->ext_member)
5969 0 : return;
5970 :
5971 : /*
5972 : * Find the parent extension. We could avoid this search if we wanted to
5973 : * add a link field to DumpableObject, but the space costs of that would
5974 : * be considerable. We assume that member objects could only have a
5975 : * direct dependency on their own extension, not any others.
5976 : */
5977 0 : for (i = 0; i < dobj->nDeps; i++)
5978 : {
5979 0 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5980 0 : if (extobj && extobj->objType == DO_EXTENSION)
5981 0 : break;
5982 0 : extobj = NULL;
5983 0 : }
5984 0 : if (extobj == NULL)
5985 0 : pg_fatal("could not find parent extension for %s %s",
5986 : objtype, objname);
5987 :
5988 0 : appendPQExpBufferStr(upgrade_buffer,
5989 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5990 0 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5991 0 : fmtId(extobj->name),
5992 0 : objtype);
5993 0 : if (objnamespace && *objnamespace)
5994 0 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5995 0 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5996 0 : }
5997 :
5998 : /*
5999 : * getNamespaces:
6000 : * get information about all namespaces in the system catalogs
6001 : */
6002 : void
6003 0 : getNamespaces(Archive *fout)
6004 : {
6005 0 : PGresult *res;
6006 0 : int ntups;
6007 0 : int i;
6008 0 : PQExpBuffer query;
6009 0 : NamespaceInfo *nsinfo;
6010 0 : int i_tableoid;
6011 0 : int i_oid;
6012 0 : int i_nspname;
6013 0 : int i_nspowner;
6014 0 : int i_nspacl;
6015 0 : int i_acldefault;
6016 :
6017 0 : query = createPQExpBuffer();
6018 :
6019 : /*
6020 : * we fetch all namespaces including system ones, so that every object we
6021 : * read in can be linked to a containing namespace.
6022 : */
6023 0 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6024 : "n.nspowner, "
6025 : "n.nspacl, "
6026 : "acldefault('n', n.nspowner) AS acldefault "
6027 : "FROM pg_namespace n");
6028 :
6029 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6030 :
6031 0 : ntups = PQntuples(res);
6032 :
6033 0 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6034 :
6035 0 : i_tableoid = PQfnumber(res, "tableoid");
6036 0 : i_oid = PQfnumber(res, "oid");
6037 0 : i_nspname = PQfnumber(res, "nspname");
6038 0 : i_nspowner = PQfnumber(res, "nspowner");
6039 0 : i_nspacl = PQfnumber(res, "nspacl");
6040 0 : i_acldefault = PQfnumber(res, "acldefault");
6041 :
6042 0 : for (i = 0; i < ntups; i++)
6043 : {
6044 0 : const char *nspowner;
6045 :
6046 0 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6047 0 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6048 0 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6049 0 : AssignDumpId(&nsinfo[i].dobj);
6050 0 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6051 0 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6052 0 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6053 0 : nsinfo[i].dacl.privtype = 0;
6054 0 : nsinfo[i].dacl.initprivs = NULL;
6055 0 : nspowner = PQgetvalue(res, i, i_nspowner);
6056 0 : nsinfo[i].nspowner = atooid(nspowner);
6057 0 : nsinfo[i].rolname = getRoleName(nspowner);
6058 :
6059 : /* Decide whether to dump this namespace */
6060 0 : selectDumpableNamespace(&nsinfo[i], fout);
6061 :
6062 : /* Mark whether namespace has an ACL */
6063 0 : if (!PQgetisnull(res, i, i_nspacl))
6064 0 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6065 :
6066 : /*
6067 : * We ignore any pg_init_privs.initprivs entry for the public schema
6068 : * and assume a predetermined default, for several reasons. First,
6069 : * dropping and recreating the schema removes its pg_init_privs entry,
6070 : * but an empty destination database starts with this ACL nonetheless.
6071 : * Second, we support dump/reload of public schema ownership changes.
6072 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6073 : * initprivs continues to reflect the initial owner. Hence,
6074 : * synthesize the value that nspacl will have after the restore's
6075 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6076 : * match the source's ACL, even if the latter was an initdb-default
6077 : * ACL, which changed in v15. An upgrade pulls in changes to most
6078 : * system object ACLs that the DBA had not customized. We've made the
6079 : * public schema depart from that, because changing its ACL so easily
6080 : * breaks applications.
6081 : */
6082 0 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6083 : {
6084 0 : PQExpBuffer aclarray = createPQExpBuffer();
6085 0 : PQExpBuffer aclitem = createPQExpBuffer();
6086 :
6087 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6088 0 : appendPQExpBufferChar(aclarray, '{');
6089 0 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6090 0 : appendPQExpBufferStr(aclitem, "=UC/");
6091 0 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6092 0 : appendPGArray(aclarray, aclitem->data);
6093 0 : resetPQExpBuffer(aclitem);
6094 0 : appendPQExpBufferStr(aclitem, "=U/");
6095 0 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6096 0 : appendPGArray(aclarray, aclitem->data);
6097 0 : appendPQExpBufferChar(aclarray, '}');
6098 :
6099 0 : nsinfo[i].dacl.privtype = 'i';
6100 0 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6101 0 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6102 :
6103 0 : destroyPQExpBuffer(aclarray);
6104 0 : destroyPQExpBuffer(aclitem);
6105 0 : }
6106 0 : }
6107 :
6108 0 : PQclear(res);
6109 0 : destroyPQExpBuffer(query);
6110 0 : }
6111 :
6112 : /*
6113 : * findNamespace:
6114 : * given a namespace OID, look up the info read by getNamespaces
6115 : */
6116 : static NamespaceInfo *
6117 0 : findNamespace(Oid nsoid)
6118 : {
6119 0 : NamespaceInfo *nsinfo;
6120 :
6121 0 : nsinfo = findNamespaceByOid(nsoid);
6122 0 : if (nsinfo == NULL)
6123 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6124 0 : return nsinfo;
6125 0 : }
6126 :
6127 : /*
6128 : * getExtensions:
6129 : * read all extensions in the system catalogs and return them in the
6130 : * ExtensionInfo* structure
6131 : *
6132 : * numExtensions is set to the number of extensions read in
6133 : */
6134 : ExtensionInfo *
6135 0 : getExtensions(Archive *fout, int *numExtensions)
6136 : {
6137 0 : DumpOptions *dopt = fout->dopt;
6138 0 : PGresult *res;
6139 0 : int ntups;
6140 0 : int i;
6141 0 : PQExpBuffer query;
6142 0 : ExtensionInfo *extinfo = NULL;
6143 0 : int i_tableoid;
6144 0 : int i_oid;
6145 0 : int i_extname;
6146 0 : int i_nspname;
6147 0 : int i_extrelocatable;
6148 0 : int i_extversion;
6149 0 : int i_extconfig;
6150 0 : int i_extcondition;
6151 :
6152 0 : query = createPQExpBuffer();
6153 :
6154 0 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6155 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6156 : "FROM pg_extension x "
6157 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6158 :
6159 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6160 :
6161 0 : ntups = PQntuples(res);
6162 0 : if (ntups == 0)
6163 0 : goto cleanup;
6164 :
6165 0 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6166 :
6167 0 : i_tableoid = PQfnumber(res, "tableoid");
6168 0 : i_oid = PQfnumber(res, "oid");
6169 0 : i_extname = PQfnumber(res, "extname");
6170 0 : i_nspname = PQfnumber(res, "nspname");
6171 0 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6172 0 : i_extversion = PQfnumber(res, "extversion");
6173 0 : i_extconfig = PQfnumber(res, "extconfig");
6174 0 : i_extcondition = PQfnumber(res, "extcondition");
6175 :
6176 0 : for (i = 0; i < ntups; i++)
6177 : {
6178 0 : extinfo[i].dobj.objType = DO_EXTENSION;
6179 0 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6180 0 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6181 0 : AssignDumpId(&extinfo[i].dobj);
6182 0 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6183 0 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6184 0 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6185 0 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6186 0 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6187 0 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6188 :
6189 : /* Decide whether we want to dump it */
6190 0 : selectDumpableExtension(&(extinfo[i]), dopt);
6191 0 : }
6192 :
6193 : cleanup:
6194 0 : PQclear(res);
6195 0 : destroyPQExpBuffer(query);
6196 :
6197 0 : *numExtensions = ntups;
6198 :
6199 0 : return extinfo;
6200 0 : }
6201 :
6202 : /*
6203 : * getTypes:
6204 : * get information about all types in the system catalogs
6205 : *
6206 : * NB: this must run after getFuncs() because we assume we can do
6207 : * findFuncByOid().
6208 : */
6209 : void
6210 0 : getTypes(Archive *fout)
6211 : {
6212 0 : PGresult *res;
6213 0 : int ntups;
6214 0 : int i;
6215 0 : PQExpBuffer query = createPQExpBuffer();
6216 0 : TypeInfo *tyinfo;
6217 0 : ShellTypeInfo *stinfo;
6218 0 : int i_tableoid;
6219 0 : int i_oid;
6220 0 : int i_typname;
6221 0 : int i_typnamespace;
6222 0 : int i_typacl;
6223 0 : int i_acldefault;
6224 0 : int i_typowner;
6225 0 : int i_typelem;
6226 0 : int i_typrelid;
6227 0 : int i_typrelkind;
6228 0 : int i_typtype;
6229 0 : int i_typisdefined;
6230 0 : int i_isarray;
6231 0 : int i_typarray;
6232 :
6233 : /*
6234 : * we include even the built-in types because those may be used as array
6235 : * elements by user-defined types
6236 : *
6237 : * we filter out the built-in types when we dump out the types
6238 : *
6239 : * same approach for undefined (shell) types and array types
6240 : *
6241 : * Note: as of 8.3 we can reliably detect whether a type is an
6242 : * auto-generated array type by checking the element type's typarray.
6243 : * (Before that the test is capable of generating false positives.) We
6244 : * still check for name beginning with '_', though, so as to avoid the
6245 : * cost of the subselect probe for all standard types. This would have to
6246 : * be revisited if the backend ever allows renaming of array types.
6247 : */
6248 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6249 : "typnamespace, typacl, "
6250 : "acldefault('T', typowner) AS acldefault, "
6251 : "typowner, "
6252 : "typelem, typrelid, typarray, "
6253 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6254 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6255 : "typtype, typisdefined, "
6256 : "typname[0] = '_' AND typelem != 0 AND "
6257 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6258 : "FROM pg_type");
6259 :
6260 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6261 :
6262 0 : ntups = PQntuples(res);
6263 :
6264 0 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6265 :
6266 0 : i_tableoid = PQfnumber(res, "tableoid");
6267 0 : i_oid = PQfnumber(res, "oid");
6268 0 : i_typname = PQfnumber(res, "typname");
6269 0 : i_typnamespace = PQfnumber(res, "typnamespace");
6270 0 : i_typacl = PQfnumber(res, "typacl");
6271 0 : i_acldefault = PQfnumber(res, "acldefault");
6272 0 : i_typowner = PQfnumber(res, "typowner");
6273 0 : i_typelem = PQfnumber(res, "typelem");
6274 0 : i_typrelid = PQfnumber(res, "typrelid");
6275 0 : i_typrelkind = PQfnumber(res, "typrelkind");
6276 0 : i_typtype = PQfnumber(res, "typtype");
6277 0 : i_typisdefined = PQfnumber(res, "typisdefined");
6278 0 : i_isarray = PQfnumber(res, "isarray");
6279 0 : i_typarray = PQfnumber(res, "typarray");
6280 :
6281 0 : for (i = 0; i < ntups; i++)
6282 : {
6283 0 : tyinfo[i].dobj.objType = DO_TYPE;
6284 0 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6285 0 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6286 0 : AssignDumpId(&tyinfo[i].dobj);
6287 0 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6288 0 : tyinfo[i].dobj.namespace =
6289 0 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6290 0 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6291 0 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6292 0 : tyinfo[i].dacl.privtype = 0;
6293 0 : tyinfo[i].dacl.initprivs = NULL;
6294 0 : tyinfo[i].ftypname = NULL; /* may get filled later */
6295 0 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6296 0 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6297 0 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6298 0 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6299 0 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6300 0 : tyinfo[i].shellType = NULL;
6301 :
6302 0 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6303 0 : tyinfo[i].isDefined = true;
6304 : else
6305 0 : tyinfo[i].isDefined = false;
6306 :
6307 0 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6308 0 : tyinfo[i].isArray = true;
6309 : else
6310 0 : tyinfo[i].isArray = false;
6311 :
6312 0 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6313 :
6314 0 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6315 0 : tyinfo[i].isMultirange = true;
6316 : else
6317 0 : tyinfo[i].isMultirange = false;
6318 :
6319 : /* Decide whether we want to dump it */
6320 0 : selectDumpableType(&tyinfo[i], fout);
6321 :
6322 : /* Mark whether type has an ACL */
6323 0 : if (!PQgetisnull(res, i, i_typacl))
6324 0 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6325 :
6326 : /*
6327 : * If it's a domain, fetch info about its constraints, if any
6328 : */
6329 0 : tyinfo[i].nDomChecks = 0;
6330 0 : tyinfo[i].domChecks = NULL;
6331 0 : tyinfo[i].notnull = NULL;
6332 0 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6333 0 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6334 0 : getDomainConstraints(fout, &(tyinfo[i]));
6335 :
6336 : /*
6337 : * If it's a base type, make a DumpableObject representing a shell
6338 : * definition of the type. We will need to dump that ahead of the I/O
6339 : * functions for the type. Similarly, range types need a shell
6340 : * definition in case they have a canonicalize function.
6341 : *
6342 : * Note: the shell type doesn't have a catId. You might think it
6343 : * should copy the base type's catId, but then it might capture the
6344 : * pg_depend entries for the type, which we don't want.
6345 : */
6346 0 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6347 0 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6348 0 : tyinfo[i].typtype == TYPTYPE_RANGE))
6349 : {
6350 0 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6351 0 : stinfo->dobj.objType = DO_SHELL_TYPE;
6352 0 : stinfo->dobj.catId = nilCatalogId;
6353 0 : AssignDumpId(&stinfo->dobj);
6354 0 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6355 0 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6356 0 : stinfo->baseType = &(tyinfo[i]);
6357 0 : tyinfo[i].shellType = stinfo;
6358 :
6359 : /*
6360 : * Initially mark the shell type as not to be dumped. We'll only
6361 : * dump it if the I/O or canonicalize functions need to be dumped;
6362 : * this is taken care of while sorting dependencies.
6363 : */
6364 0 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6365 0 : }
6366 0 : }
6367 :
6368 0 : PQclear(res);
6369 :
6370 0 : destroyPQExpBuffer(query);
6371 0 : }
6372 :
6373 : /*
6374 : * getOperators:
6375 : * get information about all operators in the system catalogs
6376 : */
6377 : void
6378 0 : getOperators(Archive *fout)
6379 : {
6380 0 : PGresult *res;
6381 0 : int ntups;
6382 0 : int i;
6383 0 : PQExpBuffer query = createPQExpBuffer();
6384 0 : OprInfo *oprinfo;
6385 0 : int i_tableoid;
6386 0 : int i_oid;
6387 0 : int i_oprname;
6388 0 : int i_oprnamespace;
6389 0 : int i_oprowner;
6390 0 : int i_oprkind;
6391 0 : int i_oprleft;
6392 0 : int i_oprright;
6393 0 : int i_oprcode;
6394 :
6395 : /*
6396 : * find all operators, including builtin operators; we filter out
6397 : * system-defined operators at dump-out time.
6398 : */
6399 :
6400 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6401 : "oprnamespace, "
6402 : "oprowner, "
6403 : "oprkind, "
6404 : "oprleft, "
6405 : "oprright, "
6406 : "oprcode::oid AS oprcode "
6407 : "FROM pg_operator");
6408 :
6409 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6410 :
6411 0 : ntups = PQntuples(res);
6412 :
6413 0 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6414 :
6415 0 : i_tableoid = PQfnumber(res, "tableoid");
6416 0 : i_oid = PQfnumber(res, "oid");
6417 0 : i_oprname = PQfnumber(res, "oprname");
6418 0 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6419 0 : i_oprowner = PQfnumber(res, "oprowner");
6420 0 : i_oprkind = PQfnumber(res, "oprkind");
6421 0 : i_oprleft = PQfnumber(res, "oprleft");
6422 0 : i_oprright = PQfnumber(res, "oprright");
6423 0 : i_oprcode = PQfnumber(res, "oprcode");
6424 :
6425 0 : for (i = 0; i < ntups; i++)
6426 : {
6427 0 : oprinfo[i].dobj.objType = DO_OPERATOR;
6428 0 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6429 0 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6430 0 : AssignDumpId(&oprinfo[i].dobj);
6431 0 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6432 0 : oprinfo[i].dobj.namespace =
6433 0 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6434 0 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6435 0 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6436 0 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6437 0 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6438 0 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6439 :
6440 : /* Decide whether we want to dump it */
6441 0 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6442 0 : }
6443 :
6444 0 : PQclear(res);
6445 :
6446 0 : destroyPQExpBuffer(query);
6447 0 : }
6448 :
6449 : /*
6450 : * getCollations:
6451 : * get information about all collations in the system catalogs
6452 : */
6453 : void
6454 0 : getCollations(Archive *fout)
6455 : {
6456 0 : PGresult *res;
6457 0 : int ntups;
6458 0 : int i;
6459 0 : PQExpBuffer query;
6460 0 : CollInfo *collinfo;
6461 0 : int i_tableoid;
6462 0 : int i_oid;
6463 0 : int i_collname;
6464 0 : int i_collnamespace;
6465 0 : int i_collowner;
6466 0 : int i_collencoding;
6467 :
6468 0 : query = createPQExpBuffer();
6469 :
6470 : /*
6471 : * find all collations, including builtin collations; we filter out
6472 : * system-defined collations at dump-out time.
6473 : */
6474 :
6475 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6476 : "collnamespace, "
6477 : "collowner, "
6478 : "collencoding "
6479 : "FROM pg_collation");
6480 :
6481 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6482 :
6483 0 : ntups = PQntuples(res);
6484 :
6485 0 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6486 :
6487 0 : i_tableoid = PQfnumber(res, "tableoid");
6488 0 : i_oid = PQfnumber(res, "oid");
6489 0 : i_collname = PQfnumber(res, "collname");
6490 0 : i_collnamespace = PQfnumber(res, "collnamespace");
6491 0 : i_collowner = PQfnumber(res, "collowner");
6492 0 : i_collencoding = PQfnumber(res, "collencoding");
6493 :
6494 0 : for (i = 0; i < ntups; i++)
6495 : {
6496 0 : collinfo[i].dobj.objType = DO_COLLATION;
6497 0 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6498 0 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6499 0 : AssignDumpId(&collinfo[i].dobj);
6500 0 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6501 0 : collinfo[i].dobj.namespace =
6502 0 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6503 0 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6504 0 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6505 :
6506 : /* Decide whether we want to dump it */
6507 0 : selectDumpableObject(&(collinfo[i].dobj), fout);
6508 0 : }
6509 :
6510 0 : PQclear(res);
6511 :
6512 0 : destroyPQExpBuffer(query);
6513 0 : }
6514 :
6515 : /*
6516 : * getConversions:
6517 : * get information about all conversions in the system catalogs
6518 : */
6519 : void
6520 0 : getConversions(Archive *fout)
6521 : {
6522 0 : PGresult *res;
6523 0 : int ntups;
6524 0 : int i;
6525 0 : PQExpBuffer query;
6526 0 : ConvInfo *convinfo;
6527 0 : int i_tableoid;
6528 0 : int i_oid;
6529 0 : int i_conname;
6530 0 : int i_connamespace;
6531 0 : int i_conowner;
6532 :
6533 0 : query = createPQExpBuffer();
6534 :
6535 : /*
6536 : * find all conversions, including builtin conversions; we filter out
6537 : * system-defined conversions at dump-out time.
6538 : */
6539 :
6540 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6541 : "connamespace, "
6542 : "conowner "
6543 : "FROM pg_conversion");
6544 :
6545 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6546 :
6547 0 : ntups = PQntuples(res);
6548 :
6549 0 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6550 :
6551 0 : i_tableoid = PQfnumber(res, "tableoid");
6552 0 : i_oid = PQfnumber(res, "oid");
6553 0 : i_conname = PQfnumber(res, "conname");
6554 0 : i_connamespace = PQfnumber(res, "connamespace");
6555 0 : i_conowner = PQfnumber(res, "conowner");
6556 :
6557 0 : for (i = 0; i < ntups; i++)
6558 : {
6559 0 : convinfo[i].dobj.objType = DO_CONVERSION;
6560 0 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6561 0 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6562 0 : AssignDumpId(&convinfo[i].dobj);
6563 0 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6564 0 : convinfo[i].dobj.namespace =
6565 0 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6566 0 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6567 :
6568 : /* Decide whether we want to dump it */
6569 0 : selectDumpableObject(&(convinfo[i].dobj), fout);
6570 0 : }
6571 :
6572 0 : PQclear(res);
6573 :
6574 0 : destroyPQExpBuffer(query);
6575 0 : }
6576 :
6577 : /*
6578 : * getAccessMethods:
6579 : * get information about all user-defined access methods
6580 : */
6581 : void
6582 0 : getAccessMethods(Archive *fout)
6583 : {
6584 0 : PGresult *res;
6585 0 : int ntups;
6586 0 : int i;
6587 0 : PQExpBuffer query;
6588 0 : AccessMethodInfo *aminfo;
6589 0 : int i_tableoid;
6590 0 : int i_oid;
6591 0 : int i_amname;
6592 0 : int i_amhandler;
6593 0 : int i_amtype;
6594 :
6595 0 : query = createPQExpBuffer();
6596 :
6597 : /*
6598 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6599 : * ACCESS METHOD, so earlier versions usually have only built-in access
6600 : * methods. v9.6 also changed the access method API, replacing dozens of
6601 : * pg_am columns with amhandler. Even if a user created an access method
6602 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6603 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6604 : * pg_am just to facilitate findAccessMethodByOid() providing the
6605 : * OID-to-name mapping.
6606 : */
6607 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6608 0 : if (fout->remoteVersion >= 90600)
6609 0 : appendPQExpBufferStr(query,
6610 : "amtype, "
6611 : "amhandler::pg_catalog.regproc AS amhandler ");
6612 : else
6613 0 : appendPQExpBufferStr(query,
6614 : "'i'::pg_catalog.\"char\" AS amtype, "
6615 : "'-'::pg_catalog.regproc AS amhandler ");
6616 0 : appendPQExpBufferStr(query, "FROM pg_am");
6617 :
6618 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6619 :
6620 0 : ntups = PQntuples(res);
6621 :
6622 0 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6623 :
6624 0 : i_tableoid = PQfnumber(res, "tableoid");
6625 0 : i_oid = PQfnumber(res, "oid");
6626 0 : i_amname = PQfnumber(res, "amname");
6627 0 : i_amhandler = PQfnumber(res, "amhandler");
6628 0 : i_amtype = PQfnumber(res, "amtype");
6629 :
6630 0 : for (i = 0; i < ntups; i++)
6631 : {
6632 0 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6633 0 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6634 0 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6635 0 : AssignDumpId(&aminfo[i].dobj);
6636 0 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6637 0 : aminfo[i].dobj.namespace = NULL;
6638 0 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6639 0 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6640 :
6641 : /* Decide whether we want to dump it */
6642 0 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6643 0 : }
6644 :
6645 0 : PQclear(res);
6646 :
6647 0 : destroyPQExpBuffer(query);
6648 0 : }
6649 :
6650 :
6651 : /*
6652 : * getOpclasses:
6653 : * get information about all opclasses in the system catalogs
6654 : */
6655 : void
6656 0 : getOpclasses(Archive *fout)
6657 : {
6658 0 : PGresult *res;
6659 0 : int ntups;
6660 0 : int i;
6661 0 : PQExpBuffer query = createPQExpBuffer();
6662 0 : OpclassInfo *opcinfo;
6663 0 : int i_tableoid;
6664 0 : int i_oid;
6665 0 : int i_opcmethod;
6666 0 : int i_opcname;
6667 0 : int i_opcnamespace;
6668 0 : int i_opcowner;
6669 :
6670 : /*
6671 : * find all opclasses, including builtin opclasses; we filter out
6672 : * system-defined opclasses at dump-out time.
6673 : */
6674 :
6675 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6676 : "opcnamespace, "
6677 : "opcowner "
6678 : "FROM pg_opclass");
6679 :
6680 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6681 :
6682 0 : ntups = PQntuples(res);
6683 :
6684 0 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6685 :
6686 0 : i_tableoid = PQfnumber(res, "tableoid");
6687 0 : i_oid = PQfnumber(res, "oid");
6688 0 : i_opcmethod = PQfnumber(res, "opcmethod");
6689 0 : i_opcname = PQfnumber(res, "opcname");
6690 0 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6691 0 : i_opcowner = PQfnumber(res, "opcowner");
6692 :
6693 0 : for (i = 0; i < ntups; i++)
6694 : {
6695 0 : opcinfo[i].dobj.objType = DO_OPCLASS;
6696 0 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6697 0 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6698 0 : AssignDumpId(&opcinfo[i].dobj);
6699 0 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6700 0 : opcinfo[i].dobj.namespace =
6701 0 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6702 0 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6703 0 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6704 :
6705 : /* Decide whether we want to dump it */
6706 0 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6707 0 : }
6708 :
6709 0 : PQclear(res);
6710 :
6711 0 : destroyPQExpBuffer(query);
6712 0 : }
6713 :
6714 : /*
6715 : * getOpfamilies:
6716 : * get information about all opfamilies in the system catalogs
6717 : */
6718 : void
6719 0 : getOpfamilies(Archive *fout)
6720 : {
6721 0 : PGresult *res;
6722 0 : int ntups;
6723 0 : int i;
6724 0 : PQExpBuffer query;
6725 0 : OpfamilyInfo *opfinfo;
6726 0 : int i_tableoid;
6727 0 : int i_oid;
6728 0 : int i_opfmethod;
6729 0 : int i_opfname;
6730 0 : int i_opfnamespace;
6731 0 : int i_opfowner;
6732 :
6733 0 : query = createPQExpBuffer();
6734 :
6735 : /*
6736 : * find all opfamilies, including builtin opfamilies; we filter out
6737 : * system-defined opfamilies at dump-out time.
6738 : */
6739 :
6740 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6741 : "opfnamespace, "
6742 : "opfowner "
6743 : "FROM pg_opfamily");
6744 :
6745 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6746 :
6747 0 : ntups = PQntuples(res);
6748 :
6749 0 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6750 :
6751 0 : i_tableoid = PQfnumber(res, "tableoid");
6752 0 : i_oid = PQfnumber(res, "oid");
6753 0 : i_opfname = PQfnumber(res, "opfname");
6754 0 : i_opfmethod = PQfnumber(res, "opfmethod");
6755 0 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6756 0 : i_opfowner = PQfnumber(res, "opfowner");
6757 :
6758 0 : for (i = 0; i < ntups; i++)
6759 : {
6760 0 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6761 0 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6762 0 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6763 0 : AssignDumpId(&opfinfo[i].dobj);
6764 0 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6765 0 : opfinfo[i].dobj.namespace =
6766 0 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6767 0 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6768 0 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6769 :
6770 : /* Decide whether we want to dump it */
6771 0 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6772 0 : }
6773 :
6774 0 : PQclear(res);
6775 :
6776 0 : destroyPQExpBuffer(query);
6777 0 : }
6778 :
6779 : /*
6780 : * getAggregates:
6781 : * get information about all user-defined aggregates in the system catalogs
6782 : */
6783 : void
6784 0 : getAggregates(Archive *fout)
6785 : {
6786 0 : DumpOptions *dopt = fout->dopt;
6787 0 : PGresult *res;
6788 0 : int ntups;
6789 0 : int i;
6790 0 : PQExpBuffer query = createPQExpBuffer();
6791 0 : AggInfo *agginfo;
6792 0 : int i_tableoid;
6793 0 : int i_oid;
6794 0 : int i_aggname;
6795 0 : int i_aggnamespace;
6796 0 : int i_pronargs;
6797 0 : int i_proargtypes;
6798 0 : int i_proowner;
6799 0 : int i_aggacl;
6800 0 : int i_acldefault;
6801 :
6802 : /*
6803 : * Find all interesting aggregates. See comment in getFuncs() for the
6804 : * rationale behind the filtering logic.
6805 : */
6806 0 : if (fout->remoteVersion >= 90600)
6807 : {
6808 0 : const char *agg_check;
6809 :
6810 0 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6811 : : "p.proisagg");
6812 :
6813 0 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6814 : "p.proname AS aggname, "
6815 : "p.pronamespace AS aggnamespace, "
6816 : "p.pronargs, p.proargtypes, "
6817 : "p.proowner, "
6818 : "p.proacl AS aggacl, "
6819 : "acldefault('f', p.proowner) AS acldefault "
6820 : "FROM pg_proc p "
6821 : "LEFT JOIN pg_init_privs pip ON "
6822 : "(p.oid = pip.objoid "
6823 : "AND pip.classoid = 'pg_proc'::regclass "
6824 : "AND pip.objsubid = 0) "
6825 : "WHERE %s AND ("
6826 : "p.pronamespace != "
6827 : "(SELECT oid FROM pg_namespace "
6828 : "WHERE nspname = 'pg_catalog') OR "
6829 : "p.proacl IS DISTINCT FROM pip.initprivs",
6830 0 : agg_check);
6831 0 : if (dopt->binary_upgrade)
6832 0 : appendPQExpBufferStr(query,
6833 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6834 : "classid = 'pg_proc'::regclass AND "
6835 : "objid = p.oid AND "
6836 : "refclassid = 'pg_extension'::regclass AND "
6837 : "deptype = 'e')");
6838 0 : appendPQExpBufferChar(query, ')');
6839 0 : }
6840 : else
6841 : {
6842 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6843 : "pronamespace AS aggnamespace, "
6844 : "pronargs, proargtypes, "
6845 : "proowner, "
6846 : "proacl AS aggacl, "
6847 : "acldefault('f', proowner) AS acldefault "
6848 : "FROM pg_proc p "
6849 : "WHERE proisagg AND ("
6850 : "pronamespace != "
6851 : "(SELECT oid FROM pg_namespace "
6852 : "WHERE nspname = 'pg_catalog')");
6853 0 : if (dopt->binary_upgrade)
6854 0 : appendPQExpBufferStr(query,
6855 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6856 : "classid = 'pg_proc'::regclass AND "
6857 : "objid = p.oid AND "
6858 : "refclassid = 'pg_extension'::regclass AND "
6859 : "deptype = 'e')");
6860 0 : appendPQExpBufferChar(query, ')');
6861 : }
6862 :
6863 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6864 :
6865 0 : ntups = PQntuples(res);
6866 :
6867 0 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6868 :
6869 0 : i_tableoid = PQfnumber(res, "tableoid");
6870 0 : i_oid = PQfnumber(res, "oid");
6871 0 : i_aggname = PQfnumber(res, "aggname");
6872 0 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6873 0 : i_pronargs = PQfnumber(res, "pronargs");
6874 0 : i_proargtypes = PQfnumber(res, "proargtypes");
6875 0 : i_proowner = PQfnumber(res, "proowner");
6876 0 : i_aggacl = PQfnumber(res, "aggacl");
6877 0 : i_acldefault = PQfnumber(res, "acldefault");
6878 :
6879 0 : for (i = 0; i < ntups; i++)
6880 : {
6881 0 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6882 0 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6883 0 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6884 0 : AssignDumpId(&agginfo[i].aggfn.dobj);
6885 0 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6886 0 : agginfo[i].aggfn.dobj.namespace =
6887 0 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6888 0 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6889 0 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6890 0 : agginfo[i].aggfn.dacl.privtype = 0;
6891 0 : agginfo[i].aggfn.dacl.initprivs = NULL;
6892 0 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6893 0 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6894 0 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6895 0 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6896 0 : if (agginfo[i].aggfn.nargs == 0)
6897 0 : agginfo[i].aggfn.argtypes = NULL;
6898 : else
6899 : {
6900 0 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6901 0 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6902 0 : agginfo[i].aggfn.argtypes,
6903 0 : agginfo[i].aggfn.nargs);
6904 : }
6905 0 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6906 :
6907 : /* Decide whether we want to dump it */
6908 0 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6909 :
6910 : /* Mark whether aggregate has an ACL */
6911 0 : if (!PQgetisnull(res, i, i_aggacl))
6912 0 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6913 0 : }
6914 :
6915 0 : PQclear(res);
6916 :
6917 0 : destroyPQExpBuffer(query);
6918 0 : }
6919 :
6920 : /*
6921 : * getFuncs:
6922 : * get information about all user-defined functions in the system catalogs
6923 : */
6924 : void
6925 0 : getFuncs(Archive *fout)
6926 : {
6927 0 : DumpOptions *dopt = fout->dopt;
6928 0 : PGresult *res;
6929 0 : int ntups;
6930 0 : int i;
6931 0 : PQExpBuffer query = createPQExpBuffer();
6932 0 : FuncInfo *finfo;
6933 0 : int i_tableoid;
6934 0 : int i_oid;
6935 0 : int i_proname;
6936 0 : int i_pronamespace;
6937 0 : int i_proowner;
6938 0 : int i_prolang;
6939 0 : int i_pronargs;
6940 0 : int i_proargtypes;
6941 0 : int i_prorettype;
6942 0 : int i_proacl;
6943 0 : int i_acldefault;
6944 :
6945 : /*
6946 : * Find all interesting functions. This is a bit complicated:
6947 : *
6948 : * 1. Always exclude aggregates; those are handled elsewhere.
6949 : *
6950 : * 2. Always exclude functions that are internally dependent on something
6951 : * else, since presumably those will be created as a result of creating
6952 : * the something else. This currently acts only to suppress constructor
6953 : * functions for range types. Note this is OK only because the
6954 : * constructors don't have any dependencies the range type doesn't have;
6955 : * otherwise we might not get creation ordering correct.
6956 : *
6957 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6958 : * they're members of extensions and we are in binary-upgrade mode then
6959 : * include them, since we want to dump extension members individually in
6960 : * that mode. Also, if they are used by casts or transforms then we need
6961 : * to gather the information about them, though they won't be dumped if
6962 : * they are built-in. Also, in 9.6 and up, include functions in
6963 : * pg_catalog if they have an ACL different from what's shown in
6964 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6965 : */
6966 0 : if (fout->remoteVersion >= 90600)
6967 : {
6968 0 : const char *not_agg_check;
6969 :
6970 0 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6971 : : "NOT p.proisagg");
6972 :
6973 0 : appendPQExpBuffer(query,
6974 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6975 : "p.pronargs, p.proargtypes, p.prorettype, "
6976 : "p.proacl, "
6977 : "acldefault('f', p.proowner) AS acldefault, "
6978 : "p.pronamespace, "
6979 : "p.proowner "
6980 : "FROM pg_proc p "
6981 : "LEFT JOIN pg_init_privs pip ON "
6982 : "(p.oid = pip.objoid "
6983 : "AND pip.classoid = 'pg_proc'::regclass "
6984 : "AND pip.objsubid = 0) "
6985 : "WHERE %s"
6986 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6987 : "WHERE classid = 'pg_proc'::regclass AND "
6988 : "objid = p.oid AND deptype = 'i')"
6989 : "\n AND ("
6990 : "\n pronamespace != "
6991 : "(SELECT oid FROM pg_namespace "
6992 : "WHERE nspname = 'pg_catalog')"
6993 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6994 : "\n WHERE pg_cast.oid > %u "
6995 : "\n AND p.oid = pg_cast.castfunc)"
6996 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6997 : "\n WHERE pg_transform.oid > %u AND "
6998 : "\n (p.oid = pg_transform.trffromsql"
6999 : "\n OR p.oid = pg_transform.trftosql))",
7000 0 : not_agg_check,
7001 0 : g_last_builtin_oid,
7002 0 : g_last_builtin_oid);
7003 0 : if (dopt->binary_upgrade)
7004 0 : appendPQExpBufferStr(query,
7005 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7006 : "classid = 'pg_proc'::regclass AND "
7007 : "objid = p.oid AND "
7008 : "refclassid = 'pg_extension'::regclass AND "
7009 : "deptype = 'e')");
7010 0 : appendPQExpBufferStr(query,
7011 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7012 0 : appendPQExpBufferChar(query, ')');
7013 0 : }
7014 : else
7015 : {
7016 0 : appendPQExpBuffer(query,
7017 : "SELECT tableoid, oid, proname, prolang, "
7018 : "pronargs, proargtypes, prorettype, proacl, "
7019 : "acldefault('f', proowner) AS acldefault, "
7020 : "pronamespace, "
7021 : "proowner "
7022 : "FROM pg_proc p "
7023 : "WHERE NOT proisagg"
7024 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7025 : "WHERE classid = 'pg_proc'::regclass AND "
7026 : "objid = p.oid AND deptype = 'i')"
7027 : "\n AND ("
7028 : "\n pronamespace != "
7029 : "(SELECT oid FROM pg_namespace "
7030 : "WHERE nspname = 'pg_catalog')"
7031 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7032 : "\n WHERE pg_cast.oid > '%u'::oid"
7033 : "\n AND p.oid = pg_cast.castfunc)",
7034 0 : g_last_builtin_oid);
7035 :
7036 0 : if (fout->remoteVersion >= 90500)
7037 0 : appendPQExpBuffer(query,
7038 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7039 : "\n WHERE pg_transform.oid > '%u'::oid"
7040 : "\n AND (p.oid = pg_transform.trffromsql"
7041 : "\n OR p.oid = pg_transform.trftosql))",
7042 0 : g_last_builtin_oid);
7043 :
7044 0 : if (dopt->binary_upgrade)
7045 0 : appendPQExpBufferStr(query,
7046 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7047 : "classid = 'pg_proc'::regclass AND "
7048 : "objid = p.oid AND "
7049 : "refclassid = 'pg_extension'::regclass AND "
7050 : "deptype = 'e')");
7051 0 : appendPQExpBufferChar(query, ')');
7052 : }
7053 :
7054 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7055 :
7056 0 : ntups = PQntuples(res);
7057 :
7058 0 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7059 :
7060 0 : i_tableoid = PQfnumber(res, "tableoid");
7061 0 : i_oid = PQfnumber(res, "oid");
7062 0 : i_proname = PQfnumber(res, "proname");
7063 0 : i_pronamespace = PQfnumber(res, "pronamespace");
7064 0 : i_proowner = PQfnumber(res, "proowner");
7065 0 : i_prolang = PQfnumber(res, "prolang");
7066 0 : i_pronargs = PQfnumber(res, "pronargs");
7067 0 : i_proargtypes = PQfnumber(res, "proargtypes");
7068 0 : i_prorettype = PQfnumber(res, "prorettype");
7069 0 : i_proacl = PQfnumber(res, "proacl");
7070 0 : i_acldefault = PQfnumber(res, "acldefault");
7071 :
7072 0 : for (i = 0; i < ntups; i++)
7073 : {
7074 0 : finfo[i].dobj.objType = DO_FUNC;
7075 0 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7076 0 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7077 0 : AssignDumpId(&finfo[i].dobj);
7078 0 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7079 0 : finfo[i].dobj.namespace =
7080 0 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7081 0 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7082 0 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7083 0 : finfo[i].dacl.privtype = 0;
7084 0 : finfo[i].dacl.initprivs = NULL;
7085 0 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7086 0 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7087 0 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7088 0 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7089 0 : if (finfo[i].nargs == 0)
7090 0 : finfo[i].argtypes = NULL;
7091 : else
7092 : {
7093 0 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7094 0 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7095 0 : finfo[i].argtypes, finfo[i].nargs);
7096 : }
7097 0 : finfo[i].postponed_def = false; /* might get set during sort */
7098 :
7099 : /* Decide whether we want to dump it */
7100 0 : selectDumpableObject(&(finfo[i].dobj), fout);
7101 :
7102 : /* Mark whether function has an ACL */
7103 0 : if (!PQgetisnull(res, i, i_proacl))
7104 0 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7105 0 : }
7106 :
7107 0 : PQclear(res);
7108 :
7109 0 : destroyPQExpBuffer(query);
7110 0 : }
7111 :
7112 : /*
7113 : * getRelationStatistics
7114 : * register the statistics object as a dependent of the relation.
7115 : *
7116 : * reltuples is passed as a string to avoid complexities in converting from/to
7117 : * floating point.
7118 : */
7119 : static RelStatsInfo *
7120 0 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7121 : char *reltuples, int32 relallvisible,
7122 : int32 relallfrozen, char relkind,
7123 : char **indAttNames, int nindAttNames)
7124 : {
7125 0 : if (!fout->dopt->dumpStatistics)
7126 0 : return NULL;
7127 :
7128 0 : if ((relkind == RELKIND_RELATION) ||
7129 0 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7130 0 : (relkind == RELKIND_INDEX) ||
7131 0 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7132 0 : (relkind == RELKIND_MATVIEW ||
7133 0 : relkind == RELKIND_FOREIGN_TABLE))
7134 : {
7135 0 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7136 0 : DumpableObject *dobj = &info->dobj;
7137 :
7138 0 : dobj->objType = DO_REL_STATS;
7139 0 : dobj->catId.tableoid = 0;
7140 0 : dobj->catId.oid = 0;
7141 0 : AssignDumpId(dobj);
7142 0 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7143 0 : dobj->dependencies[0] = rel->dumpId;
7144 0 : dobj->nDeps = 1;
7145 0 : dobj->allocDeps = 1;
7146 0 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7147 0 : dobj->name = pg_strdup(rel->name);
7148 0 : dobj->namespace = rel->namespace;
7149 0 : info->relpages = relpages;
7150 0 : info->reltuples = pstrdup(reltuples);
7151 0 : info->relallvisible = relallvisible;
7152 0 : info->relallfrozen = relallfrozen;
7153 0 : info->relkind = relkind;
7154 0 : info->indAttNames = indAttNames;
7155 0 : info->nindAttNames = nindAttNames;
7156 :
7157 : /*
7158 : * Ordinarily, stats go in SECTION_DATA for tables and
7159 : * SECTION_POST_DATA for indexes.
7160 : *
7161 : * However, the section may be updated later for materialized view
7162 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7163 : * the stats, so the stats must be restored after the data. Also, the
7164 : * materialized view definition may be postponed to SECTION_POST_DATA
7165 : * (see repairMatViewBoundaryMultiLoop()).
7166 : */
7167 0 : switch (info->relkind)
7168 : {
7169 : case RELKIND_RELATION:
7170 : case RELKIND_PARTITIONED_TABLE:
7171 : case RELKIND_MATVIEW:
7172 : case RELKIND_FOREIGN_TABLE:
7173 0 : info->section = SECTION_DATA;
7174 0 : break;
7175 : case RELKIND_INDEX:
7176 : case RELKIND_PARTITIONED_INDEX:
7177 0 : info->section = SECTION_POST_DATA;
7178 0 : break;
7179 : default:
7180 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7181 : info->relkind);
7182 0 : }
7183 :
7184 0 : return info;
7185 0 : }
7186 0 : return NULL;
7187 0 : }
7188 :
7189 : /*
7190 : * getTables
7191 : * read all the tables (no indexes) in the system catalogs,
7192 : * and return them as an array of TableInfo structures
7193 : *
7194 : * *numTables is set to the number of tables read in
7195 : */
7196 : TableInfo *
7197 0 : getTables(Archive *fout, int *numTables)
7198 : {
7199 0 : DumpOptions *dopt = fout->dopt;
7200 0 : PGresult *res;
7201 0 : int ntups;
7202 0 : int i;
7203 0 : PQExpBuffer query = createPQExpBuffer();
7204 0 : TableInfo *tblinfo;
7205 0 : int i_reltableoid;
7206 0 : int i_reloid;
7207 0 : int i_relname;
7208 0 : int i_relnamespace;
7209 0 : int i_relkind;
7210 0 : int i_reltype;
7211 0 : int i_relowner;
7212 0 : int i_relchecks;
7213 0 : int i_relhasindex;
7214 0 : int i_relhasrules;
7215 0 : int i_relpages;
7216 0 : int i_reltuples;
7217 0 : int i_relallvisible;
7218 0 : int i_relallfrozen;
7219 0 : int i_toastpages;
7220 0 : int i_owning_tab;
7221 0 : int i_owning_col;
7222 0 : int i_reltablespace;
7223 0 : int i_relhasoids;
7224 0 : int i_relhastriggers;
7225 0 : int i_relpersistence;
7226 0 : int i_relispopulated;
7227 0 : int i_relreplident;
7228 0 : int i_relrowsec;
7229 0 : int i_relforcerowsec;
7230 0 : int i_relfrozenxid;
7231 0 : int i_toastfrozenxid;
7232 0 : int i_toastoid;
7233 0 : int i_relminmxid;
7234 0 : int i_toastminmxid;
7235 0 : int i_reloptions;
7236 0 : int i_checkoption;
7237 0 : int i_toastreloptions;
7238 0 : int i_reloftype;
7239 0 : int i_foreignserver;
7240 0 : int i_amname;
7241 0 : int i_is_identity_sequence;
7242 0 : int i_relacl;
7243 0 : int i_acldefault;
7244 0 : int i_ispartition;
7245 :
7246 : /*
7247 : * Find all the tables and table-like objects.
7248 : *
7249 : * We must fetch all tables in this phase because otherwise we cannot
7250 : * correctly identify inherited columns, owned sequences, etc.
7251 : *
7252 : * We include system catalogs, so that we can work if a user table is
7253 : * defined to inherit from a system catalog (pretty weird, but...)
7254 : *
7255 : * Note: in this phase we should collect only a minimal amount of
7256 : * information about each table, basically just enough to decide if it is
7257 : * interesting. In particular, since we do not yet have lock on any user
7258 : * table, we MUST NOT invoke any server-side data collection functions
7259 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7260 : * wrong answers if any concurrent DDL is happening.
7261 : */
7262 :
7263 0 : appendPQExpBufferStr(query,
7264 : "SELECT c.tableoid, c.oid, c.relname, "
7265 : "c.relnamespace, c.relkind, c.reltype, "
7266 : "c.relowner, "
7267 : "c.relchecks, "
7268 : "c.relhasindex, c.relhasrules, c.relpages, "
7269 : "c.reltuples, c.relallvisible, ");
7270 :
7271 0 : if (fout->remoteVersion >= 180000)
7272 0 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7273 : else
7274 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7275 :
7276 0 : appendPQExpBufferStr(query,
7277 : "c.relhastriggers, c.relpersistence, "
7278 : "c.reloftype, "
7279 : "c.relacl, "
7280 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7281 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7282 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7283 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7284 : "ELSE 0 END AS foreignserver, "
7285 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7286 : "tc.oid AS toid, "
7287 : "tc.relpages AS toastpages, "
7288 : "tc.reloptions AS toast_reloptions, "
7289 : "d.refobjid AS owning_tab, "
7290 : "d.refobjsubid AS owning_col, "
7291 : "tsp.spcname AS reltablespace, ");
7292 :
7293 0 : if (fout->remoteVersion >= 120000)
7294 0 : appendPQExpBufferStr(query,
7295 : "false AS relhasoids, ");
7296 : else
7297 0 : appendPQExpBufferStr(query,
7298 : "c.relhasoids, ");
7299 :
7300 0 : if (fout->remoteVersion >= 90300)
7301 0 : appendPQExpBufferStr(query,
7302 : "c.relispopulated, ");
7303 : else
7304 0 : appendPQExpBufferStr(query,
7305 : "'t' as relispopulated, ");
7306 :
7307 0 : if (fout->remoteVersion >= 90400)
7308 0 : appendPQExpBufferStr(query,
7309 : "c.relreplident, ");
7310 : else
7311 0 : appendPQExpBufferStr(query,
7312 : "'d' AS relreplident, ");
7313 :
7314 0 : if (fout->remoteVersion >= 90500)
7315 0 : appendPQExpBufferStr(query,
7316 : "c.relrowsecurity, c.relforcerowsecurity, ");
7317 : else
7318 0 : appendPQExpBufferStr(query,
7319 : "false AS relrowsecurity, "
7320 : "false AS relforcerowsecurity, ");
7321 :
7322 0 : if (fout->remoteVersion >= 90300)
7323 0 : appendPQExpBufferStr(query,
7324 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7325 : else
7326 0 : appendPQExpBufferStr(query,
7327 : "0 AS relminmxid, 0 AS tminmxid, ");
7328 :
7329 0 : if (fout->remoteVersion >= 90300)
7330 0 : appendPQExpBufferStr(query,
7331 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7332 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7333 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7334 : else
7335 0 : appendPQExpBufferStr(query,
7336 : "c.reloptions, NULL AS checkoption, ");
7337 :
7338 0 : if (fout->remoteVersion >= 90600)
7339 0 : appendPQExpBufferStr(query,
7340 : "am.amname, ");
7341 : else
7342 0 : appendPQExpBufferStr(query,
7343 : "NULL AS amname, ");
7344 :
7345 0 : if (fout->remoteVersion >= 90600)
7346 0 : appendPQExpBufferStr(query,
7347 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7348 : else
7349 0 : appendPQExpBufferStr(query,
7350 : "false AS is_identity_sequence, ");
7351 :
7352 0 : if (fout->remoteVersion >= 100000)
7353 0 : appendPQExpBufferStr(query,
7354 : "c.relispartition AS ispartition ");
7355 : else
7356 0 : appendPQExpBufferStr(query,
7357 : "false AS ispartition ");
7358 :
7359 : /*
7360 : * Left join to pg_depend to pick up dependency info linking sequences to
7361 : * their owning column, if any (note this dependency is AUTO except for
7362 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7363 : * collect the spcname.
7364 : */
7365 0 : appendPQExpBufferStr(query,
7366 : "\nFROM pg_class c\n"
7367 : "LEFT JOIN pg_depend d ON "
7368 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7369 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7370 : "d.objsubid = 0 AND "
7371 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7372 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7373 :
7374 : /*
7375 : * In 9.6 and up, left join to pg_am to pick up the amname.
7376 : */
7377 0 : if (fout->remoteVersion >= 90600)
7378 0 : appendPQExpBufferStr(query,
7379 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7380 :
7381 : /*
7382 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7383 : * that versions 10 and 11 have them, but later versions do not, so
7384 : * emitting them causes the upgrade to fail.
7385 : */
7386 0 : appendPQExpBufferStr(query,
7387 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7388 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7389 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7390 :
7391 : /*
7392 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7393 : * relkinds are possible in older servers, but it's not worth the trouble
7394 : * to emit a version-dependent list.
7395 : *
7396 : * Composite-type table entries won't be dumped as such, but we have to
7397 : * make a DumpableObject for them so that we can track dependencies of the
7398 : * composite type (pg_depend entries for columns of the composite type
7399 : * link to the pg_class entry not the pg_type entry).
7400 : */
7401 0 : appendPQExpBufferStr(query,
7402 : "WHERE c.relkind IN ("
7403 : CppAsString2(RELKIND_RELATION) ", "
7404 : CppAsString2(RELKIND_SEQUENCE) ", "
7405 : CppAsString2(RELKIND_VIEW) ", "
7406 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7407 : CppAsString2(RELKIND_MATVIEW) ", "
7408 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7409 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7410 : "ORDER BY c.oid");
7411 :
7412 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7413 :
7414 0 : ntups = PQntuples(res);
7415 :
7416 0 : *numTables = ntups;
7417 :
7418 : /*
7419 : * Extract data from result and lock dumpable tables. We do the locking
7420 : * before anything else, to minimize the window wherein a table could
7421 : * disappear under us.
7422 : *
7423 : * Note that we have to save info about all tables here, even when dumping
7424 : * only one, because we don't yet know which tables might be inheritance
7425 : * ancestors of the target table.
7426 : */
7427 0 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7428 :
7429 0 : i_reltableoid = PQfnumber(res, "tableoid");
7430 0 : i_reloid = PQfnumber(res, "oid");
7431 0 : i_relname = PQfnumber(res, "relname");
7432 0 : i_relnamespace = PQfnumber(res, "relnamespace");
7433 0 : i_relkind = PQfnumber(res, "relkind");
7434 0 : i_reltype = PQfnumber(res, "reltype");
7435 0 : i_relowner = PQfnumber(res, "relowner");
7436 0 : i_relchecks = PQfnumber(res, "relchecks");
7437 0 : i_relhasindex = PQfnumber(res, "relhasindex");
7438 0 : i_relhasrules = PQfnumber(res, "relhasrules");
7439 0 : i_relpages = PQfnumber(res, "relpages");
7440 0 : i_reltuples = PQfnumber(res, "reltuples");
7441 0 : i_relallvisible = PQfnumber(res, "relallvisible");
7442 0 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7443 0 : i_toastpages = PQfnumber(res, "toastpages");
7444 0 : i_owning_tab = PQfnumber(res, "owning_tab");
7445 0 : i_owning_col = PQfnumber(res, "owning_col");
7446 0 : i_reltablespace = PQfnumber(res, "reltablespace");
7447 0 : i_relhasoids = PQfnumber(res, "relhasoids");
7448 0 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7449 0 : i_relpersistence = PQfnumber(res, "relpersistence");
7450 0 : i_relispopulated = PQfnumber(res, "relispopulated");
7451 0 : i_relreplident = PQfnumber(res, "relreplident");
7452 0 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7453 0 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7454 0 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7455 0 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7456 0 : i_toastoid = PQfnumber(res, "toid");
7457 0 : i_relminmxid = PQfnumber(res, "relminmxid");
7458 0 : i_toastminmxid = PQfnumber(res, "tminmxid");
7459 0 : i_reloptions = PQfnumber(res, "reloptions");
7460 0 : i_checkoption = PQfnumber(res, "checkoption");
7461 0 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7462 0 : i_reloftype = PQfnumber(res, "reloftype");
7463 0 : i_foreignserver = PQfnumber(res, "foreignserver");
7464 0 : i_amname = PQfnumber(res, "amname");
7465 0 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7466 0 : i_relacl = PQfnumber(res, "relacl");
7467 0 : i_acldefault = PQfnumber(res, "acldefault");
7468 0 : i_ispartition = PQfnumber(res, "ispartition");
7469 :
7470 0 : if (dopt->lockWaitTimeout)
7471 : {
7472 : /*
7473 : * Arrange to fail instead of waiting forever for a table lock.
7474 : *
7475 : * NB: this coding assumes that the only queries issued within the
7476 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7477 : * applied to other things too.
7478 : */
7479 0 : resetPQExpBuffer(query);
7480 0 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7481 0 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7482 0 : ExecuteSqlStatement(fout, query->data);
7483 0 : }
7484 :
7485 0 : resetPQExpBuffer(query);
7486 :
7487 0 : for (i = 0; i < ntups; i++)
7488 : {
7489 0 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7490 0 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7491 :
7492 0 : tblinfo[i].dobj.objType = DO_TABLE;
7493 0 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7494 0 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7495 0 : AssignDumpId(&tblinfo[i].dobj);
7496 0 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7497 0 : tblinfo[i].dobj.namespace =
7498 0 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7499 0 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7500 0 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7501 0 : tblinfo[i].dacl.privtype = 0;
7502 0 : tblinfo[i].dacl.initprivs = NULL;
7503 0 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7504 0 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7505 0 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7506 0 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7507 0 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7508 0 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7509 0 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7510 0 : if (PQgetisnull(res, i, i_toastpages))
7511 0 : tblinfo[i].toastpages = 0;
7512 : else
7513 0 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7514 0 : if (PQgetisnull(res, i, i_owning_tab))
7515 : {
7516 0 : tblinfo[i].owning_tab = InvalidOid;
7517 0 : tblinfo[i].owning_col = 0;
7518 0 : }
7519 : else
7520 : {
7521 0 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7522 0 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7523 : }
7524 0 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7525 0 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7526 0 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7527 0 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7528 0 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7529 0 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7530 0 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7531 0 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7532 0 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7533 0 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7534 0 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7535 0 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7536 0 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7537 0 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7538 0 : if (PQgetisnull(res, i, i_checkoption))
7539 0 : tblinfo[i].checkoption = NULL;
7540 : else
7541 0 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7542 0 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7543 0 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7544 0 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7545 0 : if (PQgetisnull(res, i, i_amname))
7546 0 : tblinfo[i].amname = NULL;
7547 : else
7548 0 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7549 0 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7550 0 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7551 :
7552 : /* other fields were zeroed above */
7553 :
7554 : /*
7555 : * Decide whether we want to dump this table.
7556 : */
7557 0 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7558 0 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7559 : else
7560 0 : selectDumpableTable(&tblinfo[i], fout);
7561 :
7562 : /*
7563 : * Now, consider the table "interesting" if we need to dump its
7564 : * definition, data or its statistics. Later on, we'll skip a lot of
7565 : * data collection for uninteresting tables.
7566 : *
7567 : * Note: the "interesting" flag will also be set by flagInhTables for
7568 : * parents of interesting tables, so that we collect necessary
7569 : * inheritance info even when the parents are not themselves being
7570 : * dumped. This is the main reason why we need an "interesting" flag
7571 : * that's separate from the components-to-dump bitmask.
7572 : */
7573 0 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7574 : (DUMP_COMPONENT_DEFINITION |
7575 : DUMP_COMPONENT_DATA |
7576 0 : DUMP_COMPONENT_STATISTICS)) != 0;
7577 :
7578 0 : tblinfo[i].dummy_view = false; /* might get set during sort */
7579 0 : tblinfo[i].postponed_def = false; /* might get set during sort */
7580 :
7581 : /* Tables have data */
7582 0 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7583 :
7584 : /* Mark whether table has an ACL */
7585 0 : if (!PQgetisnull(res, i, i_relacl))
7586 0 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7587 0 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7588 :
7589 : /* Add statistics */
7590 0 : if (tblinfo[i].interesting)
7591 : {
7592 0 : RelStatsInfo *stats;
7593 :
7594 0 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7595 0 : tblinfo[i].relpages,
7596 0 : PQgetvalue(res, i, i_reltuples),
7597 0 : relallvisible, relallfrozen,
7598 0 : tblinfo[i].relkind, NULL, 0);
7599 0 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7600 0 : tblinfo[i].stats = stats;
7601 0 : }
7602 :
7603 : /*
7604 : * Read-lock target tables to make sure they aren't DROPPED or altered
7605 : * in schema before we get around to dumping them.
7606 : *
7607 : * Note that we don't explicitly lock parents of the target tables; we
7608 : * assume our lock on the child is enough to prevent schema
7609 : * alterations to parent tables.
7610 : *
7611 : * NOTE: it'd be kinda nice to lock other relations too, not only
7612 : * plain or partitioned tables, but the backend doesn't presently
7613 : * allow that.
7614 : *
7615 : * We only need to lock the table for certain components; see
7616 : * pg_dump.h
7617 : */
7618 0 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7619 0 : (tblinfo[i].relkind == RELKIND_RELATION ||
7620 0 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7621 : {
7622 : /*
7623 : * Tables are locked in batches. When dumping from a remote
7624 : * server this can save a significant amount of time by reducing
7625 : * the number of round trips.
7626 : */
7627 0 : if (query->len == 0)
7628 0 : appendPQExpBuffer(query, "LOCK TABLE %s",
7629 0 : fmtQualifiedDumpable(&tblinfo[i]));
7630 : else
7631 : {
7632 0 : appendPQExpBuffer(query, ", %s",
7633 0 : fmtQualifiedDumpable(&tblinfo[i]));
7634 :
7635 : /* Arbitrarily end a batch when query length reaches 100K. */
7636 0 : if (query->len >= 100000)
7637 : {
7638 : /* Lock another batch of tables. */
7639 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7640 0 : ExecuteSqlStatement(fout, query->data);
7641 0 : resetPQExpBuffer(query);
7642 0 : }
7643 : }
7644 0 : }
7645 0 : }
7646 :
7647 0 : if (query->len != 0)
7648 : {
7649 : /* Lock the tables in the last batch. */
7650 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7651 0 : ExecuteSqlStatement(fout, query->data);
7652 0 : }
7653 :
7654 0 : if (dopt->lockWaitTimeout)
7655 : {
7656 0 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7657 0 : }
7658 :
7659 0 : PQclear(res);
7660 :
7661 0 : destroyPQExpBuffer(query);
7662 :
7663 0 : return tblinfo;
7664 0 : }
7665 :
7666 : /*
7667 : * getOwnedSeqs
7668 : * identify owned sequences and mark them as dumpable if owning table is
7669 : *
7670 : * We used to do this in getTables(), but it's better to do it after the
7671 : * index used by findTableByOid() has been set up.
7672 : */
7673 : void
7674 0 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7675 : {
7676 0 : int i;
7677 :
7678 : /*
7679 : * Force sequences that are "owned" by table columns to be dumped whenever
7680 : * their owning table is being dumped.
7681 : */
7682 0 : for (i = 0; i < numTables; i++)
7683 : {
7684 0 : TableInfo *seqinfo = &tblinfo[i];
7685 0 : TableInfo *owning_tab;
7686 :
7687 0 : if (!OidIsValid(seqinfo->owning_tab))
7688 0 : continue; /* not an owned sequence */
7689 :
7690 0 : owning_tab = findTableByOid(seqinfo->owning_tab);
7691 0 : if (owning_tab == NULL)
7692 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7693 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7694 :
7695 : /*
7696 : * For an identity sequence, dump exactly the same components for the
7697 : * sequence as for the owning table. This is important because we
7698 : * treat the identity sequence as an integral part of the table. For
7699 : * example, there is not any DDL command that allows creation of such
7700 : * a sequence independently of the table.
7701 : *
7702 : * For other owned sequences such as serial sequences, we need to dump
7703 : * the components that are being dumped for the table and any
7704 : * components that the sequence is explicitly marked with.
7705 : *
7706 : * We can't simply use the set of components which are being dumped
7707 : * for the table as the table might be in an extension (and only the
7708 : * non-extension components, eg: ACLs if changed, security labels, and
7709 : * policies, are being dumped) while the sequence is not (and
7710 : * therefore the definition and other components should also be
7711 : * dumped).
7712 : *
7713 : * If the sequence is part of the extension then it should be properly
7714 : * marked by checkExtensionMembership() and this will be a no-op as
7715 : * the table will be equivalently marked.
7716 : */
7717 0 : if (seqinfo->is_identity_sequence)
7718 0 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7719 : else
7720 0 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7721 :
7722 : /* Make sure that necessary data is available if we're dumping it */
7723 0 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7724 : {
7725 0 : seqinfo->interesting = true;
7726 0 : owning_tab->interesting = true;
7727 0 : }
7728 0 : }
7729 0 : }
7730 :
7731 : /*
7732 : * getInherits
7733 : * read all the inheritance information
7734 : * from the system catalogs return them in the InhInfo* structure
7735 : *
7736 : * numInherits is set to the number of pairs read in
7737 : */
7738 : InhInfo *
7739 0 : getInherits(Archive *fout, int *numInherits)
7740 : {
7741 0 : PGresult *res;
7742 0 : int ntups;
7743 0 : int i;
7744 0 : PQExpBuffer query = createPQExpBuffer();
7745 0 : InhInfo *inhinfo;
7746 :
7747 0 : int i_inhrelid;
7748 0 : int i_inhparent;
7749 :
7750 : /* find all the inheritance information */
7751 0 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7752 :
7753 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7754 :
7755 0 : ntups = PQntuples(res);
7756 :
7757 0 : *numInherits = ntups;
7758 :
7759 0 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7760 :
7761 0 : i_inhrelid = PQfnumber(res, "inhrelid");
7762 0 : i_inhparent = PQfnumber(res, "inhparent");
7763 :
7764 0 : for (i = 0; i < ntups; i++)
7765 : {
7766 0 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7767 0 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7768 0 : }
7769 :
7770 0 : PQclear(res);
7771 :
7772 0 : destroyPQExpBuffer(query);
7773 :
7774 0 : return inhinfo;
7775 0 : }
7776 :
7777 : /*
7778 : * getPartitioningInfo
7779 : * get information about partitioning
7780 : *
7781 : * For the most part, we only collect partitioning info about tables we
7782 : * intend to dump. However, this function has to consider all partitioned
7783 : * tables in the database, because we need to know about parents of partitions
7784 : * we are going to dump even if the parents themselves won't be dumped.
7785 : *
7786 : * Specifically, what we need to know is whether each partitioned table
7787 : * has an "unsafe" partitioning scheme that requires us to force
7788 : * load-via-partition-root mode for its children. Currently the only case
7789 : * for which we force that is hash partitioning on enum columns, since the
7790 : * hash codes depend on enum value OIDs which won't be replicated across
7791 : * dump-and-reload. There are other cases in which load-via-partition-root
7792 : * might be necessary, but we expect users to cope with them.
7793 : */
7794 : void
7795 0 : getPartitioningInfo(Archive *fout)
7796 : {
7797 0 : PQExpBuffer query;
7798 0 : PGresult *res;
7799 0 : int ntups;
7800 :
7801 : /* hash partitioning didn't exist before v11 */
7802 0 : if (fout->remoteVersion < 110000)
7803 0 : return;
7804 : /* needn't bother if not dumping data */
7805 0 : if (!fout->dopt->dumpData)
7806 0 : return;
7807 :
7808 0 : query = createPQExpBuffer();
7809 :
7810 : /*
7811 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7812 : * appears among the partition opclasses. We needn't check partstrat.
7813 : *
7814 : * Note that this query may well retrieve info about tables we aren't
7815 : * going to dump and hence have no lock on. That's okay since we need not
7816 : * invoke any unsafe server-side functions.
7817 : */
7818 0 : appendPQExpBufferStr(query,
7819 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7820 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7821 : "ON c.opcmethod = a.oid\n"
7822 : "WHERE opcname = 'enum_ops' "
7823 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7824 : "AND amname = 'hash') = ANY(partclass)");
7825 :
7826 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7827 :
7828 0 : ntups = PQntuples(res);
7829 :
7830 0 : for (int i = 0; i < ntups; i++)
7831 : {
7832 0 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7833 0 : TableInfo *tbinfo;
7834 :
7835 0 : tbinfo = findTableByOid(tabrelid);
7836 0 : if (tbinfo == NULL)
7837 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7838 : tabrelid);
7839 0 : tbinfo->unsafe_partitions = true;
7840 0 : }
7841 :
7842 0 : PQclear(res);
7843 :
7844 0 : destroyPQExpBuffer(query);
7845 0 : }
7846 :
7847 : /*
7848 : * getIndexes
7849 : * get information about every index on a dumpable table
7850 : *
7851 : * Note: index data is not returned directly to the caller, but it
7852 : * does get entered into the DumpableObject tables.
7853 : */
7854 : void
7855 0 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7856 : {
7857 0 : PQExpBuffer query = createPQExpBuffer();
7858 0 : PQExpBuffer tbloids = createPQExpBuffer();
7859 0 : PGresult *res;
7860 0 : int ntups;
7861 0 : int curtblindx;
7862 0 : IndxInfo *indxinfo;
7863 0 : int i_tableoid,
7864 : i_oid,
7865 : i_indrelid,
7866 : i_indexname,
7867 : i_relpages,
7868 : i_reltuples,
7869 : i_relallvisible,
7870 : i_relallfrozen,
7871 : i_parentidx,
7872 : i_indexdef,
7873 : i_indnkeyatts,
7874 : i_indnatts,
7875 : i_indkey,
7876 : i_indisclustered,
7877 : i_indisreplident,
7878 : i_indnullsnotdistinct,
7879 : i_contype,
7880 : i_conname,
7881 : i_condeferrable,
7882 : i_condeferred,
7883 : i_conperiod,
7884 : i_contableoid,
7885 : i_conoid,
7886 : i_condef,
7887 : i_indattnames,
7888 : i_tablespace,
7889 : i_indreloptions,
7890 : i_indstatcols,
7891 : i_indstatvals;
7892 :
7893 : /*
7894 : * We want to perform just one query against pg_index. However, we
7895 : * mustn't try to select every row of the catalog and then sort it out on
7896 : * the client side, because some of the server-side functions we need
7897 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7898 : * build an array of the OIDs of tables we care about (and now have lock
7899 : * on!), and use a WHERE clause to constrain which rows are selected.
7900 : */
7901 0 : appendPQExpBufferChar(tbloids, '{');
7902 0 : for (int i = 0; i < numTables; i++)
7903 : {
7904 0 : TableInfo *tbinfo = &tblinfo[i];
7905 :
7906 0 : if (!tbinfo->hasindex)
7907 0 : continue;
7908 :
7909 : /*
7910 : * We can ignore indexes of uninteresting tables.
7911 : */
7912 0 : if (!tbinfo->interesting)
7913 0 : continue;
7914 :
7915 : /* OK, we need info for this table */
7916 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
7917 0 : appendPQExpBufferChar(tbloids, ',');
7918 0 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7919 0 : }
7920 0 : appendPQExpBufferChar(tbloids, '}');
7921 :
7922 0 : appendPQExpBufferStr(query,
7923 : "SELECT t.tableoid, t.oid, i.indrelid, "
7924 : "t.relname AS indexname, "
7925 : "t.relpages, t.reltuples, t.relallvisible, ");
7926 :
7927 0 : if (fout->remoteVersion >= 180000)
7928 0 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7929 : else
7930 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7931 :
7932 0 : appendPQExpBufferStr(query,
7933 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7934 : "i.indkey, i.indisclustered, "
7935 : "c.contype, c.conname, "
7936 : "c.condeferrable, c.condeferred, "
7937 : "c.tableoid AS contableoid, "
7938 : "c.oid AS conoid, "
7939 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7940 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7941 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7942 : " FROM pg_catalog.pg_attribute "
7943 : " WHERE attrelid = i.indexrelid) "
7944 : "ELSE NULL END AS indattnames, "
7945 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7946 : "t.reloptions AS indreloptions, ");
7947 :
7948 :
7949 0 : if (fout->remoteVersion >= 90400)
7950 0 : appendPQExpBufferStr(query,
7951 : "i.indisreplident, ");
7952 : else
7953 0 : appendPQExpBufferStr(query,
7954 : "false AS indisreplident, ");
7955 :
7956 0 : if (fout->remoteVersion >= 110000)
7957 0 : appendPQExpBufferStr(query,
7958 : "inh.inhparent AS parentidx, "
7959 : "i.indnkeyatts AS indnkeyatts, "
7960 : "i.indnatts AS indnatts, "
7961 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7962 : " FROM pg_catalog.pg_attribute "
7963 : " WHERE attrelid = i.indexrelid AND "
7964 : " attstattarget >= 0) AS indstatcols, "
7965 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7966 : " FROM pg_catalog.pg_attribute "
7967 : " WHERE attrelid = i.indexrelid AND "
7968 : " attstattarget >= 0) AS indstatvals, ");
7969 : else
7970 0 : appendPQExpBufferStr(query,
7971 : "0 AS parentidx, "
7972 : "i.indnatts AS indnkeyatts, "
7973 : "i.indnatts AS indnatts, "
7974 : "'' AS indstatcols, "
7975 : "'' AS indstatvals, ");
7976 :
7977 0 : if (fout->remoteVersion >= 150000)
7978 0 : appendPQExpBufferStr(query,
7979 : "i.indnullsnotdistinct, ");
7980 : else
7981 0 : appendPQExpBufferStr(query,
7982 : "false AS indnullsnotdistinct, ");
7983 :
7984 0 : if (fout->remoteVersion >= 180000)
7985 0 : appendPQExpBufferStr(query,
7986 : "c.conperiod ");
7987 : else
7988 0 : appendPQExpBufferStr(query,
7989 : "NULL AS conperiod ");
7990 :
7991 : /*
7992 : * The point of the messy-looking outer join is to find a constraint that
7993 : * is related by an internal dependency link to the index. If we find one,
7994 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7995 : * index won't have more than one internal dependency.
7996 : *
7997 : * Note: the check on conrelid is redundant, but useful because that
7998 : * column is indexed while conindid is not.
7999 : */
8000 0 : if (fout->remoteVersion >= 110000)
8001 : {
8002 0 : appendPQExpBuffer(query,
8003 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8004 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8005 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8006 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
8007 : "LEFT JOIN pg_catalog.pg_constraint c "
8008 : "ON (i.indrelid = c.conrelid AND "
8009 : "i.indexrelid = c.conindid AND "
8010 : "c.contype IN ('p','u','x')) "
8011 : "LEFT JOIN pg_catalog.pg_inherits inh "
8012 : "ON (inh.inhrelid = indexrelid) "
8013 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
8014 : "AND i.indisready "
8015 : "ORDER BY i.indrelid, indexname",
8016 0 : tbloids->data);
8017 0 : }
8018 : else
8019 : {
8020 : /*
8021 : * the test on indisready is necessary in 9.2, and harmless in
8022 : * earlier/later versions
8023 : */
8024 0 : appendPQExpBuffer(query,
8025 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8026 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8027 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8028 : "LEFT JOIN pg_catalog.pg_constraint c "
8029 : "ON (i.indrelid = c.conrelid AND "
8030 : "i.indexrelid = c.conindid AND "
8031 : "c.contype IN ('p','u','x')) "
8032 : "WHERE i.indisvalid AND i.indisready "
8033 : "ORDER BY i.indrelid, indexname",
8034 0 : tbloids->data);
8035 : }
8036 :
8037 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8038 :
8039 0 : ntups = PQntuples(res);
8040 :
8041 0 : i_tableoid = PQfnumber(res, "tableoid");
8042 0 : i_oid = PQfnumber(res, "oid");
8043 0 : i_indrelid = PQfnumber(res, "indrelid");
8044 0 : i_indexname = PQfnumber(res, "indexname");
8045 0 : i_relpages = PQfnumber(res, "relpages");
8046 0 : i_reltuples = PQfnumber(res, "reltuples");
8047 0 : i_relallvisible = PQfnumber(res, "relallvisible");
8048 0 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8049 0 : i_parentidx = PQfnumber(res, "parentidx");
8050 0 : i_indexdef = PQfnumber(res, "indexdef");
8051 0 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8052 0 : i_indnatts = PQfnumber(res, "indnatts");
8053 0 : i_indkey = PQfnumber(res, "indkey");
8054 0 : i_indisclustered = PQfnumber(res, "indisclustered");
8055 0 : i_indisreplident = PQfnumber(res, "indisreplident");
8056 0 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8057 0 : i_contype = PQfnumber(res, "contype");
8058 0 : i_conname = PQfnumber(res, "conname");
8059 0 : i_condeferrable = PQfnumber(res, "condeferrable");
8060 0 : i_condeferred = PQfnumber(res, "condeferred");
8061 0 : i_conperiod = PQfnumber(res, "conperiod");
8062 0 : i_contableoid = PQfnumber(res, "contableoid");
8063 0 : i_conoid = PQfnumber(res, "conoid");
8064 0 : i_condef = PQfnumber(res, "condef");
8065 0 : i_indattnames = PQfnumber(res, "indattnames");
8066 0 : i_tablespace = PQfnumber(res, "tablespace");
8067 0 : i_indreloptions = PQfnumber(res, "indreloptions");
8068 0 : i_indstatcols = PQfnumber(res, "indstatcols");
8069 0 : i_indstatvals = PQfnumber(res, "indstatvals");
8070 :
8071 0 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8072 :
8073 : /*
8074 : * Outer loop iterates once per table, not once per row. Incrementing of
8075 : * j is handled by the inner loop.
8076 : */
8077 0 : curtblindx = -1;
8078 0 : for (int j = 0; j < ntups;)
8079 : {
8080 0 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8081 0 : TableInfo *tbinfo = NULL;
8082 0 : char **indAttNames = NULL;
8083 0 : int nindAttNames = 0;
8084 0 : int numinds;
8085 :
8086 : /* Count rows for this table */
8087 0 : for (numinds = 1; numinds < ntups - j; numinds++)
8088 0 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8089 0 : break;
8090 :
8091 : /*
8092 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8093 : * order.
8094 : */
8095 0 : while (++curtblindx < numTables)
8096 : {
8097 0 : tbinfo = &tblinfo[curtblindx];
8098 0 : if (tbinfo->dobj.catId.oid == indrelid)
8099 0 : break;
8100 : }
8101 0 : if (curtblindx >= numTables)
8102 0 : pg_fatal("unrecognized table OID %u", indrelid);
8103 : /* cross-check that we only got requested tables */
8104 0 : if (!tbinfo->hasindex ||
8105 0 : !tbinfo->interesting)
8106 0 : pg_fatal("unexpected index data for table \"%s\"",
8107 : tbinfo->dobj.name);
8108 :
8109 : /* Save data for this table */
8110 0 : tbinfo->indexes = indxinfo + j;
8111 0 : tbinfo->numIndexes = numinds;
8112 :
8113 0 : for (int c = 0; c < numinds; c++, j++)
8114 : {
8115 0 : char contype;
8116 0 : char indexkind;
8117 0 : RelStatsInfo *relstats;
8118 0 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8119 0 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8120 0 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8121 :
8122 0 : indxinfo[j].dobj.objType = DO_INDEX;
8123 0 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8124 0 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8125 0 : AssignDumpId(&indxinfo[j].dobj);
8126 0 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8127 0 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8128 0 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8129 0 : indxinfo[j].indextable = tbinfo;
8130 0 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8131 0 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8132 0 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8133 0 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8134 0 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8135 0 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8136 0 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8137 0 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8138 0 : parseOidArray(PQgetvalue(res, j, i_indkey),
8139 0 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8140 0 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8141 0 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8142 0 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8143 0 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8144 0 : indxinfo[j].partattaches = (SimplePtrList)
8145 0 : {
8146 : NULL, NULL
8147 : };
8148 :
8149 0 : if (indxinfo[j].parentidx == 0)
8150 0 : indexkind = RELKIND_INDEX;
8151 : else
8152 0 : indexkind = RELKIND_PARTITIONED_INDEX;
8153 :
8154 0 : if (!PQgetisnull(res, j, i_indattnames))
8155 : {
8156 0 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8157 : &indAttNames, &nindAttNames))
8158 0 : pg_fatal("could not parse %s array", "indattnames");
8159 0 : }
8160 :
8161 0 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8162 0 : PQgetvalue(res, j, i_reltuples),
8163 0 : relallvisible, relallfrozen, indexkind,
8164 0 : indAttNames, nindAttNames);
8165 :
8166 0 : contype = *(PQgetvalue(res, j, i_contype));
8167 0 : if (contype == 'p' || contype == 'u' || contype == 'x')
8168 : {
8169 : /*
8170 : * If we found a constraint matching the index, create an
8171 : * entry for it.
8172 : */
8173 0 : ConstraintInfo *constrinfo;
8174 :
8175 0 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8176 0 : constrinfo->dobj.objType = DO_CONSTRAINT;
8177 0 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8178 0 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8179 0 : AssignDumpId(&constrinfo->dobj);
8180 0 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8181 0 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8182 0 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8183 0 : constrinfo->contable = tbinfo;
8184 0 : constrinfo->condomain = NULL;
8185 0 : constrinfo->contype = contype;
8186 0 : if (contype == 'x')
8187 0 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8188 : else
8189 0 : constrinfo->condef = NULL;
8190 0 : constrinfo->confrelid = InvalidOid;
8191 0 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8192 0 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8193 0 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8194 0 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8195 0 : constrinfo->conislocal = true;
8196 0 : constrinfo->separate = true;
8197 :
8198 0 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8199 0 : if (relstats != NULL)
8200 0 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8201 0 : }
8202 : else
8203 : {
8204 : /* Plain secondary index */
8205 0 : indxinfo[j].indexconstraint = 0;
8206 : }
8207 0 : }
8208 0 : }
8209 :
8210 0 : PQclear(res);
8211 :
8212 0 : destroyPQExpBuffer(query);
8213 0 : destroyPQExpBuffer(tbloids);
8214 0 : }
8215 :
8216 : /*
8217 : * getExtendedStatistics
8218 : * get information about extended-statistics objects.
8219 : *
8220 : * Note: extended statistics data is not returned directly to the caller, but
8221 : * it does get entered into the DumpableObject tables.
8222 : */
8223 : void
8224 0 : getExtendedStatistics(Archive *fout)
8225 : {
8226 0 : PQExpBuffer query;
8227 0 : PGresult *res;
8228 0 : StatsExtInfo *statsextinfo;
8229 0 : int ntups;
8230 0 : int i_tableoid;
8231 0 : int i_oid;
8232 0 : int i_stxname;
8233 0 : int i_stxnamespace;
8234 0 : int i_stxowner;
8235 0 : int i_stxrelid;
8236 0 : int i_stattarget;
8237 0 : int i;
8238 :
8239 : /* Extended statistics were new in v10 */
8240 0 : if (fout->remoteVersion < 100000)
8241 0 : return;
8242 :
8243 0 : query = createPQExpBuffer();
8244 :
8245 0 : if (fout->remoteVersion < 130000)
8246 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8247 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8248 : "FROM pg_catalog.pg_statistic_ext");
8249 : else
8250 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8251 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8252 : "FROM pg_catalog.pg_statistic_ext");
8253 :
8254 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8255 :
8256 0 : ntups = PQntuples(res);
8257 :
8258 0 : i_tableoid = PQfnumber(res, "tableoid");
8259 0 : i_oid = PQfnumber(res, "oid");
8260 0 : i_stxname = PQfnumber(res, "stxname");
8261 0 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8262 0 : i_stxowner = PQfnumber(res, "stxowner");
8263 0 : i_stxrelid = PQfnumber(res, "stxrelid");
8264 0 : i_stattarget = PQfnumber(res, "stxstattarget");
8265 :
8266 0 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8267 :
8268 0 : for (i = 0; i < ntups; i++)
8269 : {
8270 0 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8271 0 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8272 0 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8273 0 : AssignDumpId(&statsextinfo[i].dobj);
8274 0 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8275 0 : statsextinfo[i].dobj.namespace =
8276 0 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8277 0 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8278 0 : statsextinfo[i].stattable =
8279 0 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8280 0 : if (PQgetisnull(res, i, i_stattarget))
8281 0 : statsextinfo[i].stattarget = -1;
8282 : else
8283 0 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8284 :
8285 : /* Decide whether we want to dump it */
8286 0 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8287 0 : }
8288 :
8289 0 : PQclear(res);
8290 0 : destroyPQExpBuffer(query);
8291 0 : }
8292 :
8293 : /*
8294 : * getConstraints
8295 : *
8296 : * Get info about constraints on dumpable tables.
8297 : *
8298 : * Currently handles foreign keys only.
8299 : * Unique and primary key constraints are handled with indexes,
8300 : * while check constraints are processed in getTableAttrs().
8301 : */
8302 : void
8303 0 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8304 : {
8305 0 : PQExpBuffer query = createPQExpBuffer();
8306 0 : PQExpBuffer tbloids = createPQExpBuffer();
8307 0 : PGresult *res;
8308 0 : int ntups;
8309 0 : int curtblindx;
8310 0 : TableInfo *tbinfo = NULL;
8311 0 : ConstraintInfo *constrinfo;
8312 0 : int i_contableoid,
8313 : i_conoid,
8314 : i_conrelid,
8315 : i_conname,
8316 : i_confrelid,
8317 : i_conindid,
8318 : i_condef;
8319 :
8320 : /*
8321 : * We want to perform just one query against pg_constraint. However, we
8322 : * mustn't try to select every row of the catalog and then sort it out on
8323 : * the client side, because some of the server-side functions we need
8324 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8325 : * build an array of the OIDs of tables we care about (and now have lock
8326 : * on!), and use a WHERE clause to constrain which rows are selected.
8327 : */
8328 0 : appendPQExpBufferChar(tbloids, '{');
8329 0 : for (int i = 0; i < numTables; i++)
8330 : {
8331 0 : TableInfo *tinfo = &tblinfo[i];
8332 :
8333 0 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8334 0 : continue;
8335 :
8336 : /* OK, we need info for this table */
8337 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
8338 0 : appendPQExpBufferChar(tbloids, ',');
8339 0 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8340 0 : }
8341 0 : appendPQExpBufferChar(tbloids, '}');
8342 :
8343 0 : appendPQExpBufferStr(query,
8344 : "SELECT c.tableoid, c.oid, "
8345 : "conrelid, conname, confrelid, ");
8346 0 : if (fout->remoteVersion >= 110000)
8347 0 : appendPQExpBufferStr(query, "conindid, ");
8348 : else
8349 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8350 0 : appendPQExpBuffer(query,
8351 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8352 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8353 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8354 : "WHERE contype = 'f' ",
8355 0 : tbloids->data);
8356 0 : if (fout->remoteVersion >= 110000)
8357 0 : appendPQExpBufferStr(query,
8358 : "AND conparentid = 0 ");
8359 0 : appendPQExpBufferStr(query,
8360 : "ORDER BY conrelid, conname");
8361 :
8362 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8363 :
8364 0 : ntups = PQntuples(res);
8365 :
8366 0 : i_contableoid = PQfnumber(res, "tableoid");
8367 0 : i_conoid = PQfnumber(res, "oid");
8368 0 : i_conrelid = PQfnumber(res, "conrelid");
8369 0 : i_conname = PQfnumber(res, "conname");
8370 0 : i_confrelid = PQfnumber(res, "confrelid");
8371 0 : i_conindid = PQfnumber(res, "conindid");
8372 0 : i_condef = PQfnumber(res, "condef");
8373 :
8374 0 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8375 :
8376 0 : curtblindx = -1;
8377 0 : for (int j = 0; j < ntups; j++)
8378 : {
8379 0 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8380 0 : TableInfo *reftable;
8381 :
8382 : /*
8383 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8384 : * order.
8385 : */
8386 0 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8387 : {
8388 0 : while (++curtblindx < numTables)
8389 : {
8390 0 : tbinfo = &tblinfo[curtblindx];
8391 0 : if (tbinfo->dobj.catId.oid == conrelid)
8392 0 : break;
8393 : }
8394 0 : if (curtblindx >= numTables)
8395 0 : pg_fatal("unrecognized table OID %u", conrelid);
8396 0 : }
8397 :
8398 0 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8399 0 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8400 0 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8401 0 : AssignDumpId(&constrinfo[j].dobj);
8402 0 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8403 0 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8404 0 : constrinfo[j].contable = tbinfo;
8405 0 : constrinfo[j].condomain = NULL;
8406 0 : constrinfo[j].contype = 'f';
8407 0 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8408 0 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8409 0 : constrinfo[j].conindex = 0;
8410 0 : constrinfo[j].condeferrable = false;
8411 0 : constrinfo[j].condeferred = false;
8412 0 : constrinfo[j].conislocal = true;
8413 0 : constrinfo[j].separate = true;
8414 :
8415 : /*
8416 : * Restoring an FK that points to a partitioned table requires that
8417 : * all partition indexes have been attached beforehand. Ensure that
8418 : * happens by making the constraint depend on each index partition
8419 : * attach object.
8420 : */
8421 0 : reftable = findTableByOid(constrinfo[j].confrelid);
8422 0 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8423 : {
8424 0 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8425 :
8426 0 : if (indexOid != InvalidOid)
8427 : {
8428 0 : for (int k = 0; k < reftable->numIndexes; k++)
8429 : {
8430 0 : IndxInfo *refidx;
8431 :
8432 : /* not our index? */
8433 0 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8434 0 : continue;
8435 :
8436 0 : refidx = &reftable->indexes[k];
8437 0 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8438 0 : break;
8439 0 : }
8440 0 : }
8441 0 : }
8442 0 : }
8443 :
8444 0 : PQclear(res);
8445 :
8446 0 : destroyPQExpBuffer(query);
8447 0 : destroyPQExpBuffer(tbloids);
8448 0 : }
8449 :
8450 : /*
8451 : * addConstrChildIdxDeps
8452 : *
8453 : * Recursive subroutine for getConstraints
8454 : *
8455 : * Given an object representing a foreign key constraint and an index on the
8456 : * partitioned table it references, mark the constraint object as dependent
8457 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8458 : * drilling down to their partitions if any. This ensures that the FK is not
8459 : * restored until the index is fully marked valid.
8460 : */
8461 : static void
8462 0 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8463 : {
8464 0 : SimplePtrListCell *cell;
8465 :
8466 0 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8467 :
8468 0 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8469 : {
8470 0 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8471 :
8472 0 : addObjectDependency(dobj, attach->dobj.dumpId);
8473 :
8474 0 : if (attach->partitionIdx->partattaches.head != NULL)
8475 0 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8476 0 : }
8477 0 : }
8478 :
8479 : /*
8480 : * getDomainConstraints
8481 : *
8482 : * Get info about constraints on a domain.
8483 : */
8484 : static void
8485 0 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8486 : {
8487 0 : ConstraintInfo *constrinfo;
8488 0 : PQExpBuffer query = createPQExpBuffer();
8489 0 : PGresult *res;
8490 0 : int i_tableoid,
8491 : i_oid,
8492 : i_conname,
8493 : i_consrc,
8494 : i_convalidated,
8495 : i_contype;
8496 0 : int ntups;
8497 :
8498 0 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8499 : {
8500 : /*
8501 : * Set up query for constraint-specific details. For servers 17 and
8502 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8503 : * just the latter.
8504 : */
8505 0 : appendPQExpBuffer(query,
8506 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8507 : "SELECT tableoid, oid, conname, "
8508 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8509 : "convalidated, contype "
8510 : "FROM pg_catalog.pg_constraint "
8511 : "WHERE contypid = $1 AND contype IN (%s) "
8512 : "ORDER BY conname",
8513 0 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8514 :
8515 0 : ExecuteSqlStatement(fout, query->data);
8516 :
8517 0 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8518 0 : }
8519 :
8520 0 : printfPQExpBuffer(query,
8521 : "EXECUTE getDomainConstraints('%u')",
8522 0 : tyinfo->dobj.catId.oid);
8523 :
8524 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8525 :
8526 0 : ntups = PQntuples(res);
8527 :
8528 0 : i_tableoid = PQfnumber(res, "tableoid");
8529 0 : i_oid = PQfnumber(res, "oid");
8530 0 : i_conname = PQfnumber(res, "conname");
8531 0 : i_consrc = PQfnumber(res, "consrc");
8532 0 : i_convalidated = PQfnumber(res, "convalidated");
8533 0 : i_contype = PQfnumber(res, "contype");
8534 :
8535 0 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8536 0 : tyinfo->domChecks = constrinfo;
8537 :
8538 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8539 0 : for (int i = 0, j = 0; i < ntups; i++)
8540 : {
8541 0 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8542 0 : char contype = (PQgetvalue(res, i, i_contype))[0];
8543 0 : ConstraintInfo *constraint;
8544 :
8545 0 : if (contype == CONSTRAINT_CHECK)
8546 : {
8547 0 : constraint = &constrinfo[j++];
8548 0 : tyinfo->nDomChecks++;
8549 0 : }
8550 : else
8551 : {
8552 0 : Assert(contype == CONSTRAINT_NOTNULL);
8553 0 : Assert(tyinfo->notnull == NULL);
8554 : /* use last item in array for the not-null constraint */
8555 0 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8556 0 : constraint = tyinfo->notnull;
8557 : }
8558 :
8559 0 : constraint->dobj.objType = DO_CONSTRAINT;
8560 0 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8561 0 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8562 0 : AssignDumpId(&(constraint->dobj));
8563 0 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8564 0 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8565 0 : constraint->contable = NULL;
8566 0 : constraint->condomain = tyinfo;
8567 0 : constraint->contype = contype;
8568 0 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8569 0 : constraint->confrelid = InvalidOid;
8570 0 : constraint->conindex = 0;
8571 0 : constraint->condeferrable = false;
8572 0 : constraint->condeferred = false;
8573 0 : constraint->conislocal = true;
8574 :
8575 0 : constraint->separate = !validated;
8576 :
8577 : /*
8578 : * Make the domain depend on the constraint, ensuring it won't be
8579 : * output till any constraint dependencies are OK. If the constraint
8580 : * has not been validated, it's going to be dumped after the domain
8581 : * anyway, so this doesn't matter.
8582 : */
8583 0 : if (validated)
8584 0 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8585 0 : }
8586 :
8587 0 : PQclear(res);
8588 :
8589 0 : destroyPQExpBuffer(query);
8590 0 : }
8591 :
8592 : /*
8593 : * getRules
8594 : * get basic information about every rule in the system
8595 : */
8596 : void
8597 0 : getRules(Archive *fout)
8598 : {
8599 0 : PGresult *res;
8600 0 : int ntups;
8601 0 : int i;
8602 0 : PQExpBuffer query = createPQExpBuffer();
8603 0 : RuleInfo *ruleinfo;
8604 0 : int i_tableoid;
8605 0 : int i_oid;
8606 0 : int i_rulename;
8607 0 : int i_ruletable;
8608 0 : int i_ev_type;
8609 0 : int i_is_instead;
8610 0 : int i_ev_enabled;
8611 :
8612 0 : appendPQExpBufferStr(query, "SELECT "
8613 : "tableoid, oid, rulename, "
8614 : "ev_class AS ruletable, ev_type, is_instead, "
8615 : "ev_enabled "
8616 : "FROM pg_rewrite "
8617 : "ORDER BY oid");
8618 :
8619 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8620 :
8621 0 : ntups = PQntuples(res);
8622 :
8623 0 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8624 :
8625 0 : i_tableoid = PQfnumber(res, "tableoid");
8626 0 : i_oid = PQfnumber(res, "oid");
8627 0 : i_rulename = PQfnumber(res, "rulename");
8628 0 : i_ruletable = PQfnumber(res, "ruletable");
8629 0 : i_ev_type = PQfnumber(res, "ev_type");
8630 0 : i_is_instead = PQfnumber(res, "is_instead");
8631 0 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8632 :
8633 0 : for (i = 0; i < ntups; i++)
8634 : {
8635 0 : Oid ruletableoid;
8636 :
8637 0 : ruleinfo[i].dobj.objType = DO_RULE;
8638 0 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8639 0 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8640 0 : AssignDumpId(&ruleinfo[i].dobj);
8641 0 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8642 0 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8643 0 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8644 0 : if (ruleinfo[i].ruletable == NULL)
8645 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8646 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8647 0 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8648 0 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8649 0 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8650 0 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8651 0 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8652 0 : if (ruleinfo[i].ruletable)
8653 : {
8654 : /*
8655 : * If the table is a view or materialized view, force its ON
8656 : * SELECT rule to be sorted before the view itself --- this
8657 : * ensures that any dependencies for the rule affect the table's
8658 : * positioning. Other rules are forced to appear after their
8659 : * table.
8660 : */
8661 0 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8662 0 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8663 0 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8664 : {
8665 0 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8666 0 : ruleinfo[i].dobj.dumpId);
8667 : /* We'll merge the rule into CREATE VIEW, if possible */
8668 0 : ruleinfo[i].separate = false;
8669 0 : }
8670 : else
8671 : {
8672 0 : addObjectDependency(&ruleinfo[i].dobj,
8673 0 : ruleinfo[i].ruletable->dobj.dumpId);
8674 0 : ruleinfo[i].separate = true;
8675 : }
8676 0 : }
8677 : else
8678 0 : ruleinfo[i].separate = true;
8679 0 : }
8680 :
8681 0 : PQclear(res);
8682 :
8683 0 : destroyPQExpBuffer(query);
8684 0 : }
8685 :
8686 : /*
8687 : * getTriggers
8688 : * get information about every trigger on a dumpable table
8689 : *
8690 : * Note: trigger data is not returned directly to the caller, but it
8691 : * does get entered into the DumpableObject tables.
8692 : */
8693 : void
8694 0 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8695 : {
8696 0 : PQExpBuffer query = createPQExpBuffer();
8697 0 : PQExpBuffer tbloids = createPQExpBuffer();
8698 0 : PGresult *res;
8699 0 : int ntups;
8700 0 : int curtblindx;
8701 0 : TriggerInfo *tginfo;
8702 0 : int i_tableoid,
8703 : i_oid,
8704 : i_tgrelid,
8705 : i_tgname,
8706 : i_tgenabled,
8707 : i_tgispartition,
8708 : i_tgdef;
8709 :
8710 : /*
8711 : * We want to perform just one query against pg_trigger. However, we
8712 : * mustn't try to select every row of the catalog and then sort it out on
8713 : * the client side, because some of the server-side functions we need
8714 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8715 : * build an array of the OIDs of tables we care about (and now have lock
8716 : * on!), and use a WHERE clause to constrain which rows are selected.
8717 : */
8718 0 : appendPQExpBufferChar(tbloids, '{');
8719 0 : for (int i = 0; i < numTables; i++)
8720 : {
8721 0 : TableInfo *tbinfo = &tblinfo[i];
8722 :
8723 0 : if (!tbinfo->hastriggers ||
8724 0 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8725 0 : continue;
8726 :
8727 : /* OK, we need info for this table */
8728 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
8729 0 : appendPQExpBufferChar(tbloids, ',');
8730 0 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8731 0 : }
8732 0 : appendPQExpBufferChar(tbloids, '}');
8733 :
8734 0 : if (fout->remoteVersion >= 150000)
8735 : {
8736 : /*
8737 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8738 : * result in non-forward-compatible dumps of WHEN clauses due to
8739 : * under-parenthesization.
8740 : *
8741 : * NB: We need to see partition triggers in case the tgenabled flag
8742 : * has been changed from the parent.
8743 : */
8744 0 : appendPQExpBuffer(query,
8745 : "SELECT t.tgrelid, t.tgname, "
8746 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8747 : "t.tgenabled, t.tableoid, t.oid, "
8748 : "t.tgparentid <> 0 AS tgispartition\n"
8749 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8750 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8751 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8752 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8753 : "OR t.tgenabled != u.tgenabled) "
8754 : "ORDER BY t.tgrelid, t.tgname",
8755 0 : tbloids->data);
8756 0 : }
8757 0 : else if (fout->remoteVersion >= 130000)
8758 : {
8759 : /*
8760 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8761 : * result in non-forward-compatible dumps of WHEN clauses due to
8762 : * under-parenthesization.
8763 : *
8764 : * NB: We need to see tgisinternal triggers in partitions, in case the
8765 : * tgenabled flag has been changed from the parent.
8766 : */
8767 0 : appendPQExpBuffer(query,
8768 : "SELECT t.tgrelid, t.tgname, "
8769 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8770 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8771 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8772 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8773 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8774 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8775 : "ORDER BY t.tgrelid, t.tgname",
8776 0 : tbloids->data);
8777 0 : }
8778 0 : else if (fout->remoteVersion >= 110000)
8779 : {
8780 : /*
8781 : * NB: We need to see tgisinternal triggers in partitions, in case the
8782 : * tgenabled flag has been changed from the parent. No tgparentid in
8783 : * version 11-12, so we have to match them via pg_depend.
8784 : *
8785 : * See above about pretty=true in pg_get_triggerdef.
8786 : */
8787 0 : appendPQExpBuffer(query,
8788 : "SELECT t.tgrelid, t.tgname, "
8789 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8790 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8791 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8792 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8793 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8794 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8795 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8796 : " d.objid = t.oid "
8797 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8798 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8799 : "ORDER BY t.tgrelid, t.tgname",
8800 0 : tbloids->data);
8801 0 : }
8802 : else
8803 : {
8804 : /* See above about pretty=true in pg_get_triggerdef */
8805 0 : appendPQExpBuffer(query,
8806 : "SELECT t.tgrelid, t.tgname, "
8807 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8808 : "t.tgenabled, false as tgispartition, "
8809 : "t.tableoid, t.oid "
8810 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8811 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8812 : "WHERE NOT tgisinternal "
8813 : "ORDER BY t.tgrelid, t.tgname",
8814 0 : tbloids->data);
8815 : }
8816 :
8817 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8818 :
8819 0 : ntups = PQntuples(res);
8820 :
8821 0 : i_tableoid = PQfnumber(res, "tableoid");
8822 0 : i_oid = PQfnumber(res, "oid");
8823 0 : i_tgrelid = PQfnumber(res, "tgrelid");
8824 0 : i_tgname = PQfnumber(res, "tgname");
8825 0 : i_tgenabled = PQfnumber(res, "tgenabled");
8826 0 : i_tgispartition = PQfnumber(res, "tgispartition");
8827 0 : i_tgdef = PQfnumber(res, "tgdef");
8828 :
8829 0 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8830 :
8831 : /*
8832 : * Outer loop iterates once per table, not once per row. Incrementing of
8833 : * j is handled by the inner loop.
8834 : */
8835 0 : curtblindx = -1;
8836 0 : for (int j = 0; j < ntups;)
8837 : {
8838 0 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8839 0 : TableInfo *tbinfo = NULL;
8840 0 : int numtrigs;
8841 :
8842 : /* Count rows for this table */
8843 0 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8844 0 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8845 0 : break;
8846 :
8847 : /*
8848 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8849 : * order.
8850 : */
8851 0 : while (++curtblindx < numTables)
8852 : {
8853 0 : tbinfo = &tblinfo[curtblindx];
8854 0 : if (tbinfo->dobj.catId.oid == tgrelid)
8855 0 : break;
8856 : }
8857 0 : if (curtblindx >= numTables)
8858 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8859 :
8860 : /* Save data for this table */
8861 0 : tbinfo->triggers = tginfo + j;
8862 0 : tbinfo->numTriggers = numtrigs;
8863 :
8864 0 : for (int c = 0; c < numtrigs; c++, j++)
8865 : {
8866 0 : tginfo[j].dobj.objType = DO_TRIGGER;
8867 0 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8868 0 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8869 0 : AssignDumpId(&tginfo[j].dobj);
8870 0 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8871 0 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8872 0 : tginfo[j].tgtable = tbinfo;
8873 0 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8874 0 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8875 0 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8876 0 : }
8877 0 : }
8878 :
8879 0 : PQclear(res);
8880 :
8881 0 : destroyPQExpBuffer(query);
8882 0 : destroyPQExpBuffer(tbloids);
8883 0 : }
8884 :
8885 : /*
8886 : * getEventTriggers
8887 : * get information about event triggers
8888 : */
8889 : void
8890 0 : getEventTriggers(Archive *fout)
8891 : {
8892 0 : int i;
8893 0 : PQExpBuffer query;
8894 0 : PGresult *res;
8895 0 : EventTriggerInfo *evtinfo;
8896 0 : int i_tableoid,
8897 : i_oid,
8898 : i_evtname,
8899 : i_evtevent,
8900 : i_evtowner,
8901 : i_evttags,
8902 : i_evtfname,
8903 : i_evtenabled;
8904 0 : int ntups;
8905 :
8906 : /* Before 9.3, there are no event triggers */
8907 0 : if (fout->remoteVersion < 90300)
8908 0 : return;
8909 :
8910 0 : query = createPQExpBuffer();
8911 :
8912 0 : appendPQExpBufferStr(query,
8913 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8914 : "evtevent, evtowner, "
8915 : "array_to_string(array("
8916 : "select quote_literal(x) "
8917 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8918 : "e.evtfoid::regproc as evtfname "
8919 : "FROM pg_event_trigger e "
8920 : "ORDER BY e.oid");
8921 :
8922 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8923 :
8924 0 : ntups = PQntuples(res);
8925 :
8926 0 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8927 :
8928 0 : i_tableoid = PQfnumber(res, "tableoid");
8929 0 : i_oid = PQfnumber(res, "oid");
8930 0 : i_evtname = PQfnumber(res, "evtname");
8931 0 : i_evtevent = PQfnumber(res, "evtevent");
8932 0 : i_evtowner = PQfnumber(res, "evtowner");
8933 0 : i_evttags = PQfnumber(res, "evttags");
8934 0 : i_evtfname = PQfnumber(res, "evtfname");
8935 0 : i_evtenabled = PQfnumber(res, "evtenabled");
8936 :
8937 0 : for (i = 0; i < ntups; i++)
8938 : {
8939 0 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8940 0 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8941 0 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8942 0 : AssignDumpId(&evtinfo[i].dobj);
8943 0 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8944 0 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8945 0 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8946 0 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8947 0 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8948 0 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8949 0 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8950 :
8951 : /* Decide whether we want to dump it */
8952 0 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8953 0 : }
8954 :
8955 0 : PQclear(res);
8956 :
8957 0 : destroyPQExpBuffer(query);
8958 0 : }
8959 :
8960 : /*
8961 : * getProcLangs
8962 : * get basic information about every procedural language in the system
8963 : *
8964 : * NB: this must run after getFuncs() because we assume we can do
8965 : * findFuncByOid().
8966 : */
8967 : void
8968 0 : getProcLangs(Archive *fout)
8969 : {
8970 0 : PGresult *res;
8971 0 : int ntups;
8972 0 : int i;
8973 0 : PQExpBuffer query = createPQExpBuffer();
8974 0 : ProcLangInfo *planginfo;
8975 0 : int i_tableoid;
8976 0 : int i_oid;
8977 0 : int i_lanname;
8978 0 : int i_lanpltrusted;
8979 0 : int i_lanplcallfoid;
8980 0 : int i_laninline;
8981 0 : int i_lanvalidator;
8982 0 : int i_lanacl;
8983 0 : int i_acldefault;
8984 0 : int i_lanowner;
8985 :
8986 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8987 : "lanname, lanpltrusted, lanplcallfoid, "
8988 : "laninline, lanvalidator, "
8989 : "lanacl, "
8990 : "acldefault('l', lanowner) AS acldefault, "
8991 : "lanowner "
8992 : "FROM pg_language "
8993 : "WHERE lanispl "
8994 : "ORDER BY oid");
8995 :
8996 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8997 :
8998 0 : ntups = PQntuples(res);
8999 :
9000 0 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
9001 :
9002 0 : i_tableoid = PQfnumber(res, "tableoid");
9003 0 : i_oid = PQfnumber(res, "oid");
9004 0 : i_lanname = PQfnumber(res, "lanname");
9005 0 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
9006 0 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
9007 0 : i_laninline = PQfnumber(res, "laninline");
9008 0 : i_lanvalidator = PQfnumber(res, "lanvalidator");
9009 0 : i_lanacl = PQfnumber(res, "lanacl");
9010 0 : i_acldefault = PQfnumber(res, "acldefault");
9011 0 : i_lanowner = PQfnumber(res, "lanowner");
9012 :
9013 0 : for (i = 0; i < ntups; i++)
9014 : {
9015 0 : planginfo[i].dobj.objType = DO_PROCLANG;
9016 0 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9017 0 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9018 0 : AssignDumpId(&planginfo[i].dobj);
9019 :
9020 0 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9021 0 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9022 0 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9023 0 : planginfo[i].dacl.privtype = 0;
9024 0 : planginfo[i].dacl.initprivs = NULL;
9025 0 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9026 0 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9027 0 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9028 0 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9029 0 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9030 :
9031 : /* Decide whether we want to dump it */
9032 0 : selectDumpableProcLang(&(planginfo[i]), fout);
9033 :
9034 : /* Mark whether language has an ACL */
9035 0 : if (!PQgetisnull(res, i, i_lanacl))
9036 0 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9037 0 : }
9038 :
9039 0 : PQclear(res);
9040 :
9041 0 : destroyPQExpBuffer(query);
9042 0 : }
9043 :
9044 : /*
9045 : * getCasts
9046 : * get basic information about most casts in the system
9047 : *
9048 : * Skip casts from a range to its multirange, since we'll create those
9049 : * automatically.
9050 : */
9051 : void
9052 0 : getCasts(Archive *fout)
9053 : {
9054 0 : PGresult *res;
9055 0 : int ntups;
9056 0 : int i;
9057 0 : PQExpBuffer query = createPQExpBuffer();
9058 0 : CastInfo *castinfo;
9059 0 : int i_tableoid;
9060 0 : int i_oid;
9061 0 : int i_castsource;
9062 0 : int i_casttarget;
9063 0 : int i_castfunc;
9064 0 : int i_castcontext;
9065 0 : int i_castmethod;
9066 :
9067 0 : if (fout->remoteVersion >= 140000)
9068 : {
9069 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9070 : "castsource, casttarget, castfunc, castcontext, "
9071 : "castmethod "
9072 : "FROM pg_cast c "
9073 : "WHERE NOT EXISTS ( "
9074 : "SELECT 1 FROM pg_range r "
9075 : "WHERE c.castsource = r.rngtypid "
9076 : "AND c.casttarget = r.rngmultitypid "
9077 : ") "
9078 : "ORDER BY 3,4");
9079 0 : }
9080 : else
9081 : {
9082 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9083 : "castsource, casttarget, castfunc, castcontext, "
9084 : "castmethod "
9085 : "FROM pg_cast ORDER BY 3,4");
9086 : }
9087 :
9088 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9089 :
9090 0 : ntups = PQntuples(res);
9091 :
9092 0 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9093 :
9094 0 : i_tableoid = PQfnumber(res, "tableoid");
9095 0 : i_oid = PQfnumber(res, "oid");
9096 0 : i_castsource = PQfnumber(res, "castsource");
9097 0 : i_casttarget = PQfnumber(res, "casttarget");
9098 0 : i_castfunc = PQfnumber(res, "castfunc");
9099 0 : i_castcontext = PQfnumber(res, "castcontext");
9100 0 : i_castmethod = PQfnumber(res, "castmethod");
9101 :
9102 0 : for (i = 0; i < ntups; i++)
9103 : {
9104 0 : PQExpBufferData namebuf;
9105 0 : TypeInfo *sTypeInfo;
9106 0 : TypeInfo *tTypeInfo;
9107 :
9108 0 : castinfo[i].dobj.objType = DO_CAST;
9109 0 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9110 0 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9111 0 : AssignDumpId(&castinfo[i].dobj);
9112 0 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9113 0 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9114 0 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9115 0 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9116 0 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9117 :
9118 : /*
9119 : * Try to name cast as concatenation of typnames. This is only used
9120 : * for purposes of sorting. If we fail to find either type, the name
9121 : * will be an empty string.
9122 : */
9123 0 : initPQExpBuffer(&namebuf);
9124 0 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9125 0 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9126 0 : if (sTypeInfo && tTypeInfo)
9127 0 : appendPQExpBuffer(&namebuf, "%s %s",
9128 0 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9129 0 : castinfo[i].dobj.name = namebuf.data;
9130 :
9131 : /* Decide whether we want to dump it */
9132 0 : selectDumpableCast(&(castinfo[i]), fout);
9133 0 : }
9134 :
9135 0 : PQclear(res);
9136 :
9137 0 : destroyPQExpBuffer(query);
9138 0 : }
9139 :
9140 : static char *
9141 0 : get_language_name(Archive *fout, Oid langid)
9142 : {
9143 0 : PQExpBuffer query;
9144 0 : PGresult *res;
9145 0 : char *lanname;
9146 :
9147 0 : query = createPQExpBuffer();
9148 0 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9149 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9150 0 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9151 0 : destroyPQExpBuffer(query);
9152 0 : PQclear(res);
9153 :
9154 0 : return lanname;
9155 0 : }
9156 :
9157 : /*
9158 : * getTransforms
9159 : * get basic information about every transform in the system
9160 : */
9161 : void
9162 0 : getTransforms(Archive *fout)
9163 : {
9164 0 : PGresult *res;
9165 0 : int ntups;
9166 0 : int i;
9167 0 : PQExpBuffer query;
9168 0 : TransformInfo *transforminfo;
9169 0 : int i_tableoid;
9170 0 : int i_oid;
9171 0 : int i_trftype;
9172 0 : int i_trflang;
9173 0 : int i_trffromsql;
9174 0 : int i_trftosql;
9175 :
9176 : /* Transforms didn't exist pre-9.5 */
9177 0 : if (fout->remoteVersion < 90500)
9178 0 : return;
9179 :
9180 0 : query = createPQExpBuffer();
9181 :
9182 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9183 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9184 : "FROM pg_transform "
9185 : "ORDER BY 3,4");
9186 :
9187 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9188 :
9189 0 : ntups = PQntuples(res);
9190 :
9191 0 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9192 :
9193 0 : i_tableoid = PQfnumber(res, "tableoid");
9194 0 : i_oid = PQfnumber(res, "oid");
9195 0 : i_trftype = PQfnumber(res, "trftype");
9196 0 : i_trflang = PQfnumber(res, "trflang");
9197 0 : i_trffromsql = PQfnumber(res, "trffromsql");
9198 0 : i_trftosql = PQfnumber(res, "trftosql");
9199 :
9200 0 : for (i = 0; i < ntups; i++)
9201 : {
9202 0 : PQExpBufferData namebuf;
9203 0 : TypeInfo *typeInfo;
9204 0 : char *lanname;
9205 :
9206 0 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9207 0 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9208 0 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9209 0 : AssignDumpId(&transforminfo[i].dobj);
9210 0 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9211 0 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9212 0 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9213 0 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9214 :
9215 : /*
9216 : * Try to name transform as concatenation of type and language name.
9217 : * This is only used for purposes of sorting. If we fail to find
9218 : * either, the name will be an empty string.
9219 : */
9220 0 : initPQExpBuffer(&namebuf);
9221 0 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9222 0 : lanname = get_language_name(fout, transforminfo[i].trflang);
9223 0 : if (typeInfo && lanname)
9224 0 : appendPQExpBuffer(&namebuf, "%s %s",
9225 0 : typeInfo->dobj.name, lanname);
9226 0 : transforminfo[i].dobj.name = namebuf.data;
9227 0 : free(lanname);
9228 :
9229 : /* Decide whether we want to dump it */
9230 0 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9231 0 : }
9232 :
9233 0 : PQclear(res);
9234 :
9235 0 : destroyPQExpBuffer(query);
9236 0 : }
9237 :
9238 : /*
9239 : * getTableAttrs -
9240 : * for each interesting table, read info about its attributes
9241 : * (names, types, default values, CHECK constraints, etc)
9242 : *
9243 : * modifies tblinfo
9244 : */
9245 : void
9246 0 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9247 : {
9248 0 : DumpOptions *dopt = fout->dopt;
9249 0 : PQExpBuffer q = createPQExpBuffer();
9250 0 : PQExpBuffer tbloids = createPQExpBuffer();
9251 0 : PQExpBuffer checkoids = createPQExpBuffer();
9252 0 : PQExpBuffer invalidnotnulloids = NULL;
9253 0 : PGresult *res;
9254 0 : int ntups;
9255 0 : int curtblindx;
9256 0 : int i_attrelid;
9257 0 : int i_attnum;
9258 0 : int i_attname;
9259 0 : int i_atttypname;
9260 0 : int i_attstattarget;
9261 0 : int i_attstorage;
9262 0 : int i_typstorage;
9263 0 : int i_attidentity;
9264 0 : int i_attgenerated;
9265 0 : int i_attisdropped;
9266 0 : int i_attlen;
9267 0 : int i_attalign;
9268 0 : int i_attislocal;
9269 0 : int i_notnull_name;
9270 0 : int i_notnull_comment;
9271 0 : int i_notnull_noinherit;
9272 0 : int i_notnull_islocal;
9273 0 : int i_notnull_invalidoid;
9274 0 : int i_attoptions;
9275 0 : int i_attcollation;
9276 0 : int i_attcompression;
9277 0 : int i_attfdwoptions;
9278 0 : int i_attmissingval;
9279 0 : int i_atthasdef;
9280 :
9281 : /*
9282 : * We want to perform just one query against pg_attribute, and then just
9283 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9284 : * (for CHECK constraints and for NOT NULL constraints). However, we
9285 : * mustn't try to select every row of those catalogs and then sort it out
9286 : * on the client side, because some of the server-side functions we need
9287 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9288 : * build an array of the OIDs of tables we care about (and now have lock
9289 : * on!), and use a WHERE clause to constrain which rows are selected.
9290 : */
9291 0 : appendPQExpBufferChar(tbloids, '{');
9292 0 : appendPQExpBufferChar(checkoids, '{');
9293 0 : for (int i = 0; i < numTables; i++)
9294 : {
9295 0 : TableInfo *tbinfo = &tblinfo[i];
9296 :
9297 : /* Don't bother to collect info for sequences */
9298 0 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9299 0 : continue;
9300 :
9301 : /*
9302 : * Don't bother with uninteresting tables, either. For binary
9303 : * upgrades, this is bypassed for pg_largeobject_metadata and
9304 : * pg_shdepend so that the columns names are collected for the
9305 : * corresponding COPY commands. Restoring the data for those catalogs
9306 : * is faster than restoring the equivalent set of large object
9307 : * commands. We can only do this for upgrades from v12 and newer; in
9308 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9309 : * the OID column is hidden and won't be dumped.
9310 : */
9311 0 : if (!tbinfo->interesting &&
9312 0 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9313 0 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9314 0 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9315 0 : continue;
9316 :
9317 : /* OK, we need info for this table */
9318 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
9319 0 : appendPQExpBufferChar(tbloids, ',');
9320 0 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9321 :
9322 0 : if (tbinfo->ncheck > 0)
9323 : {
9324 : /* Also make a list of the ones with check constraints */
9325 0 : if (checkoids->len > 1) /* do we have more than the '{'? */
9326 0 : appendPQExpBufferChar(checkoids, ',');
9327 0 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9328 0 : }
9329 0 : }
9330 0 : appendPQExpBufferChar(tbloids, '}');
9331 0 : appendPQExpBufferChar(checkoids, '}');
9332 :
9333 : /*
9334 : * Find all the user attributes and their types.
9335 : *
9336 : * Since we only want to dump COLLATE clauses for attributes whose
9337 : * collation is different from their type's default, we use a CASE here to
9338 : * suppress uninteresting attcollations cheaply.
9339 : */
9340 0 : appendPQExpBufferStr(q,
9341 : "SELECT\n"
9342 : "a.attrelid,\n"
9343 : "a.attnum,\n"
9344 : "a.attname,\n"
9345 : "a.attstattarget,\n"
9346 : "a.attstorage,\n"
9347 : "t.typstorage,\n"
9348 : "a.atthasdef,\n"
9349 : "a.attisdropped,\n"
9350 : "a.attlen,\n"
9351 : "a.attalign,\n"
9352 : "a.attislocal,\n"
9353 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9354 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9355 : "CASE WHEN a.attcollation <> t.typcollation "
9356 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9357 : "pg_catalog.array_to_string(ARRAY("
9358 : "SELECT pg_catalog.quote_ident(option_name) || "
9359 : "' ' || pg_catalog.quote_literal(option_value) "
9360 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9361 : "ORDER BY option_name"
9362 : "), E',\n ') AS attfdwoptions,\n");
9363 :
9364 : /*
9365 : * Find out any NOT NULL markings for each column. In 18 and up we read
9366 : * pg_constraint to obtain the constraint name, and for valid constraints
9367 : * also pg_description to obtain its comment. notnull_noinherit is set
9368 : * according to the NO INHERIT property. For versions prior to 18, we
9369 : * store an empty string as the name when a constraint is marked as
9370 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9371 : * without a name); also, such cases are never NO INHERIT.
9372 : *
9373 : * For invalid constraints, we need to store their OIDs for processing
9374 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9375 : * is invalid, and NULL otherwise. Their comments are handled not here
9376 : * but by collectComments, because they're their own dumpable object.
9377 : *
9378 : * We track in notnull_islocal whether the constraint was defined directly
9379 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9380 : * might modify this later.
9381 : */
9382 0 : if (fout->remoteVersion >= 180000)
9383 0 : appendPQExpBufferStr(q,
9384 : "co.conname AS notnull_name,\n"
9385 : "CASE WHEN co.convalidated THEN pt.description"
9386 : " ELSE NULL END AS notnull_comment,\n"
9387 : "CASE WHEN NOT co.convalidated THEN co.oid "
9388 : "ELSE NULL END AS notnull_invalidoid,\n"
9389 : "co.connoinherit AS notnull_noinherit,\n"
9390 : "co.conislocal AS notnull_islocal,\n");
9391 : else
9392 0 : appendPQExpBufferStr(q,
9393 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9394 : "NULL AS notnull_comment,\n"
9395 : "NULL AS notnull_invalidoid,\n"
9396 : "false AS notnull_noinherit,\n"
9397 : "CASE WHEN a.attislocal THEN true\n"
9398 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9399 : " ELSE false\n"
9400 : "END AS notnull_islocal,\n");
9401 :
9402 0 : if (fout->remoteVersion >= 140000)
9403 0 : appendPQExpBufferStr(q,
9404 : "a.attcompression AS attcompression,\n");
9405 : else
9406 0 : appendPQExpBufferStr(q,
9407 : "'' AS attcompression,\n");
9408 :
9409 0 : if (fout->remoteVersion >= 100000)
9410 0 : appendPQExpBufferStr(q,
9411 : "a.attidentity,\n");
9412 : else
9413 0 : appendPQExpBufferStr(q,
9414 : "'' AS attidentity,\n");
9415 :
9416 0 : if (fout->remoteVersion >= 110000)
9417 0 : appendPQExpBufferStr(q,
9418 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9419 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9420 : else
9421 0 : appendPQExpBufferStr(q,
9422 : "NULL AS attmissingval,\n");
9423 :
9424 0 : if (fout->remoteVersion >= 120000)
9425 0 : appendPQExpBufferStr(q,
9426 : "a.attgenerated\n");
9427 : else
9428 0 : appendPQExpBufferStr(q,
9429 : "'' AS attgenerated\n");
9430 :
9431 : /* need left join to pg_type to not fail on dropped columns ... */
9432 0 : appendPQExpBuffer(q,
9433 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9434 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9435 : "LEFT JOIN pg_catalog.pg_type t "
9436 : "ON (a.atttypid = t.oid)\n",
9437 0 : tbloids->data);
9438 :
9439 : /*
9440 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9441 : * entries and pg_description to get their comments.
9442 : */
9443 0 : if (fout->remoteVersion >= 180000)
9444 0 : appendPQExpBufferStr(q,
9445 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9446 : "(a.attrelid = co.conrelid\n"
9447 : " AND co.contype = 'n' AND "
9448 : "co.conkey = array[a.attnum])\n"
9449 : " LEFT JOIN pg_catalog.pg_description pt ON "
9450 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9451 :
9452 0 : appendPQExpBufferStr(q,
9453 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9454 : "ORDER BY a.attrelid, a.attnum");
9455 :
9456 0 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9457 :
9458 0 : ntups = PQntuples(res);
9459 :
9460 0 : i_attrelid = PQfnumber(res, "attrelid");
9461 0 : i_attnum = PQfnumber(res, "attnum");
9462 0 : i_attname = PQfnumber(res, "attname");
9463 0 : i_atttypname = PQfnumber(res, "atttypname");
9464 0 : i_attstattarget = PQfnumber(res, "attstattarget");
9465 0 : i_attstorage = PQfnumber(res, "attstorage");
9466 0 : i_typstorage = PQfnumber(res, "typstorage");
9467 0 : i_attidentity = PQfnumber(res, "attidentity");
9468 0 : i_attgenerated = PQfnumber(res, "attgenerated");
9469 0 : i_attisdropped = PQfnumber(res, "attisdropped");
9470 0 : i_attlen = PQfnumber(res, "attlen");
9471 0 : i_attalign = PQfnumber(res, "attalign");
9472 0 : i_attislocal = PQfnumber(res, "attislocal");
9473 0 : i_notnull_name = PQfnumber(res, "notnull_name");
9474 0 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9475 0 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9476 0 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9477 0 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9478 0 : i_attoptions = PQfnumber(res, "attoptions");
9479 0 : i_attcollation = PQfnumber(res, "attcollation");
9480 0 : i_attcompression = PQfnumber(res, "attcompression");
9481 0 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9482 0 : i_attmissingval = PQfnumber(res, "attmissingval");
9483 0 : i_atthasdef = PQfnumber(res, "atthasdef");
9484 :
9485 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9486 0 : resetPQExpBuffer(tbloids);
9487 0 : appendPQExpBufferChar(tbloids, '{');
9488 :
9489 : /*
9490 : * Outer loop iterates once per table, not once per row. Incrementing of
9491 : * r is handled by the inner loop.
9492 : */
9493 0 : curtblindx = -1;
9494 0 : for (int r = 0; r < ntups;)
9495 : {
9496 0 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9497 0 : TableInfo *tbinfo = NULL;
9498 0 : int numatts;
9499 0 : bool hasdefaults;
9500 :
9501 : /* Count rows for this table */
9502 0 : for (numatts = 1; numatts < ntups - r; numatts++)
9503 0 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9504 0 : break;
9505 :
9506 : /*
9507 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9508 : * order.
9509 : */
9510 0 : while (++curtblindx < numTables)
9511 : {
9512 0 : tbinfo = &tblinfo[curtblindx];
9513 0 : if (tbinfo->dobj.catId.oid == attrelid)
9514 0 : break;
9515 : }
9516 0 : if (curtblindx >= numTables)
9517 0 : pg_fatal("unrecognized table OID %u", attrelid);
9518 : /* cross-check that we only got requested tables */
9519 0 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9520 0 : (!tbinfo->interesting &&
9521 0 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9522 0 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9523 0 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9524 0 : pg_fatal("unexpected column data for table \"%s\"",
9525 : tbinfo->dobj.name);
9526 :
9527 : /* Save data for this table */
9528 0 : tbinfo->numatts = numatts;
9529 0 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9530 0 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9531 0 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9532 0 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9533 0 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9534 0 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9535 0 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9536 0 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9537 0 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9538 0 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9539 0 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9540 0 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9541 0 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9542 0 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9543 0 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9544 0 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9545 0 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9546 0 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9547 0 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9548 0 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9549 0 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9550 0 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9551 0 : hasdefaults = false;
9552 :
9553 0 : for (int j = 0; j < numatts; j++, r++)
9554 : {
9555 0 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9556 0 : pg_fatal("invalid column numbering in table \"%s\"",
9557 : tbinfo->dobj.name);
9558 0 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9559 0 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9560 0 : if (PQgetisnull(res, r, i_attstattarget))
9561 0 : tbinfo->attstattarget[j] = -1;
9562 : else
9563 0 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9564 0 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9565 0 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9566 0 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9567 0 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9568 0 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9569 0 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9570 0 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9571 0 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9572 0 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9573 :
9574 : /* Handle not-null constraint name and flags */
9575 0 : determineNotNullFlags(fout, res, r,
9576 0 : tbinfo, j,
9577 0 : i_notnull_name,
9578 0 : i_notnull_comment,
9579 0 : i_notnull_invalidoid,
9580 0 : i_notnull_noinherit,
9581 0 : i_notnull_islocal,
9582 : &invalidnotnulloids);
9583 :
9584 0 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9585 0 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9586 0 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9587 0 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9588 0 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9589 0 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9590 0 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9591 0 : tbinfo->attrdefs[j] = NULL; /* fix below */
9592 0 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9593 0 : hasdefaults = true;
9594 0 : }
9595 :
9596 0 : if (hasdefaults)
9597 : {
9598 : /* Collect OIDs of interesting tables that have defaults */
9599 0 : if (tbloids->len > 1) /* do we have more than the '{'? */
9600 0 : appendPQExpBufferChar(tbloids, ',');
9601 0 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9602 0 : }
9603 0 : }
9604 :
9605 : /* If invalidnotnulloids has any data, finalize it */
9606 0 : if (invalidnotnulloids != NULL)
9607 0 : appendPQExpBufferChar(invalidnotnulloids, '}');
9608 :
9609 0 : PQclear(res);
9610 :
9611 : /*
9612 : * Now get info about column defaults. This is skipped for a data-only
9613 : * dump, as it is only needed for table schemas.
9614 : */
9615 0 : if (dopt->dumpSchema && tbloids->len > 1)
9616 : {
9617 0 : AttrDefInfo *attrdefs;
9618 0 : int numDefaults;
9619 0 : TableInfo *tbinfo = NULL;
9620 :
9621 0 : pg_log_info("finding table default expressions");
9622 :
9623 0 : appendPQExpBufferChar(tbloids, '}');
9624 :
9625 0 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9626 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9627 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9628 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9629 : "ORDER BY a.adrelid, a.adnum",
9630 0 : tbloids->data);
9631 :
9632 0 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9633 :
9634 0 : numDefaults = PQntuples(res);
9635 0 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9636 :
9637 0 : curtblindx = -1;
9638 0 : for (int j = 0; j < numDefaults; j++)
9639 : {
9640 0 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9641 0 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9642 0 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9643 0 : int adnum = atoi(PQgetvalue(res, j, 3));
9644 0 : char *adsrc = PQgetvalue(res, j, 4);
9645 :
9646 : /*
9647 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9648 : * OID order.
9649 : */
9650 0 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9651 : {
9652 0 : while (++curtblindx < numTables)
9653 : {
9654 0 : tbinfo = &tblinfo[curtblindx];
9655 0 : if (tbinfo->dobj.catId.oid == adrelid)
9656 0 : break;
9657 : }
9658 0 : if (curtblindx >= numTables)
9659 0 : pg_fatal("unrecognized table OID %u", adrelid);
9660 0 : }
9661 :
9662 0 : if (adnum <= 0 || adnum > tbinfo->numatts)
9663 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9664 : adnum, tbinfo->dobj.name);
9665 :
9666 : /*
9667 : * dropped columns shouldn't have defaults, but just in case,
9668 : * ignore 'em
9669 : */
9670 0 : if (tbinfo->attisdropped[adnum - 1])
9671 0 : continue;
9672 :
9673 0 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9674 0 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9675 0 : attrdefs[j].dobj.catId.oid = adoid;
9676 0 : AssignDumpId(&attrdefs[j].dobj);
9677 0 : attrdefs[j].adtable = tbinfo;
9678 0 : attrdefs[j].adnum = adnum;
9679 0 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9680 :
9681 0 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9682 0 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9683 :
9684 0 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9685 :
9686 : /*
9687 : * Figure out whether the default/generation expression should be
9688 : * dumped as part of the main CREATE TABLE (or similar) command or
9689 : * as a separate ALTER TABLE (or similar) command. The preference
9690 : * is to put it into the CREATE command, but in some cases that's
9691 : * not possible.
9692 : */
9693 0 : if (tbinfo->attgenerated[adnum - 1])
9694 : {
9695 : /*
9696 : * Column generation expressions cannot be dumped separately,
9697 : * because there is no syntax for it. By setting separate to
9698 : * false here we prevent the "default" from being processed as
9699 : * its own dumpable object. Later, flagInhAttrs() will mark
9700 : * it as not to be dumped at all, if possible (that is, if it
9701 : * can be inherited from a parent).
9702 : */
9703 0 : attrdefs[j].separate = false;
9704 0 : }
9705 0 : else if (tbinfo->relkind == RELKIND_VIEW)
9706 : {
9707 : /*
9708 : * Defaults on a VIEW must always be dumped as separate ALTER
9709 : * TABLE commands.
9710 : */
9711 0 : attrdefs[j].separate = true;
9712 0 : }
9713 0 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9714 : {
9715 : /* column will be suppressed, print default separately */
9716 0 : attrdefs[j].separate = true;
9717 0 : }
9718 : else
9719 : {
9720 0 : attrdefs[j].separate = false;
9721 : }
9722 :
9723 0 : if (!attrdefs[j].separate)
9724 : {
9725 : /*
9726 : * Mark the default as needing to appear before the table, so
9727 : * that any dependencies it has must be emitted before the
9728 : * CREATE TABLE. If this is not possible, we'll change to
9729 : * "separate" mode while sorting dependencies.
9730 : */
9731 0 : addObjectDependency(&tbinfo->dobj,
9732 0 : attrdefs[j].dobj.dumpId);
9733 0 : }
9734 :
9735 0 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9736 0 : }
9737 :
9738 0 : PQclear(res);
9739 0 : }
9740 :
9741 : /*
9742 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9743 : * data-only dump, as it is only needed for table schemas.
9744 : */
9745 0 : if (dopt->dumpSchema && invalidnotnulloids)
9746 : {
9747 0 : ConstraintInfo *constrs;
9748 0 : int numConstrs;
9749 0 : int i_tableoid;
9750 0 : int i_oid;
9751 0 : int i_conrelid;
9752 0 : int i_conname;
9753 0 : int i_consrc;
9754 0 : int i_conislocal;
9755 :
9756 0 : pg_log_info("finding invalid not-null constraints");
9757 :
9758 0 : resetPQExpBuffer(q);
9759 0 : appendPQExpBuffer(q,
9760 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9761 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9762 : "conislocal, convalidated "
9763 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9764 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9765 : "ORDER BY c.conrelid, c.conname",
9766 0 : invalidnotnulloids->data);
9767 :
9768 0 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9769 :
9770 0 : numConstrs = PQntuples(res);
9771 0 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9772 :
9773 0 : i_tableoid = PQfnumber(res, "tableoid");
9774 0 : i_oid = PQfnumber(res, "oid");
9775 0 : i_conrelid = PQfnumber(res, "conrelid");
9776 0 : i_conname = PQfnumber(res, "conname");
9777 0 : i_consrc = PQfnumber(res, "consrc");
9778 0 : i_conislocal = PQfnumber(res, "conislocal");
9779 :
9780 : /* As above, this loop iterates once per table, not once per row */
9781 0 : curtblindx = -1;
9782 0 : for (int j = 0; j < numConstrs;)
9783 : {
9784 0 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9785 0 : TableInfo *tbinfo = NULL;
9786 0 : int numcons;
9787 :
9788 : /* Count rows for this table */
9789 0 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9790 0 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9791 0 : break;
9792 :
9793 : /*
9794 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9795 : * OID order.
9796 : */
9797 0 : while (++curtblindx < numTables)
9798 : {
9799 0 : tbinfo = &tblinfo[curtblindx];
9800 0 : if (tbinfo->dobj.catId.oid == conrelid)
9801 0 : break;
9802 : }
9803 0 : if (curtblindx >= numTables)
9804 0 : pg_fatal("unrecognized table OID %u", conrelid);
9805 :
9806 0 : for (int c = 0; c < numcons; c++, j++)
9807 : {
9808 0 : constrs[j].dobj.objType = DO_CONSTRAINT;
9809 0 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9810 0 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9811 0 : AssignDumpId(&constrs[j].dobj);
9812 0 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9813 0 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9814 0 : constrs[j].contable = tbinfo;
9815 0 : constrs[j].condomain = NULL;
9816 0 : constrs[j].contype = 'n';
9817 0 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9818 0 : constrs[j].confrelid = InvalidOid;
9819 0 : constrs[j].conindex = 0;
9820 0 : constrs[j].condeferrable = false;
9821 0 : constrs[j].condeferred = false;
9822 0 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9823 :
9824 : /*
9825 : * All invalid not-null constraints must be dumped separately,
9826 : * because CREATE TABLE would not create them as invalid, and
9827 : * also because they must be created after potentially
9828 : * violating data has been loaded.
9829 : */
9830 0 : constrs[j].separate = true;
9831 :
9832 0 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9833 0 : }
9834 0 : }
9835 0 : PQclear(res);
9836 0 : }
9837 :
9838 : /*
9839 : * Get info about table CHECK constraints. This is skipped for a
9840 : * data-only dump, as it is only needed for table schemas.
9841 : */
9842 0 : if (dopt->dumpSchema && checkoids->len > 2)
9843 : {
9844 0 : ConstraintInfo *constrs;
9845 0 : int numConstrs;
9846 0 : int i_tableoid;
9847 0 : int i_oid;
9848 0 : int i_conrelid;
9849 0 : int i_conname;
9850 0 : int i_consrc;
9851 0 : int i_conislocal;
9852 0 : int i_convalidated;
9853 :
9854 0 : pg_log_info("finding table check constraints");
9855 :
9856 0 : resetPQExpBuffer(q);
9857 0 : appendPQExpBuffer(q,
9858 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9859 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9860 : "conislocal, convalidated "
9861 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9862 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9863 : "WHERE contype = 'c' "
9864 : "ORDER BY c.conrelid, c.conname",
9865 0 : checkoids->data);
9866 :
9867 0 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9868 :
9869 0 : numConstrs = PQntuples(res);
9870 0 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9871 :
9872 0 : i_tableoid = PQfnumber(res, "tableoid");
9873 0 : i_oid = PQfnumber(res, "oid");
9874 0 : i_conrelid = PQfnumber(res, "conrelid");
9875 0 : i_conname = PQfnumber(res, "conname");
9876 0 : i_consrc = PQfnumber(res, "consrc");
9877 0 : i_conislocal = PQfnumber(res, "conislocal");
9878 0 : i_convalidated = PQfnumber(res, "convalidated");
9879 :
9880 : /* As above, this loop iterates once per table, not once per row */
9881 0 : curtblindx = -1;
9882 0 : for (int j = 0; j < numConstrs;)
9883 : {
9884 0 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9885 0 : TableInfo *tbinfo = NULL;
9886 0 : int numcons;
9887 :
9888 : /* Count rows for this table */
9889 0 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9890 0 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9891 0 : break;
9892 :
9893 : /*
9894 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9895 : * OID order.
9896 : */
9897 0 : while (++curtblindx < numTables)
9898 : {
9899 0 : tbinfo = &tblinfo[curtblindx];
9900 0 : if (tbinfo->dobj.catId.oid == conrelid)
9901 0 : break;
9902 : }
9903 0 : if (curtblindx >= numTables)
9904 0 : pg_fatal("unrecognized table OID %u", conrelid);
9905 :
9906 0 : if (numcons != tbinfo->ncheck)
9907 : {
9908 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9909 : "expected %d check constraints on table \"%s\" but found %d",
9910 : tbinfo->ncheck),
9911 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9912 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9913 0 : exit_nicely(1);
9914 : }
9915 :
9916 0 : tbinfo->checkexprs = constrs + j;
9917 :
9918 0 : for (int c = 0; c < numcons; c++, j++)
9919 : {
9920 0 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9921 :
9922 0 : constrs[j].dobj.objType = DO_CONSTRAINT;
9923 0 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9924 0 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9925 0 : AssignDumpId(&constrs[j].dobj);
9926 0 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9927 0 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9928 0 : constrs[j].contable = tbinfo;
9929 0 : constrs[j].condomain = NULL;
9930 0 : constrs[j].contype = 'c';
9931 0 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9932 0 : constrs[j].confrelid = InvalidOid;
9933 0 : constrs[j].conindex = 0;
9934 0 : constrs[j].condeferrable = false;
9935 0 : constrs[j].condeferred = false;
9936 0 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9937 :
9938 : /*
9939 : * An unvalidated constraint needs to be dumped separately, so
9940 : * that potentially-violating existing data is loaded before
9941 : * the constraint.
9942 : */
9943 0 : constrs[j].separate = !validated;
9944 :
9945 0 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9946 :
9947 : /*
9948 : * Mark the constraint as needing to appear before the table
9949 : * --- this is so that any other dependencies of the
9950 : * constraint will be emitted before we try to create the
9951 : * table. If the constraint is to be dumped separately, it
9952 : * will be dumped after data is loaded anyway, so don't do it.
9953 : * (There's an automatic dependency in the opposite direction
9954 : * anyway, so don't need to add one manually here.)
9955 : */
9956 0 : if (!constrs[j].separate)
9957 0 : addObjectDependency(&tbinfo->dobj,
9958 0 : constrs[j].dobj.dumpId);
9959 :
9960 : /*
9961 : * We will detect later whether the constraint must be split
9962 : * out from the table definition.
9963 : */
9964 0 : }
9965 0 : }
9966 :
9967 0 : PQclear(res);
9968 0 : }
9969 :
9970 0 : destroyPQExpBuffer(q);
9971 0 : destroyPQExpBuffer(tbloids);
9972 0 : destroyPQExpBuffer(checkoids);
9973 0 : }
9974 :
9975 : /*
9976 : * Based on the getTableAttrs query's row corresponding to one column, set
9977 : * the name and flags to handle a not-null constraint for that column in
9978 : * the tbinfo struct.
9979 : *
9980 : * Result row 'r' is for tbinfo's attribute 'j'.
9981 : *
9982 : * There are four possibilities:
9983 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9984 : * (the constraint name) remains NULL.
9985 : * 2) The column has a constraint with no name (this is the case when
9986 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9987 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9988 : * 3) The column has an invalid not-null constraint. This must be treated
9989 : * as a separate object (because it must be created after the table data
9990 : * is loaded). So we add its OID to invalidnotnulloids for processing
9991 : * elsewhere and do nothing further with it here. We distinguish this
9992 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9993 : * value, which is the constraint OID. Valid constraints have a null OID.
9994 : * 4) The column has a constraint with a known name; in that case
9995 : * notnull_constrs carries that name and dumpTableSchema will print
9996 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9997 : * (table_column_not_null) and there's no comment on the constraint,
9998 : * there's no need to print that name in the dump, so notnull_constrs
9999 : * is set to the empty string and it behaves as case 2.
10000 : *
10001 : * In a child table that inherits from a parent already containing NOT NULL
10002 : * constraints and the columns in the child don't have their own NOT NULL
10003 : * declarations, we suppress printing constraints in the child: the
10004 : * constraints are acquired at the point where the child is attached to the
10005 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
10006 : * set not here but in flagInhAttrs. That flag is also used when the
10007 : * constraint was validated in a child but all its parent have it as NOT
10008 : * VALID.
10009 : *
10010 : * Any of these constraints might have the NO INHERIT bit. If so we set
10011 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10012 : *
10013 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
10014 : * to do the right thing in all but the trivial case. However, the downside
10015 : * of getting it wrong is simply that the name is printed rather than
10016 : * suppressed, so it's not a big deal.
10017 : *
10018 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10019 : * constraints are found, it is initialized and filled with the array of
10020 : * OIDs of such constraints, for later processing.
10021 : */
10022 : static void
10023 0 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
10024 : TableInfo *tbinfo, int j,
10025 : int i_notnull_name,
10026 : int i_notnull_comment,
10027 : int i_notnull_invalidoid,
10028 : int i_notnull_noinherit,
10029 : int i_notnull_islocal,
10030 : PQExpBuffer *invalidnotnulloids)
10031 : {
10032 0 : DumpOptions *dopt = fout->dopt;
10033 :
10034 : /*
10035 : * If this not-null constraint is not valid, list its OID in
10036 : * invalidnotnulloids and do nothing further. It'll be processed
10037 : * elsewhere later.
10038 : *
10039 : * Because invalid not-null constraints are rare, we don't want to malloc
10040 : * invalidnotnulloids until we're sure we're going it need it, which
10041 : * happens here.
10042 : */
10043 0 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10044 : {
10045 0 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10046 :
10047 0 : if (*invalidnotnulloids == NULL)
10048 : {
10049 0 : *invalidnotnulloids = createPQExpBuffer();
10050 0 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10051 0 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10052 0 : }
10053 : else
10054 0 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10055 :
10056 : /*
10057 : * Track when a parent constraint is invalid for the cases where a
10058 : * child constraint has been validated independenly.
10059 : */
10060 0 : tbinfo->notnull_invalid[j] = true;
10061 :
10062 : /* nothing else to do */
10063 0 : tbinfo->notnull_constrs[j] = NULL;
10064 : return;
10065 0 : }
10066 :
10067 : /*
10068 : * notnull_noinh is straight from the query result. notnull_islocal also,
10069 : * though flagInhAttrs may change that one later.
10070 : */
10071 0 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10072 0 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10073 0 : tbinfo->notnull_invalid[j] = false;
10074 :
10075 : /*
10076 : * Determine a constraint name to use. If the column is not marked not-
10077 : * null, we set NULL which cues ... to do nothing. An empty string says
10078 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10079 : * use.
10080 : */
10081 0 : if (fout->remoteVersion < 180000)
10082 : {
10083 : /*
10084 : * < 18 doesn't have not-null names, so an unnamed constraint is
10085 : * sufficient.
10086 : */
10087 0 : if (PQgetisnull(res, r, i_notnull_name))
10088 0 : tbinfo->notnull_constrs[j] = NULL;
10089 : else
10090 0 : tbinfo->notnull_constrs[j] = "";
10091 0 : }
10092 : else
10093 : {
10094 0 : if (PQgetisnull(res, r, i_notnull_name))
10095 0 : tbinfo->notnull_constrs[j] = NULL;
10096 : else
10097 : {
10098 : /*
10099 : * In binary upgrade of inheritance child tables, must have a
10100 : * constraint name that we can UPDATE later; same if there's a
10101 : * comment on the constraint.
10102 : */
10103 0 : if ((dopt->binary_upgrade &&
10104 0 : !tbinfo->ispartition &&
10105 0 : !tbinfo->notnull_islocal) ||
10106 0 : !PQgetisnull(res, r, i_notnull_comment))
10107 : {
10108 0 : tbinfo->notnull_constrs[j] =
10109 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10110 0 : }
10111 : else
10112 : {
10113 0 : char *default_name;
10114 :
10115 : /* XXX should match ChooseConstraintName better */
10116 0 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10117 0 : tbinfo->attnames[j]);
10118 0 : if (strcmp(default_name,
10119 0 : PQgetvalue(res, r, i_notnull_name)) == 0)
10120 0 : tbinfo->notnull_constrs[j] = "";
10121 : else
10122 : {
10123 0 : tbinfo->notnull_constrs[j] =
10124 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10125 : }
10126 0 : free(default_name);
10127 0 : }
10128 : }
10129 : }
10130 0 : }
10131 :
10132 : /*
10133 : * Test whether a column should be printed as part of table's CREATE TABLE.
10134 : * Column number is zero-based.
10135 : *
10136 : * Normally this is always true, but it's false for dropped columns, as well
10137 : * as those that were inherited without any local definition. (If we print
10138 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10139 : * For partitions, it's always true, because we want the partitions to be
10140 : * created independently and ATTACH PARTITION used afterwards.
10141 : *
10142 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10143 : * attisdropped state later, so as to keep control of the physical column
10144 : * order.
10145 : *
10146 : * This function exists because there are scattered nonobvious places that
10147 : * must be kept in sync with this decision.
10148 : */
10149 : bool
10150 0 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10151 : {
10152 0 : if (dopt->binary_upgrade)
10153 0 : return true;
10154 0 : if (tbinfo->attisdropped[colno])
10155 0 : return false;
10156 0 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10157 0 : }
10158 :
10159 :
10160 : /*
10161 : * getTSParsers:
10162 : * get information about all text search parsers in the system catalogs
10163 : */
10164 : void
10165 0 : getTSParsers(Archive *fout)
10166 : {
10167 0 : PGresult *res;
10168 0 : int ntups;
10169 0 : int i;
10170 0 : PQExpBuffer query;
10171 0 : TSParserInfo *prsinfo;
10172 0 : int i_tableoid;
10173 0 : int i_oid;
10174 0 : int i_prsname;
10175 0 : int i_prsnamespace;
10176 0 : int i_prsstart;
10177 0 : int i_prstoken;
10178 0 : int i_prsend;
10179 0 : int i_prsheadline;
10180 0 : int i_prslextype;
10181 :
10182 0 : query = createPQExpBuffer();
10183 :
10184 : /*
10185 : * find all text search objects, including builtin ones; we filter out
10186 : * system-defined objects at dump-out time.
10187 : */
10188 :
10189 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10190 : "prsstart::oid, prstoken::oid, "
10191 : "prsend::oid, prsheadline::oid, prslextype::oid "
10192 : "FROM pg_ts_parser");
10193 :
10194 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10195 :
10196 0 : ntups = PQntuples(res);
10197 :
10198 0 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10199 :
10200 0 : i_tableoid = PQfnumber(res, "tableoid");
10201 0 : i_oid = PQfnumber(res, "oid");
10202 0 : i_prsname = PQfnumber(res, "prsname");
10203 0 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10204 0 : i_prsstart = PQfnumber(res, "prsstart");
10205 0 : i_prstoken = PQfnumber(res, "prstoken");
10206 0 : i_prsend = PQfnumber(res, "prsend");
10207 0 : i_prsheadline = PQfnumber(res, "prsheadline");
10208 0 : i_prslextype = PQfnumber(res, "prslextype");
10209 :
10210 0 : for (i = 0; i < ntups; i++)
10211 : {
10212 0 : prsinfo[i].dobj.objType = DO_TSPARSER;
10213 0 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10214 0 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10215 0 : AssignDumpId(&prsinfo[i].dobj);
10216 0 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10217 0 : prsinfo[i].dobj.namespace =
10218 0 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10219 0 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10220 0 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10221 0 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10222 0 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10223 0 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10224 :
10225 : /* Decide whether we want to dump it */
10226 0 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10227 0 : }
10228 :
10229 0 : PQclear(res);
10230 :
10231 0 : destroyPQExpBuffer(query);
10232 0 : }
10233 :
10234 : /*
10235 : * getTSDictionaries:
10236 : * get information about all text search dictionaries in the system catalogs
10237 : */
10238 : void
10239 0 : getTSDictionaries(Archive *fout)
10240 : {
10241 0 : PGresult *res;
10242 0 : int ntups;
10243 0 : int i;
10244 0 : PQExpBuffer query;
10245 0 : TSDictInfo *dictinfo;
10246 0 : int i_tableoid;
10247 0 : int i_oid;
10248 0 : int i_dictname;
10249 0 : int i_dictnamespace;
10250 0 : int i_dictowner;
10251 0 : int i_dicttemplate;
10252 0 : int i_dictinitoption;
10253 :
10254 0 : query = createPQExpBuffer();
10255 :
10256 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10257 : "dictnamespace, dictowner, "
10258 : "dicttemplate, dictinitoption "
10259 : "FROM pg_ts_dict");
10260 :
10261 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10262 :
10263 0 : ntups = PQntuples(res);
10264 :
10265 0 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10266 :
10267 0 : i_tableoid = PQfnumber(res, "tableoid");
10268 0 : i_oid = PQfnumber(res, "oid");
10269 0 : i_dictname = PQfnumber(res, "dictname");
10270 0 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10271 0 : i_dictowner = PQfnumber(res, "dictowner");
10272 0 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10273 0 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10274 :
10275 0 : for (i = 0; i < ntups; i++)
10276 : {
10277 0 : dictinfo[i].dobj.objType = DO_TSDICT;
10278 0 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10279 0 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10280 0 : AssignDumpId(&dictinfo[i].dobj);
10281 0 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10282 0 : dictinfo[i].dobj.namespace =
10283 0 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10284 0 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10285 0 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10286 0 : if (PQgetisnull(res, i, i_dictinitoption))
10287 0 : dictinfo[i].dictinitoption = NULL;
10288 : else
10289 0 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10290 :
10291 : /* Decide whether we want to dump it */
10292 0 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10293 0 : }
10294 :
10295 0 : PQclear(res);
10296 :
10297 0 : destroyPQExpBuffer(query);
10298 0 : }
10299 :
10300 : /*
10301 : * getTSTemplates:
10302 : * get information about all text search templates in the system catalogs
10303 : */
10304 : void
10305 0 : getTSTemplates(Archive *fout)
10306 : {
10307 0 : PGresult *res;
10308 0 : int ntups;
10309 0 : int i;
10310 0 : PQExpBuffer query;
10311 0 : TSTemplateInfo *tmplinfo;
10312 0 : int i_tableoid;
10313 0 : int i_oid;
10314 0 : int i_tmplname;
10315 0 : int i_tmplnamespace;
10316 0 : int i_tmplinit;
10317 0 : int i_tmpllexize;
10318 :
10319 0 : query = createPQExpBuffer();
10320 :
10321 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10322 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10323 : "FROM pg_ts_template");
10324 :
10325 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10326 :
10327 0 : ntups = PQntuples(res);
10328 :
10329 0 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10330 :
10331 0 : i_tableoid = PQfnumber(res, "tableoid");
10332 0 : i_oid = PQfnumber(res, "oid");
10333 0 : i_tmplname = PQfnumber(res, "tmplname");
10334 0 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10335 0 : i_tmplinit = PQfnumber(res, "tmplinit");
10336 0 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10337 :
10338 0 : for (i = 0; i < ntups; i++)
10339 : {
10340 0 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10341 0 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10342 0 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10343 0 : AssignDumpId(&tmplinfo[i].dobj);
10344 0 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10345 0 : tmplinfo[i].dobj.namespace =
10346 0 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10347 0 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10348 0 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10349 :
10350 : /* Decide whether we want to dump it */
10351 0 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10352 0 : }
10353 :
10354 0 : PQclear(res);
10355 :
10356 0 : destroyPQExpBuffer(query);
10357 0 : }
10358 :
10359 : /*
10360 : * getTSConfigurations:
10361 : * get information about all text search configurations
10362 : */
10363 : void
10364 0 : getTSConfigurations(Archive *fout)
10365 : {
10366 0 : PGresult *res;
10367 0 : int ntups;
10368 0 : int i;
10369 0 : PQExpBuffer query;
10370 0 : TSConfigInfo *cfginfo;
10371 0 : int i_tableoid;
10372 0 : int i_oid;
10373 0 : int i_cfgname;
10374 0 : int i_cfgnamespace;
10375 0 : int i_cfgowner;
10376 0 : int i_cfgparser;
10377 :
10378 0 : query = createPQExpBuffer();
10379 :
10380 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10381 : "cfgnamespace, cfgowner, cfgparser "
10382 : "FROM pg_ts_config");
10383 :
10384 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10385 :
10386 0 : ntups = PQntuples(res);
10387 :
10388 0 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10389 :
10390 0 : i_tableoid = PQfnumber(res, "tableoid");
10391 0 : i_oid = PQfnumber(res, "oid");
10392 0 : i_cfgname = PQfnumber(res, "cfgname");
10393 0 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10394 0 : i_cfgowner = PQfnumber(res, "cfgowner");
10395 0 : i_cfgparser = PQfnumber(res, "cfgparser");
10396 :
10397 0 : for (i = 0; i < ntups; i++)
10398 : {
10399 0 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10400 0 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10401 0 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10402 0 : AssignDumpId(&cfginfo[i].dobj);
10403 0 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10404 0 : cfginfo[i].dobj.namespace =
10405 0 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10406 0 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10407 0 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10408 :
10409 : /* Decide whether we want to dump it */
10410 0 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10411 0 : }
10412 :
10413 0 : PQclear(res);
10414 :
10415 0 : destroyPQExpBuffer(query);
10416 0 : }
10417 :
10418 : /*
10419 : * getForeignDataWrappers:
10420 : * get information about all foreign-data wrappers in the system catalogs
10421 : */
10422 : void
10423 0 : getForeignDataWrappers(Archive *fout)
10424 : {
10425 0 : PGresult *res;
10426 0 : int ntups;
10427 0 : int i;
10428 0 : PQExpBuffer query;
10429 0 : FdwInfo *fdwinfo;
10430 0 : int i_tableoid;
10431 0 : int i_oid;
10432 0 : int i_fdwname;
10433 0 : int i_fdwowner;
10434 0 : int i_fdwhandler;
10435 0 : int i_fdwvalidator;
10436 0 : int i_fdwacl;
10437 0 : int i_acldefault;
10438 0 : int i_fdwoptions;
10439 :
10440 0 : query = createPQExpBuffer();
10441 :
10442 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10443 : "fdwowner, "
10444 : "fdwhandler::pg_catalog.regproc, "
10445 : "fdwvalidator::pg_catalog.regproc, "
10446 : "fdwacl, "
10447 : "acldefault('F', fdwowner) AS acldefault, "
10448 : "array_to_string(ARRAY("
10449 : "SELECT quote_ident(option_name) || ' ' || "
10450 : "quote_literal(option_value) "
10451 : "FROM pg_options_to_table(fdwoptions) "
10452 : "ORDER BY option_name"
10453 : "), E',\n ') AS fdwoptions "
10454 : "FROM pg_foreign_data_wrapper");
10455 :
10456 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10457 :
10458 0 : ntups = PQntuples(res);
10459 :
10460 0 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10461 :
10462 0 : i_tableoid = PQfnumber(res, "tableoid");
10463 0 : i_oid = PQfnumber(res, "oid");
10464 0 : i_fdwname = PQfnumber(res, "fdwname");
10465 0 : i_fdwowner = PQfnumber(res, "fdwowner");
10466 0 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10467 0 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10468 0 : i_fdwacl = PQfnumber(res, "fdwacl");
10469 0 : i_acldefault = PQfnumber(res, "acldefault");
10470 0 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10471 :
10472 0 : for (i = 0; i < ntups; i++)
10473 : {
10474 0 : fdwinfo[i].dobj.objType = DO_FDW;
10475 0 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10476 0 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10477 0 : AssignDumpId(&fdwinfo[i].dobj);
10478 0 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10479 0 : fdwinfo[i].dobj.namespace = NULL;
10480 0 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10481 0 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10482 0 : fdwinfo[i].dacl.privtype = 0;
10483 0 : fdwinfo[i].dacl.initprivs = NULL;
10484 0 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10485 0 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10486 0 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10487 0 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10488 :
10489 : /* Decide whether we want to dump it */
10490 0 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10491 :
10492 : /* Mark whether FDW has an ACL */
10493 0 : if (!PQgetisnull(res, i, i_fdwacl))
10494 0 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10495 0 : }
10496 :
10497 0 : PQclear(res);
10498 :
10499 0 : destroyPQExpBuffer(query);
10500 0 : }
10501 :
10502 : /*
10503 : * getForeignServers:
10504 : * get information about all foreign servers in the system catalogs
10505 : */
10506 : void
10507 0 : getForeignServers(Archive *fout)
10508 : {
10509 0 : PGresult *res;
10510 0 : int ntups;
10511 0 : int i;
10512 0 : PQExpBuffer query;
10513 0 : ForeignServerInfo *srvinfo;
10514 0 : int i_tableoid;
10515 0 : int i_oid;
10516 0 : int i_srvname;
10517 0 : int i_srvowner;
10518 0 : int i_srvfdw;
10519 0 : int i_srvtype;
10520 0 : int i_srvversion;
10521 0 : int i_srvacl;
10522 0 : int i_acldefault;
10523 0 : int i_srvoptions;
10524 :
10525 0 : query = createPQExpBuffer();
10526 :
10527 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10528 : "srvowner, "
10529 : "srvfdw, srvtype, srvversion, srvacl, "
10530 : "acldefault('S', srvowner) AS acldefault, "
10531 : "array_to_string(ARRAY("
10532 : "SELECT quote_ident(option_name) || ' ' || "
10533 : "quote_literal(option_value) "
10534 : "FROM pg_options_to_table(srvoptions) "
10535 : "ORDER BY option_name"
10536 : "), E',\n ') AS srvoptions "
10537 : "FROM pg_foreign_server");
10538 :
10539 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10540 :
10541 0 : ntups = PQntuples(res);
10542 :
10543 0 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10544 :
10545 0 : i_tableoid = PQfnumber(res, "tableoid");
10546 0 : i_oid = PQfnumber(res, "oid");
10547 0 : i_srvname = PQfnumber(res, "srvname");
10548 0 : i_srvowner = PQfnumber(res, "srvowner");
10549 0 : i_srvfdw = PQfnumber(res, "srvfdw");
10550 0 : i_srvtype = PQfnumber(res, "srvtype");
10551 0 : i_srvversion = PQfnumber(res, "srvversion");
10552 0 : i_srvacl = PQfnumber(res, "srvacl");
10553 0 : i_acldefault = PQfnumber(res, "acldefault");
10554 0 : i_srvoptions = PQfnumber(res, "srvoptions");
10555 :
10556 0 : for (i = 0; i < ntups; i++)
10557 : {
10558 0 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10559 0 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10560 0 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10561 0 : AssignDumpId(&srvinfo[i].dobj);
10562 0 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10563 0 : srvinfo[i].dobj.namespace = NULL;
10564 0 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10565 0 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10566 0 : srvinfo[i].dacl.privtype = 0;
10567 0 : srvinfo[i].dacl.initprivs = NULL;
10568 0 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10569 0 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10570 0 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10571 0 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10572 0 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10573 :
10574 : /* Decide whether we want to dump it */
10575 0 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10576 :
10577 : /* Servers have user mappings */
10578 0 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10579 :
10580 : /* Mark whether server has an ACL */
10581 0 : if (!PQgetisnull(res, i, i_srvacl))
10582 0 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10583 0 : }
10584 :
10585 0 : PQclear(res);
10586 :
10587 0 : destroyPQExpBuffer(query);
10588 0 : }
10589 :
10590 : /*
10591 : * getDefaultACLs:
10592 : * get information about all default ACL information in the system catalogs
10593 : */
10594 : void
10595 0 : getDefaultACLs(Archive *fout)
10596 : {
10597 0 : DumpOptions *dopt = fout->dopt;
10598 0 : DefaultACLInfo *daclinfo;
10599 0 : PQExpBuffer query;
10600 0 : PGresult *res;
10601 0 : int i_oid;
10602 0 : int i_tableoid;
10603 0 : int i_defaclrole;
10604 0 : int i_defaclnamespace;
10605 0 : int i_defaclobjtype;
10606 0 : int i_defaclacl;
10607 0 : int i_acldefault;
10608 0 : int i,
10609 : ntups;
10610 :
10611 0 : query = createPQExpBuffer();
10612 :
10613 : /*
10614 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10615 : * ACL for their object type. We should dump them as deltas from the
10616 : * default ACL, since that will be used as a starting point for
10617 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10618 : * non-global entries can only add privileges not revoke them. We must
10619 : * dump those as-is (i.e., as deltas from an empty ACL).
10620 : *
10621 : * We can use defaclobjtype as the object type for acldefault(), except
10622 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10623 : * 's'.
10624 : */
10625 0 : appendPQExpBufferStr(query,
10626 : "SELECT oid, tableoid, "
10627 : "defaclrole, "
10628 : "defaclnamespace, "
10629 : "defaclobjtype, "
10630 : "defaclacl, "
10631 : "CASE WHEN defaclnamespace = 0 THEN "
10632 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10633 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10634 : "defaclrole) ELSE '{}' END AS acldefault "
10635 : "FROM pg_default_acl");
10636 :
10637 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10638 :
10639 0 : ntups = PQntuples(res);
10640 :
10641 0 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10642 :
10643 0 : i_oid = PQfnumber(res, "oid");
10644 0 : i_tableoid = PQfnumber(res, "tableoid");
10645 0 : i_defaclrole = PQfnumber(res, "defaclrole");
10646 0 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10647 0 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10648 0 : i_defaclacl = PQfnumber(res, "defaclacl");
10649 0 : i_acldefault = PQfnumber(res, "acldefault");
10650 :
10651 0 : for (i = 0; i < ntups; i++)
10652 : {
10653 0 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10654 :
10655 0 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10656 0 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10657 0 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10658 0 : AssignDumpId(&daclinfo[i].dobj);
10659 : /* cheesy ... is it worth coming up with a better object name? */
10660 0 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10661 :
10662 0 : if (nspid != InvalidOid)
10663 0 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10664 : else
10665 0 : daclinfo[i].dobj.namespace = NULL;
10666 :
10667 0 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10668 0 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10669 0 : daclinfo[i].dacl.privtype = 0;
10670 0 : daclinfo[i].dacl.initprivs = NULL;
10671 0 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10672 0 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10673 :
10674 : /* Default ACLs are ACLs, of course */
10675 0 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10676 :
10677 : /* Decide whether we want to dump it */
10678 0 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10679 0 : }
10680 :
10681 0 : PQclear(res);
10682 :
10683 0 : destroyPQExpBuffer(query);
10684 0 : }
10685 :
10686 : /*
10687 : * getRoleName -- look up the name of a role, given its OID
10688 : *
10689 : * In current usage, we don't expect failures, so error out for a bad OID.
10690 : */
10691 : static const char *
10692 0 : getRoleName(const char *roleoid_str)
10693 : {
10694 0 : Oid roleoid = atooid(roleoid_str);
10695 :
10696 : /*
10697 : * Do binary search to find the appropriate item.
10698 : */
10699 0 : if (nrolenames > 0)
10700 : {
10701 0 : RoleNameItem *low = &rolenames[0];
10702 0 : RoleNameItem *high = &rolenames[nrolenames - 1];
10703 :
10704 0 : while (low <= high)
10705 : {
10706 0 : RoleNameItem *middle = low + (high - low) / 2;
10707 :
10708 0 : if (roleoid < middle->roleoid)
10709 0 : high = middle - 1;
10710 0 : else if (roleoid > middle->roleoid)
10711 0 : low = middle + 1;
10712 : else
10713 0 : return middle->rolename; /* found a match */
10714 0 : }
10715 0 : }
10716 :
10717 0 : pg_fatal("role with OID %u does not exist", roleoid);
10718 0 : return NULL; /* keep compiler quiet */
10719 0 : }
10720 :
10721 : /*
10722 : * collectRoleNames --
10723 : *
10724 : * Construct a table of all known roles.
10725 : * The table is sorted by OID for speed in lookup.
10726 : */
10727 : static void
10728 0 : collectRoleNames(Archive *fout)
10729 : {
10730 0 : PGresult *res;
10731 0 : const char *query;
10732 0 : int i;
10733 :
10734 0 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10735 :
10736 0 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10737 :
10738 0 : nrolenames = PQntuples(res);
10739 :
10740 0 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10741 :
10742 0 : for (i = 0; i < nrolenames; i++)
10743 : {
10744 0 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10745 0 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10746 0 : }
10747 :
10748 0 : PQclear(res);
10749 0 : }
10750 :
10751 : /*
10752 : * getAdditionalACLs
10753 : *
10754 : * We have now created all the DumpableObjects, and collected the ACL data
10755 : * that appears in the directly-associated catalog entries. However, there's
10756 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10757 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10758 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10759 : * Also, in versions having the pg_init_privs catalog, read that and load the
10760 : * information into the relevant DumpableObjects.
10761 : */
10762 : static void
10763 0 : getAdditionalACLs(Archive *fout)
10764 : {
10765 0 : PQExpBuffer query = createPQExpBuffer();
10766 0 : PGresult *res;
10767 0 : int ntups,
10768 : i;
10769 :
10770 : /* Check for per-column ACLs */
10771 0 : appendPQExpBufferStr(query,
10772 : "SELECT DISTINCT attrelid FROM pg_attribute "
10773 : "WHERE attacl IS NOT NULL");
10774 :
10775 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10776 :
10777 0 : ntups = PQntuples(res);
10778 0 : for (i = 0; i < ntups; i++)
10779 : {
10780 0 : Oid relid = atooid(PQgetvalue(res, i, 0));
10781 0 : TableInfo *tblinfo;
10782 :
10783 0 : tblinfo = findTableByOid(relid);
10784 : /* OK to ignore tables we haven't got a DumpableObject for */
10785 0 : if (tblinfo)
10786 : {
10787 0 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10788 0 : tblinfo->hascolumnACLs = true;
10789 0 : }
10790 0 : }
10791 0 : PQclear(res);
10792 :
10793 : /* Fetch initial-privileges data */
10794 0 : if (fout->remoteVersion >= 90600)
10795 : {
10796 0 : printfPQExpBuffer(query,
10797 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10798 : "FROM pg_init_privs");
10799 :
10800 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10801 :
10802 0 : ntups = PQntuples(res);
10803 0 : for (i = 0; i < ntups; i++)
10804 : {
10805 0 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10806 0 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10807 0 : int objsubid = atoi(PQgetvalue(res, i, 2));
10808 0 : char privtype = *(PQgetvalue(res, i, 3));
10809 0 : char *initprivs = PQgetvalue(res, i, 4);
10810 0 : CatalogId objId;
10811 0 : DumpableObject *dobj;
10812 :
10813 0 : objId.tableoid = classoid;
10814 0 : objId.oid = objoid;
10815 0 : dobj = findObjectByCatalogId(objId);
10816 : /* OK to ignore entries we haven't got a DumpableObject for */
10817 0 : if (dobj)
10818 : {
10819 : /* Cope with sub-object initprivs */
10820 0 : if (objsubid != 0)
10821 : {
10822 0 : if (dobj->objType == DO_TABLE)
10823 : {
10824 : /* For a column initprivs, set the table's ACL flags */
10825 0 : dobj->components |= DUMP_COMPONENT_ACL;
10826 0 : ((TableInfo *) dobj)->hascolumnACLs = true;
10827 0 : }
10828 : else
10829 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10830 : classoid, objoid, objsubid);
10831 0 : continue;
10832 : }
10833 :
10834 : /*
10835 : * We ignore any pg_init_privs.initprivs entry for the public
10836 : * schema, as explained in getNamespaces().
10837 : */
10838 0 : if (dobj->objType == DO_NAMESPACE &&
10839 0 : strcmp(dobj->name, "public") == 0)
10840 0 : continue;
10841 :
10842 : /* Else it had better be of a type we think has ACLs */
10843 0 : if (dobj->objType == DO_NAMESPACE ||
10844 0 : dobj->objType == DO_TYPE ||
10845 0 : dobj->objType == DO_FUNC ||
10846 0 : dobj->objType == DO_AGG ||
10847 0 : dobj->objType == DO_TABLE ||
10848 0 : dobj->objType == DO_PROCLANG ||
10849 0 : dobj->objType == DO_FDW ||
10850 0 : dobj->objType == DO_FOREIGN_SERVER)
10851 : {
10852 0 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10853 :
10854 0 : daobj->dacl.privtype = privtype;
10855 0 : daobj->dacl.initprivs = pstrdup(initprivs);
10856 0 : }
10857 : else
10858 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10859 : classoid, objoid, objsubid);
10860 0 : }
10861 0 : }
10862 0 : PQclear(res);
10863 0 : }
10864 :
10865 0 : destroyPQExpBuffer(query);
10866 0 : }
10867 :
10868 : /*
10869 : * dumpCommentExtended --
10870 : *
10871 : * This routine is used to dump any comments associated with the
10872 : * object handed to this routine. The routine takes the object type
10873 : * and object name (ready to print, except for schema decoration), plus
10874 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10875 : * plus catalog ID and subid which are the lookup key for pg_description,
10876 : * plus the dump ID for the object (for setting a dependency).
10877 : * If a matching pg_description entry is found, it is dumped.
10878 : *
10879 : * Note: in some cases, such as comments for triggers and rules, the "type"
10880 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10881 : * but it doesn't seem worth complicating the API for all callers to make
10882 : * it cleaner.
10883 : *
10884 : * Note: although this routine takes a dumpId for dependency purposes,
10885 : * that purpose is just to mark the dependency in the emitted dump file
10886 : * for possible future use by pg_restore. We do NOT use it for determining
10887 : * ordering of the comment in the dump file, because this routine is called
10888 : * after dependency sorting occurs. This routine should be called just after
10889 : * calling ArchiveEntry() for the specified object.
10890 : */
10891 : static void
10892 0 : dumpCommentExtended(Archive *fout, const char *type,
10893 : const char *name, const char *namespace,
10894 : const char *owner, CatalogId catalogId,
10895 : int subid, DumpId dumpId,
10896 : const char *initdb_comment)
10897 : {
10898 0 : DumpOptions *dopt = fout->dopt;
10899 0 : CommentItem *comments;
10900 0 : int ncomments;
10901 :
10902 : /* do nothing, if --no-comments is supplied */
10903 0 : if (dopt->no_comments)
10904 0 : return;
10905 :
10906 : /* Comments are schema not data ... except LO comments are data */
10907 0 : if (strcmp(type, "LARGE OBJECT") != 0)
10908 : {
10909 0 : if (!dopt->dumpSchema)
10910 0 : return;
10911 0 : }
10912 : else
10913 : {
10914 : /* We do dump LO comments in binary-upgrade mode */
10915 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
10916 0 : return;
10917 : }
10918 :
10919 : /* Search for comments associated with catalogId, using table */
10920 0 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10921 : &comments);
10922 :
10923 : /* Is there one matching the subid? */
10924 0 : while (ncomments > 0)
10925 : {
10926 0 : if (comments->objsubid == subid)
10927 0 : break;
10928 0 : comments++;
10929 0 : ncomments--;
10930 : }
10931 :
10932 0 : if (initdb_comment != NULL)
10933 : {
10934 : static CommentItem empty_comment = {.descr = ""};
10935 :
10936 : /*
10937 : * initdb creates this object with a comment. Skip dumping the
10938 : * initdb-provided comment, which would complicate matters for
10939 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10940 : * comment, replicate that.
10941 : */
10942 0 : if (ncomments == 0)
10943 : {
10944 0 : comments = &empty_comment;
10945 0 : ncomments = 1;
10946 0 : }
10947 0 : else if (strcmp(comments->descr, initdb_comment) == 0)
10948 0 : ncomments = 0;
10949 0 : }
10950 :
10951 : /* If a comment exists, build COMMENT ON statement */
10952 0 : if (ncomments > 0)
10953 : {
10954 0 : PQExpBuffer query = createPQExpBuffer();
10955 0 : PQExpBuffer tag = createPQExpBuffer();
10956 :
10957 0 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10958 0 : if (namespace && *namespace)
10959 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10960 0 : appendPQExpBuffer(query, "%s IS ", name);
10961 0 : appendStringLiteralAH(query, comments->descr, fout);
10962 0 : appendPQExpBufferStr(query, ";\n");
10963 :
10964 0 : appendPQExpBuffer(tag, "%s %s", type, name);
10965 :
10966 : /*
10967 : * We mark comments as SECTION_NONE because they really belong in the
10968 : * same section as their parent, whether that is pre-data or
10969 : * post-data.
10970 : */
10971 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10972 0 : ARCHIVE_OPTS(.tag = tag->data,
10973 : .namespace = namespace,
10974 : .owner = owner,
10975 : .description = "COMMENT",
10976 : .section = SECTION_NONE,
10977 : .createStmt = query->data,
10978 : .deps = &dumpId,
10979 : .nDeps = 1));
10980 :
10981 0 : destroyPQExpBuffer(query);
10982 0 : destroyPQExpBuffer(tag);
10983 0 : }
10984 0 : }
10985 :
10986 : /*
10987 : * dumpComment --
10988 : *
10989 : * Typical simplification of the above function.
10990 : */
10991 : static inline void
10992 0 : dumpComment(Archive *fout, const char *type,
10993 : const char *name, const char *namespace,
10994 : const char *owner, CatalogId catalogId,
10995 : int subid, DumpId dumpId)
10996 : {
10997 0 : dumpCommentExtended(fout, type, name, namespace, owner,
10998 0 : catalogId, subid, dumpId, NULL);
10999 0 : }
11000 :
11001 : /*
11002 : * appendNamedArgument --
11003 : *
11004 : * Convenience routine for constructing parameters of the form:
11005 : * 'paraname', 'value'::type
11006 : */
11007 : static void
11008 0 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
11009 : const char *argtype, const char *argval)
11010 : {
11011 0 : appendPQExpBufferStr(out, ",\n\t");
11012 :
11013 0 : appendStringLiteralAH(out, argname, fout);
11014 0 : appendPQExpBufferStr(out, ", ");
11015 :
11016 0 : appendStringLiteralAH(out, argval, fout);
11017 0 : appendPQExpBuffer(out, "::%s", argtype);
11018 0 : }
11019 :
11020 : /*
11021 : * fetchAttributeStats --
11022 : *
11023 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11024 : */
11025 : static PGresult *
11026 0 : fetchAttributeStats(Archive *fout)
11027 : {
11028 0 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11029 0 : PQExpBuffer nspnames = createPQExpBuffer();
11030 0 : PQExpBuffer relnames = createPQExpBuffer();
11031 0 : int count = 0;
11032 0 : PGresult *res = NULL;
11033 : static TocEntry *te;
11034 : static bool restarted;
11035 0 : int max_rels = MAX_ATTR_STATS_RELS;
11036 :
11037 : /*
11038 : * Our query for retrieving statistics for multiple relations uses WITH
11039 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11040 : * in v9.4. For older versions, we resort to gathering statistics for a
11041 : * single relation at a time.
11042 : */
11043 0 : if (fout->remoteVersion < 90400)
11044 0 : max_rels = 1;
11045 :
11046 : /* If we're just starting, set our TOC pointer. */
11047 0 : if (!te)
11048 0 : te = AH->toc->next;
11049 :
11050 : /*
11051 : * We can't easily avoid a second TOC scan for the tar format because it
11052 : * writes restore.sql separately, which means we must execute the queries
11053 : * twice. This feels risky, but there is no known reason it should
11054 : * generate different output than the first pass. Even if it does, the
11055 : * worst-case scenario is that restore.sql might have different statistics
11056 : * data than the archive.
11057 : */
11058 0 : if (!restarted && te == AH->toc && AH->format == archTar)
11059 : {
11060 0 : te = AH->toc->next;
11061 0 : restarted = true;
11062 0 : }
11063 :
11064 0 : appendPQExpBufferChar(nspnames, '{');
11065 0 : appendPQExpBufferChar(relnames, '{');
11066 :
11067 : /*
11068 : * Scan the TOC for the next set of relevant stats entries. We assume
11069 : * that statistics are dumped in the order they are listed in the TOC.
11070 : * This is perhaps not the sturdiest assumption, so we verify it matches
11071 : * reality in dumpRelationStats_dumper().
11072 : */
11073 0 : for (; te != AH->toc && count < max_rels; te = te->next)
11074 : {
11075 0 : if ((te->reqs & REQ_STATS) != 0 &&
11076 0 : strcmp(te->desc, "STATISTICS DATA") == 0)
11077 : {
11078 0 : appendPGArray(nspnames, te->namespace);
11079 0 : appendPGArray(relnames, te->tag);
11080 0 : count++;
11081 0 : }
11082 0 : }
11083 :
11084 0 : appendPQExpBufferChar(nspnames, '}');
11085 0 : appendPQExpBufferChar(relnames, '}');
11086 :
11087 : /* Execute the query for the next batch of relations. */
11088 0 : if (count > 0)
11089 : {
11090 0 : PQExpBuffer query = createPQExpBuffer();
11091 :
11092 0 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11093 0 : appendStringLiteralAH(query, nspnames->data, fout);
11094 0 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11095 0 : appendStringLiteralAH(query, relnames->data, fout);
11096 0 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11097 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11098 0 : destroyPQExpBuffer(query);
11099 0 : }
11100 :
11101 0 : destroyPQExpBuffer(nspnames);
11102 0 : destroyPQExpBuffer(relnames);
11103 0 : return res;
11104 0 : }
11105 :
11106 : /*
11107 : * dumpRelationStats_dumper --
11108 : *
11109 : * Generate command to import stats into the relation on the new database.
11110 : * This routine is called by the Archiver when it wants the statistics to be
11111 : * dumped.
11112 : */
11113 : static char *
11114 0 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11115 : {
11116 0 : const RelStatsInfo *rsinfo = userArg;
11117 : static PGresult *res;
11118 : static int rownum;
11119 0 : PQExpBuffer query;
11120 0 : PQExpBufferData out_data;
11121 0 : PQExpBuffer out = &out_data;
11122 0 : int i_schemaname;
11123 0 : int i_tablename;
11124 0 : int i_attname;
11125 0 : int i_inherited;
11126 0 : int i_null_frac;
11127 0 : int i_avg_width;
11128 0 : int i_n_distinct;
11129 0 : int i_most_common_vals;
11130 0 : int i_most_common_freqs;
11131 0 : int i_histogram_bounds;
11132 0 : int i_correlation;
11133 0 : int i_most_common_elems;
11134 0 : int i_most_common_elem_freqs;
11135 0 : int i_elem_count_histogram;
11136 0 : int i_range_length_histogram;
11137 0 : int i_range_empty_frac;
11138 0 : int i_range_bounds_histogram;
11139 : static TocEntry *expected_te;
11140 :
11141 : /*
11142 : * fetchAttributeStats() assumes that the statistics are dumped in the
11143 : * order they are listed in the TOC. We verify that here for safety.
11144 : */
11145 0 : if (!expected_te)
11146 0 : expected_te = ((ArchiveHandle *) fout)->toc;
11147 :
11148 0 : expected_te = expected_te->next;
11149 0 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11150 0 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11151 0 : expected_te = expected_te->next;
11152 :
11153 0 : if (te != expected_te)
11154 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11155 : te->dumpId, te->desc, te->tag,
11156 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11157 :
11158 0 : query = createPQExpBuffer();
11159 0 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11160 : {
11161 0 : appendPQExpBufferStr(query,
11162 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11163 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11164 : "s.null_frac, s.avg_width, s.n_distinct, "
11165 : "s.most_common_vals, s.most_common_freqs, "
11166 : "s.histogram_bounds, s.correlation, "
11167 : "s.most_common_elems, s.most_common_elem_freqs, "
11168 : "s.elem_count_histogram, ");
11169 :
11170 0 : if (fout->remoteVersion >= 170000)
11171 0 : appendPQExpBufferStr(query,
11172 : "s.range_length_histogram, "
11173 : "s.range_empty_frac, "
11174 : "s.range_bounds_histogram ");
11175 : else
11176 0 : appendPQExpBufferStr(query,
11177 : "NULL AS range_length_histogram,"
11178 : "NULL AS range_empty_frac,"
11179 : "NULL AS range_bounds_histogram ");
11180 :
11181 : /*
11182 : * The results must be in the order of the relations supplied in the
11183 : * parameters to ensure we remain in sync as we walk through the TOC.
11184 : * The redundant filter clause on s.tablename = ANY(...) seems
11185 : * sufficient to convince the planner to use
11186 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11187 : * This may not work for all versions.
11188 : *
11189 : * Our query for retrieving statistics for multiple relations uses
11190 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11191 : * introduced in v9.4. For older versions, we resort to gathering
11192 : * statistics for a single relation at a time.
11193 : */
11194 0 : if (fout->remoteVersion >= 90400)
11195 0 : appendPQExpBufferStr(query,
11196 : "FROM pg_catalog.pg_stats s "
11197 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11198 : "ON s.schemaname = u.schemaname "
11199 : "AND s.tablename = u.tablename "
11200 : "WHERE s.tablename = ANY($2) "
11201 : "ORDER BY u.ord, s.attname, s.inherited");
11202 : else
11203 0 : appendPQExpBufferStr(query,
11204 : "FROM pg_catalog.pg_stats s "
11205 : "WHERE s.schemaname = $1[1] "
11206 : "AND s.tablename = $2[1] "
11207 : "ORDER BY s.attname, s.inherited");
11208 :
11209 0 : ExecuteSqlStatement(fout, query->data);
11210 :
11211 0 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11212 0 : resetPQExpBuffer(query);
11213 0 : }
11214 :
11215 0 : initPQExpBuffer(out);
11216 :
11217 : /* restore relation stats */
11218 0 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11219 0 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11220 0 : fout->remoteVersion);
11221 0 : appendPQExpBufferStr(out, "\t'schemaname', ");
11222 0 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11223 0 : appendPQExpBufferStr(out, ",\n");
11224 0 : appendPQExpBufferStr(out, "\t'relname', ");
11225 0 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11226 0 : appendPQExpBufferStr(out, ",\n");
11227 0 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11228 :
11229 : /*
11230 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11231 : * the relation is empty, or it could mean that it hadn't yet been
11232 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11233 : * This ambiguity allegedly can cause the planner to choose inefficient
11234 : * plans after restoring to v18 or newer. To deal with this, let's just
11235 : * set reltuples to -1 in that case.
11236 : */
11237 0 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11238 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11239 : else
11240 0 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11241 :
11242 0 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11243 0 : rsinfo->relallvisible);
11244 :
11245 0 : if (fout->remoteVersion >= 180000)
11246 0 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11247 :
11248 0 : appendPQExpBufferStr(out, "\n);\n");
11249 :
11250 : /* Fetch the next batch of attribute statistics if needed. */
11251 0 : if (rownum >= PQntuples(res))
11252 : {
11253 0 : PQclear(res);
11254 0 : res = fetchAttributeStats(fout);
11255 0 : rownum = 0;
11256 0 : }
11257 :
11258 0 : i_schemaname = PQfnumber(res, "schemaname");
11259 0 : i_tablename = PQfnumber(res, "tablename");
11260 0 : i_attname = PQfnumber(res, "attname");
11261 0 : i_inherited = PQfnumber(res, "inherited");
11262 0 : i_null_frac = PQfnumber(res, "null_frac");
11263 0 : i_avg_width = PQfnumber(res, "avg_width");
11264 0 : i_n_distinct = PQfnumber(res, "n_distinct");
11265 0 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11266 0 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11267 0 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11268 0 : i_correlation = PQfnumber(res, "correlation");
11269 0 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11270 0 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11271 0 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11272 0 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11273 0 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11274 0 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11275 :
11276 : /* restore attribute stats */
11277 0 : for (; rownum < PQntuples(res); rownum++)
11278 : {
11279 0 : const char *attname;
11280 :
11281 : /* Stop if the next stat row in our cache isn't for this relation. */
11282 0 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11283 0 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11284 0 : break;
11285 :
11286 0 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11287 0 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11288 0 : fout->remoteVersion);
11289 0 : appendPQExpBufferStr(out, "\t'schemaname', ");
11290 0 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11291 0 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11292 0 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11293 :
11294 0 : if (PQgetisnull(res, rownum, i_attname))
11295 0 : pg_fatal("unexpected null attname");
11296 0 : attname = PQgetvalue(res, rownum, i_attname);
11297 :
11298 : /*
11299 : * Indexes look up attname in indAttNames to derive attnum, all others
11300 : * use attname directly. We must specify attnum for indexes, since
11301 : * their attnames are not necessarily stable across dump/reload.
11302 : */
11303 0 : if (rsinfo->nindAttNames == 0)
11304 : {
11305 0 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11306 0 : appendStringLiteralAH(out, attname, fout);
11307 0 : }
11308 : else
11309 : {
11310 0 : bool found = false;
11311 :
11312 0 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11313 : {
11314 0 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11315 : {
11316 0 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11317 0 : i + 1);
11318 0 : found = true;
11319 0 : break;
11320 : }
11321 0 : }
11322 :
11323 0 : if (!found)
11324 0 : pg_fatal("could not find index attname \"%s\"", attname);
11325 0 : }
11326 :
11327 0 : if (!PQgetisnull(res, rownum, i_inherited))
11328 0 : appendNamedArgument(out, fout, "inherited", "boolean",
11329 0 : PQgetvalue(res, rownum, i_inherited));
11330 0 : if (!PQgetisnull(res, rownum, i_null_frac))
11331 0 : appendNamedArgument(out, fout, "null_frac", "real",
11332 0 : PQgetvalue(res, rownum, i_null_frac));
11333 0 : if (!PQgetisnull(res, rownum, i_avg_width))
11334 0 : appendNamedArgument(out, fout, "avg_width", "integer",
11335 0 : PQgetvalue(res, rownum, i_avg_width));
11336 0 : if (!PQgetisnull(res, rownum, i_n_distinct))
11337 0 : appendNamedArgument(out, fout, "n_distinct", "real",
11338 0 : PQgetvalue(res, rownum, i_n_distinct));
11339 0 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11340 0 : appendNamedArgument(out, fout, "most_common_vals", "text",
11341 0 : PQgetvalue(res, rownum, i_most_common_vals));
11342 0 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11343 0 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11344 0 : PQgetvalue(res, rownum, i_most_common_freqs));
11345 0 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11346 0 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11347 0 : PQgetvalue(res, rownum, i_histogram_bounds));
11348 0 : if (!PQgetisnull(res, rownum, i_correlation))
11349 0 : appendNamedArgument(out, fout, "correlation", "real",
11350 0 : PQgetvalue(res, rownum, i_correlation));
11351 0 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11352 0 : appendNamedArgument(out, fout, "most_common_elems", "text",
11353 0 : PQgetvalue(res, rownum, i_most_common_elems));
11354 0 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11355 0 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11356 0 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11357 0 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11358 0 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11359 0 : PQgetvalue(res, rownum, i_elem_count_histogram));
11360 0 : if (fout->remoteVersion >= 170000)
11361 : {
11362 0 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11363 0 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11364 0 : PQgetvalue(res, rownum, i_range_length_histogram));
11365 0 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11366 0 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11367 0 : PQgetvalue(res, rownum, i_range_empty_frac));
11368 0 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11369 0 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11370 0 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11371 0 : }
11372 0 : appendPQExpBufferStr(out, "\n);\n");
11373 0 : }
11374 :
11375 0 : destroyPQExpBuffer(query);
11376 0 : return out->data;
11377 0 : }
11378 :
11379 : /*
11380 : * dumpRelationStats --
11381 : *
11382 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11383 : * care of gathering the statistics and generating the restore commands when
11384 : * they are needed.
11385 : */
11386 : static void
11387 0 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11388 : {
11389 0 : const DumpableObject *dobj = &rsinfo->dobj;
11390 :
11391 : /* nothing to do if we are not dumping statistics */
11392 0 : if (!fout->dopt->dumpStatistics)
11393 0 : return;
11394 :
11395 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11396 0 : ARCHIVE_OPTS(.tag = dobj->name,
11397 : .namespace = dobj->namespace->dobj.name,
11398 : .description = "STATISTICS DATA",
11399 : .section = rsinfo->section,
11400 : .defnFn = dumpRelationStats_dumper,
11401 : .defnArg = rsinfo,
11402 : .deps = dobj->dependencies,
11403 : .nDeps = dobj->nDeps));
11404 0 : }
11405 :
11406 : /*
11407 : * dumpTableComment --
11408 : *
11409 : * As above, but dump comments for both the specified table (or view)
11410 : * and its columns.
11411 : */
11412 : static void
11413 0 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11414 : const char *reltypename)
11415 : {
11416 0 : DumpOptions *dopt = fout->dopt;
11417 0 : CommentItem *comments;
11418 0 : int ncomments;
11419 0 : PQExpBuffer query;
11420 0 : PQExpBuffer tag;
11421 :
11422 : /* do nothing, if --no-comments is supplied */
11423 0 : if (dopt->no_comments)
11424 0 : return;
11425 :
11426 : /* Comments are SCHEMA not data */
11427 0 : if (!dopt->dumpSchema)
11428 0 : return;
11429 :
11430 : /* Search for comments associated with relation, using table */
11431 0 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11432 0 : tbinfo->dobj.catId.oid,
11433 : &comments);
11434 :
11435 : /* If comments exist, build COMMENT ON statements */
11436 0 : if (ncomments <= 0)
11437 0 : return;
11438 :
11439 0 : query = createPQExpBuffer();
11440 0 : tag = createPQExpBuffer();
11441 :
11442 0 : while (ncomments > 0)
11443 : {
11444 0 : const char *descr = comments->descr;
11445 0 : int objsubid = comments->objsubid;
11446 :
11447 0 : if (objsubid == 0)
11448 : {
11449 0 : resetPQExpBuffer(tag);
11450 0 : appendPQExpBuffer(tag, "%s %s", reltypename,
11451 0 : fmtId(tbinfo->dobj.name));
11452 :
11453 0 : resetPQExpBuffer(query);
11454 0 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11455 0 : fmtQualifiedDumpable(tbinfo));
11456 0 : appendStringLiteralAH(query, descr, fout);
11457 0 : appendPQExpBufferStr(query, ";\n");
11458 :
11459 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11460 0 : ARCHIVE_OPTS(.tag = tag->data,
11461 : .namespace = tbinfo->dobj.namespace->dobj.name,
11462 : .owner = tbinfo->rolname,
11463 : .description = "COMMENT",
11464 : .section = SECTION_NONE,
11465 : .createStmt = query->data,
11466 : .deps = &(tbinfo->dobj.dumpId),
11467 : .nDeps = 1));
11468 0 : }
11469 0 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11470 : {
11471 0 : resetPQExpBuffer(tag);
11472 0 : appendPQExpBuffer(tag, "COLUMN %s.",
11473 0 : fmtId(tbinfo->dobj.name));
11474 0 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11475 :
11476 0 : resetPQExpBuffer(query);
11477 0 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11478 0 : fmtQualifiedDumpable(tbinfo));
11479 0 : appendPQExpBuffer(query, "%s IS ",
11480 0 : fmtId(tbinfo->attnames[objsubid - 1]));
11481 0 : appendStringLiteralAH(query, descr, fout);
11482 0 : appendPQExpBufferStr(query, ";\n");
11483 :
11484 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11485 0 : ARCHIVE_OPTS(.tag = tag->data,
11486 : .namespace = tbinfo->dobj.namespace->dobj.name,
11487 : .owner = tbinfo->rolname,
11488 : .description = "COMMENT",
11489 : .section = SECTION_NONE,
11490 : .createStmt = query->data,
11491 : .deps = &(tbinfo->dobj.dumpId),
11492 : .nDeps = 1));
11493 0 : }
11494 :
11495 0 : comments++;
11496 0 : ncomments--;
11497 0 : }
11498 :
11499 0 : destroyPQExpBuffer(query);
11500 0 : destroyPQExpBuffer(tag);
11501 0 : }
11502 :
11503 : /*
11504 : * findComments --
11505 : *
11506 : * Find the comment(s), if any, associated with the given object. All the
11507 : * objsubid values associated with the given classoid/objoid are found with
11508 : * one search.
11509 : */
11510 : static int
11511 0 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11512 : {
11513 0 : CommentItem *middle = NULL;
11514 0 : CommentItem *low;
11515 0 : CommentItem *high;
11516 0 : int nmatch;
11517 :
11518 : /*
11519 : * Do binary search to find some item matching the object.
11520 : */
11521 0 : low = &comments[0];
11522 0 : high = &comments[ncomments - 1];
11523 0 : while (low <= high)
11524 : {
11525 0 : middle = low + (high - low) / 2;
11526 :
11527 0 : if (classoid < middle->classoid)
11528 0 : high = middle - 1;
11529 0 : else if (classoid > middle->classoid)
11530 0 : low = middle + 1;
11531 0 : else if (objoid < middle->objoid)
11532 0 : high = middle - 1;
11533 0 : else if (objoid > middle->objoid)
11534 0 : low = middle + 1;
11535 : else
11536 0 : break; /* found a match */
11537 : }
11538 :
11539 0 : if (low > high) /* no matches */
11540 : {
11541 0 : *items = NULL;
11542 0 : return 0;
11543 : }
11544 :
11545 : /*
11546 : * Now determine how many items match the object. The search loop
11547 : * invariant still holds: only items between low and high inclusive could
11548 : * match.
11549 : */
11550 0 : nmatch = 1;
11551 0 : while (middle > low)
11552 : {
11553 0 : if (classoid != middle[-1].classoid ||
11554 0 : objoid != middle[-1].objoid)
11555 0 : break;
11556 0 : middle--;
11557 0 : nmatch++;
11558 : }
11559 :
11560 0 : *items = middle;
11561 :
11562 0 : middle += nmatch;
11563 0 : while (middle <= high)
11564 : {
11565 0 : if (classoid != middle->classoid ||
11566 0 : objoid != middle->objoid)
11567 0 : break;
11568 0 : middle++;
11569 0 : nmatch++;
11570 : }
11571 :
11572 0 : return nmatch;
11573 0 : }
11574 :
11575 : /*
11576 : * collectComments --
11577 : *
11578 : * Construct a table of all comments available for database objects;
11579 : * also set the has-comment component flag for each relevant object.
11580 : *
11581 : * We used to do per-object queries for the comments, but it's much faster
11582 : * to pull them all over at once, and on most databases the memory cost
11583 : * isn't high.
11584 : *
11585 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11586 : */
11587 : static void
11588 0 : collectComments(Archive *fout)
11589 : {
11590 0 : PGresult *res;
11591 0 : PQExpBuffer query;
11592 0 : int i_description;
11593 0 : int i_classoid;
11594 0 : int i_objoid;
11595 0 : int i_objsubid;
11596 0 : int ntups;
11597 0 : int i;
11598 0 : DumpableObject *dobj;
11599 :
11600 0 : query = createPQExpBuffer();
11601 :
11602 0 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11603 : "FROM pg_catalog.pg_description "
11604 : "ORDER BY classoid, objoid, objsubid");
11605 :
11606 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11607 :
11608 : /* Construct lookup table containing OIDs in numeric form */
11609 :
11610 0 : i_description = PQfnumber(res, "description");
11611 0 : i_classoid = PQfnumber(res, "classoid");
11612 0 : i_objoid = PQfnumber(res, "objoid");
11613 0 : i_objsubid = PQfnumber(res, "objsubid");
11614 :
11615 0 : ntups = PQntuples(res);
11616 :
11617 0 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11618 0 : ncomments = 0;
11619 0 : dobj = NULL;
11620 :
11621 0 : for (i = 0; i < ntups; i++)
11622 : {
11623 0 : CatalogId objId;
11624 0 : int subid;
11625 :
11626 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11627 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11628 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11629 :
11630 : /* We needn't remember comments that don't match any dumpable object */
11631 0 : if (dobj == NULL ||
11632 0 : dobj->catId.tableoid != objId.tableoid ||
11633 0 : dobj->catId.oid != objId.oid)
11634 0 : dobj = findObjectByCatalogId(objId);
11635 0 : if (dobj == NULL)
11636 0 : continue;
11637 :
11638 : /*
11639 : * Comments on columns of composite types are linked to the type's
11640 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11641 : * in the type's own DumpableObject.
11642 : */
11643 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
11644 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11645 : {
11646 0 : TypeInfo *cTypeInfo;
11647 :
11648 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11649 0 : if (cTypeInfo)
11650 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11651 0 : }
11652 : else
11653 0 : dobj->components |= DUMP_COMPONENT_COMMENT;
11654 :
11655 0 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11656 0 : comments[ncomments].classoid = objId.tableoid;
11657 0 : comments[ncomments].objoid = objId.oid;
11658 0 : comments[ncomments].objsubid = subid;
11659 0 : ncomments++;
11660 0 : }
11661 :
11662 0 : PQclear(res);
11663 0 : destroyPQExpBuffer(query);
11664 0 : }
11665 :
11666 : /*
11667 : * dumpDumpableObject
11668 : *
11669 : * This routine and its subsidiaries are responsible for creating
11670 : * ArchiveEntries (TOC objects) for each object to be dumped.
11671 : */
11672 : static void
11673 0 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11674 : {
11675 : /*
11676 : * Clear any dump-request bits for components that don't exist for this
11677 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11678 : * request for every kind of object.)
11679 : */
11680 0 : dobj->dump &= dobj->components;
11681 :
11682 : /* Now, short-circuit if there's nothing to be done here. */
11683 0 : if (dobj->dump == 0)
11684 0 : return;
11685 :
11686 0 : switch (dobj->objType)
11687 : {
11688 : case DO_NAMESPACE:
11689 0 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11690 0 : break;
11691 : case DO_EXTENSION:
11692 0 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11693 0 : break;
11694 : case DO_TYPE:
11695 0 : dumpType(fout, (const TypeInfo *) dobj);
11696 0 : break;
11697 : case DO_SHELL_TYPE:
11698 0 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11699 0 : break;
11700 : case DO_FUNC:
11701 0 : dumpFunc(fout, (const FuncInfo *) dobj);
11702 0 : break;
11703 : case DO_AGG:
11704 0 : dumpAgg(fout, (const AggInfo *) dobj);
11705 0 : break;
11706 : case DO_OPERATOR:
11707 0 : dumpOpr(fout, (const OprInfo *) dobj);
11708 0 : break;
11709 : case DO_ACCESS_METHOD:
11710 0 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11711 0 : break;
11712 : case DO_OPCLASS:
11713 0 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11714 0 : break;
11715 : case DO_OPFAMILY:
11716 0 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11717 0 : break;
11718 : case DO_COLLATION:
11719 0 : dumpCollation(fout, (const CollInfo *) dobj);
11720 0 : break;
11721 : case DO_CONVERSION:
11722 0 : dumpConversion(fout, (const ConvInfo *) dobj);
11723 0 : break;
11724 : case DO_TABLE:
11725 0 : dumpTable(fout, (const TableInfo *) dobj);
11726 0 : break;
11727 : case DO_TABLE_ATTACH:
11728 0 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11729 0 : break;
11730 : case DO_ATTRDEF:
11731 0 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11732 0 : break;
11733 : case DO_INDEX:
11734 0 : dumpIndex(fout, (const IndxInfo *) dobj);
11735 0 : break;
11736 : case DO_INDEX_ATTACH:
11737 0 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11738 0 : break;
11739 : case DO_STATSEXT:
11740 0 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11741 0 : break;
11742 : case DO_REFRESH_MATVIEW:
11743 0 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11744 0 : break;
11745 : case DO_RULE:
11746 0 : dumpRule(fout, (const RuleInfo *) dobj);
11747 0 : break;
11748 : case DO_TRIGGER:
11749 0 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11750 0 : break;
11751 : case DO_EVENT_TRIGGER:
11752 0 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11753 0 : break;
11754 : case DO_CONSTRAINT:
11755 0 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11756 0 : break;
11757 : case DO_FK_CONSTRAINT:
11758 0 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11759 0 : break;
11760 : case DO_PROCLANG:
11761 0 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11762 0 : break;
11763 : case DO_CAST:
11764 0 : dumpCast(fout, (const CastInfo *) dobj);
11765 0 : break;
11766 : case DO_TRANSFORM:
11767 0 : dumpTransform(fout, (const TransformInfo *) dobj);
11768 0 : break;
11769 : case DO_SEQUENCE_SET:
11770 0 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11771 0 : break;
11772 : case DO_TABLE_DATA:
11773 0 : dumpTableData(fout, (const TableDataInfo *) dobj);
11774 0 : break;
11775 : case DO_DUMMY_TYPE:
11776 : /* table rowtypes and array types are never dumped separately */
11777 : break;
11778 : case DO_TSPARSER:
11779 0 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11780 0 : break;
11781 : case DO_TSDICT:
11782 0 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11783 0 : break;
11784 : case DO_TSTEMPLATE:
11785 0 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11786 0 : break;
11787 : case DO_TSCONFIG:
11788 0 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11789 0 : break;
11790 : case DO_FDW:
11791 0 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11792 0 : break;
11793 : case DO_FOREIGN_SERVER:
11794 0 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11795 0 : break;
11796 : case DO_DEFAULT_ACL:
11797 0 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11798 0 : break;
11799 : case DO_LARGE_OBJECT:
11800 0 : dumpLO(fout, (const LoInfo *) dobj);
11801 0 : break;
11802 : case DO_LARGE_OBJECT_DATA:
11803 0 : if (dobj->dump & DUMP_COMPONENT_DATA)
11804 : {
11805 0 : LoInfo *loinfo;
11806 0 : TocEntry *te;
11807 :
11808 0 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11809 0 : if (loinfo == NULL)
11810 0 : pg_fatal("missing metadata for large objects \"%s\"",
11811 : dobj->name);
11812 :
11813 0 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11814 0 : ARCHIVE_OPTS(.tag = dobj->name,
11815 : .owner = loinfo->rolname,
11816 : .description = "BLOBS",
11817 : .section = SECTION_DATA,
11818 : .deps = dobj->dependencies,
11819 : .nDeps = dobj->nDeps,
11820 : .dumpFn = dumpLOs,
11821 : .dumpArg = loinfo));
11822 :
11823 : /*
11824 : * Set the TocEntry's dataLength in case we are doing a
11825 : * parallel dump and want to order dump jobs by table size.
11826 : * (We need some size estimate for every TocEntry with a
11827 : * DataDumper function.) We don't currently have any cheap
11828 : * way to estimate the size of LOs, but fortunately it doesn't
11829 : * matter too much as long as we get large batches of LOs
11830 : * processed reasonably early. Assume 8K per blob.
11831 : */
11832 0 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11833 0 : }
11834 0 : break;
11835 : case DO_POLICY:
11836 0 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11837 0 : break;
11838 : case DO_PUBLICATION:
11839 0 : dumpPublication(fout, (const PublicationInfo *) dobj);
11840 0 : break;
11841 : case DO_PUBLICATION_REL:
11842 0 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11843 0 : break;
11844 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11845 0 : dumpPublicationNamespace(fout,
11846 0 : (const PublicationSchemaInfo *) dobj);
11847 0 : break;
11848 : case DO_SUBSCRIPTION:
11849 0 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11850 0 : break;
11851 : case DO_SUBSCRIPTION_REL:
11852 0 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11853 0 : break;
11854 : case DO_REL_STATS:
11855 0 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11856 0 : break;
11857 : case DO_PRE_DATA_BOUNDARY:
11858 : case DO_POST_DATA_BOUNDARY:
11859 : /* never dumped, nothing to do */
11860 0 : break;
11861 : }
11862 0 : }
11863 :
11864 : /*
11865 : * dumpNamespace
11866 : * writes out to fout the queries to recreate a user-defined namespace
11867 : */
11868 : static void
11869 0 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11870 : {
11871 0 : DumpOptions *dopt = fout->dopt;
11872 0 : PQExpBuffer q;
11873 0 : PQExpBuffer delq;
11874 0 : char *qnspname;
11875 :
11876 : /* Do nothing if not dumping schema */
11877 0 : if (!dopt->dumpSchema)
11878 0 : return;
11879 :
11880 0 : q = createPQExpBuffer();
11881 0 : delq = createPQExpBuffer();
11882 :
11883 0 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11884 :
11885 0 : if (nspinfo->create)
11886 : {
11887 0 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11888 0 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11889 0 : }
11890 : else
11891 : {
11892 : /* see selectDumpableNamespace() */
11893 0 : appendPQExpBufferStr(delq,
11894 : "-- *not* dropping schema, since initdb creates it\n");
11895 0 : appendPQExpBufferStr(q,
11896 : "-- *not* creating schema, since initdb creates it\n");
11897 : }
11898 :
11899 0 : if (dopt->binary_upgrade)
11900 0 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11901 0 : "SCHEMA", qnspname, NULL);
11902 :
11903 0 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11904 0 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11905 0 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11906 : .owner = nspinfo->rolname,
11907 : .description = "SCHEMA",
11908 : .section = SECTION_PRE_DATA,
11909 : .createStmt = q->data,
11910 : .dropStmt = delq->data));
11911 :
11912 : /* Dump Schema Comments and Security Labels */
11913 0 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11914 : {
11915 0 : const char *initdb_comment = NULL;
11916 :
11917 0 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11918 0 : initdb_comment = "standard public schema";
11919 0 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11920 0 : NULL, nspinfo->rolname,
11921 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11922 0 : initdb_comment);
11923 0 : }
11924 :
11925 0 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11926 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11927 0 : NULL, nspinfo->rolname,
11928 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11929 :
11930 0 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11931 0 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11932 0 : qnspname, NULL, NULL,
11933 0 : NULL, nspinfo->rolname, &nspinfo->dacl);
11934 :
11935 0 : free(qnspname);
11936 :
11937 0 : destroyPQExpBuffer(q);
11938 0 : destroyPQExpBuffer(delq);
11939 0 : }
11940 :
11941 : /*
11942 : * dumpExtension
11943 : * writes out to fout the queries to recreate an extension
11944 : */
11945 : static void
11946 0 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11947 : {
11948 0 : DumpOptions *dopt = fout->dopt;
11949 0 : PQExpBuffer q;
11950 0 : PQExpBuffer delq;
11951 0 : char *qextname;
11952 :
11953 : /* Do nothing if not dumping schema */
11954 0 : if (!dopt->dumpSchema)
11955 0 : return;
11956 :
11957 0 : q = createPQExpBuffer();
11958 0 : delq = createPQExpBuffer();
11959 :
11960 0 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11961 :
11962 0 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11963 :
11964 0 : if (!dopt->binary_upgrade)
11965 : {
11966 : /*
11967 : * In a regular dump, we simply create the extension, intentionally
11968 : * not specifying a version, so that the destination installation's
11969 : * default version is used.
11970 : *
11971 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11972 : * types; but there are various scenarios in which it's convenient to
11973 : * manually create the desired extension before restoring, so we
11974 : * prefer to allow it to exist already.
11975 : */
11976 0 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11977 0 : qextname, fmtId(extinfo->namespace));
11978 0 : }
11979 : else
11980 : {
11981 : /*
11982 : * In binary-upgrade mode, it's critical to reproduce the state of the
11983 : * database exactly, so our procedure is to create an empty extension,
11984 : * restore all the contained objects normally, and add them to the
11985 : * extension one by one. This function performs just the first of
11986 : * those steps. binary_upgrade_extension_member() takes care of
11987 : * adding member objects as they're created.
11988 : */
11989 0 : int i;
11990 0 : int n;
11991 :
11992 0 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11993 :
11994 : /*
11995 : * We unconditionally create the extension, so we must drop it if it
11996 : * exists. This could happen if the user deleted 'plpgsql' and then
11997 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11998 : */
11999 0 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
12000 :
12001 0 : appendPQExpBufferStr(q,
12002 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
12003 0 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
12004 0 : appendPQExpBufferStr(q, ", ");
12005 0 : appendStringLiteralAH(q, extinfo->namespace, fout);
12006 0 : appendPQExpBufferStr(q, ", ");
12007 0 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
12008 0 : appendStringLiteralAH(q, extinfo->extversion, fout);
12009 0 : appendPQExpBufferStr(q, ", ");
12010 :
12011 : /*
12012 : * Note that we're pushing extconfig (an OID array) back into
12013 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
12014 : * preserved in binary upgrade.
12015 : */
12016 0 : if (strlen(extinfo->extconfig) > 2)
12017 0 : appendStringLiteralAH(q, extinfo->extconfig, fout);
12018 : else
12019 0 : appendPQExpBufferStr(q, "NULL");
12020 0 : appendPQExpBufferStr(q, ", ");
12021 0 : if (strlen(extinfo->extcondition) > 2)
12022 0 : appendStringLiteralAH(q, extinfo->extcondition, fout);
12023 : else
12024 0 : appendPQExpBufferStr(q, "NULL");
12025 0 : appendPQExpBufferStr(q, ", ");
12026 0 : appendPQExpBufferStr(q, "ARRAY[");
12027 0 : n = 0;
12028 0 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12029 : {
12030 0 : DumpableObject *extobj;
12031 :
12032 0 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12033 0 : if (extobj && extobj->objType == DO_EXTENSION)
12034 : {
12035 0 : if (n++ > 0)
12036 0 : appendPQExpBufferChar(q, ',');
12037 0 : appendStringLiteralAH(q, extobj->name, fout);
12038 0 : }
12039 0 : }
12040 0 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12041 0 : appendPQExpBufferStr(q, ");\n");
12042 0 : }
12043 :
12044 0 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12045 0 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12046 0 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12047 : .description = "EXTENSION",
12048 : .section = SECTION_PRE_DATA,
12049 : .createStmt = q->data,
12050 : .dropStmt = delq->data));
12051 :
12052 : /* Dump Extension Comments */
12053 0 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12054 0 : dumpComment(fout, "EXTENSION", qextname,
12055 : NULL, "",
12056 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12057 :
12058 0 : free(qextname);
12059 :
12060 0 : destroyPQExpBuffer(q);
12061 0 : destroyPQExpBuffer(delq);
12062 0 : }
12063 :
12064 : /*
12065 : * dumpType
12066 : * writes out to fout the queries to recreate a user-defined type
12067 : */
12068 : static void
12069 0 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12070 : {
12071 0 : DumpOptions *dopt = fout->dopt;
12072 :
12073 : /* Do nothing if not dumping schema */
12074 0 : if (!dopt->dumpSchema)
12075 0 : return;
12076 :
12077 : /* Dump out in proper style */
12078 0 : if (tyinfo->typtype == TYPTYPE_BASE)
12079 0 : dumpBaseType(fout, tyinfo);
12080 0 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12081 0 : dumpDomain(fout, tyinfo);
12082 0 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12083 0 : dumpCompositeType(fout, tyinfo);
12084 0 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12085 0 : dumpEnumType(fout, tyinfo);
12086 0 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12087 0 : dumpRangeType(fout, tyinfo);
12088 0 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12089 0 : dumpUndefinedType(fout, tyinfo);
12090 : else
12091 0 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12092 : tyinfo->dobj.name);
12093 0 : }
12094 :
12095 : /*
12096 : * dumpEnumType
12097 : * writes out to fout the queries to recreate a user-defined enum type
12098 : */
12099 : static void
12100 0 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12101 : {
12102 0 : DumpOptions *dopt = fout->dopt;
12103 0 : PQExpBuffer q = createPQExpBuffer();
12104 0 : PQExpBuffer delq = createPQExpBuffer();
12105 0 : PQExpBuffer query = createPQExpBuffer();
12106 0 : PGresult *res;
12107 0 : int num,
12108 : i;
12109 0 : Oid enum_oid;
12110 0 : char *qtypname;
12111 0 : char *qualtypname;
12112 0 : char *label;
12113 0 : int i_enumlabel;
12114 0 : int i_oid;
12115 :
12116 0 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12117 : {
12118 : /* Set up query for enum-specific details */
12119 0 : appendPQExpBufferStr(query,
12120 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12121 : "SELECT oid, enumlabel "
12122 : "FROM pg_catalog.pg_enum "
12123 : "WHERE enumtypid = $1 "
12124 : "ORDER BY enumsortorder");
12125 :
12126 0 : ExecuteSqlStatement(fout, query->data);
12127 :
12128 0 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12129 0 : }
12130 :
12131 0 : printfPQExpBuffer(query,
12132 : "EXECUTE dumpEnumType('%u')",
12133 0 : tyinfo->dobj.catId.oid);
12134 :
12135 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12136 :
12137 0 : num = PQntuples(res);
12138 :
12139 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12140 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12141 :
12142 : /*
12143 : * CASCADE shouldn't be required here as for normal types since the I/O
12144 : * functions are generic and do not get dropped.
12145 : */
12146 0 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12147 :
12148 0 : if (dopt->binary_upgrade)
12149 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12150 0 : tyinfo->dobj.catId.oid,
12151 : false, false);
12152 :
12153 0 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12154 0 : qualtypname);
12155 :
12156 0 : if (!dopt->binary_upgrade)
12157 : {
12158 0 : i_enumlabel = PQfnumber(res, "enumlabel");
12159 :
12160 : /* Labels with server-assigned oids */
12161 0 : for (i = 0; i < num; i++)
12162 : {
12163 0 : label = PQgetvalue(res, i, i_enumlabel);
12164 0 : if (i > 0)
12165 0 : appendPQExpBufferChar(q, ',');
12166 0 : appendPQExpBufferStr(q, "\n ");
12167 0 : appendStringLiteralAH(q, label, fout);
12168 0 : }
12169 0 : }
12170 :
12171 0 : appendPQExpBufferStr(q, "\n);\n");
12172 :
12173 0 : if (dopt->binary_upgrade)
12174 : {
12175 0 : i_oid = PQfnumber(res, "oid");
12176 0 : i_enumlabel = PQfnumber(res, "enumlabel");
12177 :
12178 : /* Labels with dump-assigned (preserved) oids */
12179 0 : for (i = 0; i < num; i++)
12180 : {
12181 0 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12182 0 : label = PQgetvalue(res, i, i_enumlabel);
12183 :
12184 0 : if (i == 0)
12185 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12186 0 : appendPQExpBuffer(q,
12187 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12188 0 : enum_oid);
12189 0 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12190 0 : appendStringLiteralAH(q, label, fout);
12191 0 : appendPQExpBufferStr(q, ";\n\n");
12192 0 : }
12193 0 : }
12194 :
12195 0 : if (dopt->binary_upgrade)
12196 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12197 0 : "TYPE", qtypname,
12198 0 : tyinfo->dobj.namespace->dobj.name);
12199 :
12200 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12201 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12202 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12203 : .namespace = tyinfo->dobj.namespace->dobj.name,
12204 : .owner = tyinfo->rolname,
12205 : .description = "TYPE",
12206 : .section = SECTION_PRE_DATA,
12207 : .createStmt = q->data,
12208 : .dropStmt = delq->data));
12209 :
12210 : /* Dump Type Comments and Security Labels */
12211 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12212 0 : dumpComment(fout, "TYPE", qtypname,
12213 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12214 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12215 :
12216 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12217 0 : dumpSecLabel(fout, "TYPE", qtypname,
12218 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12219 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12220 :
12221 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12222 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12223 0 : qtypname, NULL,
12224 0 : tyinfo->dobj.namespace->dobj.name,
12225 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12226 :
12227 0 : PQclear(res);
12228 0 : destroyPQExpBuffer(q);
12229 0 : destroyPQExpBuffer(delq);
12230 0 : destroyPQExpBuffer(query);
12231 0 : free(qtypname);
12232 0 : free(qualtypname);
12233 0 : }
12234 :
12235 : /*
12236 : * dumpRangeType
12237 : * writes out to fout the queries to recreate a user-defined range type
12238 : */
12239 : static void
12240 0 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12241 : {
12242 0 : DumpOptions *dopt = fout->dopt;
12243 0 : PQExpBuffer q = createPQExpBuffer();
12244 0 : PQExpBuffer delq = createPQExpBuffer();
12245 0 : PQExpBuffer query = createPQExpBuffer();
12246 0 : PGresult *res;
12247 0 : Oid collationOid;
12248 0 : char *qtypname;
12249 0 : char *qualtypname;
12250 0 : char *procname;
12251 :
12252 0 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12253 : {
12254 : /* Set up query for range-specific details */
12255 0 : appendPQExpBufferStr(query,
12256 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12257 :
12258 0 : appendPQExpBufferStr(query,
12259 : "SELECT ");
12260 :
12261 0 : if (fout->remoteVersion >= 140000)
12262 0 : appendPQExpBufferStr(query,
12263 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12264 : else
12265 0 : appendPQExpBufferStr(query,
12266 : "NULL AS rngmultitype, ");
12267 :
12268 0 : appendPQExpBufferStr(query,
12269 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12270 : "opc.opcname AS opcname, "
12271 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12272 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12273 : "opc.opcdefault, "
12274 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12275 : " ELSE rngcollation END AS collation, "
12276 : "rngcanonical, rngsubdiff "
12277 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12278 : " pg_catalog.pg_opclass opc "
12279 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12280 : "rngtypid = $1");
12281 :
12282 0 : ExecuteSqlStatement(fout, query->data);
12283 :
12284 0 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12285 0 : }
12286 :
12287 0 : printfPQExpBuffer(query,
12288 : "EXECUTE dumpRangeType('%u')",
12289 0 : tyinfo->dobj.catId.oid);
12290 :
12291 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12292 :
12293 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12294 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12295 :
12296 : /*
12297 : * CASCADE shouldn't be required here as for normal types since the I/O
12298 : * functions are generic and do not get dropped.
12299 : */
12300 0 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12301 :
12302 0 : if (dopt->binary_upgrade)
12303 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12304 0 : tyinfo->dobj.catId.oid,
12305 : false, true);
12306 :
12307 0 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12308 0 : qualtypname);
12309 :
12310 0 : appendPQExpBuffer(q, "\n subtype = %s",
12311 0 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12312 :
12313 0 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12314 0 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12315 0 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12316 :
12317 : /* print subtype_opclass only if not default for subtype */
12318 0 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12319 : {
12320 0 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12321 0 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12322 :
12323 0 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12324 0 : fmtId(nspname));
12325 0 : appendPQExpBufferStr(q, fmtId(opcname));
12326 0 : }
12327 :
12328 0 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12329 0 : if (OidIsValid(collationOid))
12330 : {
12331 0 : CollInfo *coll = findCollationByOid(collationOid);
12332 :
12333 0 : if (coll)
12334 0 : appendPQExpBuffer(q, ",\n collation = %s",
12335 0 : fmtQualifiedDumpable(coll));
12336 0 : }
12337 :
12338 0 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12339 0 : if (strcmp(procname, "-") != 0)
12340 0 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12341 :
12342 0 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12343 0 : if (strcmp(procname, "-") != 0)
12344 0 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12345 :
12346 0 : appendPQExpBufferStr(q, "\n);\n");
12347 :
12348 0 : if (dopt->binary_upgrade)
12349 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12350 0 : "TYPE", qtypname,
12351 0 : tyinfo->dobj.namespace->dobj.name);
12352 :
12353 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12354 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12355 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12356 : .namespace = tyinfo->dobj.namespace->dobj.name,
12357 : .owner = tyinfo->rolname,
12358 : .description = "TYPE",
12359 : .section = SECTION_PRE_DATA,
12360 : .createStmt = q->data,
12361 : .dropStmt = delq->data));
12362 :
12363 : /* Dump Type Comments and Security Labels */
12364 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12365 0 : dumpComment(fout, "TYPE", qtypname,
12366 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12367 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12368 :
12369 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12370 0 : dumpSecLabel(fout, "TYPE", qtypname,
12371 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12372 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12373 :
12374 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12375 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12376 0 : qtypname, NULL,
12377 0 : tyinfo->dobj.namespace->dobj.name,
12378 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12379 :
12380 0 : PQclear(res);
12381 0 : destroyPQExpBuffer(q);
12382 0 : destroyPQExpBuffer(delq);
12383 0 : destroyPQExpBuffer(query);
12384 0 : free(qtypname);
12385 0 : free(qualtypname);
12386 0 : }
12387 :
12388 : /*
12389 : * dumpUndefinedType
12390 : * writes out to fout the queries to recreate a !typisdefined type
12391 : *
12392 : * This is a shell type, but we use different terminology to distinguish
12393 : * this case from where we have to emit a shell type definition to break
12394 : * circular dependencies. An undefined type shouldn't ever have anything
12395 : * depending on it.
12396 : */
12397 : static void
12398 0 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12399 : {
12400 0 : DumpOptions *dopt = fout->dopt;
12401 0 : PQExpBuffer q = createPQExpBuffer();
12402 0 : PQExpBuffer delq = createPQExpBuffer();
12403 0 : char *qtypname;
12404 0 : char *qualtypname;
12405 :
12406 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12407 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12408 :
12409 0 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12410 :
12411 0 : if (dopt->binary_upgrade)
12412 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12413 0 : tyinfo->dobj.catId.oid,
12414 : false, false);
12415 :
12416 0 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12417 0 : qualtypname);
12418 :
12419 0 : if (dopt->binary_upgrade)
12420 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12421 0 : "TYPE", qtypname,
12422 0 : tyinfo->dobj.namespace->dobj.name);
12423 :
12424 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12425 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12426 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12427 : .namespace = tyinfo->dobj.namespace->dobj.name,
12428 : .owner = tyinfo->rolname,
12429 : .description = "TYPE",
12430 : .section = SECTION_PRE_DATA,
12431 : .createStmt = q->data,
12432 : .dropStmt = delq->data));
12433 :
12434 : /* Dump Type Comments and Security Labels */
12435 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12436 0 : dumpComment(fout, "TYPE", qtypname,
12437 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12438 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12439 :
12440 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12441 0 : dumpSecLabel(fout, "TYPE", qtypname,
12442 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12443 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12444 :
12445 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12446 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12447 0 : qtypname, NULL,
12448 0 : tyinfo->dobj.namespace->dobj.name,
12449 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12450 :
12451 0 : destroyPQExpBuffer(q);
12452 0 : destroyPQExpBuffer(delq);
12453 0 : free(qtypname);
12454 0 : free(qualtypname);
12455 0 : }
12456 :
12457 : /*
12458 : * dumpBaseType
12459 : * writes out to fout the queries to recreate a user-defined base type
12460 : */
12461 : static void
12462 0 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12463 : {
12464 0 : DumpOptions *dopt = fout->dopt;
12465 0 : PQExpBuffer q = createPQExpBuffer();
12466 0 : PQExpBuffer delq = createPQExpBuffer();
12467 0 : PQExpBuffer query = createPQExpBuffer();
12468 0 : PGresult *res;
12469 0 : char *qtypname;
12470 0 : char *qualtypname;
12471 0 : char *typlen;
12472 0 : char *typinput;
12473 0 : char *typoutput;
12474 0 : char *typreceive;
12475 0 : char *typsend;
12476 0 : char *typmodin;
12477 0 : char *typmodout;
12478 0 : char *typanalyze;
12479 0 : char *typsubscript;
12480 0 : Oid typreceiveoid;
12481 0 : Oid typsendoid;
12482 0 : Oid typmodinoid;
12483 0 : Oid typmodoutoid;
12484 0 : Oid typanalyzeoid;
12485 0 : Oid typsubscriptoid;
12486 0 : char *typcategory;
12487 0 : char *typispreferred;
12488 0 : char *typdelim;
12489 0 : char *typbyval;
12490 0 : char *typalign;
12491 0 : char *typstorage;
12492 0 : char *typcollatable;
12493 0 : char *typdefault;
12494 0 : bool typdefault_is_literal = false;
12495 :
12496 0 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12497 : {
12498 : /* Set up query for type-specific details */
12499 0 : appendPQExpBufferStr(query,
12500 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12501 : "SELECT typlen, "
12502 : "typinput, typoutput, typreceive, typsend, "
12503 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12504 : "typsend::pg_catalog.oid AS typsendoid, "
12505 : "typanalyze, "
12506 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12507 : "typdelim, typbyval, typalign, typstorage, "
12508 : "typmodin, typmodout, "
12509 : "typmodin::pg_catalog.oid AS typmodinoid, "
12510 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12511 : "typcategory, typispreferred, "
12512 : "(typcollation <> 0) AS typcollatable, "
12513 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12514 :
12515 0 : if (fout->remoteVersion >= 140000)
12516 0 : appendPQExpBufferStr(query,
12517 : "typsubscript, "
12518 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12519 : else
12520 0 : appendPQExpBufferStr(query,
12521 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12522 :
12523 0 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12524 : "WHERE oid = $1");
12525 :
12526 0 : ExecuteSqlStatement(fout, query->data);
12527 :
12528 0 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12529 0 : }
12530 :
12531 0 : printfPQExpBuffer(query,
12532 : "EXECUTE dumpBaseType('%u')",
12533 0 : tyinfo->dobj.catId.oid);
12534 :
12535 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12536 :
12537 0 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12538 0 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12539 0 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12540 0 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12541 0 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12542 0 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12543 0 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12544 0 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12545 0 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12546 0 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12547 0 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12548 0 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12549 0 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12550 0 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12551 0 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12552 0 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12553 0 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12554 0 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12555 0 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12556 0 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12557 0 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12558 0 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12559 0 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12560 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12561 0 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12562 : {
12563 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12564 0 : typdefault_is_literal = true; /* it needs quotes */
12565 0 : }
12566 : else
12567 0 : typdefault = NULL;
12568 :
12569 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12570 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12571 :
12572 : /*
12573 : * The reason we include CASCADE is that the circular dependency between
12574 : * the type and its I/O functions makes it impossible to drop the type any
12575 : * other way.
12576 : */
12577 0 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12578 :
12579 : /*
12580 : * We might already have a shell type, but setting pg_type_oid is
12581 : * harmless, and in any case we'd better set the array type OID.
12582 : */
12583 0 : if (dopt->binary_upgrade)
12584 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12585 0 : tyinfo->dobj.catId.oid,
12586 : false, false);
12587 :
12588 0 : appendPQExpBuffer(q,
12589 : "CREATE TYPE %s (\n"
12590 : " INTERNALLENGTH = %s",
12591 0 : qualtypname,
12592 0 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12593 :
12594 : /* regproc result is sufficiently quoted already */
12595 0 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12596 0 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12597 0 : if (OidIsValid(typreceiveoid))
12598 0 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12599 0 : if (OidIsValid(typsendoid))
12600 0 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12601 0 : if (OidIsValid(typmodinoid))
12602 0 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12603 0 : if (OidIsValid(typmodoutoid))
12604 0 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12605 0 : if (OidIsValid(typanalyzeoid))
12606 0 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12607 :
12608 0 : if (strcmp(typcollatable, "t") == 0)
12609 0 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12610 :
12611 0 : if (typdefault != NULL)
12612 : {
12613 0 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12614 0 : if (typdefault_is_literal)
12615 0 : appendStringLiteralAH(q, typdefault, fout);
12616 : else
12617 0 : appendPQExpBufferStr(q, typdefault);
12618 0 : }
12619 :
12620 0 : if (OidIsValid(typsubscriptoid))
12621 0 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12622 :
12623 0 : if (OidIsValid(tyinfo->typelem))
12624 0 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12625 0 : getFormattedTypeName(fout, tyinfo->typelem,
12626 : zeroIsError));
12627 :
12628 0 : if (strcmp(typcategory, "U") != 0)
12629 : {
12630 0 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12631 0 : appendStringLiteralAH(q, typcategory, fout);
12632 0 : }
12633 :
12634 0 : if (strcmp(typispreferred, "t") == 0)
12635 0 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12636 :
12637 0 : if (typdelim && strcmp(typdelim, ",") != 0)
12638 : {
12639 0 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12640 0 : appendStringLiteralAH(q, typdelim, fout);
12641 0 : }
12642 :
12643 0 : if (*typalign == TYPALIGN_CHAR)
12644 0 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12645 0 : else if (*typalign == TYPALIGN_SHORT)
12646 0 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12647 0 : else if (*typalign == TYPALIGN_INT)
12648 0 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12649 0 : else if (*typalign == TYPALIGN_DOUBLE)
12650 0 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12651 :
12652 0 : if (*typstorage == TYPSTORAGE_PLAIN)
12653 0 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12654 0 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12655 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12656 0 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12657 0 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12658 0 : else if (*typstorage == TYPSTORAGE_MAIN)
12659 0 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12660 :
12661 0 : if (strcmp(typbyval, "t") == 0)
12662 0 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12663 :
12664 0 : appendPQExpBufferStr(q, "\n);\n");
12665 :
12666 0 : if (dopt->binary_upgrade)
12667 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12668 0 : "TYPE", qtypname,
12669 0 : tyinfo->dobj.namespace->dobj.name);
12670 :
12671 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12672 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12673 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12674 : .namespace = tyinfo->dobj.namespace->dobj.name,
12675 : .owner = tyinfo->rolname,
12676 : .description = "TYPE",
12677 : .section = SECTION_PRE_DATA,
12678 : .createStmt = q->data,
12679 : .dropStmt = delq->data));
12680 :
12681 : /* Dump Type Comments and Security Labels */
12682 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12683 0 : dumpComment(fout, "TYPE", qtypname,
12684 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12685 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12686 :
12687 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12688 0 : dumpSecLabel(fout, "TYPE", qtypname,
12689 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12690 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12691 :
12692 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12693 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12694 0 : qtypname, NULL,
12695 0 : tyinfo->dobj.namespace->dobj.name,
12696 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12697 :
12698 0 : PQclear(res);
12699 0 : destroyPQExpBuffer(q);
12700 0 : destroyPQExpBuffer(delq);
12701 0 : destroyPQExpBuffer(query);
12702 0 : free(qtypname);
12703 0 : free(qualtypname);
12704 0 : }
12705 :
12706 : /*
12707 : * dumpDomain
12708 : * writes out to fout the queries to recreate a user-defined domain
12709 : */
12710 : static void
12711 0 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12712 : {
12713 0 : DumpOptions *dopt = fout->dopt;
12714 0 : PQExpBuffer q = createPQExpBuffer();
12715 0 : PQExpBuffer delq = createPQExpBuffer();
12716 0 : PQExpBuffer query = createPQExpBuffer();
12717 0 : PGresult *res;
12718 0 : int i;
12719 0 : char *qtypname;
12720 0 : char *qualtypname;
12721 0 : char *typnotnull;
12722 0 : char *typdefn;
12723 0 : char *typdefault;
12724 0 : Oid typcollation;
12725 0 : bool typdefault_is_literal = false;
12726 :
12727 0 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12728 : {
12729 : /* Set up query for domain-specific details */
12730 0 : appendPQExpBufferStr(query,
12731 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12732 :
12733 0 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12734 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12735 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12736 : "t.typdefault, "
12737 : "CASE WHEN t.typcollation <> u.typcollation "
12738 : "THEN t.typcollation ELSE 0 END AS typcollation "
12739 : "FROM pg_catalog.pg_type t "
12740 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12741 : "WHERE t.oid = $1");
12742 :
12743 0 : ExecuteSqlStatement(fout, query->data);
12744 :
12745 0 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12746 0 : }
12747 :
12748 0 : printfPQExpBuffer(query,
12749 : "EXECUTE dumpDomain('%u')",
12750 0 : tyinfo->dobj.catId.oid);
12751 :
12752 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12753 :
12754 0 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12755 0 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12756 0 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12757 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12758 0 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12759 : {
12760 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12761 0 : typdefault_is_literal = true; /* it needs quotes */
12762 0 : }
12763 : else
12764 0 : typdefault = NULL;
12765 0 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12766 :
12767 0 : if (dopt->binary_upgrade)
12768 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12769 0 : tyinfo->dobj.catId.oid,
12770 : true, /* force array type */
12771 : false); /* force multirange type */
12772 :
12773 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12774 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12775 :
12776 0 : appendPQExpBuffer(q,
12777 : "CREATE DOMAIN %s AS %s",
12778 0 : qualtypname,
12779 0 : typdefn);
12780 :
12781 : /* Print collation only if different from base type's collation */
12782 0 : if (OidIsValid(typcollation))
12783 : {
12784 0 : CollInfo *coll;
12785 :
12786 0 : coll = findCollationByOid(typcollation);
12787 0 : if (coll)
12788 0 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12789 0 : }
12790 :
12791 : /*
12792 : * Print a not-null constraint if there's one. In servers older than 17
12793 : * these don't have names, so just print it unadorned; in newer ones they
12794 : * do, but most of the time it's going to be the standard generated one,
12795 : * so omit the name in that case also.
12796 : */
12797 0 : if (typnotnull[0] == 't')
12798 : {
12799 0 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12800 0 : appendPQExpBufferStr(q, " NOT NULL");
12801 : else
12802 : {
12803 0 : ConstraintInfo *notnull = tyinfo->notnull;
12804 :
12805 0 : if (!notnull->separate)
12806 : {
12807 0 : char *default_name;
12808 :
12809 : /* XXX should match ChooseConstraintName better */
12810 0 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12811 :
12812 0 : if (strcmp(default_name, notnull->dobj.name) == 0)
12813 0 : appendPQExpBufferStr(q, " NOT NULL");
12814 : else
12815 0 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12816 0 : fmtId(notnull->dobj.name), notnull->condef);
12817 0 : free(default_name);
12818 0 : }
12819 0 : }
12820 0 : }
12821 :
12822 0 : if (typdefault != NULL)
12823 : {
12824 0 : appendPQExpBufferStr(q, " DEFAULT ");
12825 0 : if (typdefault_is_literal)
12826 0 : appendStringLiteralAH(q, typdefault, fout);
12827 : else
12828 0 : appendPQExpBufferStr(q, typdefault);
12829 0 : }
12830 :
12831 0 : PQclear(res);
12832 :
12833 : /*
12834 : * Add any CHECK constraints for the domain
12835 : */
12836 0 : for (i = 0; i < tyinfo->nDomChecks; i++)
12837 : {
12838 0 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12839 :
12840 0 : if (!domcheck->separate && domcheck->contype == 'c')
12841 0 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12842 0 : fmtId(domcheck->dobj.name), domcheck->condef);
12843 0 : }
12844 :
12845 0 : appendPQExpBufferStr(q, ";\n");
12846 :
12847 0 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12848 :
12849 0 : if (dopt->binary_upgrade)
12850 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12851 0 : "DOMAIN", qtypname,
12852 0 : tyinfo->dobj.namespace->dobj.name);
12853 :
12854 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12855 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12856 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12857 : .namespace = tyinfo->dobj.namespace->dobj.name,
12858 : .owner = tyinfo->rolname,
12859 : .description = "DOMAIN",
12860 : .section = SECTION_PRE_DATA,
12861 : .createStmt = q->data,
12862 : .dropStmt = delq->data));
12863 :
12864 : /* Dump Domain Comments and Security Labels */
12865 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12866 0 : dumpComment(fout, "DOMAIN", qtypname,
12867 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12868 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12869 :
12870 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12871 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12872 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12873 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12874 :
12875 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12876 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12877 0 : qtypname, NULL,
12878 0 : tyinfo->dobj.namespace->dobj.name,
12879 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12880 :
12881 : /* Dump any per-constraint comments */
12882 0 : for (i = 0; i < tyinfo->nDomChecks; i++)
12883 : {
12884 0 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12885 0 : PQExpBuffer conprefix;
12886 :
12887 : /* but only if the constraint itself was dumped here */
12888 0 : if (domcheck->separate)
12889 0 : continue;
12890 :
12891 0 : conprefix = createPQExpBuffer();
12892 0 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12893 0 : fmtId(domcheck->dobj.name));
12894 :
12895 0 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12896 0 : dumpComment(fout, conprefix->data, qtypname,
12897 0 : tyinfo->dobj.namespace->dobj.name,
12898 0 : tyinfo->rolname,
12899 0 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12900 :
12901 0 : destroyPQExpBuffer(conprefix);
12902 0 : }
12903 :
12904 : /*
12905 : * And a comment on the not-null constraint, if there's one -- but only if
12906 : * the constraint itself was dumped here
12907 : */
12908 0 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12909 : {
12910 0 : PQExpBuffer conprefix = createPQExpBuffer();
12911 :
12912 0 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12913 0 : fmtId(tyinfo->notnull->dobj.name));
12914 :
12915 0 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12916 0 : dumpComment(fout, conprefix->data, qtypname,
12917 0 : tyinfo->dobj.namespace->dobj.name,
12918 0 : tyinfo->rolname,
12919 0 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12920 0 : destroyPQExpBuffer(conprefix);
12921 0 : }
12922 :
12923 0 : destroyPQExpBuffer(q);
12924 0 : destroyPQExpBuffer(delq);
12925 0 : destroyPQExpBuffer(query);
12926 0 : free(qtypname);
12927 0 : free(qualtypname);
12928 0 : }
12929 :
12930 : /*
12931 : * dumpCompositeType
12932 : * writes out to fout the queries to recreate a user-defined stand-alone
12933 : * composite type
12934 : */
12935 : static void
12936 0 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12937 : {
12938 0 : DumpOptions *dopt = fout->dopt;
12939 0 : PQExpBuffer q = createPQExpBuffer();
12940 0 : PQExpBuffer dropped = createPQExpBuffer();
12941 0 : PQExpBuffer delq = createPQExpBuffer();
12942 0 : PQExpBuffer query = createPQExpBuffer();
12943 0 : PGresult *res;
12944 0 : char *qtypname;
12945 0 : char *qualtypname;
12946 0 : int ntups;
12947 0 : int i_attname;
12948 0 : int i_atttypdefn;
12949 0 : int i_attlen;
12950 0 : int i_attalign;
12951 0 : int i_attisdropped;
12952 0 : int i_attcollation;
12953 0 : int i;
12954 0 : int actual_atts;
12955 :
12956 0 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12957 : {
12958 : /*
12959 : * Set up query for type-specific details.
12960 : *
12961 : * Since we only want to dump COLLATE clauses for attributes whose
12962 : * collation is different from their type's default, we use a CASE
12963 : * here to suppress uninteresting attcollations cheaply. atttypid
12964 : * will be 0 for dropped columns; collation does not matter for those.
12965 : */
12966 0 : appendPQExpBufferStr(query,
12967 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12968 : "SELECT a.attname, a.attnum, "
12969 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12970 : "a.attlen, a.attalign, a.attisdropped, "
12971 : "CASE WHEN a.attcollation <> at.typcollation "
12972 : "THEN a.attcollation ELSE 0 END AS attcollation "
12973 : "FROM pg_catalog.pg_type ct "
12974 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12975 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12976 : "WHERE ct.oid = $1 "
12977 : "ORDER BY a.attnum");
12978 :
12979 0 : ExecuteSqlStatement(fout, query->data);
12980 :
12981 0 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12982 0 : }
12983 :
12984 0 : printfPQExpBuffer(query,
12985 : "EXECUTE dumpCompositeType('%u')",
12986 0 : tyinfo->dobj.catId.oid);
12987 :
12988 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12989 :
12990 0 : ntups = PQntuples(res);
12991 :
12992 0 : i_attname = PQfnumber(res, "attname");
12993 0 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12994 0 : i_attlen = PQfnumber(res, "attlen");
12995 0 : i_attalign = PQfnumber(res, "attalign");
12996 0 : i_attisdropped = PQfnumber(res, "attisdropped");
12997 0 : i_attcollation = PQfnumber(res, "attcollation");
12998 :
12999 0 : if (dopt->binary_upgrade)
13000 : {
13001 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13002 0 : tyinfo->dobj.catId.oid,
13003 : false, false);
13004 0 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
13005 0 : }
13006 :
13007 0 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
13008 0 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
13009 :
13010 0 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13011 0 : qualtypname);
13012 :
13013 0 : actual_atts = 0;
13014 0 : for (i = 0; i < ntups; i++)
13015 : {
13016 0 : char *attname;
13017 0 : char *atttypdefn;
13018 0 : char *attlen;
13019 0 : char *attalign;
13020 0 : bool attisdropped;
13021 0 : Oid attcollation;
13022 :
13023 0 : attname = PQgetvalue(res, i, i_attname);
13024 0 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13025 0 : attlen = PQgetvalue(res, i, i_attlen);
13026 0 : attalign = PQgetvalue(res, i, i_attalign);
13027 0 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13028 0 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13029 :
13030 0 : if (attisdropped && !dopt->binary_upgrade)
13031 0 : continue;
13032 :
13033 : /* Format properly if not first attr */
13034 0 : if (actual_atts++ > 0)
13035 0 : appendPQExpBufferChar(q, ',');
13036 0 : appendPQExpBufferStr(q, "\n\t");
13037 :
13038 0 : if (!attisdropped)
13039 : {
13040 0 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13041 :
13042 : /* Add collation if not default for the column type */
13043 0 : if (OidIsValid(attcollation))
13044 : {
13045 0 : CollInfo *coll;
13046 :
13047 0 : coll = findCollationByOid(attcollation);
13048 0 : if (coll)
13049 0 : appendPQExpBuffer(q, " COLLATE %s",
13050 0 : fmtQualifiedDumpable(coll));
13051 0 : }
13052 0 : }
13053 : else
13054 : {
13055 : /*
13056 : * This is a dropped attribute and we're in binary_upgrade mode.
13057 : * Insert a placeholder for it in the CREATE TYPE command, and set
13058 : * length and alignment with direct UPDATE to the catalogs
13059 : * afterwards. See similar code in dumpTableSchema().
13060 : */
13061 0 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13062 :
13063 : /* stash separately for insertion after the CREATE TYPE */
13064 0 : appendPQExpBufferStr(dropped,
13065 : "\n-- For binary upgrade, recreate dropped column.\n");
13066 0 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13067 : "SET attlen = %s, "
13068 : "attalign = '%s', attbyval = false\n"
13069 0 : "WHERE attname = ", attlen, attalign);
13070 0 : appendStringLiteralAH(dropped, attname, fout);
13071 0 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13072 0 : appendStringLiteralAH(dropped, qualtypname, fout);
13073 0 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13074 :
13075 0 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13076 0 : qualtypname);
13077 0 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13078 0 : fmtId(attname));
13079 : }
13080 0 : }
13081 0 : appendPQExpBufferStr(q, "\n);\n");
13082 0 : appendPQExpBufferStr(q, dropped->data);
13083 :
13084 0 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13085 :
13086 0 : if (dopt->binary_upgrade)
13087 0 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13088 0 : "TYPE", qtypname,
13089 0 : tyinfo->dobj.namespace->dobj.name);
13090 :
13091 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13092 0 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13093 0 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13094 : .namespace = tyinfo->dobj.namespace->dobj.name,
13095 : .owner = tyinfo->rolname,
13096 : .description = "TYPE",
13097 : .section = SECTION_PRE_DATA,
13098 : .createStmt = q->data,
13099 : .dropStmt = delq->data));
13100 :
13101 :
13102 : /* Dump Type Comments and Security Labels */
13103 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13104 0 : dumpComment(fout, "TYPE", qtypname,
13105 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13106 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13107 :
13108 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13109 0 : dumpSecLabel(fout, "TYPE", qtypname,
13110 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13111 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13112 :
13113 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13114 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13115 0 : qtypname, NULL,
13116 0 : tyinfo->dobj.namespace->dobj.name,
13117 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
13118 :
13119 : /* Dump any per-column comments */
13120 0 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13121 0 : dumpCompositeTypeColComments(fout, tyinfo, res);
13122 :
13123 0 : PQclear(res);
13124 0 : destroyPQExpBuffer(q);
13125 0 : destroyPQExpBuffer(dropped);
13126 0 : destroyPQExpBuffer(delq);
13127 0 : destroyPQExpBuffer(query);
13128 0 : free(qtypname);
13129 0 : free(qualtypname);
13130 0 : }
13131 :
13132 : /*
13133 : * dumpCompositeTypeColComments
13134 : * writes out to fout the queries to recreate comments on the columns of
13135 : * a user-defined stand-alone composite type.
13136 : *
13137 : * The caller has already made a query to collect the names and attnums
13138 : * of the type's columns, so we just pass that result into here rather
13139 : * than reading them again.
13140 : */
13141 : static void
13142 0 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13143 : PGresult *res)
13144 : {
13145 0 : CommentItem *comments;
13146 0 : int ncomments;
13147 0 : PQExpBuffer query;
13148 0 : PQExpBuffer target;
13149 0 : int i;
13150 0 : int ntups;
13151 0 : int i_attname;
13152 0 : int i_attnum;
13153 0 : int i_attisdropped;
13154 :
13155 : /* do nothing, if --no-comments is supplied */
13156 0 : if (fout->dopt->no_comments)
13157 0 : return;
13158 :
13159 : /* Search for comments associated with type's pg_class OID */
13160 0 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13161 : &comments);
13162 :
13163 : /* If no comments exist, we're done */
13164 0 : if (ncomments <= 0)
13165 0 : return;
13166 :
13167 : /* Build COMMENT ON statements */
13168 0 : query = createPQExpBuffer();
13169 0 : target = createPQExpBuffer();
13170 :
13171 0 : ntups = PQntuples(res);
13172 0 : i_attnum = PQfnumber(res, "attnum");
13173 0 : i_attname = PQfnumber(res, "attname");
13174 0 : i_attisdropped = PQfnumber(res, "attisdropped");
13175 0 : while (ncomments > 0)
13176 : {
13177 0 : const char *attname;
13178 :
13179 0 : attname = NULL;
13180 0 : for (i = 0; i < ntups; i++)
13181 : {
13182 0 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13183 0 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13184 : {
13185 0 : attname = PQgetvalue(res, i, i_attname);
13186 0 : break;
13187 : }
13188 0 : }
13189 0 : if (attname) /* just in case we don't find it */
13190 : {
13191 0 : const char *descr = comments->descr;
13192 :
13193 0 : resetPQExpBuffer(target);
13194 0 : appendPQExpBuffer(target, "COLUMN %s.",
13195 0 : fmtId(tyinfo->dobj.name));
13196 0 : appendPQExpBufferStr(target, fmtId(attname));
13197 :
13198 0 : resetPQExpBuffer(query);
13199 0 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13200 0 : fmtQualifiedDumpable(tyinfo));
13201 0 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13202 0 : appendStringLiteralAH(query, descr, fout);
13203 0 : appendPQExpBufferStr(query, ";\n");
13204 :
13205 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13206 0 : ARCHIVE_OPTS(.tag = target->data,
13207 : .namespace = tyinfo->dobj.namespace->dobj.name,
13208 : .owner = tyinfo->rolname,
13209 : .description = "COMMENT",
13210 : .section = SECTION_NONE,
13211 : .createStmt = query->data,
13212 : .deps = &(tyinfo->dobj.dumpId),
13213 : .nDeps = 1));
13214 0 : }
13215 :
13216 0 : comments++;
13217 0 : ncomments--;
13218 0 : }
13219 :
13220 0 : destroyPQExpBuffer(query);
13221 0 : destroyPQExpBuffer(target);
13222 0 : }
13223 :
13224 : /*
13225 : * dumpShellType
13226 : * writes out to fout the queries to create a shell type
13227 : *
13228 : * We dump a shell definition in advance of the I/O functions for the type.
13229 : */
13230 : static void
13231 0 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13232 : {
13233 0 : DumpOptions *dopt = fout->dopt;
13234 0 : PQExpBuffer q;
13235 :
13236 : /* Do nothing if not dumping schema */
13237 0 : if (!dopt->dumpSchema)
13238 0 : return;
13239 :
13240 0 : q = createPQExpBuffer();
13241 :
13242 : /*
13243 : * Note the lack of a DROP command for the shell type; any required DROP
13244 : * is driven off the base type entry, instead. This interacts with
13245 : * _printTocEntry()'s use of the presence of a DROP command to decide
13246 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13247 : * the shell type's owner immediately on creation; that should happen only
13248 : * after it's filled in, otherwise the backend complains.
13249 : */
13250 :
13251 0 : if (dopt->binary_upgrade)
13252 0 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13253 0 : stinfo->baseType->dobj.catId.oid,
13254 : false, false);
13255 :
13256 0 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13257 0 : fmtQualifiedDumpable(stinfo));
13258 :
13259 0 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13260 0 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13261 0 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13262 : .namespace = stinfo->dobj.namespace->dobj.name,
13263 : .owner = stinfo->baseType->rolname,
13264 : .description = "SHELL TYPE",
13265 : .section = SECTION_PRE_DATA,
13266 : .createStmt = q->data));
13267 :
13268 0 : destroyPQExpBuffer(q);
13269 0 : }
13270 :
13271 : /*
13272 : * dumpProcLang
13273 : * writes out to fout the queries to recreate a user-defined
13274 : * procedural language
13275 : */
13276 : static void
13277 0 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13278 : {
13279 0 : DumpOptions *dopt = fout->dopt;
13280 0 : PQExpBuffer defqry;
13281 0 : PQExpBuffer delqry;
13282 0 : bool useParams;
13283 0 : char *qlanname;
13284 0 : FuncInfo *funcInfo;
13285 0 : FuncInfo *inlineInfo = NULL;
13286 0 : FuncInfo *validatorInfo = NULL;
13287 :
13288 : /* Do nothing if not dumping schema */
13289 0 : if (!dopt->dumpSchema)
13290 0 : return;
13291 :
13292 : /*
13293 : * Try to find the support function(s). It is not an error if we don't
13294 : * find them --- if the functions are in the pg_catalog schema, as is
13295 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13296 : * we will emit a parameterless CREATE LANGUAGE command, which will
13297 : * require PL template knowledge in the backend to reload.)
13298 : */
13299 :
13300 0 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13301 0 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13302 0 : funcInfo = NULL; /* treat not-dumped same as not-found */
13303 :
13304 0 : if (OidIsValid(plang->laninline))
13305 : {
13306 0 : inlineInfo = findFuncByOid(plang->laninline);
13307 0 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13308 0 : inlineInfo = NULL;
13309 0 : }
13310 :
13311 0 : if (OidIsValid(plang->lanvalidator))
13312 : {
13313 0 : validatorInfo = findFuncByOid(plang->lanvalidator);
13314 0 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13315 0 : validatorInfo = NULL;
13316 0 : }
13317 :
13318 : /*
13319 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13320 : * parameters. Otherwise, we'll write a parameterless command, which will
13321 : * be interpreted as CREATE EXTENSION.
13322 : */
13323 0 : useParams = (funcInfo != NULL &&
13324 0 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13325 0 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13326 :
13327 0 : defqry = createPQExpBuffer();
13328 0 : delqry = createPQExpBuffer();
13329 :
13330 0 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13331 :
13332 0 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13333 0 : qlanname);
13334 :
13335 0 : if (useParams)
13336 : {
13337 0 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13338 0 : plang->lanpltrusted ? "TRUSTED " : "",
13339 0 : qlanname);
13340 0 : appendPQExpBuffer(defqry, " HANDLER %s",
13341 0 : fmtQualifiedDumpable(funcInfo));
13342 0 : if (OidIsValid(plang->laninline))
13343 0 : appendPQExpBuffer(defqry, " INLINE %s",
13344 0 : fmtQualifiedDumpable(inlineInfo));
13345 0 : if (OidIsValid(plang->lanvalidator))
13346 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13347 0 : fmtQualifiedDumpable(validatorInfo));
13348 0 : }
13349 : else
13350 : {
13351 : /*
13352 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13353 : * command will not fail if the language is preinstalled in the target
13354 : * database.
13355 : *
13356 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13357 : * EXISTS; perhaps we should emit that instead? But it might just add
13358 : * confusion.
13359 : */
13360 0 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13361 0 : qlanname);
13362 : }
13363 0 : appendPQExpBufferStr(defqry, ";\n");
13364 :
13365 0 : if (dopt->binary_upgrade)
13366 0 : binary_upgrade_extension_member(defqry, &plang->dobj,
13367 0 : "LANGUAGE", qlanname, NULL);
13368 :
13369 0 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13370 0 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13371 0 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13372 : .owner = plang->lanowner,
13373 : .description = "PROCEDURAL LANGUAGE",
13374 : .section = SECTION_PRE_DATA,
13375 : .createStmt = defqry->data,
13376 : .dropStmt = delqry->data,
13377 : ));
13378 :
13379 : /* Dump Proc Lang Comments and Security Labels */
13380 0 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13381 0 : dumpComment(fout, "LANGUAGE", qlanname,
13382 0 : NULL, plang->lanowner,
13383 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13384 :
13385 0 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13386 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13387 0 : NULL, plang->lanowner,
13388 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13389 :
13390 0 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13391 0 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13392 0 : qlanname, NULL, NULL,
13393 0 : NULL, plang->lanowner, &plang->dacl);
13394 :
13395 0 : free(qlanname);
13396 :
13397 0 : destroyPQExpBuffer(defqry);
13398 0 : destroyPQExpBuffer(delqry);
13399 0 : }
13400 :
13401 : /*
13402 : * format_function_arguments: generate function name and argument list
13403 : *
13404 : * This is used when we can rely on pg_get_function_arguments to format
13405 : * the argument list. Note, however, that pg_get_function_arguments
13406 : * does not special-case zero-argument aggregates.
13407 : */
13408 : static char *
13409 0 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13410 : {
13411 0 : PQExpBufferData fn;
13412 :
13413 0 : initPQExpBuffer(&fn);
13414 0 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13415 0 : if (is_agg && finfo->nargs == 0)
13416 0 : appendPQExpBufferStr(&fn, "(*)");
13417 : else
13418 0 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13419 0 : return fn.data;
13420 0 : }
13421 :
13422 : /*
13423 : * format_function_signature: generate function name and argument list
13424 : *
13425 : * Only a minimal list of input argument types is generated; this is
13426 : * sufficient to reference the function, but not to define it.
13427 : *
13428 : * If honor_quotes is false then the function name is never quoted.
13429 : * This is appropriate for use in TOC tags, but not in SQL commands.
13430 : */
13431 : static char *
13432 0 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13433 : {
13434 0 : PQExpBufferData fn;
13435 0 : int j;
13436 :
13437 0 : initPQExpBuffer(&fn);
13438 0 : if (honor_quotes)
13439 0 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13440 : else
13441 0 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13442 0 : for (j = 0; j < finfo->nargs; j++)
13443 : {
13444 0 : if (j > 0)
13445 0 : appendPQExpBufferStr(&fn, ", ");
13446 :
13447 0 : appendPQExpBufferStr(&fn,
13448 0 : getFormattedTypeName(fout, finfo->argtypes[j],
13449 : zeroIsError));
13450 0 : }
13451 0 : appendPQExpBufferChar(&fn, ')');
13452 0 : return fn.data;
13453 0 : }
13454 :
13455 :
13456 : /*
13457 : * dumpFunc:
13458 : * dump out one function
13459 : */
13460 : static void
13461 0 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13462 : {
13463 0 : DumpOptions *dopt = fout->dopt;
13464 0 : PQExpBuffer query;
13465 0 : PQExpBuffer q;
13466 0 : PQExpBuffer delqry;
13467 0 : PQExpBuffer asPart;
13468 0 : PGresult *res;
13469 0 : char *funcsig; /* identity signature */
13470 0 : char *funcfullsig = NULL; /* full signature */
13471 0 : char *funcsig_tag;
13472 0 : char *qual_funcsig;
13473 0 : char *proretset;
13474 0 : char *prosrc;
13475 0 : char *probin;
13476 0 : char *prosqlbody;
13477 0 : char *funcargs;
13478 0 : char *funciargs;
13479 0 : char *funcresult;
13480 0 : char *protrftypes;
13481 0 : char *prokind;
13482 0 : char *provolatile;
13483 0 : char *proisstrict;
13484 0 : char *prosecdef;
13485 0 : char *proleakproof;
13486 0 : char *proconfig;
13487 0 : char *procost;
13488 0 : char *prorows;
13489 0 : char *prosupport;
13490 0 : char *proparallel;
13491 0 : char *lanname;
13492 0 : char **configitems = NULL;
13493 0 : int nconfigitems = 0;
13494 0 : const char *keyword;
13495 :
13496 : /* Do nothing if not dumping schema */
13497 0 : if (!dopt->dumpSchema)
13498 0 : return;
13499 :
13500 0 : query = createPQExpBuffer();
13501 0 : q = createPQExpBuffer();
13502 0 : delqry = createPQExpBuffer();
13503 0 : asPart = createPQExpBuffer();
13504 :
13505 0 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13506 : {
13507 : /* Set up query for function-specific details */
13508 0 : appendPQExpBufferStr(query,
13509 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13510 :
13511 0 : appendPQExpBufferStr(query,
13512 : "SELECT\n"
13513 : "proretset,\n"
13514 : "prosrc,\n"
13515 : "probin,\n"
13516 : "provolatile,\n"
13517 : "proisstrict,\n"
13518 : "prosecdef,\n"
13519 : "lanname,\n"
13520 : "proconfig,\n"
13521 : "procost,\n"
13522 : "prorows,\n"
13523 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13524 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13525 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13526 : "proleakproof,\n");
13527 :
13528 0 : if (fout->remoteVersion >= 90500)
13529 0 : appendPQExpBufferStr(query,
13530 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13531 : else
13532 0 : appendPQExpBufferStr(query,
13533 : "NULL AS protrftypes,\n");
13534 :
13535 0 : if (fout->remoteVersion >= 90600)
13536 0 : appendPQExpBufferStr(query,
13537 : "proparallel,\n");
13538 : else
13539 0 : appendPQExpBufferStr(query,
13540 : "'u' AS proparallel,\n");
13541 :
13542 0 : if (fout->remoteVersion >= 110000)
13543 0 : appendPQExpBufferStr(query,
13544 : "prokind,\n");
13545 : else
13546 0 : appendPQExpBufferStr(query,
13547 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13548 :
13549 0 : if (fout->remoteVersion >= 120000)
13550 0 : appendPQExpBufferStr(query,
13551 : "prosupport,\n");
13552 : else
13553 0 : appendPQExpBufferStr(query,
13554 : "'-' AS prosupport,\n");
13555 :
13556 0 : if (fout->remoteVersion >= 140000)
13557 0 : appendPQExpBufferStr(query,
13558 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13559 : else
13560 0 : appendPQExpBufferStr(query,
13561 : "NULL AS prosqlbody\n");
13562 :
13563 0 : appendPQExpBufferStr(query,
13564 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13565 : "WHERE p.oid = $1 "
13566 : "AND l.oid = p.prolang");
13567 :
13568 0 : ExecuteSqlStatement(fout, query->data);
13569 :
13570 0 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13571 0 : }
13572 :
13573 0 : printfPQExpBuffer(query,
13574 : "EXECUTE dumpFunc('%u')",
13575 0 : finfo->dobj.catId.oid);
13576 :
13577 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13578 :
13579 0 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13580 0 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13581 : {
13582 0 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13583 0 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13584 0 : prosqlbody = NULL;
13585 0 : }
13586 : else
13587 : {
13588 0 : prosrc = NULL;
13589 0 : probin = NULL;
13590 0 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13591 : }
13592 0 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13593 0 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13594 0 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13595 0 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13596 0 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13597 0 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13598 0 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13599 0 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13600 0 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13601 0 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13602 0 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13603 0 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13604 0 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13605 0 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13606 0 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13607 :
13608 : /*
13609 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13610 : * is used.
13611 : */
13612 0 : if (prosqlbody)
13613 : {
13614 0 : appendPQExpBufferStr(asPart, prosqlbody);
13615 0 : }
13616 0 : else if (probin[0] != '\0')
13617 : {
13618 0 : appendPQExpBufferStr(asPart, "AS ");
13619 0 : appendStringLiteralAH(asPart, probin, fout);
13620 0 : if (prosrc[0] != '\0')
13621 : {
13622 0 : appendPQExpBufferStr(asPart, ", ");
13623 :
13624 : /*
13625 : * where we have bin, use dollar quoting if allowed and src
13626 : * contains quote or backslash; else use regular quoting.
13627 : */
13628 0 : if (dopt->disable_dollar_quoting ||
13629 0 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13630 0 : appendStringLiteralAH(asPart, prosrc, fout);
13631 : else
13632 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13633 0 : }
13634 0 : }
13635 : else
13636 : {
13637 0 : appendPQExpBufferStr(asPart, "AS ");
13638 : /* with no bin, dollar quote src unconditionally if allowed */
13639 0 : if (dopt->disable_dollar_quoting)
13640 0 : appendStringLiteralAH(asPart, prosrc, fout);
13641 : else
13642 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13643 : }
13644 :
13645 0 : if (*proconfig)
13646 : {
13647 0 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13648 0 : pg_fatal("could not parse %s array", "proconfig");
13649 0 : }
13650 : else
13651 : {
13652 0 : configitems = NULL;
13653 0 : nconfigitems = 0;
13654 : }
13655 :
13656 0 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13657 0 : funcsig = format_function_arguments(finfo, funciargs, false);
13658 :
13659 0 : funcsig_tag = format_function_signature(fout, finfo, false);
13660 :
13661 0 : qual_funcsig = psprintf("%s.%s",
13662 0 : fmtId(finfo->dobj.namespace->dobj.name),
13663 0 : funcsig);
13664 :
13665 0 : if (prokind[0] == PROKIND_PROCEDURE)
13666 0 : keyword = "PROCEDURE";
13667 : else
13668 0 : keyword = "FUNCTION"; /* works for window functions too */
13669 :
13670 0 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13671 0 : keyword, qual_funcsig);
13672 :
13673 0 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13674 0 : keyword,
13675 0 : fmtId(finfo->dobj.namespace->dobj.name),
13676 0 : funcfullsig ? funcfullsig :
13677 0 : funcsig);
13678 :
13679 0 : if (prokind[0] == PROKIND_PROCEDURE)
13680 : /* no result type to output */ ;
13681 0 : else if (funcresult)
13682 0 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13683 : else
13684 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13685 0 : (proretset[0] == 't') ? "SETOF " : "",
13686 0 : getFormattedTypeName(fout, finfo->prorettype,
13687 : zeroIsError));
13688 :
13689 0 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13690 :
13691 0 : if (*protrftypes)
13692 : {
13693 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13694 0 : int i;
13695 :
13696 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13697 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13698 0 : for (i = 0; typeids[i]; i++)
13699 : {
13700 0 : if (i != 0)
13701 0 : appendPQExpBufferStr(q, ", ");
13702 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13703 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13704 0 : }
13705 :
13706 0 : free(typeids);
13707 0 : }
13708 :
13709 0 : if (prokind[0] == PROKIND_WINDOW)
13710 0 : appendPQExpBufferStr(q, " WINDOW");
13711 :
13712 0 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13713 : {
13714 0 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13715 0 : appendPQExpBufferStr(q, " IMMUTABLE");
13716 0 : else if (provolatile[0] == PROVOLATILE_STABLE)
13717 0 : appendPQExpBufferStr(q, " STABLE");
13718 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13719 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13720 : finfo->dobj.name);
13721 0 : }
13722 :
13723 0 : if (proisstrict[0] == 't')
13724 0 : appendPQExpBufferStr(q, " STRICT");
13725 :
13726 0 : if (prosecdef[0] == 't')
13727 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13728 :
13729 0 : if (proleakproof[0] == 't')
13730 0 : appendPQExpBufferStr(q, " LEAKPROOF");
13731 :
13732 : /*
13733 : * COST and ROWS are emitted only if present and not default, so as not to
13734 : * break backwards-compatibility of the dump without need. Keep this code
13735 : * in sync with the defaults in functioncmds.c.
13736 : */
13737 0 : if (strcmp(procost, "0") != 0)
13738 : {
13739 0 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13740 : {
13741 : /* default cost is 1 */
13742 0 : if (strcmp(procost, "1") != 0)
13743 0 : appendPQExpBuffer(q, " COST %s", procost);
13744 0 : }
13745 : else
13746 : {
13747 : /* default cost is 100 */
13748 0 : if (strcmp(procost, "100") != 0)
13749 0 : appendPQExpBuffer(q, " COST %s", procost);
13750 : }
13751 0 : }
13752 0 : if (proretset[0] == 't' &&
13753 0 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13754 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13755 :
13756 0 : if (strcmp(prosupport, "-") != 0)
13757 : {
13758 : /* We rely on regprocout to provide quoting and qualification */
13759 0 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13760 0 : }
13761 :
13762 0 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13763 : {
13764 0 : if (proparallel[0] == PROPARALLEL_SAFE)
13765 0 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13766 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13767 0 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13768 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13769 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13770 : finfo->dobj.name);
13771 0 : }
13772 :
13773 0 : for (int i = 0; i < nconfigitems; i++)
13774 : {
13775 : /* we feel free to scribble on configitems[] here */
13776 0 : char *configitem = configitems[i];
13777 0 : char *pos;
13778 :
13779 0 : pos = strchr(configitem, '=');
13780 0 : if (pos == NULL)
13781 0 : continue;
13782 0 : *pos++ = '\0';
13783 0 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13784 :
13785 : /*
13786 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13787 : * by flatten_set_variable_args() before they were put into the
13788 : * proconfig array. However, because the quoting rules used there
13789 : * aren't exactly like SQL's, we have to break the list value apart
13790 : * and then quote the elements as string literals. (The elements may
13791 : * be double-quoted as-is, but we can't just feed them to the SQL
13792 : * parser; it would do the wrong thing with elements that are
13793 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13794 : * case for empty lists.
13795 : *
13796 : * Variables that are not so marked should just be emitted as simple
13797 : * string literals. If the variable is not known to
13798 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13799 : * to use GUC_LIST_QUOTE for extension variables.
13800 : */
13801 0 : if (variable_is_guc_list_quote(configitem))
13802 : {
13803 0 : char **namelist;
13804 0 : char **nameptr;
13805 :
13806 : /* Parse string into list of identifiers */
13807 : /* this shouldn't fail really */
13808 0 : if (SplitGUCList(pos, ',', &namelist))
13809 : {
13810 : /* Special case: represent an empty list as NULL */
13811 0 : if (*namelist == NULL)
13812 0 : appendPQExpBufferStr(q, "NULL");
13813 0 : for (nameptr = namelist; *nameptr; nameptr++)
13814 : {
13815 0 : if (nameptr != namelist)
13816 0 : appendPQExpBufferStr(q, ", ");
13817 0 : appendStringLiteralAH(q, *nameptr, fout);
13818 0 : }
13819 0 : }
13820 0 : pg_free(namelist);
13821 0 : }
13822 : else
13823 0 : appendStringLiteralAH(q, pos, fout);
13824 0 : }
13825 :
13826 0 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13827 :
13828 0 : append_depends_on_extension(fout, q, &finfo->dobj,
13829 0 : "pg_catalog.pg_proc", keyword,
13830 0 : qual_funcsig);
13831 :
13832 0 : if (dopt->binary_upgrade)
13833 0 : binary_upgrade_extension_member(q, &finfo->dobj,
13834 0 : keyword, funcsig,
13835 0 : finfo->dobj.namespace->dobj.name);
13836 :
13837 0 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13838 0 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13839 0 : ARCHIVE_OPTS(.tag = funcsig_tag,
13840 : .namespace = finfo->dobj.namespace->dobj.name,
13841 : .owner = finfo->rolname,
13842 : .description = keyword,
13843 : .section = finfo->postponed_def ?
13844 : SECTION_POST_DATA : SECTION_PRE_DATA,
13845 : .createStmt = q->data,
13846 : .dropStmt = delqry->data));
13847 :
13848 : /* Dump Function Comments and Security Labels */
13849 0 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13850 0 : dumpComment(fout, keyword, funcsig,
13851 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13852 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13853 :
13854 0 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13855 0 : dumpSecLabel(fout, keyword, funcsig,
13856 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13857 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13858 :
13859 0 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13860 0 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13861 0 : funcsig, NULL,
13862 0 : finfo->dobj.namespace->dobj.name,
13863 0 : NULL, finfo->rolname, &finfo->dacl);
13864 :
13865 0 : PQclear(res);
13866 :
13867 0 : destroyPQExpBuffer(query);
13868 0 : destroyPQExpBuffer(q);
13869 0 : destroyPQExpBuffer(delqry);
13870 0 : destroyPQExpBuffer(asPart);
13871 0 : free(funcsig);
13872 0 : free(funcfullsig);
13873 0 : free(funcsig_tag);
13874 0 : free(qual_funcsig);
13875 0 : free(configitems);
13876 0 : }
13877 :
13878 :
13879 : /*
13880 : * Dump a user-defined cast
13881 : */
13882 : static void
13883 0 : dumpCast(Archive *fout, const CastInfo *cast)
13884 : {
13885 0 : DumpOptions *dopt = fout->dopt;
13886 0 : PQExpBuffer defqry;
13887 0 : PQExpBuffer delqry;
13888 0 : PQExpBuffer labelq;
13889 0 : PQExpBuffer castargs;
13890 0 : FuncInfo *funcInfo = NULL;
13891 0 : const char *sourceType;
13892 0 : const char *targetType;
13893 :
13894 : /* Do nothing if not dumping schema */
13895 0 : if (!dopt->dumpSchema)
13896 0 : return;
13897 :
13898 : /* Cannot dump if we don't have the cast function's info */
13899 0 : if (OidIsValid(cast->castfunc))
13900 : {
13901 0 : funcInfo = findFuncByOid(cast->castfunc);
13902 0 : if (funcInfo == NULL)
13903 0 : pg_fatal("could not find function definition for function with OID %u",
13904 : cast->castfunc);
13905 0 : }
13906 :
13907 0 : defqry = createPQExpBuffer();
13908 0 : delqry = createPQExpBuffer();
13909 0 : labelq = createPQExpBuffer();
13910 0 : castargs = createPQExpBuffer();
13911 :
13912 0 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13913 0 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13914 0 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13915 0 : sourceType, targetType);
13916 :
13917 0 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13918 0 : sourceType, targetType);
13919 :
13920 0 : switch (cast->castmethod)
13921 : {
13922 : case COERCION_METHOD_BINARY:
13923 0 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13924 0 : break;
13925 : case COERCION_METHOD_INOUT:
13926 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13927 0 : break;
13928 : case COERCION_METHOD_FUNCTION:
13929 0 : if (funcInfo)
13930 : {
13931 0 : char *fsig = format_function_signature(fout, funcInfo, true);
13932 :
13933 : /*
13934 : * Always qualify the function name (format_function_signature
13935 : * won't qualify it).
13936 : */
13937 0 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13938 0 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13939 0 : free(fsig);
13940 0 : }
13941 : else
13942 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13943 0 : break;
13944 : default:
13945 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13946 0 : }
13947 :
13948 0 : if (cast->castcontext == 'a')
13949 0 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13950 0 : else if (cast->castcontext == 'i')
13951 0 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13952 0 : appendPQExpBufferStr(defqry, ";\n");
13953 :
13954 0 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13955 0 : sourceType, targetType);
13956 :
13957 0 : appendPQExpBuffer(castargs, "(%s AS %s)",
13958 0 : sourceType, targetType);
13959 :
13960 0 : if (dopt->binary_upgrade)
13961 0 : binary_upgrade_extension_member(defqry, &cast->dobj,
13962 0 : "CAST", castargs->data, NULL);
13963 :
13964 0 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13965 0 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13966 0 : ARCHIVE_OPTS(.tag = labelq->data,
13967 : .description = "CAST",
13968 : .section = SECTION_PRE_DATA,
13969 : .createStmt = defqry->data,
13970 : .dropStmt = delqry->data));
13971 :
13972 : /* Dump Cast Comments */
13973 0 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13974 0 : dumpComment(fout, "CAST", castargs->data,
13975 : NULL, "",
13976 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13977 :
13978 0 : destroyPQExpBuffer(defqry);
13979 0 : destroyPQExpBuffer(delqry);
13980 0 : destroyPQExpBuffer(labelq);
13981 0 : destroyPQExpBuffer(castargs);
13982 0 : }
13983 :
13984 : /*
13985 : * Dump a transform
13986 : */
13987 : static void
13988 0 : dumpTransform(Archive *fout, const TransformInfo *transform)
13989 : {
13990 0 : DumpOptions *dopt = fout->dopt;
13991 0 : PQExpBuffer defqry;
13992 0 : PQExpBuffer delqry;
13993 0 : PQExpBuffer labelq;
13994 0 : PQExpBuffer transformargs;
13995 0 : FuncInfo *fromsqlFuncInfo = NULL;
13996 0 : FuncInfo *tosqlFuncInfo = NULL;
13997 0 : char *lanname;
13998 0 : const char *transformType;
13999 :
14000 : /* Do nothing if not dumping schema */
14001 0 : if (!dopt->dumpSchema)
14002 0 : return;
14003 :
14004 : /* Cannot dump if we don't have the transform functions' info */
14005 0 : if (OidIsValid(transform->trffromsql))
14006 : {
14007 0 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
14008 0 : if (fromsqlFuncInfo == NULL)
14009 0 : pg_fatal("could not find function definition for function with OID %u",
14010 : transform->trffromsql);
14011 0 : }
14012 0 : if (OidIsValid(transform->trftosql))
14013 : {
14014 0 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
14015 0 : if (tosqlFuncInfo == NULL)
14016 0 : pg_fatal("could not find function definition for function with OID %u",
14017 : transform->trftosql);
14018 0 : }
14019 :
14020 0 : defqry = createPQExpBuffer();
14021 0 : delqry = createPQExpBuffer();
14022 0 : labelq = createPQExpBuffer();
14023 0 : transformargs = createPQExpBuffer();
14024 :
14025 0 : lanname = get_language_name(fout, transform->trflang);
14026 0 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14027 :
14028 0 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14029 0 : transformType, lanname);
14030 :
14031 0 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14032 0 : transformType, lanname);
14033 :
14034 0 : if (!transform->trffromsql && !transform->trftosql)
14035 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14036 :
14037 0 : if (transform->trffromsql)
14038 : {
14039 0 : if (fromsqlFuncInfo)
14040 : {
14041 0 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14042 :
14043 : /*
14044 : * Always qualify the function name (format_function_signature
14045 : * won't qualify it).
14046 : */
14047 0 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14048 0 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14049 0 : free(fsig);
14050 0 : }
14051 : else
14052 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14053 0 : }
14054 :
14055 0 : if (transform->trftosql)
14056 : {
14057 0 : if (transform->trffromsql)
14058 0 : appendPQExpBufferStr(defqry, ", ");
14059 :
14060 0 : if (tosqlFuncInfo)
14061 : {
14062 0 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14063 :
14064 : /*
14065 : * Always qualify the function name (format_function_signature
14066 : * won't qualify it).
14067 : */
14068 0 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14069 0 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14070 0 : free(fsig);
14071 0 : }
14072 : else
14073 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14074 0 : }
14075 :
14076 0 : appendPQExpBufferStr(defqry, ");\n");
14077 :
14078 0 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14079 0 : transformType, lanname);
14080 :
14081 0 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14082 0 : transformType, lanname);
14083 :
14084 0 : if (dopt->binary_upgrade)
14085 0 : binary_upgrade_extension_member(defqry, &transform->dobj,
14086 0 : "TRANSFORM", transformargs->data, NULL);
14087 :
14088 0 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14089 0 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14090 0 : ARCHIVE_OPTS(.tag = labelq->data,
14091 : .description = "TRANSFORM",
14092 : .section = SECTION_PRE_DATA,
14093 : .createStmt = defqry->data,
14094 : .dropStmt = delqry->data,
14095 : .deps = transform->dobj.dependencies,
14096 : .nDeps = transform->dobj.nDeps));
14097 :
14098 : /* Dump Transform Comments */
14099 0 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14100 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14101 : NULL, "",
14102 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14103 :
14104 0 : free(lanname);
14105 0 : destroyPQExpBuffer(defqry);
14106 0 : destroyPQExpBuffer(delqry);
14107 0 : destroyPQExpBuffer(labelq);
14108 0 : destroyPQExpBuffer(transformargs);
14109 0 : }
14110 :
14111 :
14112 : /*
14113 : * dumpOpr
14114 : * write out a single operator definition
14115 : */
14116 : static void
14117 0 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14118 : {
14119 0 : DumpOptions *dopt = fout->dopt;
14120 0 : PQExpBuffer query;
14121 0 : PQExpBuffer q;
14122 0 : PQExpBuffer delq;
14123 0 : PQExpBuffer oprid;
14124 0 : PQExpBuffer details;
14125 0 : PGresult *res;
14126 0 : int i_oprkind;
14127 0 : int i_oprcode;
14128 0 : int i_oprleft;
14129 0 : int i_oprright;
14130 0 : int i_oprcom;
14131 0 : int i_oprnegate;
14132 0 : int i_oprrest;
14133 0 : int i_oprjoin;
14134 0 : int i_oprcanmerge;
14135 0 : int i_oprcanhash;
14136 0 : char *oprkind;
14137 0 : char *oprcode;
14138 0 : char *oprleft;
14139 0 : char *oprright;
14140 0 : char *oprcom;
14141 0 : char *oprnegate;
14142 0 : char *oprrest;
14143 0 : char *oprjoin;
14144 0 : char *oprcanmerge;
14145 0 : char *oprcanhash;
14146 0 : char *oprregproc;
14147 0 : char *oprref;
14148 :
14149 : /* Do nothing if not dumping schema */
14150 0 : if (!dopt->dumpSchema)
14151 0 : return;
14152 :
14153 : /*
14154 : * some operators are invalid because they were the result of user
14155 : * defining operators before commutators exist
14156 : */
14157 0 : if (!OidIsValid(oprinfo->oprcode))
14158 0 : return;
14159 :
14160 0 : query = createPQExpBuffer();
14161 0 : q = createPQExpBuffer();
14162 0 : delq = createPQExpBuffer();
14163 0 : oprid = createPQExpBuffer();
14164 0 : details = createPQExpBuffer();
14165 :
14166 0 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14167 : {
14168 : /* Set up query for operator-specific details */
14169 0 : appendPQExpBufferStr(query,
14170 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14171 : "SELECT oprkind, "
14172 : "oprcode::pg_catalog.regprocedure, "
14173 : "oprleft::pg_catalog.regtype, "
14174 : "oprright::pg_catalog.regtype, "
14175 : "oprcom, "
14176 : "oprnegate, "
14177 : "oprrest::pg_catalog.regprocedure, "
14178 : "oprjoin::pg_catalog.regprocedure, "
14179 : "oprcanmerge, oprcanhash "
14180 : "FROM pg_catalog.pg_operator "
14181 : "WHERE oid = $1");
14182 :
14183 0 : ExecuteSqlStatement(fout, query->data);
14184 :
14185 0 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14186 0 : }
14187 :
14188 0 : printfPQExpBuffer(query,
14189 : "EXECUTE dumpOpr('%u')",
14190 0 : oprinfo->dobj.catId.oid);
14191 :
14192 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14193 :
14194 0 : i_oprkind = PQfnumber(res, "oprkind");
14195 0 : i_oprcode = PQfnumber(res, "oprcode");
14196 0 : i_oprleft = PQfnumber(res, "oprleft");
14197 0 : i_oprright = PQfnumber(res, "oprright");
14198 0 : i_oprcom = PQfnumber(res, "oprcom");
14199 0 : i_oprnegate = PQfnumber(res, "oprnegate");
14200 0 : i_oprrest = PQfnumber(res, "oprrest");
14201 0 : i_oprjoin = PQfnumber(res, "oprjoin");
14202 0 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14203 0 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14204 :
14205 0 : oprkind = PQgetvalue(res, 0, i_oprkind);
14206 0 : oprcode = PQgetvalue(res, 0, i_oprcode);
14207 0 : oprleft = PQgetvalue(res, 0, i_oprleft);
14208 0 : oprright = PQgetvalue(res, 0, i_oprright);
14209 0 : oprcom = PQgetvalue(res, 0, i_oprcom);
14210 0 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14211 0 : oprrest = PQgetvalue(res, 0, i_oprrest);
14212 0 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14213 0 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14214 0 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14215 :
14216 : /* In PG14 upwards postfix operator support does not exist anymore. */
14217 0 : if (strcmp(oprkind, "r") == 0)
14218 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14219 : oprcode);
14220 :
14221 0 : oprregproc = convertRegProcReference(oprcode);
14222 0 : if (oprregproc)
14223 : {
14224 0 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14225 0 : free(oprregproc);
14226 0 : }
14227 :
14228 0 : appendPQExpBuffer(oprid, "%s (",
14229 0 : oprinfo->dobj.name);
14230 :
14231 : /*
14232 : * right unary means there's a left arg and left unary means there's a
14233 : * right arg. (Although the "r" case is dead code for PG14 and later,
14234 : * continue to support it in case we're dumping from an old server.)
14235 : */
14236 0 : if (strcmp(oprkind, "r") == 0 ||
14237 0 : strcmp(oprkind, "b") == 0)
14238 : {
14239 0 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14240 0 : appendPQExpBufferStr(oprid, oprleft);
14241 0 : }
14242 : else
14243 0 : appendPQExpBufferStr(oprid, "NONE");
14244 :
14245 0 : if (strcmp(oprkind, "l") == 0 ||
14246 0 : strcmp(oprkind, "b") == 0)
14247 : {
14248 0 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14249 0 : appendPQExpBuffer(oprid, ", %s)", oprright);
14250 0 : }
14251 : else
14252 0 : appendPQExpBufferStr(oprid, ", NONE)");
14253 :
14254 0 : oprref = getFormattedOperatorName(oprcom);
14255 0 : if (oprref)
14256 : {
14257 0 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14258 0 : free(oprref);
14259 0 : }
14260 :
14261 0 : oprref = getFormattedOperatorName(oprnegate);
14262 0 : if (oprref)
14263 : {
14264 0 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14265 0 : free(oprref);
14266 0 : }
14267 :
14268 0 : if (strcmp(oprcanmerge, "t") == 0)
14269 0 : appendPQExpBufferStr(details, ",\n MERGES");
14270 :
14271 0 : if (strcmp(oprcanhash, "t") == 0)
14272 0 : appendPQExpBufferStr(details, ",\n HASHES");
14273 :
14274 0 : oprregproc = convertRegProcReference(oprrest);
14275 0 : if (oprregproc)
14276 : {
14277 0 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14278 0 : free(oprregproc);
14279 0 : }
14280 :
14281 0 : oprregproc = convertRegProcReference(oprjoin);
14282 0 : if (oprregproc)
14283 : {
14284 0 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14285 0 : free(oprregproc);
14286 0 : }
14287 :
14288 0 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14289 0 : fmtId(oprinfo->dobj.namespace->dobj.name),
14290 0 : oprid->data);
14291 :
14292 0 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14293 0 : fmtId(oprinfo->dobj.namespace->dobj.name),
14294 0 : oprinfo->dobj.name, details->data);
14295 :
14296 0 : if (dopt->binary_upgrade)
14297 0 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14298 0 : "OPERATOR", oprid->data,
14299 0 : oprinfo->dobj.namespace->dobj.name);
14300 :
14301 0 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14302 0 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14303 0 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14304 : .namespace = oprinfo->dobj.namespace->dobj.name,
14305 : .owner = oprinfo->rolname,
14306 : .description = "OPERATOR",
14307 : .section = SECTION_PRE_DATA,
14308 : .createStmt = q->data,
14309 : .dropStmt = delq->data));
14310 :
14311 : /* Dump Operator Comments */
14312 0 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14313 0 : dumpComment(fout, "OPERATOR", oprid->data,
14314 0 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14315 0 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14316 :
14317 0 : PQclear(res);
14318 :
14319 0 : destroyPQExpBuffer(query);
14320 0 : destroyPQExpBuffer(q);
14321 0 : destroyPQExpBuffer(delq);
14322 0 : destroyPQExpBuffer(oprid);
14323 0 : destroyPQExpBuffer(details);
14324 0 : }
14325 :
14326 : /*
14327 : * Convert a function reference obtained from pg_operator
14328 : *
14329 : * Returns allocated string of what to print, or NULL if function references
14330 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14331 : *
14332 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14333 : * part.
14334 : */
14335 : static char *
14336 0 : convertRegProcReference(const char *proc)
14337 : {
14338 0 : char *name;
14339 0 : char *paren;
14340 0 : bool inquote;
14341 :
14342 : /* In all cases "-" means a null reference */
14343 0 : if (strcmp(proc, "-") == 0)
14344 0 : return NULL;
14345 :
14346 0 : name = pg_strdup(proc);
14347 : /* find non-double-quoted left paren */
14348 0 : inquote = false;
14349 0 : for (paren = name; *paren; paren++)
14350 : {
14351 0 : if (*paren == '(' && !inquote)
14352 : {
14353 0 : *paren = '\0';
14354 0 : break;
14355 : }
14356 0 : if (*paren == '"')
14357 0 : inquote = !inquote;
14358 0 : }
14359 0 : return name;
14360 0 : }
14361 :
14362 : /*
14363 : * getFormattedOperatorName - retrieve the operator name for the
14364 : * given operator OID (presented in string form).
14365 : *
14366 : * Returns an allocated string, or NULL if the given OID is invalid.
14367 : * Caller is responsible for free'ing result string.
14368 : *
14369 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14370 : * useful in commands where the operator's argument types can be inferred from
14371 : * context. We always schema-qualify the name, though. The predecessor to
14372 : * this code tried to skip the schema qualification if possible, but that led
14373 : * to wrong results in corner cases, such as if an operator and its negator
14374 : * are in different schemas.
14375 : */
14376 : static char *
14377 0 : getFormattedOperatorName(const char *oproid)
14378 : {
14379 0 : OprInfo *oprInfo;
14380 :
14381 : /* In all cases "0" means a null reference */
14382 0 : if (strcmp(oproid, "0") == 0)
14383 0 : return NULL;
14384 :
14385 0 : oprInfo = findOprByOid(atooid(oproid));
14386 0 : if (oprInfo == NULL)
14387 : {
14388 0 : pg_log_warning("could not find operator with OID %s",
14389 : oproid);
14390 0 : return NULL;
14391 : }
14392 :
14393 0 : return psprintf("OPERATOR(%s.%s)",
14394 0 : fmtId(oprInfo->dobj.namespace->dobj.name),
14395 0 : oprInfo->dobj.name);
14396 0 : }
14397 :
14398 : /*
14399 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14400 : *
14401 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14402 : * argument lists of these functions are predetermined. Note that the
14403 : * caller should ensure we are in the proper schema, because the results
14404 : * are search path dependent!
14405 : */
14406 : static char *
14407 0 : convertTSFunction(Archive *fout, Oid funcOid)
14408 : {
14409 0 : char *result;
14410 0 : char query[128];
14411 0 : PGresult *res;
14412 :
14413 0 : snprintf(query, sizeof(query),
14414 0 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14415 0 : res = ExecuteSqlQueryForSingleRow(fout, query);
14416 :
14417 0 : result = pg_strdup(PQgetvalue(res, 0, 0));
14418 :
14419 0 : PQclear(res);
14420 :
14421 0 : return result;
14422 0 : }
14423 :
14424 : /*
14425 : * dumpAccessMethod
14426 : * write out a single access method definition
14427 : */
14428 : static void
14429 0 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14430 : {
14431 0 : DumpOptions *dopt = fout->dopt;
14432 0 : PQExpBuffer q;
14433 0 : PQExpBuffer delq;
14434 0 : char *qamname;
14435 :
14436 : /* Do nothing if not dumping schema */
14437 0 : if (!dopt->dumpSchema)
14438 0 : return;
14439 :
14440 0 : q = createPQExpBuffer();
14441 0 : delq = createPQExpBuffer();
14442 :
14443 0 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14444 :
14445 0 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14446 :
14447 0 : switch (aminfo->amtype)
14448 : {
14449 : case AMTYPE_INDEX:
14450 0 : appendPQExpBufferStr(q, "TYPE INDEX ");
14451 0 : break;
14452 : case AMTYPE_TABLE:
14453 0 : appendPQExpBufferStr(q, "TYPE TABLE ");
14454 0 : break;
14455 : default:
14456 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14457 : aminfo->amtype, qamname);
14458 0 : destroyPQExpBuffer(q);
14459 0 : destroyPQExpBuffer(delq);
14460 0 : free(qamname);
14461 0 : return;
14462 : }
14463 :
14464 0 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14465 :
14466 0 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14467 0 : qamname);
14468 :
14469 0 : if (dopt->binary_upgrade)
14470 0 : binary_upgrade_extension_member(q, &aminfo->dobj,
14471 0 : "ACCESS METHOD", qamname, NULL);
14472 :
14473 0 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14474 0 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14475 0 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14476 : .description = "ACCESS METHOD",
14477 : .section = SECTION_PRE_DATA,
14478 : .createStmt = q->data,
14479 : .dropStmt = delq->data));
14480 :
14481 : /* Dump Access Method Comments */
14482 0 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14483 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14484 : NULL, "",
14485 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14486 :
14487 0 : destroyPQExpBuffer(q);
14488 0 : destroyPQExpBuffer(delq);
14489 0 : free(qamname);
14490 0 : }
14491 :
14492 : /*
14493 : * dumpOpclass
14494 : * write out a single operator class definition
14495 : */
14496 : static void
14497 0 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14498 : {
14499 0 : DumpOptions *dopt = fout->dopt;
14500 0 : PQExpBuffer query;
14501 0 : PQExpBuffer q;
14502 0 : PQExpBuffer delq;
14503 0 : PQExpBuffer nameusing;
14504 0 : PGresult *res;
14505 0 : int ntups;
14506 0 : int i_opcintype;
14507 0 : int i_opckeytype;
14508 0 : int i_opcdefault;
14509 0 : int i_opcfamily;
14510 0 : int i_opcfamilyname;
14511 0 : int i_opcfamilynsp;
14512 0 : int i_amname;
14513 0 : int i_amopstrategy;
14514 0 : int i_amopopr;
14515 0 : int i_sortfamily;
14516 0 : int i_sortfamilynsp;
14517 0 : int i_amprocnum;
14518 0 : int i_amproc;
14519 0 : int i_amproclefttype;
14520 0 : int i_amprocrighttype;
14521 0 : char *opcintype;
14522 0 : char *opckeytype;
14523 0 : char *opcdefault;
14524 0 : char *opcfamily;
14525 0 : char *opcfamilyname;
14526 0 : char *opcfamilynsp;
14527 0 : char *amname;
14528 0 : char *amopstrategy;
14529 0 : char *amopopr;
14530 0 : char *sortfamily;
14531 0 : char *sortfamilynsp;
14532 0 : char *amprocnum;
14533 0 : char *amproc;
14534 0 : char *amproclefttype;
14535 0 : char *amprocrighttype;
14536 0 : bool needComma;
14537 0 : int i;
14538 :
14539 : /* Do nothing if not dumping schema */
14540 0 : if (!dopt->dumpSchema)
14541 0 : return;
14542 :
14543 0 : query = createPQExpBuffer();
14544 0 : q = createPQExpBuffer();
14545 0 : delq = createPQExpBuffer();
14546 0 : nameusing = createPQExpBuffer();
14547 :
14548 : /* Get additional fields from the pg_opclass row */
14549 0 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14550 : "opckeytype::pg_catalog.regtype, "
14551 : "opcdefault, opcfamily, "
14552 : "opfname AS opcfamilyname, "
14553 : "nspname AS opcfamilynsp, "
14554 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14555 : "FROM pg_catalog.pg_opclass c "
14556 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14557 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14558 : "WHERE c.oid = '%u'::pg_catalog.oid",
14559 0 : opcinfo->dobj.catId.oid);
14560 :
14561 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14562 :
14563 0 : i_opcintype = PQfnumber(res, "opcintype");
14564 0 : i_opckeytype = PQfnumber(res, "opckeytype");
14565 0 : i_opcdefault = PQfnumber(res, "opcdefault");
14566 0 : i_opcfamily = PQfnumber(res, "opcfamily");
14567 0 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14568 0 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14569 0 : i_amname = PQfnumber(res, "amname");
14570 :
14571 : /* opcintype may still be needed after we PQclear res */
14572 0 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14573 0 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14574 0 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14575 : /* opcfamily will still be needed after we PQclear res */
14576 0 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14577 0 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14578 0 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14579 : /* amname will still be needed after we PQclear res */
14580 0 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14581 :
14582 0 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14583 0 : fmtQualifiedDumpable(opcinfo));
14584 0 : appendPQExpBuffer(delq, " USING %s;\n",
14585 0 : fmtId(amname));
14586 :
14587 : /* Build the fixed portion of the CREATE command */
14588 0 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14589 0 : fmtQualifiedDumpable(opcinfo));
14590 0 : if (strcmp(opcdefault, "t") == 0)
14591 0 : appendPQExpBufferStr(q, "DEFAULT ");
14592 0 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14593 0 : opcintype,
14594 0 : fmtId(amname));
14595 0 : if (strlen(opcfamilyname) > 0)
14596 : {
14597 0 : appendPQExpBufferStr(q, " FAMILY ");
14598 0 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14599 0 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14600 0 : }
14601 0 : appendPQExpBufferStr(q, " AS\n ");
14602 :
14603 0 : needComma = false;
14604 :
14605 0 : if (strcmp(opckeytype, "-") != 0)
14606 : {
14607 0 : appendPQExpBuffer(q, "STORAGE %s",
14608 0 : opckeytype);
14609 0 : needComma = true;
14610 0 : }
14611 :
14612 0 : PQclear(res);
14613 :
14614 : /*
14615 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14616 : *
14617 : * Print only those opfamily members that are tied to the opclass by
14618 : * pg_depend entries.
14619 : */
14620 0 : resetPQExpBuffer(query);
14621 0 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14622 : "amopopr::pg_catalog.regoperator, "
14623 : "opfname AS sortfamily, "
14624 : "nspname AS sortfamilynsp "
14625 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14626 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14627 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14628 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14629 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14630 : "AND refobjid = '%u'::pg_catalog.oid "
14631 : "AND amopfamily = '%s'::pg_catalog.oid "
14632 : "ORDER BY amopstrategy",
14633 0 : opcinfo->dobj.catId.oid,
14634 0 : opcfamily);
14635 :
14636 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14637 :
14638 0 : ntups = PQntuples(res);
14639 :
14640 0 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14641 0 : i_amopopr = PQfnumber(res, "amopopr");
14642 0 : i_sortfamily = PQfnumber(res, "sortfamily");
14643 0 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14644 :
14645 0 : for (i = 0; i < ntups; i++)
14646 : {
14647 0 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14648 0 : amopopr = PQgetvalue(res, i, i_amopopr);
14649 0 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14650 0 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14651 :
14652 0 : if (needComma)
14653 0 : appendPQExpBufferStr(q, " ,\n ");
14654 :
14655 0 : appendPQExpBuffer(q, "OPERATOR %s %s",
14656 0 : amopstrategy, amopopr);
14657 :
14658 0 : if (strlen(sortfamily) > 0)
14659 : {
14660 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14661 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14662 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14663 0 : }
14664 :
14665 0 : needComma = true;
14666 0 : }
14667 :
14668 0 : PQclear(res);
14669 :
14670 : /*
14671 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14672 : *
14673 : * Print only those opfamily members that are tied to the opclass by
14674 : * pg_depend entries.
14675 : *
14676 : * We print the amproclefttype/amprocrighttype even though in most cases
14677 : * the backend could deduce the right values, because of the corner case
14678 : * of a btree sort support function for a cross-type comparison.
14679 : */
14680 0 : resetPQExpBuffer(query);
14681 :
14682 0 : appendPQExpBuffer(query, "SELECT amprocnum, "
14683 : "amproc::pg_catalog.regprocedure, "
14684 : "amproclefttype::pg_catalog.regtype, "
14685 : "amprocrighttype::pg_catalog.regtype "
14686 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14687 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14688 : "AND refobjid = '%u'::pg_catalog.oid "
14689 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14690 : "AND objid = ap.oid "
14691 : "ORDER BY amprocnum",
14692 0 : opcinfo->dobj.catId.oid);
14693 :
14694 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14695 :
14696 0 : ntups = PQntuples(res);
14697 :
14698 0 : i_amprocnum = PQfnumber(res, "amprocnum");
14699 0 : i_amproc = PQfnumber(res, "amproc");
14700 0 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14701 0 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14702 :
14703 0 : for (i = 0; i < ntups; i++)
14704 : {
14705 0 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14706 0 : amproc = PQgetvalue(res, i, i_amproc);
14707 0 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14708 0 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14709 :
14710 0 : if (needComma)
14711 0 : appendPQExpBufferStr(q, " ,\n ");
14712 :
14713 0 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14714 :
14715 0 : if (*amproclefttype && *amprocrighttype)
14716 0 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14717 :
14718 0 : appendPQExpBuffer(q, " %s", amproc);
14719 :
14720 0 : needComma = true;
14721 0 : }
14722 :
14723 0 : PQclear(res);
14724 :
14725 : /*
14726 : * If needComma is still false it means we haven't added anything after
14727 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14728 : * clause with the same datatype. This isn't sanctioned by the
14729 : * documentation, but actually DefineOpClass will treat it as a no-op.
14730 : */
14731 0 : if (!needComma)
14732 0 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14733 :
14734 0 : appendPQExpBufferStr(q, ";\n");
14735 :
14736 0 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14737 0 : appendPQExpBuffer(nameusing, " USING %s",
14738 0 : fmtId(amname));
14739 :
14740 0 : if (dopt->binary_upgrade)
14741 0 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14742 0 : "OPERATOR CLASS", nameusing->data,
14743 0 : opcinfo->dobj.namespace->dobj.name);
14744 :
14745 0 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14746 0 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14747 0 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14748 : .namespace = opcinfo->dobj.namespace->dobj.name,
14749 : .owner = opcinfo->rolname,
14750 : .description = "OPERATOR CLASS",
14751 : .section = SECTION_PRE_DATA,
14752 : .createStmt = q->data,
14753 : .dropStmt = delq->data));
14754 :
14755 : /* Dump Operator Class Comments */
14756 0 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14757 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14758 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14759 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14760 :
14761 0 : free(opcintype);
14762 0 : free(opcfamily);
14763 0 : free(amname);
14764 0 : destroyPQExpBuffer(query);
14765 0 : destroyPQExpBuffer(q);
14766 0 : destroyPQExpBuffer(delq);
14767 0 : destroyPQExpBuffer(nameusing);
14768 0 : }
14769 :
14770 : /*
14771 : * dumpOpfamily
14772 : * write out a single operator family definition
14773 : *
14774 : * Note: this also dumps any "loose" operator members that aren't bound to a
14775 : * specific opclass within the opfamily.
14776 : */
14777 : static void
14778 0 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14779 : {
14780 0 : DumpOptions *dopt = fout->dopt;
14781 0 : PQExpBuffer query;
14782 0 : PQExpBuffer q;
14783 0 : PQExpBuffer delq;
14784 0 : PQExpBuffer nameusing;
14785 0 : PGresult *res;
14786 0 : PGresult *res_ops;
14787 0 : PGresult *res_procs;
14788 0 : int ntups;
14789 0 : int i_amname;
14790 0 : int i_amopstrategy;
14791 0 : int i_amopopr;
14792 0 : int i_sortfamily;
14793 0 : int i_sortfamilynsp;
14794 0 : int i_amprocnum;
14795 0 : int i_amproc;
14796 0 : int i_amproclefttype;
14797 0 : int i_amprocrighttype;
14798 0 : char *amname;
14799 0 : char *amopstrategy;
14800 0 : char *amopopr;
14801 0 : char *sortfamily;
14802 0 : char *sortfamilynsp;
14803 0 : char *amprocnum;
14804 0 : char *amproc;
14805 0 : char *amproclefttype;
14806 0 : char *amprocrighttype;
14807 0 : bool needComma;
14808 0 : int i;
14809 :
14810 : /* Do nothing if not dumping schema */
14811 0 : if (!dopt->dumpSchema)
14812 0 : return;
14813 :
14814 0 : query = createPQExpBuffer();
14815 0 : q = createPQExpBuffer();
14816 0 : delq = createPQExpBuffer();
14817 0 : nameusing = createPQExpBuffer();
14818 :
14819 : /*
14820 : * Fetch only those opfamily members that are tied directly to the
14821 : * opfamily by pg_depend entries.
14822 : */
14823 0 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14824 : "amopopr::pg_catalog.regoperator, "
14825 : "opfname AS sortfamily, "
14826 : "nspname AS sortfamilynsp "
14827 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14828 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14829 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14830 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14831 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14832 : "AND refobjid = '%u'::pg_catalog.oid "
14833 : "AND amopfamily = '%u'::pg_catalog.oid "
14834 : "ORDER BY amopstrategy",
14835 0 : opfinfo->dobj.catId.oid,
14836 0 : opfinfo->dobj.catId.oid);
14837 :
14838 0 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14839 :
14840 0 : resetPQExpBuffer(query);
14841 :
14842 0 : appendPQExpBuffer(query, "SELECT amprocnum, "
14843 : "amproc::pg_catalog.regprocedure, "
14844 : "amproclefttype::pg_catalog.regtype, "
14845 : "amprocrighttype::pg_catalog.regtype "
14846 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14847 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14848 : "AND refobjid = '%u'::pg_catalog.oid "
14849 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14850 : "AND objid = ap.oid "
14851 : "ORDER BY amprocnum",
14852 0 : opfinfo->dobj.catId.oid);
14853 :
14854 0 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14855 :
14856 : /* Get additional fields from the pg_opfamily row */
14857 0 : resetPQExpBuffer(query);
14858 :
14859 0 : appendPQExpBuffer(query, "SELECT "
14860 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14861 : "FROM pg_catalog.pg_opfamily "
14862 : "WHERE oid = '%u'::pg_catalog.oid",
14863 0 : opfinfo->dobj.catId.oid);
14864 :
14865 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14866 :
14867 0 : i_amname = PQfnumber(res, "amname");
14868 :
14869 : /* amname will still be needed after we PQclear res */
14870 0 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14871 :
14872 0 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14873 0 : fmtQualifiedDumpable(opfinfo));
14874 0 : appendPQExpBuffer(delq, " USING %s;\n",
14875 0 : fmtId(amname));
14876 :
14877 : /* Build the fixed portion of the CREATE command */
14878 0 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14879 0 : fmtQualifiedDumpable(opfinfo));
14880 0 : appendPQExpBuffer(q, " USING %s;\n",
14881 0 : fmtId(amname));
14882 :
14883 0 : PQclear(res);
14884 :
14885 : /* Do we need an ALTER to add loose members? */
14886 0 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14887 : {
14888 0 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14889 0 : fmtQualifiedDumpable(opfinfo));
14890 0 : appendPQExpBuffer(q, " USING %s ADD\n ",
14891 0 : fmtId(amname));
14892 :
14893 0 : needComma = false;
14894 :
14895 : /*
14896 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14897 : */
14898 0 : ntups = PQntuples(res_ops);
14899 :
14900 0 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14901 0 : i_amopopr = PQfnumber(res_ops, "amopopr");
14902 0 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14903 0 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14904 :
14905 0 : for (i = 0; i < ntups; i++)
14906 : {
14907 0 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14908 0 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14909 0 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14910 0 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14911 :
14912 0 : if (needComma)
14913 0 : appendPQExpBufferStr(q, " ,\n ");
14914 :
14915 0 : appendPQExpBuffer(q, "OPERATOR %s %s",
14916 0 : amopstrategy, amopopr);
14917 :
14918 0 : if (strlen(sortfamily) > 0)
14919 : {
14920 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14921 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14922 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14923 0 : }
14924 :
14925 0 : needComma = true;
14926 0 : }
14927 :
14928 : /*
14929 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14930 : */
14931 0 : ntups = PQntuples(res_procs);
14932 :
14933 0 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14934 0 : i_amproc = PQfnumber(res_procs, "amproc");
14935 0 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14936 0 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14937 :
14938 0 : for (i = 0; i < ntups; i++)
14939 : {
14940 0 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14941 0 : amproc = PQgetvalue(res_procs, i, i_amproc);
14942 0 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14943 0 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14944 :
14945 0 : if (needComma)
14946 0 : appendPQExpBufferStr(q, " ,\n ");
14947 :
14948 0 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14949 0 : amprocnum, amproclefttype, amprocrighttype,
14950 0 : amproc);
14951 :
14952 0 : needComma = true;
14953 0 : }
14954 :
14955 0 : appendPQExpBufferStr(q, ";\n");
14956 0 : }
14957 :
14958 0 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14959 0 : appendPQExpBuffer(nameusing, " USING %s",
14960 0 : fmtId(amname));
14961 :
14962 0 : if (dopt->binary_upgrade)
14963 0 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14964 0 : "OPERATOR FAMILY", nameusing->data,
14965 0 : opfinfo->dobj.namespace->dobj.name);
14966 :
14967 0 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14968 0 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14969 0 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14970 : .namespace = opfinfo->dobj.namespace->dobj.name,
14971 : .owner = opfinfo->rolname,
14972 : .description = "OPERATOR FAMILY",
14973 : .section = SECTION_PRE_DATA,
14974 : .createStmt = q->data,
14975 : .dropStmt = delq->data));
14976 :
14977 : /* Dump Operator Family Comments */
14978 0 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14979 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14980 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14981 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14982 :
14983 0 : free(amname);
14984 0 : PQclear(res_ops);
14985 0 : PQclear(res_procs);
14986 0 : destroyPQExpBuffer(query);
14987 0 : destroyPQExpBuffer(q);
14988 0 : destroyPQExpBuffer(delq);
14989 0 : destroyPQExpBuffer(nameusing);
14990 0 : }
14991 :
14992 : /*
14993 : * dumpCollation
14994 : * write out a single collation definition
14995 : */
14996 : static void
14997 0 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14998 : {
14999 0 : DumpOptions *dopt = fout->dopt;
15000 0 : PQExpBuffer query;
15001 0 : PQExpBuffer q;
15002 0 : PQExpBuffer delq;
15003 0 : char *qcollname;
15004 0 : PGresult *res;
15005 0 : int i_collprovider;
15006 0 : int i_collisdeterministic;
15007 0 : int i_collcollate;
15008 0 : int i_collctype;
15009 0 : int i_colllocale;
15010 0 : int i_collicurules;
15011 0 : const char *collprovider;
15012 0 : const char *collcollate;
15013 0 : const char *collctype;
15014 0 : const char *colllocale;
15015 0 : const char *collicurules;
15016 :
15017 : /* Do nothing if not dumping schema */
15018 0 : if (!dopt->dumpSchema)
15019 0 : return;
15020 :
15021 0 : query = createPQExpBuffer();
15022 0 : q = createPQExpBuffer();
15023 0 : delq = createPQExpBuffer();
15024 :
15025 0 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15026 :
15027 : /* Get collation-specific details */
15028 0 : appendPQExpBufferStr(query, "SELECT ");
15029 :
15030 0 : if (fout->remoteVersion >= 100000)
15031 0 : appendPQExpBufferStr(query,
15032 : "collprovider, "
15033 : "collversion, ");
15034 : else
15035 0 : appendPQExpBufferStr(query,
15036 : "'c' AS collprovider, "
15037 : "NULL AS collversion, ");
15038 :
15039 0 : if (fout->remoteVersion >= 120000)
15040 0 : appendPQExpBufferStr(query,
15041 : "collisdeterministic, ");
15042 : else
15043 0 : appendPQExpBufferStr(query,
15044 : "true AS collisdeterministic, ");
15045 :
15046 0 : if (fout->remoteVersion >= 170000)
15047 0 : appendPQExpBufferStr(query,
15048 : "colllocale, ");
15049 0 : else if (fout->remoteVersion >= 150000)
15050 0 : appendPQExpBufferStr(query,
15051 : "colliculocale AS colllocale, ");
15052 : else
15053 0 : appendPQExpBufferStr(query,
15054 : "NULL AS colllocale, ");
15055 :
15056 0 : if (fout->remoteVersion >= 160000)
15057 0 : appendPQExpBufferStr(query,
15058 : "collicurules, ");
15059 : else
15060 0 : appendPQExpBufferStr(query,
15061 : "NULL AS collicurules, ");
15062 :
15063 0 : appendPQExpBuffer(query,
15064 : "collcollate, "
15065 : "collctype "
15066 : "FROM pg_catalog.pg_collation c "
15067 : "WHERE c.oid = '%u'::pg_catalog.oid",
15068 0 : collinfo->dobj.catId.oid);
15069 :
15070 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15071 :
15072 0 : i_collprovider = PQfnumber(res, "collprovider");
15073 0 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15074 0 : i_collcollate = PQfnumber(res, "collcollate");
15075 0 : i_collctype = PQfnumber(res, "collctype");
15076 0 : i_colllocale = PQfnumber(res, "colllocale");
15077 0 : i_collicurules = PQfnumber(res, "collicurules");
15078 :
15079 0 : collprovider = PQgetvalue(res, 0, i_collprovider);
15080 :
15081 0 : if (!PQgetisnull(res, 0, i_collcollate))
15082 0 : collcollate = PQgetvalue(res, 0, i_collcollate);
15083 : else
15084 0 : collcollate = NULL;
15085 :
15086 0 : if (!PQgetisnull(res, 0, i_collctype))
15087 0 : collctype = PQgetvalue(res, 0, i_collctype);
15088 : else
15089 0 : collctype = NULL;
15090 :
15091 : /*
15092 : * Before version 15, collcollate and collctype were of type NAME and
15093 : * non-nullable. Treat empty strings as NULL for consistency.
15094 : */
15095 0 : if (fout->remoteVersion < 150000)
15096 : {
15097 0 : if (collcollate[0] == '\0')
15098 0 : collcollate = NULL;
15099 0 : if (collctype[0] == '\0')
15100 0 : collctype = NULL;
15101 0 : }
15102 :
15103 0 : if (!PQgetisnull(res, 0, i_colllocale))
15104 0 : colllocale = PQgetvalue(res, 0, i_colllocale);
15105 : else
15106 0 : colllocale = NULL;
15107 :
15108 0 : if (!PQgetisnull(res, 0, i_collicurules))
15109 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15110 : else
15111 0 : collicurules = NULL;
15112 :
15113 0 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15114 0 : fmtQualifiedDumpable(collinfo));
15115 :
15116 0 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15117 0 : fmtQualifiedDumpable(collinfo));
15118 :
15119 0 : appendPQExpBufferStr(q, "provider = ");
15120 0 : if (collprovider[0] == 'b')
15121 0 : appendPQExpBufferStr(q, "builtin");
15122 0 : else if (collprovider[0] == 'c')
15123 0 : appendPQExpBufferStr(q, "libc");
15124 0 : else if (collprovider[0] == 'i')
15125 0 : appendPQExpBufferStr(q, "icu");
15126 0 : else if (collprovider[0] == 'd')
15127 : /* to allow dumping pg_catalog; not accepted on input */
15128 0 : appendPQExpBufferStr(q, "default");
15129 : else
15130 0 : pg_fatal("unrecognized collation provider: %s",
15131 : collprovider);
15132 :
15133 0 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15134 0 : appendPQExpBufferStr(q, ", deterministic = false");
15135 :
15136 0 : if (collprovider[0] == 'd')
15137 : {
15138 0 : if (collcollate || collctype || colllocale || collicurules)
15139 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15140 :
15141 : /* no locale -- the default collation cannot be reloaded anyway */
15142 0 : }
15143 0 : else if (collprovider[0] == 'b')
15144 : {
15145 0 : if (collcollate || collctype || !colllocale || collicurules)
15146 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15147 :
15148 0 : appendPQExpBufferStr(q, ", locale = ");
15149 0 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15150 : fout);
15151 0 : }
15152 0 : else if (collprovider[0] == 'i')
15153 : {
15154 0 : if (fout->remoteVersion >= 150000)
15155 : {
15156 0 : if (collcollate || collctype || !colllocale)
15157 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15158 :
15159 0 : appendPQExpBufferStr(q, ", locale = ");
15160 0 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15161 : fout);
15162 0 : }
15163 : else
15164 : {
15165 0 : if (!collcollate || !collctype || colllocale ||
15166 0 : strcmp(collcollate, collctype) != 0)
15167 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15168 :
15169 0 : appendPQExpBufferStr(q, ", locale = ");
15170 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15171 : }
15172 :
15173 0 : if (collicurules)
15174 : {
15175 0 : appendPQExpBufferStr(q, ", rules = ");
15176 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15177 0 : }
15178 0 : }
15179 0 : else if (collprovider[0] == 'c')
15180 : {
15181 0 : if (colllocale || collicurules || !collcollate || !collctype)
15182 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15183 :
15184 0 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15185 : {
15186 0 : appendPQExpBufferStr(q, ", locale = ");
15187 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15188 0 : }
15189 : else
15190 : {
15191 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15192 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15193 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15194 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15195 : }
15196 0 : }
15197 : else
15198 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15199 :
15200 : /*
15201 : * For binary upgrade, carry over the collation version. For normal
15202 : * dump/restore, omit the version, so that it is computed upon restore.
15203 : */
15204 0 : if (dopt->binary_upgrade)
15205 : {
15206 0 : int i_collversion;
15207 :
15208 0 : i_collversion = PQfnumber(res, "collversion");
15209 0 : if (!PQgetisnull(res, 0, i_collversion))
15210 : {
15211 0 : appendPQExpBufferStr(q, ", version = ");
15212 0 : appendStringLiteralAH(q,
15213 : PQgetvalue(res, 0, i_collversion),
15214 : fout);
15215 0 : }
15216 0 : }
15217 :
15218 0 : appendPQExpBufferStr(q, ");\n");
15219 :
15220 0 : if (dopt->binary_upgrade)
15221 0 : binary_upgrade_extension_member(q, &collinfo->dobj,
15222 0 : "COLLATION", qcollname,
15223 0 : collinfo->dobj.namespace->dobj.name);
15224 :
15225 0 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15226 0 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15227 0 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15228 : .namespace = collinfo->dobj.namespace->dobj.name,
15229 : .owner = collinfo->rolname,
15230 : .description = "COLLATION",
15231 : .section = SECTION_PRE_DATA,
15232 : .createStmt = q->data,
15233 : .dropStmt = delq->data));
15234 :
15235 : /* Dump Collation Comments */
15236 0 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15237 0 : dumpComment(fout, "COLLATION", qcollname,
15238 0 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15239 0 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15240 :
15241 0 : PQclear(res);
15242 :
15243 0 : destroyPQExpBuffer(query);
15244 0 : destroyPQExpBuffer(q);
15245 0 : destroyPQExpBuffer(delq);
15246 0 : free(qcollname);
15247 0 : }
15248 :
15249 : /*
15250 : * dumpConversion
15251 : * write out a single conversion definition
15252 : */
15253 : static void
15254 0 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15255 : {
15256 0 : DumpOptions *dopt = fout->dopt;
15257 0 : PQExpBuffer query;
15258 0 : PQExpBuffer q;
15259 0 : PQExpBuffer delq;
15260 0 : char *qconvname;
15261 0 : PGresult *res;
15262 0 : int i_conforencoding;
15263 0 : int i_contoencoding;
15264 0 : int i_conproc;
15265 0 : int i_condefault;
15266 0 : const char *conforencoding;
15267 0 : const char *contoencoding;
15268 0 : const char *conproc;
15269 0 : bool condefault;
15270 :
15271 : /* Do nothing if not dumping schema */
15272 0 : if (!dopt->dumpSchema)
15273 0 : return;
15274 :
15275 0 : query = createPQExpBuffer();
15276 0 : q = createPQExpBuffer();
15277 0 : delq = createPQExpBuffer();
15278 :
15279 0 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15280 :
15281 : /* Get conversion-specific details */
15282 0 : appendPQExpBuffer(query, "SELECT "
15283 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15284 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15285 : "conproc, condefault "
15286 : "FROM pg_catalog.pg_conversion c "
15287 : "WHERE c.oid = '%u'::pg_catalog.oid",
15288 0 : convinfo->dobj.catId.oid);
15289 :
15290 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15291 :
15292 0 : i_conforencoding = PQfnumber(res, "conforencoding");
15293 0 : i_contoencoding = PQfnumber(res, "contoencoding");
15294 0 : i_conproc = PQfnumber(res, "conproc");
15295 0 : i_condefault = PQfnumber(res, "condefault");
15296 :
15297 0 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15298 0 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15299 0 : conproc = PQgetvalue(res, 0, i_conproc);
15300 0 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15301 :
15302 0 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15303 0 : fmtQualifiedDumpable(convinfo));
15304 :
15305 0 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15306 0 : (condefault) ? "DEFAULT " : "",
15307 0 : fmtQualifiedDumpable(convinfo));
15308 0 : appendStringLiteralAH(q, conforencoding, fout);
15309 0 : appendPQExpBufferStr(q, " TO ");
15310 0 : appendStringLiteralAH(q, contoencoding, fout);
15311 : /* regproc output is already sufficiently quoted */
15312 0 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15313 :
15314 0 : if (dopt->binary_upgrade)
15315 0 : binary_upgrade_extension_member(q, &convinfo->dobj,
15316 0 : "CONVERSION", qconvname,
15317 0 : convinfo->dobj.namespace->dobj.name);
15318 :
15319 0 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15320 0 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15321 0 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15322 : .namespace = convinfo->dobj.namespace->dobj.name,
15323 : .owner = convinfo->rolname,
15324 : .description = "CONVERSION",
15325 : .section = SECTION_PRE_DATA,
15326 : .createStmt = q->data,
15327 : .dropStmt = delq->data));
15328 :
15329 : /* Dump Conversion Comments */
15330 0 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15331 0 : dumpComment(fout, "CONVERSION", qconvname,
15332 0 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15333 0 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15334 :
15335 0 : PQclear(res);
15336 :
15337 0 : destroyPQExpBuffer(query);
15338 0 : destroyPQExpBuffer(q);
15339 0 : destroyPQExpBuffer(delq);
15340 0 : free(qconvname);
15341 0 : }
15342 :
15343 : /*
15344 : * format_aggregate_signature: generate aggregate name and argument list
15345 : *
15346 : * The argument type names are qualified if needed. The aggregate name
15347 : * is never qualified.
15348 : */
15349 : static char *
15350 0 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15351 : {
15352 0 : PQExpBufferData buf;
15353 0 : int j;
15354 :
15355 0 : initPQExpBuffer(&buf);
15356 0 : if (honor_quotes)
15357 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15358 : else
15359 0 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15360 :
15361 0 : if (agginfo->aggfn.nargs == 0)
15362 0 : appendPQExpBufferStr(&buf, "(*)");
15363 : else
15364 : {
15365 0 : appendPQExpBufferChar(&buf, '(');
15366 0 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15367 0 : appendPQExpBuffer(&buf, "%s%s",
15368 0 : (j > 0) ? ", " : "",
15369 0 : getFormattedTypeName(fout,
15370 0 : agginfo->aggfn.argtypes[j],
15371 : zeroIsError));
15372 0 : appendPQExpBufferChar(&buf, ')');
15373 : }
15374 0 : return buf.data;
15375 0 : }
15376 :
15377 : /*
15378 : * dumpAgg
15379 : * write out a single aggregate definition
15380 : */
15381 : static void
15382 0 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15383 : {
15384 0 : DumpOptions *dopt = fout->dopt;
15385 0 : PQExpBuffer query;
15386 0 : PQExpBuffer q;
15387 0 : PQExpBuffer delq;
15388 0 : PQExpBuffer details;
15389 0 : char *aggsig; /* identity signature */
15390 0 : char *aggfullsig = NULL; /* full signature */
15391 0 : char *aggsig_tag;
15392 0 : PGresult *res;
15393 0 : int i_agginitval;
15394 0 : int i_aggminitval;
15395 0 : const char *aggtransfn;
15396 0 : const char *aggfinalfn;
15397 0 : const char *aggcombinefn;
15398 0 : const char *aggserialfn;
15399 0 : const char *aggdeserialfn;
15400 0 : const char *aggmtransfn;
15401 0 : const char *aggminvtransfn;
15402 0 : const char *aggmfinalfn;
15403 0 : bool aggfinalextra;
15404 0 : bool aggmfinalextra;
15405 0 : char aggfinalmodify;
15406 0 : char aggmfinalmodify;
15407 0 : const char *aggsortop;
15408 0 : char *aggsortconvop;
15409 0 : char aggkind;
15410 0 : const char *aggtranstype;
15411 0 : const char *aggtransspace;
15412 0 : const char *aggmtranstype;
15413 0 : const char *aggmtransspace;
15414 0 : const char *agginitval;
15415 0 : const char *aggminitval;
15416 0 : const char *proparallel;
15417 0 : char defaultfinalmodify;
15418 :
15419 : /* Do nothing if not dumping schema */
15420 0 : if (!dopt->dumpSchema)
15421 0 : return;
15422 :
15423 0 : query = createPQExpBuffer();
15424 0 : q = createPQExpBuffer();
15425 0 : delq = createPQExpBuffer();
15426 0 : details = createPQExpBuffer();
15427 :
15428 0 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15429 : {
15430 : /* Set up query for aggregate-specific details */
15431 0 : appendPQExpBufferStr(query,
15432 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15433 :
15434 0 : appendPQExpBufferStr(query,
15435 : "SELECT "
15436 : "aggtransfn,\n"
15437 : "aggfinalfn,\n"
15438 : "aggtranstype::pg_catalog.regtype,\n"
15439 : "agginitval,\n"
15440 : "aggsortop,\n"
15441 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15442 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15443 :
15444 0 : if (fout->remoteVersion >= 90400)
15445 0 : appendPQExpBufferStr(query,
15446 : "aggkind,\n"
15447 : "aggmtransfn,\n"
15448 : "aggminvtransfn,\n"
15449 : "aggmfinalfn,\n"
15450 : "aggmtranstype::pg_catalog.regtype,\n"
15451 : "aggfinalextra,\n"
15452 : "aggmfinalextra,\n"
15453 : "aggtransspace,\n"
15454 : "aggmtransspace,\n"
15455 : "aggminitval,\n");
15456 : else
15457 0 : appendPQExpBufferStr(query,
15458 : "'n' AS aggkind,\n"
15459 : "'-' AS aggmtransfn,\n"
15460 : "'-' AS aggminvtransfn,\n"
15461 : "'-' AS aggmfinalfn,\n"
15462 : "0 AS aggmtranstype,\n"
15463 : "false AS aggfinalextra,\n"
15464 : "false AS aggmfinalextra,\n"
15465 : "0 AS aggtransspace,\n"
15466 : "0 AS aggmtransspace,\n"
15467 : "NULL AS aggminitval,\n");
15468 :
15469 0 : if (fout->remoteVersion >= 90600)
15470 0 : appendPQExpBufferStr(query,
15471 : "aggcombinefn,\n"
15472 : "aggserialfn,\n"
15473 : "aggdeserialfn,\n"
15474 : "proparallel,\n");
15475 : else
15476 0 : appendPQExpBufferStr(query,
15477 : "'-' AS aggcombinefn,\n"
15478 : "'-' AS aggserialfn,\n"
15479 : "'-' AS aggdeserialfn,\n"
15480 : "'u' AS proparallel,\n");
15481 :
15482 0 : if (fout->remoteVersion >= 110000)
15483 0 : appendPQExpBufferStr(query,
15484 : "aggfinalmodify,\n"
15485 : "aggmfinalmodify\n");
15486 : else
15487 0 : appendPQExpBufferStr(query,
15488 : "'0' AS aggfinalmodify,\n"
15489 : "'0' AS aggmfinalmodify\n");
15490 :
15491 0 : appendPQExpBufferStr(query,
15492 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15493 : "WHERE a.aggfnoid = p.oid "
15494 : "AND p.oid = $1");
15495 :
15496 0 : ExecuteSqlStatement(fout, query->data);
15497 :
15498 0 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15499 0 : }
15500 :
15501 0 : printfPQExpBuffer(query,
15502 : "EXECUTE dumpAgg('%u')",
15503 0 : agginfo->aggfn.dobj.catId.oid);
15504 :
15505 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15506 :
15507 0 : i_agginitval = PQfnumber(res, "agginitval");
15508 0 : i_aggminitval = PQfnumber(res, "aggminitval");
15509 :
15510 0 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15511 0 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15512 0 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15513 0 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15514 0 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15515 0 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15516 0 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15517 0 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15518 0 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15519 0 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15520 0 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15521 0 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15522 0 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15523 0 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15524 0 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15525 0 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15526 0 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15527 0 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15528 0 : agginitval = PQgetvalue(res, 0, i_agginitval);
15529 0 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15530 0 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15531 :
15532 : {
15533 0 : char *funcargs;
15534 0 : char *funciargs;
15535 :
15536 0 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15537 0 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15538 0 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15539 0 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15540 0 : }
15541 :
15542 0 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15543 :
15544 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15545 0 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15546 : /* replace omitted flags for old versions */
15547 0 : if (aggfinalmodify == '0')
15548 0 : aggfinalmodify = defaultfinalmodify;
15549 0 : if (aggmfinalmodify == '0')
15550 0 : aggmfinalmodify = defaultfinalmodify;
15551 :
15552 : /* regproc and regtype output is already sufficiently quoted */
15553 0 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15554 0 : aggtransfn, aggtranstype);
15555 :
15556 0 : if (strcmp(aggtransspace, "0") != 0)
15557 : {
15558 0 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15559 0 : aggtransspace);
15560 0 : }
15561 :
15562 0 : if (!PQgetisnull(res, 0, i_agginitval))
15563 : {
15564 0 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15565 0 : appendStringLiteralAH(details, agginitval, fout);
15566 0 : }
15567 :
15568 0 : if (strcmp(aggfinalfn, "-") != 0)
15569 : {
15570 0 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15571 0 : aggfinalfn);
15572 0 : if (aggfinalextra)
15573 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15574 0 : if (aggfinalmodify != defaultfinalmodify)
15575 : {
15576 0 : switch (aggfinalmodify)
15577 : {
15578 : case AGGMODIFY_READ_ONLY:
15579 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15580 0 : break;
15581 : case AGGMODIFY_SHAREABLE:
15582 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15583 0 : break;
15584 : case AGGMODIFY_READ_WRITE:
15585 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15586 0 : break;
15587 : default:
15588 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15589 : agginfo->aggfn.dobj.name);
15590 0 : break;
15591 : }
15592 0 : }
15593 0 : }
15594 :
15595 0 : if (strcmp(aggcombinefn, "-") != 0)
15596 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15597 :
15598 0 : if (strcmp(aggserialfn, "-") != 0)
15599 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15600 :
15601 0 : if (strcmp(aggdeserialfn, "-") != 0)
15602 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15603 :
15604 0 : if (strcmp(aggmtransfn, "-") != 0)
15605 : {
15606 0 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15607 0 : aggmtransfn,
15608 0 : aggminvtransfn,
15609 0 : aggmtranstype);
15610 0 : }
15611 :
15612 0 : if (strcmp(aggmtransspace, "0") != 0)
15613 : {
15614 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15615 0 : aggmtransspace);
15616 0 : }
15617 :
15618 0 : if (!PQgetisnull(res, 0, i_aggminitval))
15619 : {
15620 0 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15621 0 : appendStringLiteralAH(details, aggminitval, fout);
15622 0 : }
15623 :
15624 0 : if (strcmp(aggmfinalfn, "-") != 0)
15625 : {
15626 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15627 0 : aggmfinalfn);
15628 0 : if (aggmfinalextra)
15629 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15630 0 : if (aggmfinalmodify != defaultfinalmodify)
15631 : {
15632 0 : switch (aggmfinalmodify)
15633 : {
15634 : case AGGMODIFY_READ_ONLY:
15635 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15636 0 : break;
15637 : case AGGMODIFY_SHAREABLE:
15638 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15639 0 : break;
15640 : case AGGMODIFY_READ_WRITE:
15641 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15642 0 : break;
15643 : default:
15644 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15645 : agginfo->aggfn.dobj.name);
15646 0 : break;
15647 : }
15648 0 : }
15649 0 : }
15650 :
15651 0 : aggsortconvop = getFormattedOperatorName(aggsortop);
15652 0 : if (aggsortconvop)
15653 : {
15654 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15655 0 : aggsortconvop);
15656 0 : free(aggsortconvop);
15657 0 : }
15658 :
15659 0 : if (aggkind == AGGKIND_HYPOTHETICAL)
15660 0 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15661 :
15662 0 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15663 : {
15664 0 : if (proparallel[0] == PROPARALLEL_SAFE)
15665 0 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15666 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15667 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15668 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15669 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15670 : agginfo->aggfn.dobj.name);
15671 0 : }
15672 :
15673 0 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15674 0 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15675 0 : aggsig);
15676 :
15677 0 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15678 0 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15679 0 : aggfullsig ? aggfullsig : aggsig, details->data);
15680 :
15681 0 : if (dopt->binary_upgrade)
15682 0 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15683 0 : "AGGREGATE", aggsig,
15684 0 : agginfo->aggfn.dobj.namespace->dobj.name);
15685 :
15686 0 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15687 0 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15688 0 : agginfo->aggfn.dobj.dumpId,
15689 0 : ARCHIVE_OPTS(.tag = aggsig_tag,
15690 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15691 : .owner = agginfo->aggfn.rolname,
15692 : .description = "AGGREGATE",
15693 : .section = SECTION_PRE_DATA,
15694 : .createStmt = q->data,
15695 : .dropStmt = delq->data));
15696 :
15697 : /* Dump Aggregate Comments */
15698 0 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15699 0 : dumpComment(fout, "AGGREGATE", aggsig,
15700 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15701 0 : agginfo->aggfn.rolname,
15702 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15703 :
15704 0 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15705 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15706 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15707 0 : agginfo->aggfn.rolname,
15708 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15709 :
15710 : /*
15711 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15712 : * command look like a function's GRANT; in particular this affects the
15713 : * syntax for zero-argument aggregates and ordered-set aggregates.
15714 : */
15715 0 : free(aggsig);
15716 :
15717 0 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15718 :
15719 0 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15720 0 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15721 0 : "FUNCTION", aggsig, NULL,
15722 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15723 0 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15724 :
15725 0 : free(aggsig);
15726 0 : free(aggfullsig);
15727 0 : free(aggsig_tag);
15728 :
15729 0 : PQclear(res);
15730 :
15731 0 : destroyPQExpBuffer(query);
15732 0 : destroyPQExpBuffer(q);
15733 0 : destroyPQExpBuffer(delq);
15734 0 : destroyPQExpBuffer(details);
15735 0 : }
15736 :
15737 : /*
15738 : * dumpTSParser
15739 : * write out a single text search parser
15740 : */
15741 : static void
15742 0 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15743 : {
15744 0 : DumpOptions *dopt = fout->dopt;
15745 0 : PQExpBuffer q;
15746 0 : PQExpBuffer delq;
15747 0 : char *qprsname;
15748 :
15749 : /* Do nothing if not dumping schema */
15750 0 : if (!dopt->dumpSchema)
15751 0 : return;
15752 :
15753 0 : q = createPQExpBuffer();
15754 0 : delq = createPQExpBuffer();
15755 :
15756 0 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15757 :
15758 0 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15759 0 : fmtQualifiedDumpable(prsinfo));
15760 :
15761 0 : appendPQExpBuffer(q, " START = %s,\n",
15762 0 : convertTSFunction(fout, prsinfo->prsstart));
15763 0 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15764 0 : convertTSFunction(fout, prsinfo->prstoken));
15765 0 : appendPQExpBuffer(q, " END = %s,\n",
15766 0 : convertTSFunction(fout, prsinfo->prsend));
15767 0 : if (prsinfo->prsheadline != InvalidOid)
15768 0 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15769 0 : convertTSFunction(fout, prsinfo->prsheadline));
15770 0 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15771 0 : convertTSFunction(fout, prsinfo->prslextype));
15772 :
15773 0 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15774 0 : fmtQualifiedDumpable(prsinfo));
15775 :
15776 0 : if (dopt->binary_upgrade)
15777 0 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15778 0 : "TEXT SEARCH PARSER", qprsname,
15779 0 : prsinfo->dobj.namespace->dobj.name);
15780 :
15781 0 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15782 0 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15783 0 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15784 : .namespace = prsinfo->dobj.namespace->dobj.name,
15785 : .description = "TEXT SEARCH PARSER",
15786 : .section = SECTION_PRE_DATA,
15787 : .createStmt = q->data,
15788 : .dropStmt = delq->data));
15789 :
15790 : /* Dump Parser Comments */
15791 0 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15792 0 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15793 0 : prsinfo->dobj.namespace->dobj.name, "",
15794 0 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15795 :
15796 0 : destroyPQExpBuffer(q);
15797 0 : destroyPQExpBuffer(delq);
15798 0 : free(qprsname);
15799 0 : }
15800 :
15801 : /*
15802 : * dumpTSDictionary
15803 : * write out a single text search dictionary
15804 : */
15805 : static void
15806 0 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15807 : {
15808 0 : DumpOptions *dopt = fout->dopt;
15809 0 : PQExpBuffer q;
15810 0 : PQExpBuffer delq;
15811 0 : PQExpBuffer query;
15812 0 : char *qdictname;
15813 0 : PGresult *res;
15814 0 : char *nspname;
15815 0 : char *tmplname;
15816 :
15817 : /* Do nothing if not dumping schema */
15818 0 : if (!dopt->dumpSchema)
15819 0 : return;
15820 :
15821 0 : q = createPQExpBuffer();
15822 0 : delq = createPQExpBuffer();
15823 0 : query = createPQExpBuffer();
15824 :
15825 0 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15826 :
15827 : /* Fetch name and namespace of the dictionary's template */
15828 0 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15829 : "FROM pg_ts_template p, pg_namespace n "
15830 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15831 0 : dictinfo->dicttemplate);
15832 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15833 0 : nspname = PQgetvalue(res, 0, 0);
15834 0 : tmplname = PQgetvalue(res, 0, 1);
15835 :
15836 0 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15837 0 : fmtQualifiedDumpable(dictinfo));
15838 :
15839 0 : appendPQExpBufferStr(q, " TEMPLATE = ");
15840 0 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15841 0 : appendPQExpBufferStr(q, fmtId(tmplname));
15842 :
15843 0 : PQclear(res);
15844 :
15845 : /* the dictinitoption can be dumped straight into the command */
15846 0 : if (dictinfo->dictinitoption)
15847 0 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15848 :
15849 0 : appendPQExpBufferStr(q, " );\n");
15850 :
15851 0 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15852 0 : fmtQualifiedDumpable(dictinfo));
15853 :
15854 0 : if (dopt->binary_upgrade)
15855 0 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15856 0 : "TEXT SEARCH DICTIONARY", qdictname,
15857 0 : dictinfo->dobj.namespace->dobj.name);
15858 :
15859 0 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15860 0 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15861 0 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15862 : .namespace = dictinfo->dobj.namespace->dobj.name,
15863 : .owner = dictinfo->rolname,
15864 : .description = "TEXT SEARCH DICTIONARY",
15865 : .section = SECTION_PRE_DATA,
15866 : .createStmt = q->data,
15867 : .dropStmt = delq->data));
15868 :
15869 : /* Dump Dictionary Comments */
15870 0 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15871 0 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15872 0 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15873 0 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15874 :
15875 0 : destroyPQExpBuffer(q);
15876 0 : destroyPQExpBuffer(delq);
15877 0 : destroyPQExpBuffer(query);
15878 0 : free(qdictname);
15879 0 : }
15880 :
15881 : /*
15882 : * dumpTSTemplate
15883 : * write out a single text search template
15884 : */
15885 : static void
15886 0 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15887 : {
15888 0 : DumpOptions *dopt = fout->dopt;
15889 0 : PQExpBuffer q;
15890 0 : PQExpBuffer delq;
15891 0 : char *qtmplname;
15892 :
15893 : /* Do nothing if not dumping schema */
15894 0 : if (!dopt->dumpSchema)
15895 0 : return;
15896 :
15897 0 : q = createPQExpBuffer();
15898 0 : delq = createPQExpBuffer();
15899 :
15900 0 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15901 :
15902 0 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15903 0 : fmtQualifiedDumpable(tmplinfo));
15904 :
15905 0 : if (tmplinfo->tmplinit != InvalidOid)
15906 0 : appendPQExpBuffer(q, " INIT = %s,\n",
15907 0 : convertTSFunction(fout, tmplinfo->tmplinit));
15908 0 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15909 0 : convertTSFunction(fout, tmplinfo->tmpllexize));
15910 :
15911 0 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15912 0 : fmtQualifiedDumpable(tmplinfo));
15913 :
15914 0 : if (dopt->binary_upgrade)
15915 0 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15916 0 : "TEXT SEARCH TEMPLATE", qtmplname,
15917 0 : tmplinfo->dobj.namespace->dobj.name);
15918 :
15919 0 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15920 0 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15921 0 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15922 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15923 : .description = "TEXT SEARCH TEMPLATE",
15924 : .section = SECTION_PRE_DATA,
15925 : .createStmt = q->data,
15926 : .dropStmt = delq->data));
15927 :
15928 : /* Dump Template Comments */
15929 0 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15930 0 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15931 0 : tmplinfo->dobj.namespace->dobj.name, "",
15932 0 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15933 :
15934 0 : destroyPQExpBuffer(q);
15935 0 : destroyPQExpBuffer(delq);
15936 0 : free(qtmplname);
15937 0 : }
15938 :
15939 : /*
15940 : * dumpTSConfig
15941 : * write out a single text search configuration
15942 : */
15943 : static void
15944 0 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15945 : {
15946 0 : DumpOptions *dopt = fout->dopt;
15947 0 : PQExpBuffer q;
15948 0 : PQExpBuffer delq;
15949 0 : PQExpBuffer query;
15950 0 : char *qcfgname;
15951 0 : PGresult *res;
15952 0 : char *nspname;
15953 0 : char *prsname;
15954 0 : int ntups,
15955 : i;
15956 0 : int i_tokenname;
15957 0 : int i_dictname;
15958 :
15959 : /* Do nothing if not dumping schema */
15960 0 : if (!dopt->dumpSchema)
15961 0 : return;
15962 :
15963 0 : q = createPQExpBuffer();
15964 0 : delq = createPQExpBuffer();
15965 0 : query = createPQExpBuffer();
15966 :
15967 0 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15968 :
15969 : /* Fetch name and namespace of the config's parser */
15970 0 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15971 : "FROM pg_ts_parser p, pg_namespace n "
15972 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15973 0 : cfginfo->cfgparser);
15974 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15975 0 : nspname = PQgetvalue(res, 0, 0);
15976 0 : prsname = PQgetvalue(res, 0, 1);
15977 :
15978 0 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15979 0 : fmtQualifiedDumpable(cfginfo));
15980 :
15981 0 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15982 0 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15983 :
15984 0 : PQclear(res);
15985 :
15986 0 : resetPQExpBuffer(query);
15987 0 : appendPQExpBuffer(query,
15988 : "SELECT\n"
15989 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15990 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15991 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15992 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15993 : "WHERE m.mapcfg = '%u'\n"
15994 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15995 0 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15996 :
15997 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15998 0 : ntups = PQntuples(res);
15999 :
16000 0 : i_tokenname = PQfnumber(res, "tokenname");
16001 0 : i_dictname = PQfnumber(res, "dictname");
16002 :
16003 0 : for (i = 0; i < ntups; i++)
16004 : {
16005 0 : char *tokenname = PQgetvalue(res, i, i_tokenname);
16006 0 : char *dictname = PQgetvalue(res, i, i_dictname);
16007 :
16008 0 : if (i == 0 ||
16009 0 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
16010 : {
16011 : /* starting a new token type, so start a new command */
16012 0 : if (i > 0)
16013 0 : appendPQExpBufferStr(q, ";\n");
16014 0 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16015 0 : fmtQualifiedDumpable(cfginfo));
16016 : /* tokenname needs quoting, dictname does NOT */
16017 0 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16018 0 : fmtId(tokenname), dictname);
16019 0 : }
16020 : else
16021 0 : appendPQExpBuffer(q, ", %s", dictname);
16022 0 : }
16023 :
16024 0 : if (ntups > 0)
16025 0 : appendPQExpBufferStr(q, ";\n");
16026 :
16027 0 : PQclear(res);
16028 :
16029 0 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16030 0 : fmtQualifiedDumpable(cfginfo));
16031 :
16032 0 : if (dopt->binary_upgrade)
16033 0 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16034 0 : "TEXT SEARCH CONFIGURATION", qcfgname,
16035 0 : cfginfo->dobj.namespace->dobj.name);
16036 :
16037 0 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16038 0 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16039 0 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16040 : .namespace = cfginfo->dobj.namespace->dobj.name,
16041 : .owner = cfginfo->rolname,
16042 : .description = "TEXT SEARCH CONFIGURATION",
16043 : .section = SECTION_PRE_DATA,
16044 : .createStmt = q->data,
16045 : .dropStmt = delq->data));
16046 :
16047 : /* Dump Configuration Comments */
16048 0 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16049 0 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16050 0 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16051 0 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16052 :
16053 0 : destroyPQExpBuffer(q);
16054 0 : destroyPQExpBuffer(delq);
16055 0 : destroyPQExpBuffer(query);
16056 0 : free(qcfgname);
16057 0 : }
16058 :
16059 : /*
16060 : * dumpForeignDataWrapper
16061 : * write out a single foreign-data wrapper definition
16062 : */
16063 : static void
16064 0 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16065 : {
16066 0 : DumpOptions *dopt = fout->dopt;
16067 0 : PQExpBuffer q;
16068 0 : PQExpBuffer delq;
16069 0 : char *qfdwname;
16070 :
16071 : /* Do nothing if not dumping schema */
16072 0 : if (!dopt->dumpSchema)
16073 0 : return;
16074 :
16075 0 : q = createPQExpBuffer();
16076 0 : delq = createPQExpBuffer();
16077 :
16078 0 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16079 :
16080 0 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16081 0 : qfdwname);
16082 :
16083 0 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16084 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16085 :
16086 0 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16087 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16088 :
16089 0 : if (strlen(fdwinfo->fdwoptions) > 0)
16090 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16091 :
16092 0 : appendPQExpBufferStr(q, ";\n");
16093 :
16094 0 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16095 0 : qfdwname);
16096 :
16097 0 : if (dopt->binary_upgrade)
16098 0 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16099 0 : "FOREIGN DATA WRAPPER", qfdwname,
16100 : NULL);
16101 :
16102 0 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16103 0 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16104 0 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16105 : .owner = fdwinfo->rolname,
16106 : .description = "FOREIGN DATA WRAPPER",
16107 : .section = SECTION_PRE_DATA,
16108 : .createStmt = q->data,
16109 : .dropStmt = delq->data));
16110 :
16111 : /* Dump Foreign Data Wrapper Comments */
16112 0 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16113 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16114 0 : NULL, fdwinfo->rolname,
16115 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16116 :
16117 : /* Handle the ACL */
16118 0 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16119 0 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16120 0 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16121 0 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16122 :
16123 0 : free(qfdwname);
16124 :
16125 0 : destroyPQExpBuffer(q);
16126 0 : destroyPQExpBuffer(delq);
16127 0 : }
16128 :
16129 : /*
16130 : * dumpForeignServer
16131 : * write out a foreign server definition
16132 : */
16133 : static void
16134 0 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16135 : {
16136 0 : DumpOptions *dopt = fout->dopt;
16137 0 : PQExpBuffer q;
16138 0 : PQExpBuffer delq;
16139 0 : PQExpBuffer query;
16140 0 : PGresult *res;
16141 0 : char *qsrvname;
16142 0 : char *fdwname;
16143 :
16144 : /* Do nothing if not dumping schema */
16145 0 : if (!dopt->dumpSchema)
16146 0 : return;
16147 :
16148 0 : q = createPQExpBuffer();
16149 0 : delq = createPQExpBuffer();
16150 0 : query = createPQExpBuffer();
16151 :
16152 0 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16153 :
16154 : /* look up the foreign-data wrapper */
16155 0 : appendPQExpBuffer(query, "SELECT fdwname "
16156 : "FROM pg_foreign_data_wrapper w "
16157 : "WHERE w.oid = '%u'",
16158 0 : srvinfo->srvfdw);
16159 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16160 0 : fdwname = PQgetvalue(res, 0, 0);
16161 :
16162 0 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16163 0 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16164 : {
16165 0 : appendPQExpBufferStr(q, " TYPE ");
16166 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16167 0 : }
16168 0 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16169 : {
16170 0 : appendPQExpBufferStr(q, " VERSION ");
16171 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16172 0 : }
16173 :
16174 0 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16175 0 : appendPQExpBufferStr(q, fmtId(fdwname));
16176 :
16177 0 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16178 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16179 :
16180 0 : appendPQExpBufferStr(q, ";\n");
16181 :
16182 0 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16183 0 : qsrvname);
16184 :
16185 0 : if (dopt->binary_upgrade)
16186 0 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16187 0 : "SERVER", qsrvname, NULL);
16188 :
16189 0 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16190 0 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16191 0 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16192 : .owner = srvinfo->rolname,
16193 : .description = "SERVER",
16194 : .section = SECTION_PRE_DATA,
16195 : .createStmt = q->data,
16196 : .dropStmt = delq->data));
16197 :
16198 : /* Dump Foreign Server Comments */
16199 0 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16200 0 : dumpComment(fout, "SERVER", qsrvname,
16201 0 : NULL, srvinfo->rolname,
16202 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16203 :
16204 : /* Handle the ACL */
16205 0 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16206 0 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16207 0 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16208 0 : NULL, srvinfo->rolname, &srvinfo->dacl);
16209 :
16210 : /* Dump user mappings */
16211 0 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16212 0 : dumpUserMappings(fout,
16213 0 : srvinfo->dobj.name, NULL,
16214 0 : srvinfo->rolname,
16215 0 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16216 :
16217 0 : PQclear(res);
16218 :
16219 0 : free(qsrvname);
16220 :
16221 0 : destroyPQExpBuffer(q);
16222 0 : destroyPQExpBuffer(delq);
16223 0 : destroyPQExpBuffer(query);
16224 0 : }
16225 :
16226 : /*
16227 : * dumpUserMappings
16228 : *
16229 : * This routine is used to dump any user mappings associated with the
16230 : * server handed to this routine. Should be called after ArchiveEntry()
16231 : * for the server.
16232 : */
16233 : static void
16234 0 : dumpUserMappings(Archive *fout,
16235 : const char *servername, const char *namespace,
16236 : const char *owner,
16237 : CatalogId catalogId, DumpId dumpId)
16238 : {
16239 0 : PQExpBuffer q;
16240 0 : PQExpBuffer delq;
16241 0 : PQExpBuffer query;
16242 0 : PQExpBuffer tag;
16243 0 : PGresult *res;
16244 0 : int ntups;
16245 0 : int i_usename;
16246 0 : int i_umoptions;
16247 0 : int i;
16248 :
16249 0 : q = createPQExpBuffer();
16250 0 : tag = createPQExpBuffer();
16251 0 : delq = createPQExpBuffer();
16252 0 : query = createPQExpBuffer();
16253 :
16254 : /*
16255 : * We read from the publicly accessible view pg_user_mappings, so as not
16256 : * to fail if run by a non-superuser. Note that the view will show
16257 : * umoptions as null if the user hasn't got privileges for the associated
16258 : * server; this means that pg_dump will dump such a mapping, but with no
16259 : * OPTIONS clause. A possible alternative is to skip such mappings
16260 : * altogether, but it's not clear that that's an improvement.
16261 : */
16262 0 : appendPQExpBuffer(query,
16263 : "SELECT usename, "
16264 : "array_to_string(ARRAY("
16265 : "SELECT quote_ident(option_name) || ' ' || "
16266 : "quote_literal(option_value) "
16267 : "FROM pg_options_to_table(umoptions) "
16268 : "ORDER BY option_name"
16269 : "), E',\n ') AS umoptions "
16270 : "FROM pg_user_mappings "
16271 : "WHERE srvid = '%u' "
16272 : "ORDER BY usename",
16273 0 : catalogId.oid);
16274 :
16275 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16276 :
16277 0 : ntups = PQntuples(res);
16278 0 : i_usename = PQfnumber(res, "usename");
16279 0 : i_umoptions = PQfnumber(res, "umoptions");
16280 :
16281 0 : for (i = 0; i < ntups; i++)
16282 : {
16283 0 : char *usename;
16284 0 : char *umoptions;
16285 :
16286 0 : usename = PQgetvalue(res, i, i_usename);
16287 0 : umoptions = PQgetvalue(res, i, i_umoptions);
16288 :
16289 0 : resetPQExpBuffer(q);
16290 0 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16291 0 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16292 :
16293 0 : if (umoptions && strlen(umoptions) > 0)
16294 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16295 :
16296 0 : appendPQExpBufferStr(q, ";\n");
16297 :
16298 0 : resetPQExpBuffer(delq);
16299 0 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16300 0 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16301 :
16302 0 : resetPQExpBuffer(tag);
16303 0 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16304 0 : usename, servername);
16305 :
16306 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16307 0 : ARCHIVE_OPTS(.tag = tag->data,
16308 : .namespace = namespace,
16309 : .owner = owner,
16310 : .description = "USER MAPPING",
16311 : .section = SECTION_PRE_DATA,
16312 : .createStmt = q->data,
16313 : .dropStmt = delq->data));
16314 0 : }
16315 :
16316 0 : PQclear(res);
16317 :
16318 0 : destroyPQExpBuffer(query);
16319 0 : destroyPQExpBuffer(delq);
16320 0 : destroyPQExpBuffer(tag);
16321 0 : destroyPQExpBuffer(q);
16322 0 : }
16323 :
16324 : /*
16325 : * Write out default privileges information
16326 : */
16327 : static void
16328 0 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16329 : {
16330 0 : DumpOptions *dopt = fout->dopt;
16331 0 : PQExpBuffer q;
16332 0 : PQExpBuffer tag;
16333 0 : const char *type;
16334 :
16335 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16336 0 : if (!dopt->dumpSchema || dopt->aclsSkip)
16337 0 : return;
16338 :
16339 0 : q = createPQExpBuffer();
16340 0 : tag = createPQExpBuffer();
16341 :
16342 0 : switch (daclinfo->defaclobjtype)
16343 : {
16344 : case DEFACLOBJ_RELATION:
16345 0 : type = "TABLES";
16346 0 : break;
16347 : case DEFACLOBJ_SEQUENCE:
16348 0 : type = "SEQUENCES";
16349 0 : break;
16350 : case DEFACLOBJ_FUNCTION:
16351 0 : type = "FUNCTIONS";
16352 0 : break;
16353 : case DEFACLOBJ_TYPE:
16354 0 : type = "TYPES";
16355 0 : break;
16356 : case DEFACLOBJ_NAMESPACE:
16357 0 : type = "SCHEMAS";
16358 0 : break;
16359 : case DEFACLOBJ_LARGEOBJECT:
16360 0 : type = "LARGE OBJECTS";
16361 0 : break;
16362 : default:
16363 : /* shouldn't get here */
16364 0 : pg_fatal("unrecognized object type in default privileges: %d",
16365 : (int) daclinfo->defaclobjtype);
16366 0 : type = ""; /* keep compiler quiet */
16367 0 : }
16368 :
16369 0 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16370 :
16371 : /* build the actual command(s) for this tuple */
16372 0 : if (!buildDefaultACLCommands(type,
16373 0 : daclinfo->dobj.namespace != NULL ?
16374 0 : daclinfo->dobj.namespace->dobj.name : NULL,
16375 0 : daclinfo->dacl.acl,
16376 0 : daclinfo->dacl.acldefault,
16377 0 : daclinfo->defaclrole,
16378 0 : fout->remoteVersion,
16379 0 : q))
16380 0 : pg_fatal("could not parse default ACL list (%s)",
16381 : daclinfo->dacl.acl);
16382 :
16383 0 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16384 0 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16385 0 : ARCHIVE_OPTS(.tag = tag->data,
16386 : .namespace = daclinfo->dobj.namespace ?
16387 : daclinfo->dobj.namespace->dobj.name : NULL,
16388 : .owner = daclinfo->defaclrole,
16389 : .description = "DEFAULT ACL",
16390 : .section = SECTION_POST_DATA,
16391 : .createStmt = q->data));
16392 :
16393 0 : destroyPQExpBuffer(tag);
16394 0 : destroyPQExpBuffer(q);
16395 0 : }
16396 :
16397 : /*----------
16398 : * Write out grant/revoke information
16399 : *
16400 : * 'objDumpId' is the dump ID of the underlying object.
16401 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16402 : * or InvalidDumpId if there is no need for a second dependency.
16403 : * 'type' must be one of
16404 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16405 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16406 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16407 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16408 : * (Currently we assume that subname is only provided for table columns.)
16409 : * 'nspname' is the namespace the object is in (NULL if none).
16410 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16411 : * to use the default for the object type.
16412 : * 'owner' is the owner, NULL if there is no owner (for languages).
16413 : * 'dacl' is the DumpableAcl struct for the object.
16414 : *
16415 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16416 : * no ACL entry was created.
16417 : *----------
16418 : */
16419 : static DumpId
16420 0 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16421 : const char *type, const char *name, const char *subname,
16422 : const char *nspname, const char *tag, const char *owner,
16423 : const DumpableAcl *dacl)
16424 : {
16425 0 : DumpId aclDumpId = InvalidDumpId;
16426 0 : DumpOptions *dopt = fout->dopt;
16427 0 : const char *acls = dacl->acl;
16428 0 : const char *acldefault = dacl->acldefault;
16429 0 : char privtype = dacl->privtype;
16430 0 : const char *initprivs = dacl->initprivs;
16431 0 : const char *baseacls;
16432 0 : PQExpBuffer sql;
16433 :
16434 : /* Do nothing if ACL dump is not enabled */
16435 0 : if (dopt->aclsSkip)
16436 0 : return InvalidDumpId;
16437 :
16438 : /* --data-only skips ACLs *except* large object ACLs */
16439 0 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16440 0 : return InvalidDumpId;
16441 :
16442 0 : sql = createPQExpBuffer();
16443 :
16444 : /*
16445 : * In binary upgrade mode, we don't run an extension's script but instead
16446 : * dump out the objects independently and then recreate them. To preserve
16447 : * any initial privileges which were set on extension objects, we need to
16448 : * compute the set of GRANT and REVOKE commands necessary to get from the
16449 : * default privileges of an object to its initial privileges as recorded
16450 : * in pg_init_privs.
16451 : *
16452 : * At restore time, we apply these commands after having called
16453 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16454 : * copy the results into pg_init_privs. This is how we preserve the
16455 : * contents of that catalog across binary upgrades.
16456 : */
16457 0 : if (dopt->binary_upgrade && privtype == 'e' &&
16458 0 : initprivs && *initprivs != '\0')
16459 : {
16460 0 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16461 0 : if (!buildACLCommands(name, subname, nspname, type,
16462 0 : initprivs, acldefault, owner,
16463 0 : "", fout->remoteVersion, sql))
16464 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16465 : initprivs, acldefault, name, type);
16466 0 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16467 0 : }
16468 :
16469 : /*
16470 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16471 : * actual current ACL, starting from the initprivs if given, else from the
16472 : * object-type-specific default. Also, while buildACLCommands will assume
16473 : * that a NULL/empty acls string means it needn't do anything, what that
16474 : * actually represents is the object-type-specific default; so we need to
16475 : * substitute the acldefault string to get the right results in that case.
16476 : */
16477 0 : if (initprivs && *initprivs != '\0')
16478 : {
16479 0 : baseacls = initprivs;
16480 0 : if (acls == NULL || *acls == '\0')
16481 0 : acls = acldefault;
16482 0 : }
16483 : else
16484 0 : baseacls = acldefault;
16485 :
16486 0 : if (!buildACLCommands(name, subname, nspname, type,
16487 0 : acls, baseacls, owner,
16488 0 : "", fout->remoteVersion, sql))
16489 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16490 : acls, baseacls, name, type);
16491 :
16492 0 : if (sql->len > 0)
16493 : {
16494 0 : PQExpBuffer tagbuf = createPQExpBuffer();
16495 0 : DumpId aclDeps[2];
16496 0 : int nDeps = 0;
16497 :
16498 0 : if (tag)
16499 0 : appendPQExpBufferStr(tagbuf, tag);
16500 0 : else if (subname)
16501 0 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16502 : else
16503 0 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16504 :
16505 0 : aclDeps[nDeps++] = objDumpId;
16506 0 : if (altDumpId != InvalidDumpId)
16507 0 : aclDeps[nDeps++] = altDumpId;
16508 :
16509 0 : aclDumpId = createDumpId();
16510 :
16511 0 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16512 0 : ARCHIVE_OPTS(.tag = tagbuf->data,
16513 : .namespace = nspname,
16514 : .owner = owner,
16515 : .description = "ACL",
16516 : .section = SECTION_NONE,
16517 : .createStmt = sql->data,
16518 : .deps = aclDeps,
16519 : .nDeps = nDeps));
16520 :
16521 0 : destroyPQExpBuffer(tagbuf);
16522 0 : }
16523 :
16524 0 : destroyPQExpBuffer(sql);
16525 :
16526 0 : return aclDumpId;
16527 0 : }
16528 :
16529 : /*
16530 : * dumpSecLabel
16531 : *
16532 : * This routine is used to dump any security labels associated with the
16533 : * object handed to this routine. The routine takes the object type
16534 : * and object name (ready to print, except for schema decoration), plus
16535 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16536 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16537 : * plus the dump ID for the object (for setting a dependency).
16538 : * If a matching pg_seclabel entry is found, it is dumped.
16539 : *
16540 : * Note: although this routine takes a dumpId for dependency purposes,
16541 : * that purpose is just to mark the dependency in the emitted dump file
16542 : * for possible future use by pg_restore. We do NOT use it for determining
16543 : * ordering of the label in the dump file, because this routine is called
16544 : * after dependency sorting occurs. This routine should be called just after
16545 : * calling ArchiveEntry() for the specified object.
16546 : */
16547 : static void
16548 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16549 : const char *namespace, const char *owner,
16550 : CatalogId catalogId, int subid, DumpId dumpId)
16551 : {
16552 0 : DumpOptions *dopt = fout->dopt;
16553 0 : SecLabelItem *labels;
16554 0 : int nlabels;
16555 0 : int i;
16556 0 : PQExpBuffer query;
16557 :
16558 : /* do nothing, if --no-security-labels is supplied */
16559 0 : if (dopt->no_security_labels)
16560 0 : return;
16561 :
16562 : /*
16563 : * Security labels are schema not data ... except large object labels are
16564 : * data
16565 : */
16566 0 : if (strcmp(type, "LARGE OBJECT") != 0)
16567 : {
16568 0 : if (!dopt->dumpSchema)
16569 0 : return;
16570 0 : }
16571 : else
16572 : {
16573 : /* We do dump large object security labels in binary-upgrade mode */
16574 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
16575 0 : return;
16576 : }
16577 :
16578 : /* Search for security labels associated with catalogId, using table */
16579 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16580 :
16581 0 : query = createPQExpBuffer();
16582 :
16583 0 : for (i = 0; i < nlabels; i++)
16584 : {
16585 : /*
16586 : * Ignore label entries for which the subid doesn't match.
16587 : */
16588 0 : if (labels[i].objsubid != subid)
16589 0 : continue;
16590 :
16591 0 : appendPQExpBuffer(query,
16592 : "SECURITY LABEL FOR %s ON %s ",
16593 0 : fmtId(labels[i].provider), type);
16594 0 : if (namespace && *namespace)
16595 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16596 0 : appendPQExpBuffer(query, "%s IS ", name);
16597 0 : appendStringLiteralAH(query, labels[i].label, fout);
16598 0 : appendPQExpBufferStr(query, ";\n");
16599 0 : }
16600 :
16601 0 : if (query->len > 0)
16602 : {
16603 0 : PQExpBuffer tag = createPQExpBuffer();
16604 :
16605 0 : appendPQExpBuffer(tag, "%s %s", type, name);
16606 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16607 0 : ARCHIVE_OPTS(.tag = tag->data,
16608 : .namespace = namespace,
16609 : .owner = owner,
16610 : .description = "SECURITY LABEL",
16611 : .section = SECTION_NONE,
16612 : .createStmt = query->data,
16613 : .deps = &dumpId,
16614 : .nDeps = 1));
16615 0 : destroyPQExpBuffer(tag);
16616 0 : }
16617 :
16618 0 : destroyPQExpBuffer(query);
16619 0 : }
16620 :
16621 : /*
16622 : * dumpTableSecLabel
16623 : *
16624 : * As above, but dump security label for both the specified table (or view)
16625 : * and its columns.
16626 : */
16627 : static void
16628 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16629 : {
16630 0 : DumpOptions *dopt = fout->dopt;
16631 0 : SecLabelItem *labels;
16632 0 : int nlabels;
16633 0 : int i;
16634 0 : PQExpBuffer query;
16635 0 : PQExpBuffer target;
16636 :
16637 : /* do nothing, if --no-security-labels is supplied */
16638 0 : if (dopt->no_security_labels)
16639 0 : return;
16640 :
16641 : /* SecLabel are SCHEMA not data */
16642 0 : if (!dopt->dumpSchema)
16643 0 : return;
16644 :
16645 : /* Search for comments associated with relation, using table */
16646 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16647 0 : tbinfo->dobj.catId.oid,
16648 : &labels);
16649 :
16650 : /* If security labels exist, build SECURITY LABEL statements */
16651 0 : if (nlabels <= 0)
16652 0 : return;
16653 :
16654 0 : query = createPQExpBuffer();
16655 0 : target = createPQExpBuffer();
16656 :
16657 0 : for (i = 0; i < nlabels; i++)
16658 : {
16659 0 : const char *colname;
16660 0 : const char *provider = labels[i].provider;
16661 0 : const char *label = labels[i].label;
16662 0 : int objsubid = labels[i].objsubid;
16663 :
16664 0 : resetPQExpBuffer(target);
16665 0 : if (objsubid == 0)
16666 : {
16667 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16668 0 : fmtQualifiedDumpable(tbinfo));
16669 0 : }
16670 : else
16671 : {
16672 0 : colname = getAttrName(objsubid, tbinfo);
16673 : /* first fmtXXX result must be consumed before calling again */
16674 0 : appendPQExpBuffer(target, "COLUMN %s",
16675 0 : fmtQualifiedDumpable(tbinfo));
16676 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16677 : }
16678 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16679 0 : fmtId(provider), target->data);
16680 0 : appendStringLiteralAH(query, label, fout);
16681 0 : appendPQExpBufferStr(query, ";\n");
16682 0 : }
16683 0 : if (query->len > 0)
16684 : {
16685 0 : resetPQExpBuffer(target);
16686 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16687 0 : fmtId(tbinfo->dobj.name));
16688 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16689 0 : ARCHIVE_OPTS(.tag = target->data,
16690 : .namespace = tbinfo->dobj.namespace->dobj.name,
16691 : .owner = tbinfo->rolname,
16692 : .description = "SECURITY LABEL",
16693 : .section = SECTION_NONE,
16694 : .createStmt = query->data,
16695 : .deps = &(tbinfo->dobj.dumpId),
16696 : .nDeps = 1));
16697 0 : }
16698 0 : destroyPQExpBuffer(query);
16699 0 : destroyPQExpBuffer(target);
16700 0 : }
16701 :
16702 : /*
16703 : * findSecLabels
16704 : *
16705 : * Find the security label(s), if any, associated with the given object.
16706 : * All the objsubid values associated with the given classoid/objoid are
16707 : * found with one search.
16708 : */
16709 : static int
16710 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16711 : {
16712 0 : SecLabelItem *middle = NULL;
16713 0 : SecLabelItem *low;
16714 0 : SecLabelItem *high;
16715 0 : int nmatch;
16716 :
16717 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16718 : {
16719 0 : *items = NULL;
16720 0 : return 0;
16721 : }
16722 :
16723 : /*
16724 : * Do binary search to find some item matching the object.
16725 : */
16726 0 : low = &seclabels[0];
16727 0 : high = &seclabels[nseclabels - 1];
16728 0 : while (low <= high)
16729 : {
16730 0 : middle = low + (high - low) / 2;
16731 :
16732 0 : if (classoid < middle->classoid)
16733 0 : high = middle - 1;
16734 0 : else if (classoid > middle->classoid)
16735 0 : low = middle + 1;
16736 0 : else if (objoid < middle->objoid)
16737 0 : high = middle - 1;
16738 0 : else if (objoid > middle->objoid)
16739 0 : low = middle + 1;
16740 : else
16741 0 : break; /* found a match */
16742 : }
16743 :
16744 0 : if (low > high) /* no matches */
16745 : {
16746 0 : *items = NULL;
16747 0 : return 0;
16748 : }
16749 :
16750 : /*
16751 : * Now determine how many items match the object. The search loop
16752 : * invariant still holds: only items between low and high inclusive could
16753 : * match.
16754 : */
16755 0 : nmatch = 1;
16756 0 : while (middle > low)
16757 : {
16758 0 : if (classoid != middle[-1].classoid ||
16759 0 : objoid != middle[-1].objoid)
16760 0 : break;
16761 0 : middle--;
16762 0 : nmatch++;
16763 : }
16764 :
16765 0 : *items = middle;
16766 :
16767 0 : middle += nmatch;
16768 0 : while (middle <= high)
16769 : {
16770 0 : if (classoid != middle->classoid ||
16771 0 : objoid != middle->objoid)
16772 0 : break;
16773 0 : middle++;
16774 0 : nmatch++;
16775 : }
16776 :
16777 0 : return nmatch;
16778 0 : }
16779 :
16780 : /*
16781 : * collectSecLabels
16782 : *
16783 : * Construct a table of all security labels available for database objects;
16784 : * also set the has-seclabel component flag for each relevant object.
16785 : *
16786 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16787 : */
16788 : static void
16789 0 : collectSecLabels(Archive *fout)
16790 : {
16791 0 : PGresult *res;
16792 0 : PQExpBuffer query;
16793 0 : int i_label;
16794 0 : int i_provider;
16795 0 : int i_classoid;
16796 0 : int i_objoid;
16797 0 : int i_objsubid;
16798 0 : int ntups;
16799 0 : int i;
16800 0 : DumpableObject *dobj;
16801 :
16802 0 : query = createPQExpBuffer();
16803 :
16804 0 : appendPQExpBufferStr(query,
16805 : "SELECT label, provider, classoid, objoid, objsubid "
16806 : "FROM pg_catalog.pg_seclabels "
16807 : "ORDER BY classoid, objoid, objsubid");
16808 :
16809 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16810 :
16811 : /* Construct lookup table containing OIDs in numeric form */
16812 0 : i_label = PQfnumber(res, "label");
16813 0 : i_provider = PQfnumber(res, "provider");
16814 0 : i_classoid = PQfnumber(res, "classoid");
16815 0 : i_objoid = PQfnumber(res, "objoid");
16816 0 : i_objsubid = PQfnumber(res, "objsubid");
16817 :
16818 0 : ntups = PQntuples(res);
16819 :
16820 0 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16821 0 : nseclabels = 0;
16822 0 : dobj = NULL;
16823 :
16824 0 : for (i = 0; i < ntups; i++)
16825 : {
16826 0 : CatalogId objId;
16827 0 : int subid;
16828 :
16829 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16830 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16831 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16832 :
16833 : /* We needn't remember labels that don't match any dumpable object */
16834 0 : if (dobj == NULL ||
16835 0 : dobj->catId.tableoid != objId.tableoid ||
16836 0 : dobj->catId.oid != objId.oid)
16837 0 : dobj = findObjectByCatalogId(objId);
16838 0 : if (dobj == NULL)
16839 0 : continue;
16840 :
16841 : /*
16842 : * Labels on columns of composite types are linked to the type's
16843 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16844 : * in the type's own DumpableObject.
16845 : */
16846 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16847 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16848 : {
16849 0 : TypeInfo *cTypeInfo;
16850 :
16851 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16852 0 : if (cTypeInfo)
16853 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16854 0 : }
16855 : else
16856 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16857 :
16858 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16859 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16860 0 : seclabels[nseclabels].classoid = objId.tableoid;
16861 0 : seclabels[nseclabels].objoid = objId.oid;
16862 0 : seclabels[nseclabels].objsubid = subid;
16863 0 : nseclabels++;
16864 0 : }
16865 :
16866 0 : PQclear(res);
16867 0 : destroyPQExpBuffer(query);
16868 0 : }
16869 :
16870 : /*
16871 : * dumpTable
16872 : * write out to fout the declarations (not data) of a user-defined table
16873 : */
16874 : static void
16875 0 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16876 : {
16877 0 : DumpOptions *dopt = fout->dopt;
16878 0 : DumpId tableAclDumpId = InvalidDumpId;
16879 0 : char *namecopy;
16880 :
16881 : /* Do nothing if not dumping schema */
16882 0 : if (!dopt->dumpSchema)
16883 0 : return;
16884 :
16885 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16886 : {
16887 0 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16888 0 : dumpSequence(fout, tbinfo);
16889 : else
16890 0 : dumpTableSchema(fout, tbinfo);
16891 0 : }
16892 :
16893 : /* Handle the ACL here */
16894 0 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16895 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16896 : {
16897 0 : const char *objtype =
16898 0 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16899 :
16900 0 : tableAclDumpId =
16901 0 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16902 0 : objtype, namecopy, NULL,
16903 0 : tbinfo->dobj.namespace->dobj.name,
16904 0 : NULL, tbinfo->rolname, &tbinfo->dacl);
16905 0 : }
16906 :
16907 : /*
16908 : * Handle column ACLs, if any. Note: we pull these with a separate query
16909 : * rather than trying to fetch them during getTableAttrs, so that we won't
16910 : * miss ACLs on system columns. Doing it this way also allows us to dump
16911 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16912 : */
16913 0 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16914 : {
16915 0 : PQExpBuffer query = createPQExpBuffer();
16916 0 : PGresult *res;
16917 0 : int i;
16918 :
16919 0 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16920 : {
16921 : /* Set up query for column ACLs */
16922 0 : appendPQExpBufferStr(query,
16923 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16924 :
16925 0 : if (fout->remoteVersion >= 90600)
16926 : {
16927 : /*
16928 : * In principle we should call acldefault('c', relowner) to
16929 : * get the default ACL for a column. However, we don't
16930 : * currently store the numeric OID of the relowner in
16931 : * TableInfo. We could convert the owner name using regrole,
16932 : * but that creates a risk of failure due to concurrent role
16933 : * renames. Given that the default ACL for columns is empty
16934 : * and is likely to stay that way, it's not worth extra cycles
16935 : * and risk to avoid hard-wiring that knowledge here.
16936 : */
16937 0 : appendPQExpBufferStr(query,
16938 : "SELECT at.attname, "
16939 : "at.attacl, "
16940 : "'{}' AS acldefault, "
16941 : "pip.privtype, pip.initprivs "
16942 : "FROM pg_catalog.pg_attribute at "
16943 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16944 : "(at.attrelid = pip.objoid "
16945 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16946 : "AND at.attnum = pip.objsubid) "
16947 : "WHERE at.attrelid = $1 AND "
16948 : "NOT at.attisdropped "
16949 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16950 : "ORDER BY at.attnum");
16951 0 : }
16952 : else
16953 : {
16954 0 : appendPQExpBufferStr(query,
16955 : "SELECT attname, attacl, '{}' AS acldefault, "
16956 : "NULL AS privtype, NULL AS initprivs "
16957 : "FROM pg_catalog.pg_attribute "
16958 : "WHERE attrelid = $1 AND NOT attisdropped "
16959 : "AND attacl IS NOT NULL "
16960 : "ORDER BY attnum");
16961 : }
16962 :
16963 0 : ExecuteSqlStatement(fout, query->data);
16964 :
16965 0 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16966 0 : }
16967 :
16968 0 : printfPQExpBuffer(query,
16969 : "EXECUTE getColumnACLs('%u')",
16970 0 : tbinfo->dobj.catId.oid);
16971 :
16972 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16973 :
16974 0 : for (i = 0; i < PQntuples(res); i++)
16975 : {
16976 0 : char *attname = PQgetvalue(res, i, 0);
16977 0 : char *attacl = PQgetvalue(res, i, 1);
16978 0 : char *acldefault = PQgetvalue(res, i, 2);
16979 0 : char privtype = *(PQgetvalue(res, i, 3));
16980 0 : char *initprivs = PQgetvalue(res, i, 4);
16981 0 : DumpableAcl coldacl;
16982 0 : char *attnamecopy;
16983 :
16984 0 : coldacl.acl = attacl;
16985 0 : coldacl.acldefault = acldefault;
16986 0 : coldacl.privtype = privtype;
16987 0 : coldacl.initprivs = initprivs;
16988 0 : attnamecopy = pg_strdup(fmtId(attname));
16989 :
16990 : /*
16991 : * Column's GRANT type is always TABLE. Each column ACL depends
16992 : * on the table-level ACL, since we can restore column ACLs in
16993 : * parallel but the table-level ACL has to be done first.
16994 : */
16995 0 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16996 0 : "TABLE", namecopy, attnamecopy,
16997 0 : tbinfo->dobj.namespace->dobj.name,
16998 0 : NULL, tbinfo->rolname, &coldacl);
16999 0 : free(attnamecopy);
17000 0 : }
17001 0 : PQclear(res);
17002 0 : destroyPQExpBuffer(query);
17003 0 : }
17004 :
17005 0 : free(namecopy);
17006 0 : }
17007 :
17008 : /*
17009 : * Create the AS clause for a view or materialized view. The semicolon is
17010 : * stripped because a materialized view must add a WITH NO DATA clause.
17011 : *
17012 : * This returns a new buffer which must be freed by the caller.
17013 : */
17014 : static PQExpBuffer
17015 0 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
17016 : {
17017 0 : PQExpBuffer query = createPQExpBuffer();
17018 0 : PQExpBuffer result = createPQExpBuffer();
17019 0 : PGresult *res;
17020 0 : int len;
17021 :
17022 : /* Fetch the view definition */
17023 0 : appendPQExpBuffer(query,
17024 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17025 0 : tbinfo->dobj.catId.oid);
17026 :
17027 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17028 :
17029 0 : if (PQntuples(res) != 1)
17030 : {
17031 0 : if (PQntuples(res) < 1)
17032 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17033 : tbinfo->dobj.name);
17034 : else
17035 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17036 : tbinfo->dobj.name);
17037 0 : }
17038 :
17039 0 : len = PQgetlength(res, 0, 0);
17040 :
17041 0 : if (len == 0)
17042 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17043 : tbinfo->dobj.name);
17044 :
17045 : /* Strip off the trailing semicolon so that other things may follow. */
17046 0 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17047 0 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17048 :
17049 0 : PQclear(res);
17050 0 : destroyPQExpBuffer(query);
17051 :
17052 0 : return result;
17053 0 : }
17054 :
17055 : /*
17056 : * Create a dummy AS clause for a view. This is used when the real view
17057 : * definition has to be postponed because of circular dependencies.
17058 : * We must duplicate the view's external properties -- column names and types
17059 : * (including collation) -- so that it works for subsequent references.
17060 : *
17061 : * This returns a new buffer which must be freed by the caller.
17062 : */
17063 : static PQExpBuffer
17064 0 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17065 : {
17066 0 : PQExpBuffer result = createPQExpBuffer();
17067 0 : int j;
17068 :
17069 0 : appendPQExpBufferStr(result, "SELECT");
17070 :
17071 0 : for (j = 0; j < tbinfo->numatts; j++)
17072 : {
17073 0 : if (j > 0)
17074 0 : appendPQExpBufferChar(result, ',');
17075 0 : appendPQExpBufferStr(result, "\n ");
17076 :
17077 0 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17078 :
17079 : /*
17080 : * Must add collation if not default for the type, because CREATE OR
17081 : * REPLACE VIEW won't change it
17082 : */
17083 0 : if (OidIsValid(tbinfo->attcollation[j]))
17084 : {
17085 0 : CollInfo *coll;
17086 :
17087 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17088 0 : if (coll)
17089 0 : appendPQExpBuffer(result, " COLLATE %s",
17090 0 : fmtQualifiedDumpable(coll));
17091 0 : }
17092 :
17093 0 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17094 0 : }
17095 :
17096 0 : return result;
17097 0 : }
17098 :
17099 : /*
17100 : * dumpTableSchema
17101 : * write the declaration (not data) of one user-defined table or view
17102 : */
17103 : static void
17104 0 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17105 : {
17106 0 : DumpOptions *dopt = fout->dopt;
17107 0 : PQExpBuffer q = createPQExpBuffer();
17108 0 : PQExpBuffer delq = createPQExpBuffer();
17109 0 : PQExpBuffer extra = createPQExpBuffer();
17110 0 : char *qrelname;
17111 0 : char *qualrelname;
17112 0 : int numParents;
17113 0 : TableInfo **parents;
17114 0 : int actual_atts; /* number of attrs in this CREATE statement */
17115 0 : const char *reltypename;
17116 0 : char *storage;
17117 0 : int j,
17118 : k;
17119 :
17120 : /* We had better have loaded per-column details about this table */
17121 0 : Assert(tbinfo->interesting);
17122 :
17123 0 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17124 0 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17125 :
17126 0 : if (tbinfo->hasoids)
17127 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17128 : qrelname);
17129 :
17130 0 : if (dopt->binary_upgrade)
17131 0 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17132 :
17133 : /* Is it a table or a view? */
17134 0 : if (tbinfo->relkind == RELKIND_VIEW)
17135 : {
17136 0 : PQExpBuffer result;
17137 :
17138 : /*
17139 : * Note: keep this code in sync with the is_view case in dumpRule()
17140 : */
17141 :
17142 0 : reltypename = "VIEW";
17143 :
17144 0 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17145 :
17146 0 : if (dopt->binary_upgrade)
17147 0 : binary_upgrade_set_pg_class_oids(fout, q,
17148 0 : tbinfo->dobj.catId.oid);
17149 :
17150 0 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17151 :
17152 0 : if (tbinfo->dummy_view)
17153 0 : result = createDummyViewAsClause(fout, tbinfo);
17154 : else
17155 : {
17156 0 : if (nonemptyReloptions(tbinfo->reloptions))
17157 : {
17158 0 : appendPQExpBufferStr(q, " WITH (");
17159 0 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17160 0 : appendPQExpBufferChar(q, ')');
17161 0 : }
17162 0 : result = createViewAsClause(fout, tbinfo);
17163 : }
17164 0 : appendPQExpBuffer(q, " AS\n%s", result->data);
17165 0 : destroyPQExpBuffer(result);
17166 :
17167 0 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17168 0 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17169 0 : appendPQExpBufferStr(q, ";\n");
17170 0 : }
17171 : else
17172 : {
17173 0 : char *partkeydef = NULL;
17174 0 : char *ftoptions = NULL;
17175 0 : char *srvname = NULL;
17176 0 : const char *foreign = "";
17177 :
17178 : /*
17179 : * Set reltypename, and collect any relkind-specific data that we
17180 : * didn't fetch during getTables().
17181 : */
17182 0 : switch (tbinfo->relkind)
17183 : {
17184 : case RELKIND_PARTITIONED_TABLE:
17185 : {
17186 0 : PQExpBuffer query = createPQExpBuffer();
17187 0 : PGresult *res;
17188 :
17189 0 : reltypename = "TABLE";
17190 :
17191 : /* retrieve partition key definition */
17192 0 : appendPQExpBuffer(query,
17193 : "SELECT pg_get_partkeydef('%u')",
17194 0 : tbinfo->dobj.catId.oid);
17195 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17196 0 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17197 0 : PQclear(res);
17198 0 : destroyPQExpBuffer(query);
17199 : break;
17200 0 : }
17201 : case RELKIND_FOREIGN_TABLE:
17202 : {
17203 0 : PQExpBuffer query = createPQExpBuffer();
17204 0 : PGresult *res;
17205 0 : int i_srvname;
17206 0 : int i_ftoptions;
17207 :
17208 0 : reltypename = "FOREIGN TABLE";
17209 :
17210 : /* retrieve name of foreign server and generic options */
17211 0 : appendPQExpBuffer(query,
17212 : "SELECT fs.srvname, "
17213 : "pg_catalog.array_to_string(ARRAY("
17214 : "SELECT pg_catalog.quote_ident(option_name) || "
17215 : "' ' || pg_catalog.quote_literal(option_value) "
17216 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17217 : "ORDER BY option_name"
17218 : "), E',\n ') AS ftoptions "
17219 : "FROM pg_catalog.pg_foreign_table ft "
17220 : "JOIN pg_catalog.pg_foreign_server fs "
17221 : "ON (fs.oid = ft.ftserver) "
17222 : "WHERE ft.ftrelid = '%u'",
17223 0 : tbinfo->dobj.catId.oid);
17224 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17225 0 : i_srvname = PQfnumber(res, "srvname");
17226 0 : i_ftoptions = PQfnumber(res, "ftoptions");
17227 0 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17228 0 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17229 0 : PQclear(res);
17230 0 : destroyPQExpBuffer(query);
17231 :
17232 0 : foreign = "FOREIGN ";
17233 : break;
17234 0 : }
17235 : case RELKIND_MATVIEW:
17236 0 : reltypename = "MATERIALIZED VIEW";
17237 0 : break;
17238 : default:
17239 0 : reltypename = "TABLE";
17240 0 : break;
17241 : }
17242 :
17243 0 : numParents = tbinfo->numParents;
17244 0 : parents = tbinfo->parents;
17245 :
17246 0 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17247 :
17248 0 : if (dopt->binary_upgrade)
17249 0 : binary_upgrade_set_pg_class_oids(fout, q,
17250 0 : tbinfo->dobj.catId.oid);
17251 :
17252 : /*
17253 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17254 : * ignore it when dumping if it was set in this case.
17255 : */
17256 0 : appendPQExpBuffer(q, "CREATE %s%s %s",
17257 0 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17258 0 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17259 : "UNLOGGED " : "",
17260 0 : reltypename,
17261 0 : qualrelname);
17262 :
17263 : /*
17264 : * Attach to type, if reloftype; except in case of a binary upgrade,
17265 : * we dump the table normally and attach it to the type afterward.
17266 : */
17267 0 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17268 0 : appendPQExpBuffer(q, " OF %s",
17269 0 : getFormattedTypeName(fout, tbinfo->reloftype,
17270 : zeroIsError));
17271 :
17272 0 : if (tbinfo->relkind != RELKIND_MATVIEW)
17273 : {
17274 : /* Dump the attributes */
17275 0 : actual_atts = 0;
17276 0 : for (j = 0; j < tbinfo->numatts; j++)
17277 : {
17278 : /*
17279 : * Normally, dump if it's locally defined in this table, and
17280 : * not dropped. But for binary upgrade, we'll dump all the
17281 : * columns, and then fix up the dropped and nonlocal cases
17282 : * below.
17283 : */
17284 0 : if (shouldPrintColumn(dopt, tbinfo, j))
17285 : {
17286 0 : bool print_default;
17287 0 : bool print_notnull;
17288 :
17289 : /*
17290 : * Default value --- suppress if to be printed separately
17291 : * or not at all.
17292 : */
17293 0 : print_default = (tbinfo->attrdefs[j] != NULL &&
17294 0 : tbinfo->attrdefs[j]->dobj.dump &&
17295 0 : !tbinfo->attrdefs[j]->separate);
17296 :
17297 : /*
17298 : * Not Null constraint --- print it if it is locally
17299 : * defined, or if binary upgrade. (In the latter case, we
17300 : * reset conislocal below.)
17301 : */
17302 0 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17303 0 : (tbinfo->notnull_islocal[j] ||
17304 0 : dopt->binary_upgrade ||
17305 0 : tbinfo->ispartition));
17306 :
17307 : /*
17308 : * Skip column if fully defined by reloftype, except in
17309 : * binary upgrade
17310 : */
17311 0 : if (OidIsValid(tbinfo->reloftype) &&
17312 0 : !print_default && !print_notnull &&
17313 0 : !dopt->binary_upgrade)
17314 0 : continue;
17315 :
17316 : /* Format properly if not first attr */
17317 0 : if (actual_atts == 0)
17318 0 : appendPQExpBufferStr(q, " (");
17319 : else
17320 0 : appendPQExpBufferChar(q, ',');
17321 0 : appendPQExpBufferStr(q, "\n ");
17322 0 : actual_atts++;
17323 :
17324 : /* Attribute name */
17325 0 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17326 :
17327 0 : if (tbinfo->attisdropped[j])
17328 : {
17329 : /*
17330 : * ALTER TABLE DROP COLUMN clears
17331 : * pg_attribute.atttypid, so we will not have gotten a
17332 : * valid type name; insert INTEGER as a stopgap. We'll
17333 : * clean things up later.
17334 : */
17335 0 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17336 : /* and skip to the next column */
17337 0 : continue;
17338 : }
17339 :
17340 : /*
17341 : * Attribute type; print it except when creating a typed
17342 : * table ('OF type_name'), but in binary-upgrade mode,
17343 : * print it in that case too.
17344 : */
17345 0 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17346 : {
17347 0 : appendPQExpBuffer(q, " %s",
17348 0 : tbinfo->atttypnames[j]);
17349 0 : }
17350 :
17351 0 : if (print_default)
17352 : {
17353 0 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17354 0 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17355 0 : tbinfo->attrdefs[j]->adef_expr);
17356 0 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17357 0 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17358 0 : tbinfo->attrdefs[j]->adef_expr);
17359 : else
17360 0 : appendPQExpBuffer(q, " DEFAULT %s",
17361 0 : tbinfo->attrdefs[j]->adef_expr);
17362 0 : }
17363 :
17364 0 : if (print_notnull)
17365 : {
17366 0 : if (tbinfo->notnull_constrs[j][0] == '\0')
17367 0 : appendPQExpBufferStr(q, " NOT NULL");
17368 : else
17369 0 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17370 0 : fmtId(tbinfo->notnull_constrs[j]));
17371 :
17372 0 : if (tbinfo->notnull_noinh[j])
17373 0 : appendPQExpBufferStr(q, " NO INHERIT");
17374 0 : }
17375 :
17376 : /* Add collation if not default for the type */
17377 0 : if (OidIsValid(tbinfo->attcollation[j]))
17378 : {
17379 0 : CollInfo *coll;
17380 :
17381 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17382 0 : if (coll)
17383 0 : appendPQExpBuffer(q, " COLLATE %s",
17384 0 : fmtQualifiedDumpable(coll));
17385 0 : }
17386 0 : }
17387 :
17388 : /*
17389 : * On the other hand, if we choose not to print a column
17390 : * (likely because it is created by inheritance), but the
17391 : * column has a locally-defined not-null constraint, we need
17392 : * to dump the constraint as a standalone object.
17393 : *
17394 : * This syntax isn't SQL-conforming, but if you wanted
17395 : * standard output you wouldn't be creating non-standard
17396 : * objects to begin with.
17397 : */
17398 0 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17399 0 : !tbinfo->attisdropped[j] &&
17400 0 : tbinfo->notnull_constrs[j] != NULL &&
17401 0 : tbinfo->notnull_islocal[j])
17402 : {
17403 : /* Format properly if not first attr */
17404 0 : if (actual_atts == 0)
17405 0 : appendPQExpBufferStr(q, " (");
17406 : else
17407 0 : appendPQExpBufferChar(q, ',');
17408 0 : appendPQExpBufferStr(q, "\n ");
17409 0 : actual_atts++;
17410 :
17411 0 : if (tbinfo->notnull_constrs[j][0] == '\0')
17412 0 : appendPQExpBuffer(q, "NOT NULL %s",
17413 0 : fmtId(tbinfo->attnames[j]));
17414 : else
17415 0 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17416 0 : tbinfo->notnull_constrs[j],
17417 0 : fmtId(tbinfo->attnames[j]));
17418 0 : }
17419 0 : }
17420 :
17421 : /*
17422 : * Add non-inherited CHECK constraints, if any.
17423 : *
17424 : * For partitions, we need to include check constraints even if
17425 : * they're not defined locally, because the ALTER TABLE ATTACH
17426 : * PARTITION that we'll emit later expects the constraint to be
17427 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17428 : */
17429 0 : for (j = 0; j < tbinfo->ncheck; j++)
17430 : {
17431 0 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17432 :
17433 0 : if (constr->separate ||
17434 0 : (!constr->conislocal && !tbinfo->ispartition))
17435 0 : continue;
17436 :
17437 0 : if (actual_atts == 0)
17438 0 : appendPQExpBufferStr(q, " (\n ");
17439 : else
17440 0 : appendPQExpBufferStr(q, ",\n ");
17441 :
17442 0 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17443 0 : fmtId(constr->dobj.name));
17444 0 : appendPQExpBufferStr(q, constr->condef);
17445 :
17446 0 : actual_atts++;
17447 0 : }
17448 :
17449 0 : if (actual_atts)
17450 0 : appendPQExpBufferStr(q, "\n)");
17451 0 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17452 : {
17453 : /*
17454 : * No attributes? we must have a parenthesized attribute list,
17455 : * even though empty, when not using the OF TYPE syntax.
17456 : */
17457 0 : appendPQExpBufferStr(q, " (\n)");
17458 0 : }
17459 :
17460 : /*
17461 : * Emit the INHERITS clause (not for partitions), except in
17462 : * binary-upgrade mode.
17463 : */
17464 0 : if (numParents > 0 && !tbinfo->ispartition &&
17465 0 : !dopt->binary_upgrade)
17466 : {
17467 0 : appendPQExpBufferStr(q, "\nINHERITS (");
17468 0 : for (k = 0; k < numParents; k++)
17469 : {
17470 0 : TableInfo *parentRel = parents[k];
17471 :
17472 0 : if (k > 0)
17473 0 : appendPQExpBufferStr(q, ", ");
17474 0 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17475 0 : }
17476 0 : appendPQExpBufferChar(q, ')');
17477 0 : }
17478 :
17479 0 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17480 0 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17481 :
17482 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17483 0 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17484 0 : }
17485 :
17486 0 : if (nonemptyReloptions(tbinfo->reloptions) ||
17487 0 : nonemptyReloptions(tbinfo->toast_reloptions))
17488 : {
17489 0 : bool addcomma = false;
17490 :
17491 0 : appendPQExpBufferStr(q, "\nWITH (");
17492 0 : if (nonemptyReloptions(tbinfo->reloptions))
17493 : {
17494 0 : addcomma = true;
17495 0 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17496 0 : }
17497 0 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17498 : {
17499 0 : if (addcomma)
17500 0 : appendPQExpBufferStr(q, ", ");
17501 0 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17502 0 : fout);
17503 0 : }
17504 0 : appendPQExpBufferChar(q, ')');
17505 0 : }
17506 :
17507 : /* Dump generic options if any */
17508 0 : if (ftoptions && ftoptions[0])
17509 0 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17510 :
17511 : /*
17512 : * For materialized views, create the AS clause just like a view. At
17513 : * this point, we always mark the view as not populated.
17514 : */
17515 0 : if (tbinfo->relkind == RELKIND_MATVIEW)
17516 : {
17517 0 : PQExpBuffer result;
17518 :
17519 0 : result = createViewAsClause(fout, tbinfo);
17520 0 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17521 0 : result->data);
17522 0 : destroyPQExpBuffer(result);
17523 0 : }
17524 : else
17525 0 : appendPQExpBufferStr(q, ";\n");
17526 :
17527 : /* Materialized views can depend on extensions */
17528 0 : if (tbinfo->relkind == RELKIND_MATVIEW)
17529 0 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17530 : "pg_catalog.pg_class",
17531 : "MATERIALIZED VIEW",
17532 0 : qualrelname);
17533 :
17534 : /*
17535 : * in binary upgrade mode, update the catalog with any missing values
17536 : * that might be present.
17537 : */
17538 0 : if (dopt->binary_upgrade)
17539 : {
17540 0 : for (j = 0; j < tbinfo->numatts; j++)
17541 : {
17542 0 : if (tbinfo->attmissingval[j][0] != '\0')
17543 : {
17544 0 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17545 0 : appendPQExpBufferStr(q,
17546 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17547 0 : appendStringLiteralAH(q, qualrelname, fout);
17548 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17549 0 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17550 0 : appendPQExpBufferChar(q, ',');
17551 0 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17552 0 : appendPQExpBufferStr(q, ");\n\n");
17553 0 : }
17554 0 : }
17555 0 : }
17556 :
17557 : /*
17558 : * To create binary-compatible heap files, we have to ensure the same
17559 : * physical column order, including dropped columns, as in the
17560 : * original. Therefore, we create dropped columns above and drop them
17561 : * here, also updating their attlen/attalign values so that the
17562 : * dropped column can be skipped properly. (We do not bother with
17563 : * restoring the original attbyval setting.) Also, inheritance
17564 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17565 : * using an INHERITS clause --- the latter would possibly mess up the
17566 : * column order. That also means we have to take care about setting
17567 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17568 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17569 : *
17570 : * We process foreign and partitioned tables here, even though they
17571 : * lack heap storage, because they can participate in inheritance
17572 : * relationships and we want this stuff to be consistent across the
17573 : * inheritance tree. We can exclude indexes, toast tables, sequences
17574 : * and matviews, even though they have storage, because we don't
17575 : * support altering or dropping columns in them, nor can they be part
17576 : * of inheritance trees.
17577 : */
17578 0 : if (dopt->binary_upgrade &&
17579 0 : (tbinfo->relkind == RELKIND_RELATION ||
17580 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17581 0 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17582 : {
17583 0 : bool firstitem;
17584 0 : bool firstitem_extra;
17585 :
17586 : /*
17587 : * Drop any dropped columns. Merge the pg_attribute manipulations
17588 : * into a single SQL command, so that we don't cause repeated
17589 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17590 : * relcache bloat while dropping N columns.
17591 : */
17592 0 : resetPQExpBuffer(extra);
17593 0 : firstitem = true;
17594 0 : for (j = 0; j < tbinfo->numatts; j++)
17595 : {
17596 0 : if (tbinfo->attisdropped[j])
17597 : {
17598 0 : if (firstitem)
17599 : {
17600 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17601 : "UPDATE pg_catalog.pg_attribute\n"
17602 : "SET attlen = v.dlen, "
17603 : "attalign = v.dalign, "
17604 : "attbyval = false\n"
17605 : "FROM (VALUES ");
17606 0 : firstitem = false;
17607 0 : }
17608 : else
17609 0 : appendPQExpBufferStr(q, ",\n ");
17610 0 : appendPQExpBufferChar(q, '(');
17611 0 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17612 0 : appendPQExpBuffer(q, ", %d, '%c')",
17613 0 : tbinfo->attlen[j],
17614 0 : tbinfo->attalign[j]);
17615 : /* The ALTER ... DROP COLUMN commands must come after */
17616 0 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17617 0 : foreign, qualrelname);
17618 0 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17619 0 : fmtId(tbinfo->attnames[j]));
17620 0 : }
17621 0 : }
17622 0 : if (!firstitem)
17623 : {
17624 0 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17625 : "WHERE attrelid = ");
17626 0 : appendStringLiteralAH(q, qualrelname, fout);
17627 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17628 : " AND attname = v.dname;\n");
17629 : /* Now we can issue the actual DROP COLUMN commands */
17630 0 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17631 0 : }
17632 :
17633 : /*
17634 : * Fix up inherited columns. As above, do the pg_attribute
17635 : * manipulations in a single SQL command.
17636 : */
17637 0 : firstitem = true;
17638 0 : for (j = 0; j < tbinfo->numatts; j++)
17639 : {
17640 0 : if (!tbinfo->attisdropped[j] &&
17641 0 : !tbinfo->attislocal[j])
17642 : {
17643 0 : if (firstitem)
17644 : {
17645 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17646 0 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17647 : "SET attislocal = false\n"
17648 : "WHERE attrelid = ");
17649 0 : appendStringLiteralAH(q, qualrelname, fout);
17650 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17651 : " AND attname IN (");
17652 0 : firstitem = false;
17653 0 : }
17654 : else
17655 0 : appendPQExpBufferStr(q, ", ");
17656 0 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17657 0 : }
17658 0 : }
17659 0 : if (!firstitem)
17660 0 : appendPQExpBufferStr(q, ");\n");
17661 :
17662 : /*
17663 : * Fix up not-null constraints that come from inheritance. As
17664 : * above, do the pg_constraint manipulations in a single SQL
17665 : * command. (Actually, two in special cases, if we're doing an
17666 : * upgrade from < 18).
17667 : */
17668 0 : firstitem = true;
17669 0 : firstitem_extra = true;
17670 0 : resetPQExpBuffer(extra);
17671 0 : for (j = 0; j < tbinfo->numatts; j++)
17672 : {
17673 : /*
17674 : * If a not-null constraint comes from inheritance, reset
17675 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17676 : * below. Special hack: in versions < 18, columns with no
17677 : * local definition need their constraint to be matched by
17678 : * column number in conkeys instead of by constraint name,
17679 : * because the latter is not available. (We distinguish the
17680 : * case because the constraint name is the empty string.)
17681 : */
17682 0 : if (tbinfo->notnull_constrs[j] != NULL &&
17683 0 : !tbinfo->notnull_islocal[j])
17684 : {
17685 0 : if (tbinfo->notnull_constrs[j][0] != '\0')
17686 : {
17687 0 : if (firstitem)
17688 : {
17689 0 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17690 : "SET conislocal = false\n"
17691 : "WHERE contype = 'n' AND conrelid = ");
17692 0 : appendStringLiteralAH(q, qualrelname, fout);
17693 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17694 : "conname IN (");
17695 0 : firstitem = false;
17696 0 : }
17697 : else
17698 0 : appendPQExpBufferStr(q, ", ");
17699 0 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17700 0 : }
17701 : else
17702 : {
17703 0 : if (firstitem_extra)
17704 : {
17705 0 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17706 : "SET conislocal = false\n"
17707 : "WHERE contype = 'n' AND conrelid = ");
17708 0 : appendStringLiteralAH(extra, qualrelname, fout);
17709 0 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17710 : "conkey IN (");
17711 0 : firstitem_extra = false;
17712 0 : }
17713 : else
17714 0 : appendPQExpBufferStr(extra, ", ");
17715 0 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17716 : }
17717 0 : }
17718 0 : }
17719 0 : if (!firstitem)
17720 0 : appendPQExpBufferStr(q, ");\n");
17721 0 : if (!firstitem_extra)
17722 0 : appendPQExpBufferStr(extra, ");\n");
17723 :
17724 0 : if (extra->len > 0)
17725 0 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17726 :
17727 : /*
17728 : * Add inherited CHECK constraints, if any.
17729 : *
17730 : * For partitions, they were already dumped, and conislocal
17731 : * doesn't need fixing.
17732 : *
17733 : * As above, issue only one direct manipulation of pg_constraint.
17734 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17735 : * commands into one as well, refrain for now due to concern about
17736 : * possible backend memory bloat if there are many such
17737 : * constraints.
17738 : */
17739 0 : resetPQExpBuffer(extra);
17740 0 : firstitem = true;
17741 0 : for (k = 0; k < tbinfo->ncheck; k++)
17742 : {
17743 0 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17744 :
17745 0 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17746 0 : continue;
17747 :
17748 0 : if (firstitem)
17749 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17750 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17751 0 : foreign, qualrelname,
17752 0 : fmtId(constr->dobj.name),
17753 0 : constr->condef);
17754 : /* Update pg_constraint after all the ALTER TABLEs */
17755 0 : if (firstitem)
17756 : {
17757 0 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17758 : "SET conislocal = false\n"
17759 : "WHERE contype = 'c' AND conrelid = ");
17760 0 : appendStringLiteralAH(extra, qualrelname, fout);
17761 0 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17762 0 : appendPQExpBufferStr(extra, " AND conname IN (");
17763 0 : firstitem = false;
17764 0 : }
17765 : else
17766 0 : appendPQExpBufferStr(extra, ", ");
17767 0 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17768 0 : }
17769 0 : if (!firstitem)
17770 : {
17771 0 : appendPQExpBufferStr(extra, ");\n");
17772 0 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17773 0 : }
17774 :
17775 0 : if (numParents > 0 && !tbinfo->ispartition)
17776 : {
17777 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17778 0 : for (k = 0; k < numParents; k++)
17779 : {
17780 0 : TableInfo *parentRel = parents[k];
17781 :
17782 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17783 0 : qualrelname,
17784 0 : fmtQualifiedDumpable(parentRel));
17785 0 : }
17786 0 : }
17787 :
17788 0 : if (OidIsValid(tbinfo->reloftype))
17789 : {
17790 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17791 0 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17792 0 : qualrelname,
17793 0 : getFormattedTypeName(fout, tbinfo->reloftype,
17794 : zeroIsError));
17795 0 : }
17796 0 : }
17797 :
17798 : /*
17799 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17800 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17801 : * TOAST tables semi-independently, here we see them only as children
17802 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17803 : * child toast table is handled below.)
17804 : */
17805 0 : if (dopt->binary_upgrade &&
17806 0 : (tbinfo->relkind == RELKIND_RELATION ||
17807 0 : tbinfo->relkind == RELKIND_MATVIEW))
17808 : {
17809 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17810 0 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17811 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17812 : "WHERE oid = ",
17813 0 : tbinfo->frozenxid, tbinfo->minmxid);
17814 0 : appendStringLiteralAH(q, qualrelname, fout);
17815 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17816 :
17817 0 : if (tbinfo->toast_oid)
17818 : {
17819 : /*
17820 : * The toast table will have the same OID at restore, so we
17821 : * can safely target it by OID.
17822 : */
17823 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17824 0 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17825 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17826 : "WHERE oid = '%u';\n",
17827 0 : tbinfo->toast_frozenxid,
17828 0 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17829 0 : }
17830 0 : }
17831 :
17832 : /*
17833 : * In binary_upgrade mode, restore matviews' populated status by
17834 : * poking pg_class directly. This is pretty ugly, but we can't use
17835 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17836 : * matview is not populated even though this matview is; in any case,
17837 : * we want to transfer the matview's heap storage, not run REFRESH.
17838 : */
17839 0 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17840 0 : tbinfo->relispopulated)
17841 : {
17842 0 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17843 0 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17844 : "SET relispopulated = 't'\n"
17845 : "WHERE oid = ");
17846 0 : appendStringLiteralAH(q, qualrelname, fout);
17847 0 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17848 0 : }
17849 :
17850 : /*
17851 : * Dump additional per-column properties that we can't handle in the
17852 : * main CREATE TABLE command.
17853 : */
17854 0 : for (j = 0; j < tbinfo->numatts; j++)
17855 : {
17856 : /* None of this applies to dropped columns */
17857 0 : if (tbinfo->attisdropped[j])
17858 0 : continue;
17859 :
17860 : /*
17861 : * Dump per-column statistics information. We only issue an ALTER
17862 : * TABLE statement if the attstattarget entry for this column is
17863 : * not the default value.
17864 : */
17865 0 : if (tbinfo->attstattarget[j] >= 0)
17866 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17867 0 : foreign, qualrelname,
17868 0 : fmtId(tbinfo->attnames[j]),
17869 0 : tbinfo->attstattarget[j]);
17870 :
17871 : /*
17872 : * Dump per-column storage information. The statement is only
17873 : * dumped if the storage has been changed from the type's default.
17874 : */
17875 0 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17876 : {
17877 0 : switch (tbinfo->attstorage[j])
17878 : {
17879 : case TYPSTORAGE_PLAIN:
17880 0 : storage = "PLAIN";
17881 0 : break;
17882 : case TYPSTORAGE_EXTERNAL:
17883 0 : storage = "EXTERNAL";
17884 0 : break;
17885 : case TYPSTORAGE_EXTENDED:
17886 0 : storage = "EXTENDED";
17887 0 : break;
17888 : case TYPSTORAGE_MAIN:
17889 0 : storage = "MAIN";
17890 0 : break;
17891 : default:
17892 0 : storage = NULL;
17893 0 : }
17894 :
17895 : /*
17896 : * Only dump the statement if it's a storage type we recognize
17897 : */
17898 0 : if (storage != NULL)
17899 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17900 0 : foreign, qualrelname,
17901 0 : fmtId(tbinfo->attnames[j]),
17902 0 : storage);
17903 0 : }
17904 :
17905 : /*
17906 : * Dump per-column compression, if it's been set.
17907 : */
17908 0 : if (!dopt->no_toast_compression)
17909 : {
17910 0 : const char *cmname;
17911 :
17912 0 : switch (tbinfo->attcompression[j])
17913 : {
17914 : case 'p':
17915 0 : cmname = "pglz";
17916 0 : break;
17917 : case 'l':
17918 0 : cmname = "lz4";
17919 0 : break;
17920 : default:
17921 0 : cmname = NULL;
17922 0 : break;
17923 : }
17924 :
17925 0 : if (cmname != NULL)
17926 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17927 0 : foreign, qualrelname,
17928 0 : fmtId(tbinfo->attnames[j]),
17929 0 : cmname);
17930 0 : }
17931 :
17932 : /*
17933 : * Dump per-column attributes.
17934 : */
17935 0 : if (tbinfo->attoptions[j][0] != '\0')
17936 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17937 0 : foreign, qualrelname,
17938 0 : fmtId(tbinfo->attnames[j]),
17939 0 : tbinfo->attoptions[j]);
17940 :
17941 : /*
17942 : * Dump per-column fdw options.
17943 : */
17944 0 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17945 0 : tbinfo->attfdwoptions[j][0] != '\0')
17946 0 : appendPQExpBuffer(q,
17947 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17948 : " %s\n"
17949 : ");\n",
17950 0 : qualrelname,
17951 0 : fmtId(tbinfo->attnames[j]),
17952 0 : tbinfo->attfdwoptions[j]);
17953 0 : } /* end loop over columns */
17954 :
17955 0 : free(partkeydef);
17956 0 : free(ftoptions);
17957 0 : free(srvname);
17958 0 : }
17959 :
17960 : /*
17961 : * dump properties we only have ALTER TABLE syntax for
17962 : */
17963 0 : if ((tbinfo->relkind == RELKIND_RELATION ||
17964 0 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17965 0 : tbinfo->relkind == RELKIND_MATVIEW) &&
17966 0 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17967 : {
17968 0 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17969 : {
17970 : /* nothing to do, will be set when the index is dumped */
17971 0 : }
17972 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17973 : {
17974 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17975 0 : qualrelname);
17976 0 : }
17977 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17978 : {
17979 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17980 0 : qualrelname);
17981 0 : }
17982 0 : }
17983 :
17984 0 : if (tbinfo->forcerowsec)
17985 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17986 0 : qualrelname);
17987 :
17988 0 : if (dopt->binary_upgrade)
17989 0 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17990 0 : reltypename, qrelname,
17991 0 : tbinfo->dobj.namespace->dobj.name);
17992 :
17993 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17994 : {
17995 0 : char *tablespace = NULL;
17996 0 : char *tableam = NULL;
17997 :
17998 : /*
17999 : * _selectTablespace() relies on tablespace-enabled objects in the
18000 : * default tablespace to have a tablespace of "" (empty string) versus
18001 : * non-tablespace-enabled objects to have a tablespace of NULL.
18002 : * getTables() sets tbinfo->reltablespace to "" for the default
18003 : * tablespace (not NULL).
18004 : */
18005 0 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
18006 0 : tablespace = tbinfo->reltablespace;
18007 :
18008 0 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
18009 0 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
18010 0 : tableam = tbinfo->amname;
18011 :
18012 0 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18013 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18014 : .namespace = tbinfo->dobj.namespace->dobj.name,
18015 : .tablespace = tablespace,
18016 : .tableam = tableam,
18017 : .relkind = tbinfo->relkind,
18018 : .owner = tbinfo->rolname,
18019 : .description = reltypename,
18020 : .section = tbinfo->postponed_def ?
18021 : SECTION_POST_DATA : SECTION_PRE_DATA,
18022 : .createStmt = q->data,
18023 : .dropStmt = delq->data));
18024 0 : }
18025 :
18026 : /* Dump Table Comments */
18027 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18028 0 : dumpTableComment(fout, tbinfo, reltypename);
18029 :
18030 : /* Dump Table Security Labels */
18031 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18032 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18033 :
18034 : /*
18035 : * Dump comments for not-null constraints that aren't to be dumped
18036 : * separately (those are processed by collectComments/dumpComment).
18037 : */
18038 0 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18039 0 : fout->remoteVersion >= 180000)
18040 : {
18041 0 : PQExpBuffer comment = NULL;
18042 0 : PQExpBuffer tag = NULL;
18043 :
18044 0 : for (j = 0; j < tbinfo->numatts; j++)
18045 : {
18046 0 : if (tbinfo->notnull_constrs[j] != NULL &&
18047 0 : tbinfo->notnull_comment[j] != NULL)
18048 : {
18049 0 : if (comment == NULL)
18050 : {
18051 0 : comment = createPQExpBuffer();
18052 0 : tag = createPQExpBuffer();
18053 0 : }
18054 : else
18055 : {
18056 0 : resetPQExpBuffer(comment);
18057 0 : resetPQExpBuffer(tag);
18058 : }
18059 :
18060 0 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18061 0 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18062 0 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18063 0 : appendPQExpBufferStr(comment, ";\n");
18064 :
18065 0 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18066 0 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18067 :
18068 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18069 0 : ARCHIVE_OPTS(.tag = tag->data,
18070 : .namespace = tbinfo->dobj.namespace->dobj.name,
18071 : .owner = tbinfo->rolname,
18072 : .description = "COMMENT",
18073 : .section = SECTION_NONE,
18074 : .createStmt = comment->data,
18075 : .deps = &(tbinfo->dobj.dumpId),
18076 : .nDeps = 1));
18077 0 : }
18078 0 : }
18079 :
18080 0 : destroyPQExpBuffer(comment);
18081 0 : destroyPQExpBuffer(tag);
18082 0 : }
18083 :
18084 : /* Dump comments on inlined table constraints */
18085 0 : for (j = 0; j < tbinfo->ncheck; j++)
18086 : {
18087 0 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18088 :
18089 0 : if (constr->separate || !constr->conislocal)
18090 0 : continue;
18091 :
18092 0 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18093 0 : dumpTableConstraintComment(fout, constr);
18094 0 : }
18095 :
18096 0 : destroyPQExpBuffer(q);
18097 0 : destroyPQExpBuffer(delq);
18098 0 : destroyPQExpBuffer(extra);
18099 0 : free(qrelname);
18100 0 : free(qualrelname);
18101 0 : }
18102 :
18103 : /*
18104 : * dumpTableAttach
18105 : * write to fout the commands to attach a child partition
18106 : *
18107 : * Child partitions are always made by creating them separately
18108 : * and then using ATTACH PARTITION, rather than using
18109 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18110 : * any possible discrepancy in column layout, to allow assigning the
18111 : * correct tablespace if different, and so that it's possible to restore
18112 : * a partition without restoring its parent. (You'll get an error from
18113 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18114 : * using "pg_restore -L" if you prefer.) The last point motivates
18115 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18116 : * rather than emitting it within the child partition's ArchiveEntry.
18117 : */
18118 : static void
18119 0 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18120 : {
18121 0 : DumpOptions *dopt = fout->dopt;
18122 0 : PQExpBuffer q;
18123 0 : PGresult *res;
18124 0 : char *partbound;
18125 :
18126 : /* Do nothing if not dumping schema */
18127 0 : if (!dopt->dumpSchema)
18128 0 : return;
18129 :
18130 0 : q = createPQExpBuffer();
18131 :
18132 0 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18133 : {
18134 : /* Set up query for partbound details */
18135 0 : appendPQExpBufferStr(q,
18136 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18137 :
18138 0 : appendPQExpBufferStr(q,
18139 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18140 : "FROM pg_class c "
18141 : "WHERE c.oid = $1");
18142 :
18143 0 : ExecuteSqlStatement(fout, q->data);
18144 :
18145 0 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18146 0 : }
18147 :
18148 0 : printfPQExpBuffer(q,
18149 : "EXECUTE dumpTableAttach('%u')",
18150 0 : attachinfo->partitionTbl->dobj.catId.oid);
18151 :
18152 0 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18153 0 : partbound = PQgetvalue(res, 0, 0);
18154 :
18155 : /* Perform ALTER TABLE on the parent */
18156 0 : printfPQExpBuffer(q,
18157 : "ALTER TABLE ONLY %s ",
18158 0 : fmtQualifiedDumpable(attachinfo->parentTbl));
18159 0 : appendPQExpBuffer(q,
18160 : "ATTACH PARTITION %s %s;\n",
18161 0 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18162 0 : partbound);
18163 :
18164 : /*
18165 : * There is no point in creating a drop query as the drop is done by table
18166 : * drop. (If you think to change this, see also _printTocEntry().)
18167 : * Although this object doesn't really have ownership as such, set the
18168 : * owner field anyway to ensure that the command is run by the correct
18169 : * role at restore time.
18170 : */
18171 0 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18172 0 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18173 : .namespace = attachinfo->dobj.namespace->dobj.name,
18174 : .owner = attachinfo->partitionTbl->rolname,
18175 : .description = "TABLE ATTACH",
18176 : .section = SECTION_PRE_DATA,
18177 : .createStmt = q->data));
18178 :
18179 0 : PQclear(res);
18180 0 : destroyPQExpBuffer(q);
18181 0 : }
18182 :
18183 : /*
18184 : * dumpAttrDef --- dump an attribute's default-value declaration
18185 : */
18186 : static void
18187 0 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18188 : {
18189 0 : DumpOptions *dopt = fout->dopt;
18190 0 : TableInfo *tbinfo = adinfo->adtable;
18191 0 : int adnum = adinfo->adnum;
18192 0 : PQExpBuffer q;
18193 0 : PQExpBuffer delq;
18194 0 : char *qualrelname;
18195 0 : char *tag;
18196 0 : char *foreign;
18197 :
18198 : /* Do nothing if not dumping schema */
18199 0 : if (!dopt->dumpSchema)
18200 0 : return;
18201 :
18202 : /* Skip if not "separate"; it was dumped in the table's definition */
18203 0 : if (!adinfo->separate)
18204 0 : return;
18205 :
18206 0 : q = createPQExpBuffer();
18207 0 : delq = createPQExpBuffer();
18208 :
18209 0 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18210 :
18211 0 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18212 :
18213 0 : appendPQExpBuffer(q,
18214 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18215 0 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18216 0 : adinfo->adef_expr);
18217 :
18218 0 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18219 0 : foreign, qualrelname,
18220 0 : fmtId(tbinfo->attnames[adnum - 1]));
18221 :
18222 0 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18223 :
18224 0 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18225 0 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18226 0 : ARCHIVE_OPTS(.tag = tag,
18227 : .namespace = tbinfo->dobj.namespace->dobj.name,
18228 : .owner = tbinfo->rolname,
18229 : .description = "DEFAULT",
18230 : .section = SECTION_PRE_DATA,
18231 : .createStmt = q->data,
18232 : .dropStmt = delq->data));
18233 :
18234 0 : free(tag);
18235 0 : destroyPQExpBuffer(q);
18236 0 : destroyPQExpBuffer(delq);
18237 0 : free(qualrelname);
18238 0 : }
18239 :
18240 : /*
18241 : * getAttrName: extract the correct name for an attribute
18242 : *
18243 : * The array tblInfo->attnames[] only provides names of user attributes;
18244 : * if a system attribute number is supplied, we have to fake it.
18245 : * We also do a little bit of bounds checking for safety's sake.
18246 : */
18247 : static const char *
18248 0 : getAttrName(int attrnum, const TableInfo *tblInfo)
18249 : {
18250 0 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18251 0 : return tblInfo->attnames[attrnum - 1];
18252 0 : switch (attrnum)
18253 : {
18254 : case SelfItemPointerAttributeNumber:
18255 0 : return "ctid";
18256 : case MinTransactionIdAttributeNumber:
18257 0 : return "xmin";
18258 : case MinCommandIdAttributeNumber:
18259 0 : return "cmin";
18260 : case MaxTransactionIdAttributeNumber:
18261 0 : return "xmax";
18262 : case MaxCommandIdAttributeNumber:
18263 0 : return "cmax";
18264 : case TableOidAttributeNumber:
18265 0 : return "tableoid";
18266 : }
18267 0 : pg_fatal("invalid column number %d for table \"%s\"",
18268 : attrnum, tblInfo->dobj.name);
18269 0 : return NULL; /* keep compiler quiet */
18270 0 : }
18271 :
18272 : /*
18273 : * dumpIndex
18274 : * write out to fout a user-defined index
18275 : */
18276 : static void
18277 0 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18278 : {
18279 0 : DumpOptions *dopt = fout->dopt;
18280 0 : TableInfo *tbinfo = indxinfo->indextable;
18281 0 : bool is_constraint = (indxinfo->indexconstraint != 0);
18282 0 : PQExpBuffer q;
18283 0 : PQExpBuffer delq;
18284 0 : char *qindxname;
18285 0 : char *qqindxname;
18286 :
18287 : /* Do nothing if not dumping schema */
18288 0 : if (!dopt->dumpSchema)
18289 0 : return;
18290 :
18291 0 : q = createPQExpBuffer();
18292 0 : delq = createPQExpBuffer();
18293 :
18294 0 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18295 0 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18296 :
18297 : /*
18298 : * If there's an associated constraint, don't dump the index per se, but
18299 : * do dump any comment for it. (This is safe because dependency ordering
18300 : * will have ensured the constraint is emitted first.) Note that the
18301 : * emitted comment has to be shown as depending on the constraint, not the
18302 : * index, in such cases.
18303 : */
18304 0 : if (!is_constraint)
18305 : {
18306 0 : char *indstatcols = indxinfo->indstatcols;
18307 0 : char *indstatvals = indxinfo->indstatvals;
18308 0 : char **indstatcolsarray = NULL;
18309 0 : char **indstatvalsarray = NULL;
18310 0 : int nstatcols = 0;
18311 0 : int nstatvals = 0;
18312 :
18313 0 : if (dopt->binary_upgrade)
18314 0 : binary_upgrade_set_pg_class_oids(fout, q,
18315 0 : indxinfo->dobj.catId.oid);
18316 :
18317 : /* Plain secondary index */
18318 0 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18319 :
18320 : /*
18321 : * Append ALTER TABLE commands as needed to set properties that we
18322 : * only have ALTER TABLE syntax for. Keep this in sync with the
18323 : * similar code in dumpConstraint!
18324 : */
18325 :
18326 : /* If the index is clustered, we need to record that. */
18327 0 : if (indxinfo->indisclustered)
18328 : {
18329 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18330 0 : fmtQualifiedDumpable(tbinfo));
18331 : /* index name is not qualified in this syntax */
18332 0 : appendPQExpBuffer(q, " ON %s;\n",
18333 0 : qindxname);
18334 0 : }
18335 :
18336 : /*
18337 : * If the index has any statistics on some of its columns, generate
18338 : * the associated ALTER INDEX queries.
18339 : */
18340 0 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18341 : {
18342 0 : int j;
18343 :
18344 0 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18345 0 : pg_fatal("could not parse index statistic columns");
18346 0 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18347 0 : pg_fatal("could not parse index statistic values");
18348 0 : if (nstatcols != nstatvals)
18349 0 : pg_fatal("mismatched number of columns and values for index statistics");
18350 :
18351 0 : for (j = 0; j < nstatcols; j++)
18352 : {
18353 0 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18354 :
18355 : /*
18356 : * Note that this is a column number, so no quotes should be
18357 : * used.
18358 : */
18359 0 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18360 0 : indstatcolsarray[j]);
18361 0 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18362 0 : indstatvalsarray[j]);
18363 0 : }
18364 0 : }
18365 :
18366 : /* Indexes can depend on extensions */
18367 0 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18368 : "pg_catalog.pg_class",
18369 0 : "INDEX", qqindxname);
18370 :
18371 : /* If the index defines identity, we need to record that. */
18372 0 : if (indxinfo->indisreplident)
18373 : {
18374 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18375 0 : fmtQualifiedDumpable(tbinfo));
18376 : /* index name is not qualified in this syntax */
18377 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18378 0 : qindxname);
18379 0 : }
18380 :
18381 : /*
18382 : * If this index is a member of a partitioned index, the backend will
18383 : * not allow us to drop it separately, so don't try. It will go away
18384 : * automatically when we drop either the index's table or the
18385 : * partitioned index. (If, in a selective restore with --clean, we
18386 : * drop neither of those, then this index will not be dropped either.
18387 : * But that's fine, and even if you think it's not, the backend won't
18388 : * let us do differently.)
18389 : */
18390 0 : if (indxinfo->parentidx == 0)
18391 0 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18392 :
18393 0 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18394 0 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18395 0 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18396 : .namespace = tbinfo->dobj.namespace->dobj.name,
18397 : .tablespace = indxinfo->tablespace,
18398 : .owner = tbinfo->rolname,
18399 : .description = "INDEX",
18400 : .section = SECTION_POST_DATA,
18401 : .createStmt = q->data,
18402 : .dropStmt = delq->data));
18403 :
18404 0 : free(indstatcolsarray);
18405 0 : free(indstatvalsarray);
18406 0 : }
18407 :
18408 : /* Dump Index Comments */
18409 0 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18410 0 : dumpComment(fout, "INDEX", qindxname,
18411 0 : tbinfo->dobj.namespace->dobj.name,
18412 0 : tbinfo->rolname,
18413 0 : indxinfo->dobj.catId, 0,
18414 0 : is_constraint ? indxinfo->indexconstraint :
18415 0 : indxinfo->dobj.dumpId);
18416 :
18417 0 : destroyPQExpBuffer(q);
18418 0 : destroyPQExpBuffer(delq);
18419 0 : free(qindxname);
18420 0 : free(qqindxname);
18421 0 : }
18422 :
18423 : /*
18424 : * dumpIndexAttach
18425 : * write out to fout a partitioned-index attachment clause
18426 : */
18427 : static void
18428 0 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18429 : {
18430 : /* Do nothing if not dumping schema */
18431 0 : if (!fout->dopt->dumpSchema)
18432 0 : return;
18433 :
18434 0 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18435 : {
18436 0 : PQExpBuffer q = createPQExpBuffer();
18437 :
18438 0 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18439 0 : fmtQualifiedDumpable(attachinfo->parentIdx));
18440 0 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18441 0 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18442 :
18443 : /*
18444 : * There is no need for a dropStmt since the drop is done implicitly
18445 : * when we drop either the index's table or the partitioned index.
18446 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18447 : * there's no way to do it anyway. (If you think to change this,
18448 : * consider also what to do with --if-exists.)
18449 : *
18450 : * Although this object doesn't really have ownership as such, set the
18451 : * owner field anyway to ensure that the command is run by the correct
18452 : * role at restore time.
18453 : */
18454 0 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18455 0 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18456 : .namespace = attachinfo->dobj.namespace->dobj.name,
18457 : .owner = attachinfo->parentIdx->indextable->rolname,
18458 : .description = "INDEX ATTACH",
18459 : .section = SECTION_POST_DATA,
18460 : .createStmt = q->data));
18461 :
18462 0 : destroyPQExpBuffer(q);
18463 0 : }
18464 0 : }
18465 :
18466 : /*
18467 : * dumpStatisticsExt
18468 : * write out to fout an extended statistics object
18469 : */
18470 : static void
18471 0 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18472 : {
18473 0 : DumpOptions *dopt = fout->dopt;
18474 0 : PQExpBuffer q;
18475 0 : PQExpBuffer delq;
18476 0 : PQExpBuffer query;
18477 0 : char *qstatsextname;
18478 0 : PGresult *res;
18479 0 : char *stxdef;
18480 :
18481 : /* Do nothing if not dumping schema */
18482 0 : if (!dopt->dumpSchema)
18483 0 : return;
18484 :
18485 0 : q = createPQExpBuffer();
18486 0 : delq = createPQExpBuffer();
18487 0 : query = createPQExpBuffer();
18488 :
18489 0 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18490 :
18491 0 : appendPQExpBuffer(query, "SELECT "
18492 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18493 0 : statsextinfo->dobj.catId.oid);
18494 :
18495 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18496 :
18497 0 : stxdef = PQgetvalue(res, 0, 0);
18498 :
18499 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18500 0 : appendPQExpBuffer(q, "%s;\n", stxdef);
18501 :
18502 : /*
18503 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18504 : * for this statistics object is not the default value.
18505 : */
18506 0 : if (statsextinfo->stattarget >= 0)
18507 : {
18508 0 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18509 0 : fmtQualifiedDumpable(statsextinfo));
18510 0 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18511 0 : statsextinfo->stattarget);
18512 0 : }
18513 :
18514 0 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18515 0 : fmtQualifiedDumpable(statsextinfo));
18516 :
18517 0 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18518 0 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18519 0 : statsextinfo->dobj.dumpId,
18520 0 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18521 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18522 : .owner = statsextinfo->rolname,
18523 : .description = "STATISTICS",
18524 : .section = SECTION_POST_DATA,
18525 : .createStmt = q->data,
18526 : .dropStmt = delq->data));
18527 :
18528 : /* Dump Statistics Comments */
18529 0 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18530 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18531 0 : statsextinfo->dobj.namespace->dobj.name,
18532 0 : statsextinfo->rolname,
18533 0 : statsextinfo->dobj.catId, 0,
18534 0 : statsextinfo->dobj.dumpId);
18535 :
18536 0 : PQclear(res);
18537 0 : destroyPQExpBuffer(q);
18538 0 : destroyPQExpBuffer(delq);
18539 0 : destroyPQExpBuffer(query);
18540 0 : free(qstatsextname);
18541 0 : }
18542 :
18543 : /*
18544 : * dumpConstraint
18545 : * write out to fout a user-defined constraint
18546 : */
18547 : static void
18548 0 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18549 : {
18550 0 : DumpOptions *dopt = fout->dopt;
18551 0 : TableInfo *tbinfo = coninfo->contable;
18552 0 : PQExpBuffer q;
18553 0 : PQExpBuffer delq;
18554 0 : char *tag = NULL;
18555 0 : char *foreign;
18556 :
18557 : /* Do nothing if not dumping schema */
18558 0 : if (!dopt->dumpSchema)
18559 0 : return;
18560 :
18561 0 : q = createPQExpBuffer();
18562 0 : delq = createPQExpBuffer();
18563 :
18564 0 : foreign = tbinfo &&
18565 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18566 :
18567 0 : if (coninfo->contype == 'p' ||
18568 0 : coninfo->contype == 'u' ||
18569 0 : coninfo->contype == 'x')
18570 : {
18571 : /* Index-related constraint */
18572 0 : IndxInfo *indxinfo;
18573 0 : int k;
18574 :
18575 0 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18576 :
18577 0 : if (indxinfo == NULL)
18578 0 : pg_fatal("missing index for constraint \"%s\"",
18579 : coninfo->dobj.name);
18580 :
18581 0 : if (dopt->binary_upgrade)
18582 0 : binary_upgrade_set_pg_class_oids(fout, q,
18583 0 : indxinfo->dobj.catId.oid);
18584 :
18585 0 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18586 0 : fmtQualifiedDumpable(tbinfo));
18587 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18588 0 : fmtId(coninfo->dobj.name));
18589 :
18590 0 : if (coninfo->condef)
18591 : {
18592 : /* pg_get_constraintdef should have provided everything */
18593 0 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18594 0 : }
18595 : else
18596 : {
18597 0 : appendPQExpBufferStr(q,
18598 0 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18599 :
18600 : /*
18601 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18602 : * indexes. Being able to create this was fixed, but we need to
18603 : * make the index distinct in order to be able to restore the
18604 : * dump.
18605 : */
18606 0 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18607 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18608 0 : appendPQExpBufferStr(q, " (");
18609 0 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18610 : {
18611 0 : int indkey = (int) indxinfo->indkeys[k];
18612 0 : const char *attname;
18613 :
18614 0 : if (indkey == InvalidAttrNumber)
18615 0 : break;
18616 0 : attname = getAttrName(indkey, tbinfo);
18617 :
18618 0 : appendPQExpBuffer(q, "%s%s",
18619 0 : (k == 0) ? "" : ", ",
18620 0 : fmtId(attname));
18621 0 : }
18622 0 : if (coninfo->conperiod)
18623 0 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18624 :
18625 0 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18626 0 : appendPQExpBufferStr(q, ") INCLUDE (");
18627 :
18628 0 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18629 : {
18630 0 : int indkey = (int) indxinfo->indkeys[k];
18631 0 : const char *attname;
18632 :
18633 0 : if (indkey == InvalidAttrNumber)
18634 0 : break;
18635 0 : attname = getAttrName(indkey, tbinfo);
18636 :
18637 0 : appendPQExpBuffer(q, "%s%s",
18638 0 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18639 0 : fmtId(attname));
18640 0 : }
18641 :
18642 0 : appendPQExpBufferChar(q, ')');
18643 :
18644 0 : if (nonemptyReloptions(indxinfo->indreloptions))
18645 : {
18646 0 : appendPQExpBufferStr(q, " WITH (");
18647 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18648 0 : appendPQExpBufferChar(q, ')');
18649 0 : }
18650 :
18651 0 : if (coninfo->condeferrable)
18652 : {
18653 0 : appendPQExpBufferStr(q, " DEFERRABLE");
18654 0 : if (coninfo->condeferred)
18655 0 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18656 0 : }
18657 :
18658 0 : appendPQExpBufferStr(q, ";\n");
18659 : }
18660 :
18661 : /*
18662 : * Append ALTER TABLE commands as needed to set properties that we
18663 : * only have ALTER TABLE syntax for. Keep this in sync with the
18664 : * similar code in dumpIndex!
18665 : */
18666 :
18667 : /* If the index is clustered, we need to record that. */
18668 0 : if (indxinfo->indisclustered)
18669 : {
18670 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18671 0 : fmtQualifiedDumpable(tbinfo));
18672 : /* index name is not qualified in this syntax */
18673 0 : appendPQExpBuffer(q, " ON %s;\n",
18674 0 : fmtId(indxinfo->dobj.name));
18675 0 : }
18676 :
18677 : /* If the index defines identity, we need to record that. */
18678 0 : if (indxinfo->indisreplident)
18679 : {
18680 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18681 0 : fmtQualifiedDumpable(tbinfo));
18682 : /* index name is not qualified in this syntax */
18683 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18684 0 : fmtId(indxinfo->dobj.name));
18685 0 : }
18686 :
18687 : /* Indexes can depend on extensions */
18688 0 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18689 : "pg_catalog.pg_class", "INDEX",
18690 0 : fmtQualifiedDumpable(indxinfo));
18691 :
18692 0 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18693 0 : fmtQualifiedDumpable(tbinfo));
18694 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18695 0 : fmtId(coninfo->dobj.name));
18696 :
18697 0 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18698 :
18699 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18700 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18701 0 : ARCHIVE_OPTS(.tag = tag,
18702 : .namespace = tbinfo->dobj.namespace->dobj.name,
18703 : .tablespace = indxinfo->tablespace,
18704 : .owner = tbinfo->rolname,
18705 : .description = "CONSTRAINT",
18706 : .section = SECTION_POST_DATA,
18707 : .createStmt = q->data,
18708 : .dropStmt = delq->data));
18709 0 : }
18710 0 : else if (coninfo->contype == 'f')
18711 : {
18712 0 : char *only;
18713 :
18714 : /*
18715 : * Foreign keys on partitioned tables are always declared as
18716 : * inheriting to partitions; for all other cases, emit them as
18717 : * applying ONLY directly to the named table, because that's how they
18718 : * work for regular inherited tables.
18719 : */
18720 0 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18721 :
18722 : /*
18723 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18724 : * current table data is not processed
18725 : */
18726 0 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18727 0 : only, fmtQualifiedDumpable(tbinfo));
18728 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18729 0 : fmtId(coninfo->dobj.name),
18730 0 : coninfo->condef);
18731 :
18732 0 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18733 0 : only, fmtQualifiedDumpable(tbinfo));
18734 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18735 0 : fmtId(coninfo->dobj.name));
18736 :
18737 0 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18738 :
18739 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18740 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18741 0 : ARCHIVE_OPTS(.tag = tag,
18742 : .namespace = tbinfo->dobj.namespace->dobj.name,
18743 : .owner = tbinfo->rolname,
18744 : .description = "FK CONSTRAINT",
18745 : .section = SECTION_POST_DATA,
18746 : .createStmt = q->data,
18747 : .dropStmt = delq->data));
18748 0 : }
18749 0 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18750 : {
18751 : /* CHECK or invalid not-null constraint on a table */
18752 :
18753 : /* Ignore if not to be dumped separately, or if it was inherited */
18754 0 : if (coninfo->separate && coninfo->conislocal)
18755 : {
18756 0 : const char *keyword;
18757 :
18758 0 : if (coninfo->contype == 'c')
18759 0 : keyword = "CHECK CONSTRAINT";
18760 : else
18761 0 : keyword = "CONSTRAINT";
18762 :
18763 : /* not ONLY since we want it to propagate to children */
18764 0 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18765 0 : fmtQualifiedDumpable(tbinfo));
18766 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18767 0 : fmtId(coninfo->dobj.name),
18768 0 : coninfo->condef);
18769 :
18770 0 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18771 0 : fmtQualifiedDumpable(tbinfo));
18772 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18773 0 : fmtId(coninfo->dobj.name));
18774 :
18775 0 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18776 :
18777 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18778 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18779 0 : ARCHIVE_OPTS(.tag = tag,
18780 : .namespace = tbinfo->dobj.namespace->dobj.name,
18781 : .owner = tbinfo->rolname,
18782 : .description = keyword,
18783 : .section = SECTION_POST_DATA,
18784 : .createStmt = q->data,
18785 : .dropStmt = delq->data));
18786 0 : }
18787 0 : }
18788 0 : else if (tbinfo == NULL)
18789 : {
18790 : /* CHECK, NOT NULL constraint on a domain */
18791 0 : TypeInfo *tyinfo = coninfo->condomain;
18792 :
18793 0 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18794 :
18795 : /* Ignore if not to be dumped separately */
18796 0 : if (coninfo->separate)
18797 : {
18798 0 : const char *keyword;
18799 :
18800 0 : if (coninfo->contype == 'c')
18801 0 : keyword = "CHECK CONSTRAINT";
18802 : else
18803 0 : keyword = "CONSTRAINT";
18804 :
18805 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18806 0 : fmtQualifiedDumpable(tyinfo));
18807 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18808 0 : fmtId(coninfo->dobj.name),
18809 0 : coninfo->condef);
18810 :
18811 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18812 0 : fmtQualifiedDumpable(tyinfo));
18813 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18814 0 : fmtId(coninfo->dobj.name));
18815 :
18816 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18817 :
18818 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18819 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18820 0 : ARCHIVE_OPTS(.tag = tag,
18821 : .namespace = tyinfo->dobj.namespace->dobj.name,
18822 : .owner = tyinfo->rolname,
18823 : .description = keyword,
18824 : .section = SECTION_POST_DATA,
18825 : .createStmt = q->data,
18826 : .dropStmt = delq->data));
18827 :
18828 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18829 : {
18830 0 : PQExpBuffer conprefix = createPQExpBuffer();
18831 0 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18832 :
18833 0 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18834 0 : fmtId(coninfo->dobj.name));
18835 :
18836 0 : dumpComment(fout, conprefix->data, qtypname,
18837 0 : tyinfo->dobj.namespace->dobj.name,
18838 0 : tyinfo->rolname,
18839 0 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18840 0 : destroyPQExpBuffer(conprefix);
18841 0 : free(qtypname);
18842 0 : }
18843 0 : }
18844 0 : }
18845 : else
18846 : {
18847 0 : pg_fatal("unrecognized constraint type: %c",
18848 : coninfo->contype);
18849 : }
18850 :
18851 : /* Dump Constraint Comments --- only works for table constraints */
18852 0 : if (tbinfo && coninfo->separate &&
18853 0 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18854 0 : dumpTableConstraintComment(fout, coninfo);
18855 :
18856 0 : free(tag);
18857 0 : destroyPQExpBuffer(q);
18858 0 : destroyPQExpBuffer(delq);
18859 0 : }
18860 :
18861 : /*
18862 : * dumpTableConstraintComment --- dump a constraint's comment if any
18863 : *
18864 : * This is split out because we need the function in two different places
18865 : * depending on whether the constraint is dumped as part of CREATE TABLE
18866 : * or as a separate ALTER command.
18867 : */
18868 : static void
18869 0 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18870 : {
18871 0 : TableInfo *tbinfo = coninfo->contable;
18872 0 : PQExpBuffer conprefix = createPQExpBuffer();
18873 0 : char *qtabname;
18874 :
18875 0 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18876 :
18877 0 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18878 0 : fmtId(coninfo->dobj.name));
18879 :
18880 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18881 0 : dumpComment(fout, conprefix->data, qtabname,
18882 0 : tbinfo->dobj.namespace->dobj.name,
18883 0 : tbinfo->rolname,
18884 0 : coninfo->dobj.catId, 0,
18885 0 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18886 :
18887 0 : destroyPQExpBuffer(conprefix);
18888 0 : free(qtabname);
18889 0 : }
18890 :
18891 : static inline SeqType
18892 0 : parse_sequence_type(const char *name)
18893 : {
18894 0 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18895 : {
18896 0 : if (strcmp(SeqTypeNames[i], name) == 0)
18897 0 : return (SeqType) i;
18898 0 : }
18899 :
18900 0 : pg_fatal("unrecognized sequence type: %s", name);
18901 0 : return (SeqType) 0; /* keep compiler quiet */
18902 0 : }
18903 :
18904 : /*
18905 : * bsearch() comparator for SequenceItem
18906 : */
18907 : static int
18908 0 : SequenceItemCmp(const void *p1, const void *p2)
18909 : {
18910 0 : SequenceItem v1 = *((const SequenceItem *) p1);
18911 0 : SequenceItem v2 = *((const SequenceItem *) p2);
18912 :
18913 0 : return pg_cmp_u32(v1.oid, v2.oid);
18914 0 : }
18915 :
18916 : /*
18917 : * collectSequences
18918 : *
18919 : * Construct a table of sequence information. This table is sorted by OID for
18920 : * speed in lookup.
18921 : */
18922 : static void
18923 0 : collectSequences(Archive *fout)
18924 : {
18925 0 : PGresult *res;
18926 0 : const char *query;
18927 :
18928 : /*
18929 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18930 : * some extra effort, we might be able to use the sorted table for those
18931 : * versions, but for now it seems unlikely to be worth it.
18932 : *
18933 : * Since version 18, we can gather the sequence data in this query with
18934 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18935 : */
18936 0 : if (fout->remoteVersion < 100000)
18937 0 : return;
18938 0 : else if (fout->remoteVersion < 180000 ||
18939 0 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18940 0 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18941 : "seqstart, seqincrement, "
18942 : "seqmax, seqmin, "
18943 : "seqcache, seqcycle, "
18944 : "NULL, 'f' "
18945 : "FROM pg_catalog.pg_sequence "
18946 : "ORDER BY seqrelid";
18947 : else
18948 0 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18949 : "seqstart, seqincrement, "
18950 : "seqmax, seqmin, "
18951 : "seqcache, seqcycle, "
18952 : "last_value, is_called "
18953 : "FROM pg_catalog.pg_sequence, "
18954 : "pg_get_sequence_data(seqrelid) "
18955 : "ORDER BY seqrelid;";
18956 :
18957 0 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18958 :
18959 0 : nsequences = PQntuples(res);
18960 0 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18961 :
18962 0 : for (int i = 0; i < nsequences; i++)
18963 : {
18964 0 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18965 0 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18966 0 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18967 0 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18968 0 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18969 0 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18970 0 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18971 0 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18972 0 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18973 0 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18974 0 : sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
18975 0 : }
18976 :
18977 0 : PQclear(res);
18978 0 : }
18979 :
18980 : /*
18981 : * dumpSequence
18982 : * write the declaration (not data) of one user-defined sequence
18983 : */
18984 : static void
18985 0 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18986 : {
18987 0 : DumpOptions *dopt = fout->dopt;
18988 0 : SequenceItem *seq;
18989 0 : bool is_ascending;
18990 0 : int64 default_minv,
18991 : default_maxv;
18992 0 : PQExpBuffer query = createPQExpBuffer();
18993 0 : PQExpBuffer delqry = createPQExpBuffer();
18994 0 : char *qseqname;
18995 0 : TableInfo *owning_tab = NULL;
18996 :
18997 0 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18998 :
18999 : /*
19000 : * For versions >= 10, the sequence information is gathered in a sorted
19001 : * table before any calls to dumpSequence(). See collectSequences() for
19002 : * more information.
19003 : */
19004 0 : if (fout->remoteVersion >= 100000)
19005 : {
19006 0 : SequenceItem key = {0};
19007 :
19008 0 : Assert(sequences);
19009 :
19010 0 : key.oid = tbinfo->dobj.catId.oid;
19011 0 : seq = bsearch(&key, sequences, nsequences,
19012 : sizeof(SequenceItem), SequenceItemCmp);
19013 0 : }
19014 : else
19015 : {
19016 0 : PGresult *res;
19017 :
19018 : /*
19019 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19020 : *
19021 : * Note: it might seem that 'bigint' potentially needs to be
19022 : * schema-qualified, but actually that's a keyword.
19023 : */
19024 0 : appendPQExpBuffer(query,
19025 : "SELECT 'bigint' AS sequence_type, "
19026 : "start_value, increment_by, max_value, min_value, "
19027 : "cache_value, is_cycled FROM %s",
19028 0 : fmtQualifiedDumpable(tbinfo));
19029 :
19030 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19031 :
19032 0 : if (PQntuples(res) != 1)
19033 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19034 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19035 : PQntuples(res)),
19036 : tbinfo->dobj.name, PQntuples(res));
19037 :
19038 0 : seq = pg_malloc0(sizeof(SequenceItem));
19039 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19040 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19041 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19042 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19043 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19044 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19045 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19046 :
19047 0 : PQclear(res);
19048 0 : }
19049 :
19050 : /* Calculate default limits for a sequence of this type */
19051 0 : is_ascending = (seq->incby >= 0);
19052 0 : if (seq->seqtype == SEQTYPE_SMALLINT)
19053 : {
19054 0 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19055 0 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19056 0 : }
19057 0 : else if (seq->seqtype == SEQTYPE_INTEGER)
19058 : {
19059 0 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19060 0 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19061 0 : }
19062 0 : else if (seq->seqtype == SEQTYPE_BIGINT)
19063 : {
19064 0 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19065 0 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19066 0 : }
19067 : else
19068 : {
19069 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19070 0 : default_minv = default_maxv = 0; /* keep compiler quiet */
19071 : }
19072 :
19073 : /*
19074 : * Identity sequences are not to be dropped separately.
19075 : */
19076 0 : if (!tbinfo->is_identity_sequence)
19077 : {
19078 0 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19079 0 : fmtQualifiedDumpable(tbinfo));
19080 0 : }
19081 :
19082 0 : resetPQExpBuffer(query);
19083 :
19084 0 : if (dopt->binary_upgrade)
19085 : {
19086 0 : binary_upgrade_set_pg_class_oids(fout, query,
19087 0 : tbinfo->dobj.catId.oid);
19088 :
19089 : /*
19090 : * In older PG versions a sequence will have a pg_type entry, but v14
19091 : * and up don't use that, so don't attempt to preserve the type OID.
19092 : */
19093 0 : }
19094 :
19095 0 : if (tbinfo->is_identity_sequence)
19096 : {
19097 0 : owning_tab = findTableByOid(tbinfo->owning_tab);
19098 :
19099 0 : appendPQExpBuffer(query,
19100 : "ALTER TABLE %s ",
19101 0 : fmtQualifiedDumpable(owning_tab));
19102 0 : appendPQExpBuffer(query,
19103 : "ALTER COLUMN %s ADD GENERATED ",
19104 0 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19105 0 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19106 0 : appendPQExpBufferStr(query, "ALWAYS");
19107 0 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19108 0 : appendPQExpBufferStr(query, "BY DEFAULT");
19109 0 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19110 0 : fmtQualifiedDumpable(tbinfo));
19111 :
19112 : /*
19113 : * Emit persistence option only if it's different from the owning
19114 : * table's. This avoids using this new syntax unnecessarily.
19115 : */
19116 0 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19117 0 : appendPQExpBuffer(query, " %s\n",
19118 0 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19119 : "UNLOGGED" : "LOGGED");
19120 0 : }
19121 : else
19122 : {
19123 0 : appendPQExpBuffer(query,
19124 : "CREATE %sSEQUENCE %s\n",
19125 0 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19126 : "UNLOGGED " : "",
19127 0 : fmtQualifiedDumpable(tbinfo));
19128 :
19129 0 : if (seq->seqtype != SEQTYPE_BIGINT)
19130 0 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19131 : }
19132 :
19133 0 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19134 :
19135 0 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19136 :
19137 0 : if (seq->minv != default_minv)
19138 0 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19139 : else
19140 0 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19141 :
19142 0 : if (seq->maxv != default_maxv)
19143 0 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19144 : else
19145 0 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19146 :
19147 0 : appendPQExpBuffer(query,
19148 : " CACHE " INT64_FORMAT "%s",
19149 0 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19150 :
19151 0 : if (tbinfo->is_identity_sequence)
19152 0 : appendPQExpBufferStr(query, "\n);\n");
19153 : else
19154 0 : appendPQExpBufferStr(query, ";\n");
19155 :
19156 : /* binary_upgrade: no need to clear TOAST table oid */
19157 :
19158 0 : if (dopt->binary_upgrade)
19159 0 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19160 0 : "SEQUENCE", qseqname,
19161 0 : tbinfo->dobj.namespace->dobj.name);
19162 :
19163 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19164 0 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19165 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19166 : .namespace = tbinfo->dobj.namespace->dobj.name,
19167 : .owner = tbinfo->rolname,
19168 : .description = "SEQUENCE",
19169 : .section = SECTION_PRE_DATA,
19170 : .createStmt = query->data,
19171 : .dropStmt = delqry->data));
19172 :
19173 : /*
19174 : * If the sequence is owned by a table column, emit the ALTER for it as a
19175 : * separate TOC entry immediately following the sequence's own entry. It's
19176 : * OK to do this rather than using full sorting logic, because the
19177 : * dependency that tells us it's owned will have forced the table to be
19178 : * created first. We can't just include the ALTER in the TOC entry
19179 : * because it will fail if we haven't reassigned the sequence owner to
19180 : * match the table's owner.
19181 : *
19182 : * We need not schema-qualify the table reference because both sequence
19183 : * and table must be in the same schema.
19184 : */
19185 0 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19186 : {
19187 0 : owning_tab = findTableByOid(tbinfo->owning_tab);
19188 :
19189 0 : if (owning_tab == NULL)
19190 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19191 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19192 :
19193 0 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19194 : {
19195 0 : resetPQExpBuffer(query);
19196 0 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19197 0 : fmtQualifiedDumpable(tbinfo));
19198 0 : appendPQExpBuffer(query, " OWNED BY %s",
19199 0 : fmtQualifiedDumpable(owning_tab));
19200 0 : appendPQExpBuffer(query, ".%s;\n",
19201 0 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19202 :
19203 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19204 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19205 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19206 : .namespace = tbinfo->dobj.namespace->dobj.name,
19207 : .owner = tbinfo->rolname,
19208 : .description = "SEQUENCE OWNED BY",
19209 : .section = SECTION_PRE_DATA,
19210 : .createStmt = query->data,
19211 : .deps = &(tbinfo->dobj.dumpId),
19212 : .nDeps = 1));
19213 0 : }
19214 0 : }
19215 :
19216 : /* Dump Sequence Comments and Security Labels */
19217 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19218 0 : dumpComment(fout, "SEQUENCE", qseqname,
19219 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19220 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19221 :
19222 0 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19223 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19224 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19225 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19226 :
19227 0 : if (fout->remoteVersion < 100000)
19228 0 : pg_free(seq);
19229 0 : destroyPQExpBuffer(query);
19230 0 : destroyPQExpBuffer(delqry);
19231 0 : free(qseqname);
19232 0 : }
19233 :
19234 : /*
19235 : * dumpSequenceData
19236 : * write the data of one user-defined sequence
19237 : */
19238 : static void
19239 0 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19240 : {
19241 0 : TableInfo *tbinfo = tdinfo->tdtable;
19242 0 : int64 last;
19243 0 : bool called;
19244 0 : PQExpBuffer query;
19245 :
19246 : /* needn't bother if not dumping sequence data */
19247 0 : if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19248 0 : return;
19249 :
19250 0 : query = createPQExpBuffer();
19251 :
19252 : /*
19253 : * For versions >= 18, the sequence information is gathered in the sorted
19254 : * array before any calls to dumpSequenceData(). See collectSequences()
19255 : * for more information.
19256 : *
19257 : * For older versions, we have to query the sequence relations
19258 : * individually.
19259 : */
19260 0 : if (fout->remoteVersion < 180000)
19261 : {
19262 0 : PGresult *res;
19263 :
19264 0 : appendPQExpBuffer(query,
19265 : "SELECT last_value, is_called FROM %s",
19266 0 : fmtQualifiedDumpable(tbinfo));
19267 :
19268 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19269 :
19270 0 : if (PQntuples(res) != 1)
19271 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19272 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19273 : PQntuples(res)),
19274 : tbinfo->dobj.name, PQntuples(res));
19275 :
19276 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19277 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19278 :
19279 0 : PQclear(res);
19280 0 : }
19281 : else
19282 : {
19283 0 : SequenceItem key = {0};
19284 0 : SequenceItem *entry;
19285 :
19286 0 : Assert(sequences);
19287 0 : Assert(tbinfo->dobj.catId.oid);
19288 :
19289 0 : key.oid = tbinfo->dobj.catId.oid;
19290 0 : entry = bsearch(&key, sequences, nsequences,
19291 : sizeof(SequenceItem), SequenceItemCmp);
19292 :
19293 0 : if (entry->null_seqtuple)
19294 0 : pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19295 : "SELECT privilege on the sequence or the sequence may "
19296 : "have been concurrently dropped",
19297 : tbinfo->dobj.name);
19298 :
19299 0 : last = entry->last_value;
19300 0 : called = entry->is_called;
19301 0 : }
19302 :
19303 0 : resetPQExpBuffer(query);
19304 0 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19305 0 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19306 0 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19307 0 : last, (called ? "true" : "false"));
19308 :
19309 0 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19310 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19311 0 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19312 : .namespace = tbinfo->dobj.namespace->dobj.name,
19313 : .owner = tbinfo->rolname,
19314 : .description = "SEQUENCE SET",
19315 : .section = SECTION_DATA,
19316 : .createStmt = query->data,
19317 : .deps = &(tbinfo->dobj.dumpId),
19318 : .nDeps = 1));
19319 :
19320 0 : destroyPQExpBuffer(query);
19321 0 : }
19322 :
19323 : /*
19324 : * dumpTrigger
19325 : * write the declaration of one user-defined table trigger
19326 : */
19327 : static void
19328 0 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19329 : {
19330 0 : DumpOptions *dopt = fout->dopt;
19331 0 : TableInfo *tbinfo = tginfo->tgtable;
19332 0 : PQExpBuffer query;
19333 0 : PQExpBuffer delqry;
19334 0 : PQExpBuffer trigprefix;
19335 0 : PQExpBuffer trigidentity;
19336 0 : char *qtabname;
19337 0 : char *tag;
19338 :
19339 : /* Do nothing if not dumping schema */
19340 0 : if (!dopt->dumpSchema)
19341 0 : return;
19342 :
19343 0 : query = createPQExpBuffer();
19344 0 : delqry = createPQExpBuffer();
19345 0 : trigprefix = createPQExpBuffer();
19346 0 : trigidentity = createPQExpBuffer();
19347 :
19348 0 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19349 :
19350 0 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19351 0 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19352 :
19353 0 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19354 0 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19355 :
19356 : /* Triggers can depend on extensions */
19357 0 : append_depends_on_extension(fout, query, &tginfo->dobj,
19358 : "pg_catalog.pg_trigger", "TRIGGER",
19359 0 : trigidentity->data);
19360 :
19361 0 : if (tginfo->tgispartition)
19362 : {
19363 0 : Assert(tbinfo->ispartition);
19364 :
19365 : /*
19366 : * Partition triggers only appear here because their 'tgenabled' flag
19367 : * differs from its parent's. The trigger is created already, so
19368 : * remove the CREATE and replace it with an ALTER. (Clear out the
19369 : * DROP query too, so that pg_dump --create does not cause errors.)
19370 : */
19371 0 : resetPQExpBuffer(query);
19372 0 : resetPQExpBuffer(delqry);
19373 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19374 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19375 0 : fmtQualifiedDumpable(tbinfo));
19376 0 : switch (tginfo->tgenabled)
19377 : {
19378 : case 'f':
19379 : case 'D':
19380 0 : appendPQExpBufferStr(query, "DISABLE");
19381 0 : break;
19382 : case 't':
19383 : case 'O':
19384 0 : appendPQExpBufferStr(query, "ENABLE");
19385 0 : break;
19386 : case 'R':
19387 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19388 0 : break;
19389 : case 'A':
19390 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19391 0 : break;
19392 : }
19393 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19394 0 : fmtId(tginfo->dobj.name));
19395 0 : }
19396 0 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19397 : {
19398 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19399 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19400 0 : fmtQualifiedDumpable(tbinfo));
19401 0 : switch (tginfo->tgenabled)
19402 : {
19403 : case 'D':
19404 : case 'f':
19405 0 : appendPQExpBufferStr(query, "DISABLE");
19406 0 : break;
19407 : case 'A':
19408 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19409 0 : break;
19410 : case 'R':
19411 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19412 0 : break;
19413 : default:
19414 0 : appendPQExpBufferStr(query, "ENABLE");
19415 0 : break;
19416 : }
19417 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19418 0 : fmtId(tginfo->dobj.name));
19419 0 : }
19420 :
19421 0 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19422 0 : fmtId(tginfo->dobj.name));
19423 :
19424 0 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19425 :
19426 0 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19427 0 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19428 0 : ARCHIVE_OPTS(.tag = tag,
19429 : .namespace = tbinfo->dobj.namespace->dobj.name,
19430 : .owner = tbinfo->rolname,
19431 : .description = "TRIGGER",
19432 : .section = SECTION_POST_DATA,
19433 : .createStmt = query->data,
19434 : .dropStmt = delqry->data));
19435 :
19436 0 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19437 0 : dumpComment(fout, trigprefix->data, qtabname,
19438 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19439 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19440 :
19441 0 : free(tag);
19442 0 : destroyPQExpBuffer(query);
19443 0 : destroyPQExpBuffer(delqry);
19444 0 : destroyPQExpBuffer(trigprefix);
19445 0 : destroyPQExpBuffer(trigidentity);
19446 0 : free(qtabname);
19447 0 : }
19448 :
19449 : /*
19450 : * dumpEventTrigger
19451 : * write the declaration of one user-defined event trigger
19452 : */
19453 : static void
19454 0 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19455 : {
19456 0 : DumpOptions *dopt = fout->dopt;
19457 0 : PQExpBuffer query;
19458 0 : PQExpBuffer delqry;
19459 0 : char *qevtname;
19460 :
19461 : /* Do nothing if not dumping schema */
19462 0 : if (!dopt->dumpSchema)
19463 0 : return;
19464 :
19465 0 : query = createPQExpBuffer();
19466 0 : delqry = createPQExpBuffer();
19467 :
19468 0 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19469 :
19470 0 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19471 0 : appendPQExpBufferStr(query, qevtname);
19472 0 : appendPQExpBufferStr(query, " ON ");
19473 0 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19474 :
19475 0 : if (strcmp("", evtinfo->evttags) != 0)
19476 : {
19477 0 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19478 0 : appendPQExpBufferStr(query, evtinfo->evttags);
19479 0 : appendPQExpBufferChar(query, ')');
19480 0 : }
19481 :
19482 0 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19483 0 : appendPQExpBufferStr(query, evtinfo->evtfname);
19484 0 : appendPQExpBufferStr(query, "();\n");
19485 :
19486 0 : if (evtinfo->evtenabled != 'O')
19487 : {
19488 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19489 0 : qevtname);
19490 0 : switch (evtinfo->evtenabled)
19491 : {
19492 : case 'D':
19493 0 : appendPQExpBufferStr(query, "DISABLE");
19494 0 : break;
19495 : case 'A':
19496 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19497 0 : break;
19498 : case 'R':
19499 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19500 0 : break;
19501 : default:
19502 0 : appendPQExpBufferStr(query, "ENABLE");
19503 0 : break;
19504 : }
19505 0 : appendPQExpBufferStr(query, ";\n");
19506 0 : }
19507 :
19508 0 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19509 0 : qevtname);
19510 :
19511 0 : if (dopt->binary_upgrade)
19512 0 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19513 0 : "EVENT TRIGGER", qevtname, NULL);
19514 :
19515 0 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19516 0 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19517 0 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19518 : .owner = evtinfo->evtowner,
19519 : .description = "EVENT TRIGGER",
19520 : .section = SECTION_POST_DATA,
19521 : .createStmt = query->data,
19522 : .dropStmt = delqry->data));
19523 :
19524 0 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19525 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19526 0 : NULL, evtinfo->evtowner,
19527 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19528 :
19529 0 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19530 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19531 0 : NULL, evtinfo->evtowner,
19532 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19533 :
19534 0 : destroyPQExpBuffer(query);
19535 0 : destroyPQExpBuffer(delqry);
19536 0 : free(qevtname);
19537 0 : }
19538 :
19539 : /*
19540 : * dumpRule
19541 : * Dump a rule
19542 : */
19543 : static void
19544 0 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19545 : {
19546 0 : DumpOptions *dopt = fout->dopt;
19547 0 : TableInfo *tbinfo = rinfo->ruletable;
19548 0 : bool is_view;
19549 0 : PQExpBuffer query;
19550 0 : PQExpBuffer cmd;
19551 0 : PQExpBuffer delcmd;
19552 0 : PQExpBuffer ruleprefix;
19553 0 : char *qtabname;
19554 0 : PGresult *res;
19555 0 : char *tag;
19556 :
19557 : /* Do nothing if not dumping schema */
19558 0 : if (!dopt->dumpSchema)
19559 0 : return;
19560 :
19561 : /*
19562 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19563 : * we do not want to dump it as a separate object.
19564 : */
19565 0 : if (!rinfo->separate)
19566 0 : return;
19567 :
19568 : /*
19569 : * If it's an ON SELECT rule, we want to print it as a view definition,
19570 : * instead of a rule.
19571 : */
19572 0 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19573 :
19574 0 : query = createPQExpBuffer();
19575 0 : cmd = createPQExpBuffer();
19576 0 : delcmd = createPQExpBuffer();
19577 0 : ruleprefix = createPQExpBuffer();
19578 :
19579 0 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19580 :
19581 0 : if (is_view)
19582 : {
19583 0 : PQExpBuffer result;
19584 :
19585 : /*
19586 : * We need OR REPLACE here because we'll be replacing a dummy view.
19587 : * Otherwise this should look largely like the regular view dump code.
19588 : */
19589 0 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19590 0 : fmtQualifiedDumpable(tbinfo));
19591 0 : if (nonemptyReloptions(tbinfo->reloptions))
19592 : {
19593 0 : appendPQExpBufferStr(cmd, " WITH (");
19594 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19595 0 : appendPQExpBufferChar(cmd, ')');
19596 0 : }
19597 0 : result = createViewAsClause(fout, tbinfo);
19598 0 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19599 0 : destroyPQExpBuffer(result);
19600 0 : if (tbinfo->checkoption != NULL)
19601 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19602 0 : tbinfo->checkoption);
19603 0 : appendPQExpBufferStr(cmd, ";\n");
19604 0 : }
19605 : else
19606 : {
19607 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19608 0 : appendPQExpBuffer(query,
19609 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19610 0 : rinfo->dobj.catId.oid);
19611 :
19612 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19613 :
19614 0 : if (PQntuples(res) != 1)
19615 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19616 : rinfo->dobj.name, tbinfo->dobj.name);
19617 :
19618 0 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19619 :
19620 0 : PQclear(res);
19621 : }
19622 :
19623 : /*
19624 : * Add the command to alter the rules replication firing semantics if it
19625 : * differs from the default.
19626 : */
19627 0 : if (rinfo->ev_enabled != 'O')
19628 : {
19629 0 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19630 0 : switch (rinfo->ev_enabled)
19631 : {
19632 : case 'A':
19633 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19634 0 : fmtId(rinfo->dobj.name));
19635 0 : break;
19636 : case 'R':
19637 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19638 0 : fmtId(rinfo->dobj.name));
19639 0 : break;
19640 : case 'D':
19641 0 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19642 0 : fmtId(rinfo->dobj.name));
19643 0 : break;
19644 : }
19645 0 : }
19646 :
19647 0 : if (is_view)
19648 : {
19649 : /*
19650 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19651 : * REPLACE VIEW to replace the rule with something with minimal
19652 : * dependencies.
19653 : */
19654 0 : PQExpBuffer result;
19655 :
19656 0 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19657 0 : fmtQualifiedDumpable(tbinfo));
19658 0 : result = createDummyViewAsClause(fout, tbinfo);
19659 0 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19660 0 : destroyPQExpBuffer(result);
19661 0 : }
19662 : else
19663 : {
19664 0 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19665 0 : fmtId(rinfo->dobj.name));
19666 0 : appendPQExpBuffer(delcmd, "ON %s;\n",
19667 0 : fmtQualifiedDumpable(tbinfo));
19668 : }
19669 :
19670 0 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19671 0 : fmtId(rinfo->dobj.name));
19672 :
19673 0 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19674 :
19675 0 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19676 0 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19677 0 : ARCHIVE_OPTS(.tag = tag,
19678 : .namespace = tbinfo->dobj.namespace->dobj.name,
19679 : .owner = tbinfo->rolname,
19680 : .description = "RULE",
19681 : .section = SECTION_POST_DATA,
19682 : .createStmt = cmd->data,
19683 : .dropStmt = delcmd->data));
19684 :
19685 : /* Dump rule comments */
19686 0 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19687 0 : dumpComment(fout, ruleprefix->data, qtabname,
19688 0 : tbinfo->dobj.namespace->dobj.name,
19689 0 : tbinfo->rolname,
19690 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19691 :
19692 0 : free(tag);
19693 0 : destroyPQExpBuffer(query);
19694 0 : destroyPQExpBuffer(cmd);
19695 0 : destroyPQExpBuffer(delcmd);
19696 0 : destroyPQExpBuffer(ruleprefix);
19697 0 : free(qtabname);
19698 0 : }
19699 :
19700 : /*
19701 : * getExtensionMembership --- obtain extension membership data
19702 : *
19703 : * We need to identify objects that are extension members as soon as they're
19704 : * loaded, so that we can correctly determine whether they need to be dumped.
19705 : * Generally speaking, extension member objects will get marked as *not* to
19706 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19707 : * command. However, in binary upgrade mode we still need to dump the members
19708 : * individually.
19709 : */
19710 : void
19711 0 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19712 : int numExtensions)
19713 : {
19714 0 : PQExpBuffer query;
19715 0 : PGresult *res;
19716 0 : int ntups,
19717 : i;
19718 0 : int i_classid,
19719 : i_objid,
19720 : i_refobjid;
19721 0 : ExtensionInfo *ext;
19722 :
19723 : /* Nothing to do if no extensions */
19724 0 : if (numExtensions == 0)
19725 0 : return;
19726 :
19727 0 : query = createPQExpBuffer();
19728 :
19729 : /* refclassid constraint is redundant but may speed the search */
19730 0 : appendPQExpBufferStr(query, "SELECT "
19731 : "classid, objid, refobjid "
19732 : "FROM pg_depend "
19733 : "WHERE refclassid = 'pg_extension'::regclass "
19734 : "AND deptype = 'e' "
19735 : "ORDER BY 3");
19736 :
19737 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19738 :
19739 0 : ntups = PQntuples(res);
19740 :
19741 0 : i_classid = PQfnumber(res, "classid");
19742 0 : i_objid = PQfnumber(res, "objid");
19743 0 : i_refobjid = PQfnumber(res, "refobjid");
19744 :
19745 : /*
19746 : * Since we ordered the SELECT by referenced ID, we can expect that
19747 : * multiple entries for the same extension will appear together; this
19748 : * saves on searches.
19749 : */
19750 0 : ext = NULL;
19751 :
19752 0 : for (i = 0; i < ntups; i++)
19753 : {
19754 0 : CatalogId objId;
19755 0 : Oid extId;
19756 :
19757 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19758 0 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19759 0 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19760 :
19761 0 : if (ext == NULL ||
19762 0 : ext->dobj.catId.oid != extId)
19763 0 : ext = findExtensionByOid(extId);
19764 :
19765 0 : if (ext == NULL)
19766 : {
19767 : /* shouldn't happen */
19768 0 : pg_log_warning("could not find referenced extension %u", extId);
19769 0 : continue;
19770 : }
19771 :
19772 0 : recordExtensionMembership(objId, ext);
19773 0 : }
19774 :
19775 0 : PQclear(res);
19776 :
19777 0 : destroyPQExpBuffer(query);
19778 0 : }
19779 :
19780 : /*
19781 : * processExtensionTables --- deal with extension configuration tables
19782 : *
19783 : * There are two parts to this process:
19784 : *
19785 : * 1. Identify and create dump records for extension configuration tables.
19786 : *
19787 : * Extensions can mark tables as "configuration", which means that the user
19788 : * is able and expected to modify those tables after the extension has been
19789 : * loaded. For these tables, we dump out only the data- the structure is
19790 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19791 : * foreign keys, which brings us to-
19792 : *
19793 : * 2. Record FK dependencies between configuration tables.
19794 : *
19795 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19796 : * the data is loaded, we have to work out what the best order for reloading
19797 : * the data is, to avoid FK violations when the tables are restored. This is
19798 : * not perfect- we can't handle circular dependencies and if any exist they
19799 : * will cause an invalid dump to be produced (though at least all of the data
19800 : * is included for a user to manually restore). This is currently documented
19801 : * but perhaps we can provide a better solution in the future.
19802 : */
19803 : void
19804 0 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19805 : int numExtensions)
19806 : {
19807 0 : DumpOptions *dopt = fout->dopt;
19808 0 : PQExpBuffer query;
19809 0 : PGresult *res;
19810 0 : int ntups,
19811 : i;
19812 0 : int i_conrelid,
19813 : i_confrelid;
19814 :
19815 : /* Nothing to do if no extensions */
19816 0 : if (numExtensions == 0)
19817 0 : return;
19818 :
19819 : /*
19820 : * Identify extension configuration tables and create TableDataInfo
19821 : * objects for them, ensuring their data will be dumped even though the
19822 : * tables themselves won't be.
19823 : *
19824 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19825 : * user data in a configuration table is treated like schema data. This
19826 : * seems appropriate since system data in a config table would get
19827 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19828 : * list of extensions to be included, none of its data is dumped.
19829 : */
19830 0 : for (i = 0; i < numExtensions; i++)
19831 : {
19832 0 : ExtensionInfo *curext = &(extinfo[i]);
19833 0 : char *extconfig = curext->extconfig;
19834 0 : char *extcondition = curext->extcondition;
19835 0 : char **extconfigarray = NULL;
19836 0 : char **extconditionarray = NULL;
19837 0 : int nconfigitems = 0;
19838 0 : int nconditionitems = 0;
19839 :
19840 : /*
19841 : * Check if this extension is listed as to include in the dump. If
19842 : * not, any table data associated with it is discarded.
19843 : */
19844 0 : if (extension_include_oids.head != NULL &&
19845 0 : !simple_oid_list_member(&extension_include_oids,
19846 0 : curext->dobj.catId.oid))
19847 0 : continue;
19848 :
19849 : /*
19850 : * Check if this extension is listed as to exclude in the dump. If
19851 : * yes, any table data associated with it is discarded.
19852 : */
19853 0 : if (extension_exclude_oids.head != NULL &&
19854 0 : simple_oid_list_member(&extension_exclude_oids,
19855 0 : curext->dobj.catId.oid))
19856 0 : continue;
19857 :
19858 0 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19859 : {
19860 0 : int j;
19861 :
19862 0 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19863 0 : pg_fatal("could not parse %s array", "extconfig");
19864 0 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19865 0 : pg_fatal("could not parse %s array", "extcondition");
19866 0 : if (nconfigitems != nconditionitems)
19867 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19868 :
19869 0 : for (j = 0; j < nconfigitems; j++)
19870 : {
19871 0 : TableInfo *configtbl;
19872 0 : Oid configtbloid = atooid(extconfigarray[j]);
19873 0 : bool dumpobj =
19874 0 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19875 :
19876 0 : configtbl = findTableByOid(configtbloid);
19877 0 : if (configtbl == NULL)
19878 0 : continue;
19879 :
19880 : /*
19881 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19882 : * unless the table or its schema is explicitly included
19883 : */
19884 0 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19885 : {
19886 : /* check table explicitly requested */
19887 0 : if (table_include_oids.head != NULL &&
19888 0 : simple_oid_list_member(&table_include_oids,
19889 0 : configtbloid))
19890 0 : dumpobj = true;
19891 :
19892 : /* check table's schema explicitly requested */
19893 0 : if (configtbl->dobj.namespace->dobj.dump &
19894 : DUMP_COMPONENT_DATA)
19895 0 : dumpobj = true;
19896 0 : }
19897 :
19898 : /* check table excluded by an exclusion switch */
19899 0 : if (table_exclude_oids.head != NULL &&
19900 0 : simple_oid_list_member(&table_exclude_oids,
19901 0 : configtbloid))
19902 0 : dumpobj = false;
19903 :
19904 : /* check schema excluded by an exclusion switch */
19905 0 : if (simple_oid_list_member(&schema_exclude_oids,
19906 0 : configtbl->dobj.namespace->dobj.catId.oid))
19907 0 : dumpobj = false;
19908 :
19909 0 : if (dumpobj)
19910 : {
19911 0 : makeTableDataInfo(dopt, configtbl);
19912 0 : if (configtbl->dataObj != NULL)
19913 : {
19914 0 : if (strlen(extconditionarray[j]) > 0)
19915 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19916 0 : }
19917 0 : }
19918 0 : }
19919 0 : }
19920 0 : if (extconfigarray)
19921 0 : free(extconfigarray);
19922 0 : if (extconditionarray)
19923 0 : free(extconditionarray);
19924 0 : }
19925 :
19926 : /*
19927 : * Now that all the TableDataInfo objects have been created for all the
19928 : * extensions, check their FK dependencies and register them to try and
19929 : * dump the data out in an order that they can be restored in.
19930 : *
19931 : * Note that this is not a problem for user tables as their FKs are
19932 : * recreated after the data has been loaded.
19933 : */
19934 :
19935 0 : query = createPQExpBuffer();
19936 :
19937 0 : printfPQExpBuffer(query,
19938 : "SELECT conrelid, confrelid "
19939 : "FROM pg_constraint "
19940 : "JOIN pg_depend ON (objid = confrelid) "
19941 : "WHERE contype = 'f' "
19942 : "AND refclassid = 'pg_extension'::regclass "
19943 : "AND classid = 'pg_class'::regclass;");
19944 :
19945 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19946 0 : ntups = PQntuples(res);
19947 :
19948 0 : i_conrelid = PQfnumber(res, "conrelid");
19949 0 : i_confrelid = PQfnumber(res, "confrelid");
19950 :
19951 : /* Now get the dependencies and register them */
19952 0 : for (i = 0; i < ntups; i++)
19953 : {
19954 0 : Oid conrelid,
19955 : confrelid;
19956 0 : TableInfo *reftable,
19957 : *contable;
19958 :
19959 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19960 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19961 0 : contable = findTableByOid(conrelid);
19962 0 : reftable = findTableByOid(confrelid);
19963 :
19964 0 : if (reftable == NULL ||
19965 0 : reftable->dataObj == NULL ||
19966 0 : contable == NULL ||
19967 0 : contable->dataObj == NULL)
19968 0 : continue;
19969 :
19970 : /*
19971 : * Make referencing TABLE_DATA object depend on the referenced table's
19972 : * TABLE_DATA object.
19973 : */
19974 0 : addObjectDependency(&contable->dataObj->dobj,
19975 0 : reftable->dataObj->dobj.dumpId);
19976 0 : }
19977 0 : PQclear(res);
19978 0 : destroyPQExpBuffer(query);
19979 0 : }
19980 :
19981 : /*
19982 : * getDependencies --- obtain available dependency data
19983 : */
19984 : static void
19985 0 : getDependencies(Archive *fout)
19986 : {
19987 0 : PQExpBuffer query;
19988 0 : PGresult *res;
19989 0 : int ntups,
19990 : i;
19991 0 : int i_classid,
19992 : i_objid,
19993 : i_refclassid,
19994 : i_refobjid,
19995 : i_deptype;
19996 0 : DumpableObject *dobj,
19997 : *refdobj;
19998 :
19999 0 : pg_log_info("reading dependency data");
20000 :
20001 0 : query = createPQExpBuffer();
20002 :
20003 : /*
20004 : * Messy query to collect the dependency data we need. Note that we
20005 : * ignore the sub-object column, so that dependencies of or on a column
20006 : * look the same as dependencies of or on a whole table.
20007 : *
20008 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
20009 : * already processed by getExtensionMembership.
20010 : */
20011 0 : appendPQExpBufferStr(query, "SELECT "
20012 : "classid, objid, refclassid, refobjid, deptype "
20013 : "FROM pg_depend "
20014 : "WHERE deptype != 'p' AND deptype != 'e'\n");
20015 :
20016 : /*
20017 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
20018 : * have to translate their dependencies into dependencies of their parent
20019 : * opfamily. Ignore internal dependencies though, as those will point to
20020 : * their parent opclass, which we needn't consider here (and if we did,
20021 : * it'd just result in circular dependencies). Also, "loose" opfamily
20022 : * entries will have dependencies on their parent opfamily, which we
20023 : * should drop since they'd likewise become useless self-dependencies.
20024 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20025 : */
20026 0 : appendPQExpBufferStr(query, "UNION ALL\n"
20027 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20028 : "FROM pg_depend d, pg_amop o "
20029 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20030 : "classid = 'pg_amop'::regclass AND objid = o.oid "
20031 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20032 :
20033 : /* Likewise for pg_amproc entries */
20034 0 : appendPQExpBufferStr(query, "UNION ALL\n"
20035 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20036 : "FROM pg_depend d, pg_amproc p "
20037 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20038 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20039 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20040 :
20041 : /* Sort the output for efficiency below */
20042 0 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20043 :
20044 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20045 :
20046 0 : ntups = PQntuples(res);
20047 :
20048 0 : i_classid = PQfnumber(res, "classid");
20049 0 : i_objid = PQfnumber(res, "objid");
20050 0 : i_refclassid = PQfnumber(res, "refclassid");
20051 0 : i_refobjid = PQfnumber(res, "refobjid");
20052 0 : i_deptype = PQfnumber(res, "deptype");
20053 :
20054 : /*
20055 : * Since we ordered the SELECT by referencing ID, we can expect that
20056 : * multiple entries for the same object will appear together; this saves
20057 : * on searches.
20058 : */
20059 0 : dobj = NULL;
20060 :
20061 0 : for (i = 0; i < ntups; i++)
20062 : {
20063 0 : CatalogId objId;
20064 0 : CatalogId refobjId;
20065 0 : char deptype;
20066 :
20067 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20068 0 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20069 0 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20070 0 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20071 0 : deptype = *(PQgetvalue(res, i, i_deptype));
20072 :
20073 0 : if (dobj == NULL ||
20074 0 : dobj->catId.tableoid != objId.tableoid ||
20075 0 : dobj->catId.oid != objId.oid)
20076 0 : dobj = findObjectByCatalogId(objId);
20077 :
20078 : /*
20079 : * Failure to find objects mentioned in pg_depend is not unexpected,
20080 : * since for example we don't collect info about TOAST tables.
20081 : */
20082 0 : if (dobj == NULL)
20083 : {
20084 : #ifdef NOT_USED
20085 : pg_log_warning("no referencing object %u %u",
20086 : objId.tableoid, objId.oid);
20087 : #endif
20088 0 : continue;
20089 : }
20090 :
20091 0 : refdobj = findObjectByCatalogId(refobjId);
20092 :
20093 0 : if (refdobj == NULL)
20094 : {
20095 : #ifdef NOT_USED
20096 : pg_log_warning("no referenced object %u %u",
20097 : refobjId.tableoid, refobjId.oid);
20098 : #endif
20099 0 : continue;
20100 : }
20101 :
20102 : /*
20103 : * For 'x' dependencies, mark the object for later; we still add the
20104 : * normal dependency, for possible ordering purposes. Currently
20105 : * pg_dump_sort.c knows to put extensions ahead of all object types
20106 : * that could possibly depend on them, but this is safer.
20107 : */
20108 0 : if (deptype == 'x')
20109 0 : dobj->depends_on_ext = true;
20110 :
20111 : /*
20112 : * Ordinarily, table rowtypes have implicit dependencies on their
20113 : * tables. However, for a composite type the implicit dependency goes
20114 : * the other way in pg_depend; which is the right thing for DROP but
20115 : * it doesn't produce the dependency ordering we need. So in that one
20116 : * case, we reverse the direction of the dependency.
20117 : */
20118 0 : if (deptype == 'i' &&
20119 0 : dobj->objType == DO_TABLE &&
20120 0 : refdobj->objType == DO_TYPE)
20121 0 : addObjectDependency(refdobj, dobj->dumpId);
20122 : else
20123 : /* normal case */
20124 0 : addObjectDependency(dobj, refdobj->dumpId);
20125 0 : }
20126 :
20127 0 : PQclear(res);
20128 :
20129 0 : destroyPQExpBuffer(query);
20130 0 : }
20131 :
20132 :
20133 : /*
20134 : * createBoundaryObjects - create dummy DumpableObjects to represent
20135 : * dump section boundaries.
20136 : */
20137 : static DumpableObject *
20138 0 : createBoundaryObjects(void)
20139 : {
20140 0 : DumpableObject *dobjs;
20141 :
20142 0 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20143 :
20144 0 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20145 0 : dobjs[0].catId = nilCatalogId;
20146 0 : AssignDumpId(dobjs + 0);
20147 0 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20148 :
20149 0 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20150 0 : dobjs[1].catId = nilCatalogId;
20151 0 : AssignDumpId(dobjs + 1);
20152 0 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20153 :
20154 0 : return dobjs;
20155 0 : }
20156 :
20157 : /*
20158 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20159 : * section boundaries.
20160 : */
20161 : static void
20162 0 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20163 : DumpableObject *boundaryObjs)
20164 : {
20165 0 : DumpableObject *preDataBound = boundaryObjs + 0;
20166 0 : DumpableObject *postDataBound = boundaryObjs + 1;
20167 0 : int i;
20168 :
20169 0 : for (i = 0; i < numObjs; i++)
20170 : {
20171 0 : DumpableObject *dobj = dobjs[i];
20172 :
20173 : /*
20174 : * The classification of object types here must match the SECTION_xxx
20175 : * values assigned during subsequent ArchiveEntry calls!
20176 : */
20177 0 : switch (dobj->objType)
20178 : {
20179 : case DO_NAMESPACE:
20180 : case DO_EXTENSION:
20181 : case DO_TYPE:
20182 : case DO_SHELL_TYPE:
20183 : case DO_FUNC:
20184 : case DO_AGG:
20185 : case DO_OPERATOR:
20186 : case DO_ACCESS_METHOD:
20187 : case DO_OPCLASS:
20188 : case DO_OPFAMILY:
20189 : case DO_COLLATION:
20190 : case DO_CONVERSION:
20191 : case DO_TABLE:
20192 : case DO_TABLE_ATTACH:
20193 : case DO_ATTRDEF:
20194 : case DO_PROCLANG:
20195 : case DO_CAST:
20196 : case DO_DUMMY_TYPE:
20197 : case DO_TSPARSER:
20198 : case DO_TSDICT:
20199 : case DO_TSTEMPLATE:
20200 : case DO_TSCONFIG:
20201 : case DO_FDW:
20202 : case DO_FOREIGN_SERVER:
20203 : case DO_TRANSFORM:
20204 : /* Pre-data objects: must come before the pre-data boundary */
20205 0 : addObjectDependency(preDataBound, dobj->dumpId);
20206 0 : break;
20207 : case DO_TABLE_DATA:
20208 : case DO_SEQUENCE_SET:
20209 : case DO_LARGE_OBJECT:
20210 : case DO_LARGE_OBJECT_DATA:
20211 : /* Data objects: must come between the boundaries */
20212 0 : addObjectDependency(dobj, preDataBound->dumpId);
20213 0 : addObjectDependency(postDataBound, dobj->dumpId);
20214 0 : break;
20215 : case DO_INDEX:
20216 : case DO_INDEX_ATTACH:
20217 : case DO_STATSEXT:
20218 : case DO_REFRESH_MATVIEW:
20219 : case DO_TRIGGER:
20220 : case DO_EVENT_TRIGGER:
20221 : case DO_DEFAULT_ACL:
20222 : case DO_POLICY:
20223 : case DO_PUBLICATION:
20224 : case DO_PUBLICATION_REL:
20225 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20226 : case DO_SUBSCRIPTION:
20227 : case DO_SUBSCRIPTION_REL:
20228 : /* Post-data objects: must come after the post-data boundary */
20229 0 : addObjectDependency(dobj, postDataBound->dumpId);
20230 0 : break;
20231 : case DO_RULE:
20232 : /* Rules are post-data, but only if dumped separately */
20233 0 : if (((RuleInfo *) dobj)->separate)
20234 0 : addObjectDependency(dobj, postDataBound->dumpId);
20235 0 : break;
20236 : case DO_CONSTRAINT:
20237 : case DO_FK_CONSTRAINT:
20238 : /* Constraints are post-data, but only if dumped separately */
20239 0 : if (((ConstraintInfo *) dobj)->separate)
20240 0 : addObjectDependency(dobj, postDataBound->dumpId);
20241 0 : break;
20242 : case DO_PRE_DATA_BOUNDARY:
20243 : /* nothing to do */
20244 : break;
20245 : case DO_POST_DATA_BOUNDARY:
20246 : /* must come after the pre-data boundary */
20247 0 : addObjectDependency(dobj, preDataBound->dumpId);
20248 0 : break;
20249 : case DO_REL_STATS:
20250 : /* stats section varies by parent object type, DATA or POST */
20251 0 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20252 : {
20253 0 : addObjectDependency(dobj, preDataBound->dumpId);
20254 0 : addObjectDependency(postDataBound, dobj->dumpId);
20255 0 : }
20256 : else
20257 0 : addObjectDependency(dobj, postDataBound->dumpId);
20258 0 : break;
20259 : }
20260 0 : }
20261 0 : }
20262 :
20263 :
20264 : /*
20265 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20266 : *
20267 : * The raw dependency data obtained by getDependencies() is not terribly
20268 : * useful in an archive dump, because in many cases there are dependency
20269 : * chains linking through objects that don't appear explicitly in the dump.
20270 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20271 : * will depend on other objects --- but the rule will not appear as a separate
20272 : * object in the dump. We need to adjust the view's dependencies to include
20273 : * whatever the rule depends on that is included in the dump.
20274 : *
20275 : * Just to make things more complicated, there are also "special" dependencies
20276 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20277 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20278 : * its table. In these cases we must leave the dependencies strictly as-is
20279 : * even if they refer to not-to-be-dumped objects.
20280 : *
20281 : * To handle this, the convention is that "special" dependencies are created
20282 : * during ArchiveEntry calls, and an archive TOC item that has any such
20283 : * entries will not be touched here. Otherwise, we recursively search the
20284 : * DumpableObject data structures to build the correct dependencies for each
20285 : * archive TOC item.
20286 : */
20287 : static void
20288 0 : BuildArchiveDependencies(Archive *fout)
20289 : {
20290 0 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20291 0 : TocEntry *te;
20292 :
20293 : /* Scan all TOC entries in the archive */
20294 0 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20295 : {
20296 0 : DumpableObject *dobj;
20297 0 : DumpId *dependencies;
20298 0 : int nDeps;
20299 0 : int allocDeps;
20300 :
20301 : /* No need to process entries that will not be dumped */
20302 0 : if (te->reqs == 0)
20303 0 : continue;
20304 : /* Ignore entries that already have "special" dependencies */
20305 0 : if (te->nDeps > 0)
20306 0 : continue;
20307 : /* Otherwise, look up the item's original DumpableObject, if any */
20308 0 : dobj = findObjectByDumpId(te->dumpId);
20309 0 : if (dobj == NULL)
20310 0 : continue;
20311 : /* No work if it has no dependencies */
20312 0 : if (dobj->nDeps <= 0)
20313 0 : continue;
20314 : /* Set up work array */
20315 0 : allocDeps = 64;
20316 0 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20317 0 : nDeps = 0;
20318 : /* Recursively find all dumpable dependencies */
20319 0 : findDumpableDependencies(AH, dobj,
20320 : &dependencies, &nDeps, &allocDeps);
20321 : /* And save 'em ... */
20322 0 : if (nDeps > 0)
20323 : {
20324 0 : dependencies = (DumpId *) pg_realloc(dependencies,
20325 0 : nDeps * sizeof(DumpId));
20326 0 : te->dependencies = dependencies;
20327 0 : te->nDeps = nDeps;
20328 0 : }
20329 : else
20330 0 : free(dependencies);
20331 0 : }
20332 0 : }
20333 :
20334 : /* Recursive search subroutine for BuildArchiveDependencies */
20335 : static void
20336 0 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20337 : DumpId **dependencies, int *nDeps, int *allocDeps)
20338 : {
20339 0 : int i;
20340 :
20341 : /*
20342 : * Ignore section boundary objects: if we search through them, we'll
20343 : * report lots of bogus dependencies.
20344 : */
20345 0 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20346 0 : dobj->objType == DO_POST_DATA_BOUNDARY)
20347 0 : return;
20348 :
20349 0 : for (i = 0; i < dobj->nDeps; i++)
20350 : {
20351 0 : DumpId depid = dobj->dependencies[i];
20352 :
20353 0 : if (TocIDRequired(AH, depid) != 0)
20354 : {
20355 : /* Object will be dumped, so just reference it as a dependency */
20356 0 : if (*nDeps >= *allocDeps)
20357 : {
20358 0 : *allocDeps *= 2;
20359 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20360 0 : *allocDeps * sizeof(DumpId));
20361 0 : }
20362 0 : (*dependencies)[*nDeps] = depid;
20363 0 : (*nDeps)++;
20364 0 : }
20365 : else
20366 : {
20367 : /*
20368 : * Object will not be dumped, so recursively consider its deps. We
20369 : * rely on the assumption that sortDumpableObjects already broke
20370 : * any dependency loops, else we might recurse infinitely.
20371 : */
20372 0 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20373 :
20374 0 : if (otherdobj)
20375 0 : findDumpableDependencies(AH, otherdobj,
20376 0 : dependencies, nDeps, allocDeps);
20377 0 : }
20378 0 : }
20379 0 : }
20380 :
20381 :
20382 : /*
20383 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20384 : * given type OID.
20385 : *
20386 : * This does not guarantee to schema-qualify the output, so it should not
20387 : * be used to create the target object name for CREATE or ALTER commands.
20388 : *
20389 : * Note that the result is cached and must not be freed by the caller.
20390 : */
20391 : static const char *
20392 0 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20393 : {
20394 0 : TypeInfo *typeInfo;
20395 0 : char *result;
20396 0 : PQExpBuffer query;
20397 0 : PGresult *res;
20398 :
20399 0 : if (oid == 0)
20400 : {
20401 0 : if ((opts & zeroAsStar) != 0)
20402 0 : return "*";
20403 0 : else if ((opts & zeroAsNone) != 0)
20404 0 : return "NONE";
20405 0 : }
20406 :
20407 : /* see if we have the result cached in the type's TypeInfo record */
20408 0 : typeInfo = findTypeByOid(oid);
20409 0 : if (typeInfo && typeInfo->ftypname)
20410 0 : return typeInfo->ftypname;
20411 :
20412 0 : query = createPQExpBuffer();
20413 0 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20414 0 : oid);
20415 :
20416 0 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20417 :
20418 : /* result of format_type is already quoted */
20419 0 : result = pg_strdup(PQgetvalue(res, 0, 0));
20420 :
20421 0 : PQclear(res);
20422 0 : destroyPQExpBuffer(query);
20423 :
20424 : /*
20425 : * Cache the result for re-use in later requests, if possible. If we
20426 : * don't have a TypeInfo for the type, the string will be leaked once the
20427 : * caller is done with it ... but that case really should not happen, so
20428 : * leaking if it does seems acceptable.
20429 : */
20430 0 : if (typeInfo)
20431 0 : typeInfo->ftypname = result;
20432 :
20433 0 : return result;
20434 0 : }
20435 :
20436 : /*
20437 : * Return a column list clause for the given relation.
20438 : *
20439 : * Special case: if there are no undropped columns in the relation, return
20440 : * "", not an invalid "()" column list.
20441 : */
20442 : static const char *
20443 0 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20444 : {
20445 0 : int numatts = ti->numatts;
20446 0 : char **attnames = ti->attnames;
20447 0 : bool *attisdropped = ti->attisdropped;
20448 0 : char *attgenerated = ti->attgenerated;
20449 0 : bool needComma;
20450 0 : int i;
20451 :
20452 0 : appendPQExpBufferChar(buffer, '(');
20453 0 : needComma = false;
20454 0 : for (i = 0; i < numatts; i++)
20455 : {
20456 0 : if (attisdropped[i])
20457 0 : continue;
20458 0 : if (attgenerated[i])
20459 0 : continue;
20460 0 : if (needComma)
20461 0 : appendPQExpBufferStr(buffer, ", ");
20462 0 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20463 0 : needComma = true;
20464 0 : }
20465 :
20466 0 : if (!needComma)
20467 0 : return ""; /* no undropped columns */
20468 :
20469 0 : appendPQExpBufferChar(buffer, ')');
20470 0 : return buffer->data;
20471 0 : }
20472 :
20473 : /*
20474 : * Check if a reloptions array is nonempty.
20475 : */
20476 : static bool
20477 0 : nonemptyReloptions(const char *reloptions)
20478 : {
20479 : /* Don't want to print it if it's just "{}" */
20480 0 : return (reloptions != NULL && strlen(reloptions) > 2);
20481 : }
20482 :
20483 : /*
20484 : * Format a reloptions array and append it to the given buffer.
20485 : *
20486 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20487 : */
20488 : static void
20489 0 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20490 : const char *prefix, Archive *fout)
20491 : {
20492 0 : bool res;
20493 :
20494 0 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20495 0 : fout->std_strings);
20496 0 : if (!res)
20497 0 : pg_log_warning("could not parse %s array", "reloptions");
20498 0 : }
20499 :
20500 : /*
20501 : * read_dump_filters - retrieve object identifier patterns from file
20502 : *
20503 : * Parse the specified filter file for include and exclude patterns, and add
20504 : * them to the relevant lists. If the filename is "-" then filters will be
20505 : * read from STDIN rather than a file.
20506 : */
20507 : static void
20508 0 : read_dump_filters(const char *filename, DumpOptions *dopt)
20509 : {
20510 0 : FilterStateData fstate;
20511 0 : char *objname;
20512 0 : FilterCommandType comtype;
20513 0 : FilterObjectType objtype;
20514 :
20515 0 : filter_init(&fstate, filename, exit_nicely);
20516 :
20517 0 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20518 : {
20519 0 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20520 : {
20521 0 : switch (objtype)
20522 : {
20523 : case FILTER_OBJECT_TYPE_NONE:
20524 : break;
20525 : case FILTER_OBJECT_TYPE_DATABASE:
20526 : case FILTER_OBJECT_TYPE_FUNCTION:
20527 : case FILTER_OBJECT_TYPE_INDEX:
20528 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20529 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20530 : case FILTER_OBJECT_TYPE_TRIGGER:
20531 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20532 : "include",
20533 0 : filter_object_type_name(objtype));
20534 0 : exit_nicely(1);
20535 : break; /* unreachable */
20536 :
20537 : case FILTER_OBJECT_TYPE_EXTENSION:
20538 0 : simple_string_list_append(&extension_include_patterns, objname);
20539 0 : break;
20540 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20541 0 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20542 0 : break;
20543 : case FILTER_OBJECT_TYPE_SCHEMA:
20544 0 : simple_string_list_append(&schema_include_patterns, objname);
20545 0 : dopt->include_everything = false;
20546 0 : break;
20547 : case FILTER_OBJECT_TYPE_TABLE:
20548 0 : simple_string_list_append(&table_include_patterns, objname);
20549 0 : dopt->include_everything = false;
20550 0 : break;
20551 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20552 0 : simple_string_list_append(&table_include_patterns_and_children,
20553 0 : objname);
20554 0 : dopt->include_everything = false;
20555 0 : break;
20556 : }
20557 0 : }
20558 0 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20559 : {
20560 0 : switch (objtype)
20561 : {
20562 : case FILTER_OBJECT_TYPE_NONE:
20563 : break;
20564 : case FILTER_OBJECT_TYPE_DATABASE:
20565 : case FILTER_OBJECT_TYPE_FUNCTION:
20566 : case FILTER_OBJECT_TYPE_INDEX:
20567 : case FILTER_OBJECT_TYPE_TRIGGER:
20568 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20569 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20570 : "exclude",
20571 0 : filter_object_type_name(objtype));
20572 0 : exit_nicely(1);
20573 : break;
20574 :
20575 : case FILTER_OBJECT_TYPE_EXTENSION:
20576 0 : simple_string_list_append(&extension_exclude_patterns, objname);
20577 0 : break;
20578 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20579 0 : simple_string_list_append(&tabledata_exclude_patterns,
20580 0 : objname);
20581 0 : break;
20582 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20583 0 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20584 0 : objname);
20585 0 : break;
20586 : case FILTER_OBJECT_TYPE_SCHEMA:
20587 0 : simple_string_list_append(&schema_exclude_patterns, objname);
20588 0 : break;
20589 : case FILTER_OBJECT_TYPE_TABLE:
20590 0 : simple_string_list_append(&table_exclude_patterns, objname);
20591 0 : break;
20592 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20593 0 : simple_string_list_append(&table_exclude_patterns_and_children,
20594 0 : objname);
20595 0 : break;
20596 : }
20597 0 : }
20598 : else
20599 : {
20600 0 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20601 0 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20602 : }
20603 :
20604 0 : if (objname)
20605 0 : free(objname);
20606 : }
20607 :
20608 0 : filter_free(&fstate);
20609 0 : }
|