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 596 : main(int argc, char **argv)
423 : {
424 : int c;
425 596 : const char *filename = NULL;
426 596 : const char *format = "p";
427 : TableInfo *tblinfo;
428 : int numTables;
429 : DumpableObject **dobjs;
430 : int numObjs;
431 : DumpableObject *boundaryObjs;
432 : int i;
433 : int optindex;
434 : RestoreOptions *ropt;
435 : Archive *fout; /* the script file */
436 596 : bool g_verbose = false;
437 596 : const char *dumpencoding = NULL;
438 596 : const char *dumpsnapshot = NULL;
439 596 : char *use_role = NULL;
440 596 : int numWorkers = 1;
441 596 : int plainText = 0;
442 596 : ArchiveFormat archiveFormat = archUnknown;
443 : ArchiveMode archiveMode;
444 596 : pg_compress_specification compression_spec = {0};
445 596 : char *compression_detail = NULL;
446 596 : char *compression_algorithm_str = "none";
447 596 : char *error_detail = NULL;
448 596 : bool user_compression_defined = false;
449 596 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
450 596 : bool data_only = false;
451 596 : bool schema_only = false;
452 596 : bool statistics_only = false;
453 596 : bool with_statistics = false;
454 596 : bool no_data = false;
455 596 : bool no_schema = false;
456 596 : 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 596 : pg_logging_init(argv[0]);
547 596 : pg_logging_set_level(PG_LOG_WARNING);
548 596 : 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 596 : init_parallel_dump_utils();
555 :
556 596 : progname = get_progname(argv[0]);
557 :
558 596 : if (argc > 1)
559 : {
560 596 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
561 : {
562 2 : help(progname);
563 2 : exit_nicely(0);
564 : }
565 594 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
566 : {
567 128 : puts("pg_dump (PostgreSQL) " PG_VERSION);
568 128 : exit_nicely(0);
569 : }
570 : }
571 :
572 466 : InitDumpOptions(&dopt);
573 :
574 2636 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
575 2636 : long_options, &optindex)) != -1)
576 : {
577 2186 : switch (c)
578 : {
579 18 : case 'a': /* Dump data only */
580 18 : data_only = true;
581 18 : break;
582 :
583 2 : case 'b': /* Dump LOs */
584 2 : dopt.outputLOs = true;
585 2 : break;
586 :
587 4 : case 'B': /* Don't dump LOs */
588 4 : dopt.dontOutputLOs = true;
589 4 : break;
590 :
591 12 : case 'c': /* clean (i.e., drop) schema prior to create */
592 12 : dopt.outputClean = 1;
593 12 : break;
594 :
595 58 : case 'C': /* Create DB */
596 58 : dopt.outputCreateDB = 1;
597 58 : break;
598 :
599 10 : case 'd': /* database name */
600 10 : dopt.cparams.dbname = pg_strdup(optarg);
601 10 : break;
602 :
603 8 : case 'e': /* include extension(s) */
604 8 : simple_string_list_append(&extension_include_patterns, optarg);
605 8 : dopt.include_everything = false;
606 8 : break;
607 :
608 4 : case 'E': /* Dump encoding */
609 4 : dumpencoding = pg_strdup(optarg);
610 4 : break;
611 :
612 386 : case 'f':
613 386 : filename = pg_strdup(optarg);
614 386 : break;
615 :
616 226 : case 'F':
617 226 : format = pg_strdup(optarg);
618 226 : break;
619 :
620 72 : case 'h': /* server host */
621 72 : dopt.cparams.pghost = pg_strdup(optarg);
622 72 : break;
623 :
624 22 : case 'j': /* number of dump jobs */
625 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
626 : PG_MAX_JOBS,
627 : &numWorkers))
628 2 : exit_nicely(1);
629 20 : break;
630 :
631 34 : case 'n': /* include schema(s) */
632 34 : simple_string_list_append(&schema_include_patterns, optarg);
633 34 : dopt.include_everything = false;
634 34 : break;
635 :
636 2 : case 'N': /* exclude schema(s) */
637 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
638 2 : break;
639 :
640 4 : case 'O': /* Don't reconnect to match owner */
641 4 : dopt.outputNoOwner = 1;
642 4 : break;
643 :
644 150 : case 'p': /* server port */
645 150 : dopt.cparams.pgport = pg_strdup(optarg);
646 150 : break;
647 :
648 4 : case 'R':
649 : /* no-op, still accepted for backwards compatibility */
650 4 : break;
651 :
652 14 : case 's': /* dump schema only */
653 14 : schema_only = true;
654 14 : break;
655 :
656 2 : case 'S': /* Username for superuser in plain text output */
657 2 : dopt.outputSuperuser = pg_strdup(optarg);
658 2 : break;
659 :
660 16 : case 't': /* include table(s) */
661 16 : simple_string_list_append(&table_include_patterns, optarg);
662 16 : dopt.include_everything = false;
663 16 : break;
664 :
665 8 : case 'T': /* exclude table(s) */
666 8 : simple_string_list_append(&table_exclude_patterns, optarg);
667 8 : break;
668 :
669 76 : case 'U':
670 76 : dopt.cparams.username = pg_strdup(optarg);
671 76 : break;
672 :
673 12 : case 'v': /* verbose */
674 12 : g_verbose = true;
675 12 : pg_logging_increase_verbosity();
676 12 : break;
677 :
678 2 : case 'w':
679 2 : dopt.cparams.promptPassword = TRI_NO;
680 2 : break;
681 :
682 0 : case 'W':
683 0 : dopt.cparams.promptPassword = TRI_YES;
684 0 : break;
685 :
686 4 : case 'x': /* skip ACL dump */
687 4 : dopt.aclsSkip = true;
688 4 : break;
689 :
690 26 : case 'Z': /* Compression */
691 26 : parse_compress_options(optarg, &compression_algorithm_str,
692 : &compression_detail);
693 26 : user_compression_defined = true;
694 26 : break;
695 :
696 270 : case 0:
697 : /* This covers the long options. */
698 270 : break;
699 :
700 4 : case 2: /* lock-wait-timeout */
701 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
702 4 : break;
703 :
704 6 : case 3: /* SET ROLE */
705 6 : use_role = pg_strdup(optarg);
706 6 : break;
707 :
708 2 : case 4: /* exclude table(s) data */
709 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
710 2 : break;
711 :
712 12 : case 5: /* section */
713 12 : set_dump_section(optarg, &dopt.dumpSections);
714 12 : break;
715 :
716 0 : case 6: /* snapshot */
717 0 : dumpsnapshot = pg_strdup(optarg);
718 0 : break;
719 :
720 300 : case 7: /* no-sync */
721 300 : dosync = false;
722 300 : break;
723 :
724 2 : case 8:
725 2 : have_extra_float_digits = true;
726 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
727 : &extra_float_digits))
728 2 : exit_nicely(1);
729 0 : break;
730 :
731 4 : case 9: /* inserts */
732 :
733 : /*
734 : * dump_inserts also stores --rows-per-insert, careful not to
735 : * overwrite that.
736 : */
737 4 : if (dopt.dump_inserts == 0)
738 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
739 4 : break;
740 :
741 4 : case 10: /* rows per insert */
742 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
743 : &dopt.dump_inserts))
744 2 : exit_nicely(1);
745 2 : break;
746 :
747 8 : case 11: /* include foreign data */
748 8 : simple_string_list_append(&foreign_servers_include_patterns,
749 : optarg);
750 8 : break;
751 :
752 2 : case 12: /* include table(s) and their children */
753 2 : simple_string_list_append(&table_include_patterns_and_children,
754 : optarg);
755 2 : dopt.include_everything = false;
756 2 : break;
757 :
758 2 : case 13: /* exclude table(s) and their children */
759 2 : simple_string_list_append(&table_exclude_patterns_and_children,
760 : optarg);
761 2 : break;
762 :
763 2 : case 14: /* exclude data of table(s) and children */
764 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
765 : optarg);
766 2 : break;
767 :
768 0 : case 15:
769 0 : if (!parse_sync_method(optarg, &sync_method))
770 0 : exit_nicely(1);
771 0 : break;
772 :
773 52 : case 16: /* read object filters from file */
774 52 : read_dump_filters(optarg, &dopt);
775 44 : break;
776 :
777 2 : case 17: /* exclude extension(s) */
778 2 : simple_string_list_append(&extension_exclude_patterns,
779 : optarg);
780 2 : break;
781 :
782 8 : case 18:
783 8 : statistics_only = true;
784 8 : break;
785 :
786 76 : case 19:
787 76 : no_data = true;
788 76 : break;
789 :
790 4 : case 20:
791 4 : no_schema = true;
792 4 : break;
793 :
794 16 : case 21:
795 16 : no_statistics = true;
796 16 : break;
797 :
798 180 : case 22:
799 180 : with_statistics = true;
800 180 : break;
801 :
802 52 : case 25:
803 52 : dopt.restrict_key = pg_strdup(optarg);
804 52 : break;
805 :
806 2 : default:
807 : /* getopt_long already emitted a complaint */
808 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
809 2 : 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 450 : if (optind < argc && dopt.cparams.dbname == NULL)
818 378 : dopt.cparams.dbname = argv[optind++];
819 :
820 : /* Complain if any arguments remain */
821 450 : if (optind < argc)
822 : {
823 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
824 : argv[optind]);
825 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
826 2 : exit_nicely(1);
827 : }
828 :
829 : /* --column-inserts implies --inserts */
830 448 : if (dopt.column_inserts && dopt.dump_inserts == 0)
831 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
832 :
833 : /* reject conflicting "-only" options */
834 448 : if (data_only && schema_only)
835 2 : pg_fatal("options %s and %s cannot be used together",
836 : "-s/--schema-only", "-a/--data-only");
837 446 : if (schema_only && statistics_only)
838 2 : pg_fatal("options %s and %s cannot be used together",
839 : "-s/--schema-only", "--statistics-only");
840 444 : if (data_only && statistics_only)
841 2 : 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 442 : if (data_only && no_data)
846 0 : pg_fatal("options %s and %s cannot be used together",
847 : "-a/--data-only", "--no-data");
848 442 : if (schema_only && no_schema)
849 0 : pg_fatal("options %s and %s cannot be used together",
850 : "-s/--schema-only", "--no-schema");
851 442 : if (statistics_only && no_statistics)
852 2 : pg_fatal("options %s and %s cannot be used together",
853 : "--statistics-only", "--no-statistics");
854 :
855 : /* reject conflicting "no-" options */
856 440 : 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 440 : if (data_only && with_statistics)
862 0 : pg_fatal("options %s and %s cannot be used together",
863 : "-a/--data-only", "--statistics");
864 440 : if (schema_only && with_statistics)
865 2 : pg_fatal("options %s and %s cannot be used together",
866 : "-s/--schema-only", "--statistics");
867 :
868 438 : if (schema_only && foreign_servers_include_patterns.head != NULL)
869 2 : pg_fatal("options %s and %s cannot be used together",
870 : "-s/--schema-only", "--include-foreign-data");
871 :
872 436 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
873 2 : pg_fatal("option %s is not supported with parallel backup",
874 : "--include-foreign-data");
875 :
876 434 : if (data_only && dopt.outputClean)
877 2 : pg_fatal("options %s and %s cannot be used together",
878 : "-c/--clean", "-a/--data-only");
879 :
880 432 : if (dopt.if_exists && !dopt.outputClean)
881 2 : 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 430 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
890 860 : data_only) && !no_data;
891 430 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
892 860 : schema_only) && !no_schema;
893 430 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
894 860 : (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 430 : if (dopt.do_nothing && dopt.dump_inserts == 0)
902 2 : 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 428 : archiveFormat = parseArchiveFormat(format, &archiveMode);
908 :
909 : /* archiveFormat specific setup */
910 426 : if (archiveFormat == archNull)
911 : {
912 308 : plainText = 1;
913 :
914 : /*
915 : * If you don't provide a restrict key, one will be appointed for you.
916 : */
917 308 : if (!dopt.restrict_key)
918 256 : dopt.restrict_key = generate_restrict_key();
919 308 : if (!dopt.restrict_key)
920 0 : pg_fatal("could not generate restrict key");
921 308 : if (!valid_restrict_key(dopt.restrict_key))
922 0 : pg_fatal("invalid restrict key");
923 : }
924 118 : 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 426 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
934 112 : !user_compression_defined)
935 : {
936 : #ifdef HAVE_LIBZ
937 100 : compression_algorithm_str = "gzip";
938 : #else
939 : compression_algorithm_str = "none";
940 : #endif
941 : }
942 :
943 : /*
944 : * Compression options
945 : */
946 426 : if (!parse_compress_algorithm(compression_algorithm_str,
947 : &compression_algorithm))
948 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
949 : compression_algorithm_str);
950 :
951 424 : parse_compress_specification(compression_algorithm, compression_detail,
952 : &compression_spec);
953 424 : error_detail = validate_compress_specification(&compression_spec);
954 424 : if (error_detail != NULL)
955 6 : pg_fatal("invalid compression specification: %s",
956 : error_detail);
957 :
958 418 : error_detail = supports_compression(compression_spec);
959 418 : 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 418 : 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 418 : if (!plainText)
976 118 : dopt.outputCreateDB = 1;
977 :
978 : /* Parallel backup only in the directory archive format so far */
979 418 : if (archiveFormat != archDirectory && numWorkers > 1)
980 2 : pg_fatal("parallel backup only supported by the directory format");
981 :
982 : /* Open the output file */
983 416 : fout = CreateArchive(filename, archiveFormat, compression_spec,
984 : dosync, archiveMode, setupDumpWorker, sync_method);
985 :
986 : /* Make dump options accessible right away */
987 414 : SetArchiveOptions(fout, &dopt, NULL);
988 :
989 : /* Register the cleanup hook */
990 414 : on_exit_close_archive(fout);
991 :
992 : /* Let the archiver know how noisy to be */
993 414 : 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 414 : fout->minRemoteVersion = 90200;
1001 414 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
1002 :
1003 414 : fout->numWorkers = numWorkers;
1004 :
1005 : /*
1006 : * Open the database using the Archiver, so it knows about it. Errors mean
1007 : * death.
1008 : */
1009 414 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
1010 410 : 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 410 : if (fout->isStandby)
1017 8 : 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 410 : g_last_builtin_oid = FirstNormalObjectId - 1;
1025 :
1026 410 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1027 :
1028 : /* Expand schema selection patterns into OID lists */
1029 410 : if (schema_include_patterns.head != NULL)
1030 : {
1031 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1032 : &schema_include_oids,
1033 : strict_names);
1034 24 : if (schema_include_oids.head == NULL)
1035 2 : pg_fatal("no matching schemas were found");
1036 : }
1037 396 : 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 396 : expand_table_name_patterns(fout, &table_include_patterns,
1044 : &table_include_oids,
1045 : strict_names, false);
1046 386 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1047 : &table_include_oids,
1048 : strict_names, true);
1049 386 : if ((table_include_patterns.head != NULL ||
1050 364 : table_include_patterns_and_children.head != NULL) &&
1051 26 : table_include_oids.head == NULL)
1052 4 : pg_fatal("no matching tables were found");
1053 :
1054 382 : expand_table_name_patterns(fout, &table_exclude_patterns,
1055 : &table_exclude_oids,
1056 : false, false);
1057 382 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1058 : &table_exclude_oids,
1059 : false, true);
1060 :
1061 382 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1062 : &tabledata_exclude_oids,
1063 : false, false);
1064 382 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1065 : &tabledata_exclude_oids,
1066 : false, true);
1067 :
1068 382 : 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 380 : if (extension_include_patterns.head != NULL)
1075 : {
1076 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1077 : &extension_include_oids,
1078 : strict_names);
1079 10 : if (extension_include_oids.head == NULL)
1080 2 : pg_fatal("no matching extensions were found");
1081 : }
1082 378 : 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 378 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1097 244 : dopt.outputLOs = true;
1098 :
1099 : /*
1100 : * Collect role names so we can map object owner OIDs to names.
1101 : */
1102 378 : collectRoleNames(fout);
1103 :
1104 : /*
1105 : * Now scan the database and create DumpableObject structs for all the
1106 : * objects we intend to dump.
1107 : */
1108 378 : tblinfo = getSchemaData(fout, &numTables);
1109 :
1110 376 : if (dopt.dumpData)
1111 : {
1112 292 : getTableData(&dopt, tblinfo, numTables, 0);
1113 292 : buildMatViewRefreshDependencies(fout);
1114 292 : if (!dopt.dumpSchema)
1115 14 : getTableDataFKConstraints();
1116 : }
1117 :
1118 376 : if (!dopt.dumpData && dopt.sequence_data)
1119 68 : 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 376 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1129 : {
1130 76 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1131 76 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1132 :
1133 76 : makeTableDataInfo(&dopt, lo_metadata);
1134 76 : 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 76 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1141 76 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1142 :
1143 : /*
1144 : * Only dump large object shdepend rows for this database.
1145 : */
1146 76 : 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 76 : if (fout->remoteVersion >= 160000)
1161 76 : 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 : }
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 376 : if (dopt.outputLOs || dopt.binary_upgrade)
1177 320 : getLOs(fout);
1178 :
1179 : /*
1180 : * Collect dependency data to assist in ordering the objects.
1181 : */
1182 376 : getDependencies(fout);
1183 :
1184 : /*
1185 : * Collect ACLs, comments, and security labels, if wanted.
1186 : */
1187 376 : if (!dopt.aclsSkip)
1188 372 : getAdditionalACLs(fout);
1189 376 : if (!dopt.no_comments)
1190 376 : collectComments(fout);
1191 376 : if (!dopt.no_security_labels)
1192 376 : collectSecLabels(fout);
1193 :
1194 : /* For binary upgrade mode, collect required pg_class information. */
1195 376 : if (dopt.binary_upgrade)
1196 76 : collectBinaryUpgradeClassOids(fout);
1197 :
1198 : /* Collect sequence information. */
1199 376 : collectSequences(fout);
1200 :
1201 : /* Lastly, create dummy objects to represent the section boundaries */
1202 376 : boundaryObjs = createBoundaryObjects();
1203 :
1204 : /* Get pointers to all the known DumpableObjects */
1205 376 : getDumpableObjects(&dobjs, &numObjs);
1206 :
1207 : /*
1208 : * Add dummy dependencies to enforce the dump section ordering.
1209 : */
1210 376 : 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 376 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1220 :
1221 376 : sortDumpableObjects(dobjs, numObjs,
1222 376 : 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 376 : dumpEncoding(fout);
1233 376 : dumpStdStrings(fout);
1234 376 : dumpSearchPath(fout);
1235 :
1236 : /* The database items are always next, unless we don't want them at all */
1237 376 : if (dopt.outputCreateDB)
1238 174 : dumpDatabase(fout);
1239 :
1240 : /* Now the rearrangeable objects. */
1241 1401708 : for (i = 0; i < numObjs; i++)
1242 1401332 : dumpDumpableObject(fout, dobjs[i]);
1243 :
1244 : /*
1245 : * Set up options info to ensure we dump what we want.
1246 : */
1247 376 : ropt = NewRestoreOptions();
1248 376 : ropt->filename = filename;
1249 :
1250 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1251 376 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1252 376 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1253 376 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1254 376 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1255 376 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1256 376 : ropt->dropSchema = dopt.outputClean;
1257 376 : ropt->dumpData = dopt.dumpData;
1258 376 : ropt->dumpSchema = dopt.dumpSchema;
1259 376 : ropt->dumpStatistics = dopt.dumpStatistics;
1260 376 : ropt->if_exists = dopt.if_exists;
1261 376 : ropt->column_inserts = dopt.column_inserts;
1262 376 : ropt->dumpSections = dopt.dumpSections;
1263 376 : ropt->aclsSkip = dopt.aclsSkip;
1264 376 : ropt->superuser = dopt.outputSuperuser;
1265 376 : ropt->createDB = dopt.outputCreateDB;
1266 376 : ropt->noOwner = dopt.outputNoOwner;
1267 376 : ropt->noTableAm = dopt.outputNoTableAm;
1268 376 : ropt->noTablespace = dopt.outputNoTablespaces;
1269 376 : ropt->disable_triggers = dopt.disable_triggers;
1270 376 : ropt->use_setsessauth = dopt.use_setsessauth;
1271 376 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1272 376 : ropt->dump_inserts = dopt.dump_inserts;
1273 376 : ropt->no_comments = dopt.no_comments;
1274 376 : ropt->no_policies = dopt.no_policies;
1275 376 : ropt->no_publications = dopt.no_publications;
1276 376 : ropt->no_security_labels = dopt.no_security_labels;
1277 376 : ropt->no_subscriptions = dopt.no_subscriptions;
1278 376 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1279 376 : ropt->include_everything = dopt.include_everything;
1280 376 : ropt->enable_row_security = dopt.enable_row_security;
1281 376 : ropt->sequence_data = dopt.sequence_data;
1282 376 : ropt->binary_upgrade = dopt.binary_upgrade;
1283 376 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1284 :
1285 376 : ropt->compression_spec = compression_spec;
1286 :
1287 376 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1288 :
1289 376 : SetArchiveOptions(fout, &dopt, ropt);
1290 :
1291 : /* Mark which entries should be output */
1292 376 : 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 376 : if (!plainText)
1300 116 : 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 376 : if (plainText)
1310 260 : RestoreArchive(fout);
1311 :
1312 374 : CloseArchive(fout);
1313 :
1314 374 : exit_nicely(0);
1315 : }
1316 :
1317 :
1318 : static void
1319 2 : help(const char *progname)
1320 : {
1321 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1322 2 : printf(_("Usage:\n"));
1323 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1324 :
1325 2 : printf(_("\nGeneral options:\n"));
1326 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1327 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1328 : " plain text (default))\n"));
1329 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1330 2 : printf(_(" -v, --verbose verbose mode\n"));
1331 2 : printf(_(" -V, --version output version information, then exit\n"));
1332 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1333 : " compress as specified\n"));
1334 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1335 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1336 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1337 2 : printf(_(" -?, --help show this help, then exit\n"));
1338 :
1339 2 : printf(_("\nOptions controlling the output content:\n"));
1340 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1341 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1342 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1343 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1344 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1345 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1346 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1347 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1348 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1349 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1350 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1351 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1352 : " plain-text format\n"));
1353 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1354 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1355 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1356 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1357 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1358 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1359 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1360 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1361 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1362 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1363 : " access to)\n"));
1364 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1365 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1366 : " do NOT dump the specified table(s), including\n"
1367 : " child and partition tables\n"));
1368 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1369 2 : 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 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1373 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1374 : " based on expressions in FILENAME\n"));
1375 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1376 2 : printf(_(" --include-foreign-data=PATTERN\n"
1377 : " include data of foreign tables on foreign\n"
1378 : " servers matching PATTERN\n"));
1379 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1380 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1381 2 : printf(_(" --no-comments do not dump comment commands\n"));
1382 2 : printf(_(" --no-data do not dump data\n"));
1383 2 : printf(_(" --no-policies do not dump row security policies\n"));
1384 2 : printf(_(" --no-publications do not dump publications\n"));
1385 2 : printf(_(" --no-schema do not dump schema\n"));
1386 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1387 2 : printf(_(" --no-statistics do not dump statistics\n"));
1388 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1389 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1390 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1391 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1392 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1393 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1394 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1395 2 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1396 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1397 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1398 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1399 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1400 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1401 2 : printf(_(" --statistics dump the statistics\n"));
1402 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1403 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1404 : " match at least one entity each\n"));
1405 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1406 : " child and partition tables\n"));
1407 2 : 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 2 : printf(_("\nConnection options:\n"));
1412 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1413 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1414 2 : printf(_(" -p, --port=PORT database server port number\n"));
1415 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1416 2 : printf(_(" -w, --no-password never prompt for password\n"));
1417 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1418 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1419 :
1420 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1421 : "variable value is used.\n\n"));
1422 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1423 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1424 2 : }
1425 :
1426 : static void
1427 442 : setup_connection(Archive *AH, const char *dumpencoding,
1428 : const char *dumpsnapshot, char *use_role)
1429 : {
1430 442 : DumpOptions *dopt = AH->dopt;
1431 442 : PGconn *conn = GetConnection(AH);
1432 : const char *std_strings;
1433 :
1434 442 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1435 :
1436 : /*
1437 : * Set the client encoding if requested.
1438 : */
1439 442 : if (dumpencoding)
1440 : {
1441 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1442 0 : pg_fatal("invalid client encoding \"%s\" specified",
1443 : dumpencoding);
1444 : }
1445 :
1446 : /*
1447 : * Get the active encoding and the standard_conforming_strings setting, so
1448 : * we know how to escape strings.
1449 : */
1450 442 : AH->encoding = PQclientEncoding(conn);
1451 442 : setFmtEncoding(AH->encoding);
1452 :
1453 442 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1454 442 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1455 :
1456 : /*
1457 : * Set the role if requested. In a parallel dump worker, we'll be passed
1458 : * use_role == NULL, but AH->use_role is already set (if user specified it
1459 : * originally) and we should use that.
1460 : */
1461 442 : if (!use_role && AH->use_role)
1462 4 : use_role = AH->use_role;
1463 :
1464 : /* Set the role if requested */
1465 442 : if (use_role)
1466 : {
1467 10 : PQExpBuffer query = createPQExpBuffer();
1468 :
1469 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1470 10 : ExecuteSqlStatement(AH, query->data);
1471 10 : destroyPQExpBuffer(query);
1472 :
1473 : /* save it for possible later use by parallel workers */
1474 10 : if (!AH->use_role)
1475 6 : AH->use_role = pg_strdup(use_role);
1476 : }
1477 :
1478 : /* Set the datestyle to ISO to ensure the dump's portability */
1479 442 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1480 :
1481 : /* Likewise, avoid using sql_standard intervalstyle */
1482 442 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1483 :
1484 : /*
1485 : * Use an explicitly specified extra_float_digits if it has been provided.
1486 : * Otherwise, set extra_float_digits so that we can dump float data
1487 : * exactly (given correctly implemented float I/O code, anyway).
1488 : */
1489 442 : if (have_extra_float_digits)
1490 : {
1491 0 : PQExpBuffer q = createPQExpBuffer();
1492 :
1493 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1494 : extra_float_digits);
1495 0 : ExecuteSqlStatement(AH, q->data);
1496 0 : destroyPQExpBuffer(q);
1497 : }
1498 : else
1499 442 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1500 :
1501 : /*
1502 : * Disable synchronized scanning, to prevent unpredictable changes in row
1503 : * ordering across a dump and reload.
1504 : */
1505 442 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1506 :
1507 : /*
1508 : * Disable timeouts if supported.
1509 : */
1510 442 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1511 442 : if (AH->remoteVersion >= 90300)
1512 442 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1513 442 : if (AH->remoteVersion >= 90600)
1514 442 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1515 442 : if (AH->remoteVersion >= 170000)
1516 442 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1517 :
1518 : /*
1519 : * Quote all identifiers, if requested.
1520 : */
1521 442 : if (quote_all_identifiers)
1522 72 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1523 :
1524 : /*
1525 : * Adjust row-security mode, if supported.
1526 : */
1527 442 : if (AH->remoteVersion >= 90500)
1528 : {
1529 442 : if (dopt->enable_row_security)
1530 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1531 : else
1532 442 : ExecuteSqlStatement(AH, "SET row_security = off");
1533 : }
1534 :
1535 : /*
1536 : * For security reasons, we restrict the expansion of non-system views and
1537 : * access to foreign tables during the pg_dump process. This restriction
1538 : * is adjusted when dumping foreign table data.
1539 : */
1540 442 : set_restrict_relation_kind(AH, "view, foreign-table");
1541 :
1542 : /*
1543 : * Initialize prepared-query state to "nothing prepared". We do this here
1544 : * so that a parallel dump worker will have its own state.
1545 : */
1546 442 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1547 :
1548 : /*
1549 : * Start transaction-snapshot mode transaction to dump consistent data.
1550 : */
1551 442 : ExecuteSqlStatement(AH, "BEGIN");
1552 :
1553 : /*
1554 : * To support the combination of serializable_deferrable with the jobs
1555 : * option we use REPEATABLE READ for the worker connections that are
1556 : * passed a snapshot. As long as the snapshot is acquired in a
1557 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1558 : * REPEATABLE READ transaction provides the appropriate integrity
1559 : * guarantees. This is a kluge, but safe for back-patching.
1560 : */
1561 442 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1562 0 : ExecuteSqlStatement(AH,
1563 : "SET TRANSACTION ISOLATION LEVEL "
1564 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1565 : else
1566 442 : ExecuteSqlStatement(AH,
1567 : "SET TRANSACTION ISOLATION LEVEL "
1568 : "REPEATABLE READ, READ ONLY");
1569 :
1570 : /*
1571 : * If user specified a snapshot to use, select that. In a parallel dump
1572 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1573 : * is already set (if the server can handle it) and we should use that.
1574 : */
1575 442 : if (dumpsnapshot)
1576 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1577 :
1578 442 : if (AH->sync_snapshot_id)
1579 : {
1580 32 : PQExpBuffer query = createPQExpBuffer();
1581 :
1582 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1583 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1584 32 : ExecuteSqlStatement(AH, query->data);
1585 32 : destroyPQExpBuffer(query);
1586 : }
1587 410 : else if (AH->numWorkers > 1)
1588 : {
1589 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1590 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1591 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1592 : }
1593 442 : }
1594 :
1595 : /* Set up connection for a parallel worker process */
1596 : static void
1597 32 : setupDumpWorker(Archive *AH)
1598 : {
1599 : /*
1600 : * We want to re-select all the same values the leader connection is
1601 : * using. We'll have inherited directly-usable values in
1602 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1603 : * inherited encoding value back to a string to pass to setup_connection.
1604 : */
1605 32 : setup_connection(AH,
1606 : pg_encoding_to_char(AH->encoding),
1607 : NULL,
1608 : NULL);
1609 32 : }
1610 :
1611 : static char *
1612 16 : get_synchronized_snapshot(Archive *fout)
1613 : {
1614 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1615 : char *result;
1616 : PGresult *res;
1617 :
1618 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1619 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1620 16 : PQclear(res);
1621 :
1622 16 : return result;
1623 : }
1624 :
1625 : static ArchiveFormat
1626 428 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1627 : {
1628 : ArchiveFormat archiveFormat;
1629 :
1630 428 : *mode = archModeWrite;
1631 :
1632 428 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1633 : {
1634 : /* This is used by pg_dumpall, and is not documented */
1635 98 : archiveFormat = archNull;
1636 98 : *mode = archModeAppend;
1637 : }
1638 330 : else if (pg_strcasecmp(format, "c") == 0)
1639 0 : archiveFormat = archCustom;
1640 330 : else if (pg_strcasecmp(format, "custom") == 0)
1641 92 : archiveFormat = archCustom;
1642 238 : else if (pg_strcasecmp(format, "d") == 0)
1643 0 : archiveFormat = archDirectory;
1644 238 : else if (pg_strcasecmp(format, "directory") == 0)
1645 20 : archiveFormat = archDirectory;
1646 218 : else if (pg_strcasecmp(format, "p") == 0)
1647 204 : archiveFormat = archNull;
1648 14 : else if (pg_strcasecmp(format, "plain") == 0)
1649 6 : archiveFormat = archNull;
1650 8 : else if (pg_strcasecmp(format, "t") == 0)
1651 0 : archiveFormat = archTar;
1652 8 : else if (pg_strcasecmp(format, "tar") == 0)
1653 6 : archiveFormat = archTar;
1654 : else
1655 2 : pg_fatal("invalid output format \"%s\" specified", format);
1656 426 : return archiveFormat;
1657 : }
1658 :
1659 : /*
1660 : * Find the OIDs of all schemas matching the given list of patterns,
1661 : * and append them to the given OID list.
1662 : */
1663 : static void
1664 432 : expand_schema_name_patterns(Archive *fout,
1665 : SimpleStringList *patterns,
1666 : SimpleOidList *oids,
1667 : bool strict_names)
1668 : {
1669 : PQExpBuffer query;
1670 : PGresult *res;
1671 : SimpleStringListCell *cell;
1672 : int i;
1673 :
1674 432 : if (patterns->head == NULL)
1675 390 : return; /* nothing to do */
1676 :
1677 42 : query = createPQExpBuffer();
1678 :
1679 : /*
1680 : * The loop below runs multiple SELECTs might sometimes result in
1681 : * duplicate entries in the OID list, but we don't care.
1682 : */
1683 :
1684 72 : for (cell = patterns->head; cell; cell = cell->next)
1685 : {
1686 : PQExpBufferData dbbuf;
1687 : int dotcnt;
1688 :
1689 42 : appendPQExpBufferStr(query,
1690 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1691 42 : initPQExpBuffer(&dbbuf);
1692 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1693 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1694 : &dotcnt);
1695 42 : if (dotcnt > 1)
1696 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1697 : cell->val);
1698 38 : else if (dotcnt == 1)
1699 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1700 32 : termPQExpBuffer(&dbbuf);
1701 :
1702 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1703 32 : if (strict_names && PQntuples(res) == 0)
1704 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1705 :
1706 58 : for (i = 0; i < PQntuples(res); i++)
1707 : {
1708 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1709 : }
1710 :
1711 30 : PQclear(res);
1712 30 : resetPQExpBuffer(query);
1713 : }
1714 :
1715 30 : destroyPQExpBuffer(query);
1716 : }
1717 :
1718 : /*
1719 : * Find the OIDs of all extensions matching the given list of patterns,
1720 : * and append them to the given OID list.
1721 : */
1722 : static void
1723 388 : expand_extension_name_patterns(Archive *fout,
1724 : SimpleStringList *patterns,
1725 : SimpleOidList *oids,
1726 : bool strict_names)
1727 : {
1728 : PQExpBuffer query;
1729 : PGresult *res;
1730 : SimpleStringListCell *cell;
1731 : int i;
1732 :
1733 388 : if (patterns->head == NULL)
1734 374 : return; /* nothing to do */
1735 :
1736 14 : query = createPQExpBuffer();
1737 :
1738 : /*
1739 : * The loop below runs multiple SELECTs might sometimes result in
1740 : * duplicate entries in the OID list, but we don't care.
1741 : */
1742 28 : for (cell = patterns->head; cell; cell = cell->next)
1743 : {
1744 : int dotcnt;
1745 :
1746 14 : appendPQExpBufferStr(query,
1747 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1748 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1749 : false, NULL, "e.extname", NULL, NULL, NULL,
1750 : &dotcnt);
1751 14 : if (dotcnt > 0)
1752 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1753 : cell->val);
1754 :
1755 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1756 14 : if (strict_names && PQntuples(res) == 0)
1757 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1758 :
1759 26 : for (i = 0; i < PQntuples(res); i++)
1760 : {
1761 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1762 : }
1763 :
1764 14 : PQclear(res);
1765 14 : resetPQExpBuffer(query);
1766 : }
1767 :
1768 14 : destroyPQExpBuffer(query);
1769 : }
1770 :
1771 : /*
1772 : * Find the OIDs of all foreign servers matching the given list of patterns,
1773 : * and append them to the given OID list.
1774 : */
1775 : static void
1776 382 : expand_foreign_server_name_patterns(Archive *fout,
1777 : SimpleStringList *patterns,
1778 : SimpleOidList *oids)
1779 : {
1780 : PQExpBuffer query;
1781 : PGresult *res;
1782 : SimpleStringListCell *cell;
1783 : int i;
1784 :
1785 382 : if (patterns->head == NULL)
1786 376 : return; /* nothing to do */
1787 :
1788 6 : query = createPQExpBuffer();
1789 :
1790 : /*
1791 : * The loop below runs multiple SELECTs might sometimes result in
1792 : * duplicate entries in the OID list, but we don't care.
1793 : */
1794 :
1795 10 : for (cell = patterns->head; cell; cell = cell->next)
1796 : {
1797 : int dotcnt;
1798 :
1799 6 : appendPQExpBufferStr(query,
1800 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1801 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1802 : false, NULL, "s.srvname", NULL, NULL, NULL,
1803 : &dotcnt);
1804 6 : if (dotcnt > 0)
1805 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1806 : cell->val);
1807 :
1808 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1809 6 : if (PQntuples(res) == 0)
1810 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1811 :
1812 8 : for (i = 0; i < PQntuples(res); i++)
1813 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1814 :
1815 4 : PQclear(res);
1816 4 : resetPQExpBuffer(query);
1817 : }
1818 :
1819 4 : destroyPQExpBuffer(query);
1820 : }
1821 :
1822 : /*
1823 : * Find the OIDs of all tables matching the given list of patterns,
1824 : * and append them to the given OID list. See also expand_dbname_patterns()
1825 : * in pg_dumpall.c
1826 : */
1827 : static void
1828 2310 : expand_table_name_patterns(Archive *fout,
1829 : SimpleStringList *patterns, SimpleOidList *oids,
1830 : bool strict_names, bool with_child_tables)
1831 : {
1832 : PQExpBuffer query;
1833 : PGresult *res;
1834 : SimpleStringListCell *cell;
1835 : int i;
1836 :
1837 2310 : if (patterns->head == NULL)
1838 2252 : return; /* nothing to do */
1839 :
1840 58 : query = createPQExpBuffer();
1841 :
1842 : /*
1843 : * this might sometimes result in duplicate entries in the OID list, but
1844 : * we don't care.
1845 : */
1846 :
1847 118 : for (cell = patterns->head; cell; cell = cell->next)
1848 : {
1849 : PQExpBufferData dbbuf;
1850 : int dotcnt;
1851 :
1852 : /*
1853 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1854 : * would be unnecessary given a pg_table_is_visible() variant taking a
1855 : * search_path argument.
1856 : *
1857 : * For with_child_tables, we start with the basic query's results and
1858 : * recursively search the inheritance tree to add child tables.
1859 : */
1860 70 : if (with_child_tables)
1861 : {
1862 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1863 : }
1864 :
1865 70 : appendPQExpBuffer(query,
1866 : "SELECT c.oid"
1867 : "\nFROM pg_catalog.pg_class c"
1868 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1869 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1870 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1871 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1872 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1873 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1874 : RELKIND_PARTITIONED_TABLE);
1875 70 : initPQExpBuffer(&dbbuf);
1876 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1877 : false, "n.nspname", "c.relname", NULL,
1878 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1879 : &dotcnt);
1880 70 : if (dotcnt > 2)
1881 2 : pg_fatal("improper relation name (too many dotted names): %s",
1882 : cell->val);
1883 68 : else if (dotcnt == 2)
1884 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1885 64 : termPQExpBuffer(&dbbuf);
1886 :
1887 64 : if (with_child_tables)
1888 : {
1889 12 : appendPQExpBufferStr(query, "UNION"
1890 : "\nSELECT i.inhrelid"
1891 : "\nFROM partition_tree p"
1892 : "\n JOIN pg_catalog.pg_inherits i"
1893 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1894 : "\n)"
1895 : "\nSELECT relid FROM partition_tree");
1896 : }
1897 :
1898 64 : ExecuteSqlStatement(fout, "RESET search_path");
1899 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1900 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1901 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1902 64 : if (strict_names && PQntuples(res) == 0)
1903 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1904 :
1905 148 : for (i = 0; i < PQntuples(res); i++)
1906 : {
1907 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1908 : }
1909 :
1910 60 : PQclear(res);
1911 60 : resetPQExpBuffer(query);
1912 : }
1913 :
1914 48 : destroyPQExpBuffer(query);
1915 : }
1916 :
1917 : /*
1918 : * Verifies that the connected database name matches the given database name,
1919 : * and if not, dies with an error about the given pattern.
1920 : *
1921 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1922 : */
1923 : static void
1924 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1925 : {
1926 : const char *db;
1927 :
1928 10 : db = PQdb(conn);
1929 10 : if (db == NULL)
1930 0 : pg_fatal("You are currently not connected to a database.");
1931 :
1932 10 : if (strcmp(db, dbname) != 0)
1933 10 : pg_fatal("cross-database references are not implemented: %s",
1934 : pattern);
1935 0 : }
1936 :
1937 : /*
1938 : * checkExtensionMembership
1939 : * Determine whether object is an extension member, and if so,
1940 : * record an appropriate dependency and set the object's dump flag.
1941 : *
1942 : * It's important to call this for each object that could be an extension
1943 : * member. Generally, we integrate this with determining the object's
1944 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1945 : *
1946 : * Returns true if object is an extension member, else false.
1947 : */
1948 : static bool
1949 1193508 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1950 : {
1951 1193508 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1952 :
1953 1193508 : if (ext == NULL)
1954 1191894 : return false;
1955 :
1956 1614 : dobj->ext_member = true;
1957 :
1958 : /* Record dependency so that getDependencies needn't deal with that */
1959 1614 : addObjectDependency(dobj, ext->dobj.dumpId);
1960 :
1961 : /*
1962 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1963 : * dumped. (Any initial ACLs will be removed later, using data from
1964 : * pg_init_privs, so that we'll dump only the delta from the extension's
1965 : * initial setup.)
1966 : *
1967 : * Prior to 9.6, we do not include any extension member components.
1968 : *
1969 : * In binary upgrades, we still dump all components of the members
1970 : * individually, since the idea is to exactly reproduce the database
1971 : * contents rather than replace the extension contents with something
1972 : * different.
1973 : *
1974 : * Note: it might be interesting someday to implement storage and delta
1975 : * dumping of extension members' RLS policies and/or security labels.
1976 : * However there is a pitfall for RLS policies: trying to dump them
1977 : * requires getting a lock on their tables, and the calling user might not
1978 : * have privileges for that. We need no lock to examine a table's ACLs,
1979 : * so the current feature doesn't have a problem of that sort.
1980 : */
1981 1614 : if (fout->dopt->binary_upgrade)
1982 354 : dobj->dump = ext->dobj.dump;
1983 : else
1984 : {
1985 1260 : if (fout->remoteVersion < 90600)
1986 0 : dobj->dump = DUMP_COMPONENT_NONE;
1987 : else
1988 1260 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1989 : }
1990 :
1991 1614 : return true;
1992 : }
1993 :
1994 : /*
1995 : * selectDumpableNamespace: policy-setting subroutine
1996 : * Mark a namespace as to be dumped or not
1997 : */
1998 : static void
1999 2916 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
2000 : {
2001 : /*
2002 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
2003 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
2004 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
2005 : */
2006 2916 : nsinfo->create = true;
2007 :
2008 : /*
2009 : * If specific tables are being dumped, do not dump any complete
2010 : * namespaces. If specific namespaces are being dumped, dump just those
2011 : * namespaces. Otherwise, dump all non-system namespaces.
2012 : */
2013 2916 : if (table_include_oids.head != NULL)
2014 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2015 2816 : else if (schema_include_oids.head != NULL)
2016 374 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2017 374 : simple_oid_list_member(&schema_include_oids,
2018 : nsinfo->dobj.catId.oid) ?
2019 374 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2020 2442 : else if (fout->remoteVersion >= 90600 &&
2021 2442 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2022 : {
2023 : /*
2024 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2025 : * they are interesting (and not the original ACLs which were set at
2026 : * initdb time, see pg_init_privs).
2027 : */
2028 334 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2029 : }
2030 2108 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2031 1014 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2032 : {
2033 : /* Other system schemas don't get dumped */
2034 1428 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2035 : }
2036 680 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2037 : {
2038 : /*
2039 : * The public schema is a strange beast that sits in a sort of
2040 : * no-mans-land between being a system object and a user object.
2041 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2042 : * a comment and an indication of ownership. If the owner is the
2043 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2044 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2045 : */
2046 326 : nsinfo->create = false;
2047 326 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2048 326 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2049 242 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2050 326 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2051 :
2052 : /*
2053 : * Also, make like it has a comment even if it doesn't; this is so
2054 : * that we'll emit a command to drop the comment, if appropriate.
2055 : * (Without this, we'd not call dumpCommentExtended for it.)
2056 : */
2057 326 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2058 : }
2059 : else
2060 354 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2061 :
2062 : /*
2063 : * In any case, a namespace can be excluded by an exclusion switch
2064 : */
2065 3952 : if (nsinfo->dobj.dump_contains &&
2066 1036 : simple_oid_list_member(&schema_exclude_oids,
2067 : nsinfo->dobj.catId.oid))
2068 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2069 :
2070 : /*
2071 : * If the schema belongs to an extension, allow extension membership to
2072 : * override the dump decision for the schema itself. However, this does
2073 : * not change dump_contains, so this won't change what we do with objects
2074 : * within the schema. (If they belong to the extension, they'll get
2075 : * suppressed by it, otherwise not.)
2076 : */
2077 2916 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2078 2916 : }
2079 :
2080 : /*
2081 : * selectDumpableTable: policy-setting subroutine
2082 : * Mark a table as to be dumped or not
2083 : */
2084 : static void
2085 99056 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2086 : {
2087 99056 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2088 450 : return; /* extension membership overrides all else */
2089 :
2090 : /*
2091 : * If specific tables are being dumped, dump just those tables; else, dump
2092 : * according to the parent namespace's dump flag.
2093 : */
2094 98606 : if (table_include_oids.head != NULL)
2095 10364 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2096 : tbinfo->dobj.catId.oid) ?
2097 5182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2098 : else
2099 93424 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2100 :
2101 : /*
2102 : * In any case, a table can be excluded by an exclusion switch
2103 : */
2104 161334 : if (tbinfo->dobj.dump &&
2105 62728 : simple_oid_list_member(&table_exclude_oids,
2106 : tbinfo->dobj.catId.oid))
2107 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2108 : }
2109 :
2110 : /*
2111 : * selectDumpableType: policy-setting subroutine
2112 : * Mark a type as to be dumped or not
2113 : *
2114 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2115 : * special type code to facilitate sorting into the desired order. (We don't
2116 : * want to consider those to be ordinary types because that would bring tables
2117 : * up into the datatype part of the dump order.) We still set the object's
2118 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2119 : * need it so that casts involving such types will be dumped correctly -- see
2120 : * dumpCast. This means the flag should be set the same as for the underlying
2121 : * object (the table or base type).
2122 : */
2123 : static void
2124 272904 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2125 : {
2126 : /* skip complex types, except for standalone composite types */
2127 272904 : if (OidIsValid(tyinfo->typrelid) &&
2128 97606 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2129 : {
2130 97242 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2131 :
2132 97242 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2133 97242 : if (tytable != NULL)
2134 97242 : tyinfo->dobj.dump = tytable->dobj.dump;
2135 : else
2136 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2137 97242 : return;
2138 : }
2139 :
2140 : /* skip auto-generated array and multirange types */
2141 175662 : if (tyinfo->isArray || tyinfo->isMultirange)
2142 : {
2143 133468 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2144 :
2145 : /*
2146 : * Fall through to set the dump flag; we assume that the subsequent
2147 : * rules will do the same thing as they would for the array's base
2148 : * type or multirange's range type. (We cannot reliably look up the
2149 : * base type here, since getTypes may not have processed it yet.)
2150 : */
2151 : }
2152 :
2153 175662 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2154 300 : return; /* extension membership overrides all else */
2155 :
2156 : /* Dump based on if the contents of the namespace are being dumped */
2157 175362 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2158 : }
2159 :
2160 : /*
2161 : * selectDumpableDefaultACL: policy-setting subroutine
2162 : * Mark a default ACL as to be dumped or not
2163 : *
2164 : * For per-schema default ACLs, dump if the schema is to be dumped.
2165 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2166 : * and aclsSkip are checked separately.
2167 : */
2168 : static void
2169 388 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2170 : {
2171 : /* Default ACLs can't be extension members */
2172 :
2173 388 : if (dinfo->dobj.namespace)
2174 : /* default ACLs are considered part of the namespace */
2175 180 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2176 : else
2177 208 : dinfo->dobj.dump = dopt->include_everything ?
2178 208 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2179 388 : }
2180 :
2181 : /*
2182 : * selectDumpableCast: policy-setting subroutine
2183 : * Mark a cast as to be dumped or not
2184 : *
2185 : * Casts do not belong to any particular namespace (since they haven't got
2186 : * names), nor do they have identifiable owners. To distinguish user-defined
2187 : * casts from built-in ones, we must resort to checking whether the cast's
2188 : * OID is in the range reserved for initdb.
2189 : */
2190 : static void
2191 90790 : selectDumpableCast(CastInfo *cast, Archive *fout)
2192 : {
2193 90790 : if (checkExtensionMembership(&cast->dobj, fout))
2194 0 : return; /* extension membership overrides all else */
2195 :
2196 : /*
2197 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2198 : * support ACLs currently.
2199 : */
2200 90790 : if (cast->dobj.catId.oid <= g_last_builtin_oid)
2201 90616 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2202 : else
2203 174 : cast->dobj.dump = fout->dopt->include_everything ?
2204 174 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2205 : }
2206 :
2207 : /*
2208 : * selectDumpableProcLang: policy-setting subroutine
2209 : * Mark a procedural language as to be dumped or not
2210 : *
2211 : * Procedural languages do not belong to any particular namespace. To
2212 : * identify built-in languages, we must resort to checking whether the
2213 : * language's OID is in the range reserved for initdb.
2214 : */
2215 : static void
2216 466 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2217 : {
2218 466 : if (checkExtensionMembership(&plang->dobj, fout))
2219 376 : return; /* extension membership overrides all else */
2220 :
2221 : /*
2222 : * Only include procedural languages when we are dumping everything.
2223 : *
2224 : * For from-initdb procedural languages, only include ACLs, as we do for
2225 : * the pg_catalog namespace. We need this because procedural languages do
2226 : * not live in any namespace.
2227 : */
2228 90 : if (!fout->dopt->include_everything)
2229 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2230 : else
2231 : {
2232 74 : if (plang->dobj.catId.oid <= g_last_builtin_oid)
2233 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2234 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2235 : else
2236 74 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2237 : }
2238 : }
2239 :
2240 : /*
2241 : * selectDumpableAccessMethod: policy-setting subroutine
2242 : * Mark an access method as to be dumped or not
2243 : *
2244 : * Access methods do not belong to any particular namespace. To identify
2245 : * built-in access methods, we must resort to checking whether the
2246 : * method's OID is in the range reserved for initdb.
2247 : */
2248 : static void
2249 2876 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2250 : {
2251 : /* see getAccessMethods() comment about v9.6. */
2252 2876 : if (fout->remoteVersion < 90600)
2253 : {
2254 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2255 0 : return;
2256 : }
2257 :
2258 2876 : if (checkExtensionMembership(&method->dobj, fout))
2259 50 : return; /* extension membership overrides all else */
2260 :
2261 : /*
2262 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2263 : * they do not support ACLs currently.
2264 : */
2265 2826 : if (method->dobj.catId.oid <= g_last_builtin_oid)
2266 2632 : method->dobj.dump = DUMP_COMPONENT_NONE;
2267 : else
2268 194 : method->dobj.dump = fout->dopt->include_everything ?
2269 194 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2270 : }
2271 :
2272 : /*
2273 : * selectDumpableExtension: policy-setting subroutine
2274 : * Mark an extension as to be dumped or not
2275 : *
2276 : * Built-in extensions should be skipped except for checking ACLs, since we
2277 : * assume those will already be installed in the target database. We identify
2278 : * such extensions by their having OIDs in the range reserved for initdb.
2279 : * We dump all user-added extensions by default. No extensions are dumped
2280 : * if include_everything is false (i.e., a --schema or --table switch was
2281 : * given), except if --extension specifies a list of extensions to dump.
2282 : */
2283 : static void
2284 438 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2285 : {
2286 : /*
2287 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2288 : * change permissions on their member objects, if they wish to, and have
2289 : * those changes preserved.
2290 : */
2291 438 : if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2292 378 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2293 : else
2294 : {
2295 : /* check if there is a list of extensions to dump */
2296 60 : if (extension_include_oids.head != NULL)
2297 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2298 8 : simple_oid_list_member(&extension_include_oids,
2299 : extinfo->dobj.catId.oid) ?
2300 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2301 : else
2302 52 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2303 52 : dopt->include_everything ?
2304 52 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2305 :
2306 : /* check that the extension is not explicitly excluded */
2307 112 : if (extinfo->dobj.dump &&
2308 52 : simple_oid_list_member(&extension_exclude_oids,
2309 : extinfo->dobj.catId.oid))
2310 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2311 : }
2312 438 : }
2313 :
2314 : /*
2315 : * selectDumpablePublicationObject: policy-setting subroutine
2316 : * Mark a publication object as to be dumped or not
2317 : *
2318 : * A publication can have schemas and tables which have schemas, but those are
2319 : * ignored in decision making, because publications are only dumped when we are
2320 : * dumping everything.
2321 : */
2322 : static void
2323 950 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2324 : {
2325 950 : if (checkExtensionMembership(dobj, fout))
2326 0 : return; /* extension membership overrides all else */
2327 :
2328 950 : dobj->dump = fout->dopt->include_everything ?
2329 950 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2330 : }
2331 :
2332 : /*
2333 : * selectDumpableStatisticsObject: policy-setting subroutine
2334 : * Mark an extended statistics object as to be dumped or not
2335 : *
2336 : * We dump an extended statistics object if the schema it's in and the table
2337 : * it's for are being dumped. (This'll need more thought if statistics
2338 : * objects ever support cross-table stats.)
2339 : */
2340 : static void
2341 326 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2342 : {
2343 326 : if (checkExtensionMembership(&sobj->dobj, fout))
2344 0 : return; /* extension membership overrides all else */
2345 :
2346 326 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2347 326 : if (sobj->stattable == NULL ||
2348 326 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2349 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2350 : }
2351 :
2352 : /*
2353 : * selectDumpableObject: policy-setting subroutine
2354 : * Mark a generic dumpable object as to be dumped or not
2355 : *
2356 : * Use this only for object types without a special-case routine above.
2357 : */
2358 : static void
2359 820466 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2360 : {
2361 820466 : if (checkExtensionMembership(dobj, fout))
2362 388 : return; /* extension membership overrides all else */
2363 :
2364 : /*
2365 : * Default policy is to dump if parent namespace is dumpable, or for
2366 : * non-namespace-associated items, dump if we're dumping "everything".
2367 : */
2368 820078 : if (dobj->namespace)
2369 818620 : dobj->dump = dobj->namespace->dobj.dump_contains;
2370 : else
2371 1458 : dobj->dump = fout->dopt->include_everything ?
2372 1458 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2373 : }
2374 :
2375 : /*
2376 : * Dump a table's contents for loading using the COPY command
2377 : * - this routine is called by the Archiver when it wants the table
2378 : * to be dumped.
2379 : */
2380 : static int
2381 8130 : dumpTableData_copy(Archive *fout, const void *dcontext)
2382 : {
2383 8130 : const TableDataInfo *tdinfo = dcontext;
2384 8130 : const TableInfo *tbinfo = tdinfo->tdtable;
2385 8130 : const char *classname = tbinfo->dobj.name;
2386 8130 : PQExpBuffer q = createPQExpBuffer();
2387 :
2388 : /*
2389 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2390 : * which uses it already.
2391 : */
2392 8130 : PQExpBuffer clistBuf = createPQExpBuffer();
2393 8130 : PGconn *conn = GetConnection(fout);
2394 : PGresult *res;
2395 : int ret;
2396 : char *copybuf;
2397 : const char *column_list;
2398 :
2399 8130 : pg_log_info("dumping contents of table \"%s.%s\"",
2400 : tbinfo->dobj.namespace->dobj.name, classname);
2401 :
2402 : /*
2403 : * Specify the column list explicitly so that we have no possibility of
2404 : * retrieving data in the wrong column order. (The default column
2405 : * ordering of COPY will not be what we want in certain corner cases
2406 : * involving ADD COLUMN and inheritance.)
2407 : */
2408 8130 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2409 :
2410 : /*
2411 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2412 : * a filter condition was specified. For other cases a simple COPY
2413 : * suffices.
2414 : */
2415 8130 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2416 : {
2417 : /* Temporary allows to access to foreign tables to dump data */
2418 154 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2419 2 : set_restrict_relation_kind(fout, "view");
2420 :
2421 154 : appendPQExpBufferStr(q, "COPY (SELECT ");
2422 : /* klugery to get rid of parens in column list */
2423 154 : if (strlen(column_list) > 2)
2424 : {
2425 154 : appendPQExpBufferStr(q, column_list + 1);
2426 154 : q->data[q->len - 1] = ' ';
2427 : }
2428 : else
2429 0 : appendPQExpBufferStr(q, "* ");
2430 :
2431 308 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2432 154 : fmtQualifiedDumpable(tbinfo),
2433 154 : tdinfo->filtercond ? tdinfo->filtercond : "");
2434 : }
2435 : else
2436 : {
2437 7976 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2438 7976 : fmtQualifiedDumpable(tbinfo),
2439 : column_list);
2440 : }
2441 8130 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2442 8128 : PQclear(res);
2443 8128 : destroyPQExpBuffer(clistBuf);
2444 :
2445 : for (;;)
2446 : {
2447 3621298 : ret = PQgetCopyData(conn, ©buf, 0);
2448 :
2449 3621298 : if (ret < 0)
2450 8128 : break; /* done or error */
2451 :
2452 3613170 : if (copybuf)
2453 : {
2454 3613170 : WriteData(fout, copybuf, ret);
2455 3613170 : PQfreemem(copybuf);
2456 : }
2457 :
2458 : /* ----------
2459 : * THROTTLE:
2460 : *
2461 : * There was considerable discussion in late July, 2000 regarding
2462 : * slowing down pg_dump when backing up large tables. Users with both
2463 : * slow & fast (multi-processor) machines experienced performance
2464 : * degradation when doing a backup.
2465 : *
2466 : * Initial attempts based on sleeping for a number of ms for each ms
2467 : * of work were deemed too complex, then a simple 'sleep in each loop'
2468 : * implementation was suggested. The latter failed because the loop
2469 : * was too tight. Finally, the following was implemented:
2470 : *
2471 : * If throttle is non-zero, then
2472 : * See how long since the last sleep.
2473 : * Work out how long to sleep (based on ratio).
2474 : * If sleep is more than 100ms, then
2475 : * sleep
2476 : * reset timer
2477 : * EndIf
2478 : * EndIf
2479 : *
2480 : * where the throttle value was the number of ms to sleep per ms of
2481 : * work. The calculation was done in each loop.
2482 : *
2483 : * Most of the hard work is done in the backend, and this solution
2484 : * still did not work particularly well: on slow machines, the ratio
2485 : * was 50:1, and on medium paced machines, 1:1, and on fast
2486 : * multi-processor machines, it had little or no effect, for reasons
2487 : * that were unclear.
2488 : *
2489 : * Further discussion ensued, and the proposal was dropped.
2490 : *
2491 : * For those people who want this feature, it can be implemented using
2492 : * gettimeofday in each loop, calculating the time since last sleep,
2493 : * multiplying that by the sleep ratio, then if the result is more
2494 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2495 : * function to sleep for a subsecond period ie.
2496 : *
2497 : * select(0, NULL, NULL, NULL, &tvi);
2498 : *
2499 : * This will return after the interval specified in the structure tvi.
2500 : * Finally, call gettimeofday again to save the 'last sleep time'.
2501 : * ----------
2502 : */
2503 : }
2504 8128 : archprintf(fout, "\\.\n\n\n");
2505 :
2506 8128 : if (ret == -2)
2507 : {
2508 : /* copy data transfer failed */
2509 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2510 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2511 0 : pg_log_error_detail("Command was: %s", q->data);
2512 0 : exit_nicely(1);
2513 : }
2514 :
2515 : /* Check command status and return to normal libpq state */
2516 8128 : res = PQgetResult(conn);
2517 8128 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2518 : {
2519 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2520 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2521 0 : pg_log_error_detail("Command was: %s", q->data);
2522 0 : exit_nicely(1);
2523 : }
2524 8128 : PQclear(res);
2525 :
2526 : /* Do this to ensure we've pumped libpq back to idle state */
2527 8128 : if (PQgetResult(conn) != NULL)
2528 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2529 : classname);
2530 :
2531 8128 : destroyPQExpBuffer(q);
2532 :
2533 : /* Revert back the setting */
2534 8128 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2535 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2536 :
2537 8128 : return 1;
2538 : }
2539 :
2540 : /*
2541 : * Dump table data using INSERT commands.
2542 : *
2543 : * Caution: when we restore from an archive file direct to database, the
2544 : * INSERT commands emitted by this function have to be parsed by
2545 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2546 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2547 : */
2548 : static int
2549 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2550 : {
2551 162 : const TableDataInfo *tdinfo = dcontext;
2552 162 : const TableInfo *tbinfo = tdinfo->tdtable;
2553 162 : DumpOptions *dopt = fout->dopt;
2554 162 : PQExpBuffer q = createPQExpBuffer();
2555 162 : PQExpBuffer insertStmt = NULL;
2556 : char *attgenerated;
2557 : PGresult *res;
2558 : int nfields,
2559 : i;
2560 162 : int rows_per_statement = dopt->dump_inserts;
2561 162 : int rows_this_statement = 0;
2562 :
2563 : /* Temporary allows to access to foreign tables to dump data */
2564 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2565 0 : set_restrict_relation_kind(fout, "view");
2566 :
2567 : /*
2568 : * If we're going to emit INSERTs with column names, the most efficient
2569 : * way to deal with generated columns is to exclude them entirely. For
2570 : * INSERTs without column names, we have to emit DEFAULT rather than the
2571 : * actual column value --- but we can save a few cycles by fetching nulls
2572 : * rather than the uninteresting-to-us value.
2573 : */
2574 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2575 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2576 162 : nfields = 0;
2577 502 : for (i = 0; i < tbinfo->numatts; i++)
2578 : {
2579 340 : if (tbinfo->attisdropped[i])
2580 4 : continue;
2581 336 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2582 16 : continue;
2583 320 : if (nfields > 0)
2584 172 : appendPQExpBufferStr(q, ", ");
2585 320 : if (tbinfo->attgenerated[i])
2586 16 : appendPQExpBufferStr(q, "NULL");
2587 : else
2588 304 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2589 320 : attgenerated[nfields] = tbinfo->attgenerated[i];
2590 320 : nfields++;
2591 : }
2592 : /* Servers before 9.4 will complain about zero-column SELECT */
2593 162 : if (nfields == 0)
2594 14 : appendPQExpBufferStr(q, "NULL");
2595 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2596 162 : fmtQualifiedDumpable(tbinfo));
2597 162 : if (tdinfo->filtercond)
2598 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2599 :
2600 162 : ExecuteSqlStatement(fout, q->data);
2601 :
2602 : while (1)
2603 : {
2604 262 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2605 : PGRES_TUPLES_OK);
2606 :
2607 : /* cross-check field count, allowing for dummy NULL if any */
2608 262 : if (nfields != PQnfields(res) &&
2609 20 : !(nfields == 0 && PQnfields(res) == 1))
2610 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2611 : tbinfo->dobj.name);
2612 :
2613 : /*
2614 : * First time through, we build as much of the INSERT statement as
2615 : * possible in "insertStmt", which we can then just print for each
2616 : * statement. If the table happens to have zero dumpable columns then
2617 : * this will be a complete statement, otherwise it will end in
2618 : * "VALUES" and be ready to have the row's column values printed.
2619 : */
2620 262 : if (insertStmt == NULL)
2621 : {
2622 : const TableInfo *targettab;
2623 :
2624 162 : insertStmt = createPQExpBuffer();
2625 :
2626 : /*
2627 : * When load-via-partition-root is set or forced, get the root
2628 : * table name for the partition table, so that we can reload data
2629 : * through the root table.
2630 : */
2631 162 : if (tbinfo->ispartition &&
2632 96 : (dopt->load_via_partition_root ||
2633 48 : forcePartitionRootLoad(tbinfo)))
2634 14 : targettab = getRootTableInfo(tbinfo);
2635 : else
2636 148 : targettab = tbinfo;
2637 :
2638 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2639 162 : fmtQualifiedDumpable(targettab));
2640 :
2641 : /* corner case for zero-column table */
2642 162 : if (nfields == 0)
2643 : {
2644 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2645 : }
2646 : else
2647 : {
2648 : /* append the list of column names if required */
2649 148 : if (dopt->column_inserts)
2650 : {
2651 66 : appendPQExpBufferChar(insertStmt, '(');
2652 200 : for (int field = 0; field < nfields; field++)
2653 : {
2654 134 : if (field > 0)
2655 68 : appendPQExpBufferStr(insertStmt, ", ");
2656 134 : appendPQExpBufferStr(insertStmt,
2657 134 : fmtId(PQfname(res, field)));
2658 : }
2659 66 : appendPQExpBufferStr(insertStmt, ") ");
2660 : }
2661 :
2662 148 : if (tbinfo->needs_override)
2663 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2664 :
2665 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2666 : }
2667 : }
2668 :
2669 6800 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2670 : {
2671 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2672 6538 : if (rows_this_statement == 0)
2673 6526 : archputs(insertStmt->data, fout);
2674 :
2675 : /*
2676 : * If it is zero-column table then we've already written the
2677 : * complete statement, which will mean we've disobeyed
2678 : * --rows-per-insert when it's set greater than 1. We do support
2679 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2680 : * UNION ALL ... but that's non-standard so we should avoid it
2681 : * given that using INSERTs is mostly only ever needed for
2682 : * cross-database exports.
2683 : */
2684 6538 : if (nfields == 0)
2685 12 : continue;
2686 :
2687 : /* Emit a row heading */
2688 6526 : if (rows_per_statement == 1)
2689 6508 : archputs(" (", fout);
2690 18 : else if (rows_this_statement > 0)
2691 12 : archputs(",\n\t(", fout);
2692 : else
2693 6 : archputs("\n\t(", fout);
2694 :
2695 19690 : for (int field = 0; field < nfields; field++)
2696 : {
2697 13164 : if (field > 0)
2698 6638 : archputs(", ", fout);
2699 13164 : if (attgenerated[field])
2700 : {
2701 4 : archputs("DEFAULT", fout);
2702 4 : continue;
2703 : }
2704 13160 : if (PQgetisnull(res, tuple, field))
2705 : {
2706 166 : archputs("NULL", fout);
2707 166 : continue;
2708 : }
2709 :
2710 : /* XXX This code is partially duplicated in ruleutils.c */
2711 12994 : switch (PQftype(res, field))
2712 : {
2713 8938 : case INT2OID:
2714 : case INT4OID:
2715 : case INT8OID:
2716 : case OIDOID:
2717 : case FLOAT4OID:
2718 : case FLOAT8OID:
2719 : case NUMERICOID:
2720 : {
2721 : /*
2722 : * These types are printed without quotes unless
2723 : * they contain values that aren't accepted by the
2724 : * scanner unquoted (e.g., 'NaN'). Note that
2725 : * strtod() and friends might accept NaN, so we
2726 : * can't use that to test.
2727 : *
2728 : * In reality we only need to defend against
2729 : * infinity and NaN, so we need not get too crazy
2730 : * about pattern matching here.
2731 : */
2732 8938 : const char *s = PQgetvalue(res, tuple, field);
2733 :
2734 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2735 8934 : archputs(s, fout);
2736 : else
2737 4 : archprintf(fout, "'%s'", s);
2738 : }
2739 8938 : break;
2740 :
2741 4 : case BITOID:
2742 : case VARBITOID:
2743 4 : archprintf(fout, "B'%s'",
2744 : PQgetvalue(res, tuple, field));
2745 4 : break;
2746 :
2747 8 : case BOOLOID:
2748 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2749 4 : archputs("true", fout);
2750 : else
2751 4 : archputs("false", fout);
2752 8 : break;
2753 :
2754 4044 : default:
2755 : /* All other types are printed as string literals. */
2756 4044 : resetPQExpBuffer(q);
2757 4044 : appendStringLiteralAH(q,
2758 : PQgetvalue(res, tuple, field),
2759 : fout);
2760 4044 : archputs(q->data, fout);
2761 4044 : break;
2762 : }
2763 : }
2764 :
2765 : /* Terminate the row ... */
2766 6526 : archputs(")", fout);
2767 :
2768 : /* ... and the statement, if the target no. of rows is reached */
2769 6526 : if (++rows_this_statement >= rows_per_statement)
2770 : {
2771 6512 : if (dopt->do_nothing)
2772 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2773 : else
2774 6512 : archputs(";\n", fout);
2775 : /* Reset the row counter */
2776 6512 : rows_this_statement = 0;
2777 : }
2778 : }
2779 :
2780 262 : if (PQntuples(res) <= 0)
2781 : {
2782 162 : PQclear(res);
2783 162 : break;
2784 : }
2785 100 : PQclear(res);
2786 : }
2787 :
2788 : /* Terminate any statements that didn't make the row count. */
2789 162 : if (rows_this_statement > 0)
2790 : {
2791 2 : if (dopt->do_nothing)
2792 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2793 : else
2794 2 : archputs(";\n", fout);
2795 : }
2796 :
2797 162 : archputs("\n\n", fout);
2798 :
2799 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2800 :
2801 162 : destroyPQExpBuffer(q);
2802 162 : if (insertStmt != NULL)
2803 162 : destroyPQExpBuffer(insertStmt);
2804 162 : free(attgenerated);
2805 :
2806 : /* Revert back the setting */
2807 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2808 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2809 :
2810 162 : return 1;
2811 : }
2812 :
2813 : /*
2814 : * getRootTableInfo:
2815 : * get the root TableInfo for the given partition table.
2816 : */
2817 : static TableInfo *
2818 158 : getRootTableInfo(const TableInfo *tbinfo)
2819 : {
2820 : TableInfo *parentTbinfo;
2821 :
2822 : Assert(tbinfo->ispartition);
2823 : Assert(tbinfo->numParents == 1);
2824 :
2825 158 : parentTbinfo = tbinfo->parents[0];
2826 158 : while (parentTbinfo->ispartition)
2827 : {
2828 : Assert(parentTbinfo->numParents == 1);
2829 0 : parentTbinfo = parentTbinfo->parents[0];
2830 : }
2831 :
2832 158 : return parentTbinfo;
2833 : }
2834 :
2835 : /*
2836 : * forcePartitionRootLoad
2837 : * Check if we must force load_via_partition_root for this partition.
2838 : *
2839 : * This is required if any level of ancestral partitioned table has an
2840 : * unsafe partitioning scheme.
2841 : */
2842 : static bool
2843 2104 : forcePartitionRootLoad(const TableInfo *tbinfo)
2844 : {
2845 : TableInfo *parentTbinfo;
2846 :
2847 : Assert(tbinfo->ispartition);
2848 : Assert(tbinfo->numParents == 1);
2849 :
2850 2104 : parentTbinfo = tbinfo->parents[0];
2851 2104 : if (parentTbinfo->unsafe_partitions)
2852 158 : return true;
2853 2386 : while (parentTbinfo->ispartition)
2854 : {
2855 : Assert(parentTbinfo->numParents == 1);
2856 440 : parentTbinfo = parentTbinfo->parents[0];
2857 440 : if (parentTbinfo->unsafe_partitions)
2858 0 : return true;
2859 : }
2860 :
2861 1946 : return false;
2862 : }
2863 :
2864 : /*
2865 : * dumpTableData -
2866 : * dump the contents of a single table
2867 : *
2868 : * Actually, this just makes an ArchiveEntry for the table contents.
2869 : */
2870 : static void
2871 8452 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2872 : {
2873 8452 : DumpOptions *dopt = fout->dopt;
2874 8452 : const TableInfo *tbinfo = tdinfo->tdtable;
2875 8452 : PQExpBuffer copyBuf = createPQExpBuffer();
2876 8452 : PQExpBuffer clistBuf = createPQExpBuffer();
2877 : DataDumperPtr dumpFn;
2878 8452 : char *tdDefn = NULL;
2879 : char *copyStmt;
2880 : const char *copyFrom;
2881 :
2882 : /* We had better have loaded per-column details about this table */
2883 : Assert(tbinfo->interesting);
2884 :
2885 : /*
2886 : * When load-via-partition-root is set or forced, get the root table name
2887 : * for the partition table, so that we can reload data through the root
2888 : * table. Then construct a comment to be inserted into the TOC entry's
2889 : * defn field, so that such cases can be identified reliably.
2890 : */
2891 8452 : if (tbinfo->ispartition &&
2892 4112 : (dopt->load_via_partition_root ||
2893 2056 : forcePartitionRootLoad(tbinfo)))
2894 144 : {
2895 : const TableInfo *parentTbinfo;
2896 : char *sanitized;
2897 :
2898 144 : parentTbinfo = getRootTableInfo(tbinfo);
2899 144 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2900 144 : sanitized = sanitize_line(copyFrom, true);
2901 144 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2902 : sanitized);
2903 144 : free(sanitized);
2904 144 : tdDefn = pg_strdup(copyBuf->data);
2905 : }
2906 : else
2907 8308 : copyFrom = fmtQualifiedDumpable(tbinfo);
2908 :
2909 8452 : if (dopt->dump_inserts == 0)
2910 : {
2911 : /* Dump/restore using COPY */
2912 8290 : dumpFn = dumpTableData_copy;
2913 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2914 8290 : printfPQExpBuffer(copyBuf, "COPY %s ",
2915 : copyFrom);
2916 8290 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2917 : fmtCopyColumnList(tbinfo, clistBuf));
2918 8290 : copyStmt = copyBuf->data;
2919 : }
2920 : else
2921 : {
2922 : /* Restore using INSERT */
2923 162 : dumpFn = dumpTableData_insert;
2924 162 : copyStmt = NULL;
2925 : }
2926 :
2927 : /*
2928 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2929 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2930 : * See comments for BuildArchiveDependencies.
2931 : */
2932 8452 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2933 : {
2934 : TocEntry *te;
2935 :
2936 8452 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2937 8452 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2938 : .namespace = tbinfo->dobj.namespace->dobj.name,
2939 : .owner = tbinfo->rolname,
2940 : .description = "TABLE DATA",
2941 : .section = SECTION_DATA,
2942 : .createStmt = tdDefn,
2943 : .copyStmt = copyStmt,
2944 : .deps = &(tbinfo->dobj.dumpId),
2945 : .nDeps = 1,
2946 : .dumpFn = dumpFn,
2947 : .dumpArg = tdinfo));
2948 :
2949 : /*
2950 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2951 : * and want to order dump jobs by table size. We choose to measure
2952 : * dataLength in table pages (including TOAST pages) during dump, so
2953 : * no scaling is needed.
2954 : *
2955 : * However, relpages is declared as "integer" in pg_class, and hence
2956 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2957 : * Cast so that we get the right interpretation of table sizes
2958 : * exceeding INT_MAX pages.
2959 : */
2960 8452 : te->dataLength = (BlockNumber) tbinfo->relpages;
2961 8452 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2962 :
2963 : /*
2964 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2965 : * and instead we'd better worry about integer overflow. Clamp to
2966 : * INT_MAX if the correct result exceeds that.
2967 : */
2968 : if (sizeof(te->dataLength) == 4 &&
2969 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2970 : te->dataLength < 0))
2971 : te->dataLength = INT_MAX;
2972 : }
2973 :
2974 8452 : destroyPQExpBuffer(copyBuf);
2975 8452 : destroyPQExpBuffer(clistBuf);
2976 8452 : }
2977 :
2978 : /*
2979 : * refreshMatViewData -
2980 : * load or refresh the contents of a single materialized view
2981 : *
2982 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2983 : * statement.
2984 : */
2985 : static void
2986 690 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2987 : {
2988 690 : TableInfo *tbinfo = tdinfo->tdtable;
2989 : PQExpBuffer q;
2990 :
2991 : /* If the materialized view is not flagged as populated, skip this. */
2992 690 : if (!tbinfo->relispopulated)
2993 136 : return;
2994 :
2995 554 : q = createPQExpBuffer();
2996 :
2997 554 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2998 554 : fmtQualifiedDumpable(tbinfo));
2999 :
3000 554 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
3001 554 : ArchiveEntry(fout,
3002 : tdinfo->dobj.catId, /* catalog ID */
3003 554 : tdinfo->dobj.dumpId, /* dump ID */
3004 554 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
3005 : .namespace = tbinfo->dobj.namespace->dobj.name,
3006 : .owner = tbinfo->rolname,
3007 : .description = "MATERIALIZED VIEW DATA",
3008 : .section = SECTION_POST_DATA,
3009 : .createStmt = q->data,
3010 : .deps = tdinfo->dobj.dependencies,
3011 : .nDeps = tdinfo->dobj.nDeps));
3012 :
3013 554 : destroyPQExpBuffer(q);
3014 : }
3015 :
3016 : /*
3017 : * getTableData -
3018 : * set up dumpable objects representing the contents of tables
3019 : */
3020 : static void
3021 360 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3022 : {
3023 : int i;
3024 :
3025 95438 : for (i = 0; i < numTables; i++)
3026 : {
3027 95078 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3028 1864 : (!relkind || tblinfo[i].relkind == relkind))
3029 11784 : makeTableDataInfo(dopt, &(tblinfo[i]));
3030 : }
3031 360 : }
3032 :
3033 : /*
3034 : * Make a dumpable object for the data of this specific table
3035 : *
3036 : * Note: we make a TableDataInfo if and only if we are going to dump the
3037 : * table data; the "dump" field in such objects isn't very interesting.
3038 : */
3039 : static void
3040 12014 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3041 : {
3042 : TableDataInfo *tdinfo;
3043 :
3044 : /*
3045 : * Nothing to do if we already decided to dump the table. This will
3046 : * happen for "config" tables.
3047 : */
3048 12014 : if (tbinfo->dataObj != NULL)
3049 2 : return;
3050 :
3051 : /* Skip VIEWs (no data to dump) */
3052 12012 : if (tbinfo->relkind == RELKIND_VIEW)
3053 968 : return;
3054 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3055 11044 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3056 76 : (foreign_servers_include_oids.head == NULL ||
3057 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3058 : tbinfo->foreign_server)))
3059 74 : return;
3060 : /* Skip partitioned tables (data in partitions) */
3061 10970 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3062 990 : return;
3063 :
3064 : /* Don't dump data in unlogged tables, if so requested */
3065 9980 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3066 82 : dopt->no_unlogged_table_data)
3067 36 : return;
3068 :
3069 : /* Check that the data is not explicitly excluded */
3070 9944 : if (simple_oid_list_member(&tabledata_exclude_oids,
3071 : tbinfo->dobj.catId.oid))
3072 16 : return;
3073 :
3074 : /* OK, let's dump it */
3075 9928 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3076 :
3077 9928 : if (tbinfo->relkind == RELKIND_MATVIEW)
3078 690 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3079 9238 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3080 786 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3081 : else
3082 8452 : tdinfo->dobj.objType = DO_TABLE_DATA;
3083 :
3084 : /*
3085 : * Note: use tableoid 0 so that this object won't be mistaken for
3086 : * something that pg_depend entries apply to.
3087 : */
3088 9928 : tdinfo->dobj.catId.tableoid = 0;
3089 9928 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3090 9928 : AssignDumpId(&tdinfo->dobj);
3091 9928 : tdinfo->dobj.name = tbinfo->dobj.name;
3092 9928 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3093 9928 : tdinfo->tdtable = tbinfo;
3094 9928 : tdinfo->filtercond = NULL; /* might get set later */
3095 9928 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3096 :
3097 : /* A TableDataInfo contains data, of course */
3098 9928 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3099 :
3100 9928 : tbinfo->dataObj = tdinfo;
3101 :
3102 : /*
3103 : * Materialized view statistics must be restored after the data, because
3104 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3105 : *
3106 : * The dependency is added here because the statistics objects are created
3107 : * first.
3108 : */
3109 9928 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3110 : {
3111 536 : tbinfo->stats->section = SECTION_POST_DATA;
3112 536 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3113 : }
3114 :
3115 : /* Make sure that we'll collect per-column info for this table. */
3116 9928 : tbinfo->interesting = true;
3117 : }
3118 :
3119 : /*
3120 : * The refresh for a materialized view must be dependent on the refresh for
3121 : * any materialized view that this one is dependent on.
3122 : *
3123 : * This must be called after all the objects are created, but before they are
3124 : * sorted.
3125 : */
3126 : static void
3127 292 : buildMatViewRefreshDependencies(Archive *fout)
3128 : {
3129 : PQExpBuffer query;
3130 : PGresult *res;
3131 : int ntups,
3132 : i;
3133 : int i_classid,
3134 : i_objid,
3135 : i_refobjid;
3136 :
3137 : /* No Mat Views before 9.3. */
3138 292 : if (fout->remoteVersion < 90300)
3139 0 : return;
3140 :
3141 292 : query = createPQExpBuffer();
3142 :
3143 292 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3144 : "( "
3145 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3146 : "FROM pg_depend d1 "
3147 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3148 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3149 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3150 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3151 : "AND d2.objid = r1.oid "
3152 : "AND d2.refobjid <> d1.objid "
3153 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3154 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3155 : CppAsString2(RELKIND_VIEW) ") "
3156 : "WHERE d1.classid = 'pg_class'::regclass "
3157 : "UNION "
3158 : "SELECT w.objid, d3.refobjid, c3.relkind "
3159 : "FROM w "
3160 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3161 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3162 : "AND d3.objid = r3.oid "
3163 : "AND d3.refobjid <> w.refobjid "
3164 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3165 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3166 : CppAsString2(RELKIND_VIEW) ") "
3167 : ") "
3168 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3169 : "FROM w "
3170 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3171 :
3172 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3173 :
3174 292 : ntups = PQntuples(res);
3175 :
3176 292 : i_classid = PQfnumber(res, "classid");
3177 292 : i_objid = PQfnumber(res, "objid");
3178 292 : i_refobjid = PQfnumber(res, "refobjid");
3179 :
3180 820 : for (i = 0; i < ntups; i++)
3181 : {
3182 : CatalogId objId;
3183 : CatalogId refobjId;
3184 : DumpableObject *dobj;
3185 : DumpableObject *refdobj;
3186 : TableInfo *tbinfo;
3187 : TableInfo *reftbinfo;
3188 :
3189 528 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3190 528 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3191 528 : refobjId.tableoid = objId.tableoid;
3192 528 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3193 :
3194 528 : dobj = findObjectByCatalogId(objId);
3195 528 : if (dobj == NULL)
3196 96 : continue;
3197 :
3198 : Assert(dobj->objType == DO_TABLE);
3199 528 : tbinfo = (TableInfo *) dobj;
3200 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3201 528 : dobj = (DumpableObject *) tbinfo->dataObj;
3202 528 : if (dobj == NULL)
3203 96 : continue;
3204 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3205 :
3206 432 : refdobj = findObjectByCatalogId(refobjId);
3207 432 : if (refdobj == NULL)
3208 0 : continue;
3209 :
3210 : Assert(refdobj->objType == DO_TABLE);
3211 432 : reftbinfo = (TableInfo *) refdobj;
3212 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3213 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3214 432 : if (refdobj == NULL)
3215 0 : continue;
3216 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3217 :
3218 432 : addObjectDependency(dobj, refdobj->dumpId);
3219 :
3220 432 : if (!reftbinfo->relispopulated)
3221 68 : tbinfo->relispopulated = false;
3222 : }
3223 :
3224 292 : PQclear(res);
3225 :
3226 292 : destroyPQExpBuffer(query);
3227 : }
3228 :
3229 : /*
3230 : * getTableDataFKConstraints -
3231 : * add dump-order dependencies reflecting foreign key constraints
3232 : *
3233 : * This code is executed only in a data-only dump --- in schema+data dumps
3234 : * we handle foreign key issues by not creating the FK constraints until
3235 : * after the data is loaded. In a data-only dump, however, we want to
3236 : * order the table data objects in such a way that a table's referenced
3237 : * tables are restored first. (In the presence of circular references or
3238 : * self-references this may be impossible; we'll detect and complain about
3239 : * that during the dependency sorting step.)
3240 : */
3241 : static void
3242 14 : getTableDataFKConstraints(void)
3243 : {
3244 : DumpableObject **dobjs;
3245 : int numObjs;
3246 : int i;
3247 :
3248 : /* Search through all the dumpable objects for FK constraints */
3249 14 : getDumpableObjects(&dobjs, &numObjs);
3250 51830 : for (i = 0; i < numObjs; i++)
3251 : {
3252 51816 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3253 : {
3254 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3255 : TableInfo *ftable;
3256 :
3257 : /* Not interesting unless both tables are to be dumped */
3258 16 : if (cinfo->contable == NULL ||
3259 16 : cinfo->contable->dataObj == NULL)
3260 8 : continue;
3261 8 : ftable = findTableByOid(cinfo->confrelid);
3262 8 : if (ftable == NULL ||
3263 8 : ftable->dataObj == NULL)
3264 0 : continue;
3265 :
3266 : /*
3267 : * Okay, make referencing table's TABLE_DATA object depend on the
3268 : * referenced table's TABLE_DATA object.
3269 : */
3270 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3271 8 : ftable->dataObj->dobj.dumpId);
3272 : }
3273 : }
3274 14 : free(dobjs);
3275 14 : }
3276 :
3277 :
3278 : /*
3279 : * dumpDatabase:
3280 : * dump the database definition
3281 : */
3282 : static void
3283 174 : dumpDatabase(Archive *fout)
3284 : {
3285 174 : DumpOptions *dopt = fout->dopt;
3286 174 : PQExpBuffer dbQry = createPQExpBuffer();
3287 174 : PQExpBuffer delQry = createPQExpBuffer();
3288 174 : PQExpBuffer creaQry = createPQExpBuffer();
3289 174 : PQExpBuffer labelq = createPQExpBuffer();
3290 174 : PGconn *conn = GetConnection(fout);
3291 : PGresult *res;
3292 : int i_tableoid,
3293 : i_oid,
3294 : i_datname,
3295 : i_datdba,
3296 : i_encoding,
3297 : i_datlocprovider,
3298 : i_collate,
3299 : i_ctype,
3300 : i_datlocale,
3301 : i_daticurules,
3302 : i_frozenxid,
3303 : i_minmxid,
3304 : i_datacl,
3305 : i_acldefault,
3306 : i_datistemplate,
3307 : i_datconnlimit,
3308 : i_datcollversion,
3309 : i_tablespace;
3310 : CatalogId dbCatId;
3311 : DumpId dbDumpId;
3312 : DumpableAcl dbdacl;
3313 : const char *datname,
3314 : *dba,
3315 : *encoding,
3316 : *datlocprovider,
3317 : *collate,
3318 : *ctype,
3319 : *locale,
3320 : *icurules,
3321 : *datistemplate,
3322 : *datconnlimit,
3323 : *tablespace;
3324 : uint32 frozenxid,
3325 : minmxid;
3326 : char *qdatname;
3327 :
3328 174 : pg_log_info("saving database definition");
3329 :
3330 : /*
3331 : * Fetch the database-level properties for this database.
3332 : */
3333 174 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3334 : "datdba, "
3335 : "pg_encoding_to_char(encoding) AS encoding, "
3336 : "datcollate, datctype, datfrozenxid, "
3337 : "datacl, acldefault('d', datdba) AS acldefault, "
3338 : "datistemplate, datconnlimit, ");
3339 174 : if (fout->remoteVersion >= 90300)
3340 174 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3341 : else
3342 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3343 174 : if (fout->remoteVersion >= 170000)
3344 174 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3345 0 : else if (fout->remoteVersion >= 150000)
3346 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3347 : else
3348 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3349 174 : if (fout->remoteVersion >= 160000)
3350 174 : appendPQExpBufferStr(dbQry, "daticurules, ");
3351 : else
3352 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3353 174 : appendPQExpBufferStr(dbQry,
3354 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3355 : "shobj_description(oid, 'pg_database') AS description "
3356 : "FROM pg_database "
3357 : "WHERE datname = current_database()");
3358 :
3359 174 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3360 :
3361 174 : i_tableoid = PQfnumber(res, "tableoid");
3362 174 : i_oid = PQfnumber(res, "oid");
3363 174 : i_datname = PQfnumber(res, "datname");
3364 174 : i_datdba = PQfnumber(res, "datdba");
3365 174 : i_encoding = PQfnumber(res, "encoding");
3366 174 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3367 174 : i_collate = PQfnumber(res, "datcollate");
3368 174 : i_ctype = PQfnumber(res, "datctype");
3369 174 : i_datlocale = PQfnumber(res, "datlocale");
3370 174 : i_daticurules = PQfnumber(res, "daticurules");
3371 174 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3372 174 : i_minmxid = PQfnumber(res, "datminmxid");
3373 174 : i_datacl = PQfnumber(res, "datacl");
3374 174 : i_acldefault = PQfnumber(res, "acldefault");
3375 174 : i_datistemplate = PQfnumber(res, "datistemplate");
3376 174 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3377 174 : i_datcollversion = PQfnumber(res, "datcollversion");
3378 174 : i_tablespace = PQfnumber(res, "tablespace");
3379 :
3380 174 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3381 174 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3382 174 : datname = PQgetvalue(res, 0, i_datname);
3383 174 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3384 174 : encoding = PQgetvalue(res, 0, i_encoding);
3385 174 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3386 174 : collate = PQgetvalue(res, 0, i_collate);
3387 174 : ctype = PQgetvalue(res, 0, i_ctype);
3388 174 : if (!PQgetisnull(res, 0, i_datlocale))
3389 28 : locale = PQgetvalue(res, 0, i_datlocale);
3390 : else
3391 146 : locale = NULL;
3392 174 : if (!PQgetisnull(res, 0, i_daticurules))
3393 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3394 : else
3395 174 : icurules = NULL;
3396 174 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3397 174 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3398 174 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3399 174 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3400 174 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3401 174 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3402 174 : tablespace = PQgetvalue(res, 0, i_tablespace);
3403 :
3404 174 : qdatname = pg_strdup(fmtId(datname));
3405 :
3406 : /*
3407 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3408 : * to preserve that), as well as the encoding, locale, and tablespace
3409 : * since those can't be altered later. Other DB properties are left to
3410 : * the DATABASE PROPERTIES entry, so that they can be applied after
3411 : * reconnecting to the target DB.
3412 : *
3413 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3414 : * shown it to be faster. When the server is in binary upgrade mode, it
3415 : * will also skip the checkpoints this strategy ordinarily performs.
3416 : */
3417 174 : if (dopt->binary_upgrade)
3418 : {
3419 74 : appendPQExpBuffer(creaQry,
3420 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3421 : "OID = %u STRATEGY = FILE_COPY",
3422 : qdatname, dbCatId.oid);
3423 : }
3424 : else
3425 : {
3426 100 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3427 : qdatname);
3428 : }
3429 174 : if (strlen(encoding) > 0)
3430 : {
3431 174 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3432 174 : appendStringLiteralAH(creaQry, encoding, fout);
3433 : }
3434 :
3435 174 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3436 174 : if (datlocprovider[0] == 'b')
3437 28 : appendPQExpBufferStr(creaQry, "builtin");
3438 146 : else if (datlocprovider[0] == 'c')
3439 146 : appendPQExpBufferStr(creaQry, "libc");
3440 0 : else if (datlocprovider[0] == 'i')
3441 0 : appendPQExpBufferStr(creaQry, "icu");
3442 : else
3443 0 : pg_fatal("unrecognized locale provider: %s",
3444 : datlocprovider);
3445 :
3446 174 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3447 : {
3448 174 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3449 174 : appendStringLiteralAH(creaQry, collate, fout);
3450 : }
3451 : else
3452 : {
3453 0 : if (strlen(collate) > 0)
3454 : {
3455 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3456 0 : appendStringLiteralAH(creaQry, collate, fout);
3457 : }
3458 0 : if (strlen(ctype) > 0)
3459 : {
3460 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3461 0 : appendStringLiteralAH(creaQry, ctype, fout);
3462 : }
3463 : }
3464 174 : if (locale)
3465 : {
3466 28 : if (datlocprovider[0] == 'b')
3467 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3468 : else
3469 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3470 :
3471 28 : appendStringLiteralAH(creaQry, locale, fout);
3472 : }
3473 :
3474 174 : if (icurules)
3475 : {
3476 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3477 0 : appendStringLiteralAH(creaQry, icurules, fout);
3478 : }
3479 :
3480 : /*
3481 : * For binary upgrade, carry over the collation version. For normal
3482 : * dump/restore, omit the version, so that it is computed upon restore.
3483 : */
3484 174 : if (dopt->binary_upgrade)
3485 : {
3486 74 : if (!PQgetisnull(res, 0, i_datcollversion))
3487 : {
3488 74 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3489 74 : appendStringLiteralAH(creaQry,
3490 : PQgetvalue(res, 0, i_datcollversion),
3491 : fout);
3492 : }
3493 : }
3494 :
3495 : /*
3496 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3497 : * thing; the decision whether to specify a tablespace should be left till
3498 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3499 : * label the DATABASE entry with the tablespace and let the normal
3500 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3501 : * attention to default_tablespace, so that won't work.
3502 : */
3503 174 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3504 10 : !dopt->outputNoTablespaces)
3505 10 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3506 : fmtId(tablespace));
3507 174 : appendPQExpBufferStr(creaQry, ";\n");
3508 :
3509 174 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3510 : qdatname);
3511 :
3512 174 : dbDumpId = createDumpId();
3513 :
3514 174 : ArchiveEntry(fout,
3515 : dbCatId, /* catalog ID */
3516 : dbDumpId, /* dump ID */
3517 174 : ARCHIVE_OPTS(.tag = datname,
3518 : .owner = dba,
3519 : .description = "DATABASE",
3520 : .section = SECTION_PRE_DATA,
3521 : .createStmt = creaQry->data,
3522 : .dropStmt = delQry->data));
3523 :
3524 : /* Compute correct tag for archive entry */
3525 174 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3526 :
3527 : /* Dump DB comment if any */
3528 : {
3529 : /*
3530 : * 8.2 and up keep comments on shared objects in a shared table, so we
3531 : * cannot use the dumpComment() code used for other database objects.
3532 : * Be careful that the ArchiveEntry parameters match that function.
3533 : */
3534 174 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3535 :
3536 174 : if (comment && *comment && !dopt->no_comments)
3537 : {
3538 84 : resetPQExpBuffer(dbQry);
3539 :
3540 : /*
3541 : * Generates warning when loaded into a differently-named
3542 : * database.
3543 : */
3544 84 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3545 84 : appendStringLiteralAH(dbQry, comment, fout);
3546 84 : appendPQExpBufferStr(dbQry, ";\n");
3547 :
3548 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3549 84 : ARCHIVE_OPTS(.tag = labelq->data,
3550 : .owner = dba,
3551 : .description = "COMMENT",
3552 : .section = SECTION_NONE,
3553 : .createStmt = dbQry->data,
3554 : .deps = &dbDumpId,
3555 : .nDeps = 1));
3556 : }
3557 : }
3558 :
3559 : /* Dump DB security label, if enabled */
3560 174 : if (!dopt->no_security_labels)
3561 : {
3562 : PGresult *shres;
3563 : PQExpBuffer seclabelQry;
3564 :
3565 174 : seclabelQry = createPQExpBuffer();
3566 :
3567 174 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3568 174 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3569 174 : resetPQExpBuffer(seclabelQry);
3570 174 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3571 174 : if (seclabelQry->len > 0)
3572 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3573 0 : ARCHIVE_OPTS(.tag = labelq->data,
3574 : .owner = dba,
3575 : .description = "SECURITY LABEL",
3576 : .section = SECTION_NONE,
3577 : .createStmt = seclabelQry->data,
3578 : .deps = &dbDumpId,
3579 : .nDeps = 1));
3580 174 : destroyPQExpBuffer(seclabelQry);
3581 174 : PQclear(shres);
3582 : }
3583 :
3584 : /*
3585 : * Dump ACL if any. Note that we do not support initial privileges
3586 : * (pg_init_privs) on databases.
3587 : */
3588 174 : dbdacl.privtype = 0;
3589 174 : dbdacl.initprivs = NULL;
3590 :
3591 174 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3592 : qdatname, NULL, NULL,
3593 : NULL, dba, &dbdacl);
3594 :
3595 : /*
3596 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3597 : * non-default database-level properties. (The reason this must be
3598 : * separate is that we cannot put any additional commands into the TOC
3599 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3600 : * in an implicit transaction block, and the backend won't allow CREATE
3601 : * DATABASE in that context.)
3602 : */
3603 174 : resetPQExpBuffer(creaQry);
3604 174 : resetPQExpBuffer(delQry);
3605 :
3606 174 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3607 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3608 : qdatname, datconnlimit);
3609 :
3610 174 : if (strcmp(datistemplate, "t") == 0)
3611 : {
3612 22 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3613 : qdatname);
3614 :
3615 : /*
3616 : * The backend won't accept DROP DATABASE on a template database. We
3617 : * can deal with that by removing the template marking before the DROP
3618 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3619 : * since no such command is currently supported, fake it with a direct
3620 : * UPDATE on pg_database.
3621 : */
3622 22 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3623 : "SET datistemplate = false WHERE datname = ");
3624 22 : appendStringLiteralAH(delQry, datname, fout);
3625 22 : appendPQExpBufferStr(delQry, ";\n");
3626 : }
3627 :
3628 : /*
3629 : * We do not restore pg_database.dathasloginevt because it is set
3630 : * automatically on login event trigger creation.
3631 : */
3632 :
3633 : /* Add database-specific SET options */
3634 174 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3635 :
3636 : /*
3637 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3638 : * entry, too, for lack of a better place.
3639 : */
3640 174 : if (dopt->binary_upgrade)
3641 : {
3642 74 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3643 74 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3644 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3645 : "WHERE datname = ",
3646 : frozenxid, minmxid);
3647 74 : appendStringLiteralAH(creaQry, datname, fout);
3648 74 : appendPQExpBufferStr(creaQry, ";\n");
3649 : }
3650 :
3651 174 : if (creaQry->len > 0)
3652 82 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3653 82 : ARCHIVE_OPTS(.tag = datname,
3654 : .owner = dba,
3655 : .description = "DATABASE PROPERTIES",
3656 : .section = SECTION_PRE_DATA,
3657 : .createStmt = creaQry->data,
3658 : .dropStmt = delQry->data,
3659 : .deps = &dbDumpId));
3660 :
3661 : /*
3662 : * pg_largeobject comes from the old system intact, so set its
3663 : * relfrozenxids, relminmxids and relfilenode.
3664 : *
3665 : * pg_largeobject_metadata also comes from the old system intact for
3666 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3667 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3668 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3669 : * changed its storage format in v16.
3670 : */
3671 174 : if (dopt->binary_upgrade)
3672 : {
3673 : PGresult *lo_res;
3674 74 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3675 74 : PQExpBuffer loOutQry = createPQExpBuffer();
3676 74 : PQExpBuffer lomOutQry = createPQExpBuffer();
3677 74 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3678 74 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3679 : int ii_relfrozenxid,
3680 : ii_relfilenode,
3681 : ii_oid,
3682 : ii_relminmxid;
3683 :
3684 74 : if (fout->remoteVersion >= 90300)
3685 74 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3686 : "FROM pg_catalog.pg_class\n"
3687 : "WHERE oid IN (%u, %u, %u, %u);\n",
3688 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3689 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3690 : else
3691 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3692 : "FROM pg_catalog.pg_class\n"
3693 : "WHERE oid IN (%u, %u);\n",
3694 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3695 :
3696 74 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3697 :
3698 74 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3699 74 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3700 74 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3701 74 : ii_oid = PQfnumber(lo_res, "oid");
3702 :
3703 74 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3704 74 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3705 74 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3706 74 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3707 370 : for (int i = 0; i < PQntuples(lo_res); ++i)
3708 : {
3709 : Oid oid;
3710 : RelFileNumber relfilenumber;
3711 : PQExpBuffer horizonQry;
3712 : PQExpBuffer outQry;
3713 :
3714 296 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3715 296 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3716 :
3717 296 : if (oid == LargeObjectRelationId ||
3718 : oid == LargeObjectLOidPNIndexId)
3719 : {
3720 148 : horizonQry = loHorizonQry;
3721 148 : outQry = loOutQry;
3722 : }
3723 : else
3724 : {
3725 148 : horizonQry = lomHorizonQry;
3726 148 : outQry = lomOutQry;
3727 : }
3728 :
3729 296 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3730 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3731 : "WHERE oid = %u;\n",
3732 296 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3733 296 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3734 296 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3735 :
3736 296 : if (oid == LargeObjectRelationId ||
3737 : oid == LargeObjectMetadataRelationId)
3738 148 : appendPQExpBuffer(outQry,
3739 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3740 : relfilenumber);
3741 148 : else if (oid == LargeObjectLOidPNIndexId ||
3742 : oid == LargeObjectMetadataOidIndexId)
3743 148 : appendPQExpBuffer(outQry,
3744 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3745 : relfilenumber);
3746 : }
3747 :
3748 74 : appendPQExpBufferStr(loOutQry,
3749 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3750 74 : appendPQExpBufferStr(lomOutQry,
3751 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3752 :
3753 74 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3754 74 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3755 :
3756 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3757 74 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3758 : .description = "pg_largeobject",
3759 : .section = SECTION_PRE_DATA,
3760 : .createStmt = loOutQry->data));
3761 :
3762 74 : if (fout->remoteVersion >= 160000)
3763 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3764 74 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3765 : .description = "pg_largeobject_metadata",
3766 : .section = SECTION_PRE_DATA,
3767 : .createStmt = lomOutQry->data));
3768 :
3769 74 : PQclear(lo_res);
3770 :
3771 74 : destroyPQExpBuffer(loFrozenQry);
3772 74 : destroyPQExpBuffer(loHorizonQry);
3773 74 : destroyPQExpBuffer(lomHorizonQry);
3774 74 : destroyPQExpBuffer(loOutQry);
3775 74 : destroyPQExpBuffer(lomOutQry);
3776 : }
3777 :
3778 174 : PQclear(res);
3779 :
3780 174 : free(qdatname);
3781 174 : destroyPQExpBuffer(dbQry);
3782 174 : destroyPQExpBuffer(delQry);
3783 174 : destroyPQExpBuffer(creaQry);
3784 174 : destroyPQExpBuffer(labelq);
3785 174 : }
3786 :
3787 : /*
3788 : * Collect any database-specific or role-and-database-specific SET options
3789 : * for this database, and append them to outbuf.
3790 : */
3791 : static void
3792 174 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3793 : const char *dbname, Oid dboid)
3794 : {
3795 174 : PGconn *conn = GetConnection(AH);
3796 174 : PQExpBuffer buf = createPQExpBuffer();
3797 : PGresult *res;
3798 :
3799 : /* First collect database-specific options */
3800 174 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3801 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3802 : dboid);
3803 :
3804 174 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3805 :
3806 234 : for (int i = 0; i < PQntuples(res); i++)
3807 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3808 : "DATABASE", dbname, NULL, NULL,
3809 : outbuf);
3810 :
3811 174 : PQclear(res);
3812 :
3813 : /* Now look for role-and-database-specific options */
3814 174 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3815 : "FROM pg_db_role_setting s, pg_roles r "
3816 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3817 : dboid);
3818 :
3819 174 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3820 :
3821 174 : for (int i = 0; i < PQntuples(res); i++)
3822 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3823 0 : "ROLE", PQgetvalue(res, i, 0),
3824 : "DATABASE", dbname,
3825 : outbuf);
3826 :
3827 174 : PQclear(res);
3828 :
3829 174 : destroyPQExpBuffer(buf);
3830 174 : }
3831 :
3832 : /*
3833 : * dumpEncoding: put the correct encoding into the archive
3834 : */
3835 : static void
3836 376 : dumpEncoding(Archive *AH)
3837 : {
3838 376 : const char *encname = pg_encoding_to_char(AH->encoding);
3839 376 : PQExpBuffer qry = createPQExpBuffer();
3840 :
3841 376 : pg_log_info("saving encoding = %s", encname);
3842 :
3843 376 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3844 376 : appendStringLiteralAH(qry, encname, AH);
3845 376 : appendPQExpBufferStr(qry, ";\n");
3846 :
3847 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3848 376 : ARCHIVE_OPTS(.tag = "ENCODING",
3849 : .description = "ENCODING",
3850 : .section = SECTION_PRE_DATA,
3851 : .createStmt = qry->data));
3852 :
3853 376 : destroyPQExpBuffer(qry);
3854 376 : }
3855 :
3856 :
3857 : /*
3858 : * dumpStdStrings: put the correct escape string behavior into the archive
3859 : */
3860 : static void
3861 376 : dumpStdStrings(Archive *AH)
3862 : {
3863 376 : const char *stdstrings = AH->std_strings ? "on" : "off";
3864 376 : PQExpBuffer qry = createPQExpBuffer();
3865 :
3866 376 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3867 : stdstrings);
3868 :
3869 376 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3870 : stdstrings);
3871 :
3872 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3873 376 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3874 : .description = "STDSTRINGS",
3875 : .section = SECTION_PRE_DATA,
3876 : .createStmt = qry->data));
3877 :
3878 376 : destroyPQExpBuffer(qry);
3879 376 : }
3880 :
3881 : /*
3882 : * dumpSearchPath: record the active search_path in the archive
3883 : */
3884 : static void
3885 376 : dumpSearchPath(Archive *AH)
3886 : {
3887 376 : PQExpBuffer qry = createPQExpBuffer();
3888 376 : PQExpBuffer path = createPQExpBuffer();
3889 : PGresult *res;
3890 376 : char **schemanames = NULL;
3891 376 : int nschemanames = 0;
3892 : int i;
3893 :
3894 : /*
3895 : * We use the result of current_schemas(), not the search_path GUC,
3896 : * because that might contain wildcards such as "$user", which won't
3897 : * necessarily have the same value during restore. Also, this way avoids
3898 : * listing schemas that may appear in search_path but not actually exist,
3899 : * which seems like a prudent exclusion.
3900 : */
3901 376 : res = ExecuteSqlQueryForSingleRow(AH,
3902 : "SELECT pg_catalog.current_schemas(false)");
3903 :
3904 376 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3905 0 : pg_fatal("could not parse result of current_schemas()");
3906 :
3907 : /*
3908 : * We use set_config(), not a simple "SET search_path" command, because
3909 : * the latter has less-clean behavior if the search path is empty. While
3910 : * that's likely to get fixed at some point, it seems like a good idea to
3911 : * be as backwards-compatible as possible in what we put into archives.
3912 : */
3913 376 : for (i = 0; i < nschemanames; i++)
3914 : {
3915 0 : if (i > 0)
3916 0 : appendPQExpBufferStr(path, ", ");
3917 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3918 : }
3919 :
3920 376 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3921 376 : appendStringLiteralAH(qry, path->data, AH);
3922 376 : appendPQExpBufferStr(qry, ", false);\n");
3923 :
3924 376 : pg_log_info("saving \"search_path = %s\"", path->data);
3925 :
3926 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3927 376 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3928 : .description = "SEARCHPATH",
3929 : .section = SECTION_PRE_DATA,
3930 : .createStmt = qry->data));
3931 :
3932 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3933 376 : AH->searchpath = pg_strdup(qry->data);
3934 :
3935 376 : free(schemanames);
3936 376 : PQclear(res);
3937 376 : destroyPQExpBuffer(qry);
3938 376 : destroyPQExpBuffer(path);
3939 376 : }
3940 :
3941 :
3942 : /*
3943 : * getLOs:
3944 : * Collect schema-level data about large objects
3945 : */
3946 : static void
3947 320 : getLOs(Archive *fout)
3948 : {
3949 320 : DumpOptions *dopt = fout->dopt;
3950 320 : PQExpBuffer loQry = createPQExpBuffer();
3951 : PGresult *res;
3952 : int ntups;
3953 : int i;
3954 : int n;
3955 : int i_oid;
3956 : int i_lomowner;
3957 : int i_lomacl;
3958 : int i_acldefault;
3959 :
3960 320 : pg_log_info("reading large objects");
3961 :
3962 : /*
3963 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3964 : * with the same owner/ACL appear together.
3965 : */
3966 320 : appendPQExpBufferStr(loQry,
3967 : "SELECT oid, lomowner, lomacl, "
3968 : "acldefault('L', lomowner) AS acldefault "
3969 : "FROM pg_largeobject_metadata "
3970 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3971 :
3972 320 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3973 :
3974 320 : i_oid = PQfnumber(res, "oid");
3975 320 : i_lomowner = PQfnumber(res, "lomowner");
3976 320 : i_lomacl = PQfnumber(res, "lomacl");
3977 320 : i_acldefault = PQfnumber(res, "acldefault");
3978 :
3979 320 : ntups = PQntuples(res);
3980 :
3981 : /*
3982 : * Group the blobs into suitably-sized groups that have the same owner and
3983 : * ACL setting, and build a metadata and a data DumpableObject for each
3984 : * group. (If we supported initprivs for blobs, we'd have to insist that
3985 : * groups also share initprivs settings, since the DumpableObject only has
3986 : * room for one.) i is the index of the first tuple in the current group,
3987 : * and n is the number of tuples we include in the group.
3988 : */
3989 500 : for (i = 0; i < ntups; i += n)
3990 : {
3991 180 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3992 180 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3993 180 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3994 : LoInfo *loinfo;
3995 : DumpableObject *lodata;
3996 : char namebuf[64];
3997 :
3998 : /* Scan to find first tuple not to be included in group */
3999 180 : n = 1;
4000 210 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4001 : {
4002 108 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4003 98 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4004 : break;
4005 30 : n++;
4006 : }
4007 :
4008 : /* Build the metadata DumpableObject */
4009 180 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4010 :
4011 180 : loinfo->dobj.objType = DO_LARGE_OBJECT;
4012 180 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4013 180 : loinfo->dobj.catId.oid = thisoid;
4014 180 : AssignDumpId(&loinfo->dobj);
4015 :
4016 180 : if (n > 1)
4017 20 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4018 20 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4019 : else
4020 160 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4021 180 : loinfo->dobj.name = pg_strdup(namebuf);
4022 180 : loinfo->dacl.acl = pg_strdup(thisacl);
4023 180 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4024 180 : loinfo->dacl.privtype = 0;
4025 180 : loinfo->dacl.initprivs = NULL;
4026 180 : loinfo->rolname = getRoleName(thisowner);
4027 180 : loinfo->numlos = n;
4028 180 : loinfo->looids[0] = thisoid;
4029 : /* Collect OIDs of the remaining blobs in this group */
4030 210 : for (int k = 1; k < n; k++)
4031 : {
4032 : CatalogId extraID;
4033 :
4034 30 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4035 :
4036 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4037 30 : extraID.tableoid = LargeObjectRelationId;
4038 30 : extraID.oid = loinfo->looids[k];
4039 30 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4040 : }
4041 :
4042 : /* LOs have data */
4043 180 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4044 :
4045 : /* Mark whether LO group has a non-empty ACL */
4046 180 : if (!PQgetisnull(res, i, i_lomacl))
4047 78 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4048 :
4049 : /*
4050 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4051 : * as it will be copied by pg_upgrade, which simply copies the
4052 : * pg_largeobject table. We *do* however dump out anything but the
4053 : * data, as pg_upgrade copies just pg_largeobject, but not
4054 : * pg_largeobject_metadata, after the dump is restored. In versions
4055 : * before v12, this is done via proper large object commands. In
4056 : * newer versions, we dump the content of pg_largeobject_metadata and
4057 : * any associated pg_shdepend rows, which is faster to restore. (On
4058 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4059 : * column is hidden and won't be dumped.)
4060 : */
4061 180 : if (dopt->binary_upgrade)
4062 : {
4063 26 : if (fout->remoteVersion >= 120000)
4064 : {
4065 : /*
4066 : * We should've saved pg_largeobject_metadata's dump ID before
4067 : * this point.
4068 : */
4069 : Assert(lo_metadata_dumpId);
4070 :
4071 26 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4072 :
4073 : /*
4074 : * Mark the large object as dependent on
4075 : * pg_largeobject_metadata so that any large object
4076 : * comments/seclables are dumped after it.
4077 : */
4078 26 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4079 26 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4080 26 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4081 : }
4082 : else
4083 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4084 : }
4085 :
4086 : /*
4087 : * Create a "BLOBS" data item for the group, too. This is just a
4088 : * placeholder for sorting; it carries no data now.
4089 : */
4090 180 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4091 180 : lodata->objType = DO_LARGE_OBJECT_DATA;
4092 180 : lodata->catId = nilCatalogId;
4093 180 : AssignDumpId(lodata);
4094 180 : lodata->name = pg_strdup(namebuf);
4095 180 : lodata->components |= DUMP_COMPONENT_DATA;
4096 : /* Set up explicit dependency from data to metadata */
4097 180 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4098 180 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4099 180 : lodata->nDeps = lodata->allocDeps = 1;
4100 : }
4101 :
4102 320 : PQclear(res);
4103 320 : destroyPQExpBuffer(loQry);
4104 320 : }
4105 :
4106 : /*
4107 : * dumpLO
4108 : *
4109 : * dump the definition (metadata) of the given large object group
4110 : */
4111 : static void
4112 168 : dumpLO(Archive *fout, const LoInfo *loinfo)
4113 : {
4114 168 : PQExpBuffer cquery = createPQExpBuffer();
4115 :
4116 : /*
4117 : * The "definition" is just a newline-separated list of OIDs. We need to
4118 : * put something into the dropStmt too, but it can just be a comment.
4119 : */
4120 366 : for (int i = 0; i < loinfo->numlos; i++)
4121 198 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4122 :
4123 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4124 154 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4125 154 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4126 : .owner = loinfo->rolname,
4127 : .description = "BLOB METADATA",
4128 : .section = SECTION_DATA,
4129 : .createStmt = cquery->data,
4130 : .dropStmt = "-- dummy"));
4131 :
4132 : /*
4133 : * Dump per-blob comments and seclabels if any. We assume these are rare
4134 : * enough that it's okay to generate retail TOC entries for them.
4135 : */
4136 168 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4137 : DUMP_COMPONENT_SECLABEL))
4138 : {
4139 206 : for (int i = 0; i < loinfo->numlos; i++)
4140 : {
4141 : CatalogId catId;
4142 : char namebuf[32];
4143 :
4144 : /* Build identifying info for this blob */
4145 118 : catId.tableoid = loinfo->dobj.catId.tableoid;
4146 118 : catId.oid = loinfo->looids[i];
4147 118 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4148 :
4149 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4150 118 : dumpComment(fout, "LARGE OBJECT", namebuf,
4151 118 : NULL, loinfo->rolname,
4152 118 : catId, 0, loinfo->dobj.dumpId);
4153 :
4154 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4155 20 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4156 20 : NULL, loinfo->rolname,
4157 20 : catId, 0, loinfo->dobj.dumpId);
4158 : }
4159 : }
4160 :
4161 : /*
4162 : * Dump the ACLs if any (remember that all blobs in the group will have
4163 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4164 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4165 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4166 : * string to emit a mutated version for each blob.
4167 : */
4168 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4169 : {
4170 : char namebuf[32];
4171 :
4172 : /* Build identifying info for the first blob */
4173 66 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4174 :
4175 66 : if (loinfo->numlos > 1)
4176 : {
4177 : char tagbuf[64];
4178 :
4179 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4180 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4181 :
4182 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4183 : "LARGE OBJECT", namebuf, NULL, NULL,
4184 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4185 : }
4186 : else
4187 : {
4188 66 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4189 : "LARGE OBJECT", namebuf, NULL, NULL,
4190 66 : NULL, loinfo->rolname, &loinfo->dacl);
4191 : }
4192 : }
4193 :
4194 168 : destroyPQExpBuffer(cquery);
4195 168 : }
4196 :
4197 : /*
4198 : * dumpLOs:
4199 : * dump the data contents of the large objects in the given group
4200 : */
4201 : static int
4202 146 : dumpLOs(Archive *fout, const void *arg)
4203 : {
4204 146 : const LoInfo *loinfo = (const LoInfo *) arg;
4205 146 : PGconn *conn = GetConnection(fout);
4206 : char buf[LOBBUFSIZE];
4207 :
4208 146 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4209 :
4210 308 : for (int i = 0; i < loinfo->numlos; i++)
4211 : {
4212 162 : Oid loOid = loinfo->looids[i];
4213 : int loFd;
4214 : int cnt;
4215 :
4216 : /* Open the LO */
4217 162 : loFd = lo_open(conn, loOid, INV_READ);
4218 162 : if (loFd == -1)
4219 0 : pg_fatal("could not open large object %u: %s",
4220 : loOid, PQerrorMessage(conn));
4221 :
4222 162 : StartLO(fout, loOid);
4223 :
4224 : /* Now read it in chunks, sending data to archive */
4225 : do
4226 : {
4227 254 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4228 254 : if (cnt < 0)
4229 0 : pg_fatal("error reading large object %u: %s",
4230 : loOid, PQerrorMessage(conn));
4231 :
4232 254 : WriteData(fout, buf, cnt);
4233 254 : } while (cnt > 0);
4234 :
4235 162 : lo_close(conn, loFd);
4236 :
4237 162 : EndLO(fout, loOid);
4238 : }
4239 :
4240 146 : return 1;
4241 : }
4242 :
4243 : /*
4244 : * getPolicies
4245 : * get information about all RLS policies on dumpable tables.
4246 : */
4247 : void
4248 376 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4249 : {
4250 376 : DumpOptions *dopt = fout->dopt;
4251 : PQExpBuffer query;
4252 : PQExpBuffer tbloids;
4253 : PGresult *res;
4254 : PolicyInfo *polinfo;
4255 : int i_oid;
4256 : int i_tableoid;
4257 : int i_polrelid;
4258 : int i_polname;
4259 : int i_polcmd;
4260 : int i_polpermissive;
4261 : int i_polroles;
4262 : int i_polqual;
4263 : int i_polwithcheck;
4264 : int i,
4265 : j,
4266 : ntups;
4267 :
4268 : /* No policies before 9.5 */
4269 376 : if (fout->remoteVersion < 90500)
4270 0 : return;
4271 :
4272 : /* Skip if --no-policies was specified */
4273 376 : if (dopt->no_policies)
4274 2 : return;
4275 :
4276 374 : query = createPQExpBuffer();
4277 374 : tbloids = createPQExpBuffer();
4278 :
4279 : /*
4280 : * Identify tables of interest, and check which ones have RLS enabled.
4281 : */
4282 374 : appendPQExpBufferChar(tbloids, '{');
4283 98716 : for (i = 0; i < numTables; i++)
4284 : {
4285 98342 : TableInfo *tbinfo = &tblinfo[i];
4286 :
4287 : /* Ignore row security on tables not to be dumped */
4288 98342 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4289 84594 : continue;
4290 :
4291 : /* It can't have RLS or policies if it's not a table */
4292 13748 : if (tbinfo->relkind != RELKIND_RELATION &&
4293 3892 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4294 2700 : continue;
4295 :
4296 : /* Add it to the list of table OIDs to be probed below */
4297 11048 : if (tbloids->len > 1) /* do we have more than the '{'? */
4298 10804 : appendPQExpBufferChar(tbloids, ',');
4299 11048 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4300 :
4301 : /* Is RLS enabled? (That's separate from whether it has policies) */
4302 11048 : if (tbinfo->rowsec)
4303 : {
4304 106 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4305 :
4306 : /*
4307 : * We represent RLS being enabled on a table by creating a
4308 : * PolicyInfo object with null polname.
4309 : *
4310 : * Note: use tableoid 0 so that this object won't be mistaken for
4311 : * something that pg_depend entries apply to.
4312 : */
4313 106 : polinfo = pg_malloc(sizeof(PolicyInfo));
4314 106 : polinfo->dobj.objType = DO_POLICY;
4315 106 : polinfo->dobj.catId.tableoid = 0;
4316 106 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4317 106 : AssignDumpId(&polinfo->dobj);
4318 106 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4319 106 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4320 106 : polinfo->poltable = tbinfo;
4321 106 : polinfo->polname = NULL;
4322 106 : polinfo->polcmd = '\0';
4323 106 : polinfo->polpermissive = 0;
4324 106 : polinfo->polroles = NULL;
4325 106 : polinfo->polqual = NULL;
4326 106 : polinfo->polwithcheck = NULL;
4327 : }
4328 : }
4329 374 : appendPQExpBufferChar(tbloids, '}');
4330 :
4331 : /*
4332 : * Now, read all RLS policies belonging to the tables of interest, and
4333 : * create PolicyInfo objects for them. (Note that we must filter the
4334 : * results server-side not locally, because we dare not apply pg_get_expr
4335 : * to tables we don't have lock on.)
4336 : */
4337 374 : pg_log_info("reading row-level security policies");
4338 :
4339 374 : printfPQExpBuffer(query,
4340 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4341 374 : if (fout->remoteVersion >= 100000)
4342 374 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4343 : else
4344 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4345 374 : appendPQExpBuffer(query,
4346 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4347 : " 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, "
4348 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4349 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4350 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4351 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4352 : tbloids->data);
4353 :
4354 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4355 :
4356 374 : ntups = PQntuples(res);
4357 374 : if (ntups > 0)
4358 : {
4359 86 : i_oid = PQfnumber(res, "oid");
4360 86 : i_tableoid = PQfnumber(res, "tableoid");
4361 86 : i_polrelid = PQfnumber(res, "polrelid");
4362 86 : i_polname = PQfnumber(res, "polname");
4363 86 : i_polcmd = PQfnumber(res, "polcmd");
4364 86 : i_polpermissive = PQfnumber(res, "polpermissive");
4365 86 : i_polroles = PQfnumber(res, "polroles");
4366 86 : i_polqual = PQfnumber(res, "polqual");
4367 86 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4368 :
4369 86 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4370 :
4371 632 : for (j = 0; j < ntups; j++)
4372 : {
4373 546 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4374 546 : TableInfo *tbinfo = findTableByOid(polrelid);
4375 :
4376 546 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4377 :
4378 546 : polinfo[j].dobj.objType = DO_POLICY;
4379 546 : polinfo[j].dobj.catId.tableoid =
4380 546 : atooid(PQgetvalue(res, j, i_tableoid));
4381 546 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4382 546 : AssignDumpId(&polinfo[j].dobj);
4383 546 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4384 546 : polinfo[j].poltable = tbinfo;
4385 546 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4386 546 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4387 :
4388 546 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4389 546 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4390 :
4391 546 : if (PQgetisnull(res, j, i_polroles))
4392 242 : polinfo[j].polroles = NULL;
4393 : else
4394 304 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4395 :
4396 546 : if (PQgetisnull(res, j, i_polqual))
4397 76 : polinfo[j].polqual = NULL;
4398 : else
4399 470 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4400 :
4401 546 : if (PQgetisnull(res, j, i_polwithcheck))
4402 288 : polinfo[j].polwithcheck = NULL;
4403 : else
4404 258 : polinfo[j].polwithcheck
4405 258 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4406 : }
4407 : }
4408 :
4409 374 : PQclear(res);
4410 :
4411 374 : destroyPQExpBuffer(query);
4412 374 : destroyPQExpBuffer(tbloids);
4413 : }
4414 :
4415 : /*
4416 : * dumpPolicy
4417 : * dump the definition of the given policy
4418 : */
4419 : static void
4420 652 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4421 : {
4422 652 : DumpOptions *dopt = fout->dopt;
4423 652 : TableInfo *tbinfo = polinfo->poltable;
4424 : PQExpBuffer query;
4425 : PQExpBuffer delqry;
4426 : PQExpBuffer polprefix;
4427 : char *qtabname;
4428 : const char *cmd;
4429 : char *tag;
4430 :
4431 : /* Do nothing if not dumping schema */
4432 652 : if (!dopt->dumpSchema)
4433 98 : return;
4434 :
4435 : /*
4436 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4437 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4438 : * ROW LEVEL SECURITY.
4439 : */
4440 554 : if (polinfo->polname == NULL)
4441 : {
4442 92 : query = createPQExpBuffer();
4443 :
4444 92 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4445 92 : fmtQualifiedDumpable(tbinfo));
4446 :
4447 : /*
4448 : * We must emit the ROW SECURITY object's dependency on its table
4449 : * explicitly, because it will not match anything in pg_depend (unlike
4450 : * the case for other PolicyInfo objects).
4451 : */
4452 92 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4453 92 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4454 92 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4455 : .namespace = polinfo->dobj.namespace->dobj.name,
4456 : .owner = tbinfo->rolname,
4457 : .description = "ROW SECURITY",
4458 : .section = SECTION_POST_DATA,
4459 : .createStmt = query->data,
4460 : .deps = &(tbinfo->dobj.dumpId),
4461 : .nDeps = 1));
4462 :
4463 92 : destroyPQExpBuffer(query);
4464 92 : return;
4465 : }
4466 :
4467 462 : if (polinfo->polcmd == '*')
4468 154 : cmd = "";
4469 308 : else if (polinfo->polcmd == 'r')
4470 82 : cmd = " FOR SELECT";
4471 226 : else if (polinfo->polcmd == 'a')
4472 62 : cmd = " FOR INSERT";
4473 164 : else if (polinfo->polcmd == 'w')
4474 82 : cmd = " FOR UPDATE";
4475 82 : else if (polinfo->polcmd == 'd')
4476 82 : cmd = " FOR DELETE";
4477 : else
4478 0 : pg_fatal("unexpected policy command type: %c",
4479 : polinfo->polcmd);
4480 :
4481 462 : query = createPQExpBuffer();
4482 462 : delqry = createPQExpBuffer();
4483 462 : polprefix = createPQExpBuffer();
4484 :
4485 462 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4486 :
4487 462 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4488 :
4489 462 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4490 462 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4491 :
4492 462 : if (polinfo->polroles != NULL)
4493 248 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4494 :
4495 462 : if (polinfo->polqual != NULL)
4496 400 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4497 :
4498 462 : if (polinfo->polwithcheck != NULL)
4499 216 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4500 :
4501 462 : appendPQExpBufferStr(query, ";\n");
4502 :
4503 462 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4504 462 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4505 :
4506 462 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4507 462 : fmtId(polinfo->polname));
4508 :
4509 462 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4510 :
4511 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4512 462 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4513 462 : ARCHIVE_OPTS(.tag = tag,
4514 : .namespace = polinfo->dobj.namespace->dobj.name,
4515 : .owner = tbinfo->rolname,
4516 : .description = "POLICY",
4517 : .section = SECTION_POST_DATA,
4518 : .createStmt = query->data,
4519 : .dropStmt = delqry->data));
4520 :
4521 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4522 62 : dumpComment(fout, polprefix->data, qtabname,
4523 62 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4524 62 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4525 :
4526 462 : free(tag);
4527 462 : destroyPQExpBuffer(query);
4528 462 : destroyPQExpBuffer(delqry);
4529 462 : destroyPQExpBuffer(polprefix);
4530 462 : free(qtabname);
4531 : }
4532 :
4533 : /*
4534 : * getPublications
4535 : * get information about publications
4536 : */
4537 : void
4538 376 : getPublications(Archive *fout)
4539 : {
4540 376 : DumpOptions *dopt = fout->dopt;
4541 : PQExpBuffer query;
4542 : PGresult *res;
4543 : PublicationInfo *pubinfo;
4544 : int i_tableoid;
4545 : int i_oid;
4546 : int i_pubname;
4547 : int i_pubowner;
4548 : int i_puballtables;
4549 : int i_puballsequences;
4550 : int i_pubinsert;
4551 : int i_pubupdate;
4552 : int i_pubdelete;
4553 : int i_pubtruncate;
4554 : int i_pubviaroot;
4555 : int i_pubgencols;
4556 : int i,
4557 : ntups;
4558 :
4559 376 : if (dopt->no_publications || fout->remoteVersion < 100000)
4560 0 : return;
4561 :
4562 376 : query = createPQExpBuffer();
4563 :
4564 : /* Get the publications. */
4565 376 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4566 : "p.pubowner, p.puballtables, p.pubinsert, "
4567 : "p.pubupdate, p.pubdelete, ");
4568 :
4569 376 : if (fout->remoteVersion >= 110000)
4570 376 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4571 : else
4572 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4573 :
4574 376 : if (fout->remoteVersion >= 130000)
4575 376 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4576 : else
4577 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4578 :
4579 376 : if (fout->remoteVersion >= 180000)
4580 376 : appendPQExpBufferStr(query, "p.pubgencols, ");
4581 : else
4582 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4583 :
4584 376 : if (fout->remoteVersion >= 190000)
4585 376 : appendPQExpBufferStr(query, "p.puballsequences ");
4586 : else
4587 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4588 :
4589 376 : appendPQExpBufferStr(query, "FROM pg_publication p");
4590 :
4591 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4592 :
4593 376 : ntups = PQntuples(res);
4594 :
4595 376 : if (ntups == 0)
4596 270 : goto cleanup;
4597 :
4598 106 : i_tableoid = PQfnumber(res, "tableoid");
4599 106 : i_oid = PQfnumber(res, "oid");
4600 106 : i_pubname = PQfnumber(res, "pubname");
4601 106 : i_pubowner = PQfnumber(res, "pubowner");
4602 106 : i_puballtables = PQfnumber(res, "puballtables");
4603 106 : i_puballsequences = PQfnumber(res, "puballsequences");
4604 106 : i_pubinsert = PQfnumber(res, "pubinsert");
4605 106 : i_pubupdate = PQfnumber(res, "pubupdate");
4606 106 : i_pubdelete = PQfnumber(res, "pubdelete");
4607 106 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4608 106 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4609 106 : i_pubgencols = PQfnumber(res, "pubgencols");
4610 :
4611 106 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4612 :
4613 808 : for (i = 0; i < ntups; i++)
4614 : {
4615 702 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4616 702 : pubinfo[i].dobj.catId.tableoid =
4617 702 : atooid(PQgetvalue(res, i, i_tableoid));
4618 702 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4619 702 : AssignDumpId(&pubinfo[i].dobj);
4620 702 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4621 702 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4622 702 : pubinfo[i].puballtables =
4623 702 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4624 702 : pubinfo[i].puballsequences =
4625 702 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4626 702 : pubinfo[i].pubinsert =
4627 702 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4628 702 : pubinfo[i].pubupdate =
4629 702 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4630 702 : pubinfo[i].pubdelete =
4631 702 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4632 702 : pubinfo[i].pubtruncate =
4633 702 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4634 702 : pubinfo[i].pubviaroot =
4635 702 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4636 702 : pubinfo[i].pubgencols_type =
4637 702 : *(PQgetvalue(res, i, i_pubgencols));
4638 :
4639 : /* Decide whether we want to dump it */
4640 702 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4641 : }
4642 :
4643 106 : cleanup:
4644 376 : PQclear(res);
4645 :
4646 376 : destroyPQExpBuffer(query);
4647 : }
4648 :
4649 : /*
4650 : * dumpPublication
4651 : * dump the definition of the given publication
4652 : */
4653 : static void
4654 570 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4655 : {
4656 570 : DumpOptions *dopt = fout->dopt;
4657 : PQExpBuffer delq;
4658 : PQExpBuffer query;
4659 : char *qpubname;
4660 570 : bool first = true;
4661 :
4662 : /* Do nothing if not dumping schema */
4663 570 : if (!dopt->dumpSchema)
4664 84 : return;
4665 :
4666 486 : delq = createPQExpBuffer();
4667 486 : query = createPQExpBuffer();
4668 :
4669 486 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4670 :
4671 486 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4672 : qpubname);
4673 :
4674 486 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4675 : qpubname);
4676 :
4677 486 : if (pubinfo->puballtables && pubinfo->puballsequences)
4678 62 : appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4679 424 : else if (pubinfo->puballtables)
4680 64 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4681 360 : else if (pubinfo->puballsequences)
4682 62 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4683 :
4684 486 : appendPQExpBufferStr(query, " WITH (publish = '");
4685 486 : if (pubinfo->pubinsert)
4686 : {
4687 362 : appendPQExpBufferStr(query, "insert");
4688 362 : first = false;
4689 : }
4690 :
4691 486 : if (pubinfo->pubupdate)
4692 : {
4693 362 : if (!first)
4694 362 : appendPQExpBufferStr(query, ", ");
4695 :
4696 362 : appendPQExpBufferStr(query, "update");
4697 362 : first = false;
4698 : }
4699 :
4700 486 : if (pubinfo->pubdelete)
4701 : {
4702 362 : if (!first)
4703 362 : appendPQExpBufferStr(query, ", ");
4704 :
4705 362 : appendPQExpBufferStr(query, "delete");
4706 362 : first = false;
4707 : }
4708 :
4709 486 : if (pubinfo->pubtruncate)
4710 : {
4711 362 : if (!first)
4712 362 : appendPQExpBufferStr(query, ", ");
4713 :
4714 362 : appendPQExpBufferStr(query, "truncate");
4715 362 : first = false;
4716 : }
4717 :
4718 486 : appendPQExpBufferChar(query, '\'');
4719 :
4720 486 : if (pubinfo->pubviaroot)
4721 10 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4722 :
4723 486 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4724 62 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4725 :
4726 486 : appendPQExpBufferStr(query, ");\n");
4727 :
4728 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4729 486 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4730 486 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4731 : .owner = pubinfo->rolname,
4732 : .description = "PUBLICATION",
4733 : .section = SECTION_POST_DATA,
4734 : .createStmt = query->data,
4735 : .dropStmt = delq->data));
4736 :
4737 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4738 62 : dumpComment(fout, "PUBLICATION", qpubname,
4739 62 : NULL, pubinfo->rolname,
4740 62 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4741 :
4742 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4743 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4744 0 : NULL, pubinfo->rolname,
4745 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4746 :
4747 486 : destroyPQExpBuffer(delq);
4748 486 : destroyPQExpBuffer(query);
4749 486 : free(qpubname);
4750 : }
4751 :
4752 : /*
4753 : * getPublicationNamespaces
4754 : * get information about publication membership for dumpable schemas.
4755 : */
4756 : void
4757 376 : getPublicationNamespaces(Archive *fout)
4758 : {
4759 : PQExpBuffer query;
4760 : PGresult *res;
4761 : PublicationSchemaInfo *pubsinfo;
4762 376 : DumpOptions *dopt = fout->dopt;
4763 : int i_tableoid;
4764 : int i_oid;
4765 : int i_pnpubid;
4766 : int i_pnnspid;
4767 : int i,
4768 : j,
4769 : ntups;
4770 :
4771 376 : if (dopt->no_publications || fout->remoteVersion < 150000)
4772 0 : return;
4773 :
4774 376 : query = createPQExpBuffer();
4775 :
4776 : /* Collect all publication membership info. */
4777 376 : appendPQExpBufferStr(query,
4778 : "SELECT tableoid, oid, pnpubid, pnnspid "
4779 : "FROM pg_catalog.pg_publication_namespace");
4780 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4781 :
4782 376 : ntups = PQntuples(res);
4783 :
4784 376 : i_tableoid = PQfnumber(res, "tableoid");
4785 376 : i_oid = PQfnumber(res, "oid");
4786 376 : i_pnpubid = PQfnumber(res, "pnpubid");
4787 376 : i_pnnspid = PQfnumber(res, "pnnspid");
4788 :
4789 : /* this allocation may be more than we need */
4790 376 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4791 376 : j = 0;
4792 :
4793 626 : for (i = 0; i < ntups; i++)
4794 : {
4795 250 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4796 250 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4797 : PublicationInfo *pubinfo;
4798 : NamespaceInfo *nspinfo;
4799 :
4800 : /*
4801 : * Ignore any entries for which we aren't interested in either the
4802 : * publication or the rel.
4803 : */
4804 250 : pubinfo = findPublicationByOid(pnpubid);
4805 250 : if (pubinfo == NULL)
4806 0 : continue;
4807 250 : nspinfo = findNamespaceByOid(pnnspid);
4808 250 : if (nspinfo == NULL)
4809 0 : continue;
4810 :
4811 : /* OK, make a DumpableObject for this relationship */
4812 250 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4813 250 : pubsinfo[j].dobj.catId.tableoid =
4814 250 : atooid(PQgetvalue(res, i, i_tableoid));
4815 250 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4816 250 : AssignDumpId(&pubsinfo[j].dobj);
4817 250 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4818 250 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4819 250 : pubsinfo[j].publication = pubinfo;
4820 250 : pubsinfo[j].pubschema = nspinfo;
4821 :
4822 : /* Decide whether we want to dump it */
4823 250 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4824 :
4825 250 : j++;
4826 : }
4827 :
4828 376 : PQclear(res);
4829 376 : destroyPQExpBuffer(query);
4830 : }
4831 :
4832 : /*
4833 : * getPublicationTables
4834 : * get information about publication membership for dumpable tables.
4835 : */
4836 : void
4837 376 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4838 : {
4839 : PQExpBuffer query;
4840 : PGresult *res;
4841 : PublicationRelInfo *pubrinfo;
4842 376 : DumpOptions *dopt = fout->dopt;
4843 : int i_tableoid;
4844 : int i_oid;
4845 : int i_prpubid;
4846 : int i_prrelid;
4847 : int i_prrelqual;
4848 : int i_prattrs;
4849 : int i,
4850 : j,
4851 : ntups;
4852 :
4853 376 : if (dopt->no_publications || fout->remoteVersion < 100000)
4854 0 : return;
4855 :
4856 376 : query = createPQExpBuffer();
4857 :
4858 : /* Collect all publication membership info. */
4859 376 : if (fout->remoteVersion >= 150000)
4860 376 : appendPQExpBufferStr(query,
4861 : "SELECT tableoid, oid, prpubid, prrelid, "
4862 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4863 : "(CASE\n"
4864 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4865 : " (SELECT array_agg(attname)\n"
4866 : " FROM\n"
4867 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4868 : " pg_catalog.pg_attribute\n"
4869 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4870 : " ELSE NULL END) prattrs "
4871 : "FROM pg_catalog.pg_publication_rel pr");
4872 : else
4873 0 : appendPQExpBufferStr(query,
4874 : "SELECT tableoid, oid, prpubid, prrelid, "
4875 : "NULL AS prrelqual, NULL AS prattrs "
4876 : "FROM pg_catalog.pg_publication_rel");
4877 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4878 :
4879 376 : ntups = PQntuples(res);
4880 :
4881 376 : i_tableoid = PQfnumber(res, "tableoid");
4882 376 : i_oid = PQfnumber(res, "oid");
4883 376 : i_prpubid = PQfnumber(res, "prpubid");
4884 376 : i_prrelid = PQfnumber(res, "prrelid");
4885 376 : i_prrelqual = PQfnumber(res, "prrelqual");
4886 376 : i_prattrs = PQfnumber(res, "prattrs");
4887 :
4888 : /* this allocation may be more than we need */
4889 376 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4890 376 : j = 0;
4891 :
4892 1076 : for (i = 0; i < ntups; i++)
4893 : {
4894 700 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4895 700 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4896 : PublicationInfo *pubinfo;
4897 : TableInfo *tbinfo;
4898 :
4899 : /*
4900 : * Ignore any entries for which we aren't interested in either the
4901 : * publication or the rel.
4902 : */
4903 700 : pubinfo = findPublicationByOid(prpubid);
4904 700 : if (pubinfo == NULL)
4905 0 : continue;
4906 700 : tbinfo = findTableByOid(prrelid);
4907 700 : if (tbinfo == NULL)
4908 0 : continue;
4909 :
4910 : /* OK, make a DumpableObject for this relationship */
4911 700 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4912 700 : pubrinfo[j].dobj.catId.tableoid =
4913 700 : atooid(PQgetvalue(res, i, i_tableoid));
4914 700 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4915 700 : AssignDumpId(&pubrinfo[j].dobj);
4916 700 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4917 700 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4918 700 : pubrinfo[j].publication = pubinfo;
4919 700 : pubrinfo[j].pubtable = tbinfo;
4920 700 : if (PQgetisnull(res, i, i_prrelqual))
4921 388 : pubrinfo[j].pubrelqual = NULL;
4922 : else
4923 312 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4924 :
4925 700 : if (!PQgetisnull(res, i, i_prattrs))
4926 : {
4927 : char **attnames;
4928 : int nattnames;
4929 : PQExpBuffer attribs;
4930 :
4931 222 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4932 : &attnames, &nattnames))
4933 0 : pg_fatal("could not parse %s array", "prattrs");
4934 222 : attribs = createPQExpBuffer();
4935 638 : for (int k = 0; k < nattnames; k++)
4936 : {
4937 416 : if (k > 0)
4938 194 : appendPQExpBufferStr(attribs, ", ");
4939 :
4940 416 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4941 : }
4942 222 : pubrinfo[j].pubrattrs = attribs->data;
4943 222 : free(attribs); /* but not attribs->data */
4944 222 : free(attnames);
4945 : }
4946 : else
4947 478 : pubrinfo[j].pubrattrs = NULL;
4948 :
4949 : /* Decide whether we want to dump it */
4950 700 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4951 :
4952 700 : j++;
4953 : }
4954 :
4955 376 : PQclear(res);
4956 376 : destroyPQExpBuffer(query);
4957 : }
4958 :
4959 : /*
4960 : * dumpPublicationNamespace
4961 : * dump the definition of the given publication schema mapping.
4962 : */
4963 : static void
4964 198 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4965 : {
4966 198 : DumpOptions *dopt = fout->dopt;
4967 198 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4968 198 : PublicationInfo *pubinfo = pubsinfo->publication;
4969 : PQExpBuffer query;
4970 : char *tag;
4971 :
4972 : /* Do nothing if not dumping schema */
4973 198 : if (!dopt->dumpSchema)
4974 24 : return;
4975 :
4976 174 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4977 :
4978 174 : query = createPQExpBuffer();
4979 :
4980 174 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4981 174 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4982 :
4983 : /*
4984 : * There is no point in creating drop query as the drop is done by schema
4985 : * drop.
4986 : */
4987 174 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4988 174 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4989 174 : ARCHIVE_OPTS(.tag = tag,
4990 : .namespace = schemainfo->dobj.name,
4991 : .owner = pubinfo->rolname,
4992 : .description = "PUBLICATION TABLES IN SCHEMA",
4993 : .section = SECTION_POST_DATA,
4994 : .createStmt = query->data));
4995 :
4996 : /* These objects can't currently have comments or seclabels */
4997 :
4998 174 : free(tag);
4999 174 : destroyPQExpBuffer(query);
5000 : }
5001 :
5002 : /*
5003 : * dumpPublicationTable
5004 : * dump the definition of the given publication table mapping
5005 : */
5006 : static void
5007 568 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
5008 : {
5009 568 : DumpOptions *dopt = fout->dopt;
5010 568 : PublicationInfo *pubinfo = pubrinfo->publication;
5011 568 : TableInfo *tbinfo = pubrinfo->pubtable;
5012 : PQExpBuffer query;
5013 : char *tag;
5014 :
5015 : /* Do nothing if not dumping schema */
5016 568 : if (!dopt->dumpSchema)
5017 84 : return;
5018 :
5019 484 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5020 :
5021 484 : query = createPQExpBuffer();
5022 :
5023 484 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5024 484 : fmtId(pubinfo->dobj.name));
5025 484 : appendPQExpBuffer(query, " %s",
5026 484 : fmtQualifiedDumpable(tbinfo));
5027 :
5028 484 : if (pubrinfo->pubrattrs)
5029 154 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5030 :
5031 484 : if (pubrinfo->pubrelqual)
5032 : {
5033 : /*
5034 : * It's necessary to add parentheses around the expression because
5035 : * pg_get_expr won't supply the parentheses for things like WHERE
5036 : * TRUE.
5037 : */
5038 216 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5039 : }
5040 484 : appendPQExpBufferStr(query, ";\n");
5041 :
5042 : /*
5043 : * There is no point in creating a drop query as the drop is done by table
5044 : * drop. (If you think to change this, see also _printTocEntry().)
5045 : * Although this object doesn't really have ownership as such, set the
5046 : * owner field anyway to ensure that the command is run by the correct
5047 : * role at restore time.
5048 : */
5049 484 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5050 484 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5051 484 : ARCHIVE_OPTS(.tag = tag,
5052 : .namespace = tbinfo->dobj.namespace->dobj.name,
5053 : .owner = pubinfo->rolname,
5054 : .description = "PUBLICATION TABLE",
5055 : .section = SECTION_POST_DATA,
5056 : .createStmt = query->data));
5057 :
5058 : /* These objects can't currently have comments or seclabels */
5059 :
5060 484 : free(tag);
5061 484 : destroyPQExpBuffer(query);
5062 : }
5063 :
5064 : /*
5065 : * Is the currently connected user a superuser?
5066 : */
5067 : static bool
5068 374 : is_superuser(Archive *fout)
5069 : {
5070 374 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5071 : const char *val;
5072 :
5073 374 : val = PQparameterStatus(AH->connection, "is_superuser");
5074 :
5075 374 : if (val && strcmp(val, "on") == 0)
5076 368 : return true;
5077 :
5078 6 : return false;
5079 : }
5080 :
5081 : /*
5082 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5083 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5084 : * the setting query is effective only where available.
5085 : */
5086 : static void
5087 444 : set_restrict_relation_kind(Archive *AH, const char *value)
5088 : {
5089 444 : PQExpBuffer query = createPQExpBuffer();
5090 : PGresult *res;
5091 :
5092 444 : appendPQExpBuffer(query,
5093 : "SELECT set_config(name, '%s', false) "
5094 : "FROM pg_settings "
5095 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5096 : value);
5097 444 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5098 :
5099 444 : PQclear(res);
5100 444 : destroyPQExpBuffer(query);
5101 444 : }
5102 :
5103 : /*
5104 : * getSubscriptions
5105 : * get information about subscriptions
5106 : */
5107 : void
5108 376 : getSubscriptions(Archive *fout)
5109 : {
5110 376 : DumpOptions *dopt = fout->dopt;
5111 : PQExpBuffer query;
5112 : PGresult *res;
5113 : SubscriptionInfo *subinfo;
5114 : int i_tableoid;
5115 : int i_oid;
5116 : int i_subname;
5117 : int i_subowner;
5118 : int i_subbinary;
5119 : int i_substream;
5120 : int i_subtwophasestate;
5121 : int i_subdisableonerr;
5122 : int i_subpasswordrequired;
5123 : int i_subrunasowner;
5124 : int i_subconninfo;
5125 : int i_subslotname;
5126 : int i_subsynccommit;
5127 : int i_subpublications;
5128 : int i_suborigin;
5129 : int i_suboriginremotelsn;
5130 : int i_subenabled;
5131 : int i_subfailover;
5132 : int i_subretaindeadtuples;
5133 : int i_submaxretention;
5134 : int i,
5135 : ntups;
5136 :
5137 376 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5138 2 : return;
5139 :
5140 374 : if (!is_superuser(fout))
5141 : {
5142 : int n;
5143 :
5144 6 : res = ExecuteSqlQuery(fout,
5145 : "SELECT count(*) FROM pg_subscription "
5146 : "WHERE subdbid = (SELECT oid FROM pg_database"
5147 : " WHERE datname = current_database())",
5148 : PGRES_TUPLES_OK);
5149 6 : n = atoi(PQgetvalue(res, 0, 0));
5150 6 : if (n > 0)
5151 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5152 6 : PQclear(res);
5153 6 : return;
5154 : }
5155 :
5156 368 : query = createPQExpBuffer();
5157 :
5158 : /* Get the subscriptions in current database. */
5159 368 : appendPQExpBufferStr(query,
5160 : "SELECT s.tableoid, s.oid, s.subname,\n"
5161 : " s.subowner,\n"
5162 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5163 : " s.subpublications,\n");
5164 :
5165 368 : if (fout->remoteVersion >= 140000)
5166 368 : appendPQExpBufferStr(query, " s.subbinary,\n");
5167 : else
5168 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5169 :
5170 368 : if (fout->remoteVersion >= 140000)
5171 368 : appendPQExpBufferStr(query, " s.substream,\n");
5172 : else
5173 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5174 :
5175 368 : if (fout->remoteVersion >= 150000)
5176 368 : appendPQExpBufferStr(query,
5177 : " s.subtwophasestate,\n"
5178 : " s.subdisableonerr,\n");
5179 : else
5180 0 : appendPQExpBuffer(query,
5181 : " '%c' AS subtwophasestate,\n"
5182 : " false AS subdisableonerr,\n",
5183 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5184 :
5185 368 : if (fout->remoteVersion >= 160000)
5186 368 : appendPQExpBufferStr(query,
5187 : " s.subpasswordrequired,\n"
5188 : " s.subrunasowner,\n"
5189 : " s.suborigin,\n");
5190 : else
5191 0 : appendPQExpBuffer(query,
5192 : " 't' AS subpasswordrequired,\n"
5193 : " 't' AS subrunasowner,\n"
5194 : " '%s' AS suborigin,\n",
5195 : LOGICALREP_ORIGIN_ANY);
5196 :
5197 368 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5198 76 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5199 : " s.subenabled,\n");
5200 : else
5201 292 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5202 : " false AS subenabled,\n");
5203 :
5204 368 : if (fout->remoteVersion >= 170000)
5205 368 : appendPQExpBufferStr(query,
5206 : " s.subfailover,\n");
5207 : else
5208 0 : appendPQExpBufferStr(query,
5209 : " false AS subfailover,\n");
5210 :
5211 368 : if (fout->remoteVersion >= 190000)
5212 368 : appendPQExpBufferStr(query,
5213 : " s.subretaindeadtuples,\n");
5214 : else
5215 0 : appendPQExpBufferStr(query,
5216 : " false AS subretaindeadtuples,\n");
5217 :
5218 368 : if (fout->remoteVersion >= 190000)
5219 368 : appendPQExpBufferStr(query,
5220 : " s.submaxretention\n");
5221 : else
5222 0 : appendPQExpBuffer(query,
5223 : " 0 AS submaxretention\n");
5224 :
5225 368 : appendPQExpBufferStr(query,
5226 : "FROM pg_subscription s\n");
5227 :
5228 368 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5229 76 : appendPQExpBufferStr(query,
5230 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5231 : " ON o.external_id = 'pg_' || s.oid::text \n");
5232 :
5233 368 : appendPQExpBufferStr(query,
5234 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5235 : " WHERE datname = current_database())");
5236 :
5237 368 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5238 :
5239 368 : ntups = PQntuples(res);
5240 :
5241 : /*
5242 : * Get subscription fields. We don't include subskiplsn in the dump as
5243 : * after restoring the dump this value may no longer be relevant.
5244 : */
5245 368 : i_tableoid = PQfnumber(res, "tableoid");
5246 368 : i_oid = PQfnumber(res, "oid");
5247 368 : i_subname = PQfnumber(res, "subname");
5248 368 : i_subowner = PQfnumber(res, "subowner");
5249 368 : i_subenabled = PQfnumber(res, "subenabled");
5250 368 : i_subbinary = PQfnumber(res, "subbinary");
5251 368 : i_substream = PQfnumber(res, "substream");
5252 368 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5253 368 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5254 368 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5255 368 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5256 368 : i_subfailover = PQfnumber(res, "subfailover");
5257 368 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5258 368 : i_submaxretention = PQfnumber(res, "submaxretention");
5259 368 : i_subconninfo = PQfnumber(res, "subconninfo");
5260 368 : i_subslotname = PQfnumber(res, "subslotname");
5261 368 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5262 368 : i_subpublications = PQfnumber(res, "subpublications");
5263 368 : i_suborigin = PQfnumber(res, "suborigin");
5264 368 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5265 :
5266 368 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5267 :
5268 624 : for (i = 0; i < ntups; i++)
5269 : {
5270 256 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5271 256 : subinfo[i].dobj.catId.tableoid =
5272 256 : atooid(PQgetvalue(res, i, i_tableoid));
5273 256 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5274 256 : AssignDumpId(&subinfo[i].dobj);
5275 256 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5276 256 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5277 :
5278 256 : subinfo[i].subenabled =
5279 256 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5280 256 : subinfo[i].subbinary =
5281 256 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5282 256 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5283 256 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5284 256 : subinfo[i].subdisableonerr =
5285 256 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5286 256 : subinfo[i].subpasswordrequired =
5287 256 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5288 256 : subinfo[i].subrunasowner =
5289 256 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5290 256 : subinfo[i].subfailover =
5291 256 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5292 256 : subinfo[i].subretaindeadtuples =
5293 256 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5294 256 : subinfo[i].submaxretention =
5295 256 : atoi(PQgetvalue(res, i, i_submaxretention));
5296 512 : subinfo[i].subconninfo =
5297 256 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5298 256 : if (PQgetisnull(res, i, i_subslotname))
5299 0 : subinfo[i].subslotname = NULL;
5300 : else
5301 256 : subinfo[i].subslotname =
5302 256 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5303 512 : subinfo[i].subsynccommit =
5304 256 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5305 512 : subinfo[i].subpublications =
5306 256 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5307 256 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5308 256 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5309 254 : subinfo[i].suboriginremotelsn = NULL;
5310 : else
5311 2 : subinfo[i].suboriginremotelsn =
5312 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5313 :
5314 : /* Decide whether we want to dump it */
5315 256 : selectDumpableObject(&(subinfo[i].dobj), fout);
5316 : }
5317 368 : PQclear(res);
5318 :
5319 368 : destroyPQExpBuffer(query);
5320 : }
5321 :
5322 : /*
5323 : * getSubscriptionRelations
5324 : * Get information about subscription membership for dumpable relations. This
5325 : * will be used only in binary-upgrade mode for PG17 or later versions.
5326 : */
5327 : void
5328 376 : getSubscriptionRelations(Archive *fout)
5329 : {
5330 376 : DumpOptions *dopt = fout->dopt;
5331 376 : SubscriptionInfo *subinfo = NULL;
5332 : SubRelInfo *subrinfo;
5333 : PGresult *res;
5334 : int i_srsubid;
5335 : int i_srrelid;
5336 : int i_srsubstate;
5337 : int i_srsublsn;
5338 : int ntups;
5339 376 : Oid last_srsubid = InvalidOid;
5340 :
5341 376 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5342 76 : fout->remoteVersion < 170000)
5343 300 : return;
5344 :
5345 76 : res = ExecuteSqlQuery(fout,
5346 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5347 : "FROM pg_catalog.pg_subscription_rel "
5348 : "ORDER BY srsubid",
5349 : PGRES_TUPLES_OK);
5350 76 : ntups = PQntuples(res);
5351 76 : if (ntups == 0)
5352 74 : goto cleanup;
5353 :
5354 : /* Get pg_subscription_rel attributes */
5355 2 : i_srsubid = PQfnumber(res, "srsubid");
5356 2 : i_srrelid = PQfnumber(res, "srrelid");
5357 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5358 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5359 :
5360 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5361 8 : for (int i = 0; i < ntups; i++)
5362 : {
5363 6 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5364 6 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5365 : TableInfo *tblinfo;
5366 :
5367 : /*
5368 : * If we switched to a new subscription, check if the subscription
5369 : * exists.
5370 : */
5371 6 : if (cur_srsubid != last_srsubid)
5372 : {
5373 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5374 4 : if (subinfo == NULL)
5375 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5376 :
5377 4 : last_srsubid = cur_srsubid;
5378 : }
5379 :
5380 6 : tblinfo = findTableByOid(relid);
5381 6 : if (tblinfo == NULL)
5382 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5383 : relid);
5384 :
5385 : /* OK, make a DumpableObject for this relationship */
5386 6 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5387 6 : subrinfo[i].dobj.catId.tableoid = relid;
5388 6 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5389 6 : AssignDumpId(&subrinfo[i].dobj);
5390 6 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5391 6 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5392 6 : subrinfo[i].subinfo = subinfo;
5393 6 : subrinfo[i].tblinfo = tblinfo;
5394 6 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5395 6 : if (PQgetisnull(res, i, i_srsublsn))
5396 2 : subrinfo[i].srsublsn = NULL;
5397 : else
5398 4 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5399 :
5400 : /* Decide whether we want to dump it */
5401 6 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5402 : }
5403 :
5404 2 : cleanup:
5405 76 : PQclear(res);
5406 : }
5407 :
5408 : /*
5409 : * dumpSubscriptionTable
5410 : * Dump the definition of the given subscription table mapping. This will be
5411 : * used only in binary-upgrade mode for PG17 or later versions.
5412 : */
5413 : static void
5414 6 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5415 : {
5416 6 : DumpOptions *dopt = fout->dopt;
5417 6 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5418 : PQExpBuffer query;
5419 : char *tag;
5420 :
5421 : /* Do nothing if not dumping schema */
5422 6 : if (!dopt->dumpSchema)
5423 0 : return;
5424 :
5425 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5426 :
5427 6 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5428 :
5429 6 : query = createPQExpBuffer();
5430 :
5431 6 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5432 : {
5433 : /*
5434 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5435 : * to pg_subscription_rel table. This will be used only in
5436 : * binary-upgrade mode.
5437 : */
5438 6 : appendPQExpBufferStr(query,
5439 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5440 6 : appendPQExpBufferStr(query,
5441 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5442 6 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5443 6 : appendPQExpBuffer(query,
5444 : ", %u, '%c'",
5445 6 : subrinfo->tblinfo->dobj.catId.oid,
5446 6 : subrinfo->srsubstate);
5447 :
5448 6 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5449 4 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5450 : else
5451 2 : appendPQExpBufferStr(query, ", NULL");
5452 :
5453 6 : appendPQExpBufferStr(query, ");\n");
5454 : }
5455 :
5456 : /*
5457 : * There is no point in creating a drop query as the drop is done by table
5458 : * drop. (If you think to change this, see also _printTocEntry().)
5459 : * Although this object doesn't really have ownership as such, set the
5460 : * owner field anyway to ensure that the command is run by the correct
5461 : * role at restore time.
5462 : */
5463 6 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5464 6 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5465 6 : ARCHIVE_OPTS(.tag = tag,
5466 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5467 : .owner = subinfo->rolname,
5468 : .description = "SUBSCRIPTION TABLE",
5469 : .section = SECTION_POST_DATA,
5470 : .createStmt = query->data));
5471 :
5472 : /* These objects can't currently have comments or seclabels */
5473 :
5474 6 : free(tag);
5475 6 : destroyPQExpBuffer(query);
5476 : }
5477 :
5478 : /*
5479 : * dumpSubscription
5480 : * dump the definition of the given subscription
5481 : */
5482 : static void
5483 220 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5484 : {
5485 220 : DumpOptions *dopt = fout->dopt;
5486 : PQExpBuffer delq;
5487 : PQExpBuffer query;
5488 : PQExpBuffer publications;
5489 : char *qsubname;
5490 220 : char **pubnames = NULL;
5491 220 : int npubnames = 0;
5492 : int i;
5493 :
5494 : /* Do nothing if not dumping schema */
5495 220 : if (!dopt->dumpSchema)
5496 36 : return;
5497 :
5498 184 : delq = createPQExpBuffer();
5499 184 : query = createPQExpBuffer();
5500 :
5501 184 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5502 :
5503 184 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5504 : qsubname);
5505 :
5506 184 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5507 : qsubname);
5508 184 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5509 :
5510 : /* Build list of quoted publications and append them to query. */
5511 184 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5512 0 : pg_fatal("could not parse %s array", "subpublications");
5513 :
5514 184 : publications = createPQExpBuffer();
5515 368 : for (i = 0; i < npubnames; i++)
5516 : {
5517 184 : if (i > 0)
5518 0 : appendPQExpBufferStr(publications, ", ");
5519 :
5520 184 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5521 : }
5522 :
5523 184 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5524 184 : if (subinfo->subslotname)
5525 184 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5526 : else
5527 0 : appendPQExpBufferStr(query, "NONE");
5528 :
5529 184 : if (subinfo->subbinary)
5530 0 : appendPQExpBufferStr(query, ", binary = true");
5531 :
5532 184 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5533 60 : appendPQExpBufferStr(query, ", streaming = on");
5534 124 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5535 64 : appendPQExpBufferStr(query, ", streaming = parallel");
5536 : else
5537 60 : appendPQExpBufferStr(query, ", streaming = off");
5538 :
5539 184 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5540 0 : appendPQExpBufferStr(query, ", two_phase = on");
5541 :
5542 184 : if (subinfo->subdisableonerr)
5543 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5544 :
5545 184 : if (!subinfo->subpasswordrequired)
5546 0 : appendPQExpBufferStr(query, ", password_required = false");
5547 :
5548 184 : if (subinfo->subrunasowner)
5549 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5550 :
5551 184 : if (subinfo->subfailover)
5552 2 : appendPQExpBufferStr(query, ", failover = true");
5553 :
5554 184 : if (subinfo->subretaindeadtuples)
5555 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5556 :
5557 184 : if (subinfo->submaxretention)
5558 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5559 :
5560 184 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5561 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5562 :
5563 184 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5564 60 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5565 :
5566 184 : appendPQExpBufferStr(query, ");\n");
5567 :
5568 : /*
5569 : * In binary-upgrade mode, we allow the replication to continue after the
5570 : * upgrade.
5571 : */
5572 184 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5573 : {
5574 10 : if (subinfo->suboriginremotelsn)
5575 : {
5576 : /*
5577 : * Preserve the remote_lsn for the subscriber's replication
5578 : * origin. This value is required to start the replication from
5579 : * the position before the upgrade. This value will be stale if
5580 : * the publisher gets upgraded before the subscriber node.
5581 : * However, this shouldn't be a problem as the upgrade of the
5582 : * publisher ensures that all the transactions were replicated
5583 : * before upgrading it.
5584 : */
5585 2 : appendPQExpBufferStr(query,
5586 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5587 2 : appendPQExpBufferStr(query,
5588 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5589 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5590 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5591 : }
5592 :
5593 10 : if (subinfo->subenabled)
5594 : {
5595 : /*
5596 : * Enable the subscription to allow the replication to continue
5597 : * after the upgrade.
5598 : */
5599 2 : appendPQExpBufferStr(query,
5600 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5601 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5602 : }
5603 : }
5604 :
5605 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5606 184 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5607 184 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5608 : .owner = subinfo->rolname,
5609 : .description = "SUBSCRIPTION",
5610 : .section = SECTION_POST_DATA,
5611 : .createStmt = query->data,
5612 : .dropStmt = delq->data));
5613 :
5614 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5615 60 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5616 60 : NULL, subinfo->rolname,
5617 60 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5618 :
5619 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5620 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5621 0 : NULL, subinfo->rolname,
5622 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5623 :
5624 184 : destroyPQExpBuffer(publications);
5625 184 : free(pubnames);
5626 :
5627 184 : destroyPQExpBuffer(delq);
5628 184 : destroyPQExpBuffer(query);
5629 184 : free(qsubname);
5630 : }
5631 :
5632 : /*
5633 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5634 : * the object needs.
5635 : */
5636 : static void
5637 10152 : append_depends_on_extension(Archive *fout,
5638 : PQExpBuffer create,
5639 : const DumpableObject *dobj,
5640 : const char *catalog,
5641 : const char *keyword,
5642 : const char *objname)
5643 : {
5644 10152 : if (dobj->depends_on_ext)
5645 : {
5646 : char *nm;
5647 : PGresult *res;
5648 : PQExpBuffer query;
5649 : int ntups;
5650 : int i_extname;
5651 : int i;
5652 :
5653 : /* dodge fmtId() non-reentrancy */
5654 84 : nm = pg_strdup(objname);
5655 :
5656 84 : query = createPQExpBuffer();
5657 84 : appendPQExpBuffer(query,
5658 : "SELECT e.extname "
5659 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5660 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5661 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5662 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5663 : catalog,
5664 84 : dobj->catId.oid);
5665 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5666 84 : ntups = PQntuples(res);
5667 84 : i_extname = PQfnumber(res, "extname");
5668 168 : for (i = 0; i < ntups; i++)
5669 : {
5670 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5671 : keyword, nm,
5672 84 : fmtId(PQgetvalue(res, i, i_extname)));
5673 : }
5674 :
5675 84 : PQclear(res);
5676 84 : destroyPQExpBuffer(query);
5677 84 : pg_free(nm);
5678 : }
5679 10152 : }
5680 :
5681 : static Oid
5682 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5683 : {
5684 : /*
5685 : * If the old version didn't assign an array type, but the new version
5686 : * does, we must select an unused type OID to assign. This currently only
5687 : * happens for domains, when upgrading pre-v11 to v11 and up.
5688 : *
5689 : * Note: local state here is kind of ugly, but we must have some, since we
5690 : * mustn't choose the same unused OID more than once.
5691 : */
5692 : static Oid next_possible_free_oid = FirstNormalObjectId;
5693 : PGresult *res;
5694 : bool is_dup;
5695 :
5696 : do
5697 : {
5698 0 : ++next_possible_free_oid;
5699 0 : printfPQExpBuffer(upgrade_query,
5700 : "SELECT EXISTS(SELECT 1 "
5701 : "FROM pg_catalog.pg_type "
5702 : "WHERE oid = '%u'::pg_catalog.oid);",
5703 : next_possible_free_oid);
5704 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5705 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5706 0 : PQclear(res);
5707 0 : } while (is_dup);
5708 :
5709 0 : return next_possible_free_oid;
5710 : }
5711 :
5712 : static void
5713 1892 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5714 : PQExpBuffer upgrade_buffer,
5715 : Oid pg_type_oid,
5716 : bool force_array_type,
5717 : bool include_multirange_type)
5718 : {
5719 1892 : PQExpBuffer upgrade_query = createPQExpBuffer();
5720 : PGresult *res;
5721 : Oid pg_type_array_oid;
5722 : Oid pg_type_multirange_oid;
5723 : Oid pg_type_multirange_array_oid;
5724 : TypeInfo *tinfo;
5725 :
5726 1892 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5727 1892 : appendPQExpBuffer(upgrade_buffer,
5728 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5729 : pg_type_oid);
5730 :
5731 1892 : tinfo = findTypeByOid(pg_type_oid);
5732 1892 : if (tinfo)
5733 1892 : pg_type_array_oid = tinfo->typarray;
5734 : else
5735 0 : pg_type_array_oid = InvalidOid;
5736 :
5737 1892 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5738 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5739 :
5740 1892 : if (OidIsValid(pg_type_array_oid))
5741 : {
5742 1888 : appendPQExpBufferStr(upgrade_buffer,
5743 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5744 1888 : appendPQExpBuffer(upgrade_buffer,
5745 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5746 : pg_type_array_oid);
5747 : }
5748 :
5749 : /*
5750 : * Pre-set the multirange type oid and its own array type oid.
5751 : */
5752 1892 : if (include_multirange_type)
5753 : {
5754 16 : if (fout->remoteVersion >= 140000)
5755 : {
5756 16 : printfPQExpBuffer(upgrade_query,
5757 : "SELECT t.oid, t.typarray "
5758 : "FROM pg_catalog.pg_type t "
5759 : "JOIN pg_catalog.pg_range r "
5760 : "ON t.oid = r.rngmultitypid "
5761 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5762 : pg_type_oid);
5763 :
5764 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5765 :
5766 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5767 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5768 :
5769 16 : PQclear(res);
5770 : }
5771 : else
5772 : {
5773 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5774 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5775 : }
5776 :
5777 16 : appendPQExpBufferStr(upgrade_buffer,
5778 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5779 16 : appendPQExpBuffer(upgrade_buffer,
5780 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5781 : pg_type_multirange_oid);
5782 16 : appendPQExpBufferStr(upgrade_buffer,
5783 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5784 16 : appendPQExpBuffer(upgrade_buffer,
5785 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5786 : pg_type_multirange_array_oid);
5787 : }
5788 :
5789 1892 : destroyPQExpBuffer(upgrade_query);
5790 1892 : }
5791 :
5792 : static void
5793 1742 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5794 : PQExpBuffer upgrade_buffer,
5795 : const TableInfo *tbinfo)
5796 : {
5797 1742 : Oid pg_type_oid = tbinfo->reltype;
5798 :
5799 1742 : if (OidIsValid(pg_type_oid))
5800 1742 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5801 : pg_type_oid, false, false);
5802 1742 : }
5803 :
5804 : /*
5805 : * bsearch() comparator for BinaryUpgradeClassOidItem
5806 : */
5807 : static int
5808 24932 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5809 : {
5810 24932 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5811 24932 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5812 :
5813 24932 : return pg_cmp_u32(v1.oid, v2.oid);
5814 : }
5815 :
5816 : /*
5817 : * collectBinaryUpgradeClassOids
5818 : *
5819 : * Construct a table of pg_class information required for
5820 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5821 : * lookup.
5822 : */
5823 : static void
5824 76 : collectBinaryUpgradeClassOids(Archive *fout)
5825 : {
5826 : PGresult *res;
5827 : const char *query;
5828 :
5829 76 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5830 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5831 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5832 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5833 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5834 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5835 : "ORDER BY c.oid;";
5836 :
5837 76 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5838 :
5839 76 : nbinaryUpgradeClassOids = PQntuples(res);
5840 76 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5841 76 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5842 :
5843 35402 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5844 : {
5845 35326 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5846 35326 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5847 35326 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5848 35326 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5849 35326 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5850 35326 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5851 35326 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5852 : }
5853 :
5854 76 : PQclear(res);
5855 76 : }
5856 :
5857 : static void
5858 2526 : binary_upgrade_set_pg_class_oids(Archive *fout,
5859 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5860 : {
5861 2526 : BinaryUpgradeClassOidItem key = {0};
5862 : BinaryUpgradeClassOidItem *entry;
5863 :
5864 : Assert(binaryUpgradeClassOids);
5865 :
5866 : /*
5867 : * Preserve the OID and relfilenumber of the table, table's index, table's
5868 : * toast table and toast table's index if any.
5869 : *
5870 : * One complexity is that the current table definition might not require
5871 : * the creation of a TOAST table, but the old database might have a TOAST
5872 : * table that was created earlier, before some wide columns were dropped.
5873 : * By setting the TOAST oid we force creation of the TOAST heap and index
5874 : * by the new backend, so we can copy the files during binary upgrade
5875 : * without worrying about this case.
5876 : */
5877 2526 : key.oid = pg_class_oid;
5878 2526 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5879 : sizeof(BinaryUpgradeClassOidItem),
5880 : BinaryUpgradeClassOidItemCmp);
5881 :
5882 2526 : appendPQExpBufferStr(upgrade_buffer,
5883 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5884 :
5885 2526 : if (entry->relkind != RELKIND_INDEX &&
5886 1966 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5887 : {
5888 1910 : appendPQExpBuffer(upgrade_buffer,
5889 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5890 : pg_class_oid);
5891 :
5892 : /*
5893 : * Not every relation has storage. Also, in a pre-v12 database,
5894 : * partitioned tables have a relfilenumber, which should not be
5895 : * preserved when upgrading.
5896 : */
5897 1910 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5898 1582 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5899 1582 : appendPQExpBuffer(upgrade_buffer,
5900 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5901 : entry->relfilenumber);
5902 :
5903 : /*
5904 : * In a pre-v12 database, partitioned tables might be marked as having
5905 : * toast tables, but we should ignore them if so.
5906 : */
5907 1910 : if (OidIsValid(entry->toast_oid) &&
5908 554 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5909 : {
5910 554 : appendPQExpBuffer(upgrade_buffer,
5911 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5912 : entry->toast_oid);
5913 554 : appendPQExpBuffer(upgrade_buffer,
5914 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5915 : entry->toast_relfilenumber);
5916 :
5917 : /* every toast table has an index */
5918 554 : appendPQExpBuffer(upgrade_buffer,
5919 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5920 : entry->toast_index_oid);
5921 554 : appendPQExpBuffer(upgrade_buffer,
5922 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5923 : entry->toast_index_relfilenumber);
5924 : }
5925 : }
5926 : else
5927 : {
5928 : /* Preserve the OID and relfilenumber of the index */
5929 616 : appendPQExpBuffer(upgrade_buffer,
5930 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5931 : pg_class_oid);
5932 616 : appendPQExpBuffer(upgrade_buffer,
5933 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5934 : entry->relfilenumber);
5935 : }
5936 :
5937 2526 : appendPQExpBufferChar(upgrade_buffer, '\n');
5938 2526 : }
5939 :
5940 : /*
5941 : * If the DumpableObject is a member of an extension, add a suitable
5942 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5943 : *
5944 : * For somewhat historical reasons, objname should already be quoted,
5945 : * but not objnamespace (if any).
5946 : */
5947 : static void
5948 3026 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5949 : const DumpableObject *dobj,
5950 : const char *objtype,
5951 : const char *objname,
5952 : const char *objnamespace)
5953 : {
5954 3026 : DumpableObject *extobj = NULL;
5955 : int i;
5956 :
5957 3026 : if (!dobj->ext_member)
5958 2984 : return;
5959 :
5960 : /*
5961 : * Find the parent extension. We could avoid this search if we wanted to
5962 : * add a link field to DumpableObject, but the space costs of that would
5963 : * be considerable. We assume that member objects could only have a
5964 : * direct dependency on their own extension, not any others.
5965 : */
5966 42 : for (i = 0; i < dobj->nDeps; i++)
5967 : {
5968 42 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5969 42 : if (extobj && extobj->objType == DO_EXTENSION)
5970 42 : break;
5971 0 : extobj = NULL;
5972 : }
5973 42 : if (extobj == NULL)
5974 0 : pg_fatal("could not find parent extension for %s %s",
5975 : objtype, objname);
5976 :
5977 42 : appendPQExpBufferStr(upgrade_buffer,
5978 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5979 42 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5980 42 : fmtId(extobj->name),
5981 : objtype);
5982 42 : if (objnamespace && *objnamespace)
5983 36 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5984 42 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5985 : }
5986 :
5987 : /*
5988 : * getNamespaces:
5989 : * get information about all namespaces in the system catalogs
5990 : */
5991 : void
5992 378 : getNamespaces(Archive *fout)
5993 : {
5994 : PGresult *res;
5995 : int ntups;
5996 : int i;
5997 : PQExpBuffer query;
5998 : NamespaceInfo *nsinfo;
5999 : int i_tableoid;
6000 : int i_oid;
6001 : int i_nspname;
6002 : int i_nspowner;
6003 : int i_nspacl;
6004 : int i_acldefault;
6005 :
6006 378 : query = createPQExpBuffer();
6007 :
6008 : /*
6009 : * we fetch all namespaces including system ones, so that every object we
6010 : * read in can be linked to a containing namespace.
6011 : */
6012 378 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6013 : "n.nspowner, "
6014 : "n.nspacl, "
6015 : "acldefault('n', n.nspowner) AS acldefault "
6016 : "FROM pg_namespace n");
6017 :
6018 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6019 :
6020 378 : ntups = PQntuples(res);
6021 :
6022 378 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6023 :
6024 378 : i_tableoid = PQfnumber(res, "tableoid");
6025 378 : i_oid = PQfnumber(res, "oid");
6026 378 : i_nspname = PQfnumber(res, "nspname");
6027 378 : i_nspowner = PQfnumber(res, "nspowner");
6028 378 : i_nspacl = PQfnumber(res, "nspacl");
6029 378 : i_acldefault = PQfnumber(res, "acldefault");
6030 :
6031 3294 : for (i = 0; i < ntups; i++)
6032 : {
6033 : const char *nspowner;
6034 :
6035 2916 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6036 2916 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6037 2916 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6038 2916 : AssignDumpId(&nsinfo[i].dobj);
6039 2916 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6040 2916 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6041 2916 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6042 2916 : nsinfo[i].dacl.privtype = 0;
6043 2916 : nsinfo[i].dacl.initprivs = NULL;
6044 2916 : nspowner = PQgetvalue(res, i, i_nspowner);
6045 2916 : nsinfo[i].nspowner = atooid(nspowner);
6046 2916 : nsinfo[i].rolname = getRoleName(nspowner);
6047 :
6048 : /* Decide whether to dump this namespace */
6049 2916 : selectDumpableNamespace(&nsinfo[i], fout);
6050 :
6051 : /* Mark whether namespace has an ACL */
6052 2916 : if (!PQgetisnull(res, i, i_nspacl))
6053 1258 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6054 :
6055 : /*
6056 : * We ignore any pg_init_privs.initprivs entry for the public schema
6057 : * and assume a predetermined default, for several reasons. First,
6058 : * dropping and recreating the schema removes its pg_init_privs entry,
6059 : * but an empty destination database starts with this ACL nonetheless.
6060 : * Second, we support dump/reload of public schema ownership changes.
6061 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6062 : * initprivs continues to reflect the initial owner. Hence,
6063 : * synthesize the value that nspacl will have after the restore's
6064 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6065 : * match the source's ACL, even if the latter was an initdb-default
6066 : * ACL, which changed in v15. An upgrade pulls in changes to most
6067 : * system object ACLs that the DBA had not customized. We've made the
6068 : * public schema depart from that, because changing its ACL so easily
6069 : * breaks applications.
6070 : */
6071 2916 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6072 : {
6073 370 : PQExpBuffer aclarray = createPQExpBuffer();
6074 370 : PQExpBuffer aclitem = createPQExpBuffer();
6075 :
6076 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6077 370 : appendPQExpBufferChar(aclarray, '{');
6078 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6079 370 : appendPQExpBufferStr(aclitem, "=UC/");
6080 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6081 370 : appendPGArray(aclarray, aclitem->data);
6082 370 : resetPQExpBuffer(aclitem);
6083 370 : appendPQExpBufferStr(aclitem, "=U/");
6084 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6085 370 : appendPGArray(aclarray, aclitem->data);
6086 370 : appendPQExpBufferChar(aclarray, '}');
6087 :
6088 370 : nsinfo[i].dacl.privtype = 'i';
6089 370 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6090 370 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6091 :
6092 370 : destroyPQExpBuffer(aclarray);
6093 370 : destroyPQExpBuffer(aclitem);
6094 : }
6095 : }
6096 :
6097 378 : PQclear(res);
6098 378 : destroyPQExpBuffer(query);
6099 378 : }
6100 :
6101 : /*
6102 : * findNamespace:
6103 : * given a namespace OID, look up the info read by getNamespaces
6104 : */
6105 : static NamespaceInfo *
6106 1191834 : findNamespace(Oid nsoid)
6107 : {
6108 : NamespaceInfo *nsinfo;
6109 :
6110 1191834 : nsinfo = findNamespaceByOid(nsoid);
6111 1191834 : if (nsinfo == NULL)
6112 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6113 1191834 : return nsinfo;
6114 : }
6115 :
6116 : /*
6117 : * getExtensions:
6118 : * read all extensions in the system catalogs and return them in the
6119 : * ExtensionInfo* structure
6120 : *
6121 : * numExtensions is set to the number of extensions read in
6122 : */
6123 : ExtensionInfo *
6124 378 : getExtensions(Archive *fout, int *numExtensions)
6125 : {
6126 378 : DumpOptions *dopt = fout->dopt;
6127 : PGresult *res;
6128 : int ntups;
6129 : int i;
6130 : PQExpBuffer query;
6131 378 : ExtensionInfo *extinfo = NULL;
6132 : int i_tableoid;
6133 : int i_oid;
6134 : int i_extname;
6135 : int i_nspname;
6136 : int i_extrelocatable;
6137 : int i_extversion;
6138 : int i_extconfig;
6139 : int i_extcondition;
6140 :
6141 378 : query = createPQExpBuffer();
6142 :
6143 378 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6144 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6145 : "FROM pg_extension x "
6146 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6147 :
6148 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6149 :
6150 378 : ntups = PQntuples(res);
6151 378 : if (ntups == 0)
6152 0 : goto cleanup;
6153 :
6154 378 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6155 :
6156 378 : i_tableoid = PQfnumber(res, "tableoid");
6157 378 : i_oid = PQfnumber(res, "oid");
6158 378 : i_extname = PQfnumber(res, "extname");
6159 378 : i_nspname = PQfnumber(res, "nspname");
6160 378 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6161 378 : i_extversion = PQfnumber(res, "extversion");
6162 378 : i_extconfig = PQfnumber(res, "extconfig");
6163 378 : i_extcondition = PQfnumber(res, "extcondition");
6164 :
6165 816 : for (i = 0; i < ntups; i++)
6166 : {
6167 438 : extinfo[i].dobj.objType = DO_EXTENSION;
6168 438 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6169 438 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6170 438 : AssignDumpId(&extinfo[i].dobj);
6171 438 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6172 438 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6173 438 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6174 438 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6175 438 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6176 438 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6177 :
6178 : /* Decide whether we want to dump it */
6179 438 : selectDumpableExtension(&(extinfo[i]), dopt);
6180 : }
6181 :
6182 378 : cleanup:
6183 378 : PQclear(res);
6184 378 : destroyPQExpBuffer(query);
6185 :
6186 378 : *numExtensions = ntups;
6187 :
6188 378 : return extinfo;
6189 : }
6190 :
6191 : /*
6192 : * getTypes:
6193 : * get information about all types in the system catalogs
6194 : *
6195 : * NB: this must run after getFuncs() because we assume we can do
6196 : * findFuncByOid().
6197 : */
6198 : void
6199 376 : getTypes(Archive *fout)
6200 : {
6201 : PGresult *res;
6202 : int ntups;
6203 : int i;
6204 376 : PQExpBuffer query = createPQExpBuffer();
6205 : TypeInfo *tyinfo;
6206 : ShellTypeInfo *stinfo;
6207 : int i_tableoid;
6208 : int i_oid;
6209 : int i_typname;
6210 : int i_typnamespace;
6211 : int i_typacl;
6212 : int i_acldefault;
6213 : int i_typowner;
6214 : int i_typelem;
6215 : int i_typrelid;
6216 : int i_typrelkind;
6217 : int i_typtype;
6218 : int i_typisdefined;
6219 : int i_isarray;
6220 : int i_typarray;
6221 :
6222 : /*
6223 : * we include even the built-in types because those may be used as array
6224 : * elements by user-defined types
6225 : *
6226 : * we filter out the built-in types when we dump out the types
6227 : *
6228 : * same approach for undefined (shell) types and array types
6229 : *
6230 : * Note: as of 8.3 we can reliably detect whether a type is an
6231 : * auto-generated array type by checking the element type's typarray.
6232 : * (Before that the test is capable of generating false positives.) We
6233 : * still check for name beginning with '_', though, so as to avoid the
6234 : * cost of the subselect probe for all standard types. This would have to
6235 : * be revisited if the backend ever allows renaming of array types.
6236 : */
6237 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6238 : "typnamespace, typacl, "
6239 : "acldefault('T', typowner) AS acldefault, "
6240 : "typowner, "
6241 : "typelem, typrelid, typarray, "
6242 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6243 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6244 : "typtype, typisdefined, "
6245 : "typname[0] = '_' AND typelem != 0 AND "
6246 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6247 : "FROM pg_type");
6248 :
6249 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6250 :
6251 376 : ntups = PQntuples(res);
6252 :
6253 376 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6254 :
6255 376 : i_tableoid = PQfnumber(res, "tableoid");
6256 376 : i_oid = PQfnumber(res, "oid");
6257 376 : i_typname = PQfnumber(res, "typname");
6258 376 : i_typnamespace = PQfnumber(res, "typnamespace");
6259 376 : i_typacl = PQfnumber(res, "typacl");
6260 376 : i_acldefault = PQfnumber(res, "acldefault");
6261 376 : i_typowner = PQfnumber(res, "typowner");
6262 376 : i_typelem = PQfnumber(res, "typelem");
6263 376 : i_typrelid = PQfnumber(res, "typrelid");
6264 376 : i_typrelkind = PQfnumber(res, "typrelkind");
6265 376 : i_typtype = PQfnumber(res, "typtype");
6266 376 : i_typisdefined = PQfnumber(res, "typisdefined");
6267 376 : i_isarray = PQfnumber(res, "isarray");
6268 376 : i_typarray = PQfnumber(res, "typarray");
6269 :
6270 273280 : for (i = 0; i < ntups; i++)
6271 : {
6272 272904 : tyinfo[i].dobj.objType = DO_TYPE;
6273 272904 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6274 272904 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6275 272904 : AssignDumpId(&tyinfo[i].dobj);
6276 272904 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6277 545808 : tyinfo[i].dobj.namespace =
6278 272904 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6279 272904 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6280 272904 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6281 272904 : tyinfo[i].dacl.privtype = 0;
6282 272904 : tyinfo[i].dacl.initprivs = NULL;
6283 272904 : tyinfo[i].ftypname = NULL; /* may get filled later */
6284 272904 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6285 272904 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6286 272904 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6287 272904 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6288 272904 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6289 272904 : tyinfo[i].shellType = NULL;
6290 :
6291 272904 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6292 272800 : tyinfo[i].isDefined = true;
6293 : else
6294 104 : tyinfo[i].isDefined = false;
6295 :
6296 272904 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6297 130948 : tyinfo[i].isArray = true;
6298 : else
6299 141956 : tyinfo[i].isArray = false;
6300 :
6301 272904 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6302 :
6303 272904 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6304 2520 : tyinfo[i].isMultirange = true;
6305 : else
6306 270384 : tyinfo[i].isMultirange = false;
6307 :
6308 : /* Decide whether we want to dump it */
6309 272904 : selectDumpableType(&tyinfo[i], fout);
6310 :
6311 : /* Mark whether type has an ACL */
6312 272904 : if (!PQgetisnull(res, i, i_typacl))
6313 410 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6314 :
6315 : /*
6316 : * If it's a domain, fetch info about its constraints, if any
6317 : */
6318 272904 : tyinfo[i].nDomChecks = 0;
6319 272904 : tyinfo[i].domChecks = NULL;
6320 272904 : tyinfo[i].notnull = NULL;
6321 272904 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6322 29974 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6323 316 : getDomainConstraints(fout, &(tyinfo[i]));
6324 :
6325 : /*
6326 : * If it's a base type, make a DumpableObject representing a shell
6327 : * definition of the type. We will need to dump that ahead of the I/O
6328 : * functions for the type. Similarly, range types need a shell
6329 : * definition in case they have a canonicalize function.
6330 : *
6331 : * Note: the shell type doesn't have a catId. You might think it
6332 : * should copy the base type's catId, but then it might capture the
6333 : * pg_depend entries for the type, which we don't want.
6334 : */
6335 272904 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6336 29974 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6337 14554 : tyinfo[i].typtype == TYPTYPE_RANGE))
6338 : {
6339 15668 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6340 15668 : stinfo->dobj.objType = DO_SHELL_TYPE;
6341 15668 : stinfo->dobj.catId = nilCatalogId;
6342 15668 : AssignDumpId(&stinfo->dobj);
6343 15668 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6344 15668 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6345 15668 : stinfo->baseType = &(tyinfo[i]);
6346 15668 : tyinfo[i].shellType = stinfo;
6347 :
6348 : /*
6349 : * Initially mark the shell type as not to be dumped. We'll only
6350 : * dump it if the I/O or canonicalize functions need to be dumped;
6351 : * this is taken care of while sorting dependencies.
6352 : */
6353 15668 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6354 : }
6355 : }
6356 :
6357 376 : PQclear(res);
6358 :
6359 376 : destroyPQExpBuffer(query);
6360 376 : }
6361 :
6362 : /*
6363 : * getOperators:
6364 : * get information about all operators in the system catalogs
6365 : */
6366 : void
6367 376 : getOperators(Archive *fout)
6368 : {
6369 : PGresult *res;
6370 : int ntups;
6371 : int i;
6372 376 : PQExpBuffer query = createPQExpBuffer();
6373 : OprInfo *oprinfo;
6374 : int i_tableoid;
6375 : int i_oid;
6376 : int i_oprname;
6377 : int i_oprnamespace;
6378 : int i_oprowner;
6379 : int i_oprkind;
6380 : int i_oprleft;
6381 : int i_oprright;
6382 : int i_oprcode;
6383 :
6384 : /*
6385 : * find all operators, including builtin operators; we filter out
6386 : * system-defined operators at dump-out time.
6387 : */
6388 :
6389 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6390 : "oprnamespace, "
6391 : "oprowner, "
6392 : "oprkind, "
6393 : "oprleft, "
6394 : "oprright, "
6395 : "oprcode::oid AS oprcode "
6396 : "FROM pg_operator");
6397 :
6398 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6399 :
6400 376 : ntups = PQntuples(res);
6401 :
6402 376 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6403 :
6404 376 : i_tableoid = PQfnumber(res, "tableoid");
6405 376 : i_oid = PQfnumber(res, "oid");
6406 376 : i_oprname = PQfnumber(res, "oprname");
6407 376 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6408 376 : i_oprowner = PQfnumber(res, "oprowner");
6409 376 : i_oprkind = PQfnumber(res, "oprkind");
6410 376 : i_oprleft = PQfnumber(res, "oprleft");
6411 376 : i_oprright = PQfnumber(res, "oprright");
6412 376 : i_oprcode = PQfnumber(res, "oprcode");
6413 :
6414 303340 : for (i = 0; i < ntups; i++)
6415 : {
6416 302964 : oprinfo[i].dobj.objType = DO_OPERATOR;
6417 302964 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6418 302964 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6419 302964 : AssignDumpId(&oprinfo[i].dobj);
6420 302964 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6421 605928 : oprinfo[i].dobj.namespace =
6422 302964 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6423 302964 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6424 302964 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6425 302964 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6426 302964 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6427 302964 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6428 :
6429 : /* Decide whether we want to dump it */
6430 302964 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6431 : }
6432 :
6433 376 : PQclear(res);
6434 :
6435 376 : destroyPQExpBuffer(query);
6436 376 : }
6437 :
6438 : /*
6439 : * getCollations:
6440 : * get information about all collations in the system catalogs
6441 : */
6442 : void
6443 376 : getCollations(Archive *fout)
6444 : {
6445 : PGresult *res;
6446 : int ntups;
6447 : int i;
6448 : PQExpBuffer query;
6449 : CollInfo *collinfo;
6450 : int i_tableoid;
6451 : int i_oid;
6452 : int i_collname;
6453 : int i_collnamespace;
6454 : int i_collowner;
6455 : int i_collencoding;
6456 :
6457 376 : query = createPQExpBuffer();
6458 :
6459 : /*
6460 : * find all collations, including builtin collations; we filter out
6461 : * system-defined collations at dump-out time.
6462 : */
6463 :
6464 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6465 : "collnamespace, "
6466 : "collowner, "
6467 : "collencoding "
6468 : "FROM pg_collation");
6469 :
6470 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6471 :
6472 376 : ntups = PQntuples(res);
6473 :
6474 376 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6475 :
6476 376 : i_tableoid = PQfnumber(res, "tableoid");
6477 376 : i_oid = PQfnumber(res, "oid");
6478 376 : i_collname = PQfnumber(res, "collname");
6479 376 : i_collnamespace = PQfnumber(res, "collnamespace");
6480 376 : i_collowner = PQfnumber(res, "collowner");
6481 376 : i_collencoding = PQfnumber(res, "collencoding");
6482 :
6483 307414 : for (i = 0; i < ntups; i++)
6484 : {
6485 307038 : collinfo[i].dobj.objType = DO_COLLATION;
6486 307038 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6487 307038 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6488 307038 : AssignDumpId(&collinfo[i].dobj);
6489 307038 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6490 614076 : collinfo[i].dobj.namespace =
6491 307038 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6492 307038 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6493 307038 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6494 :
6495 : /* Decide whether we want to dump it */
6496 307038 : selectDumpableObject(&(collinfo[i].dobj), fout);
6497 : }
6498 :
6499 376 : PQclear(res);
6500 :
6501 376 : destroyPQExpBuffer(query);
6502 376 : }
6503 :
6504 : /*
6505 : * getConversions:
6506 : * get information about all conversions in the system catalogs
6507 : */
6508 : void
6509 376 : getConversions(Archive *fout)
6510 : {
6511 : PGresult *res;
6512 : int ntups;
6513 : int i;
6514 : PQExpBuffer query;
6515 : ConvInfo *convinfo;
6516 : int i_tableoid;
6517 : int i_oid;
6518 : int i_conname;
6519 : int i_connamespace;
6520 : int i_conowner;
6521 :
6522 376 : query = createPQExpBuffer();
6523 :
6524 : /*
6525 : * find all conversions, including builtin conversions; we filter out
6526 : * system-defined conversions at dump-out time.
6527 : */
6528 :
6529 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6530 : "connamespace, "
6531 : "conowner "
6532 : "FROM pg_conversion");
6533 :
6534 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6535 :
6536 376 : ntups = PQntuples(res);
6537 :
6538 376 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6539 :
6540 376 : i_tableoid = PQfnumber(res, "tableoid");
6541 376 : i_oid = PQfnumber(res, "oid");
6542 376 : i_conname = PQfnumber(res, "conname");
6543 376 : i_connamespace = PQfnumber(res, "connamespace");
6544 376 : i_conowner = PQfnumber(res, "conowner");
6545 :
6546 48594 : for (i = 0; i < ntups; i++)
6547 : {
6548 48218 : convinfo[i].dobj.objType = DO_CONVERSION;
6549 48218 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6550 48218 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6551 48218 : AssignDumpId(&convinfo[i].dobj);
6552 48218 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6553 96436 : convinfo[i].dobj.namespace =
6554 48218 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6555 48218 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6556 :
6557 : /* Decide whether we want to dump it */
6558 48218 : selectDumpableObject(&(convinfo[i].dobj), fout);
6559 : }
6560 :
6561 376 : PQclear(res);
6562 :
6563 376 : destroyPQExpBuffer(query);
6564 376 : }
6565 :
6566 : /*
6567 : * getAccessMethods:
6568 : * get information about all user-defined access methods
6569 : */
6570 : void
6571 376 : getAccessMethods(Archive *fout)
6572 : {
6573 : PGresult *res;
6574 : int ntups;
6575 : int i;
6576 : PQExpBuffer query;
6577 : AccessMethodInfo *aminfo;
6578 : int i_tableoid;
6579 : int i_oid;
6580 : int i_amname;
6581 : int i_amhandler;
6582 : int i_amtype;
6583 :
6584 376 : query = createPQExpBuffer();
6585 :
6586 : /*
6587 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6588 : * ACCESS METHOD, so earlier versions usually have only built-in access
6589 : * methods. v9.6 also changed the access method API, replacing dozens of
6590 : * pg_am columns with amhandler. Even if a user created an access method
6591 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6592 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6593 : * pg_am just to facilitate findAccessMethodByOid() providing the
6594 : * OID-to-name mapping.
6595 : */
6596 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6597 376 : if (fout->remoteVersion >= 90600)
6598 376 : appendPQExpBufferStr(query,
6599 : "amtype, "
6600 : "amhandler::pg_catalog.regproc AS amhandler ");
6601 : else
6602 0 : appendPQExpBufferStr(query,
6603 : "'i'::pg_catalog.\"char\" AS amtype, "
6604 : "'-'::pg_catalog.regproc AS amhandler ");
6605 376 : appendPQExpBufferStr(query, "FROM pg_am");
6606 :
6607 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6608 :
6609 376 : ntups = PQntuples(res);
6610 :
6611 376 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6612 :
6613 376 : i_tableoid = PQfnumber(res, "tableoid");
6614 376 : i_oid = PQfnumber(res, "oid");
6615 376 : i_amname = PQfnumber(res, "amname");
6616 376 : i_amhandler = PQfnumber(res, "amhandler");
6617 376 : i_amtype = PQfnumber(res, "amtype");
6618 :
6619 3252 : for (i = 0; i < ntups; i++)
6620 : {
6621 2876 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6622 2876 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6623 2876 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6624 2876 : AssignDumpId(&aminfo[i].dobj);
6625 2876 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6626 2876 : aminfo[i].dobj.namespace = NULL;
6627 2876 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6628 2876 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6629 :
6630 : /* Decide whether we want to dump it */
6631 2876 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6632 : }
6633 :
6634 376 : PQclear(res);
6635 :
6636 376 : destroyPQExpBuffer(query);
6637 376 : }
6638 :
6639 :
6640 : /*
6641 : * getOpclasses:
6642 : * get information about all opclasses in the system catalogs
6643 : */
6644 : void
6645 376 : getOpclasses(Archive *fout)
6646 : {
6647 : PGresult *res;
6648 : int ntups;
6649 : int i;
6650 376 : PQExpBuffer query = createPQExpBuffer();
6651 : OpclassInfo *opcinfo;
6652 : int i_tableoid;
6653 : int i_oid;
6654 : int i_opcmethod;
6655 : int i_opcname;
6656 : int i_opcnamespace;
6657 : int i_opcowner;
6658 :
6659 : /*
6660 : * find all opclasses, including builtin opclasses; we filter out
6661 : * system-defined opclasses at dump-out time.
6662 : */
6663 :
6664 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6665 : "opcnamespace, "
6666 : "opcowner "
6667 : "FROM pg_opclass");
6668 :
6669 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6670 :
6671 376 : ntups = PQntuples(res);
6672 :
6673 376 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6674 :
6675 376 : i_tableoid = PQfnumber(res, "tableoid");
6676 376 : i_oid = PQfnumber(res, "oid");
6677 376 : i_opcmethod = PQfnumber(res, "opcmethod");
6678 376 : i_opcname = PQfnumber(res, "opcname");
6679 376 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6680 376 : i_opcowner = PQfnumber(res, "opcowner");
6681 :
6682 67992 : for (i = 0; i < ntups; i++)
6683 : {
6684 67616 : opcinfo[i].dobj.objType = DO_OPCLASS;
6685 67616 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6686 67616 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6687 67616 : AssignDumpId(&opcinfo[i].dobj);
6688 67616 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6689 135232 : opcinfo[i].dobj.namespace =
6690 67616 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6691 67616 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6692 67616 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6693 :
6694 : /* Decide whether we want to dump it */
6695 67616 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6696 : }
6697 :
6698 376 : PQclear(res);
6699 :
6700 376 : destroyPQExpBuffer(query);
6701 376 : }
6702 :
6703 : /*
6704 : * getOpfamilies:
6705 : * get information about all opfamilies in the system catalogs
6706 : */
6707 : void
6708 376 : getOpfamilies(Archive *fout)
6709 : {
6710 : PGresult *res;
6711 : int ntups;
6712 : int i;
6713 : PQExpBuffer query;
6714 : OpfamilyInfo *opfinfo;
6715 : int i_tableoid;
6716 : int i_oid;
6717 : int i_opfmethod;
6718 : int i_opfname;
6719 : int i_opfnamespace;
6720 : int i_opfowner;
6721 :
6722 376 : query = createPQExpBuffer();
6723 :
6724 : /*
6725 : * find all opfamilies, including builtin opfamilies; we filter out
6726 : * system-defined opfamilies at dump-out time.
6727 : */
6728 :
6729 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6730 : "opfnamespace, "
6731 : "opfowner "
6732 : "FROM pg_opfamily");
6733 :
6734 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6735 :
6736 376 : ntups = PQntuples(res);
6737 :
6738 376 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6739 :
6740 376 : i_tableoid = PQfnumber(res, "tableoid");
6741 376 : i_oid = PQfnumber(res, "oid");
6742 376 : i_opfname = PQfnumber(res, "opfname");
6743 376 : i_opfmethod = PQfnumber(res, "opfmethod");
6744 376 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6745 376 : i_opfowner = PQfnumber(res, "opfowner");
6746 :
6747 56302 : for (i = 0; i < ntups; i++)
6748 : {
6749 55926 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6750 55926 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6751 55926 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6752 55926 : AssignDumpId(&opfinfo[i].dobj);
6753 55926 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6754 111852 : opfinfo[i].dobj.namespace =
6755 55926 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6756 55926 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6757 55926 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6758 :
6759 : /* Decide whether we want to dump it */
6760 55926 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6761 : }
6762 :
6763 376 : PQclear(res);
6764 :
6765 376 : destroyPQExpBuffer(query);
6766 376 : }
6767 :
6768 : /*
6769 : * getAggregates:
6770 : * get information about all user-defined aggregates in the system catalogs
6771 : */
6772 : void
6773 376 : getAggregates(Archive *fout)
6774 : {
6775 376 : DumpOptions *dopt = fout->dopt;
6776 : PGresult *res;
6777 : int ntups;
6778 : int i;
6779 376 : PQExpBuffer query = createPQExpBuffer();
6780 : AggInfo *agginfo;
6781 : int i_tableoid;
6782 : int i_oid;
6783 : int i_aggname;
6784 : int i_aggnamespace;
6785 : int i_pronargs;
6786 : int i_proargtypes;
6787 : int i_proowner;
6788 : int i_aggacl;
6789 : int i_acldefault;
6790 :
6791 : /*
6792 : * Find all interesting aggregates. See comment in getFuncs() for the
6793 : * rationale behind the filtering logic.
6794 : */
6795 376 : if (fout->remoteVersion >= 90600)
6796 : {
6797 : const char *agg_check;
6798 :
6799 752 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6800 376 : : "p.proisagg");
6801 :
6802 376 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6803 : "p.proname AS aggname, "
6804 : "p.pronamespace AS aggnamespace, "
6805 : "p.pronargs, p.proargtypes, "
6806 : "p.proowner, "
6807 : "p.proacl AS aggacl, "
6808 : "acldefault('f', p.proowner) AS acldefault "
6809 : "FROM pg_proc p "
6810 : "LEFT JOIN pg_init_privs pip ON "
6811 : "(p.oid = pip.objoid "
6812 : "AND pip.classoid = 'pg_proc'::regclass "
6813 : "AND pip.objsubid = 0) "
6814 : "WHERE %s AND ("
6815 : "p.pronamespace != "
6816 : "(SELECT oid FROM pg_namespace "
6817 : "WHERE nspname = 'pg_catalog') OR "
6818 : "p.proacl IS DISTINCT FROM pip.initprivs",
6819 : agg_check);
6820 376 : if (dopt->binary_upgrade)
6821 76 : appendPQExpBufferStr(query,
6822 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6823 : "classid = 'pg_proc'::regclass AND "
6824 : "objid = p.oid AND "
6825 : "refclassid = 'pg_extension'::regclass AND "
6826 : "deptype = 'e')");
6827 376 : appendPQExpBufferChar(query, ')');
6828 : }
6829 : else
6830 : {
6831 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6832 : "pronamespace AS aggnamespace, "
6833 : "pronargs, proargtypes, "
6834 : "proowner, "
6835 : "proacl AS aggacl, "
6836 : "acldefault('f', proowner) AS acldefault "
6837 : "FROM pg_proc p "
6838 : "WHERE proisagg AND ("
6839 : "pronamespace != "
6840 : "(SELECT oid FROM pg_namespace "
6841 : "WHERE nspname = 'pg_catalog')");
6842 0 : if (dopt->binary_upgrade)
6843 0 : appendPQExpBufferStr(query,
6844 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6845 : "classid = 'pg_proc'::regclass AND "
6846 : "objid = p.oid AND "
6847 : "refclassid = 'pg_extension'::regclass AND "
6848 : "deptype = 'e')");
6849 0 : appendPQExpBufferChar(query, ')');
6850 : }
6851 :
6852 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6853 :
6854 376 : ntups = PQntuples(res);
6855 :
6856 376 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6857 :
6858 376 : i_tableoid = PQfnumber(res, "tableoid");
6859 376 : i_oid = PQfnumber(res, "oid");
6860 376 : i_aggname = PQfnumber(res, "aggname");
6861 376 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6862 376 : i_pronargs = PQfnumber(res, "pronargs");
6863 376 : i_proargtypes = PQfnumber(res, "proargtypes");
6864 376 : i_proowner = PQfnumber(res, "proowner");
6865 376 : i_aggacl = PQfnumber(res, "aggacl");
6866 376 : i_acldefault = PQfnumber(res, "acldefault");
6867 :
6868 1174 : for (i = 0; i < ntups; i++)
6869 : {
6870 798 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6871 798 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6872 798 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6873 798 : AssignDumpId(&agginfo[i].aggfn.dobj);
6874 798 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6875 1596 : agginfo[i].aggfn.dobj.namespace =
6876 798 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6877 798 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6878 798 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6879 798 : agginfo[i].aggfn.dacl.privtype = 0;
6880 798 : agginfo[i].aggfn.dacl.initprivs = NULL;
6881 798 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6882 798 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6883 798 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6884 798 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6885 798 : if (agginfo[i].aggfn.nargs == 0)
6886 112 : agginfo[i].aggfn.argtypes = NULL;
6887 : else
6888 : {
6889 686 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6890 686 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6891 686 : agginfo[i].aggfn.argtypes,
6892 686 : agginfo[i].aggfn.nargs);
6893 : }
6894 798 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6895 :
6896 : /* Decide whether we want to dump it */
6897 798 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6898 :
6899 : /* Mark whether aggregate has an ACL */
6900 798 : if (!PQgetisnull(res, i, i_aggacl))
6901 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6902 : }
6903 :
6904 376 : PQclear(res);
6905 :
6906 376 : destroyPQExpBuffer(query);
6907 376 : }
6908 :
6909 : /*
6910 : * getFuncs:
6911 : * get information about all user-defined functions in the system catalogs
6912 : */
6913 : void
6914 376 : getFuncs(Archive *fout)
6915 : {
6916 376 : DumpOptions *dopt = fout->dopt;
6917 : PGresult *res;
6918 : int ntups;
6919 : int i;
6920 376 : PQExpBuffer query = createPQExpBuffer();
6921 : FuncInfo *finfo;
6922 : int i_tableoid;
6923 : int i_oid;
6924 : int i_proname;
6925 : int i_pronamespace;
6926 : int i_proowner;
6927 : int i_prolang;
6928 : int i_pronargs;
6929 : int i_proargtypes;
6930 : int i_prorettype;
6931 : int i_proacl;
6932 : int i_acldefault;
6933 :
6934 : /*
6935 : * Find all interesting functions. This is a bit complicated:
6936 : *
6937 : * 1. Always exclude aggregates; those are handled elsewhere.
6938 : *
6939 : * 2. Always exclude functions that are internally dependent on something
6940 : * else, since presumably those will be created as a result of creating
6941 : * the something else. This currently acts only to suppress constructor
6942 : * functions for range types. Note this is OK only because the
6943 : * constructors don't have any dependencies the range type doesn't have;
6944 : * otherwise we might not get creation ordering correct.
6945 : *
6946 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6947 : * they're members of extensions and we are in binary-upgrade mode then
6948 : * include them, since we want to dump extension members individually in
6949 : * that mode. Also, if they are used by casts or transforms then we need
6950 : * to gather the information about them, though they won't be dumped if
6951 : * they are built-in. Also, in 9.6 and up, include functions in
6952 : * pg_catalog if they have an ACL different from what's shown in
6953 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6954 : */
6955 376 : if (fout->remoteVersion >= 90600)
6956 : {
6957 : const char *not_agg_check;
6958 :
6959 752 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6960 376 : : "NOT p.proisagg");
6961 :
6962 376 : appendPQExpBuffer(query,
6963 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6964 : "p.pronargs, p.proargtypes, p.prorettype, "
6965 : "p.proacl, "
6966 : "acldefault('f', p.proowner) AS acldefault, "
6967 : "p.pronamespace, "
6968 : "p.proowner "
6969 : "FROM pg_proc p "
6970 : "LEFT JOIN pg_init_privs pip ON "
6971 : "(p.oid = pip.objoid "
6972 : "AND pip.classoid = 'pg_proc'::regclass "
6973 : "AND pip.objsubid = 0) "
6974 : "WHERE %s"
6975 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6976 : "WHERE classid = 'pg_proc'::regclass AND "
6977 : "objid = p.oid AND deptype = 'i')"
6978 : "\n AND ("
6979 : "\n pronamespace != "
6980 : "(SELECT oid FROM pg_namespace "
6981 : "WHERE nspname = 'pg_catalog')"
6982 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6983 : "\n WHERE pg_cast.oid > %u "
6984 : "\n AND p.oid = pg_cast.castfunc)"
6985 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6986 : "\n WHERE pg_transform.oid > %u AND "
6987 : "\n (p.oid = pg_transform.trffromsql"
6988 : "\n OR p.oid = pg_transform.trftosql))",
6989 : not_agg_check,
6990 : g_last_builtin_oid,
6991 : g_last_builtin_oid);
6992 376 : if (dopt->binary_upgrade)
6993 76 : appendPQExpBufferStr(query,
6994 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6995 : "classid = 'pg_proc'::regclass AND "
6996 : "objid = p.oid AND "
6997 : "refclassid = 'pg_extension'::regclass AND "
6998 : "deptype = 'e')");
6999 376 : appendPQExpBufferStr(query,
7000 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7001 376 : appendPQExpBufferChar(query, ')');
7002 : }
7003 : else
7004 : {
7005 0 : appendPQExpBuffer(query,
7006 : "SELECT tableoid, oid, proname, prolang, "
7007 : "pronargs, proargtypes, prorettype, proacl, "
7008 : "acldefault('f', proowner) AS acldefault, "
7009 : "pronamespace, "
7010 : "proowner "
7011 : "FROM pg_proc p "
7012 : "WHERE NOT proisagg"
7013 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7014 : "WHERE classid = 'pg_proc'::regclass AND "
7015 : "objid = p.oid AND deptype = 'i')"
7016 : "\n AND ("
7017 : "\n pronamespace != "
7018 : "(SELECT oid FROM pg_namespace "
7019 : "WHERE nspname = 'pg_catalog')"
7020 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7021 : "\n WHERE pg_cast.oid > '%u'::oid"
7022 : "\n AND p.oid = pg_cast.castfunc)",
7023 : g_last_builtin_oid);
7024 :
7025 0 : if (fout->remoteVersion >= 90500)
7026 0 : appendPQExpBuffer(query,
7027 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7028 : "\n WHERE pg_transform.oid > '%u'::oid"
7029 : "\n AND (p.oid = pg_transform.trffromsql"
7030 : "\n OR p.oid = pg_transform.trftosql))",
7031 : g_last_builtin_oid);
7032 :
7033 0 : if (dopt->binary_upgrade)
7034 0 : appendPQExpBufferStr(query,
7035 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7036 : "classid = 'pg_proc'::regclass AND "
7037 : "objid = p.oid AND "
7038 : "refclassid = 'pg_extension'::regclass AND "
7039 : "deptype = 'e')");
7040 0 : appendPQExpBufferChar(query, ')');
7041 : }
7042 :
7043 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7044 :
7045 376 : ntups = PQntuples(res);
7046 :
7047 376 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7048 :
7049 376 : i_tableoid = PQfnumber(res, "tableoid");
7050 376 : i_oid = PQfnumber(res, "oid");
7051 376 : i_proname = PQfnumber(res, "proname");
7052 376 : i_pronamespace = PQfnumber(res, "pronamespace");
7053 376 : i_proowner = PQfnumber(res, "proowner");
7054 376 : i_prolang = PQfnumber(res, "prolang");
7055 376 : i_pronargs = PQfnumber(res, "pronargs");
7056 376 : i_proargtypes = PQfnumber(res, "proargtypes");
7057 376 : i_prorettype = PQfnumber(res, "prorettype");
7058 376 : i_proacl = PQfnumber(res, "proacl");
7059 376 : i_acldefault = PQfnumber(res, "acldefault");
7060 :
7061 9956 : for (i = 0; i < ntups; i++)
7062 : {
7063 9580 : finfo[i].dobj.objType = DO_FUNC;
7064 9580 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7065 9580 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7066 9580 : AssignDumpId(&finfo[i].dobj);
7067 9580 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7068 19160 : finfo[i].dobj.namespace =
7069 9580 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7070 9580 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7071 9580 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7072 9580 : finfo[i].dacl.privtype = 0;
7073 9580 : finfo[i].dacl.initprivs = NULL;
7074 9580 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7075 9580 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7076 9580 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7077 9580 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7078 9580 : if (finfo[i].nargs == 0)
7079 2144 : finfo[i].argtypes = NULL;
7080 : else
7081 : {
7082 7436 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7083 7436 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7084 7436 : finfo[i].argtypes, finfo[i].nargs);
7085 : }
7086 9580 : finfo[i].postponed_def = false; /* might get set during sort */
7087 :
7088 : /* Decide whether we want to dump it */
7089 9580 : selectDumpableObject(&(finfo[i].dobj), fout);
7090 :
7091 : /* Mark whether function has an ACL */
7092 9580 : if (!PQgetisnull(res, i, i_proacl))
7093 280 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7094 : }
7095 :
7096 376 : PQclear(res);
7097 :
7098 376 : destroyPQExpBuffer(query);
7099 376 : }
7100 :
7101 : /*
7102 : * getRelationStatistics
7103 : * register the statistics object as a dependent of the relation.
7104 : *
7105 : * reltuples is passed as a string to avoid complexities in converting from/to
7106 : * floating point.
7107 : */
7108 : static RelStatsInfo *
7109 19176 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7110 : char *reltuples, int32 relallvisible,
7111 : int32 relallfrozen, char relkind,
7112 : char **indAttNames, int nindAttNames)
7113 : {
7114 19176 : if (!fout->dopt->dumpStatistics)
7115 12108 : return NULL;
7116 :
7117 7068 : if ((relkind == RELKIND_RELATION) ||
7118 2968 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7119 1772 : (relkind == RELKIND_INDEX) ||
7120 1144 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7121 538 : (relkind == RELKIND_MATVIEW ||
7122 : relkind == RELKIND_FOREIGN_TABLE))
7123 : {
7124 6594 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7125 6594 : DumpableObject *dobj = &info->dobj;
7126 :
7127 6594 : dobj->objType = DO_REL_STATS;
7128 6594 : dobj->catId.tableoid = 0;
7129 6594 : dobj->catId.oid = 0;
7130 6594 : AssignDumpId(dobj);
7131 6594 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7132 6594 : dobj->dependencies[0] = rel->dumpId;
7133 6594 : dobj->nDeps = 1;
7134 6594 : dobj->allocDeps = 1;
7135 6594 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7136 6594 : dobj->name = pg_strdup(rel->name);
7137 6594 : dobj->namespace = rel->namespace;
7138 6594 : info->relpages = relpages;
7139 6594 : info->reltuples = pstrdup(reltuples);
7140 6594 : info->relallvisible = relallvisible;
7141 6594 : info->relallfrozen = relallfrozen;
7142 6594 : info->relkind = relkind;
7143 6594 : info->indAttNames = indAttNames;
7144 6594 : info->nindAttNames = nindAttNames;
7145 :
7146 : /*
7147 : * Ordinarily, stats go in SECTION_DATA for tables and
7148 : * SECTION_POST_DATA for indexes.
7149 : *
7150 : * However, the section may be updated later for materialized view
7151 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7152 : * the stats, so the stats must be restored after the data. Also, the
7153 : * materialized view definition may be postponed to SECTION_POST_DATA
7154 : * (see repairMatViewBoundaryMultiLoop()).
7155 : */
7156 6594 : switch (info->relkind)
7157 : {
7158 4770 : case RELKIND_RELATION:
7159 : case RELKIND_PARTITIONED_TABLE:
7160 : case RELKIND_MATVIEW:
7161 : case RELKIND_FOREIGN_TABLE:
7162 4770 : info->section = SECTION_DATA;
7163 4770 : break;
7164 1824 : case RELKIND_INDEX:
7165 : case RELKIND_PARTITIONED_INDEX:
7166 1824 : info->section = SECTION_POST_DATA;
7167 1824 : break;
7168 0 : default:
7169 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7170 : info->relkind);
7171 : }
7172 :
7173 6594 : return info;
7174 : }
7175 474 : return NULL;
7176 : }
7177 :
7178 : /*
7179 : * getTables
7180 : * read all the tables (no indexes) in the system catalogs,
7181 : * and return them as an array of TableInfo structures
7182 : *
7183 : * *numTables is set to the number of tables read in
7184 : */
7185 : TableInfo *
7186 378 : getTables(Archive *fout, int *numTables)
7187 : {
7188 378 : DumpOptions *dopt = fout->dopt;
7189 : PGresult *res;
7190 : int ntups;
7191 : int i;
7192 378 : PQExpBuffer query = createPQExpBuffer();
7193 : TableInfo *tblinfo;
7194 : int i_reltableoid;
7195 : int i_reloid;
7196 : int i_relname;
7197 : int i_relnamespace;
7198 : int i_relkind;
7199 : int i_reltype;
7200 : int i_relowner;
7201 : int i_relchecks;
7202 : int i_relhasindex;
7203 : int i_relhasrules;
7204 : int i_relpages;
7205 : int i_reltuples;
7206 : int i_relallvisible;
7207 : int i_relallfrozen;
7208 : int i_toastpages;
7209 : int i_owning_tab;
7210 : int i_owning_col;
7211 : int i_reltablespace;
7212 : int i_relhasoids;
7213 : int i_relhastriggers;
7214 : int i_relpersistence;
7215 : int i_relispopulated;
7216 : int i_relreplident;
7217 : int i_relrowsec;
7218 : int i_relforcerowsec;
7219 : int i_relfrozenxid;
7220 : int i_toastfrozenxid;
7221 : int i_toastoid;
7222 : int i_relminmxid;
7223 : int i_toastminmxid;
7224 : int i_reloptions;
7225 : int i_checkoption;
7226 : int i_toastreloptions;
7227 : int i_reloftype;
7228 : int i_foreignserver;
7229 : int i_amname;
7230 : int i_is_identity_sequence;
7231 : int i_relacl;
7232 : int i_acldefault;
7233 : int i_ispartition;
7234 :
7235 : /*
7236 : * Find all the tables and table-like objects.
7237 : *
7238 : * We must fetch all tables in this phase because otherwise we cannot
7239 : * correctly identify inherited columns, owned sequences, etc.
7240 : *
7241 : * We include system catalogs, so that we can work if a user table is
7242 : * defined to inherit from a system catalog (pretty weird, but...)
7243 : *
7244 : * Note: in this phase we should collect only a minimal amount of
7245 : * information about each table, basically just enough to decide if it is
7246 : * interesting. In particular, since we do not yet have lock on any user
7247 : * table, we MUST NOT invoke any server-side data collection functions
7248 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7249 : * wrong answers if any concurrent DDL is happening.
7250 : */
7251 :
7252 378 : appendPQExpBufferStr(query,
7253 : "SELECT c.tableoid, c.oid, c.relname, "
7254 : "c.relnamespace, c.relkind, c.reltype, "
7255 : "c.relowner, "
7256 : "c.relchecks, "
7257 : "c.relhasindex, c.relhasrules, c.relpages, "
7258 : "c.reltuples, c.relallvisible, ");
7259 :
7260 378 : if (fout->remoteVersion >= 180000)
7261 378 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7262 : else
7263 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7264 :
7265 378 : appendPQExpBufferStr(query,
7266 : "c.relhastriggers, c.relpersistence, "
7267 : "c.reloftype, "
7268 : "c.relacl, "
7269 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7270 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7271 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7272 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7273 : "ELSE 0 END AS foreignserver, "
7274 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7275 : "tc.oid AS toid, "
7276 : "tc.relpages AS toastpages, "
7277 : "tc.reloptions AS toast_reloptions, "
7278 : "d.refobjid AS owning_tab, "
7279 : "d.refobjsubid AS owning_col, "
7280 : "tsp.spcname AS reltablespace, ");
7281 :
7282 378 : if (fout->remoteVersion >= 120000)
7283 378 : appendPQExpBufferStr(query,
7284 : "false AS relhasoids, ");
7285 : else
7286 0 : appendPQExpBufferStr(query,
7287 : "c.relhasoids, ");
7288 :
7289 378 : if (fout->remoteVersion >= 90300)
7290 378 : appendPQExpBufferStr(query,
7291 : "c.relispopulated, ");
7292 : else
7293 0 : appendPQExpBufferStr(query,
7294 : "'t' as relispopulated, ");
7295 :
7296 378 : if (fout->remoteVersion >= 90400)
7297 378 : appendPQExpBufferStr(query,
7298 : "c.relreplident, ");
7299 : else
7300 0 : appendPQExpBufferStr(query,
7301 : "'d' AS relreplident, ");
7302 :
7303 378 : if (fout->remoteVersion >= 90500)
7304 378 : appendPQExpBufferStr(query,
7305 : "c.relrowsecurity, c.relforcerowsecurity, ");
7306 : else
7307 0 : appendPQExpBufferStr(query,
7308 : "false AS relrowsecurity, "
7309 : "false AS relforcerowsecurity, ");
7310 :
7311 378 : if (fout->remoteVersion >= 90300)
7312 378 : appendPQExpBufferStr(query,
7313 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7314 : else
7315 0 : appendPQExpBufferStr(query,
7316 : "0 AS relminmxid, 0 AS tminmxid, ");
7317 :
7318 378 : if (fout->remoteVersion >= 90300)
7319 378 : appendPQExpBufferStr(query,
7320 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7321 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7322 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7323 : else
7324 0 : appendPQExpBufferStr(query,
7325 : "c.reloptions, NULL AS checkoption, ");
7326 :
7327 378 : if (fout->remoteVersion >= 90600)
7328 378 : appendPQExpBufferStr(query,
7329 : "am.amname, ");
7330 : else
7331 0 : appendPQExpBufferStr(query,
7332 : "NULL AS amname, ");
7333 :
7334 378 : if (fout->remoteVersion >= 90600)
7335 378 : appendPQExpBufferStr(query,
7336 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7337 : else
7338 0 : appendPQExpBufferStr(query,
7339 : "false AS is_identity_sequence, ");
7340 :
7341 378 : if (fout->remoteVersion >= 100000)
7342 378 : appendPQExpBufferStr(query,
7343 : "c.relispartition AS ispartition ");
7344 : else
7345 0 : appendPQExpBufferStr(query,
7346 : "false AS ispartition ");
7347 :
7348 : /*
7349 : * Left join to pg_depend to pick up dependency info linking sequences to
7350 : * their owning column, if any (note this dependency is AUTO except for
7351 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7352 : * collect the spcname.
7353 : */
7354 378 : appendPQExpBufferStr(query,
7355 : "\nFROM pg_class c\n"
7356 : "LEFT JOIN pg_depend d ON "
7357 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7358 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7359 : "d.objsubid = 0 AND "
7360 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7361 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7362 :
7363 : /*
7364 : * In 9.6 and up, left join to pg_am to pick up the amname.
7365 : */
7366 378 : if (fout->remoteVersion >= 90600)
7367 378 : appendPQExpBufferStr(query,
7368 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7369 :
7370 : /*
7371 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7372 : * that versions 10 and 11 have them, but later versions do not, so
7373 : * emitting them causes the upgrade to fail.
7374 : */
7375 378 : appendPQExpBufferStr(query,
7376 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7377 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7378 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7379 :
7380 : /*
7381 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7382 : * relkinds are possible in older servers, but it's not worth the trouble
7383 : * to emit a version-dependent list.
7384 : *
7385 : * Composite-type table entries won't be dumped as such, but we have to
7386 : * make a DumpableObject for them so that we can track dependencies of the
7387 : * composite type (pg_depend entries for columns of the composite type
7388 : * link to the pg_class entry not the pg_type entry).
7389 : */
7390 378 : appendPQExpBufferStr(query,
7391 : "WHERE c.relkind IN ("
7392 : CppAsString2(RELKIND_RELATION) ", "
7393 : CppAsString2(RELKIND_SEQUENCE) ", "
7394 : CppAsString2(RELKIND_VIEW) ", "
7395 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7396 : CppAsString2(RELKIND_MATVIEW) ", "
7397 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7398 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7399 : "ORDER BY c.oid");
7400 :
7401 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7402 :
7403 378 : ntups = PQntuples(res);
7404 :
7405 378 : *numTables = ntups;
7406 :
7407 : /*
7408 : * Extract data from result and lock dumpable tables. We do the locking
7409 : * before anything else, to minimize the window wherein a table could
7410 : * disappear under us.
7411 : *
7412 : * Note that we have to save info about all tables here, even when dumping
7413 : * only one, because we don't yet know which tables might be inheritance
7414 : * ancestors of the target table.
7415 : */
7416 378 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7417 :
7418 378 : i_reltableoid = PQfnumber(res, "tableoid");
7419 378 : i_reloid = PQfnumber(res, "oid");
7420 378 : i_relname = PQfnumber(res, "relname");
7421 378 : i_relnamespace = PQfnumber(res, "relnamespace");
7422 378 : i_relkind = PQfnumber(res, "relkind");
7423 378 : i_reltype = PQfnumber(res, "reltype");
7424 378 : i_relowner = PQfnumber(res, "relowner");
7425 378 : i_relchecks = PQfnumber(res, "relchecks");
7426 378 : i_relhasindex = PQfnumber(res, "relhasindex");
7427 378 : i_relhasrules = PQfnumber(res, "relhasrules");
7428 378 : i_relpages = PQfnumber(res, "relpages");
7429 378 : i_reltuples = PQfnumber(res, "reltuples");
7430 378 : i_relallvisible = PQfnumber(res, "relallvisible");
7431 378 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7432 378 : i_toastpages = PQfnumber(res, "toastpages");
7433 378 : i_owning_tab = PQfnumber(res, "owning_tab");
7434 378 : i_owning_col = PQfnumber(res, "owning_col");
7435 378 : i_reltablespace = PQfnumber(res, "reltablespace");
7436 378 : i_relhasoids = PQfnumber(res, "relhasoids");
7437 378 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7438 378 : i_relpersistence = PQfnumber(res, "relpersistence");
7439 378 : i_relispopulated = PQfnumber(res, "relispopulated");
7440 378 : i_relreplident = PQfnumber(res, "relreplident");
7441 378 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7442 378 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7443 378 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7444 378 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7445 378 : i_toastoid = PQfnumber(res, "toid");
7446 378 : i_relminmxid = PQfnumber(res, "relminmxid");
7447 378 : i_toastminmxid = PQfnumber(res, "tminmxid");
7448 378 : i_reloptions = PQfnumber(res, "reloptions");
7449 378 : i_checkoption = PQfnumber(res, "checkoption");
7450 378 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7451 378 : i_reloftype = PQfnumber(res, "reloftype");
7452 378 : i_foreignserver = PQfnumber(res, "foreignserver");
7453 378 : i_amname = PQfnumber(res, "amname");
7454 378 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7455 378 : i_relacl = PQfnumber(res, "relacl");
7456 378 : i_acldefault = PQfnumber(res, "acldefault");
7457 378 : i_ispartition = PQfnumber(res, "ispartition");
7458 :
7459 378 : if (dopt->lockWaitTimeout)
7460 : {
7461 : /*
7462 : * Arrange to fail instead of waiting forever for a table lock.
7463 : *
7464 : * NB: this coding assumes that the only queries issued within the
7465 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7466 : * applied to other things too.
7467 : */
7468 4 : resetPQExpBuffer(query);
7469 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7470 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7471 4 : ExecuteSqlStatement(fout, query->data);
7472 : }
7473 :
7474 378 : resetPQExpBuffer(query);
7475 :
7476 99800 : for (i = 0; i < ntups; i++)
7477 : {
7478 99422 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7479 99422 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7480 :
7481 99422 : tblinfo[i].dobj.objType = DO_TABLE;
7482 99422 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7483 99422 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7484 99422 : AssignDumpId(&tblinfo[i].dobj);
7485 99422 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7486 198844 : tblinfo[i].dobj.namespace =
7487 99422 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7488 99422 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7489 99422 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7490 99422 : tblinfo[i].dacl.privtype = 0;
7491 99422 : tblinfo[i].dacl.initprivs = NULL;
7492 99422 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7493 99422 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7494 99422 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7495 99422 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7496 99422 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7497 99422 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7498 99422 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7499 99422 : if (PQgetisnull(res, i, i_toastpages))
7500 79940 : tblinfo[i].toastpages = 0;
7501 : else
7502 19482 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7503 99422 : if (PQgetisnull(res, i, i_owning_tab))
7504 : {
7505 98592 : tblinfo[i].owning_tab = InvalidOid;
7506 98592 : tblinfo[i].owning_col = 0;
7507 : }
7508 : else
7509 : {
7510 830 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7511 830 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7512 : }
7513 99422 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7514 99422 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7515 99422 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7516 99422 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7517 99422 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7518 99422 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7519 99422 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7520 99422 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7521 99422 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7522 99422 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7523 99422 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7524 99422 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7525 99422 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7526 99422 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7527 99422 : if (PQgetisnull(res, i, i_checkoption))
7528 99330 : tblinfo[i].checkoption = NULL;
7529 : else
7530 92 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7531 99422 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7532 99422 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7533 99422 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7534 99422 : if (PQgetisnull(res, i, i_amname))
7535 59650 : tblinfo[i].amname = NULL;
7536 : else
7537 39772 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7538 99422 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7539 99422 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7540 :
7541 : /* other fields were zeroed above */
7542 :
7543 : /*
7544 : * Decide whether we want to dump this table.
7545 : */
7546 99422 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7547 366 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7548 : else
7549 99056 : selectDumpableTable(&tblinfo[i], fout);
7550 :
7551 : /*
7552 : * Now, consider the table "interesting" if we need to dump its
7553 : * definition, data or its statistics. Later on, we'll skip a lot of
7554 : * data collection for uninteresting tables.
7555 : *
7556 : * Note: the "interesting" flag will also be set by flagInhTables for
7557 : * parents of interesting tables, so that we collect necessary
7558 : * inheritance info even when the parents are not themselves being
7559 : * dumped. This is the main reason why we need an "interesting" flag
7560 : * that's separate from the components-to-dump bitmask.
7561 : */
7562 99422 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7563 : (DUMP_COMPONENT_DEFINITION |
7564 : DUMP_COMPONENT_DATA |
7565 99422 : DUMP_COMPONENT_STATISTICS)) != 0;
7566 :
7567 99422 : tblinfo[i].dummy_view = false; /* might get set during sort */
7568 99422 : tblinfo[i].postponed_def = false; /* might get set during sort */
7569 :
7570 : /* Tables have data */
7571 99422 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7572 :
7573 : /* Mark whether table has an ACL */
7574 99422 : if (!PQgetisnull(res, i, i_relacl))
7575 79760 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7576 99422 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7577 :
7578 : /* Add statistics */
7579 99422 : if (tblinfo[i].interesting)
7580 : {
7581 : RelStatsInfo *stats;
7582 :
7583 27916 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7584 13958 : tblinfo[i].relpages,
7585 : PQgetvalue(res, i, i_reltuples),
7586 : relallvisible, relallfrozen,
7587 13958 : tblinfo[i].relkind, NULL, 0);
7588 13958 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7589 796 : tblinfo[i].stats = stats;
7590 : }
7591 :
7592 : /*
7593 : * Read-lock target tables to make sure they aren't DROPPED or altered
7594 : * in schema before we get around to dumping them.
7595 : *
7596 : * Note that we don't explicitly lock parents of the target tables; we
7597 : * assume our lock on the child is enough to prevent schema
7598 : * alterations to parent tables.
7599 : *
7600 : * NOTE: it'd be kinda nice to lock other relations too, not only
7601 : * plain or partitioned tables, but the backend doesn't presently
7602 : * allow that.
7603 : *
7604 : * We only need to lock the table for certain components; see
7605 : * pg_dump.h
7606 : */
7607 99422 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7608 13958 : (tblinfo[i].relkind == RELKIND_RELATION ||
7609 3958 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7610 : {
7611 : /*
7612 : * Tables are locked in batches. When dumping from a remote
7613 : * server this can save a significant amount of time by reducing
7614 : * the number of round trips.
7615 : */
7616 11208 : if (query->len == 0)
7617 248 : appendPQExpBuffer(query, "LOCK TABLE %s",
7618 248 : fmtQualifiedDumpable(&tblinfo[i]));
7619 : else
7620 : {
7621 10960 : appendPQExpBuffer(query, ", %s",
7622 10960 : fmtQualifiedDumpable(&tblinfo[i]));
7623 :
7624 : /* Arbitrarily end a batch when query length reaches 100K. */
7625 10960 : if (query->len >= 100000)
7626 : {
7627 : /* Lock another batch of tables. */
7628 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7629 0 : ExecuteSqlStatement(fout, query->data);
7630 0 : resetPQExpBuffer(query);
7631 : }
7632 : }
7633 : }
7634 : }
7635 :
7636 378 : if (query->len != 0)
7637 : {
7638 : /* Lock the tables in the last batch. */
7639 248 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7640 248 : ExecuteSqlStatement(fout, query->data);
7641 : }
7642 :
7643 376 : if (dopt->lockWaitTimeout)
7644 : {
7645 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7646 : }
7647 :
7648 376 : PQclear(res);
7649 :
7650 376 : destroyPQExpBuffer(query);
7651 :
7652 376 : return tblinfo;
7653 : }
7654 :
7655 : /*
7656 : * getOwnedSeqs
7657 : * identify owned sequences and mark them as dumpable if owning table is
7658 : *
7659 : * We used to do this in getTables(), but it's better to do it after the
7660 : * index used by findTableByOid() has been set up.
7661 : */
7662 : void
7663 376 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7664 : {
7665 : int i;
7666 :
7667 : /*
7668 : * Force sequences that are "owned" by table columns to be dumped whenever
7669 : * their owning table is being dumped.
7670 : */
7671 99258 : for (i = 0; i < numTables; i++)
7672 : {
7673 98882 : TableInfo *seqinfo = &tblinfo[i];
7674 : TableInfo *owning_tab;
7675 :
7676 98882 : if (!OidIsValid(seqinfo->owning_tab))
7677 98058 : continue; /* not an owned sequence */
7678 :
7679 824 : owning_tab = findTableByOid(seqinfo->owning_tab);
7680 824 : if (owning_tab == NULL)
7681 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7682 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7683 :
7684 : /*
7685 : * For an identity sequence, dump exactly the same components for the
7686 : * sequence as for the owning table. This is important because we
7687 : * treat the identity sequence as an integral part of the table. For
7688 : * example, there is not any DDL command that allows creation of such
7689 : * a sequence independently of the table.
7690 : *
7691 : * For other owned sequences such as serial sequences, we need to dump
7692 : * the components that are being dumped for the table and any
7693 : * components that the sequence is explicitly marked with.
7694 : *
7695 : * We can't simply use the set of components which are being dumped
7696 : * for the table as the table might be in an extension (and only the
7697 : * non-extension components, eg: ACLs if changed, security labels, and
7698 : * policies, are being dumped) while the sequence is not (and
7699 : * therefore the definition and other components should also be
7700 : * dumped).
7701 : *
7702 : * If the sequence is part of the extension then it should be properly
7703 : * marked by checkExtensionMembership() and this will be a no-op as
7704 : * the table will be equivalently marked.
7705 : */
7706 824 : if (seqinfo->is_identity_sequence)
7707 398 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7708 : else
7709 426 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7710 :
7711 : /* Make sure that necessary data is available if we're dumping it */
7712 824 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7713 : {
7714 632 : seqinfo->interesting = true;
7715 632 : owning_tab->interesting = true;
7716 : }
7717 : }
7718 376 : }
7719 :
7720 : /*
7721 : * getInherits
7722 : * read all the inheritance information
7723 : * from the system catalogs return them in the InhInfo* structure
7724 : *
7725 : * numInherits is set to the number of pairs read in
7726 : */
7727 : InhInfo *
7728 376 : getInherits(Archive *fout, int *numInherits)
7729 : {
7730 : PGresult *res;
7731 : int ntups;
7732 : int i;
7733 376 : PQExpBuffer query = createPQExpBuffer();
7734 : InhInfo *inhinfo;
7735 :
7736 : int i_inhrelid;
7737 : int i_inhparent;
7738 :
7739 : /* find all the inheritance information */
7740 376 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7741 :
7742 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7743 :
7744 376 : ntups = PQntuples(res);
7745 :
7746 376 : *numInherits = ntups;
7747 :
7748 376 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7749 :
7750 376 : i_inhrelid = PQfnumber(res, "inhrelid");
7751 376 : i_inhparent = PQfnumber(res, "inhparent");
7752 :
7753 7168 : for (i = 0; i < ntups; i++)
7754 : {
7755 6792 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7756 6792 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7757 : }
7758 :
7759 376 : PQclear(res);
7760 :
7761 376 : destroyPQExpBuffer(query);
7762 :
7763 376 : return inhinfo;
7764 : }
7765 :
7766 : /*
7767 : * getPartitioningInfo
7768 : * get information about partitioning
7769 : *
7770 : * For the most part, we only collect partitioning info about tables we
7771 : * intend to dump. However, this function has to consider all partitioned
7772 : * tables in the database, because we need to know about parents of partitions
7773 : * we are going to dump even if the parents themselves won't be dumped.
7774 : *
7775 : * Specifically, what we need to know is whether each partitioned table
7776 : * has an "unsafe" partitioning scheme that requires us to force
7777 : * load-via-partition-root mode for its children. Currently the only case
7778 : * for which we force that is hash partitioning on enum columns, since the
7779 : * hash codes depend on enum value OIDs which won't be replicated across
7780 : * dump-and-reload. There are other cases in which load-via-partition-root
7781 : * might be necessary, but we expect users to cope with them.
7782 : */
7783 : void
7784 376 : getPartitioningInfo(Archive *fout)
7785 : {
7786 : PQExpBuffer query;
7787 : PGresult *res;
7788 : int ntups;
7789 :
7790 : /* hash partitioning didn't exist before v11 */
7791 376 : if (fout->remoteVersion < 110000)
7792 0 : return;
7793 : /* needn't bother if not dumping data */
7794 376 : if (!fout->dopt->dumpData)
7795 84 : return;
7796 :
7797 292 : query = createPQExpBuffer();
7798 :
7799 : /*
7800 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7801 : * appears among the partition opclasses. We needn't check partstrat.
7802 : *
7803 : * Note that this query may well retrieve info about tables we aren't
7804 : * going to dump and hence have no lock on. That's okay since we need not
7805 : * invoke any unsafe server-side functions.
7806 : */
7807 292 : appendPQExpBufferStr(query,
7808 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7809 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7810 : "ON c.opcmethod = a.oid\n"
7811 : "WHERE opcname = 'enum_ops' "
7812 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7813 : "AND amname = 'hash') = ANY(partclass)");
7814 :
7815 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7816 :
7817 292 : ntups = PQntuples(res);
7818 :
7819 378 : for (int i = 0; i < ntups; i++)
7820 : {
7821 86 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7822 : TableInfo *tbinfo;
7823 :
7824 86 : tbinfo = findTableByOid(tabrelid);
7825 86 : if (tbinfo == NULL)
7826 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7827 : tabrelid);
7828 86 : tbinfo->unsafe_partitions = true;
7829 : }
7830 :
7831 292 : PQclear(res);
7832 :
7833 292 : destroyPQExpBuffer(query);
7834 : }
7835 :
7836 : /*
7837 : * getIndexes
7838 : * get information about every index on a dumpable table
7839 : *
7840 : * Note: index data is not returned directly to the caller, but it
7841 : * does get entered into the DumpableObject tables.
7842 : */
7843 : void
7844 376 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7845 : {
7846 376 : PQExpBuffer query = createPQExpBuffer();
7847 376 : PQExpBuffer tbloids = createPQExpBuffer();
7848 : PGresult *res;
7849 : int ntups;
7850 : int curtblindx;
7851 : IndxInfo *indxinfo;
7852 : int i_tableoid,
7853 : i_oid,
7854 : i_indrelid,
7855 : i_indexname,
7856 : i_relpages,
7857 : i_reltuples,
7858 : i_relallvisible,
7859 : i_relallfrozen,
7860 : i_parentidx,
7861 : i_indexdef,
7862 : i_indnkeyatts,
7863 : i_indnatts,
7864 : i_indkey,
7865 : i_indisclustered,
7866 : i_indisreplident,
7867 : i_indnullsnotdistinct,
7868 : i_contype,
7869 : i_conname,
7870 : i_condeferrable,
7871 : i_condeferred,
7872 : i_conperiod,
7873 : i_contableoid,
7874 : i_conoid,
7875 : i_condef,
7876 : i_indattnames,
7877 : i_tablespace,
7878 : i_indreloptions,
7879 : i_indstatcols,
7880 : i_indstatvals;
7881 :
7882 : /*
7883 : * We want to perform just one query against pg_index. However, we
7884 : * mustn't try to select every row of the catalog and then sort it out on
7885 : * the client side, because some of the server-side functions we need
7886 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7887 : * build an array of the OIDs of tables we care about (and now have lock
7888 : * on!), and use a WHERE clause to constrain which rows are selected.
7889 : */
7890 376 : appendPQExpBufferChar(tbloids, '{');
7891 99258 : for (int i = 0; i < numTables; i++)
7892 : {
7893 98882 : TableInfo *tbinfo = &tblinfo[i];
7894 :
7895 98882 : if (!tbinfo->hasindex)
7896 70018 : continue;
7897 :
7898 : /*
7899 : * We can ignore indexes of uninteresting tables.
7900 : */
7901 28864 : if (!tbinfo->interesting)
7902 24850 : continue;
7903 :
7904 : /* OK, we need info for this table */
7905 4014 : if (tbloids->len > 1) /* do we have more than the '{'? */
7906 3856 : appendPQExpBufferChar(tbloids, ',');
7907 4014 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7908 : }
7909 376 : appendPQExpBufferChar(tbloids, '}');
7910 :
7911 376 : appendPQExpBufferStr(query,
7912 : "SELECT t.tableoid, t.oid, i.indrelid, "
7913 : "t.relname AS indexname, "
7914 : "t.relpages, t.reltuples, t.relallvisible, ");
7915 :
7916 376 : if (fout->remoteVersion >= 180000)
7917 376 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7918 : else
7919 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7920 :
7921 376 : appendPQExpBufferStr(query,
7922 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7923 : "i.indkey, i.indisclustered, "
7924 : "c.contype, c.conname, "
7925 : "c.condeferrable, c.condeferred, "
7926 : "c.tableoid AS contableoid, "
7927 : "c.oid AS conoid, "
7928 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7929 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7930 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7931 : " FROM pg_catalog.pg_attribute "
7932 : " WHERE attrelid = i.indexrelid) "
7933 : "ELSE NULL END AS indattnames, "
7934 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7935 : "t.reloptions AS indreloptions, ");
7936 :
7937 :
7938 376 : if (fout->remoteVersion >= 90400)
7939 376 : appendPQExpBufferStr(query,
7940 : "i.indisreplident, ");
7941 : else
7942 0 : appendPQExpBufferStr(query,
7943 : "false AS indisreplident, ");
7944 :
7945 376 : if (fout->remoteVersion >= 110000)
7946 376 : appendPQExpBufferStr(query,
7947 : "inh.inhparent AS parentidx, "
7948 : "i.indnkeyatts AS indnkeyatts, "
7949 : "i.indnatts AS indnatts, "
7950 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7951 : " FROM pg_catalog.pg_attribute "
7952 : " WHERE attrelid = i.indexrelid AND "
7953 : " attstattarget >= 0) AS indstatcols, "
7954 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7955 : " FROM pg_catalog.pg_attribute "
7956 : " WHERE attrelid = i.indexrelid AND "
7957 : " attstattarget >= 0) AS indstatvals, ");
7958 : else
7959 0 : appendPQExpBufferStr(query,
7960 : "0 AS parentidx, "
7961 : "i.indnatts AS indnkeyatts, "
7962 : "i.indnatts AS indnatts, "
7963 : "'' AS indstatcols, "
7964 : "'' AS indstatvals, ");
7965 :
7966 376 : if (fout->remoteVersion >= 150000)
7967 376 : appendPQExpBufferStr(query,
7968 : "i.indnullsnotdistinct, ");
7969 : else
7970 0 : appendPQExpBufferStr(query,
7971 : "false AS indnullsnotdistinct, ");
7972 :
7973 376 : if (fout->remoteVersion >= 180000)
7974 376 : appendPQExpBufferStr(query,
7975 : "c.conperiod ");
7976 : else
7977 0 : appendPQExpBufferStr(query,
7978 : "NULL AS conperiod ");
7979 :
7980 : /*
7981 : * The point of the messy-looking outer join is to find a constraint that
7982 : * is related by an internal dependency link to the index. If we find one,
7983 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7984 : * index won't have more than one internal dependency.
7985 : *
7986 : * Note: the check on conrelid is redundant, but useful because that
7987 : * column is indexed while conindid is not.
7988 : */
7989 376 : if (fout->remoteVersion >= 110000)
7990 : {
7991 376 : appendPQExpBuffer(query,
7992 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7993 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7994 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7995 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7996 : "LEFT JOIN pg_catalog.pg_constraint c "
7997 : "ON (i.indrelid = c.conrelid AND "
7998 : "i.indexrelid = c.conindid AND "
7999 : "c.contype IN ('p','u','x')) "
8000 : "LEFT JOIN pg_catalog.pg_inherits inh "
8001 : "ON (inh.inhrelid = indexrelid) "
8002 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
8003 : "AND i.indisready "
8004 : "ORDER BY i.indrelid, indexname",
8005 : tbloids->data);
8006 : }
8007 : else
8008 : {
8009 : /*
8010 : * the test on indisready is necessary in 9.2, and harmless in
8011 : * earlier/later versions
8012 : */
8013 0 : appendPQExpBuffer(query,
8014 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8015 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8016 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8017 : "LEFT JOIN pg_catalog.pg_constraint c "
8018 : "ON (i.indrelid = c.conrelid AND "
8019 : "i.indexrelid = c.conindid AND "
8020 : "c.contype IN ('p','u','x')) "
8021 : "WHERE i.indisvalid AND i.indisready "
8022 : "ORDER BY i.indrelid, indexname",
8023 : tbloids->data);
8024 : }
8025 :
8026 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8027 :
8028 376 : ntups = PQntuples(res);
8029 :
8030 376 : i_tableoid = PQfnumber(res, "tableoid");
8031 376 : i_oid = PQfnumber(res, "oid");
8032 376 : i_indrelid = PQfnumber(res, "indrelid");
8033 376 : i_indexname = PQfnumber(res, "indexname");
8034 376 : i_relpages = PQfnumber(res, "relpages");
8035 376 : i_reltuples = PQfnumber(res, "reltuples");
8036 376 : i_relallvisible = PQfnumber(res, "relallvisible");
8037 376 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8038 376 : i_parentidx = PQfnumber(res, "parentidx");
8039 376 : i_indexdef = PQfnumber(res, "indexdef");
8040 376 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8041 376 : i_indnatts = PQfnumber(res, "indnatts");
8042 376 : i_indkey = PQfnumber(res, "indkey");
8043 376 : i_indisclustered = PQfnumber(res, "indisclustered");
8044 376 : i_indisreplident = PQfnumber(res, "indisreplident");
8045 376 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8046 376 : i_contype = PQfnumber(res, "contype");
8047 376 : i_conname = PQfnumber(res, "conname");
8048 376 : i_condeferrable = PQfnumber(res, "condeferrable");
8049 376 : i_condeferred = PQfnumber(res, "condeferred");
8050 376 : i_conperiod = PQfnumber(res, "conperiod");
8051 376 : i_contableoid = PQfnumber(res, "contableoid");
8052 376 : i_conoid = PQfnumber(res, "conoid");
8053 376 : i_condef = PQfnumber(res, "condef");
8054 376 : i_indattnames = PQfnumber(res, "indattnames");
8055 376 : i_tablespace = PQfnumber(res, "tablespace");
8056 376 : i_indreloptions = PQfnumber(res, "indreloptions");
8057 376 : i_indstatcols = PQfnumber(res, "indstatcols");
8058 376 : i_indstatvals = PQfnumber(res, "indstatvals");
8059 :
8060 376 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8061 :
8062 : /*
8063 : * Outer loop iterates once per table, not once per row. Incrementing of
8064 : * j is handled by the inner loop.
8065 : */
8066 376 : curtblindx = -1;
8067 4350 : for (int j = 0; j < ntups;)
8068 : {
8069 3974 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8070 3974 : TableInfo *tbinfo = NULL;
8071 3974 : char **indAttNames = NULL;
8072 3974 : int nindAttNames = 0;
8073 : int numinds;
8074 :
8075 : /* Count rows for this table */
8076 5218 : for (numinds = 1; numinds < ntups - j; numinds++)
8077 5060 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8078 3816 : break;
8079 :
8080 : /*
8081 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8082 : * order.
8083 : */
8084 46654 : while (++curtblindx < numTables)
8085 : {
8086 46654 : tbinfo = &tblinfo[curtblindx];
8087 46654 : if (tbinfo->dobj.catId.oid == indrelid)
8088 3974 : break;
8089 : }
8090 3974 : if (curtblindx >= numTables)
8091 0 : pg_fatal("unrecognized table OID %u", indrelid);
8092 : /* cross-check that we only got requested tables */
8093 3974 : if (!tbinfo->hasindex ||
8094 3974 : !tbinfo->interesting)
8095 0 : pg_fatal("unexpected index data for table \"%s\"",
8096 : tbinfo->dobj.name);
8097 :
8098 : /* Save data for this table */
8099 3974 : tbinfo->indexes = indxinfo + j;
8100 3974 : tbinfo->numIndexes = numinds;
8101 :
8102 9192 : for (int c = 0; c < numinds; c++, j++)
8103 : {
8104 : char contype;
8105 : char indexkind;
8106 : RelStatsInfo *relstats;
8107 5218 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8108 5218 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8109 5218 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8110 :
8111 5218 : indxinfo[j].dobj.objType = DO_INDEX;
8112 5218 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8113 5218 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8114 5218 : AssignDumpId(&indxinfo[j].dobj);
8115 5218 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8116 5218 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8117 5218 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8118 5218 : indxinfo[j].indextable = tbinfo;
8119 5218 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8120 5218 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8121 5218 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8122 5218 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8123 5218 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8124 5218 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8125 5218 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8126 5218 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8127 5218 : parseOidArray(PQgetvalue(res, j, i_indkey),
8128 5218 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8129 5218 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8130 5218 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8131 5218 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8132 5218 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8133 5218 : indxinfo[j].partattaches = (SimplePtrList)
8134 : {
8135 : NULL, NULL
8136 : };
8137 :
8138 5218 : if (indxinfo[j].parentidx == 0)
8139 4070 : indexkind = RELKIND_INDEX;
8140 : else
8141 1148 : indexkind = RELKIND_PARTITIONED_INDEX;
8142 :
8143 5218 : if (!PQgetisnull(res, j, i_indattnames))
8144 : {
8145 292 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8146 : &indAttNames, &nindAttNames))
8147 0 : pg_fatal("could not parse %s array", "indattnames");
8148 : }
8149 :
8150 5218 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8151 : PQgetvalue(res, j, i_reltuples),
8152 : relallvisible, relallfrozen, indexkind,
8153 : indAttNames, nindAttNames);
8154 :
8155 5218 : contype = *(PQgetvalue(res, j, i_contype));
8156 5218 : if (contype == 'p' || contype == 'u' || contype == 'x')
8157 3034 : {
8158 : /*
8159 : * If we found a constraint matching the index, create an
8160 : * entry for it.
8161 : */
8162 : ConstraintInfo *constrinfo;
8163 :
8164 3034 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8165 3034 : constrinfo->dobj.objType = DO_CONSTRAINT;
8166 3034 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8167 3034 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8168 3034 : AssignDumpId(&constrinfo->dobj);
8169 3034 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8170 3034 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8171 3034 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8172 3034 : constrinfo->contable = tbinfo;
8173 3034 : constrinfo->condomain = NULL;
8174 3034 : constrinfo->contype = contype;
8175 3034 : if (contype == 'x')
8176 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8177 : else
8178 3014 : constrinfo->condef = NULL;
8179 3034 : constrinfo->confrelid = InvalidOid;
8180 3034 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8181 3034 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8182 3034 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8183 3034 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8184 3034 : constrinfo->conislocal = true;
8185 3034 : constrinfo->separate = true;
8186 :
8187 3034 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8188 3034 : if (relstats != NULL)
8189 1060 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8190 : }
8191 : else
8192 : {
8193 : /* Plain secondary index */
8194 2184 : indxinfo[j].indexconstraint = 0;
8195 : }
8196 : }
8197 : }
8198 :
8199 376 : PQclear(res);
8200 :
8201 376 : destroyPQExpBuffer(query);
8202 376 : destroyPQExpBuffer(tbloids);
8203 376 : }
8204 :
8205 : /*
8206 : * getExtendedStatistics
8207 : * get information about extended-statistics objects.
8208 : *
8209 : * Note: extended statistics data is not returned directly to the caller, but
8210 : * it does get entered into the DumpableObject tables.
8211 : */
8212 : void
8213 376 : getExtendedStatistics(Archive *fout)
8214 : {
8215 : PQExpBuffer query;
8216 : PGresult *res;
8217 : StatsExtInfo *statsextinfo;
8218 : int ntups;
8219 : int i_tableoid;
8220 : int i_oid;
8221 : int i_stxname;
8222 : int i_stxnamespace;
8223 : int i_stxowner;
8224 : int i_stxrelid;
8225 : int i_stattarget;
8226 : int i;
8227 :
8228 : /* Extended statistics were new in v10 */
8229 376 : if (fout->remoteVersion < 100000)
8230 0 : return;
8231 :
8232 376 : query = createPQExpBuffer();
8233 :
8234 376 : if (fout->remoteVersion < 130000)
8235 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8236 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8237 : "FROM pg_catalog.pg_statistic_ext");
8238 : else
8239 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8240 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8241 : "FROM pg_catalog.pg_statistic_ext");
8242 :
8243 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8244 :
8245 376 : ntups = PQntuples(res);
8246 :
8247 376 : i_tableoid = PQfnumber(res, "tableoid");
8248 376 : i_oid = PQfnumber(res, "oid");
8249 376 : i_stxname = PQfnumber(res, "stxname");
8250 376 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8251 376 : i_stxowner = PQfnumber(res, "stxowner");
8252 376 : i_stxrelid = PQfnumber(res, "stxrelid");
8253 376 : i_stattarget = PQfnumber(res, "stxstattarget");
8254 :
8255 376 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8256 :
8257 702 : for (i = 0; i < ntups; i++)
8258 : {
8259 326 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8260 326 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8261 326 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8262 326 : AssignDumpId(&statsextinfo[i].dobj);
8263 326 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8264 652 : statsextinfo[i].dobj.namespace =
8265 326 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8266 326 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8267 652 : statsextinfo[i].stattable =
8268 326 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8269 326 : if (PQgetisnull(res, i, i_stattarget))
8270 236 : statsextinfo[i].stattarget = -1;
8271 : else
8272 90 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8273 :
8274 : /* Decide whether we want to dump it */
8275 326 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8276 : }
8277 :
8278 376 : PQclear(res);
8279 376 : destroyPQExpBuffer(query);
8280 : }
8281 :
8282 : /*
8283 : * getConstraints
8284 : *
8285 : * Get info about constraints on dumpable tables.
8286 : *
8287 : * Currently handles foreign keys only.
8288 : * Unique and primary key constraints are handled with indexes,
8289 : * while check constraints are processed in getTableAttrs().
8290 : */
8291 : void
8292 376 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8293 : {
8294 376 : PQExpBuffer query = createPQExpBuffer();
8295 376 : PQExpBuffer tbloids = createPQExpBuffer();
8296 : PGresult *res;
8297 : int ntups;
8298 : int curtblindx;
8299 376 : TableInfo *tbinfo = NULL;
8300 : ConstraintInfo *constrinfo;
8301 : int i_contableoid,
8302 : i_conoid,
8303 : i_conrelid,
8304 : i_conname,
8305 : i_confrelid,
8306 : i_conindid,
8307 : i_condef;
8308 :
8309 : /*
8310 : * We want to perform just one query against pg_constraint. However, we
8311 : * mustn't try to select every row of the catalog and then sort it out on
8312 : * the client side, because some of the server-side functions we need
8313 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8314 : * build an array of the OIDs of tables we care about (and now have lock
8315 : * on!), and use a WHERE clause to constrain which rows are selected.
8316 : */
8317 376 : appendPQExpBufferChar(tbloids, '{');
8318 99258 : for (int i = 0; i < numTables; i++)
8319 : {
8320 98882 : TableInfo *tinfo = &tblinfo[i];
8321 :
8322 98882 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8323 85026 : continue;
8324 :
8325 : /* OK, we need info for this table */
8326 13856 : if (tbloids->len > 1) /* do we have more than the '{'? */
8327 13606 : appendPQExpBufferChar(tbloids, ',');
8328 13856 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8329 : }
8330 376 : appendPQExpBufferChar(tbloids, '}');
8331 :
8332 376 : appendPQExpBufferStr(query,
8333 : "SELECT c.tableoid, c.oid, "
8334 : "conrelid, conname, confrelid, ");
8335 376 : if (fout->remoteVersion >= 110000)
8336 376 : appendPQExpBufferStr(query, "conindid, ");
8337 : else
8338 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8339 376 : appendPQExpBuffer(query,
8340 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8341 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8342 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8343 : "WHERE contype = 'f' ",
8344 : tbloids->data);
8345 376 : if (fout->remoteVersion >= 110000)
8346 376 : appendPQExpBufferStr(query,
8347 : "AND conparentid = 0 ");
8348 376 : appendPQExpBufferStr(query,
8349 : "ORDER BY conrelid, conname");
8350 :
8351 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8352 :
8353 376 : ntups = PQntuples(res);
8354 :
8355 376 : i_contableoid = PQfnumber(res, "tableoid");
8356 376 : i_conoid = PQfnumber(res, "oid");
8357 376 : i_conrelid = PQfnumber(res, "conrelid");
8358 376 : i_conname = PQfnumber(res, "conname");
8359 376 : i_confrelid = PQfnumber(res, "confrelid");
8360 376 : i_conindid = PQfnumber(res, "conindid");
8361 376 : i_condef = PQfnumber(res, "condef");
8362 :
8363 376 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8364 :
8365 376 : curtblindx = -1;
8366 718 : for (int j = 0; j < ntups; j++)
8367 : {
8368 342 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8369 : TableInfo *reftable;
8370 :
8371 : /*
8372 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8373 : * order.
8374 : */
8375 342 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8376 : {
8377 26990 : while (++curtblindx < numTables)
8378 : {
8379 26990 : tbinfo = &tblinfo[curtblindx];
8380 26990 : if (tbinfo->dobj.catId.oid == conrelid)
8381 322 : break;
8382 : }
8383 322 : if (curtblindx >= numTables)
8384 0 : pg_fatal("unrecognized table OID %u", conrelid);
8385 : }
8386 :
8387 342 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8388 342 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8389 342 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8390 342 : AssignDumpId(&constrinfo[j].dobj);
8391 342 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8392 342 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8393 342 : constrinfo[j].contable = tbinfo;
8394 342 : constrinfo[j].condomain = NULL;
8395 342 : constrinfo[j].contype = 'f';
8396 342 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8397 342 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8398 342 : constrinfo[j].conindex = 0;
8399 342 : constrinfo[j].condeferrable = false;
8400 342 : constrinfo[j].condeferred = false;
8401 342 : constrinfo[j].conislocal = true;
8402 342 : constrinfo[j].separate = true;
8403 :
8404 : /*
8405 : * Restoring an FK that points to a partitioned table requires that
8406 : * all partition indexes have been attached beforehand. Ensure that
8407 : * happens by making the constraint depend on each index partition
8408 : * attach object.
8409 : */
8410 342 : reftable = findTableByOid(constrinfo[j].confrelid);
8411 342 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8412 : {
8413 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8414 :
8415 40 : if (indexOid != InvalidOid)
8416 : {
8417 40 : for (int k = 0; k < reftable->numIndexes; k++)
8418 : {
8419 : IndxInfo *refidx;
8420 :
8421 : /* not our index? */
8422 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8423 0 : continue;
8424 :
8425 40 : refidx = &reftable->indexes[k];
8426 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8427 40 : break;
8428 : }
8429 : }
8430 : }
8431 : }
8432 :
8433 376 : PQclear(res);
8434 :
8435 376 : destroyPQExpBuffer(query);
8436 376 : destroyPQExpBuffer(tbloids);
8437 376 : }
8438 :
8439 : /*
8440 : * addConstrChildIdxDeps
8441 : *
8442 : * Recursive subroutine for getConstraints
8443 : *
8444 : * Given an object representing a foreign key constraint and an index on the
8445 : * partitioned table it references, mark the constraint object as dependent
8446 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8447 : * drilling down to their partitions if any. This ensures that the FK is not
8448 : * restored until the index is fully marked valid.
8449 : */
8450 : static void
8451 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8452 : {
8453 : SimplePtrListCell *cell;
8454 :
8455 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8456 :
8457 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8458 : {
8459 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8460 :
8461 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8462 :
8463 220 : if (attach->partitionIdx->partattaches.head != NULL)
8464 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8465 : }
8466 90 : }
8467 :
8468 : /*
8469 : * getDomainConstraints
8470 : *
8471 : * Get info about constraints on a domain.
8472 : */
8473 : static void
8474 316 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8475 : {
8476 : ConstraintInfo *constrinfo;
8477 316 : PQExpBuffer query = createPQExpBuffer();
8478 : PGresult *res;
8479 : int i_tableoid,
8480 : i_oid,
8481 : i_conname,
8482 : i_consrc,
8483 : i_convalidated,
8484 : i_contype;
8485 : int ntups;
8486 :
8487 316 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8488 : {
8489 : /*
8490 : * Set up query for constraint-specific details. For servers 17 and
8491 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8492 : * just the latter.
8493 : */
8494 86 : appendPQExpBuffer(query,
8495 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8496 : "SELECT tableoid, oid, conname, "
8497 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8498 : "convalidated, contype "
8499 : "FROM pg_catalog.pg_constraint "
8500 : "WHERE contypid = $1 AND contype IN (%s) "
8501 : "ORDER BY conname",
8502 86 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8503 :
8504 86 : ExecuteSqlStatement(fout, query->data);
8505 :
8506 86 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8507 : }
8508 :
8509 316 : printfPQExpBuffer(query,
8510 : "EXECUTE getDomainConstraints('%u')",
8511 : tyinfo->dobj.catId.oid);
8512 :
8513 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8514 :
8515 316 : ntups = PQntuples(res);
8516 :
8517 316 : i_tableoid = PQfnumber(res, "tableoid");
8518 316 : i_oid = PQfnumber(res, "oid");
8519 316 : i_conname = PQfnumber(res, "conname");
8520 316 : i_consrc = PQfnumber(res, "consrc");
8521 316 : i_convalidated = PQfnumber(res, "convalidated");
8522 316 : i_contype = PQfnumber(res, "contype");
8523 :
8524 316 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8525 316 : tyinfo->domChecks = constrinfo;
8526 :
8527 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8528 648 : for (int i = 0, j = 0; i < ntups; i++)
8529 : {
8530 332 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8531 332 : char contype = (PQgetvalue(res, i, i_contype))[0];
8532 : ConstraintInfo *constraint;
8533 :
8534 332 : if (contype == CONSTRAINT_CHECK)
8535 : {
8536 226 : constraint = &constrinfo[j++];
8537 226 : tyinfo->nDomChecks++;
8538 : }
8539 : else
8540 : {
8541 : Assert(contype == CONSTRAINT_NOTNULL);
8542 : Assert(tyinfo->notnull == NULL);
8543 : /* use last item in array for the not-null constraint */
8544 106 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8545 106 : constraint = tyinfo->notnull;
8546 : }
8547 :
8548 332 : constraint->dobj.objType = DO_CONSTRAINT;
8549 332 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8550 332 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8551 332 : AssignDumpId(&(constraint->dobj));
8552 332 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8553 332 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8554 332 : constraint->contable = NULL;
8555 332 : constraint->condomain = tyinfo;
8556 332 : constraint->contype = contype;
8557 332 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8558 332 : constraint->confrelid = InvalidOid;
8559 332 : constraint->conindex = 0;
8560 332 : constraint->condeferrable = false;
8561 332 : constraint->condeferred = false;
8562 332 : constraint->conislocal = true;
8563 :
8564 332 : constraint->separate = !validated;
8565 :
8566 : /*
8567 : * Make the domain depend on the constraint, ensuring it won't be
8568 : * output till any constraint dependencies are OK. If the constraint
8569 : * has not been validated, it's going to be dumped after the domain
8570 : * anyway, so this doesn't matter.
8571 : */
8572 332 : if (validated)
8573 322 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8574 : }
8575 :
8576 316 : PQclear(res);
8577 :
8578 316 : destroyPQExpBuffer(query);
8579 316 : }
8580 :
8581 : /*
8582 : * getRules
8583 : * get basic information about every rule in the system
8584 : */
8585 : void
8586 376 : getRules(Archive *fout)
8587 : {
8588 : PGresult *res;
8589 : int ntups;
8590 : int i;
8591 376 : PQExpBuffer query = createPQExpBuffer();
8592 : RuleInfo *ruleinfo;
8593 : int i_tableoid;
8594 : int i_oid;
8595 : int i_rulename;
8596 : int i_ruletable;
8597 : int i_ev_type;
8598 : int i_is_instead;
8599 : int i_ev_enabled;
8600 :
8601 376 : appendPQExpBufferStr(query, "SELECT "
8602 : "tableoid, oid, rulename, "
8603 : "ev_class AS ruletable, ev_type, is_instead, "
8604 : "ev_enabled "
8605 : "FROM pg_rewrite "
8606 : "ORDER BY oid");
8607 :
8608 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8609 :
8610 376 : ntups = PQntuples(res);
8611 :
8612 376 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8613 :
8614 376 : i_tableoid = PQfnumber(res, "tableoid");
8615 376 : i_oid = PQfnumber(res, "oid");
8616 376 : i_rulename = PQfnumber(res, "rulename");
8617 376 : i_ruletable = PQfnumber(res, "ruletable");
8618 376 : i_ev_type = PQfnumber(res, "ev_type");
8619 376 : i_is_instead = PQfnumber(res, "is_instead");
8620 376 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8621 :
8622 58686 : for (i = 0; i < ntups; i++)
8623 : {
8624 : Oid ruletableoid;
8625 :
8626 58310 : ruleinfo[i].dobj.objType = DO_RULE;
8627 58310 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8628 58310 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8629 58310 : AssignDumpId(&ruleinfo[i].dobj);
8630 58310 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8631 58310 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8632 58310 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8633 58310 : if (ruleinfo[i].ruletable == NULL)
8634 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8635 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8636 58310 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8637 58310 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8638 58310 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8639 58310 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8640 58310 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8641 58310 : if (ruleinfo[i].ruletable)
8642 : {
8643 : /*
8644 : * If the table is a view or materialized view, force its ON
8645 : * SELECT rule to be sorted before the view itself --- this
8646 : * ensures that any dependencies for the rule affect the table's
8647 : * positioning. Other rules are forced to appear after their
8648 : * table.
8649 : */
8650 58310 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8651 1398 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8652 57848 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8653 : {
8654 57012 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8655 57012 : ruleinfo[i].dobj.dumpId);
8656 : /* We'll merge the rule into CREATE VIEW, if possible */
8657 57012 : ruleinfo[i].separate = false;
8658 : }
8659 : else
8660 : {
8661 1298 : addObjectDependency(&ruleinfo[i].dobj,
8662 1298 : ruleinfo[i].ruletable->dobj.dumpId);
8663 1298 : ruleinfo[i].separate = true;
8664 : }
8665 : }
8666 : else
8667 0 : ruleinfo[i].separate = true;
8668 : }
8669 :
8670 376 : PQclear(res);
8671 :
8672 376 : destroyPQExpBuffer(query);
8673 376 : }
8674 :
8675 : /*
8676 : * getTriggers
8677 : * get information about every trigger on a dumpable table
8678 : *
8679 : * Note: trigger data is not returned directly to the caller, but it
8680 : * does get entered into the DumpableObject tables.
8681 : */
8682 : void
8683 376 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8684 : {
8685 376 : PQExpBuffer query = createPQExpBuffer();
8686 376 : PQExpBuffer tbloids = createPQExpBuffer();
8687 : PGresult *res;
8688 : int ntups;
8689 : int curtblindx;
8690 : TriggerInfo *tginfo;
8691 : int i_tableoid,
8692 : i_oid,
8693 : i_tgrelid,
8694 : i_tgname,
8695 : i_tgenabled,
8696 : i_tgispartition,
8697 : i_tgdef;
8698 :
8699 : /*
8700 : * We want to perform just one query against pg_trigger. However, we
8701 : * mustn't try to select every row of the catalog and then sort it out on
8702 : * the client side, because some of the server-side functions we need
8703 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8704 : * build an array of the OIDs of tables we care about (and now have lock
8705 : * on!), and use a WHERE clause to constrain which rows are selected.
8706 : */
8707 376 : appendPQExpBufferChar(tbloids, '{');
8708 99258 : for (int i = 0; i < numTables; i++)
8709 : {
8710 98882 : TableInfo *tbinfo = &tblinfo[i];
8711 :
8712 98882 : if (!tbinfo->hastriggers ||
8713 2228 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8714 97178 : continue;
8715 :
8716 : /* OK, we need info for this table */
8717 1704 : if (tbloids->len > 1) /* do we have more than the '{'? */
8718 1602 : appendPQExpBufferChar(tbloids, ',');
8719 1704 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8720 : }
8721 376 : appendPQExpBufferChar(tbloids, '}');
8722 :
8723 376 : if (fout->remoteVersion >= 150000)
8724 : {
8725 : /*
8726 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8727 : * result in non-forward-compatible dumps of WHEN clauses due to
8728 : * under-parenthesization.
8729 : *
8730 : * NB: We need to see partition triggers in case the tgenabled flag
8731 : * has been changed from the parent.
8732 : */
8733 376 : appendPQExpBuffer(query,
8734 : "SELECT t.tgrelid, t.tgname, "
8735 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8736 : "t.tgenabled, t.tableoid, t.oid, "
8737 : "t.tgparentid <> 0 AS tgispartition\n"
8738 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8739 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8740 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8741 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8742 : "OR t.tgenabled != u.tgenabled) "
8743 : "ORDER BY t.tgrelid, t.tgname",
8744 : tbloids->data);
8745 : }
8746 0 : else if (fout->remoteVersion >= 130000)
8747 : {
8748 : /*
8749 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8750 : * result in non-forward-compatible dumps of WHEN clauses due to
8751 : * under-parenthesization.
8752 : *
8753 : * NB: We need to see tgisinternal triggers in partitions, in case the
8754 : * tgenabled flag has been changed from the parent.
8755 : */
8756 0 : appendPQExpBuffer(query,
8757 : "SELECT t.tgrelid, t.tgname, "
8758 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8759 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8760 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8761 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8762 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8763 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8764 : "ORDER BY t.tgrelid, t.tgname",
8765 : tbloids->data);
8766 : }
8767 0 : else if (fout->remoteVersion >= 110000)
8768 : {
8769 : /*
8770 : * NB: We need to see tgisinternal triggers in partitions, in case the
8771 : * tgenabled flag has been changed from the parent. No tgparentid in
8772 : * version 11-12, so we have to match them via pg_depend.
8773 : *
8774 : * See above about pretty=true in pg_get_triggerdef.
8775 : */
8776 0 : appendPQExpBuffer(query,
8777 : "SELECT t.tgrelid, t.tgname, "
8778 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8779 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8780 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8781 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8782 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8783 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8784 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8785 : " d.objid = t.oid "
8786 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8787 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8788 : "ORDER BY t.tgrelid, t.tgname",
8789 : tbloids->data);
8790 : }
8791 : else
8792 : {
8793 : /* See above about pretty=true in pg_get_triggerdef */
8794 0 : appendPQExpBuffer(query,
8795 : "SELECT t.tgrelid, t.tgname, "
8796 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8797 : "t.tgenabled, false as tgispartition, "
8798 : "t.tableoid, t.oid "
8799 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8800 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8801 : "WHERE NOT tgisinternal "
8802 : "ORDER BY t.tgrelid, t.tgname",
8803 : tbloids->data);
8804 : }
8805 :
8806 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8807 :
8808 376 : ntups = PQntuples(res);
8809 :
8810 376 : i_tableoid = PQfnumber(res, "tableoid");
8811 376 : i_oid = PQfnumber(res, "oid");
8812 376 : i_tgrelid = PQfnumber(res, "tgrelid");
8813 376 : i_tgname = PQfnumber(res, "tgname");
8814 376 : i_tgenabled = PQfnumber(res, "tgenabled");
8815 376 : i_tgispartition = PQfnumber(res, "tgispartition");
8816 376 : i_tgdef = PQfnumber(res, "tgdef");
8817 :
8818 376 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8819 :
8820 : /*
8821 : * Outer loop iterates once per table, not once per row. Incrementing of
8822 : * j is handled by the inner loop.
8823 : */
8824 376 : curtblindx = -1;
8825 988 : for (int j = 0; j < ntups;)
8826 : {
8827 612 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8828 612 : TableInfo *tbinfo = NULL;
8829 : int numtrigs;
8830 :
8831 : /* Count rows for this table */
8832 1046 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8833 944 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8834 510 : break;
8835 :
8836 : /*
8837 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8838 : * order.
8839 : */
8840 32074 : while (++curtblindx < numTables)
8841 : {
8842 32074 : tbinfo = &tblinfo[curtblindx];
8843 32074 : if (tbinfo->dobj.catId.oid == tgrelid)
8844 612 : break;
8845 : }
8846 612 : if (curtblindx >= numTables)
8847 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8848 :
8849 : /* Save data for this table */
8850 612 : tbinfo->triggers = tginfo + j;
8851 612 : tbinfo->numTriggers = numtrigs;
8852 :
8853 1658 : for (int c = 0; c < numtrigs; c++, j++)
8854 : {
8855 1046 : tginfo[j].dobj.objType = DO_TRIGGER;
8856 1046 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8857 1046 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8858 1046 : AssignDumpId(&tginfo[j].dobj);
8859 1046 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8860 1046 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8861 1046 : tginfo[j].tgtable = tbinfo;
8862 1046 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8863 1046 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8864 1046 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8865 : }
8866 : }
8867 :
8868 376 : PQclear(res);
8869 :
8870 376 : destroyPQExpBuffer(query);
8871 376 : destroyPQExpBuffer(tbloids);
8872 376 : }
8873 :
8874 : /*
8875 : * getEventTriggers
8876 : * get information about event triggers
8877 : */
8878 : void
8879 376 : getEventTriggers(Archive *fout)
8880 : {
8881 : int i;
8882 : PQExpBuffer query;
8883 : PGresult *res;
8884 : EventTriggerInfo *evtinfo;
8885 : int i_tableoid,
8886 : i_oid,
8887 : i_evtname,
8888 : i_evtevent,
8889 : i_evtowner,
8890 : i_evttags,
8891 : i_evtfname,
8892 : i_evtenabled;
8893 : int ntups;
8894 :
8895 : /* Before 9.3, there are no event triggers */
8896 376 : if (fout->remoteVersion < 90300)
8897 0 : return;
8898 :
8899 376 : query = createPQExpBuffer();
8900 :
8901 376 : appendPQExpBufferStr(query,
8902 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8903 : "evtevent, evtowner, "
8904 : "array_to_string(array("
8905 : "select quote_literal(x) "
8906 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8907 : "e.evtfoid::regproc as evtfname "
8908 : "FROM pg_event_trigger e "
8909 : "ORDER BY e.oid");
8910 :
8911 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8912 :
8913 376 : ntups = PQntuples(res);
8914 :
8915 376 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8916 :
8917 376 : i_tableoid = PQfnumber(res, "tableoid");
8918 376 : i_oid = PQfnumber(res, "oid");
8919 376 : i_evtname = PQfnumber(res, "evtname");
8920 376 : i_evtevent = PQfnumber(res, "evtevent");
8921 376 : i_evtowner = PQfnumber(res, "evtowner");
8922 376 : i_evttags = PQfnumber(res, "evttags");
8923 376 : i_evtfname = PQfnumber(res, "evtfname");
8924 376 : i_evtenabled = PQfnumber(res, "evtenabled");
8925 :
8926 480 : for (i = 0; i < ntups; i++)
8927 : {
8928 104 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8929 104 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8930 104 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8931 104 : AssignDumpId(&evtinfo[i].dobj);
8932 104 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8933 104 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8934 104 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8935 104 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8936 104 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8937 104 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8938 104 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8939 :
8940 : /* Decide whether we want to dump it */
8941 104 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8942 : }
8943 :
8944 376 : PQclear(res);
8945 :
8946 376 : destroyPQExpBuffer(query);
8947 : }
8948 :
8949 : /*
8950 : * getProcLangs
8951 : * get basic information about every procedural language in the system
8952 : *
8953 : * NB: this must run after getFuncs() because we assume we can do
8954 : * findFuncByOid().
8955 : */
8956 : void
8957 376 : getProcLangs(Archive *fout)
8958 : {
8959 : PGresult *res;
8960 : int ntups;
8961 : int i;
8962 376 : PQExpBuffer query = createPQExpBuffer();
8963 : ProcLangInfo *planginfo;
8964 : int i_tableoid;
8965 : int i_oid;
8966 : int i_lanname;
8967 : int i_lanpltrusted;
8968 : int i_lanplcallfoid;
8969 : int i_laninline;
8970 : int i_lanvalidator;
8971 : int i_lanacl;
8972 : int i_acldefault;
8973 : int i_lanowner;
8974 :
8975 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8976 : "lanname, lanpltrusted, lanplcallfoid, "
8977 : "laninline, lanvalidator, "
8978 : "lanacl, "
8979 : "acldefault('l', lanowner) AS acldefault, "
8980 : "lanowner "
8981 : "FROM pg_language "
8982 : "WHERE lanispl "
8983 : "ORDER BY oid");
8984 :
8985 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8986 :
8987 376 : ntups = PQntuples(res);
8988 :
8989 376 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8990 :
8991 376 : i_tableoid = PQfnumber(res, "tableoid");
8992 376 : i_oid = PQfnumber(res, "oid");
8993 376 : i_lanname = PQfnumber(res, "lanname");
8994 376 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8995 376 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8996 376 : i_laninline = PQfnumber(res, "laninline");
8997 376 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8998 376 : i_lanacl = PQfnumber(res, "lanacl");
8999 376 : i_acldefault = PQfnumber(res, "acldefault");
9000 376 : i_lanowner = PQfnumber(res, "lanowner");
9001 :
9002 842 : for (i = 0; i < ntups; i++)
9003 : {
9004 466 : planginfo[i].dobj.objType = DO_PROCLANG;
9005 466 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9006 466 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9007 466 : AssignDumpId(&planginfo[i].dobj);
9008 :
9009 466 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9010 466 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9011 466 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9012 466 : planginfo[i].dacl.privtype = 0;
9013 466 : planginfo[i].dacl.initprivs = NULL;
9014 466 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9015 466 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9016 466 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9017 466 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9018 466 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9019 :
9020 : /* Decide whether we want to dump it */
9021 466 : selectDumpableProcLang(&(planginfo[i]), fout);
9022 :
9023 : /* Mark whether language has an ACL */
9024 466 : if (!PQgetisnull(res, i, i_lanacl))
9025 90 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9026 : }
9027 :
9028 376 : PQclear(res);
9029 :
9030 376 : destroyPQExpBuffer(query);
9031 376 : }
9032 :
9033 : /*
9034 : * getCasts
9035 : * get basic information about most casts in the system
9036 : *
9037 : * Skip casts from a range to its multirange, since we'll create those
9038 : * automatically.
9039 : */
9040 : void
9041 376 : getCasts(Archive *fout)
9042 : {
9043 : PGresult *res;
9044 : int ntups;
9045 : int i;
9046 376 : PQExpBuffer query = createPQExpBuffer();
9047 : CastInfo *castinfo;
9048 : int i_tableoid;
9049 : int i_oid;
9050 : int i_castsource;
9051 : int i_casttarget;
9052 : int i_castfunc;
9053 : int i_castcontext;
9054 : int i_castmethod;
9055 :
9056 376 : if (fout->remoteVersion >= 140000)
9057 : {
9058 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9059 : "castsource, casttarget, castfunc, castcontext, "
9060 : "castmethod "
9061 : "FROM pg_cast c "
9062 : "WHERE NOT EXISTS ( "
9063 : "SELECT 1 FROM pg_range r "
9064 : "WHERE c.castsource = r.rngtypid "
9065 : "AND c.casttarget = r.rngmultitypid "
9066 : ") "
9067 : "ORDER BY 3,4");
9068 : }
9069 : else
9070 : {
9071 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9072 : "castsource, casttarget, castfunc, castcontext, "
9073 : "castmethod "
9074 : "FROM pg_cast ORDER BY 3,4");
9075 : }
9076 :
9077 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9078 :
9079 376 : ntups = PQntuples(res);
9080 :
9081 376 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9082 :
9083 376 : i_tableoid = PQfnumber(res, "tableoid");
9084 376 : i_oid = PQfnumber(res, "oid");
9085 376 : i_castsource = PQfnumber(res, "castsource");
9086 376 : i_casttarget = PQfnumber(res, "casttarget");
9087 376 : i_castfunc = PQfnumber(res, "castfunc");
9088 376 : i_castcontext = PQfnumber(res, "castcontext");
9089 376 : i_castmethod = PQfnumber(res, "castmethod");
9090 :
9091 91166 : for (i = 0; i < ntups; i++)
9092 : {
9093 : PQExpBufferData namebuf;
9094 : TypeInfo *sTypeInfo;
9095 : TypeInfo *tTypeInfo;
9096 :
9097 90790 : castinfo[i].dobj.objType = DO_CAST;
9098 90790 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9099 90790 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9100 90790 : AssignDumpId(&castinfo[i].dobj);
9101 90790 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9102 90790 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9103 90790 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9104 90790 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9105 90790 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9106 :
9107 : /*
9108 : * Try to name cast as concatenation of typnames. This is only used
9109 : * for purposes of sorting. If we fail to find either type, the name
9110 : * will be an empty string.
9111 : */
9112 90790 : initPQExpBuffer(&namebuf);
9113 90790 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9114 90790 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9115 90790 : if (sTypeInfo && tTypeInfo)
9116 90790 : appendPQExpBuffer(&namebuf, "%s %s",
9117 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9118 90790 : castinfo[i].dobj.name = namebuf.data;
9119 :
9120 : /* Decide whether we want to dump it */
9121 90790 : selectDumpableCast(&(castinfo[i]), fout);
9122 : }
9123 :
9124 376 : PQclear(res);
9125 :
9126 376 : destroyPQExpBuffer(query);
9127 376 : }
9128 :
9129 : static char *
9130 176 : get_language_name(Archive *fout, Oid langid)
9131 : {
9132 : PQExpBuffer query;
9133 : PGresult *res;
9134 : char *lanname;
9135 :
9136 176 : query = createPQExpBuffer();
9137 176 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9138 176 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9139 176 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9140 176 : destroyPQExpBuffer(query);
9141 176 : PQclear(res);
9142 :
9143 176 : return lanname;
9144 : }
9145 :
9146 : /*
9147 : * getTransforms
9148 : * get basic information about every transform in the system
9149 : */
9150 : void
9151 376 : getTransforms(Archive *fout)
9152 : {
9153 : PGresult *res;
9154 : int ntups;
9155 : int i;
9156 : PQExpBuffer query;
9157 : TransformInfo *transforminfo;
9158 : int i_tableoid;
9159 : int i_oid;
9160 : int i_trftype;
9161 : int i_trflang;
9162 : int i_trffromsql;
9163 : int i_trftosql;
9164 :
9165 : /* Transforms didn't exist pre-9.5 */
9166 376 : if (fout->remoteVersion < 90500)
9167 0 : return;
9168 :
9169 376 : query = createPQExpBuffer();
9170 :
9171 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9172 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9173 : "FROM pg_transform "
9174 : "ORDER BY 3,4");
9175 :
9176 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9177 :
9178 376 : ntups = PQntuples(res);
9179 :
9180 376 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9181 :
9182 376 : i_tableoid = PQfnumber(res, "tableoid");
9183 376 : i_oid = PQfnumber(res, "oid");
9184 376 : i_trftype = PQfnumber(res, "trftype");
9185 376 : i_trflang = PQfnumber(res, "trflang");
9186 376 : i_trffromsql = PQfnumber(res, "trffromsql");
9187 376 : i_trftosql = PQfnumber(res, "trftosql");
9188 :
9189 480 : for (i = 0; i < ntups; i++)
9190 : {
9191 : PQExpBufferData namebuf;
9192 : TypeInfo *typeInfo;
9193 : char *lanname;
9194 :
9195 104 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9196 104 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9197 104 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9198 104 : AssignDumpId(&transforminfo[i].dobj);
9199 104 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9200 104 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9201 104 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9202 104 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9203 :
9204 : /*
9205 : * Try to name transform as concatenation of type and language name.
9206 : * This is only used for purposes of sorting. If we fail to find
9207 : * either, the name will be an empty string.
9208 : */
9209 104 : initPQExpBuffer(&namebuf);
9210 104 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9211 104 : lanname = get_language_name(fout, transforminfo[i].trflang);
9212 104 : if (typeInfo && lanname)
9213 104 : appendPQExpBuffer(&namebuf, "%s %s",
9214 : typeInfo->dobj.name, lanname);
9215 104 : transforminfo[i].dobj.name = namebuf.data;
9216 104 : free(lanname);
9217 :
9218 : /* Decide whether we want to dump it */
9219 104 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9220 : }
9221 :
9222 376 : PQclear(res);
9223 :
9224 376 : destroyPQExpBuffer(query);
9225 : }
9226 :
9227 : /*
9228 : * getTableAttrs -
9229 : * for each interesting table, read info about its attributes
9230 : * (names, types, default values, CHECK constraints, etc)
9231 : *
9232 : * modifies tblinfo
9233 : */
9234 : void
9235 376 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9236 : {
9237 376 : DumpOptions *dopt = fout->dopt;
9238 376 : PQExpBuffer q = createPQExpBuffer();
9239 376 : PQExpBuffer tbloids = createPQExpBuffer();
9240 376 : PQExpBuffer checkoids = createPQExpBuffer();
9241 376 : PQExpBuffer invalidnotnulloids = NULL;
9242 : PGresult *res;
9243 : int ntups;
9244 : int curtblindx;
9245 : int i_attrelid;
9246 : int i_attnum;
9247 : int i_attname;
9248 : int i_atttypname;
9249 : int i_attstattarget;
9250 : int i_attstorage;
9251 : int i_typstorage;
9252 : int i_attidentity;
9253 : int i_attgenerated;
9254 : int i_attisdropped;
9255 : int i_attlen;
9256 : int i_attalign;
9257 : int i_attislocal;
9258 : int i_notnull_name;
9259 : int i_notnull_comment;
9260 : int i_notnull_noinherit;
9261 : int i_notnull_islocal;
9262 : int i_notnull_invalidoid;
9263 : int i_attoptions;
9264 : int i_attcollation;
9265 : int i_attcompression;
9266 : int i_attfdwoptions;
9267 : int i_attmissingval;
9268 : int i_atthasdef;
9269 :
9270 : /*
9271 : * We want to perform just one query against pg_attribute, and then just
9272 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9273 : * (for CHECK constraints and for NOT NULL constraints). However, we
9274 : * mustn't try to select every row of those catalogs and then sort it out
9275 : * on the client side, because some of the server-side functions we need
9276 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9277 : * build an array of the OIDs of tables we care about (and now have lock
9278 : * on!), and use a WHERE clause to constrain which rows are selected.
9279 : */
9280 376 : appendPQExpBufferChar(tbloids, '{');
9281 376 : appendPQExpBufferChar(checkoids, '{');
9282 99258 : for (int i = 0; i < numTables; i++)
9283 : {
9284 98882 : TableInfo *tbinfo = &tblinfo[i];
9285 :
9286 : /* Don't bother to collect info for sequences */
9287 98882 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9288 1276 : continue;
9289 :
9290 : /*
9291 : * Don't bother with uninteresting tables, either. For binary
9292 : * upgrades, this is bypassed for pg_largeobject_metadata and
9293 : * pg_shdepend so that the columns names are collected for the
9294 : * corresponding COPY commands. Restoring the data for those catalogs
9295 : * is faster than restoring the equivalent set of large object
9296 : * commands. We can only do this for upgrades from v12 and newer; in
9297 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9298 : * the OID column is hidden and won't be dumped.
9299 : */
9300 97606 : if (!tbinfo->interesting &&
9301 84456 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9302 16376 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9303 16300 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9304 84304 : continue;
9305 :
9306 : /* OK, we need info for this table */
9307 13302 : if (tbloids->len > 1) /* do we have more than the '{'? */
9308 13014 : appendPQExpBufferChar(tbloids, ',');
9309 13302 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9310 :
9311 13302 : if (tbinfo->ncheck > 0)
9312 : {
9313 : /* Also make a list of the ones with check constraints */
9314 1056 : if (checkoids->len > 1) /* do we have more than the '{'? */
9315 918 : appendPQExpBufferChar(checkoids, ',');
9316 1056 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9317 : }
9318 : }
9319 376 : appendPQExpBufferChar(tbloids, '}');
9320 376 : appendPQExpBufferChar(checkoids, '}');
9321 :
9322 : /*
9323 : * Find all the user attributes and their types.
9324 : *
9325 : * Since we only want to dump COLLATE clauses for attributes whose
9326 : * collation is different from their type's default, we use a CASE here to
9327 : * suppress uninteresting attcollations cheaply.
9328 : */
9329 376 : appendPQExpBufferStr(q,
9330 : "SELECT\n"
9331 : "a.attrelid,\n"
9332 : "a.attnum,\n"
9333 : "a.attname,\n"
9334 : "a.attstattarget,\n"
9335 : "a.attstorage,\n"
9336 : "t.typstorage,\n"
9337 : "a.atthasdef,\n"
9338 : "a.attisdropped,\n"
9339 : "a.attlen,\n"
9340 : "a.attalign,\n"
9341 : "a.attislocal,\n"
9342 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9343 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9344 : "CASE WHEN a.attcollation <> t.typcollation "
9345 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9346 : "pg_catalog.array_to_string(ARRAY("
9347 : "SELECT pg_catalog.quote_ident(option_name) || "
9348 : "' ' || pg_catalog.quote_literal(option_value) "
9349 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9350 : "ORDER BY option_name"
9351 : "), E',\n ') AS attfdwoptions,\n");
9352 :
9353 : /*
9354 : * Find out any NOT NULL markings for each column. In 18 and up we read
9355 : * pg_constraint to obtain the constraint name, and for valid constraints
9356 : * also pg_description to obtain its comment. notnull_noinherit is set
9357 : * according to the NO INHERIT property. For versions prior to 18, we
9358 : * store an empty string as the name when a constraint is marked as
9359 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9360 : * without a name); also, such cases are never NO INHERIT.
9361 : *
9362 : * For invalid constraints, we need to store their OIDs for processing
9363 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9364 : * is invalid, and NULL otherwise. Their comments are handled not here
9365 : * but by collectComments, because they're their own dumpable object.
9366 : *
9367 : * We track in notnull_islocal whether the constraint was defined directly
9368 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9369 : * might modify this later.
9370 : */
9371 376 : if (fout->remoteVersion >= 180000)
9372 376 : appendPQExpBufferStr(q,
9373 : "co.conname AS notnull_name,\n"
9374 : "CASE WHEN co.convalidated THEN pt.description"
9375 : " ELSE NULL END AS notnull_comment,\n"
9376 : "CASE WHEN NOT co.convalidated THEN co.oid "
9377 : "ELSE NULL END AS notnull_invalidoid,\n"
9378 : "co.connoinherit AS notnull_noinherit,\n"
9379 : "co.conislocal AS notnull_islocal,\n");
9380 : else
9381 0 : appendPQExpBufferStr(q,
9382 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9383 : "NULL AS notnull_comment,\n"
9384 : "NULL AS notnull_invalidoid,\n"
9385 : "false AS notnull_noinherit,\n"
9386 : "CASE WHEN a.attislocal THEN true\n"
9387 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9388 : " ELSE false\n"
9389 : "END AS notnull_islocal,\n");
9390 :
9391 376 : if (fout->remoteVersion >= 140000)
9392 376 : appendPQExpBufferStr(q,
9393 : "a.attcompression AS attcompression,\n");
9394 : else
9395 0 : appendPQExpBufferStr(q,
9396 : "'' AS attcompression,\n");
9397 :
9398 376 : if (fout->remoteVersion >= 100000)
9399 376 : appendPQExpBufferStr(q,
9400 : "a.attidentity,\n");
9401 : else
9402 0 : appendPQExpBufferStr(q,
9403 : "'' AS attidentity,\n");
9404 :
9405 376 : if (fout->remoteVersion >= 110000)
9406 376 : appendPQExpBufferStr(q,
9407 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9408 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9409 : else
9410 0 : appendPQExpBufferStr(q,
9411 : "NULL AS attmissingval,\n");
9412 :
9413 376 : if (fout->remoteVersion >= 120000)
9414 376 : appendPQExpBufferStr(q,
9415 : "a.attgenerated\n");
9416 : else
9417 0 : appendPQExpBufferStr(q,
9418 : "'' AS attgenerated\n");
9419 :
9420 : /* need left join to pg_type to not fail on dropped columns ... */
9421 376 : appendPQExpBuffer(q,
9422 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9423 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9424 : "LEFT JOIN pg_catalog.pg_type t "
9425 : "ON (a.atttypid = t.oid)\n",
9426 : tbloids->data);
9427 :
9428 : /*
9429 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9430 : * entries and pg_description to get their comments.
9431 : */
9432 376 : if (fout->remoteVersion >= 180000)
9433 376 : appendPQExpBufferStr(q,
9434 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9435 : "(a.attrelid = co.conrelid\n"
9436 : " AND co.contype = 'n' AND "
9437 : "co.conkey = array[a.attnum])\n"
9438 : " LEFT JOIN pg_catalog.pg_description pt ON "
9439 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9440 :
9441 376 : appendPQExpBufferStr(q,
9442 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9443 : "ORDER BY a.attrelid, a.attnum");
9444 :
9445 376 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9446 :
9447 376 : ntups = PQntuples(res);
9448 :
9449 376 : i_attrelid = PQfnumber(res, "attrelid");
9450 376 : i_attnum = PQfnumber(res, "attnum");
9451 376 : i_attname = PQfnumber(res, "attname");
9452 376 : i_atttypname = PQfnumber(res, "atttypname");
9453 376 : i_attstattarget = PQfnumber(res, "attstattarget");
9454 376 : i_attstorage = PQfnumber(res, "attstorage");
9455 376 : i_typstorage = PQfnumber(res, "typstorage");
9456 376 : i_attidentity = PQfnumber(res, "attidentity");
9457 376 : i_attgenerated = PQfnumber(res, "attgenerated");
9458 376 : i_attisdropped = PQfnumber(res, "attisdropped");
9459 376 : i_attlen = PQfnumber(res, "attlen");
9460 376 : i_attalign = PQfnumber(res, "attalign");
9461 376 : i_attislocal = PQfnumber(res, "attislocal");
9462 376 : i_notnull_name = PQfnumber(res, "notnull_name");
9463 376 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9464 376 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9465 376 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9466 376 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9467 376 : i_attoptions = PQfnumber(res, "attoptions");
9468 376 : i_attcollation = PQfnumber(res, "attcollation");
9469 376 : i_attcompression = PQfnumber(res, "attcompression");
9470 376 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9471 376 : i_attmissingval = PQfnumber(res, "attmissingval");
9472 376 : i_atthasdef = PQfnumber(res, "atthasdef");
9473 :
9474 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9475 376 : resetPQExpBuffer(tbloids);
9476 376 : appendPQExpBufferChar(tbloids, '{');
9477 :
9478 : /*
9479 : * Outer loop iterates once per table, not once per row. Incrementing of
9480 : * r is handled by the inner loop.
9481 : */
9482 376 : curtblindx = -1;
9483 13402 : for (int r = 0; r < ntups;)
9484 : {
9485 13026 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9486 13026 : TableInfo *tbinfo = NULL;
9487 : int numatts;
9488 : bool hasdefaults;
9489 :
9490 : /* Count rows for this table */
9491 49372 : for (numatts = 1; numatts < ntups - r; numatts++)
9492 49090 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9493 12744 : break;
9494 :
9495 : /*
9496 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9497 : * order.
9498 : */
9499 68256 : while (++curtblindx < numTables)
9500 : {
9501 68256 : tbinfo = &tblinfo[curtblindx];
9502 68256 : if (tbinfo->dobj.catId.oid == attrelid)
9503 13026 : break;
9504 : }
9505 13026 : if (curtblindx >= numTables)
9506 0 : pg_fatal("unrecognized table OID %u", attrelid);
9507 : /* cross-check that we only got requested tables */
9508 13026 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9509 13026 : (!tbinfo->interesting &&
9510 152 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9511 152 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9512 76 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9513 0 : pg_fatal("unexpected column data for table \"%s\"",
9514 : tbinfo->dobj.name);
9515 :
9516 : /* Save data for this table */
9517 13026 : tbinfo->numatts = numatts;
9518 13026 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9519 13026 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9520 13026 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9521 13026 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9522 13026 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9523 13026 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9524 13026 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9525 13026 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9526 13026 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9527 13026 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9528 13026 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9529 13026 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9530 13026 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9531 13026 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9532 13026 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9533 13026 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9534 13026 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9535 13026 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9536 13026 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9537 13026 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9538 13026 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9539 13026 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9540 13026 : hasdefaults = false;
9541 :
9542 62398 : for (int j = 0; j < numatts; j++, r++)
9543 : {
9544 49372 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9545 0 : pg_fatal("invalid column numbering in table \"%s\"",
9546 : tbinfo->dobj.name);
9547 49372 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9548 49372 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9549 49372 : if (PQgetisnull(res, r, i_attstattarget))
9550 49292 : tbinfo->attstattarget[j] = -1;
9551 : else
9552 80 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9553 49372 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9554 49372 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9555 49372 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9556 49372 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9557 49372 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9558 49372 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9559 49372 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9560 49372 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9561 49372 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9562 :
9563 : /* Handle not-null constraint name and flags */
9564 49372 : determineNotNullFlags(fout, res, r,
9565 : tbinfo, j,
9566 : i_notnull_name,
9567 : i_notnull_comment,
9568 : i_notnull_invalidoid,
9569 : i_notnull_noinherit,
9570 : i_notnull_islocal,
9571 : &invalidnotnulloids);
9572 :
9573 49372 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9574 49372 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9575 49372 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9576 49372 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9577 49372 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9578 49372 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9579 49372 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9580 49372 : tbinfo->attrdefs[j] = NULL; /* fix below */
9581 49372 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9582 2536 : hasdefaults = true;
9583 : }
9584 :
9585 13026 : if (hasdefaults)
9586 : {
9587 : /* Collect OIDs of interesting tables that have defaults */
9588 1904 : if (tbloids->len > 1) /* do we have more than the '{'? */
9589 1768 : appendPQExpBufferChar(tbloids, ',');
9590 1904 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9591 : }
9592 : }
9593 :
9594 : /* If invalidnotnulloids has any data, finalize it */
9595 376 : if (invalidnotnulloids != NULL)
9596 86 : appendPQExpBufferChar(invalidnotnulloids, '}');
9597 :
9598 376 : PQclear(res);
9599 :
9600 : /*
9601 : * Now get info about column defaults. This is skipped for a data-only
9602 : * dump, as it is only needed for table schemas.
9603 : */
9604 376 : if (dopt->dumpSchema && tbloids->len > 1)
9605 : {
9606 : AttrDefInfo *attrdefs;
9607 : int numDefaults;
9608 120 : TableInfo *tbinfo = NULL;
9609 :
9610 120 : pg_log_info("finding table default expressions");
9611 :
9612 120 : appendPQExpBufferChar(tbloids, '}');
9613 :
9614 120 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9615 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9616 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9617 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9618 : "ORDER BY a.adrelid, a.adnum",
9619 : tbloids->data);
9620 :
9621 120 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9622 :
9623 120 : numDefaults = PQntuples(res);
9624 120 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9625 :
9626 120 : curtblindx = -1;
9627 2460 : for (int j = 0; j < numDefaults; j++)
9628 : {
9629 2340 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9630 2340 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9631 2340 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9632 2340 : int adnum = atoi(PQgetvalue(res, j, 3));
9633 2340 : char *adsrc = PQgetvalue(res, j, 4);
9634 :
9635 : /*
9636 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9637 : * OID order.
9638 : */
9639 2340 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9640 : {
9641 37446 : while (++curtblindx < numTables)
9642 : {
9643 37446 : tbinfo = &tblinfo[curtblindx];
9644 37446 : if (tbinfo->dobj.catId.oid == adrelid)
9645 1768 : break;
9646 : }
9647 1768 : if (curtblindx >= numTables)
9648 0 : pg_fatal("unrecognized table OID %u", adrelid);
9649 : }
9650 :
9651 2340 : if (adnum <= 0 || adnum > tbinfo->numatts)
9652 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9653 : adnum, tbinfo->dobj.name);
9654 :
9655 : /*
9656 : * dropped columns shouldn't have defaults, but just in case,
9657 : * ignore 'em
9658 : */
9659 2340 : if (tbinfo->attisdropped[adnum - 1])
9660 0 : continue;
9661 :
9662 2340 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9663 2340 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9664 2340 : attrdefs[j].dobj.catId.oid = adoid;
9665 2340 : AssignDumpId(&attrdefs[j].dobj);
9666 2340 : attrdefs[j].adtable = tbinfo;
9667 2340 : attrdefs[j].adnum = adnum;
9668 2340 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9669 :
9670 2340 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9671 2340 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9672 :
9673 2340 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9674 :
9675 : /*
9676 : * Figure out whether the default/generation expression should be
9677 : * dumped as part of the main CREATE TABLE (or similar) command or
9678 : * as a separate ALTER TABLE (or similar) command. The preference
9679 : * is to put it into the CREATE command, but in some cases that's
9680 : * not possible.
9681 : */
9682 2340 : if (tbinfo->attgenerated[adnum - 1])
9683 : {
9684 : /*
9685 : * Column generation expressions cannot be dumped separately,
9686 : * because there is no syntax for it. By setting separate to
9687 : * false here we prevent the "default" from being processed as
9688 : * its own dumpable object. Later, flagInhAttrs() will mark
9689 : * it as not to be dumped at all, if possible (that is, if it
9690 : * can be inherited from a parent).
9691 : */
9692 1312 : attrdefs[j].separate = false;
9693 : }
9694 1028 : else if (tbinfo->relkind == RELKIND_VIEW)
9695 : {
9696 : /*
9697 : * Defaults on a VIEW must always be dumped as separate ALTER
9698 : * TABLE commands.
9699 : */
9700 64 : attrdefs[j].separate = true;
9701 : }
9702 964 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9703 : {
9704 : /* column will be suppressed, print default separately */
9705 8 : attrdefs[j].separate = true;
9706 : }
9707 : else
9708 : {
9709 956 : attrdefs[j].separate = false;
9710 : }
9711 :
9712 2340 : if (!attrdefs[j].separate)
9713 : {
9714 : /*
9715 : * Mark the default as needing to appear before the table, so
9716 : * that any dependencies it has must be emitted before the
9717 : * CREATE TABLE. If this is not possible, we'll change to
9718 : * "separate" mode while sorting dependencies.
9719 : */
9720 2268 : addObjectDependency(&tbinfo->dobj,
9721 2268 : attrdefs[j].dobj.dumpId);
9722 : }
9723 :
9724 2340 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9725 : }
9726 :
9727 120 : PQclear(res);
9728 : }
9729 :
9730 : /*
9731 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9732 : * data-only dump, as it is only needed for table schemas.
9733 : */
9734 376 : if (dopt->dumpSchema && invalidnotnulloids)
9735 : {
9736 : ConstraintInfo *constrs;
9737 : int numConstrs;
9738 : int i_tableoid;
9739 : int i_oid;
9740 : int i_conrelid;
9741 : int i_conname;
9742 : int i_consrc;
9743 : int i_conislocal;
9744 :
9745 74 : pg_log_info("finding invalid not-null constraints");
9746 :
9747 74 : resetPQExpBuffer(q);
9748 74 : appendPQExpBuffer(q,
9749 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9750 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9751 : "conislocal, convalidated "
9752 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9753 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9754 : "ORDER BY c.conrelid, c.conname",
9755 74 : invalidnotnulloids->data);
9756 :
9757 74 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9758 :
9759 74 : numConstrs = PQntuples(res);
9760 74 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9761 :
9762 74 : i_tableoid = PQfnumber(res, "tableoid");
9763 74 : i_oid = PQfnumber(res, "oid");
9764 74 : i_conrelid = PQfnumber(res, "conrelid");
9765 74 : i_conname = PQfnumber(res, "conname");
9766 74 : i_consrc = PQfnumber(res, "consrc");
9767 74 : i_conislocal = PQfnumber(res, "conislocal");
9768 :
9769 : /* As above, this loop iterates once per table, not once per row */
9770 74 : curtblindx = -1;
9771 208 : for (int j = 0; j < numConstrs;)
9772 : {
9773 134 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9774 134 : TableInfo *tbinfo = NULL;
9775 : int numcons;
9776 :
9777 : /* Count rows for this table */
9778 134 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9779 60 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9780 60 : break;
9781 :
9782 : /*
9783 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9784 : * OID order.
9785 : */
9786 24936 : while (++curtblindx < numTables)
9787 : {
9788 24936 : tbinfo = &tblinfo[curtblindx];
9789 24936 : if (tbinfo->dobj.catId.oid == conrelid)
9790 134 : break;
9791 : }
9792 134 : if (curtblindx >= numTables)
9793 0 : pg_fatal("unrecognized table OID %u", conrelid);
9794 :
9795 268 : for (int c = 0; c < numcons; c++, j++)
9796 : {
9797 134 : constrs[j].dobj.objType = DO_CONSTRAINT;
9798 134 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9799 134 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9800 134 : AssignDumpId(&constrs[j].dobj);
9801 134 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9802 134 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9803 134 : constrs[j].contable = tbinfo;
9804 134 : constrs[j].condomain = NULL;
9805 134 : constrs[j].contype = 'n';
9806 134 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9807 134 : constrs[j].confrelid = InvalidOid;
9808 134 : constrs[j].conindex = 0;
9809 134 : constrs[j].condeferrable = false;
9810 134 : constrs[j].condeferred = false;
9811 134 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9812 :
9813 : /*
9814 : * All invalid not-null constraints must be dumped separately,
9815 : * because CREATE TABLE would not create them as invalid, and
9816 : * also because they must be created after potentially
9817 : * violating data has been loaded.
9818 : */
9819 134 : constrs[j].separate = true;
9820 :
9821 134 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9822 : }
9823 : }
9824 74 : PQclear(res);
9825 : }
9826 :
9827 : /*
9828 : * Get info about table CHECK constraints. This is skipped for a
9829 : * data-only dump, as it is only needed for table schemas.
9830 : */
9831 376 : if (dopt->dumpSchema && checkoids->len > 2)
9832 : {
9833 : ConstraintInfo *constrs;
9834 : int numConstrs;
9835 : int i_tableoid;
9836 : int i_oid;
9837 : int i_conrelid;
9838 : int i_conname;
9839 : int i_consrc;
9840 : int i_conislocal;
9841 : int i_convalidated;
9842 :
9843 122 : pg_log_info("finding table check constraints");
9844 :
9845 122 : resetPQExpBuffer(q);
9846 122 : appendPQExpBuffer(q,
9847 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9848 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9849 : "conislocal, convalidated "
9850 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9851 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9852 : "WHERE contype = 'c' "
9853 : "ORDER BY c.conrelid, c.conname",
9854 : checkoids->data);
9855 :
9856 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9857 :
9858 122 : numConstrs = PQntuples(res);
9859 122 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9860 :
9861 122 : i_tableoid = PQfnumber(res, "tableoid");
9862 122 : i_oid = PQfnumber(res, "oid");
9863 122 : i_conrelid = PQfnumber(res, "conrelid");
9864 122 : i_conname = PQfnumber(res, "conname");
9865 122 : i_consrc = PQfnumber(res, "consrc");
9866 122 : i_conislocal = PQfnumber(res, "conislocal");
9867 122 : i_convalidated = PQfnumber(res, "convalidated");
9868 :
9869 : /* As above, this loop iterates once per table, not once per row */
9870 122 : curtblindx = -1;
9871 1076 : for (int j = 0; j < numConstrs;)
9872 : {
9873 954 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9874 954 : TableInfo *tbinfo = NULL;
9875 : int numcons;
9876 :
9877 : /* Count rows for this table */
9878 1224 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9879 1102 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9880 832 : break;
9881 :
9882 : /*
9883 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9884 : * OID order.
9885 : */
9886 36064 : while (++curtblindx < numTables)
9887 : {
9888 36064 : tbinfo = &tblinfo[curtblindx];
9889 36064 : if (tbinfo->dobj.catId.oid == conrelid)
9890 954 : break;
9891 : }
9892 954 : if (curtblindx >= numTables)
9893 0 : pg_fatal("unrecognized table OID %u", conrelid);
9894 :
9895 954 : if (numcons != tbinfo->ncheck)
9896 : {
9897 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9898 : "expected %d check constraints on table \"%s\" but found %d",
9899 : tbinfo->ncheck),
9900 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9901 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9902 0 : exit_nicely(1);
9903 : }
9904 :
9905 954 : tbinfo->checkexprs = constrs + j;
9906 :
9907 2178 : for (int c = 0; c < numcons; c++, j++)
9908 : {
9909 1224 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9910 :
9911 1224 : constrs[j].dobj.objType = DO_CONSTRAINT;
9912 1224 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9913 1224 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9914 1224 : AssignDumpId(&constrs[j].dobj);
9915 1224 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9916 1224 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9917 1224 : constrs[j].contable = tbinfo;
9918 1224 : constrs[j].condomain = NULL;
9919 1224 : constrs[j].contype = 'c';
9920 1224 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9921 1224 : constrs[j].confrelid = InvalidOid;
9922 1224 : constrs[j].conindex = 0;
9923 1224 : constrs[j].condeferrable = false;
9924 1224 : constrs[j].condeferred = false;
9925 1224 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9926 :
9927 : /*
9928 : * An unvalidated constraint needs to be dumped separately, so
9929 : * that potentially-violating existing data is loaded before
9930 : * the constraint.
9931 : */
9932 1224 : constrs[j].separate = !validated;
9933 :
9934 1224 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9935 :
9936 : /*
9937 : * Mark the constraint as needing to appear before the table
9938 : * --- this is so that any other dependencies of the
9939 : * constraint will be emitted before we try to create the
9940 : * table. If the constraint is to be dumped separately, it
9941 : * will be dumped after data is loaded anyway, so don't do it.
9942 : * (There's an automatic dependency in the opposite direction
9943 : * anyway, so don't need to add one manually here.)
9944 : */
9945 1224 : if (!constrs[j].separate)
9946 1094 : addObjectDependency(&tbinfo->dobj,
9947 1094 : constrs[j].dobj.dumpId);
9948 :
9949 : /*
9950 : * We will detect later whether the constraint must be split
9951 : * out from the table definition.
9952 : */
9953 : }
9954 : }
9955 :
9956 122 : PQclear(res);
9957 : }
9958 :
9959 376 : destroyPQExpBuffer(q);
9960 376 : destroyPQExpBuffer(tbloids);
9961 376 : destroyPQExpBuffer(checkoids);
9962 376 : }
9963 :
9964 : /*
9965 : * Based on the getTableAttrs query's row corresponding to one column, set
9966 : * the name and flags to handle a not-null constraint for that column in
9967 : * the tbinfo struct.
9968 : *
9969 : * Result row 'r' is for tbinfo's attribute 'j'.
9970 : *
9971 : * There are four possibilities:
9972 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9973 : * (the constraint name) remains NULL.
9974 : * 2) The column has a constraint with no name (this is the case when
9975 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9976 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9977 : * 3) The column has an invalid not-null constraint. This must be treated
9978 : * as a separate object (because it must be created after the table data
9979 : * is loaded). So we add its OID to invalidnotnulloids for processing
9980 : * elsewhere and do nothing further with it here. We distinguish this
9981 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9982 : * value, which is the constraint OID. Valid constraints have a null OID.
9983 : * 4) The column has a constraint with a known name; in that case
9984 : * notnull_constrs carries that name and dumpTableSchema will print
9985 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9986 : * (table_column_not_null) and there's no comment on the constraint,
9987 : * there's no need to print that name in the dump, so notnull_constrs
9988 : * is set to the empty string and it behaves as case 2.
9989 : *
9990 : * In a child table that inherits from a parent already containing NOT NULL
9991 : * constraints and the columns in the child don't have their own NOT NULL
9992 : * declarations, we suppress printing constraints in the child: the
9993 : * constraints are acquired at the point where the child is attached to the
9994 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9995 : * set not here but in flagInhAttrs. That flag is also used when the
9996 : * constraint was validated in a child but all its parent have it as NOT
9997 : * VALID.
9998 : *
9999 : * Any of these constraints might have the NO INHERIT bit. If so we set
10000 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10001 : *
10002 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
10003 : * to do the right thing in all but the trivial case. However, the downside
10004 : * of getting it wrong is simply that the name is printed rather than
10005 : * suppressed, so it's not a big deal.
10006 : *
10007 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10008 : * constraints are found, it is initialized and filled with the array of
10009 : * OIDs of such constraints, for later processing.
10010 : */
10011 : static void
10012 49372 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
10013 : TableInfo *tbinfo, int j,
10014 : int i_notnull_name,
10015 : int i_notnull_comment,
10016 : int i_notnull_invalidoid,
10017 : int i_notnull_noinherit,
10018 : int i_notnull_islocal,
10019 : PQExpBuffer *invalidnotnulloids)
10020 : {
10021 49372 : DumpOptions *dopt = fout->dopt;
10022 :
10023 : /*
10024 : * If this not-null constraint is not valid, list its OID in
10025 : * invalidnotnulloids and do nothing further. It'll be processed
10026 : * elsewhere later.
10027 : *
10028 : * Because invalid not-null constraints are rare, we don't want to malloc
10029 : * invalidnotnulloids until we're sure we're going it need it, which
10030 : * happens here.
10031 : */
10032 49372 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10033 : {
10034 146 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10035 :
10036 146 : if (*invalidnotnulloids == NULL)
10037 : {
10038 86 : *invalidnotnulloids = createPQExpBuffer();
10039 86 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10040 86 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10041 : }
10042 : else
10043 60 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10044 :
10045 : /*
10046 : * Track when a parent constraint is invalid for the cases where a
10047 : * child constraint has been validated independenly.
10048 : */
10049 146 : tbinfo->notnull_invalid[j] = true;
10050 :
10051 : /* nothing else to do */
10052 146 : tbinfo->notnull_constrs[j] = NULL;
10053 146 : return;
10054 : }
10055 :
10056 : /*
10057 : * notnull_noinh is straight from the query result. notnull_islocal also,
10058 : * though flagInhAttrs may change that one later.
10059 : */
10060 49226 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10061 49226 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10062 49226 : tbinfo->notnull_invalid[j] = false;
10063 :
10064 : /*
10065 : * Determine a constraint name to use. If the column is not marked not-
10066 : * null, we set NULL which cues ... to do nothing. An empty string says
10067 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10068 : * use.
10069 : */
10070 49226 : if (fout->remoteVersion < 180000)
10071 : {
10072 : /*
10073 : * < 18 doesn't have not-null names, so an unnamed constraint is
10074 : * sufficient.
10075 : */
10076 0 : if (PQgetisnull(res, r, i_notnull_name))
10077 0 : tbinfo->notnull_constrs[j] = NULL;
10078 : else
10079 0 : tbinfo->notnull_constrs[j] = "";
10080 : }
10081 : else
10082 : {
10083 49226 : if (PQgetisnull(res, r, i_notnull_name))
10084 43976 : tbinfo->notnull_constrs[j] = NULL;
10085 : else
10086 : {
10087 : /*
10088 : * In binary upgrade of inheritance child tables, must have a
10089 : * constraint name that we can UPDATE later; same if there's a
10090 : * comment on the constraint.
10091 : */
10092 5250 : if ((dopt->binary_upgrade &&
10093 666 : !tbinfo->ispartition &&
10094 5756 : !tbinfo->notnull_islocal) ||
10095 5250 : !PQgetisnull(res, r, i_notnull_comment))
10096 : {
10097 96 : tbinfo->notnull_constrs[j] =
10098 96 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10099 : }
10100 : else
10101 : {
10102 : char *default_name;
10103 :
10104 : /* XXX should match ChooseConstraintName better */
10105 5154 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10106 5154 : tbinfo->attnames[j]);
10107 5154 : if (strcmp(default_name,
10108 5154 : PQgetvalue(res, r, i_notnull_name)) == 0)
10109 3406 : tbinfo->notnull_constrs[j] = "";
10110 : else
10111 : {
10112 1748 : tbinfo->notnull_constrs[j] =
10113 1748 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10114 : }
10115 5154 : free(default_name);
10116 : }
10117 : }
10118 : }
10119 : }
10120 :
10121 : /*
10122 : * Test whether a column should be printed as part of table's CREATE TABLE.
10123 : * Column number is zero-based.
10124 : *
10125 : * Normally this is always true, but it's false for dropped columns, as well
10126 : * as those that were inherited without any local definition. (If we print
10127 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10128 : * For partitions, it's always true, because we want the partitions to be
10129 : * created independently and ATTACH PARTITION used afterwards.
10130 : *
10131 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10132 : * attisdropped state later, so as to keep control of the physical column
10133 : * order.
10134 : *
10135 : * This function exists because there are scattered nonobvious places that
10136 : * must be kept in sync with this decision.
10137 : */
10138 : bool
10139 80064 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10140 : {
10141 80064 : if (dopt->binary_upgrade)
10142 12456 : return true;
10143 67608 : if (tbinfo->attisdropped[colno])
10144 1452 : return false;
10145 66156 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10146 : }
10147 :
10148 :
10149 : /*
10150 : * getTSParsers:
10151 : * get information about all text search parsers in the system catalogs
10152 : */
10153 : void
10154 376 : getTSParsers(Archive *fout)
10155 : {
10156 : PGresult *res;
10157 : int ntups;
10158 : int i;
10159 : PQExpBuffer query;
10160 : TSParserInfo *prsinfo;
10161 : int i_tableoid;
10162 : int i_oid;
10163 : int i_prsname;
10164 : int i_prsnamespace;
10165 : int i_prsstart;
10166 : int i_prstoken;
10167 : int i_prsend;
10168 : int i_prsheadline;
10169 : int i_prslextype;
10170 :
10171 376 : query = createPQExpBuffer();
10172 :
10173 : /*
10174 : * find all text search objects, including builtin ones; we filter out
10175 : * system-defined objects at dump-out time.
10176 : */
10177 :
10178 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10179 : "prsstart::oid, prstoken::oid, "
10180 : "prsend::oid, prsheadline::oid, prslextype::oid "
10181 : "FROM pg_ts_parser");
10182 :
10183 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10184 :
10185 376 : ntups = PQntuples(res);
10186 :
10187 376 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10188 :
10189 376 : i_tableoid = PQfnumber(res, "tableoid");
10190 376 : i_oid = PQfnumber(res, "oid");
10191 376 : i_prsname = PQfnumber(res, "prsname");
10192 376 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10193 376 : i_prsstart = PQfnumber(res, "prsstart");
10194 376 : i_prstoken = PQfnumber(res, "prstoken");
10195 376 : i_prsend = PQfnumber(res, "prsend");
10196 376 : i_prsheadline = PQfnumber(res, "prsheadline");
10197 376 : i_prslextype = PQfnumber(res, "prslextype");
10198 :
10199 842 : for (i = 0; i < ntups; i++)
10200 : {
10201 466 : prsinfo[i].dobj.objType = DO_TSPARSER;
10202 466 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10203 466 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10204 466 : AssignDumpId(&prsinfo[i].dobj);
10205 466 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10206 932 : prsinfo[i].dobj.namespace =
10207 466 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10208 466 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10209 466 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10210 466 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10211 466 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10212 466 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10213 :
10214 : /* Decide whether we want to dump it */
10215 466 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10216 : }
10217 :
10218 376 : PQclear(res);
10219 :
10220 376 : destroyPQExpBuffer(query);
10221 376 : }
10222 :
10223 : /*
10224 : * getTSDictionaries:
10225 : * get information about all text search dictionaries in the system catalogs
10226 : */
10227 : void
10228 376 : getTSDictionaries(Archive *fout)
10229 : {
10230 : PGresult *res;
10231 : int ntups;
10232 : int i;
10233 : PQExpBuffer query;
10234 : TSDictInfo *dictinfo;
10235 : int i_tableoid;
10236 : int i_oid;
10237 : int i_dictname;
10238 : int i_dictnamespace;
10239 : int i_dictowner;
10240 : int i_dicttemplate;
10241 : int i_dictinitoption;
10242 :
10243 376 : query = createPQExpBuffer();
10244 :
10245 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10246 : "dictnamespace, dictowner, "
10247 : "dicttemplate, dictinitoption "
10248 : "FROM pg_ts_dict");
10249 :
10250 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10251 :
10252 376 : ntups = PQntuples(res);
10253 :
10254 376 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10255 :
10256 376 : i_tableoid = PQfnumber(res, "tableoid");
10257 376 : i_oid = PQfnumber(res, "oid");
10258 376 : i_dictname = PQfnumber(res, "dictname");
10259 376 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10260 376 : i_dictowner = PQfnumber(res, "dictowner");
10261 376 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10262 376 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10263 :
10264 12624 : for (i = 0; i < ntups; i++)
10265 : {
10266 12248 : dictinfo[i].dobj.objType = DO_TSDICT;
10267 12248 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10268 12248 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10269 12248 : AssignDumpId(&dictinfo[i].dobj);
10270 12248 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10271 24496 : dictinfo[i].dobj.namespace =
10272 12248 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10273 12248 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10274 12248 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10275 12248 : if (PQgetisnull(res, i, i_dictinitoption))
10276 466 : dictinfo[i].dictinitoption = NULL;
10277 : else
10278 11782 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10279 :
10280 : /* Decide whether we want to dump it */
10281 12248 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10282 : }
10283 :
10284 376 : PQclear(res);
10285 :
10286 376 : destroyPQExpBuffer(query);
10287 376 : }
10288 :
10289 : /*
10290 : * getTSTemplates:
10291 : * get information about all text search templates in the system catalogs
10292 : */
10293 : void
10294 376 : getTSTemplates(Archive *fout)
10295 : {
10296 : PGresult *res;
10297 : int ntups;
10298 : int i;
10299 : PQExpBuffer query;
10300 : TSTemplateInfo *tmplinfo;
10301 : int i_tableoid;
10302 : int i_oid;
10303 : int i_tmplname;
10304 : int i_tmplnamespace;
10305 : int i_tmplinit;
10306 : int i_tmpllexize;
10307 :
10308 376 : query = createPQExpBuffer();
10309 :
10310 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10311 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10312 : "FROM pg_ts_template");
10313 :
10314 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10315 :
10316 376 : ntups = PQntuples(res);
10317 :
10318 376 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10319 :
10320 376 : i_tableoid = PQfnumber(res, "tableoid");
10321 376 : i_oid = PQfnumber(res, "oid");
10322 376 : i_tmplname = PQfnumber(res, "tmplname");
10323 376 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10324 376 : i_tmplinit = PQfnumber(res, "tmplinit");
10325 376 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10326 :
10327 2346 : for (i = 0; i < ntups; i++)
10328 : {
10329 1970 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10330 1970 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10331 1970 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10332 1970 : AssignDumpId(&tmplinfo[i].dobj);
10333 1970 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10334 3940 : tmplinfo[i].dobj.namespace =
10335 1970 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10336 1970 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10337 1970 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10338 :
10339 : /* Decide whether we want to dump it */
10340 1970 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10341 : }
10342 :
10343 376 : PQclear(res);
10344 :
10345 376 : destroyPQExpBuffer(query);
10346 376 : }
10347 :
10348 : /*
10349 : * getTSConfigurations:
10350 : * get information about all text search configurations
10351 : */
10352 : void
10353 376 : getTSConfigurations(Archive *fout)
10354 : {
10355 : PGresult *res;
10356 : int ntups;
10357 : int i;
10358 : PQExpBuffer query;
10359 : TSConfigInfo *cfginfo;
10360 : int i_tableoid;
10361 : int i_oid;
10362 : int i_cfgname;
10363 : int i_cfgnamespace;
10364 : int i_cfgowner;
10365 : int i_cfgparser;
10366 :
10367 376 : query = createPQExpBuffer();
10368 :
10369 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10370 : "cfgnamespace, cfgowner, cfgparser "
10371 : "FROM pg_ts_config");
10372 :
10373 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10374 :
10375 376 : ntups = PQntuples(res);
10376 :
10377 376 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10378 :
10379 376 : i_tableoid = PQfnumber(res, "tableoid");
10380 376 : i_oid = PQfnumber(res, "oid");
10381 376 : i_cfgname = PQfnumber(res, "cfgname");
10382 376 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10383 376 : i_cfgowner = PQfnumber(res, "cfgowner");
10384 376 : i_cfgparser = PQfnumber(res, "cfgparser");
10385 :
10386 12554 : for (i = 0; i < ntups; i++)
10387 : {
10388 12178 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10389 12178 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10390 12178 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10391 12178 : AssignDumpId(&cfginfo[i].dobj);
10392 12178 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10393 24356 : cfginfo[i].dobj.namespace =
10394 12178 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10395 12178 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10396 12178 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10397 :
10398 : /* Decide whether we want to dump it */
10399 12178 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10400 : }
10401 :
10402 376 : PQclear(res);
10403 :
10404 376 : destroyPQExpBuffer(query);
10405 376 : }
10406 :
10407 : /*
10408 : * getForeignDataWrappers:
10409 : * get information about all foreign-data wrappers in the system catalogs
10410 : */
10411 : void
10412 376 : getForeignDataWrappers(Archive *fout)
10413 : {
10414 : PGresult *res;
10415 : int ntups;
10416 : int i;
10417 : PQExpBuffer query;
10418 : FdwInfo *fdwinfo;
10419 : int i_tableoid;
10420 : int i_oid;
10421 : int i_fdwname;
10422 : int i_fdwowner;
10423 : int i_fdwhandler;
10424 : int i_fdwvalidator;
10425 : int i_fdwacl;
10426 : int i_acldefault;
10427 : int i_fdwoptions;
10428 :
10429 376 : query = createPQExpBuffer();
10430 :
10431 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10432 : "fdwowner, "
10433 : "fdwhandler::pg_catalog.regproc, "
10434 : "fdwvalidator::pg_catalog.regproc, "
10435 : "fdwacl, "
10436 : "acldefault('F', fdwowner) AS acldefault, "
10437 : "array_to_string(ARRAY("
10438 : "SELECT quote_ident(option_name) || ' ' || "
10439 : "quote_literal(option_value) "
10440 : "FROM pg_options_to_table(fdwoptions) "
10441 : "ORDER BY option_name"
10442 : "), E',\n ') AS fdwoptions "
10443 : "FROM pg_foreign_data_wrapper");
10444 :
10445 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10446 :
10447 376 : ntups = PQntuples(res);
10448 :
10449 376 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10450 :
10451 376 : i_tableoid = PQfnumber(res, "tableoid");
10452 376 : i_oid = PQfnumber(res, "oid");
10453 376 : i_fdwname = PQfnumber(res, "fdwname");
10454 376 : i_fdwowner = PQfnumber(res, "fdwowner");
10455 376 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10456 376 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10457 376 : i_fdwacl = PQfnumber(res, "fdwacl");
10458 376 : i_acldefault = PQfnumber(res, "acldefault");
10459 376 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10460 :
10461 518 : for (i = 0; i < ntups; i++)
10462 : {
10463 142 : fdwinfo[i].dobj.objType = DO_FDW;
10464 142 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10465 142 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10466 142 : AssignDumpId(&fdwinfo[i].dobj);
10467 142 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10468 142 : fdwinfo[i].dobj.namespace = NULL;
10469 142 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10470 142 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10471 142 : fdwinfo[i].dacl.privtype = 0;
10472 142 : fdwinfo[i].dacl.initprivs = NULL;
10473 142 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10474 142 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10475 142 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10476 142 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10477 :
10478 : /* Decide whether we want to dump it */
10479 142 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10480 :
10481 : /* Mark whether FDW has an ACL */
10482 142 : if (!PQgetisnull(res, i, i_fdwacl))
10483 90 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10484 : }
10485 :
10486 376 : PQclear(res);
10487 :
10488 376 : destroyPQExpBuffer(query);
10489 376 : }
10490 :
10491 : /*
10492 : * getForeignServers:
10493 : * get information about all foreign servers in the system catalogs
10494 : */
10495 : void
10496 376 : getForeignServers(Archive *fout)
10497 : {
10498 : PGresult *res;
10499 : int ntups;
10500 : int i;
10501 : PQExpBuffer query;
10502 : ForeignServerInfo *srvinfo;
10503 : int i_tableoid;
10504 : int i_oid;
10505 : int i_srvname;
10506 : int i_srvowner;
10507 : int i_srvfdw;
10508 : int i_srvtype;
10509 : int i_srvversion;
10510 : int i_srvacl;
10511 : int i_acldefault;
10512 : int i_srvoptions;
10513 :
10514 376 : query = createPQExpBuffer();
10515 :
10516 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10517 : "srvowner, "
10518 : "srvfdw, srvtype, srvversion, srvacl, "
10519 : "acldefault('S', srvowner) AS acldefault, "
10520 : "array_to_string(ARRAY("
10521 : "SELECT quote_ident(option_name) || ' ' || "
10522 : "quote_literal(option_value) "
10523 : "FROM pg_options_to_table(srvoptions) "
10524 : "ORDER BY option_name"
10525 : "), E',\n ') AS srvoptions "
10526 : "FROM pg_foreign_server");
10527 :
10528 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10529 :
10530 376 : ntups = PQntuples(res);
10531 :
10532 376 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10533 :
10534 376 : i_tableoid = PQfnumber(res, "tableoid");
10535 376 : i_oid = PQfnumber(res, "oid");
10536 376 : i_srvname = PQfnumber(res, "srvname");
10537 376 : i_srvowner = PQfnumber(res, "srvowner");
10538 376 : i_srvfdw = PQfnumber(res, "srvfdw");
10539 376 : i_srvtype = PQfnumber(res, "srvtype");
10540 376 : i_srvversion = PQfnumber(res, "srvversion");
10541 376 : i_srvacl = PQfnumber(res, "srvacl");
10542 376 : i_acldefault = PQfnumber(res, "acldefault");
10543 376 : i_srvoptions = PQfnumber(res, "srvoptions");
10544 :
10545 526 : for (i = 0; i < ntups; i++)
10546 : {
10547 150 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10548 150 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10549 150 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10550 150 : AssignDumpId(&srvinfo[i].dobj);
10551 150 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10552 150 : srvinfo[i].dobj.namespace = NULL;
10553 150 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10554 150 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10555 150 : srvinfo[i].dacl.privtype = 0;
10556 150 : srvinfo[i].dacl.initprivs = NULL;
10557 150 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10558 150 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10559 150 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10560 150 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10561 150 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10562 :
10563 : /* Decide whether we want to dump it */
10564 150 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10565 :
10566 : /* Servers have user mappings */
10567 150 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10568 :
10569 : /* Mark whether server has an ACL */
10570 150 : if (!PQgetisnull(res, i, i_srvacl))
10571 90 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10572 : }
10573 :
10574 376 : PQclear(res);
10575 :
10576 376 : destroyPQExpBuffer(query);
10577 376 : }
10578 :
10579 : /*
10580 : * getDefaultACLs:
10581 : * get information about all default ACL information in the system catalogs
10582 : */
10583 : void
10584 376 : getDefaultACLs(Archive *fout)
10585 : {
10586 376 : DumpOptions *dopt = fout->dopt;
10587 : DefaultACLInfo *daclinfo;
10588 : PQExpBuffer query;
10589 : PGresult *res;
10590 : int i_oid;
10591 : int i_tableoid;
10592 : int i_defaclrole;
10593 : int i_defaclnamespace;
10594 : int i_defaclobjtype;
10595 : int i_defaclacl;
10596 : int i_acldefault;
10597 : int i,
10598 : ntups;
10599 :
10600 376 : query = createPQExpBuffer();
10601 :
10602 : /*
10603 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10604 : * ACL for their object type. We should dump them as deltas from the
10605 : * default ACL, since that will be used as a starting point for
10606 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10607 : * non-global entries can only add privileges not revoke them. We must
10608 : * dump those as-is (i.e., as deltas from an empty ACL).
10609 : *
10610 : * We can use defaclobjtype as the object type for acldefault(), except
10611 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10612 : * 's'.
10613 : */
10614 376 : appendPQExpBufferStr(query,
10615 : "SELECT oid, tableoid, "
10616 : "defaclrole, "
10617 : "defaclnamespace, "
10618 : "defaclobjtype, "
10619 : "defaclacl, "
10620 : "CASE WHEN defaclnamespace = 0 THEN "
10621 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10622 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10623 : "defaclrole) ELSE '{}' END AS acldefault "
10624 : "FROM pg_default_acl");
10625 :
10626 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10627 :
10628 376 : ntups = PQntuples(res);
10629 :
10630 376 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10631 :
10632 376 : i_oid = PQfnumber(res, "oid");
10633 376 : i_tableoid = PQfnumber(res, "tableoid");
10634 376 : i_defaclrole = PQfnumber(res, "defaclrole");
10635 376 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10636 376 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10637 376 : i_defaclacl = PQfnumber(res, "defaclacl");
10638 376 : i_acldefault = PQfnumber(res, "acldefault");
10639 :
10640 764 : for (i = 0; i < ntups; i++)
10641 : {
10642 388 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10643 :
10644 388 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10645 388 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10646 388 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10647 388 : AssignDumpId(&daclinfo[i].dobj);
10648 : /* cheesy ... is it worth coming up with a better object name? */
10649 388 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10650 :
10651 388 : if (nspid != InvalidOid)
10652 180 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10653 : else
10654 208 : daclinfo[i].dobj.namespace = NULL;
10655 :
10656 388 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10657 388 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10658 388 : daclinfo[i].dacl.privtype = 0;
10659 388 : daclinfo[i].dacl.initprivs = NULL;
10660 388 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10661 388 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10662 :
10663 : /* Default ACLs are ACLs, of course */
10664 388 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10665 :
10666 : /* Decide whether we want to dump it */
10667 388 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10668 : }
10669 :
10670 376 : PQclear(res);
10671 :
10672 376 : destroyPQExpBuffer(query);
10673 376 : }
10674 :
10675 : /*
10676 : * getRoleName -- look up the name of a role, given its OID
10677 : *
10678 : * In current usage, we don't expect failures, so error out for a bad OID.
10679 : */
10680 : static const char *
10681 1194696 : getRoleName(const char *roleoid_str)
10682 : {
10683 1194696 : Oid roleoid = atooid(roleoid_str);
10684 :
10685 : /*
10686 : * Do binary search to find the appropriate item.
10687 : */
10688 1194696 : if (nrolenames > 0)
10689 : {
10690 1194696 : RoleNameItem *low = &rolenames[0];
10691 1194696 : RoleNameItem *high = &rolenames[nrolenames - 1];
10692 :
10693 4778566 : while (low <= high)
10694 : {
10695 4778566 : RoleNameItem *middle = low + (high - low) / 2;
10696 :
10697 4778566 : if (roleoid < middle->roleoid)
10698 3581758 : high = middle - 1;
10699 1196808 : else if (roleoid > middle->roleoid)
10700 2112 : low = middle + 1;
10701 : else
10702 1194696 : return middle->rolename; /* found a match */
10703 : }
10704 : }
10705 :
10706 0 : pg_fatal("role with OID %u does not exist", roleoid);
10707 : return NULL; /* keep compiler quiet */
10708 : }
10709 :
10710 : /*
10711 : * collectRoleNames --
10712 : *
10713 : * Construct a table of all known roles.
10714 : * The table is sorted by OID for speed in lookup.
10715 : */
10716 : static void
10717 378 : collectRoleNames(Archive *fout)
10718 : {
10719 : PGresult *res;
10720 : const char *query;
10721 : int i;
10722 :
10723 378 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10724 :
10725 378 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10726 :
10727 378 : nrolenames = PQntuples(res);
10728 :
10729 378 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10730 :
10731 7414 : for (i = 0; i < nrolenames; i++)
10732 : {
10733 7036 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10734 7036 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10735 : }
10736 :
10737 378 : PQclear(res);
10738 378 : }
10739 :
10740 : /*
10741 : * getAdditionalACLs
10742 : *
10743 : * We have now created all the DumpableObjects, and collected the ACL data
10744 : * that appears in the directly-associated catalog entries. However, there's
10745 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10746 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10747 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10748 : * Also, in versions having the pg_init_privs catalog, read that and load the
10749 : * information into the relevant DumpableObjects.
10750 : */
10751 : static void
10752 372 : getAdditionalACLs(Archive *fout)
10753 : {
10754 372 : PQExpBuffer query = createPQExpBuffer();
10755 : PGresult *res;
10756 : int ntups,
10757 : i;
10758 :
10759 : /* Check for per-column ACLs */
10760 372 : appendPQExpBufferStr(query,
10761 : "SELECT DISTINCT attrelid FROM pg_attribute "
10762 : "WHERE attacl IS NOT NULL");
10763 :
10764 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10765 :
10766 372 : ntups = PQntuples(res);
10767 1084 : for (i = 0; i < ntups; i++)
10768 : {
10769 712 : Oid relid = atooid(PQgetvalue(res, i, 0));
10770 : TableInfo *tblinfo;
10771 :
10772 712 : tblinfo = findTableByOid(relid);
10773 : /* OK to ignore tables we haven't got a DumpableObject for */
10774 712 : if (tblinfo)
10775 : {
10776 712 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10777 712 : tblinfo->hascolumnACLs = true;
10778 : }
10779 : }
10780 372 : PQclear(res);
10781 :
10782 : /* Fetch initial-privileges data */
10783 372 : if (fout->remoteVersion >= 90600)
10784 : {
10785 372 : printfPQExpBuffer(query,
10786 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10787 : "FROM pg_init_privs");
10788 :
10789 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10790 :
10791 372 : ntups = PQntuples(res);
10792 88408 : for (i = 0; i < ntups; i++)
10793 : {
10794 88036 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10795 88036 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10796 88036 : int objsubid = atoi(PQgetvalue(res, i, 2));
10797 88036 : char privtype = *(PQgetvalue(res, i, 3));
10798 88036 : char *initprivs = PQgetvalue(res, i, 4);
10799 : CatalogId objId;
10800 : DumpableObject *dobj;
10801 :
10802 88036 : objId.tableoid = classoid;
10803 88036 : objId.oid = objoid;
10804 88036 : dobj = findObjectByCatalogId(objId);
10805 : /* OK to ignore entries we haven't got a DumpableObject for */
10806 88036 : if (dobj)
10807 : {
10808 : /* Cope with sub-object initprivs */
10809 63200 : if (objsubid != 0)
10810 : {
10811 7488 : if (dobj->objType == DO_TABLE)
10812 : {
10813 : /* For a column initprivs, set the table's ACL flags */
10814 7488 : dobj->components |= DUMP_COMPONENT_ACL;
10815 7488 : ((TableInfo *) dobj)->hascolumnACLs = true;
10816 : }
10817 : else
10818 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10819 : classoid, objoid, objsubid);
10820 7852 : continue;
10821 : }
10822 :
10823 : /*
10824 : * We ignore any pg_init_privs.initprivs entry for the public
10825 : * schema, as explained in getNamespaces().
10826 : */
10827 55712 : if (dobj->objType == DO_NAMESPACE &&
10828 736 : strcmp(dobj->name, "public") == 0)
10829 364 : continue;
10830 :
10831 : /* Else it had better be of a type we think has ACLs */
10832 55348 : if (dobj->objType == DO_NAMESPACE ||
10833 54976 : dobj->objType == DO_TYPE ||
10834 54928 : dobj->objType == DO_FUNC ||
10835 54744 : dobj->objType == DO_AGG ||
10836 54696 : dobj->objType == DO_TABLE ||
10837 0 : dobj->objType == DO_PROCLANG ||
10838 0 : dobj->objType == DO_FDW ||
10839 0 : dobj->objType == DO_FOREIGN_SERVER)
10840 55348 : {
10841 55348 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10842 :
10843 55348 : daobj->dacl.privtype = privtype;
10844 55348 : daobj->dacl.initprivs = pstrdup(initprivs);
10845 : }
10846 : else
10847 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10848 : classoid, objoid, objsubid);
10849 : }
10850 : }
10851 372 : PQclear(res);
10852 : }
10853 :
10854 372 : destroyPQExpBuffer(query);
10855 372 : }
10856 :
10857 : /*
10858 : * dumpCommentExtended --
10859 : *
10860 : * This routine is used to dump any comments associated with the
10861 : * object handed to this routine. The routine takes the object type
10862 : * and object name (ready to print, except for schema decoration), plus
10863 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10864 : * plus catalog ID and subid which are the lookup key for pg_description,
10865 : * plus the dump ID for the object (for setting a dependency).
10866 : * If a matching pg_description entry is found, it is dumped.
10867 : *
10868 : * Note: in some cases, such as comments for triggers and rules, the "type"
10869 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10870 : * but it doesn't seem worth complicating the API for all callers to make
10871 : * it cleaner.
10872 : *
10873 : * Note: although this routine takes a dumpId for dependency purposes,
10874 : * that purpose is just to mark the dependency in the emitted dump file
10875 : * for possible future use by pg_restore. We do NOT use it for determining
10876 : * ordering of the comment in the dump file, because this routine is called
10877 : * after dependency sorting occurs. This routine should be called just after
10878 : * calling ArchiveEntry() for the specified object.
10879 : */
10880 : static void
10881 13026 : dumpCommentExtended(Archive *fout, const char *type,
10882 : const char *name, const char *namespace,
10883 : const char *owner, CatalogId catalogId,
10884 : int subid, DumpId dumpId,
10885 : const char *initdb_comment)
10886 : {
10887 13026 : DumpOptions *dopt = fout->dopt;
10888 : CommentItem *comments;
10889 : int ncomments;
10890 :
10891 : /* do nothing, if --no-comments is supplied */
10892 13026 : if (dopt->no_comments)
10893 0 : return;
10894 :
10895 : /* Comments are schema not data ... except LO comments are data */
10896 13026 : if (strcmp(type, "LARGE OBJECT") != 0)
10897 : {
10898 12908 : if (!dopt->dumpSchema)
10899 0 : return;
10900 : }
10901 : else
10902 : {
10903 : /* We do dump LO comments in binary-upgrade mode */
10904 118 : if (!dopt->dumpData && !dopt->binary_upgrade)
10905 0 : return;
10906 : }
10907 :
10908 : /* Search for comments associated with catalogId, using table */
10909 13026 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10910 : &comments);
10911 :
10912 : /* Is there one matching the subid? */
10913 13026 : while (ncomments > 0)
10914 : {
10915 12934 : if (comments->objsubid == subid)
10916 12934 : break;
10917 0 : comments++;
10918 0 : ncomments--;
10919 : }
10920 :
10921 13026 : if (initdb_comment != NULL)
10922 : {
10923 : static CommentItem empty_comment = {.descr = ""};
10924 :
10925 : /*
10926 : * initdb creates this object with a comment. Skip dumping the
10927 : * initdb-provided comment, which would complicate matters for
10928 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10929 : * comment, replicate that.
10930 : */
10931 234 : if (ncomments == 0)
10932 : {
10933 8 : comments = &empty_comment;
10934 8 : ncomments = 1;
10935 : }
10936 226 : else if (strcmp(comments->descr, initdb_comment) == 0)
10937 226 : ncomments = 0;
10938 : }
10939 :
10940 : /* If a comment exists, build COMMENT ON statement */
10941 13026 : if (ncomments > 0)
10942 : {
10943 12716 : PQExpBuffer query = createPQExpBuffer();
10944 12716 : PQExpBuffer tag = createPQExpBuffer();
10945 :
10946 12716 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10947 12716 : if (namespace && *namespace)
10948 12360 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10949 12716 : appendPQExpBuffer(query, "%s IS ", name);
10950 12716 : appendStringLiteralAH(query, comments->descr, fout);
10951 12716 : appendPQExpBufferStr(query, ";\n");
10952 :
10953 12716 : appendPQExpBuffer(tag, "%s %s", type, name);
10954 :
10955 : /*
10956 : * We mark comments as SECTION_NONE because they really belong in the
10957 : * same section as their parent, whether that is pre-data or
10958 : * post-data.
10959 : */
10960 12716 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10961 12716 : ARCHIVE_OPTS(.tag = tag->data,
10962 : .namespace = namespace,
10963 : .owner = owner,
10964 : .description = "COMMENT",
10965 : .section = SECTION_NONE,
10966 : .createStmt = query->data,
10967 : .deps = &dumpId,
10968 : .nDeps = 1));
10969 :
10970 12716 : destroyPQExpBuffer(query);
10971 12716 : destroyPQExpBuffer(tag);
10972 : }
10973 : }
10974 :
10975 : /*
10976 : * dumpComment --
10977 : *
10978 : * Typical simplification of the above function.
10979 : */
10980 : static inline void
10981 12710 : dumpComment(Archive *fout, const char *type,
10982 : const char *name, const char *namespace,
10983 : const char *owner, CatalogId catalogId,
10984 : int subid, DumpId dumpId)
10985 : {
10986 12710 : dumpCommentExtended(fout, type, name, namespace, owner,
10987 : catalogId, subid, dumpId, NULL);
10988 12710 : }
10989 :
10990 : /*
10991 : * appendNamedArgument --
10992 : *
10993 : * Convenience routine for constructing parameters of the form:
10994 : * 'paraname', 'value'::type
10995 : */
10996 : static void
10997 9850 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10998 : const char *argtype, const char *argval)
10999 : {
11000 9850 : appendPQExpBufferStr(out, ",\n\t");
11001 :
11002 9850 : appendStringLiteralAH(out, argname, fout);
11003 9850 : appendPQExpBufferStr(out, ", ");
11004 :
11005 9850 : appendStringLiteralAH(out, argval, fout);
11006 9850 : appendPQExpBuffer(out, "::%s", argtype);
11007 9850 : }
11008 :
11009 : /*
11010 : * fetchAttributeStats --
11011 : *
11012 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11013 : */
11014 : static PGresult *
11015 2070 : fetchAttributeStats(Archive *fout)
11016 : {
11017 2070 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11018 2070 : PQExpBuffer nspnames = createPQExpBuffer();
11019 2070 : PQExpBuffer relnames = createPQExpBuffer();
11020 2070 : int count = 0;
11021 2070 : PGresult *res = NULL;
11022 : static TocEntry *te;
11023 : static bool restarted;
11024 2070 : int max_rels = MAX_ATTR_STATS_RELS;
11025 :
11026 : /*
11027 : * Our query for retrieving statistics for multiple relations uses WITH
11028 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11029 : * in v9.4. For older versions, we resort to gathering statistics for a
11030 : * single relation at a time.
11031 : */
11032 2070 : if (fout->remoteVersion < 90400)
11033 0 : max_rels = 1;
11034 :
11035 : /* If we're just starting, set our TOC pointer. */
11036 2070 : if (!te)
11037 124 : te = AH->toc->next;
11038 :
11039 : /*
11040 : * We can't easily avoid a second TOC scan for the tar format because it
11041 : * writes restore.sql separately, which means we must execute the queries
11042 : * twice. This feels risky, but there is no known reason it should
11043 : * generate different output than the first pass. Even if it does, the
11044 : * worst-case scenario is that restore.sql might have different statistics
11045 : * data than the archive.
11046 : */
11047 2070 : if (!restarted && te == AH->toc && AH->format == archTar)
11048 : {
11049 2 : te = AH->toc->next;
11050 2 : restarted = true;
11051 : }
11052 :
11053 2070 : appendPQExpBufferChar(nspnames, '{');
11054 2070 : appendPQExpBufferChar(relnames, '{');
11055 :
11056 : /*
11057 : * Scan the TOC for the next set of relevant stats entries. We assume
11058 : * that statistics are dumped in the order they are listed in the TOC.
11059 : * This is perhaps not the sturdiest assumption, so we verify it matches
11060 : * reality in dumpRelationStats_dumper().
11061 : */
11062 31320 : for (; te != AH->toc && count < max_rels; te = te->next)
11063 : {
11064 29250 : if ((te->reqs & REQ_STATS) != 0 &&
11065 6456 : strcmp(te->desc, "STATISTICS DATA") == 0)
11066 : {
11067 6456 : appendPGArray(nspnames, te->namespace);
11068 6456 : appendPGArray(relnames, te->tag);
11069 6456 : count++;
11070 : }
11071 : }
11072 :
11073 2070 : appendPQExpBufferChar(nspnames, '}');
11074 2070 : appendPQExpBufferChar(relnames, '}');
11075 :
11076 : /* Execute the query for the next batch of relations. */
11077 2070 : if (count > 0)
11078 : {
11079 210 : PQExpBuffer query = createPQExpBuffer();
11080 :
11081 210 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11082 210 : appendStringLiteralAH(query, nspnames->data, fout);
11083 210 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11084 210 : appendStringLiteralAH(query, relnames->data, fout);
11085 210 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11086 210 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11087 210 : destroyPQExpBuffer(query);
11088 : }
11089 :
11090 2070 : destroyPQExpBuffer(nspnames);
11091 2070 : destroyPQExpBuffer(relnames);
11092 2070 : return res;
11093 : }
11094 :
11095 : /*
11096 : * dumpRelationStats_dumper --
11097 : *
11098 : * Generate command to import stats into the relation on the new database.
11099 : * This routine is called by the Archiver when it wants the statistics to be
11100 : * dumped.
11101 : */
11102 : static char *
11103 6456 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11104 : {
11105 6456 : const RelStatsInfo *rsinfo = userArg;
11106 : static PGresult *res;
11107 : static int rownum;
11108 : PQExpBuffer query;
11109 : PQExpBufferData out_data;
11110 6456 : PQExpBuffer out = &out_data;
11111 : int i_schemaname;
11112 : int i_tablename;
11113 : int i_attname;
11114 : int i_inherited;
11115 : int i_null_frac;
11116 : int i_avg_width;
11117 : int i_n_distinct;
11118 : int i_most_common_vals;
11119 : int i_most_common_freqs;
11120 : int i_histogram_bounds;
11121 : int i_correlation;
11122 : int i_most_common_elems;
11123 : int i_most_common_elem_freqs;
11124 : int i_elem_count_histogram;
11125 : int i_range_length_histogram;
11126 : int i_range_empty_frac;
11127 : int i_range_bounds_histogram;
11128 : static TocEntry *expected_te;
11129 :
11130 : /*
11131 : * fetchAttributeStats() assumes that the statistics are dumped in the
11132 : * order they are listed in the TOC. We verify that here for safety.
11133 : */
11134 6456 : if (!expected_te)
11135 124 : expected_te = ((ArchiveHandle *) fout)->toc;
11136 :
11137 6456 : expected_te = expected_te->next;
11138 25708 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11139 6456 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11140 19252 : expected_te = expected_te->next;
11141 :
11142 6456 : if (te != expected_te)
11143 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11144 : te->dumpId, te->desc, te->tag,
11145 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11146 :
11147 6456 : query = createPQExpBuffer();
11148 6456 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11149 : {
11150 124 : appendPQExpBufferStr(query,
11151 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11152 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11153 : "s.null_frac, s.avg_width, s.n_distinct, "
11154 : "s.most_common_vals, s.most_common_freqs, "
11155 : "s.histogram_bounds, s.correlation, "
11156 : "s.most_common_elems, s.most_common_elem_freqs, "
11157 : "s.elem_count_histogram, ");
11158 :
11159 124 : if (fout->remoteVersion >= 170000)
11160 124 : appendPQExpBufferStr(query,
11161 : "s.range_length_histogram, "
11162 : "s.range_empty_frac, "
11163 : "s.range_bounds_histogram ");
11164 : else
11165 0 : appendPQExpBufferStr(query,
11166 : "NULL AS range_length_histogram,"
11167 : "NULL AS range_empty_frac,"
11168 : "NULL AS range_bounds_histogram ");
11169 :
11170 : /*
11171 : * The results must be in the order of the relations supplied in the
11172 : * parameters to ensure we remain in sync as we walk through the TOC.
11173 : * The redundant filter clause on s.tablename = ANY(...) seems
11174 : * sufficient to convince the planner to use
11175 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11176 : * This may not work for all versions.
11177 : *
11178 : * Our query for retrieving statistics for multiple relations uses
11179 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11180 : * introduced in v9.4. For older versions, we resort to gathering
11181 : * statistics for a single relation at a time.
11182 : */
11183 124 : if (fout->remoteVersion >= 90400)
11184 124 : appendPQExpBufferStr(query,
11185 : "FROM pg_catalog.pg_stats s "
11186 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11187 : "ON s.schemaname = u.schemaname "
11188 : "AND s.tablename = u.tablename "
11189 : "WHERE s.tablename = ANY($2) "
11190 : "ORDER BY u.ord, s.attname, s.inherited");
11191 : else
11192 0 : appendPQExpBufferStr(query,
11193 : "FROM pg_catalog.pg_stats s "
11194 : "WHERE s.schemaname = $1[1] "
11195 : "AND s.tablename = $2[1] "
11196 : "ORDER BY s.attname, s.inherited");
11197 :
11198 124 : ExecuteSqlStatement(fout, query->data);
11199 :
11200 124 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11201 124 : resetPQExpBuffer(query);
11202 : }
11203 :
11204 6456 : initPQExpBuffer(out);
11205 :
11206 : /* restore relation stats */
11207 6456 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11208 6456 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11209 : fout->remoteVersion);
11210 6456 : appendPQExpBufferStr(out, "\t'schemaname', ");
11211 6456 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11212 6456 : appendPQExpBufferStr(out, ",\n");
11213 6456 : appendPQExpBufferStr(out, "\t'relname', ");
11214 6456 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11215 6456 : appendPQExpBufferStr(out, ",\n");
11216 6456 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11217 :
11218 : /*
11219 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11220 : * the relation is empty, or it could mean that it hadn't yet been
11221 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11222 : * This ambiguity allegedly can cause the planner to choose inefficient
11223 : * plans after restoring to v18 or newer. To deal with this, let's just
11224 : * set reltuples to -1 in that case.
11225 : */
11226 6456 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11227 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11228 : else
11229 6456 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11230 :
11231 6456 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11232 6456 : rsinfo->relallvisible);
11233 :
11234 6456 : if (fout->remoteVersion >= 180000)
11235 6456 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11236 :
11237 6456 : appendPQExpBufferStr(out, "\n);\n");
11238 :
11239 : /* Fetch the next batch of attribute statistics if needed. */
11240 6456 : if (rownum >= PQntuples(res))
11241 : {
11242 2070 : PQclear(res);
11243 2070 : res = fetchAttributeStats(fout);
11244 2070 : rownum = 0;
11245 : }
11246 :
11247 6456 : i_schemaname = PQfnumber(res, "schemaname");
11248 6456 : i_tablename = PQfnumber(res, "tablename");
11249 6456 : i_attname = PQfnumber(res, "attname");
11250 6456 : i_inherited = PQfnumber(res, "inherited");
11251 6456 : i_null_frac = PQfnumber(res, "null_frac");
11252 6456 : i_avg_width = PQfnumber(res, "avg_width");
11253 6456 : i_n_distinct = PQfnumber(res, "n_distinct");
11254 6456 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11255 6456 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11256 6456 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11257 6456 : i_correlation = PQfnumber(res, "correlation");
11258 6456 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11259 6456 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11260 6456 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11261 6456 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11262 6456 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11263 6456 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11264 :
11265 : /* restore attribute stats */
11266 7950 : for (; rownum < PQntuples(res); rownum++)
11267 : {
11268 : const char *attname;
11269 :
11270 : /* Stop if the next stat row in our cache isn't for this relation. */
11271 5880 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11272 1494 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11273 : break;
11274 :
11275 1494 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11276 1494 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11277 : fout->remoteVersion);
11278 1494 : appendPQExpBufferStr(out, "\t'schemaname', ");
11279 1494 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11280 1494 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11281 1494 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11282 :
11283 1494 : if (PQgetisnull(res, rownum, i_attname))
11284 0 : pg_fatal("unexpected null attname");
11285 1494 : attname = PQgetvalue(res, rownum, i_attname);
11286 :
11287 : /*
11288 : * Indexes look up attname in indAttNames to derive attnum, all others
11289 : * use attname directly. We must specify attnum for indexes, since
11290 : * their attnames are not necessarily stable across dump/reload.
11291 : */
11292 1494 : if (rsinfo->nindAttNames == 0)
11293 : {
11294 1424 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11295 1424 : appendStringLiteralAH(out, attname, fout);
11296 : }
11297 : else
11298 : {
11299 70 : bool found = false;
11300 :
11301 132 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11302 : {
11303 132 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11304 : {
11305 70 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11306 : i + 1);
11307 70 : found = true;
11308 70 : break;
11309 : }
11310 : }
11311 :
11312 70 : if (!found)
11313 0 : pg_fatal("could not find index attname \"%s\"", attname);
11314 : }
11315 :
11316 1494 : if (!PQgetisnull(res, rownum, i_inherited))
11317 1494 : appendNamedArgument(out, fout, "inherited", "boolean",
11318 1494 : PQgetvalue(res, rownum, i_inherited));
11319 1494 : if (!PQgetisnull(res, rownum, i_null_frac))
11320 1494 : appendNamedArgument(out, fout, "null_frac", "real",
11321 1494 : PQgetvalue(res, rownum, i_null_frac));
11322 1494 : if (!PQgetisnull(res, rownum, i_avg_width))
11323 1494 : appendNamedArgument(out, fout, "avg_width", "integer",
11324 1494 : PQgetvalue(res, rownum, i_avg_width));
11325 1494 : if (!PQgetisnull(res, rownum, i_n_distinct))
11326 1494 : appendNamedArgument(out, fout, "n_distinct", "real",
11327 1494 : PQgetvalue(res, rownum, i_n_distinct));
11328 1494 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11329 744 : appendNamedArgument(out, fout, "most_common_vals", "text",
11330 744 : PQgetvalue(res, rownum, i_most_common_vals));
11331 1494 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11332 744 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11333 744 : PQgetvalue(res, rownum, i_most_common_freqs));
11334 1494 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11335 890 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11336 890 : PQgetvalue(res, rownum, i_histogram_bounds));
11337 1494 : if (!PQgetisnull(res, rownum, i_correlation))
11338 1432 : appendNamedArgument(out, fout, "correlation", "real",
11339 1432 : PQgetvalue(res, rownum, i_correlation));
11340 1494 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11341 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11342 16 : PQgetvalue(res, rownum, i_most_common_elems));
11343 1494 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11344 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11345 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11346 1494 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11347 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11348 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11349 1494 : if (fout->remoteVersion >= 170000)
11350 : {
11351 1494 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11352 6 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11353 6 : PQgetvalue(res, rownum, i_range_length_histogram));
11354 1494 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11355 6 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11356 6 : PQgetvalue(res, rownum, i_range_empty_frac));
11357 1494 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11358 6 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11359 6 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11360 : }
11361 1494 : appendPQExpBufferStr(out, "\n);\n");
11362 : }
11363 :
11364 6456 : destroyPQExpBuffer(query);
11365 6456 : return out->data;
11366 : }
11367 :
11368 : /*
11369 : * dumpRelationStats --
11370 : *
11371 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11372 : * care of gathering the statistics and generating the restore commands when
11373 : * they are needed.
11374 : */
11375 : static void
11376 6594 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11377 : {
11378 6594 : const DumpableObject *dobj = &rsinfo->dobj;
11379 :
11380 : /* nothing to do if we are not dumping statistics */
11381 6594 : if (!fout->dopt->dumpStatistics)
11382 0 : return;
11383 :
11384 6594 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11385 6594 : ARCHIVE_OPTS(.tag = dobj->name,
11386 : .namespace = dobj->namespace->dobj.name,
11387 : .description = "STATISTICS DATA",
11388 : .section = rsinfo->section,
11389 : .defnFn = dumpRelationStats_dumper,
11390 : .defnArg = rsinfo,
11391 : .deps = dobj->dependencies,
11392 : .nDeps = dobj->nDeps));
11393 : }
11394 :
11395 : /*
11396 : * dumpTableComment --
11397 : *
11398 : * As above, but dump comments for both the specified table (or view)
11399 : * and its columns.
11400 : */
11401 : static void
11402 148 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11403 : const char *reltypename)
11404 : {
11405 148 : DumpOptions *dopt = fout->dopt;
11406 : CommentItem *comments;
11407 : int ncomments;
11408 : PQExpBuffer query;
11409 : PQExpBuffer tag;
11410 :
11411 : /* do nothing, if --no-comments is supplied */
11412 148 : if (dopt->no_comments)
11413 0 : return;
11414 :
11415 : /* Comments are SCHEMA not data */
11416 148 : if (!dopt->dumpSchema)
11417 0 : return;
11418 :
11419 : /* Search for comments associated with relation, using table */
11420 148 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11421 148 : tbinfo->dobj.catId.oid,
11422 : &comments);
11423 :
11424 : /* If comments exist, build COMMENT ON statements */
11425 148 : if (ncomments <= 0)
11426 0 : return;
11427 :
11428 148 : query = createPQExpBuffer();
11429 148 : tag = createPQExpBuffer();
11430 :
11431 424 : while (ncomments > 0)
11432 : {
11433 276 : const char *descr = comments->descr;
11434 276 : int objsubid = comments->objsubid;
11435 :
11436 276 : if (objsubid == 0)
11437 : {
11438 64 : resetPQExpBuffer(tag);
11439 64 : appendPQExpBuffer(tag, "%s %s", reltypename,
11440 64 : fmtId(tbinfo->dobj.name));
11441 :
11442 64 : resetPQExpBuffer(query);
11443 64 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11444 64 : fmtQualifiedDumpable(tbinfo));
11445 64 : appendStringLiteralAH(query, descr, fout);
11446 64 : appendPQExpBufferStr(query, ";\n");
11447 :
11448 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11449 64 : ARCHIVE_OPTS(.tag = tag->data,
11450 : .namespace = tbinfo->dobj.namespace->dobj.name,
11451 : .owner = tbinfo->rolname,
11452 : .description = "COMMENT",
11453 : .section = SECTION_NONE,
11454 : .createStmt = query->data,
11455 : .deps = &(tbinfo->dobj.dumpId),
11456 : .nDeps = 1));
11457 : }
11458 212 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11459 : {
11460 212 : resetPQExpBuffer(tag);
11461 212 : appendPQExpBuffer(tag, "COLUMN %s.",
11462 212 : fmtId(tbinfo->dobj.name));
11463 212 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11464 :
11465 212 : resetPQExpBuffer(query);
11466 212 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11467 212 : fmtQualifiedDumpable(tbinfo));
11468 212 : appendPQExpBuffer(query, "%s IS ",
11469 212 : fmtId(tbinfo->attnames[objsubid - 1]));
11470 212 : appendStringLiteralAH(query, descr, fout);
11471 212 : appendPQExpBufferStr(query, ";\n");
11472 :
11473 212 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11474 212 : ARCHIVE_OPTS(.tag = tag->data,
11475 : .namespace = tbinfo->dobj.namespace->dobj.name,
11476 : .owner = tbinfo->rolname,
11477 : .description = "COMMENT",
11478 : .section = SECTION_NONE,
11479 : .createStmt = query->data,
11480 : .deps = &(tbinfo->dobj.dumpId),
11481 : .nDeps = 1));
11482 : }
11483 :
11484 276 : comments++;
11485 276 : ncomments--;
11486 : }
11487 :
11488 148 : destroyPQExpBuffer(query);
11489 148 : destroyPQExpBuffer(tag);
11490 : }
11491 :
11492 : /*
11493 : * findComments --
11494 : *
11495 : * Find the comment(s), if any, associated with the given object. All the
11496 : * objsubid values associated with the given classoid/objoid are found with
11497 : * one search.
11498 : */
11499 : static int
11500 13238 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11501 : {
11502 13238 : CommentItem *middle = NULL;
11503 : CommentItem *low;
11504 : CommentItem *high;
11505 : int nmatch;
11506 :
11507 : /*
11508 : * Do binary search to find some item matching the object.
11509 : */
11510 13238 : low = &comments[0];
11511 13238 : high = &comments[ncomments - 1];
11512 132454 : while (low <= high)
11513 : {
11514 132362 : middle = low + (high - low) / 2;
11515 :
11516 132362 : if (classoid < middle->classoid)
11517 15570 : high = middle - 1;
11518 116792 : else if (classoid > middle->classoid)
11519 14438 : low = middle + 1;
11520 102354 : else if (objoid < middle->objoid)
11521 43348 : high = middle - 1;
11522 59006 : else if (objoid > middle->objoid)
11523 45860 : low = middle + 1;
11524 : else
11525 13146 : break; /* found a match */
11526 : }
11527 :
11528 13238 : if (low > high) /* no matches */
11529 : {
11530 92 : *items = NULL;
11531 92 : return 0;
11532 : }
11533 :
11534 : /*
11535 : * Now determine how many items match the object. The search loop
11536 : * invariant still holds: only items between low and high inclusive could
11537 : * match.
11538 : */
11539 13146 : nmatch = 1;
11540 13272 : while (middle > low)
11541 : {
11542 6042 : if (classoid != middle[-1].classoid ||
11543 5854 : objoid != middle[-1].objoid)
11544 : break;
11545 126 : middle--;
11546 126 : nmatch++;
11547 : }
11548 :
11549 13146 : *items = middle;
11550 :
11551 13146 : middle += nmatch;
11552 13148 : while (middle <= high)
11553 : {
11554 6676 : if (classoid != middle->classoid ||
11555 6392 : objoid != middle->objoid)
11556 : break;
11557 2 : middle++;
11558 2 : nmatch++;
11559 : }
11560 :
11561 13146 : return nmatch;
11562 : }
11563 :
11564 : /*
11565 : * collectComments --
11566 : *
11567 : * Construct a table of all comments available for database objects;
11568 : * also set the has-comment component flag for each relevant object.
11569 : *
11570 : * We used to do per-object queries for the comments, but it's much faster
11571 : * to pull them all over at once, and on most databases the memory cost
11572 : * isn't high.
11573 : *
11574 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11575 : */
11576 : static void
11577 376 : collectComments(Archive *fout)
11578 : {
11579 : PGresult *res;
11580 : PQExpBuffer query;
11581 : int i_description;
11582 : int i_classoid;
11583 : int i_objoid;
11584 : int i_objsubid;
11585 : int ntups;
11586 : int i;
11587 : DumpableObject *dobj;
11588 :
11589 376 : query = createPQExpBuffer();
11590 :
11591 376 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11592 : "FROM pg_catalog.pg_description "
11593 : "ORDER BY classoid, objoid, objsubid");
11594 :
11595 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11596 :
11597 : /* Construct lookup table containing OIDs in numeric form */
11598 :
11599 376 : i_description = PQfnumber(res, "description");
11600 376 : i_classoid = PQfnumber(res, "classoid");
11601 376 : i_objoid = PQfnumber(res, "objoid");
11602 376 : i_objsubid = PQfnumber(res, "objsubid");
11603 :
11604 376 : ntups = PQntuples(res);
11605 :
11606 376 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11607 376 : ncomments = 0;
11608 376 : dobj = NULL;
11609 :
11610 2018562 : for (i = 0; i < ntups; i++)
11611 : {
11612 : CatalogId objId;
11613 : int subid;
11614 :
11615 2018186 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11616 2018186 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11617 2018186 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11618 :
11619 : /* We needn't remember comments that don't match any dumpable object */
11620 2018186 : if (dobj == NULL ||
11621 725698 : dobj->catId.tableoid != objId.tableoid ||
11622 721088 : dobj->catId.oid != objId.oid)
11623 2018006 : dobj = findObjectByCatalogId(objId);
11624 2018186 : if (dobj == NULL)
11625 1292124 : continue;
11626 :
11627 : /*
11628 : * Comments on columns of composite types are linked to the type's
11629 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11630 : * in the type's own DumpableObject.
11631 : */
11632 726062 : if (subid != 0 && dobj->objType == DO_TABLE &&
11633 388 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11634 90 : {
11635 : TypeInfo *cTypeInfo;
11636 :
11637 90 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11638 90 : if (cTypeInfo)
11639 90 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11640 : }
11641 : else
11642 725972 : dobj->components |= DUMP_COMPONENT_COMMENT;
11643 :
11644 726062 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11645 726062 : comments[ncomments].classoid = objId.tableoid;
11646 726062 : comments[ncomments].objoid = objId.oid;
11647 726062 : comments[ncomments].objsubid = subid;
11648 726062 : ncomments++;
11649 : }
11650 :
11651 376 : PQclear(res);
11652 376 : destroyPQExpBuffer(query);
11653 376 : }
11654 :
11655 : /*
11656 : * dumpDumpableObject
11657 : *
11658 : * This routine and its subsidiaries are responsible for creating
11659 : * ArchiveEntries (TOC objects) for each object to be dumped.
11660 : */
11661 : static void
11662 1401332 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11663 : {
11664 : /*
11665 : * Clear any dump-request bits for components that don't exist for this
11666 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11667 : * request for every kind of object.)
11668 : */
11669 1401332 : dobj->dump &= dobj->components;
11670 :
11671 : /* Now, short-circuit if there's nothing to be done here. */
11672 1401332 : if (dobj->dump == 0)
11673 1248260 : return;
11674 :
11675 153072 : switch (dobj->objType)
11676 : {
11677 978 : case DO_NAMESPACE:
11678 978 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11679 978 : break;
11680 48 : case DO_EXTENSION:
11681 48 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11682 48 : break;
11683 1846 : case DO_TYPE:
11684 1846 : dumpType(fout, (const TypeInfo *) dobj);
11685 1846 : break;
11686 146 : case DO_SHELL_TYPE:
11687 146 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11688 146 : break;
11689 3656 : case DO_FUNC:
11690 3656 : dumpFunc(fout, (const FuncInfo *) dobj);
11691 3656 : break;
11692 584 : case DO_AGG:
11693 584 : dumpAgg(fout, (const AggInfo *) dobj);
11694 584 : break;
11695 5044 : case DO_OPERATOR:
11696 5044 : dumpOpr(fout, (const OprInfo *) dobj);
11697 5044 : break;
11698 160 : case DO_ACCESS_METHOD:
11699 160 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11700 160 : break;
11701 1332 : case DO_OPCLASS:
11702 1332 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11703 1332 : break;
11704 1110 : case DO_OPFAMILY:
11705 1110 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11706 1110 : break;
11707 5074 : case DO_COLLATION:
11708 5074 : dumpCollation(fout, (const CollInfo *) dobj);
11709 5074 : break;
11710 844 : case DO_CONVERSION:
11711 844 : dumpConversion(fout, (const ConvInfo *) dobj);
11712 844 : break;
11713 62652 : case DO_TABLE:
11714 62652 : dumpTable(fout, (const TableInfo *) dobj);
11715 62652 : break;
11716 2782 : case DO_TABLE_ATTACH:
11717 2782 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11718 2782 : break;
11719 2064 : case DO_ATTRDEF:
11720 2064 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11721 2064 : break;
11722 5200 : case DO_INDEX:
11723 5200 : dumpIndex(fout, (const IndxInfo *) dobj);
11724 5200 : break;
11725 1148 : case DO_INDEX_ATTACH:
11726 1148 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11727 1148 : break;
11728 266 : case DO_STATSEXT:
11729 266 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11730 266 : break;
11731 690 : case DO_REFRESH_MATVIEW:
11732 690 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11733 690 : break;
11734 2258 : case DO_RULE:
11735 2258 : dumpRule(fout, (const RuleInfo *) dobj);
11736 2258 : break;
11737 1046 : case DO_TRIGGER:
11738 1046 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11739 1046 : break;
11740 84 : case DO_EVENT_TRIGGER:
11741 84 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11742 84 : break;
11743 4636 : case DO_CONSTRAINT:
11744 4636 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11745 4636 : break;
11746 342 : case DO_FK_CONSTRAINT:
11747 342 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11748 342 : break;
11749 164 : case DO_PROCLANG:
11750 164 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11751 164 : break;
11752 134 : case DO_CAST:
11753 134 : dumpCast(fout, (const CastInfo *) dobj);
11754 134 : break;
11755 84 : case DO_TRANSFORM:
11756 84 : dumpTransform(fout, (const TransformInfo *) dobj);
11757 84 : break;
11758 786 : case DO_SEQUENCE_SET:
11759 786 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11760 786 : break;
11761 8452 : case DO_TABLE_DATA:
11762 8452 : dumpTableData(fout, (const TableDataInfo *) dobj);
11763 8452 : break;
11764 28164 : case DO_DUMMY_TYPE:
11765 : /* table rowtypes and array types are never dumped separately */
11766 28164 : break;
11767 82 : case DO_TSPARSER:
11768 82 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11769 82 : break;
11770 358 : case DO_TSDICT:
11771 358 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11772 358 : break;
11773 106 : case DO_TSTEMPLATE:
11774 106 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11775 106 : break;
11776 308 : case DO_TSCONFIG:
11777 308 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11778 308 : break;
11779 104 : case DO_FDW:
11780 104 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11781 104 : break;
11782 112 : case DO_FOREIGN_SERVER:
11783 112 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11784 112 : break;
11785 320 : case DO_DEFAULT_ACL:
11786 320 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11787 320 : break;
11788 168 : case DO_LARGE_OBJECT:
11789 168 : dumpLO(fout, (const LoInfo *) dobj);
11790 168 : break;
11791 180 : case DO_LARGE_OBJECT_DATA:
11792 180 : if (dobj->dump & DUMP_COMPONENT_DATA)
11793 : {
11794 : LoInfo *loinfo;
11795 : TocEntry *te;
11796 :
11797 180 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11798 180 : if (loinfo == NULL)
11799 0 : pg_fatal("missing metadata for large objects \"%s\"",
11800 : dobj->name);
11801 :
11802 180 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11803 180 : ARCHIVE_OPTS(.tag = dobj->name,
11804 : .owner = loinfo->rolname,
11805 : .description = "BLOBS",
11806 : .section = SECTION_DATA,
11807 : .deps = dobj->dependencies,
11808 : .nDeps = dobj->nDeps,
11809 : .dumpFn = dumpLOs,
11810 : .dumpArg = loinfo));
11811 :
11812 : /*
11813 : * Set the TocEntry's dataLength in case we are doing a
11814 : * parallel dump and want to order dump jobs by table size.
11815 : * (We need some size estimate for every TocEntry with a
11816 : * DataDumper function.) We don't currently have any cheap
11817 : * way to estimate the size of LOs, but fortunately it doesn't
11818 : * matter too much as long as we get large batches of LOs
11819 : * processed reasonably early. Assume 8K per blob.
11820 : */
11821 180 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11822 : }
11823 180 : break;
11824 652 : case DO_POLICY:
11825 652 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11826 652 : break;
11827 570 : case DO_PUBLICATION:
11828 570 : dumpPublication(fout, (const PublicationInfo *) dobj);
11829 570 : break;
11830 568 : case DO_PUBLICATION_REL:
11831 568 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11832 568 : break;
11833 198 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11834 198 : dumpPublicationNamespace(fout,
11835 : (const PublicationSchemaInfo *) dobj);
11836 198 : break;
11837 220 : case DO_SUBSCRIPTION:
11838 220 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11839 220 : break;
11840 6 : case DO_SUBSCRIPTION_REL:
11841 6 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11842 6 : break;
11843 6594 : case DO_REL_STATS:
11844 6594 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11845 6594 : break;
11846 752 : case DO_PRE_DATA_BOUNDARY:
11847 : case DO_POST_DATA_BOUNDARY:
11848 : /* never dumped, nothing to do */
11849 752 : break;
11850 : }
11851 : }
11852 :
11853 : /*
11854 : * dumpNamespace
11855 : * writes out to fout the queries to recreate a user-defined namespace
11856 : */
11857 : static void
11858 978 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11859 : {
11860 978 : DumpOptions *dopt = fout->dopt;
11861 : PQExpBuffer q;
11862 : PQExpBuffer delq;
11863 : char *qnspname;
11864 :
11865 : /* Do nothing if not dumping schema */
11866 978 : if (!dopt->dumpSchema)
11867 56 : return;
11868 :
11869 922 : q = createPQExpBuffer();
11870 922 : delq = createPQExpBuffer();
11871 :
11872 922 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11873 :
11874 922 : if (nspinfo->create)
11875 : {
11876 616 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11877 616 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11878 : }
11879 : else
11880 : {
11881 : /* see selectDumpableNamespace() */
11882 306 : appendPQExpBufferStr(delq,
11883 : "-- *not* dropping schema, since initdb creates it\n");
11884 306 : appendPQExpBufferStr(q,
11885 : "-- *not* creating schema, since initdb creates it\n");
11886 : }
11887 :
11888 922 : if (dopt->binary_upgrade)
11889 188 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11890 : "SCHEMA", qnspname, NULL);
11891 :
11892 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11893 370 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11894 370 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11895 : .owner = nspinfo->rolname,
11896 : .description = "SCHEMA",
11897 : .section = SECTION_PRE_DATA,
11898 : .createStmt = q->data,
11899 : .dropStmt = delq->data));
11900 :
11901 : /* Dump Schema Comments and Security Labels */
11902 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11903 : {
11904 316 : const char *initdb_comment = NULL;
11905 :
11906 316 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11907 234 : initdb_comment = "standard public schema";
11908 316 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11909 316 : NULL, nspinfo->rolname,
11910 316 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11911 : initdb_comment);
11912 : }
11913 :
11914 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11915 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11916 0 : NULL, nspinfo->rolname,
11917 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11918 :
11919 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11920 718 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11921 : qnspname, NULL, NULL,
11922 718 : NULL, nspinfo->rolname, &nspinfo->dacl);
11923 :
11924 922 : free(qnspname);
11925 :
11926 922 : destroyPQExpBuffer(q);
11927 922 : destroyPQExpBuffer(delq);
11928 : }
11929 :
11930 : /*
11931 : * dumpExtension
11932 : * writes out to fout the queries to recreate an extension
11933 : */
11934 : static void
11935 48 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11936 : {
11937 48 : DumpOptions *dopt = fout->dopt;
11938 : PQExpBuffer q;
11939 : PQExpBuffer delq;
11940 : char *qextname;
11941 :
11942 : /* Do nothing if not dumping schema */
11943 48 : if (!dopt->dumpSchema)
11944 2 : return;
11945 :
11946 46 : q = createPQExpBuffer();
11947 46 : delq = createPQExpBuffer();
11948 :
11949 46 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11950 :
11951 46 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11952 :
11953 46 : if (!dopt->binary_upgrade)
11954 : {
11955 : /*
11956 : * In a regular dump, we simply create the extension, intentionally
11957 : * not specifying a version, so that the destination installation's
11958 : * default version is used.
11959 : *
11960 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11961 : * types; but there are various scenarios in which it's convenient to
11962 : * manually create the desired extension before restoring, so we
11963 : * prefer to allow it to exist already.
11964 : */
11965 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11966 34 : qextname, fmtId(extinfo->namespace));
11967 : }
11968 : else
11969 : {
11970 : /*
11971 : * In binary-upgrade mode, it's critical to reproduce the state of the
11972 : * database exactly, so our procedure is to create an empty extension,
11973 : * restore all the contained objects normally, and add them to the
11974 : * extension one by one. This function performs just the first of
11975 : * those steps. binary_upgrade_extension_member() takes care of
11976 : * adding member objects as they're created.
11977 : */
11978 : int i;
11979 : int n;
11980 :
11981 12 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11982 :
11983 : /*
11984 : * We unconditionally create the extension, so we must drop it if it
11985 : * exists. This could happen if the user deleted 'plpgsql' and then
11986 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11987 : */
11988 12 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11989 :
11990 12 : appendPQExpBufferStr(q,
11991 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11992 12 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11993 12 : appendPQExpBufferStr(q, ", ");
11994 12 : appendStringLiteralAH(q, extinfo->namespace, fout);
11995 12 : appendPQExpBufferStr(q, ", ");
11996 12 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11997 12 : appendStringLiteralAH(q, extinfo->extversion, fout);
11998 12 : appendPQExpBufferStr(q, ", ");
11999 :
12000 : /*
12001 : * Note that we're pushing extconfig (an OID array) back into
12002 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
12003 : * preserved in binary upgrade.
12004 : */
12005 12 : if (strlen(extinfo->extconfig) > 2)
12006 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
12007 : else
12008 10 : appendPQExpBufferStr(q, "NULL");
12009 12 : appendPQExpBufferStr(q, ", ");
12010 12 : if (strlen(extinfo->extcondition) > 2)
12011 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
12012 : else
12013 10 : appendPQExpBufferStr(q, "NULL");
12014 12 : appendPQExpBufferStr(q, ", ");
12015 12 : appendPQExpBufferStr(q, "ARRAY[");
12016 12 : n = 0;
12017 24 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12018 : {
12019 : DumpableObject *extobj;
12020 :
12021 12 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12022 12 : if (extobj && extobj->objType == DO_EXTENSION)
12023 : {
12024 0 : if (n++ > 0)
12025 0 : appendPQExpBufferChar(q, ',');
12026 0 : appendStringLiteralAH(q, extobj->name, fout);
12027 : }
12028 : }
12029 12 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12030 12 : appendPQExpBufferStr(q, ");\n");
12031 : }
12032 :
12033 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12034 46 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12035 46 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12036 : .description = "EXTENSION",
12037 : .section = SECTION_PRE_DATA,
12038 : .createStmt = q->data,
12039 : .dropStmt = delq->data));
12040 :
12041 : /* Dump Extension Comments */
12042 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12043 46 : dumpComment(fout, "EXTENSION", qextname,
12044 : NULL, "",
12045 46 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12046 :
12047 46 : free(qextname);
12048 :
12049 46 : destroyPQExpBuffer(q);
12050 46 : destroyPQExpBuffer(delq);
12051 : }
12052 :
12053 : /*
12054 : * dumpType
12055 : * writes out to fout the queries to recreate a user-defined type
12056 : */
12057 : static void
12058 1846 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12059 : {
12060 1846 : DumpOptions *dopt = fout->dopt;
12061 :
12062 : /* Do nothing if not dumping schema */
12063 1846 : if (!dopt->dumpSchema)
12064 98 : return;
12065 :
12066 : /* Dump out in proper style */
12067 1748 : if (tyinfo->typtype == TYPTYPE_BASE)
12068 566 : dumpBaseType(fout, tyinfo);
12069 1182 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12070 304 : dumpDomain(fout, tyinfo);
12071 878 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12072 260 : dumpCompositeType(fout, tyinfo);
12073 618 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12074 170 : dumpEnumType(fout, tyinfo);
12075 448 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12076 224 : dumpRangeType(fout, tyinfo);
12077 224 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12078 74 : dumpUndefinedType(fout, tyinfo);
12079 : else
12080 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12081 : tyinfo->dobj.name);
12082 : }
12083 :
12084 : /*
12085 : * dumpEnumType
12086 : * writes out to fout the queries to recreate a user-defined enum type
12087 : */
12088 : static void
12089 170 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12090 : {
12091 170 : DumpOptions *dopt = fout->dopt;
12092 170 : PQExpBuffer q = createPQExpBuffer();
12093 170 : PQExpBuffer delq = createPQExpBuffer();
12094 170 : PQExpBuffer query = createPQExpBuffer();
12095 : PGresult *res;
12096 : int num,
12097 : i;
12098 : Oid enum_oid;
12099 : char *qtypname;
12100 : char *qualtypname;
12101 : char *label;
12102 : int i_enumlabel;
12103 : int i_oid;
12104 :
12105 170 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12106 : {
12107 : /* Set up query for enum-specific details */
12108 80 : appendPQExpBufferStr(query,
12109 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12110 : "SELECT oid, enumlabel "
12111 : "FROM pg_catalog.pg_enum "
12112 : "WHERE enumtypid = $1 "
12113 : "ORDER BY enumsortorder");
12114 :
12115 80 : ExecuteSqlStatement(fout, query->data);
12116 :
12117 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12118 : }
12119 :
12120 170 : printfPQExpBuffer(query,
12121 : "EXECUTE dumpEnumType('%u')",
12122 170 : tyinfo->dobj.catId.oid);
12123 :
12124 170 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12125 :
12126 170 : num = PQntuples(res);
12127 :
12128 170 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12129 170 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12130 :
12131 : /*
12132 : * CASCADE shouldn't be required here as for normal types since the I/O
12133 : * functions are generic and do not get dropped.
12134 : */
12135 170 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12136 :
12137 170 : if (dopt->binary_upgrade)
12138 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12139 12 : tyinfo->dobj.catId.oid,
12140 : false, false);
12141 :
12142 170 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12143 : qualtypname);
12144 :
12145 170 : if (!dopt->binary_upgrade)
12146 : {
12147 158 : i_enumlabel = PQfnumber(res, "enumlabel");
12148 :
12149 : /* Labels with server-assigned oids */
12150 964 : for (i = 0; i < num; i++)
12151 : {
12152 806 : label = PQgetvalue(res, i, i_enumlabel);
12153 806 : if (i > 0)
12154 648 : appendPQExpBufferChar(q, ',');
12155 806 : appendPQExpBufferStr(q, "\n ");
12156 806 : appendStringLiteralAH(q, label, fout);
12157 : }
12158 : }
12159 :
12160 170 : appendPQExpBufferStr(q, "\n);\n");
12161 :
12162 170 : if (dopt->binary_upgrade)
12163 : {
12164 12 : i_oid = PQfnumber(res, "oid");
12165 12 : i_enumlabel = PQfnumber(res, "enumlabel");
12166 :
12167 : /* Labels with dump-assigned (preserved) oids */
12168 124 : for (i = 0; i < num; i++)
12169 : {
12170 112 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12171 112 : label = PQgetvalue(res, i, i_enumlabel);
12172 :
12173 112 : if (i == 0)
12174 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12175 112 : appendPQExpBuffer(q,
12176 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12177 : enum_oid);
12178 112 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12179 112 : appendStringLiteralAH(q, label, fout);
12180 112 : appendPQExpBufferStr(q, ";\n\n");
12181 : }
12182 : }
12183 :
12184 170 : if (dopt->binary_upgrade)
12185 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12186 : "TYPE", qtypname,
12187 12 : tyinfo->dobj.namespace->dobj.name);
12188 :
12189 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12190 170 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12191 170 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12192 : .namespace = tyinfo->dobj.namespace->dobj.name,
12193 : .owner = tyinfo->rolname,
12194 : .description = "TYPE",
12195 : .section = SECTION_PRE_DATA,
12196 : .createStmt = q->data,
12197 : .dropStmt = delq->data));
12198 :
12199 : /* Dump Type Comments and Security Labels */
12200 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12201 64 : dumpComment(fout, "TYPE", qtypname,
12202 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12203 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12204 :
12205 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12206 0 : dumpSecLabel(fout, "TYPE", qtypname,
12207 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12208 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12209 :
12210 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12211 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12212 : qtypname, NULL,
12213 64 : tyinfo->dobj.namespace->dobj.name,
12214 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12215 :
12216 170 : PQclear(res);
12217 170 : destroyPQExpBuffer(q);
12218 170 : destroyPQExpBuffer(delq);
12219 170 : destroyPQExpBuffer(query);
12220 170 : free(qtypname);
12221 170 : free(qualtypname);
12222 170 : }
12223 :
12224 : /*
12225 : * dumpRangeType
12226 : * writes out to fout the queries to recreate a user-defined range type
12227 : */
12228 : static void
12229 224 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12230 : {
12231 224 : DumpOptions *dopt = fout->dopt;
12232 224 : PQExpBuffer q = createPQExpBuffer();
12233 224 : PQExpBuffer delq = createPQExpBuffer();
12234 224 : PQExpBuffer query = createPQExpBuffer();
12235 : PGresult *res;
12236 : Oid collationOid;
12237 : char *qtypname;
12238 : char *qualtypname;
12239 : char *procname;
12240 :
12241 224 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12242 : {
12243 : /* Set up query for range-specific details */
12244 80 : appendPQExpBufferStr(query,
12245 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12246 :
12247 80 : appendPQExpBufferStr(query,
12248 : "SELECT ");
12249 :
12250 80 : if (fout->remoteVersion >= 140000)
12251 80 : appendPQExpBufferStr(query,
12252 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12253 : else
12254 0 : appendPQExpBufferStr(query,
12255 : "NULL AS rngmultitype, ");
12256 :
12257 80 : appendPQExpBufferStr(query,
12258 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12259 : "opc.opcname AS opcname, "
12260 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12261 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12262 : "opc.opcdefault, "
12263 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12264 : " ELSE rngcollation END AS collation, "
12265 : "rngcanonical, rngsubdiff "
12266 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12267 : " pg_catalog.pg_opclass opc "
12268 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12269 : "rngtypid = $1");
12270 :
12271 80 : ExecuteSqlStatement(fout, query->data);
12272 :
12273 80 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12274 : }
12275 :
12276 224 : printfPQExpBuffer(query,
12277 : "EXECUTE dumpRangeType('%u')",
12278 224 : tyinfo->dobj.catId.oid);
12279 :
12280 224 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12281 :
12282 224 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12283 224 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12284 :
12285 : /*
12286 : * CASCADE shouldn't be required here as for normal types since the I/O
12287 : * functions are generic and do not get dropped.
12288 : */
12289 224 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12290 :
12291 224 : if (dopt->binary_upgrade)
12292 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12293 16 : tyinfo->dobj.catId.oid,
12294 : false, true);
12295 :
12296 224 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12297 : qualtypname);
12298 :
12299 224 : appendPQExpBuffer(q, "\n subtype = %s",
12300 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12301 :
12302 224 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12303 224 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12304 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12305 :
12306 : /* print subtype_opclass only if not default for subtype */
12307 224 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12308 : {
12309 64 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12310 64 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12311 :
12312 64 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12313 : fmtId(nspname));
12314 64 : appendPQExpBufferStr(q, fmtId(opcname));
12315 : }
12316 :
12317 224 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12318 224 : if (OidIsValid(collationOid))
12319 : {
12320 74 : CollInfo *coll = findCollationByOid(collationOid);
12321 :
12322 74 : if (coll)
12323 74 : appendPQExpBuffer(q, ",\n collation = %s",
12324 74 : fmtQualifiedDumpable(coll));
12325 : }
12326 :
12327 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12328 224 : if (strcmp(procname, "-") != 0)
12329 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12330 :
12331 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12332 224 : if (strcmp(procname, "-") != 0)
12333 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12334 :
12335 224 : appendPQExpBufferStr(q, "\n);\n");
12336 :
12337 224 : if (dopt->binary_upgrade)
12338 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12339 : "TYPE", qtypname,
12340 16 : tyinfo->dobj.namespace->dobj.name);
12341 :
12342 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12343 224 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12344 224 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12345 : .namespace = tyinfo->dobj.namespace->dobj.name,
12346 : .owner = tyinfo->rolname,
12347 : .description = "TYPE",
12348 : .section = SECTION_PRE_DATA,
12349 : .createStmt = q->data,
12350 : .dropStmt = delq->data));
12351 :
12352 : /* Dump Type Comments and Security Labels */
12353 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12354 100 : dumpComment(fout, "TYPE", qtypname,
12355 100 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12356 100 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12357 :
12358 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12359 0 : dumpSecLabel(fout, "TYPE", qtypname,
12360 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12361 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12362 :
12363 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12364 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12365 : qtypname, NULL,
12366 64 : tyinfo->dobj.namespace->dobj.name,
12367 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12368 :
12369 224 : PQclear(res);
12370 224 : destroyPQExpBuffer(q);
12371 224 : destroyPQExpBuffer(delq);
12372 224 : destroyPQExpBuffer(query);
12373 224 : free(qtypname);
12374 224 : free(qualtypname);
12375 224 : }
12376 :
12377 : /*
12378 : * dumpUndefinedType
12379 : * writes out to fout the queries to recreate a !typisdefined type
12380 : *
12381 : * This is a shell type, but we use different terminology to distinguish
12382 : * this case from where we have to emit a shell type definition to break
12383 : * circular dependencies. An undefined type shouldn't ever have anything
12384 : * depending on it.
12385 : */
12386 : static void
12387 74 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12388 : {
12389 74 : DumpOptions *dopt = fout->dopt;
12390 74 : PQExpBuffer q = createPQExpBuffer();
12391 74 : PQExpBuffer delq = createPQExpBuffer();
12392 : char *qtypname;
12393 : char *qualtypname;
12394 :
12395 74 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12396 74 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12397 :
12398 74 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12399 :
12400 74 : if (dopt->binary_upgrade)
12401 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12402 4 : tyinfo->dobj.catId.oid,
12403 : false, false);
12404 :
12405 74 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12406 : qualtypname);
12407 :
12408 74 : if (dopt->binary_upgrade)
12409 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12410 : "TYPE", qtypname,
12411 4 : tyinfo->dobj.namespace->dobj.name);
12412 :
12413 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12414 74 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12415 74 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12416 : .namespace = tyinfo->dobj.namespace->dobj.name,
12417 : .owner = tyinfo->rolname,
12418 : .description = "TYPE",
12419 : .section = SECTION_PRE_DATA,
12420 : .createStmt = q->data,
12421 : .dropStmt = delq->data));
12422 :
12423 : /* Dump Type Comments and Security Labels */
12424 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12425 64 : dumpComment(fout, "TYPE", qtypname,
12426 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12427 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12428 :
12429 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12430 0 : dumpSecLabel(fout, "TYPE", qtypname,
12431 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12432 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12433 :
12434 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12435 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12436 : qtypname, NULL,
12437 0 : tyinfo->dobj.namespace->dobj.name,
12438 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12439 :
12440 74 : destroyPQExpBuffer(q);
12441 74 : destroyPQExpBuffer(delq);
12442 74 : free(qtypname);
12443 74 : free(qualtypname);
12444 74 : }
12445 :
12446 : /*
12447 : * dumpBaseType
12448 : * writes out to fout the queries to recreate a user-defined base type
12449 : */
12450 : static void
12451 566 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12452 : {
12453 566 : DumpOptions *dopt = fout->dopt;
12454 566 : PQExpBuffer q = createPQExpBuffer();
12455 566 : PQExpBuffer delq = createPQExpBuffer();
12456 566 : PQExpBuffer query = createPQExpBuffer();
12457 : PGresult *res;
12458 : char *qtypname;
12459 : char *qualtypname;
12460 : char *typlen;
12461 : char *typinput;
12462 : char *typoutput;
12463 : char *typreceive;
12464 : char *typsend;
12465 : char *typmodin;
12466 : char *typmodout;
12467 : char *typanalyze;
12468 : char *typsubscript;
12469 : Oid typreceiveoid;
12470 : Oid typsendoid;
12471 : Oid typmodinoid;
12472 : Oid typmodoutoid;
12473 : Oid typanalyzeoid;
12474 : Oid typsubscriptoid;
12475 : char *typcategory;
12476 : char *typispreferred;
12477 : char *typdelim;
12478 : char *typbyval;
12479 : char *typalign;
12480 : char *typstorage;
12481 : char *typcollatable;
12482 : char *typdefault;
12483 566 : bool typdefault_is_literal = false;
12484 :
12485 566 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12486 : {
12487 : /* Set up query for type-specific details */
12488 80 : appendPQExpBufferStr(query,
12489 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12490 : "SELECT typlen, "
12491 : "typinput, typoutput, typreceive, typsend, "
12492 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12493 : "typsend::pg_catalog.oid AS typsendoid, "
12494 : "typanalyze, "
12495 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12496 : "typdelim, typbyval, typalign, typstorage, "
12497 : "typmodin, typmodout, "
12498 : "typmodin::pg_catalog.oid AS typmodinoid, "
12499 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12500 : "typcategory, typispreferred, "
12501 : "(typcollation <> 0) AS typcollatable, "
12502 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12503 :
12504 80 : if (fout->remoteVersion >= 140000)
12505 80 : appendPQExpBufferStr(query,
12506 : "typsubscript, "
12507 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12508 : else
12509 0 : appendPQExpBufferStr(query,
12510 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12511 :
12512 80 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12513 : "WHERE oid = $1");
12514 :
12515 80 : ExecuteSqlStatement(fout, query->data);
12516 :
12517 80 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12518 : }
12519 :
12520 566 : printfPQExpBuffer(query,
12521 : "EXECUTE dumpBaseType('%u')",
12522 566 : tyinfo->dobj.catId.oid);
12523 :
12524 566 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12525 :
12526 566 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12527 566 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12528 566 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12529 566 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12530 566 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12531 566 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12532 566 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12533 566 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12534 566 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12535 566 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12536 566 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12537 566 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12538 566 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12539 566 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12540 566 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12541 566 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12542 566 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12543 566 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12544 566 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12545 566 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12546 566 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12547 566 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12548 566 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12549 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12550 566 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12551 : {
12552 84 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12553 84 : typdefault_is_literal = true; /* it needs quotes */
12554 : }
12555 : else
12556 482 : typdefault = NULL;
12557 :
12558 566 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12559 566 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12560 :
12561 : /*
12562 : * The reason we include CASCADE is that the circular dependency between
12563 : * the type and its I/O functions makes it impossible to drop the type any
12564 : * other way.
12565 : */
12566 566 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12567 :
12568 : /*
12569 : * We might already have a shell type, but setting pg_type_oid is
12570 : * harmless, and in any case we'd better set the array type OID.
12571 : */
12572 566 : if (dopt->binary_upgrade)
12573 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12574 16 : tyinfo->dobj.catId.oid,
12575 : false, false);
12576 :
12577 566 : appendPQExpBuffer(q,
12578 : "CREATE TYPE %s (\n"
12579 : " INTERNALLENGTH = %s",
12580 : qualtypname,
12581 566 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12582 :
12583 : /* regproc result is sufficiently quoted already */
12584 566 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12585 566 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12586 566 : if (OidIsValid(typreceiveoid))
12587 420 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12588 566 : if (OidIsValid(typsendoid))
12589 420 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12590 566 : if (OidIsValid(typmodinoid))
12591 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12592 566 : if (OidIsValid(typmodoutoid))
12593 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12594 566 : if (OidIsValid(typanalyzeoid))
12595 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12596 :
12597 566 : if (strcmp(typcollatable, "t") == 0)
12598 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12599 :
12600 566 : if (typdefault != NULL)
12601 : {
12602 84 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12603 84 : if (typdefault_is_literal)
12604 84 : appendStringLiteralAH(q, typdefault, fout);
12605 : else
12606 0 : appendPQExpBufferStr(q, typdefault);
12607 : }
12608 :
12609 566 : if (OidIsValid(typsubscriptoid))
12610 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12611 :
12612 566 : if (OidIsValid(tyinfo->typelem))
12613 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12614 52 : getFormattedTypeName(fout, tyinfo->typelem,
12615 : zeroIsError));
12616 :
12617 566 : if (strcmp(typcategory, "U") != 0)
12618 : {
12619 322 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12620 322 : appendStringLiteralAH(q, typcategory, fout);
12621 : }
12622 :
12623 566 : if (strcmp(typispreferred, "t") == 0)
12624 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12625 :
12626 566 : if (typdelim && strcmp(typdelim, ",") != 0)
12627 : {
12628 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12629 6 : appendStringLiteralAH(q, typdelim, fout);
12630 : }
12631 :
12632 566 : if (*typalign == TYPALIGN_CHAR)
12633 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12634 542 : else if (*typalign == TYPALIGN_SHORT)
12635 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12636 530 : else if (*typalign == TYPALIGN_INT)
12637 374 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12638 156 : else if (*typalign == TYPALIGN_DOUBLE)
12639 156 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12640 :
12641 566 : if (*typstorage == TYPSTORAGE_PLAIN)
12642 416 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12643 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12644 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12645 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12646 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12647 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12648 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12649 :
12650 566 : if (strcmp(typbyval, "t") == 0)
12651 274 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12652 :
12653 566 : appendPQExpBufferStr(q, "\n);\n");
12654 :
12655 566 : if (dopt->binary_upgrade)
12656 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12657 : "TYPE", qtypname,
12658 16 : tyinfo->dobj.namespace->dobj.name);
12659 :
12660 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12661 566 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12662 566 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12663 : .namespace = tyinfo->dobj.namespace->dobj.name,
12664 : .owner = tyinfo->rolname,
12665 : .description = "TYPE",
12666 : .section = SECTION_PRE_DATA,
12667 : .createStmt = q->data,
12668 : .dropStmt = delq->data));
12669 :
12670 : /* Dump Type Comments and Security Labels */
12671 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12672 496 : dumpComment(fout, "TYPE", qtypname,
12673 496 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12674 496 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12675 :
12676 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12677 0 : dumpSecLabel(fout, "TYPE", qtypname,
12678 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12679 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12680 :
12681 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12682 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12683 : qtypname, NULL,
12684 64 : tyinfo->dobj.namespace->dobj.name,
12685 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12686 :
12687 566 : PQclear(res);
12688 566 : destroyPQExpBuffer(q);
12689 566 : destroyPQExpBuffer(delq);
12690 566 : destroyPQExpBuffer(query);
12691 566 : free(qtypname);
12692 566 : free(qualtypname);
12693 566 : }
12694 :
12695 : /*
12696 : * dumpDomain
12697 : * writes out to fout the queries to recreate a user-defined domain
12698 : */
12699 : static void
12700 304 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12701 : {
12702 304 : DumpOptions *dopt = fout->dopt;
12703 304 : PQExpBuffer q = createPQExpBuffer();
12704 304 : PQExpBuffer delq = createPQExpBuffer();
12705 304 : PQExpBuffer query = createPQExpBuffer();
12706 : PGresult *res;
12707 : int i;
12708 : char *qtypname;
12709 : char *qualtypname;
12710 : char *typnotnull;
12711 : char *typdefn;
12712 : char *typdefault;
12713 : Oid typcollation;
12714 304 : bool typdefault_is_literal = false;
12715 :
12716 304 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12717 : {
12718 : /* Set up query for domain-specific details */
12719 74 : appendPQExpBufferStr(query,
12720 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12721 :
12722 74 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12723 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12724 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12725 : "t.typdefault, "
12726 : "CASE WHEN t.typcollation <> u.typcollation "
12727 : "THEN t.typcollation ELSE 0 END AS typcollation "
12728 : "FROM pg_catalog.pg_type t "
12729 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12730 : "WHERE t.oid = $1");
12731 :
12732 74 : ExecuteSqlStatement(fout, query->data);
12733 :
12734 74 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12735 : }
12736 :
12737 304 : printfPQExpBuffer(query,
12738 : "EXECUTE dumpDomain('%u')",
12739 304 : tyinfo->dobj.catId.oid);
12740 :
12741 304 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12742 :
12743 304 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12744 304 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12745 304 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12746 74 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12747 230 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12748 : {
12749 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12750 0 : typdefault_is_literal = true; /* it needs quotes */
12751 : }
12752 : else
12753 230 : typdefault = NULL;
12754 304 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12755 :
12756 304 : if (dopt->binary_upgrade)
12757 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12758 50 : tyinfo->dobj.catId.oid,
12759 : true, /* force array type */
12760 : false); /* force multirange type */
12761 :
12762 304 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12763 304 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12764 :
12765 304 : appendPQExpBuffer(q,
12766 : "CREATE DOMAIN %s AS %s",
12767 : qualtypname,
12768 : typdefn);
12769 :
12770 : /* Print collation only if different from base type's collation */
12771 304 : if (OidIsValid(typcollation))
12772 : {
12773 : CollInfo *coll;
12774 :
12775 64 : coll = findCollationByOid(typcollation);
12776 64 : if (coll)
12777 64 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12778 : }
12779 :
12780 : /*
12781 : * Print a not-null constraint if there's one. In servers older than 17
12782 : * these don't have names, so just print it unadorned; in newer ones they
12783 : * do, but most of the time it's going to be the standard generated one,
12784 : * so omit the name in that case also.
12785 : */
12786 304 : if (typnotnull[0] == 't')
12787 : {
12788 94 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12789 0 : appendPQExpBufferStr(q, " NOT NULL");
12790 : else
12791 : {
12792 94 : ConstraintInfo *notnull = tyinfo->notnull;
12793 :
12794 94 : if (!notnull->separate)
12795 : {
12796 : char *default_name;
12797 :
12798 : /* XXX should match ChooseConstraintName better */
12799 94 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12800 :
12801 94 : if (strcmp(default_name, notnull->dobj.name) == 0)
12802 30 : appendPQExpBufferStr(q, " NOT NULL");
12803 : else
12804 64 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12805 64 : fmtId(notnull->dobj.name), notnull->condef);
12806 94 : free(default_name);
12807 : }
12808 : }
12809 : }
12810 :
12811 304 : if (typdefault != NULL)
12812 : {
12813 74 : appendPQExpBufferStr(q, " DEFAULT ");
12814 74 : if (typdefault_is_literal)
12815 0 : appendStringLiteralAH(q, typdefault, fout);
12816 : else
12817 74 : appendPQExpBufferStr(q, typdefault);
12818 : }
12819 :
12820 304 : PQclear(res);
12821 :
12822 : /*
12823 : * Add any CHECK constraints for the domain
12824 : */
12825 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12826 : {
12827 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12828 :
12829 214 : if (!domcheck->separate && domcheck->contype == 'c')
12830 204 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12831 204 : fmtId(domcheck->dobj.name), domcheck->condef);
12832 : }
12833 :
12834 304 : appendPQExpBufferStr(q, ";\n");
12835 :
12836 304 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12837 :
12838 304 : if (dopt->binary_upgrade)
12839 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12840 : "DOMAIN", qtypname,
12841 50 : tyinfo->dobj.namespace->dobj.name);
12842 :
12843 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12844 304 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12845 304 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12846 : .namespace = tyinfo->dobj.namespace->dobj.name,
12847 : .owner = tyinfo->rolname,
12848 : .description = "DOMAIN",
12849 : .section = SECTION_PRE_DATA,
12850 : .createStmt = q->data,
12851 : .dropStmt = delq->data));
12852 :
12853 : /* Dump Domain Comments and Security Labels */
12854 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12855 0 : dumpComment(fout, "DOMAIN", qtypname,
12856 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12857 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12858 :
12859 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12860 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12861 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12862 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12863 :
12864 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12865 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12866 : qtypname, NULL,
12867 64 : tyinfo->dobj.namespace->dobj.name,
12868 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12869 :
12870 : /* Dump any per-constraint comments */
12871 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12872 : {
12873 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12874 : PQExpBuffer conprefix;
12875 :
12876 : /* but only if the constraint itself was dumped here */
12877 214 : if (domcheck->separate)
12878 10 : continue;
12879 :
12880 204 : conprefix = createPQExpBuffer();
12881 204 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12882 204 : fmtId(domcheck->dobj.name));
12883 :
12884 204 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12885 64 : dumpComment(fout, conprefix->data, qtypname,
12886 64 : tyinfo->dobj.namespace->dobj.name,
12887 64 : tyinfo->rolname,
12888 64 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12889 :
12890 204 : destroyPQExpBuffer(conprefix);
12891 : }
12892 :
12893 : /*
12894 : * And a comment on the not-null constraint, if there's one -- but only if
12895 : * the constraint itself was dumped here
12896 : */
12897 304 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12898 : {
12899 94 : PQExpBuffer conprefix = createPQExpBuffer();
12900 :
12901 94 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12902 94 : fmtId(tyinfo->notnull->dobj.name));
12903 :
12904 94 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12905 64 : dumpComment(fout, conprefix->data, qtypname,
12906 64 : tyinfo->dobj.namespace->dobj.name,
12907 64 : tyinfo->rolname,
12908 64 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12909 94 : destroyPQExpBuffer(conprefix);
12910 : }
12911 :
12912 304 : destroyPQExpBuffer(q);
12913 304 : destroyPQExpBuffer(delq);
12914 304 : destroyPQExpBuffer(query);
12915 304 : free(qtypname);
12916 304 : free(qualtypname);
12917 304 : }
12918 :
12919 : /*
12920 : * dumpCompositeType
12921 : * writes out to fout the queries to recreate a user-defined stand-alone
12922 : * composite type
12923 : */
12924 : static void
12925 260 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12926 : {
12927 260 : DumpOptions *dopt = fout->dopt;
12928 260 : PQExpBuffer q = createPQExpBuffer();
12929 260 : PQExpBuffer dropped = createPQExpBuffer();
12930 260 : PQExpBuffer delq = createPQExpBuffer();
12931 260 : PQExpBuffer query = createPQExpBuffer();
12932 : PGresult *res;
12933 : char *qtypname;
12934 : char *qualtypname;
12935 : int ntups;
12936 : int i_attname;
12937 : int i_atttypdefn;
12938 : int i_attlen;
12939 : int i_attalign;
12940 : int i_attisdropped;
12941 : int i_attcollation;
12942 : int i;
12943 : int actual_atts;
12944 :
12945 260 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12946 : {
12947 : /*
12948 : * Set up query for type-specific details.
12949 : *
12950 : * Since we only want to dump COLLATE clauses for attributes whose
12951 : * collation is different from their type's default, we use a CASE
12952 : * here to suppress uninteresting attcollations cheaply. atttypid
12953 : * will be 0 for dropped columns; collation does not matter for those.
12954 : */
12955 110 : appendPQExpBufferStr(query,
12956 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12957 : "SELECT a.attname, a.attnum, "
12958 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12959 : "a.attlen, a.attalign, a.attisdropped, "
12960 : "CASE WHEN a.attcollation <> at.typcollation "
12961 : "THEN a.attcollation ELSE 0 END AS attcollation "
12962 : "FROM pg_catalog.pg_type ct "
12963 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12964 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12965 : "WHERE ct.oid = $1 "
12966 : "ORDER BY a.attnum");
12967 :
12968 110 : ExecuteSqlStatement(fout, query->data);
12969 :
12970 110 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12971 : }
12972 :
12973 260 : printfPQExpBuffer(query,
12974 : "EXECUTE dumpCompositeType('%u')",
12975 260 : tyinfo->dobj.catId.oid);
12976 :
12977 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12978 :
12979 260 : ntups = PQntuples(res);
12980 :
12981 260 : i_attname = PQfnumber(res, "attname");
12982 260 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12983 260 : i_attlen = PQfnumber(res, "attlen");
12984 260 : i_attalign = PQfnumber(res, "attalign");
12985 260 : i_attisdropped = PQfnumber(res, "attisdropped");
12986 260 : i_attcollation = PQfnumber(res, "attcollation");
12987 :
12988 260 : if (dopt->binary_upgrade)
12989 : {
12990 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12991 36 : tyinfo->dobj.catId.oid,
12992 : false, false);
12993 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12994 : }
12995 :
12996 260 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12997 260 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12998 :
12999 260 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13000 : qualtypname);
13001 :
13002 260 : actual_atts = 0;
13003 824 : for (i = 0; i < ntups; i++)
13004 : {
13005 : char *attname;
13006 : char *atttypdefn;
13007 : char *attlen;
13008 : char *attalign;
13009 : bool attisdropped;
13010 : Oid attcollation;
13011 :
13012 564 : attname = PQgetvalue(res, i, i_attname);
13013 564 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13014 564 : attlen = PQgetvalue(res, i, i_attlen);
13015 564 : attalign = PQgetvalue(res, i, i_attalign);
13016 564 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13017 564 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13018 :
13019 564 : if (attisdropped && !dopt->binary_upgrade)
13020 16 : continue;
13021 :
13022 : /* Format properly if not first attr */
13023 548 : if (actual_atts++ > 0)
13024 288 : appendPQExpBufferChar(q, ',');
13025 548 : appendPQExpBufferStr(q, "\n\t");
13026 :
13027 548 : if (!attisdropped)
13028 : {
13029 544 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13030 :
13031 : /* Add collation if not default for the column type */
13032 544 : if (OidIsValid(attcollation))
13033 : {
13034 : CollInfo *coll;
13035 :
13036 0 : coll = findCollationByOid(attcollation);
13037 0 : if (coll)
13038 0 : appendPQExpBuffer(q, " COLLATE %s",
13039 0 : fmtQualifiedDumpable(coll));
13040 : }
13041 : }
13042 : else
13043 : {
13044 : /*
13045 : * This is a dropped attribute and we're in binary_upgrade mode.
13046 : * Insert a placeholder for it in the CREATE TYPE command, and set
13047 : * length and alignment with direct UPDATE to the catalogs
13048 : * afterwards. See similar code in dumpTableSchema().
13049 : */
13050 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13051 :
13052 : /* stash separately for insertion after the CREATE TYPE */
13053 4 : appendPQExpBufferStr(dropped,
13054 : "\n-- For binary upgrade, recreate dropped column.\n");
13055 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13056 : "SET attlen = %s, "
13057 : "attalign = '%s', attbyval = false\n"
13058 : "WHERE attname = ", attlen, attalign);
13059 4 : appendStringLiteralAH(dropped, attname, fout);
13060 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13061 4 : appendStringLiteralAH(dropped, qualtypname, fout);
13062 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13063 :
13064 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13065 : qualtypname);
13066 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13067 : fmtId(attname));
13068 : }
13069 : }
13070 260 : appendPQExpBufferStr(q, "\n);\n");
13071 260 : appendPQExpBufferStr(q, dropped->data);
13072 :
13073 260 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13074 :
13075 260 : if (dopt->binary_upgrade)
13076 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13077 : "TYPE", qtypname,
13078 36 : tyinfo->dobj.namespace->dobj.name);
13079 :
13080 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13081 226 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13082 226 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13083 : .namespace = tyinfo->dobj.namespace->dobj.name,
13084 : .owner = tyinfo->rolname,
13085 : .description = "TYPE",
13086 : .section = SECTION_PRE_DATA,
13087 : .createStmt = q->data,
13088 : .dropStmt = delq->data));
13089 :
13090 :
13091 : /* Dump Type Comments and Security Labels */
13092 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13093 64 : dumpComment(fout, "TYPE", qtypname,
13094 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13095 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13096 :
13097 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13098 0 : dumpSecLabel(fout, "TYPE", qtypname,
13099 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13100 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13101 :
13102 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13103 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13104 : qtypname, NULL,
13105 36 : tyinfo->dobj.namespace->dobj.name,
13106 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
13107 :
13108 : /* Dump any per-column comments */
13109 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13110 64 : dumpCompositeTypeColComments(fout, tyinfo, res);
13111 :
13112 260 : PQclear(res);
13113 260 : destroyPQExpBuffer(q);
13114 260 : destroyPQExpBuffer(dropped);
13115 260 : destroyPQExpBuffer(delq);
13116 260 : destroyPQExpBuffer(query);
13117 260 : free(qtypname);
13118 260 : free(qualtypname);
13119 260 : }
13120 :
13121 : /*
13122 : * dumpCompositeTypeColComments
13123 : * writes out to fout the queries to recreate comments on the columns of
13124 : * a user-defined stand-alone composite type.
13125 : *
13126 : * The caller has already made a query to collect the names and attnums
13127 : * of the type's columns, so we just pass that result into here rather
13128 : * than reading them again.
13129 : */
13130 : static void
13131 64 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13132 : PGresult *res)
13133 : {
13134 : CommentItem *comments;
13135 : int ncomments;
13136 : PQExpBuffer query;
13137 : PQExpBuffer target;
13138 : int i;
13139 : int ntups;
13140 : int i_attname;
13141 : int i_attnum;
13142 : int i_attisdropped;
13143 :
13144 : /* do nothing, if --no-comments is supplied */
13145 64 : if (fout->dopt->no_comments)
13146 0 : return;
13147 :
13148 : /* Search for comments associated with type's pg_class OID */
13149 64 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13150 : &comments);
13151 :
13152 : /* If no comments exist, we're done */
13153 64 : if (ncomments <= 0)
13154 0 : return;
13155 :
13156 : /* Build COMMENT ON statements */
13157 64 : query = createPQExpBuffer();
13158 64 : target = createPQExpBuffer();
13159 :
13160 64 : ntups = PQntuples(res);
13161 64 : i_attnum = PQfnumber(res, "attnum");
13162 64 : i_attname = PQfnumber(res, "attname");
13163 64 : i_attisdropped = PQfnumber(res, "attisdropped");
13164 128 : while (ncomments > 0)
13165 : {
13166 : const char *attname;
13167 :
13168 64 : attname = NULL;
13169 64 : for (i = 0; i < ntups; i++)
13170 : {
13171 64 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13172 64 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13173 : {
13174 64 : attname = PQgetvalue(res, i, i_attname);
13175 64 : break;
13176 : }
13177 : }
13178 64 : if (attname) /* just in case we don't find it */
13179 : {
13180 64 : const char *descr = comments->descr;
13181 :
13182 64 : resetPQExpBuffer(target);
13183 64 : appendPQExpBuffer(target, "COLUMN %s.",
13184 64 : fmtId(tyinfo->dobj.name));
13185 64 : appendPQExpBufferStr(target, fmtId(attname));
13186 :
13187 64 : resetPQExpBuffer(query);
13188 64 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13189 64 : fmtQualifiedDumpable(tyinfo));
13190 64 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13191 64 : appendStringLiteralAH(query, descr, fout);
13192 64 : appendPQExpBufferStr(query, ";\n");
13193 :
13194 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13195 64 : ARCHIVE_OPTS(.tag = target->data,
13196 : .namespace = tyinfo->dobj.namespace->dobj.name,
13197 : .owner = tyinfo->rolname,
13198 : .description = "COMMENT",
13199 : .section = SECTION_NONE,
13200 : .createStmt = query->data,
13201 : .deps = &(tyinfo->dobj.dumpId),
13202 : .nDeps = 1));
13203 : }
13204 :
13205 64 : comments++;
13206 64 : ncomments--;
13207 : }
13208 :
13209 64 : destroyPQExpBuffer(query);
13210 64 : destroyPQExpBuffer(target);
13211 : }
13212 :
13213 : /*
13214 : * dumpShellType
13215 : * writes out to fout the queries to create a shell type
13216 : *
13217 : * We dump a shell definition in advance of the I/O functions for the type.
13218 : */
13219 : static void
13220 146 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13221 : {
13222 146 : DumpOptions *dopt = fout->dopt;
13223 : PQExpBuffer q;
13224 :
13225 : /* Do nothing if not dumping schema */
13226 146 : if (!dopt->dumpSchema)
13227 12 : return;
13228 :
13229 134 : q = createPQExpBuffer();
13230 :
13231 : /*
13232 : * Note the lack of a DROP command for the shell type; any required DROP
13233 : * is driven off the base type entry, instead. This interacts with
13234 : * _printTocEntry()'s use of the presence of a DROP command to decide
13235 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13236 : * the shell type's owner immediately on creation; that should happen only
13237 : * after it's filled in, otherwise the backend complains.
13238 : */
13239 :
13240 134 : if (dopt->binary_upgrade)
13241 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13242 16 : stinfo->baseType->dobj.catId.oid,
13243 : false, false);
13244 :
13245 134 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13246 134 : fmtQualifiedDumpable(stinfo));
13247 :
13248 134 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13249 134 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13250 134 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13251 : .namespace = stinfo->dobj.namespace->dobj.name,
13252 : .owner = stinfo->baseType->rolname,
13253 : .description = "SHELL TYPE",
13254 : .section = SECTION_PRE_DATA,
13255 : .createStmt = q->data));
13256 :
13257 134 : destroyPQExpBuffer(q);
13258 : }
13259 :
13260 : /*
13261 : * dumpProcLang
13262 : * writes out to fout the queries to recreate a user-defined
13263 : * procedural language
13264 : */
13265 : static void
13266 164 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13267 : {
13268 164 : DumpOptions *dopt = fout->dopt;
13269 : PQExpBuffer defqry;
13270 : PQExpBuffer delqry;
13271 : bool useParams;
13272 : char *qlanname;
13273 : FuncInfo *funcInfo;
13274 164 : FuncInfo *inlineInfo = NULL;
13275 164 : FuncInfo *validatorInfo = NULL;
13276 :
13277 : /* Do nothing if not dumping schema */
13278 164 : if (!dopt->dumpSchema)
13279 26 : return;
13280 :
13281 : /*
13282 : * Try to find the support function(s). It is not an error if we don't
13283 : * find them --- if the functions are in the pg_catalog schema, as is
13284 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13285 : * we will emit a parameterless CREATE LANGUAGE command, which will
13286 : * require PL template knowledge in the backend to reload.)
13287 : */
13288 :
13289 138 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13290 138 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13291 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13292 :
13293 138 : if (OidIsValid(plang->laninline))
13294 : {
13295 76 : inlineInfo = findFuncByOid(plang->laninline);
13296 76 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13297 2 : inlineInfo = NULL;
13298 : }
13299 :
13300 138 : if (OidIsValid(plang->lanvalidator))
13301 : {
13302 76 : validatorInfo = findFuncByOid(plang->lanvalidator);
13303 76 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13304 2 : validatorInfo = NULL;
13305 : }
13306 :
13307 : /*
13308 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13309 : * parameters. Otherwise, we'll write a parameterless command, which will
13310 : * be interpreted as CREATE EXTENSION.
13311 : */
13312 60 : useParams = (funcInfo != NULL &&
13313 258 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13314 60 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13315 :
13316 138 : defqry = createPQExpBuffer();
13317 138 : delqry = createPQExpBuffer();
13318 :
13319 138 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13320 :
13321 138 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13322 : qlanname);
13323 :
13324 138 : if (useParams)
13325 : {
13326 60 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13327 60 : plang->lanpltrusted ? "TRUSTED " : "",
13328 : qlanname);
13329 60 : appendPQExpBuffer(defqry, " HANDLER %s",
13330 60 : fmtQualifiedDumpable(funcInfo));
13331 60 : if (OidIsValid(plang->laninline))
13332 0 : appendPQExpBuffer(defqry, " INLINE %s",
13333 0 : fmtQualifiedDumpable(inlineInfo));
13334 60 : if (OidIsValid(plang->lanvalidator))
13335 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13336 0 : fmtQualifiedDumpable(validatorInfo));
13337 : }
13338 : else
13339 : {
13340 : /*
13341 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13342 : * command will not fail if the language is preinstalled in the target
13343 : * database.
13344 : *
13345 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13346 : * EXISTS; perhaps we should emit that instead? But it might just add
13347 : * confusion.
13348 : */
13349 78 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13350 : qlanname);
13351 : }
13352 138 : appendPQExpBufferStr(defqry, ";\n");
13353 :
13354 138 : if (dopt->binary_upgrade)
13355 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13356 : "LANGUAGE", qlanname, NULL);
13357 :
13358 138 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13359 62 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13360 62 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13361 : .owner = plang->lanowner,
13362 : .description = "PROCEDURAL LANGUAGE",
13363 : .section = SECTION_PRE_DATA,
13364 : .createStmt = defqry->data,
13365 : .dropStmt = delqry->data,
13366 : ));
13367 :
13368 : /* Dump Proc Lang Comments and Security Labels */
13369 138 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13370 0 : dumpComment(fout, "LANGUAGE", qlanname,
13371 0 : NULL, plang->lanowner,
13372 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13373 :
13374 138 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13375 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13376 0 : NULL, plang->lanowner,
13377 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13378 :
13379 138 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13380 76 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13381 : qlanname, NULL, NULL,
13382 76 : NULL, plang->lanowner, &plang->dacl);
13383 :
13384 138 : free(qlanname);
13385 :
13386 138 : destroyPQExpBuffer(defqry);
13387 138 : destroyPQExpBuffer(delqry);
13388 : }
13389 :
13390 : /*
13391 : * format_function_arguments: generate function name and argument list
13392 : *
13393 : * This is used when we can rely on pg_get_function_arguments to format
13394 : * the argument list. Note, however, that pg_get_function_arguments
13395 : * does not special-case zero-argument aggregates.
13396 : */
13397 : static char *
13398 8204 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13399 : {
13400 : PQExpBufferData fn;
13401 :
13402 8204 : initPQExpBuffer(&fn);
13403 8204 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13404 8204 : if (is_agg && finfo->nargs == 0)
13405 160 : appendPQExpBufferStr(&fn, "(*)");
13406 : else
13407 8044 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13408 8204 : return fn.data;
13409 : }
13410 :
13411 : /*
13412 : * format_function_signature: generate function name and argument list
13413 : *
13414 : * Only a minimal list of input argument types is generated; this is
13415 : * sufficient to reference the function, but not to define it.
13416 : *
13417 : * If honor_quotes is false then the function name is never quoted.
13418 : * This is appropriate for use in TOC tags, but not in SQL commands.
13419 : */
13420 : static char *
13421 4318 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13422 : {
13423 : PQExpBufferData fn;
13424 : int j;
13425 :
13426 4318 : initPQExpBuffer(&fn);
13427 4318 : if (honor_quotes)
13428 786 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13429 : else
13430 3532 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13431 7920 : for (j = 0; j < finfo->nargs; j++)
13432 : {
13433 3602 : if (j > 0)
13434 844 : appendPQExpBufferStr(&fn, ", ");
13435 :
13436 3602 : appendPQExpBufferStr(&fn,
13437 3602 : getFormattedTypeName(fout, finfo->argtypes[j],
13438 : zeroIsError));
13439 : }
13440 4318 : appendPQExpBufferChar(&fn, ')');
13441 4318 : return fn.data;
13442 : }
13443 :
13444 :
13445 : /*
13446 : * dumpFunc:
13447 : * dump out one function
13448 : */
13449 : static void
13450 3656 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13451 : {
13452 3656 : DumpOptions *dopt = fout->dopt;
13453 : PQExpBuffer query;
13454 : PQExpBuffer q;
13455 : PQExpBuffer delqry;
13456 : PQExpBuffer asPart;
13457 : PGresult *res;
13458 : char *funcsig; /* identity signature */
13459 3656 : char *funcfullsig = NULL; /* full signature */
13460 : char *funcsig_tag;
13461 : char *qual_funcsig;
13462 : char *proretset;
13463 : char *prosrc;
13464 : char *probin;
13465 : char *prosqlbody;
13466 : char *funcargs;
13467 : char *funciargs;
13468 : char *funcresult;
13469 : char *protrftypes;
13470 : char *prokind;
13471 : char *provolatile;
13472 : char *proisstrict;
13473 : char *prosecdef;
13474 : char *proleakproof;
13475 : char *proconfig;
13476 : char *procost;
13477 : char *prorows;
13478 : char *prosupport;
13479 : char *proparallel;
13480 : char *lanname;
13481 3656 : char **configitems = NULL;
13482 3656 : int nconfigitems = 0;
13483 : const char *keyword;
13484 :
13485 : /* Do nothing if not dumping schema */
13486 3656 : if (!dopt->dumpSchema)
13487 124 : return;
13488 :
13489 3532 : query = createPQExpBuffer();
13490 3532 : q = createPQExpBuffer();
13491 3532 : delqry = createPQExpBuffer();
13492 3532 : asPart = createPQExpBuffer();
13493 :
13494 3532 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13495 : {
13496 : /* Set up query for function-specific details */
13497 132 : appendPQExpBufferStr(query,
13498 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13499 :
13500 132 : appendPQExpBufferStr(query,
13501 : "SELECT\n"
13502 : "proretset,\n"
13503 : "prosrc,\n"
13504 : "probin,\n"
13505 : "provolatile,\n"
13506 : "proisstrict,\n"
13507 : "prosecdef,\n"
13508 : "lanname,\n"
13509 : "proconfig,\n"
13510 : "procost,\n"
13511 : "prorows,\n"
13512 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13513 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13514 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13515 : "proleakproof,\n");
13516 :
13517 132 : if (fout->remoteVersion >= 90500)
13518 132 : appendPQExpBufferStr(query,
13519 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13520 : else
13521 0 : appendPQExpBufferStr(query,
13522 : "NULL AS protrftypes,\n");
13523 :
13524 132 : if (fout->remoteVersion >= 90600)
13525 132 : appendPQExpBufferStr(query,
13526 : "proparallel,\n");
13527 : else
13528 0 : appendPQExpBufferStr(query,
13529 : "'u' AS proparallel,\n");
13530 :
13531 132 : if (fout->remoteVersion >= 110000)
13532 132 : appendPQExpBufferStr(query,
13533 : "prokind,\n");
13534 : else
13535 0 : appendPQExpBufferStr(query,
13536 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13537 :
13538 132 : if (fout->remoteVersion >= 120000)
13539 132 : appendPQExpBufferStr(query,
13540 : "prosupport,\n");
13541 : else
13542 0 : appendPQExpBufferStr(query,
13543 : "'-' AS prosupport,\n");
13544 :
13545 132 : if (fout->remoteVersion >= 140000)
13546 132 : appendPQExpBufferStr(query,
13547 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13548 : else
13549 0 : appendPQExpBufferStr(query,
13550 : "NULL AS prosqlbody\n");
13551 :
13552 132 : appendPQExpBufferStr(query,
13553 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13554 : "WHERE p.oid = $1 "
13555 : "AND l.oid = p.prolang");
13556 :
13557 132 : ExecuteSqlStatement(fout, query->data);
13558 :
13559 132 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13560 : }
13561 :
13562 3532 : printfPQExpBuffer(query,
13563 : "EXECUTE dumpFunc('%u')",
13564 3532 : finfo->dobj.catId.oid);
13565 :
13566 3532 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13567 :
13568 3532 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13569 3532 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13570 : {
13571 3436 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13572 3436 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13573 3436 : prosqlbody = NULL;
13574 : }
13575 : else
13576 : {
13577 96 : prosrc = NULL;
13578 96 : probin = NULL;
13579 96 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13580 : }
13581 3532 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13582 3532 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13583 3532 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13584 3532 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13585 3532 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13586 3532 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13587 3532 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13588 3532 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13589 3532 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13590 3532 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13591 3532 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13592 3532 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13593 3532 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13594 3532 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13595 3532 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13596 :
13597 : /*
13598 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13599 : * is used.
13600 : */
13601 3532 : if (prosqlbody)
13602 : {
13603 96 : appendPQExpBufferStr(asPart, prosqlbody);
13604 : }
13605 3436 : else if (probin[0] != '\0')
13606 : {
13607 294 : appendPQExpBufferStr(asPart, "AS ");
13608 294 : appendStringLiteralAH(asPart, probin, fout);
13609 294 : if (prosrc[0] != '\0')
13610 : {
13611 294 : appendPQExpBufferStr(asPart, ", ");
13612 :
13613 : /*
13614 : * where we have bin, use dollar quoting if allowed and src
13615 : * contains quote or backslash; else use regular quoting.
13616 : */
13617 294 : if (dopt->disable_dollar_quoting ||
13618 294 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13619 294 : appendStringLiteralAH(asPart, prosrc, fout);
13620 : else
13621 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13622 : }
13623 : }
13624 : else
13625 : {
13626 3142 : appendPQExpBufferStr(asPart, "AS ");
13627 : /* with no bin, dollar quote src unconditionally if allowed */
13628 3142 : if (dopt->disable_dollar_quoting)
13629 0 : appendStringLiteralAH(asPart, prosrc, fout);
13630 : else
13631 3142 : appendStringLiteralDQ(asPart, prosrc, NULL);
13632 : }
13633 :
13634 3532 : if (*proconfig)
13635 : {
13636 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13637 0 : pg_fatal("could not parse %s array", "proconfig");
13638 : }
13639 : else
13640 : {
13641 3502 : configitems = NULL;
13642 3502 : nconfigitems = 0;
13643 : }
13644 :
13645 3532 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13646 3532 : funcsig = format_function_arguments(finfo, funciargs, false);
13647 :
13648 3532 : funcsig_tag = format_function_signature(fout, finfo, false);
13649 :
13650 3532 : qual_funcsig = psprintf("%s.%s",
13651 3532 : fmtId(finfo->dobj.namespace->dobj.name),
13652 : funcsig);
13653 :
13654 3532 : if (prokind[0] == PROKIND_PROCEDURE)
13655 184 : keyword = "PROCEDURE";
13656 : else
13657 3348 : keyword = "FUNCTION"; /* works for window functions too */
13658 :
13659 3532 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13660 : keyword, qual_funcsig);
13661 :
13662 7064 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13663 : keyword,
13664 3532 : fmtId(finfo->dobj.namespace->dobj.name),
13665 : funcfullsig ? funcfullsig :
13666 : funcsig);
13667 :
13668 3532 : if (prokind[0] == PROKIND_PROCEDURE)
13669 : /* no result type to output */ ;
13670 3348 : else if (funcresult)
13671 3348 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13672 : else
13673 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13674 0 : (proretset[0] == 't') ? "SETOF " : "",
13675 0 : getFormattedTypeName(fout, finfo->prorettype,
13676 : zeroIsError));
13677 :
13678 3532 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13679 :
13680 3532 : if (*protrftypes)
13681 : {
13682 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13683 : int i;
13684 :
13685 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13686 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13687 0 : for (i = 0; typeids[i]; i++)
13688 : {
13689 0 : if (i != 0)
13690 0 : appendPQExpBufferStr(q, ", ");
13691 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13692 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13693 : }
13694 :
13695 0 : free(typeids);
13696 : }
13697 :
13698 3532 : if (prokind[0] == PROKIND_WINDOW)
13699 10 : appendPQExpBufferStr(q, " WINDOW");
13700 :
13701 3532 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13702 : {
13703 702 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13704 660 : appendPQExpBufferStr(q, " IMMUTABLE");
13705 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13706 42 : appendPQExpBufferStr(q, " STABLE");
13707 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13708 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13709 : finfo->dobj.name);
13710 : }
13711 :
13712 3532 : if (proisstrict[0] == 't')
13713 716 : appendPQExpBufferStr(q, " STRICT");
13714 :
13715 3532 : if (prosecdef[0] == 't')
13716 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13717 :
13718 3532 : if (proleakproof[0] == 't')
13719 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13720 :
13721 : /*
13722 : * COST and ROWS are emitted only if present and not default, so as not to
13723 : * break backwards-compatibility of the dump without need. Keep this code
13724 : * in sync with the defaults in functioncmds.c.
13725 : */
13726 3532 : if (strcmp(procost, "0") != 0)
13727 : {
13728 3532 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13729 : {
13730 : /* default cost is 1 */
13731 752 : if (strcmp(procost, "1") != 0)
13732 0 : appendPQExpBuffer(q, " COST %s", procost);
13733 : }
13734 : else
13735 : {
13736 : /* default cost is 100 */
13737 2780 : if (strcmp(procost, "100") != 0)
13738 12 : appendPQExpBuffer(q, " COST %s", procost);
13739 : }
13740 : }
13741 3532 : if (proretset[0] == 't' &&
13742 374 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13743 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13744 :
13745 3532 : if (strcmp(prosupport, "-") != 0)
13746 : {
13747 : /* We rely on regprocout to provide quoting and qualification */
13748 84 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13749 : }
13750 :
13751 3532 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13752 : {
13753 232 : if (proparallel[0] == PROPARALLEL_SAFE)
13754 222 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13755 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13756 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13757 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13758 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13759 : finfo->dobj.name);
13760 : }
13761 :
13762 3612 : for (int i = 0; i < nconfigitems; i++)
13763 : {
13764 : /* we feel free to scribble on configitems[] here */
13765 80 : char *configitem = configitems[i];
13766 : char *pos;
13767 :
13768 80 : pos = strchr(configitem, '=');
13769 80 : if (pos == NULL)
13770 0 : continue;
13771 80 : *pos++ = '\0';
13772 80 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13773 :
13774 : /*
13775 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13776 : * by flatten_set_variable_args() before they were put into the
13777 : * proconfig array. However, because the quoting rules used there
13778 : * aren't exactly like SQL's, we have to break the list value apart
13779 : * and then quote the elements as string literals. (The elements may
13780 : * be double-quoted as-is, but we can't just feed them to the SQL
13781 : * parser; it would do the wrong thing with elements that are
13782 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13783 : * case for empty lists.
13784 : *
13785 : * Variables that are not so marked should just be emitted as simple
13786 : * string literals. If the variable is not known to
13787 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13788 : * to use GUC_LIST_QUOTE for extension variables.
13789 : */
13790 80 : if (variable_is_guc_list_quote(configitem))
13791 : {
13792 : char **namelist;
13793 : char **nameptr;
13794 :
13795 : /* Parse string into list of identifiers */
13796 : /* this shouldn't fail really */
13797 30 : if (SplitGUCList(pos, ',', &namelist))
13798 : {
13799 : /* Special case: represent an empty list as NULL */
13800 30 : if (*namelist == NULL)
13801 10 : appendPQExpBufferStr(q, "NULL");
13802 80 : for (nameptr = namelist; *nameptr; nameptr++)
13803 : {
13804 50 : if (nameptr != namelist)
13805 30 : appendPQExpBufferStr(q, ", ");
13806 50 : appendStringLiteralAH(q, *nameptr, fout);
13807 : }
13808 : }
13809 30 : pg_free(namelist);
13810 : }
13811 : else
13812 50 : appendStringLiteralAH(q, pos, fout);
13813 : }
13814 :
13815 3532 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13816 :
13817 3532 : append_depends_on_extension(fout, q, &finfo->dobj,
13818 : "pg_catalog.pg_proc", keyword,
13819 : qual_funcsig);
13820 :
13821 3532 : if (dopt->binary_upgrade)
13822 590 : binary_upgrade_extension_member(q, &finfo->dobj,
13823 : keyword, funcsig,
13824 590 : finfo->dobj.namespace->dobj.name);
13825 :
13826 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13827 3340 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13828 3340 : ARCHIVE_OPTS(.tag = funcsig_tag,
13829 : .namespace = finfo->dobj.namespace->dobj.name,
13830 : .owner = finfo->rolname,
13831 : .description = keyword,
13832 : .section = finfo->postponed_def ?
13833 : SECTION_POST_DATA : SECTION_PRE_DATA,
13834 : .createStmt = q->data,
13835 : .dropStmt = delqry->data));
13836 :
13837 : /* Dump Function Comments and Security Labels */
13838 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13839 18 : dumpComment(fout, keyword, funcsig,
13840 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13841 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13842 :
13843 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13844 0 : dumpSecLabel(fout, keyword, funcsig,
13845 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13846 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13847 :
13848 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13849 200 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13850 : funcsig, NULL,
13851 200 : finfo->dobj.namespace->dobj.name,
13852 200 : NULL, finfo->rolname, &finfo->dacl);
13853 :
13854 3532 : PQclear(res);
13855 :
13856 3532 : destroyPQExpBuffer(query);
13857 3532 : destroyPQExpBuffer(q);
13858 3532 : destroyPQExpBuffer(delqry);
13859 3532 : destroyPQExpBuffer(asPart);
13860 3532 : free(funcsig);
13861 3532 : free(funcfullsig);
13862 3532 : free(funcsig_tag);
13863 3532 : free(qual_funcsig);
13864 3532 : free(configitems);
13865 : }
13866 :
13867 :
13868 : /*
13869 : * Dump a user-defined cast
13870 : */
13871 : static void
13872 134 : dumpCast(Archive *fout, const CastInfo *cast)
13873 : {
13874 134 : DumpOptions *dopt = fout->dopt;
13875 : PQExpBuffer defqry;
13876 : PQExpBuffer delqry;
13877 : PQExpBuffer labelq;
13878 : PQExpBuffer castargs;
13879 134 : FuncInfo *funcInfo = NULL;
13880 : const char *sourceType;
13881 : const char *targetType;
13882 :
13883 : /* Do nothing if not dumping schema */
13884 134 : if (!dopt->dumpSchema)
13885 12 : return;
13886 :
13887 : /* Cannot dump if we don't have the cast function's info */
13888 122 : if (OidIsValid(cast->castfunc))
13889 : {
13890 72 : funcInfo = findFuncByOid(cast->castfunc);
13891 72 : if (funcInfo == NULL)
13892 0 : pg_fatal("could not find function definition for function with OID %u",
13893 : cast->castfunc);
13894 : }
13895 :
13896 122 : defqry = createPQExpBuffer();
13897 122 : delqry = createPQExpBuffer();
13898 122 : labelq = createPQExpBuffer();
13899 122 : castargs = createPQExpBuffer();
13900 :
13901 122 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13902 122 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13903 122 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13904 : sourceType, targetType);
13905 :
13906 122 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13907 : sourceType, targetType);
13908 :
13909 122 : switch (cast->castmethod)
13910 : {
13911 50 : case COERCION_METHOD_BINARY:
13912 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13913 50 : break;
13914 0 : case COERCION_METHOD_INOUT:
13915 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13916 0 : break;
13917 72 : case COERCION_METHOD_FUNCTION:
13918 72 : if (funcInfo)
13919 : {
13920 72 : char *fsig = format_function_signature(fout, funcInfo, true);
13921 :
13922 : /*
13923 : * Always qualify the function name (format_function_signature
13924 : * won't qualify it).
13925 : */
13926 72 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13927 72 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13928 72 : free(fsig);
13929 : }
13930 : else
13931 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13932 72 : break;
13933 0 : default:
13934 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13935 : }
13936 :
13937 122 : if (cast->castcontext == 'a')
13938 62 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13939 60 : else if (cast->castcontext == 'i')
13940 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13941 122 : appendPQExpBufferStr(defqry, ";\n");
13942 :
13943 122 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13944 : sourceType, targetType);
13945 :
13946 122 : appendPQExpBuffer(castargs, "(%s AS %s)",
13947 : sourceType, targetType);
13948 :
13949 122 : if (dopt->binary_upgrade)
13950 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13951 14 : "CAST", castargs->data, NULL);
13952 :
13953 122 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13954 122 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13955 122 : ARCHIVE_OPTS(.tag = labelq->data,
13956 : .description = "CAST",
13957 : .section = SECTION_PRE_DATA,
13958 : .createStmt = defqry->data,
13959 : .dropStmt = delqry->data));
13960 :
13961 : /* Dump Cast Comments */
13962 122 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13963 0 : dumpComment(fout, "CAST", castargs->data,
13964 : NULL, "",
13965 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13966 :
13967 122 : destroyPQExpBuffer(defqry);
13968 122 : destroyPQExpBuffer(delqry);
13969 122 : destroyPQExpBuffer(labelq);
13970 122 : destroyPQExpBuffer(castargs);
13971 : }
13972 :
13973 : /*
13974 : * Dump a transform
13975 : */
13976 : static void
13977 84 : dumpTransform(Archive *fout, const TransformInfo *transform)
13978 : {
13979 84 : DumpOptions *dopt = fout->dopt;
13980 : PQExpBuffer defqry;
13981 : PQExpBuffer delqry;
13982 : PQExpBuffer labelq;
13983 : PQExpBuffer transformargs;
13984 84 : FuncInfo *fromsqlFuncInfo = NULL;
13985 84 : FuncInfo *tosqlFuncInfo = NULL;
13986 : char *lanname;
13987 : const char *transformType;
13988 :
13989 : /* Do nothing if not dumping schema */
13990 84 : if (!dopt->dumpSchema)
13991 12 : return;
13992 :
13993 : /* Cannot dump if we don't have the transform functions' info */
13994 72 : if (OidIsValid(transform->trffromsql))
13995 : {
13996 72 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13997 72 : if (fromsqlFuncInfo == NULL)
13998 0 : pg_fatal("could not find function definition for function with OID %u",
13999 : transform->trffromsql);
14000 : }
14001 72 : if (OidIsValid(transform->trftosql))
14002 : {
14003 72 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
14004 72 : if (tosqlFuncInfo == NULL)
14005 0 : pg_fatal("could not find function definition for function with OID %u",
14006 : transform->trftosql);
14007 : }
14008 :
14009 72 : defqry = createPQExpBuffer();
14010 72 : delqry = createPQExpBuffer();
14011 72 : labelq = createPQExpBuffer();
14012 72 : transformargs = createPQExpBuffer();
14013 :
14014 72 : lanname = get_language_name(fout, transform->trflang);
14015 72 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14016 :
14017 72 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14018 : transformType, lanname);
14019 :
14020 72 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14021 : transformType, lanname);
14022 :
14023 72 : if (!transform->trffromsql && !transform->trftosql)
14024 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14025 :
14026 72 : if (transform->trffromsql)
14027 : {
14028 72 : if (fromsqlFuncInfo)
14029 : {
14030 72 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14031 :
14032 : /*
14033 : * Always qualify the function name (format_function_signature
14034 : * won't qualify it).
14035 : */
14036 72 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14037 72 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14038 72 : free(fsig);
14039 : }
14040 : else
14041 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14042 : }
14043 :
14044 72 : if (transform->trftosql)
14045 : {
14046 72 : if (transform->trffromsql)
14047 72 : appendPQExpBufferStr(defqry, ", ");
14048 :
14049 72 : if (tosqlFuncInfo)
14050 : {
14051 72 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14052 :
14053 : /*
14054 : * Always qualify the function name (format_function_signature
14055 : * won't qualify it).
14056 : */
14057 72 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14058 72 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14059 72 : free(fsig);
14060 : }
14061 : else
14062 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14063 : }
14064 :
14065 72 : appendPQExpBufferStr(defqry, ");\n");
14066 :
14067 72 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14068 : transformType, lanname);
14069 :
14070 72 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14071 : transformType, lanname);
14072 :
14073 72 : if (dopt->binary_upgrade)
14074 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
14075 4 : "TRANSFORM", transformargs->data, NULL);
14076 :
14077 72 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14078 72 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14079 72 : ARCHIVE_OPTS(.tag = labelq->data,
14080 : .description = "TRANSFORM",
14081 : .section = SECTION_PRE_DATA,
14082 : .createStmt = defqry->data,
14083 : .dropStmt = delqry->data,
14084 : .deps = transform->dobj.dependencies,
14085 : .nDeps = transform->dobj.nDeps));
14086 :
14087 : /* Dump Transform Comments */
14088 72 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14089 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14090 : NULL, "",
14091 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14092 :
14093 72 : free(lanname);
14094 72 : destroyPQExpBuffer(defqry);
14095 72 : destroyPQExpBuffer(delqry);
14096 72 : destroyPQExpBuffer(labelq);
14097 72 : destroyPQExpBuffer(transformargs);
14098 : }
14099 :
14100 :
14101 : /*
14102 : * dumpOpr
14103 : * write out a single operator definition
14104 : */
14105 : static void
14106 5044 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14107 : {
14108 5044 : DumpOptions *dopt = fout->dopt;
14109 : PQExpBuffer query;
14110 : PQExpBuffer q;
14111 : PQExpBuffer delq;
14112 : PQExpBuffer oprid;
14113 : PQExpBuffer details;
14114 : PGresult *res;
14115 : int i_oprkind;
14116 : int i_oprcode;
14117 : int i_oprleft;
14118 : int i_oprright;
14119 : int i_oprcom;
14120 : int i_oprnegate;
14121 : int i_oprrest;
14122 : int i_oprjoin;
14123 : int i_oprcanmerge;
14124 : int i_oprcanhash;
14125 : char *oprkind;
14126 : char *oprcode;
14127 : char *oprleft;
14128 : char *oprright;
14129 : char *oprcom;
14130 : char *oprnegate;
14131 : char *oprrest;
14132 : char *oprjoin;
14133 : char *oprcanmerge;
14134 : char *oprcanhash;
14135 : char *oprregproc;
14136 : char *oprref;
14137 :
14138 : /* Do nothing if not dumping schema */
14139 5044 : if (!dopt->dumpSchema)
14140 12 : return;
14141 :
14142 : /*
14143 : * some operators are invalid because they were the result of user
14144 : * defining operators before commutators exist
14145 : */
14146 5032 : if (!OidIsValid(oprinfo->oprcode))
14147 28 : return;
14148 :
14149 5004 : query = createPQExpBuffer();
14150 5004 : q = createPQExpBuffer();
14151 5004 : delq = createPQExpBuffer();
14152 5004 : oprid = createPQExpBuffer();
14153 5004 : details = createPQExpBuffer();
14154 :
14155 5004 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14156 : {
14157 : /* Set up query for operator-specific details */
14158 80 : appendPQExpBufferStr(query,
14159 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14160 : "SELECT oprkind, "
14161 : "oprcode::pg_catalog.regprocedure, "
14162 : "oprleft::pg_catalog.regtype, "
14163 : "oprright::pg_catalog.regtype, "
14164 : "oprcom, "
14165 : "oprnegate, "
14166 : "oprrest::pg_catalog.regprocedure, "
14167 : "oprjoin::pg_catalog.regprocedure, "
14168 : "oprcanmerge, oprcanhash "
14169 : "FROM pg_catalog.pg_operator "
14170 : "WHERE oid = $1");
14171 :
14172 80 : ExecuteSqlStatement(fout, query->data);
14173 :
14174 80 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14175 : }
14176 :
14177 5004 : printfPQExpBuffer(query,
14178 : "EXECUTE dumpOpr('%u')",
14179 5004 : oprinfo->dobj.catId.oid);
14180 :
14181 5004 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14182 :
14183 5004 : i_oprkind = PQfnumber(res, "oprkind");
14184 5004 : i_oprcode = PQfnumber(res, "oprcode");
14185 5004 : i_oprleft = PQfnumber(res, "oprleft");
14186 5004 : i_oprright = PQfnumber(res, "oprright");
14187 5004 : i_oprcom = PQfnumber(res, "oprcom");
14188 5004 : i_oprnegate = PQfnumber(res, "oprnegate");
14189 5004 : i_oprrest = PQfnumber(res, "oprrest");
14190 5004 : i_oprjoin = PQfnumber(res, "oprjoin");
14191 5004 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14192 5004 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14193 :
14194 5004 : oprkind = PQgetvalue(res, 0, i_oprkind);
14195 5004 : oprcode = PQgetvalue(res, 0, i_oprcode);
14196 5004 : oprleft = PQgetvalue(res, 0, i_oprleft);
14197 5004 : oprright = PQgetvalue(res, 0, i_oprright);
14198 5004 : oprcom = PQgetvalue(res, 0, i_oprcom);
14199 5004 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14200 5004 : oprrest = PQgetvalue(res, 0, i_oprrest);
14201 5004 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14202 5004 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14203 5004 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14204 :
14205 : /* In PG14 upwards postfix operator support does not exist anymore. */
14206 5004 : if (strcmp(oprkind, "r") == 0)
14207 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14208 : oprcode);
14209 :
14210 5004 : oprregproc = convertRegProcReference(oprcode);
14211 5004 : if (oprregproc)
14212 : {
14213 5004 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14214 5004 : free(oprregproc);
14215 : }
14216 :
14217 5004 : appendPQExpBuffer(oprid, "%s (",
14218 5004 : oprinfo->dobj.name);
14219 :
14220 : /*
14221 : * right unary means there's a left arg and left unary means there's a
14222 : * right arg. (Although the "r" case is dead code for PG14 and later,
14223 : * continue to support it in case we're dumping from an old server.)
14224 : */
14225 5004 : if (strcmp(oprkind, "r") == 0 ||
14226 5004 : strcmp(oprkind, "b") == 0)
14227 : {
14228 4718 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14229 4718 : appendPQExpBufferStr(oprid, oprleft);
14230 : }
14231 : else
14232 286 : appendPQExpBufferStr(oprid, "NONE");
14233 :
14234 5004 : if (strcmp(oprkind, "l") == 0 ||
14235 4718 : strcmp(oprkind, "b") == 0)
14236 : {
14237 5004 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14238 5004 : appendPQExpBuffer(oprid, ", %s)", oprright);
14239 : }
14240 : else
14241 0 : appendPQExpBufferStr(oprid, ", NONE)");
14242 :
14243 5004 : oprref = getFormattedOperatorName(oprcom);
14244 5004 : if (oprref)
14245 : {
14246 3358 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14247 3358 : free(oprref);
14248 : }
14249 :
14250 5004 : oprref = getFormattedOperatorName(oprnegate);
14251 5004 : if (oprref)
14252 : {
14253 2362 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14254 2362 : free(oprref);
14255 : }
14256 :
14257 5004 : if (strcmp(oprcanmerge, "t") == 0)
14258 376 : appendPQExpBufferStr(details, ",\n MERGES");
14259 :
14260 5004 : if (strcmp(oprcanhash, "t") == 0)
14261 282 : appendPQExpBufferStr(details, ",\n HASHES");
14262 :
14263 5004 : oprregproc = convertRegProcReference(oprrest);
14264 5004 : if (oprregproc)
14265 : {
14266 3064 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14267 3064 : free(oprregproc);
14268 : }
14269 :
14270 5004 : oprregproc = convertRegProcReference(oprjoin);
14271 5004 : if (oprregproc)
14272 : {
14273 3064 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14274 3064 : free(oprregproc);
14275 : }
14276 :
14277 5004 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14278 5004 : fmtId(oprinfo->dobj.namespace->dobj.name),
14279 : oprid->data);
14280 :
14281 5004 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14282 5004 : fmtId(oprinfo->dobj.namespace->dobj.name),
14283 5004 : oprinfo->dobj.name, details->data);
14284 :
14285 5004 : if (dopt->binary_upgrade)
14286 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14287 24 : "OPERATOR", oprid->data,
14288 24 : oprinfo->dobj.namespace->dobj.name);
14289 :
14290 5004 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14291 5004 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14292 5004 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14293 : .namespace = oprinfo->dobj.namespace->dobj.name,
14294 : .owner = oprinfo->rolname,
14295 : .description = "OPERATOR",
14296 : .section = SECTION_PRE_DATA,
14297 : .createStmt = q->data,
14298 : .dropStmt = delq->data));
14299 :
14300 : /* Dump Operator Comments */
14301 5004 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14302 4830 : dumpComment(fout, "OPERATOR", oprid->data,
14303 4830 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14304 4830 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14305 :
14306 5004 : PQclear(res);
14307 :
14308 5004 : destroyPQExpBuffer(query);
14309 5004 : destroyPQExpBuffer(q);
14310 5004 : destroyPQExpBuffer(delq);
14311 5004 : destroyPQExpBuffer(oprid);
14312 5004 : destroyPQExpBuffer(details);
14313 : }
14314 :
14315 : /*
14316 : * Convert a function reference obtained from pg_operator
14317 : *
14318 : * Returns allocated string of what to print, or NULL if function references
14319 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14320 : *
14321 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14322 : * part.
14323 : */
14324 : static char *
14325 15012 : convertRegProcReference(const char *proc)
14326 : {
14327 : char *name;
14328 : char *paren;
14329 : bool inquote;
14330 :
14331 : /* In all cases "-" means a null reference */
14332 15012 : if (strcmp(proc, "-") == 0)
14333 3880 : return NULL;
14334 :
14335 11132 : name = pg_strdup(proc);
14336 : /* find non-double-quoted left paren */
14337 11132 : inquote = false;
14338 133992 : for (paren = name; *paren; paren++)
14339 : {
14340 133992 : if (*paren == '(' && !inquote)
14341 : {
14342 11132 : *paren = '\0';
14343 11132 : break;
14344 : }
14345 122860 : if (*paren == '"')
14346 100 : inquote = !inquote;
14347 : }
14348 11132 : return name;
14349 : }
14350 :
14351 : /*
14352 : * getFormattedOperatorName - retrieve the operator name for the
14353 : * given operator OID (presented in string form).
14354 : *
14355 : * Returns an allocated string, or NULL if the given OID is invalid.
14356 : * Caller is responsible for free'ing result string.
14357 : *
14358 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14359 : * useful in commands where the operator's argument types can be inferred from
14360 : * context. We always schema-qualify the name, though. The predecessor to
14361 : * this code tried to skip the schema qualification if possible, but that led
14362 : * to wrong results in corner cases, such as if an operator and its negator
14363 : * are in different schemas.
14364 : */
14365 : static char *
14366 10578 : getFormattedOperatorName(const char *oproid)
14367 : {
14368 : OprInfo *oprInfo;
14369 :
14370 : /* In all cases "0" means a null reference */
14371 10578 : if (strcmp(oproid, "0") == 0)
14372 4858 : return NULL;
14373 :
14374 5720 : oprInfo = findOprByOid(atooid(oproid));
14375 5720 : if (oprInfo == NULL)
14376 : {
14377 0 : pg_log_warning("could not find operator with OID %s",
14378 : oproid);
14379 0 : return NULL;
14380 : }
14381 :
14382 5720 : return psprintf("OPERATOR(%s.%s)",
14383 5720 : fmtId(oprInfo->dobj.namespace->dobj.name),
14384 : oprInfo->dobj.name);
14385 : }
14386 :
14387 : /*
14388 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14389 : *
14390 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14391 : * argument lists of these functions are predetermined. Note that the
14392 : * caller should ensure we are in the proper schema, because the results
14393 : * are search path dependent!
14394 : */
14395 : static char *
14396 410 : convertTSFunction(Archive *fout, Oid funcOid)
14397 : {
14398 : char *result;
14399 : char query[128];
14400 : PGresult *res;
14401 :
14402 410 : snprintf(query, sizeof(query),
14403 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14404 410 : res = ExecuteSqlQueryForSingleRow(fout, query);
14405 :
14406 410 : result = pg_strdup(PQgetvalue(res, 0, 0));
14407 :
14408 410 : PQclear(res);
14409 :
14410 410 : return result;
14411 : }
14412 :
14413 : /*
14414 : * dumpAccessMethod
14415 : * write out a single access method definition
14416 : */
14417 : static void
14418 160 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14419 : {
14420 160 : DumpOptions *dopt = fout->dopt;
14421 : PQExpBuffer q;
14422 : PQExpBuffer delq;
14423 : char *qamname;
14424 :
14425 : /* Do nothing if not dumping schema */
14426 160 : if (!dopt->dumpSchema)
14427 24 : return;
14428 :
14429 136 : q = createPQExpBuffer();
14430 136 : delq = createPQExpBuffer();
14431 :
14432 136 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14433 :
14434 136 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14435 :
14436 136 : switch (aminfo->amtype)
14437 : {
14438 64 : case AMTYPE_INDEX:
14439 64 : appendPQExpBufferStr(q, "TYPE INDEX ");
14440 64 : break;
14441 72 : case AMTYPE_TABLE:
14442 72 : appendPQExpBufferStr(q, "TYPE TABLE ");
14443 72 : break;
14444 0 : default:
14445 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14446 : aminfo->amtype, qamname);
14447 0 : destroyPQExpBuffer(q);
14448 0 : destroyPQExpBuffer(delq);
14449 0 : free(qamname);
14450 0 : return;
14451 : }
14452 :
14453 136 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14454 :
14455 136 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14456 : qamname);
14457 :
14458 136 : if (dopt->binary_upgrade)
14459 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14460 : "ACCESS METHOD", qamname, NULL);
14461 :
14462 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14463 136 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14464 136 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14465 : .description = "ACCESS METHOD",
14466 : .section = SECTION_PRE_DATA,
14467 : .createStmt = q->data,
14468 : .dropStmt = delq->data));
14469 :
14470 : /* Dump Access Method Comments */
14471 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14472 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14473 : NULL, "",
14474 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14475 :
14476 136 : destroyPQExpBuffer(q);
14477 136 : destroyPQExpBuffer(delq);
14478 136 : free(qamname);
14479 : }
14480 :
14481 : /*
14482 : * dumpOpclass
14483 : * write out a single operator class definition
14484 : */
14485 : static void
14486 1332 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14487 : {
14488 1332 : DumpOptions *dopt = fout->dopt;
14489 : PQExpBuffer query;
14490 : PQExpBuffer q;
14491 : PQExpBuffer delq;
14492 : PQExpBuffer nameusing;
14493 : PGresult *res;
14494 : int ntups;
14495 : int i_opcintype;
14496 : int i_opckeytype;
14497 : int i_opcdefault;
14498 : int i_opcfamily;
14499 : int i_opcfamilyname;
14500 : int i_opcfamilynsp;
14501 : int i_amname;
14502 : int i_amopstrategy;
14503 : int i_amopopr;
14504 : int i_sortfamily;
14505 : int i_sortfamilynsp;
14506 : int i_amprocnum;
14507 : int i_amproc;
14508 : int i_amproclefttype;
14509 : int i_amprocrighttype;
14510 : char *opcintype;
14511 : char *opckeytype;
14512 : char *opcdefault;
14513 : char *opcfamily;
14514 : char *opcfamilyname;
14515 : char *opcfamilynsp;
14516 : char *amname;
14517 : char *amopstrategy;
14518 : char *amopopr;
14519 : char *sortfamily;
14520 : char *sortfamilynsp;
14521 : char *amprocnum;
14522 : char *amproc;
14523 : char *amproclefttype;
14524 : char *amprocrighttype;
14525 : bool needComma;
14526 : int i;
14527 :
14528 : /* Do nothing if not dumping schema */
14529 1332 : if (!dopt->dumpSchema)
14530 36 : return;
14531 :
14532 1296 : query = createPQExpBuffer();
14533 1296 : q = createPQExpBuffer();
14534 1296 : delq = createPQExpBuffer();
14535 1296 : nameusing = createPQExpBuffer();
14536 :
14537 : /* Get additional fields from the pg_opclass row */
14538 1296 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14539 : "opckeytype::pg_catalog.regtype, "
14540 : "opcdefault, opcfamily, "
14541 : "opfname AS opcfamilyname, "
14542 : "nspname AS opcfamilynsp, "
14543 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14544 : "FROM pg_catalog.pg_opclass c "
14545 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14546 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14547 : "WHERE c.oid = '%u'::pg_catalog.oid",
14548 1296 : opcinfo->dobj.catId.oid);
14549 :
14550 1296 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14551 :
14552 1296 : i_opcintype = PQfnumber(res, "opcintype");
14553 1296 : i_opckeytype = PQfnumber(res, "opckeytype");
14554 1296 : i_opcdefault = PQfnumber(res, "opcdefault");
14555 1296 : i_opcfamily = PQfnumber(res, "opcfamily");
14556 1296 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14557 1296 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14558 1296 : i_amname = PQfnumber(res, "amname");
14559 :
14560 : /* opcintype may still be needed after we PQclear res */
14561 1296 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14562 1296 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14563 1296 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14564 : /* opcfamily will still be needed after we PQclear res */
14565 1296 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14566 1296 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14567 1296 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14568 : /* amname will still be needed after we PQclear res */
14569 1296 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14570 :
14571 1296 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14572 1296 : fmtQualifiedDumpable(opcinfo));
14573 1296 : appendPQExpBuffer(delq, " USING %s;\n",
14574 : fmtId(amname));
14575 :
14576 : /* Build the fixed portion of the CREATE command */
14577 1296 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14578 1296 : fmtQualifiedDumpable(opcinfo));
14579 1296 : if (strcmp(opcdefault, "t") == 0)
14580 732 : appendPQExpBufferStr(q, "DEFAULT ");
14581 1296 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14582 : opcintype,
14583 : fmtId(amname));
14584 1296 : if (strlen(opcfamilyname) > 0)
14585 : {
14586 1296 : appendPQExpBufferStr(q, " FAMILY ");
14587 1296 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14588 1296 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14589 : }
14590 1296 : appendPQExpBufferStr(q, " AS\n ");
14591 :
14592 1296 : needComma = false;
14593 :
14594 1296 : if (strcmp(opckeytype, "-") != 0)
14595 : {
14596 504 : appendPQExpBuffer(q, "STORAGE %s",
14597 : opckeytype);
14598 504 : needComma = true;
14599 : }
14600 :
14601 1296 : PQclear(res);
14602 :
14603 : /*
14604 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14605 : *
14606 : * Print only those opfamily members that are tied to the opclass by
14607 : * pg_depend entries.
14608 : */
14609 1296 : resetPQExpBuffer(query);
14610 1296 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14611 : "amopopr::pg_catalog.regoperator, "
14612 : "opfname AS sortfamily, "
14613 : "nspname AS sortfamilynsp "
14614 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14615 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14616 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14617 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14618 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14619 : "AND refobjid = '%u'::pg_catalog.oid "
14620 : "AND amopfamily = '%s'::pg_catalog.oid "
14621 : "ORDER BY amopstrategy",
14622 1296 : opcinfo->dobj.catId.oid,
14623 : opcfamily);
14624 :
14625 1296 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14626 :
14627 1296 : ntups = PQntuples(res);
14628 :
14629 1296 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14630 1296 : i_amopopr = PQfnumber(res, "amopopr");
14631 1296 : i_sortfamily = PQfnumber(res, "sortfamily");
14632 1296 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14633 :
14634 1700 : for (i = 0; i < ntups; i++)
14635 : {
14636 404 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14637 404 : amopopr = PQgetvalue(res, i, i_amopopr);
14638 404 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14639 404 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14640 :
14641 404 : if (needComma)
14642 256 : appendPQExpBufferStr(q, " ,\n ");
14643 :
14644 404 : appendPQExpBuffer(q, "OPERATOR %s %s",
14645 : amopstrategy, amopopr);
14646 :
14647 404 : if (strlen(sortfamily) > 0)
14648 : {
14649 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14650 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14651 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14652 : }
14653 :
14654 404 : needComma = true;
14655 : }
14656 :
14657 1296 : PQclear(res);
14658 :
14659 : /*
14660 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14661 : *
14662 : * Print only those opfamily members that are tied to the opclass by
14663 : * pg_depend entries.
14664 : *
14665 : * We print the amproclefttype/amprocrighttype even though in most cases
14666 : * the backend could deduce the right values, because of the corner case
14667 : * of a btree sort support function for a cross-type comparison.
14668 : */
14669 1296 : resetPQExpBuffer(query);
14670 :
14671 1296 : appendPQExpBuffer(query, "SELECT amprocnum, "
14672 : "amproc::pg_catalog.regprocedure, "
14673 : "amproclefttype::pg_catalog.regtype, "
14674 : "amprocrighttype::pg_catalog.regtype "
14675 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14676 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14677 : "AND refobjid = '%u'::pg_catalog.oid "
14678 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14679 : "AND objid = ap.oid "
14680 : "ORDER BY amprocnum",
14681 1296 : opcinfo->dobj.catId.oid);
14682 :
14683 1296 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14684 :
14685 1296 : ntups = PQntuples(res);
14686 :
14687 1296 : i_amprocnum = PQfnumber(res, "amprocnum");
14688 1296 : i_amproc = PQfnumber(res, "amproc");
14689 1296 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14690 1296 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14691 :
14692 1360 : for (i = 0; i < ntups; i++)
14693 : {
14694 64 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14695 64 : amproc = PQgetvalue(res, i, i_amproc);
14696 64 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14697 64 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14698 :
14699 64 : if (needComma)
14700 64 : appendPQExpBufferStr(q, " ,\n ");
14701 :
14702 64 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14703 :
14704 64 : if (*amproclefttype && *amprocrighttype)
14705 64 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14706 :
14707 64 : appendPQExpBuffer(q, " %s", amproc);
14708 :
14709 64 : needComma = true;
14710 : }
14711 :
14712 1296 : PQclear(res);
14713 :
14714 : /*
14715 : * If needComma is still false it means we haven't added anything after
14716 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14717 : * clause with the same datatype. This isn't sanctioned by the
14718 : * documentation, but actually DefineOpClass will treat it as a no-op.
14719 : */
14720 1296 : if (!needComma)
14721 644 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14722 :
14723 1296 : appendPQExpBufferStr(q, ";\n");
14724 :
14725 1296 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14726 1296 : appendPQExpBuffer(nameusing, " USING %s",
14727 : fmtId(amname));
14728 :
14729 1296 : if (dopt->binary_upgrade)
14730 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14731 12 : "OPERATOR CLASS", nameusing->data,
14732 12 : opcinfo->dobj.namespace->dobj.name);
14733 :
14734 1296 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14735 1296 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14736 1296 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14737 : .namespace = opcinfo->dobj.namespace->dobj.name,
14738 : .owner = opcinfo->rolname,
14739 : .description = "OPERATOR CLASS",
14740 : .section = SECTION_PRE_DATA,
14741 : .createStmt = q->data,
14742 : .dropStmt = delq->data));
14743 :
14744 : /* Dump Operator Class Comments */
14745 1296 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14746 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14747 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14748 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14749 :
14750 1296 : free(opcintype);
14751 1296 : free(opcfamily);
14752 1296 : free(amname);
14753 1296 : destroyPQExpBuffer(query);
14754 1296 : destroyPQExpBuffer(q);
14755 1296 : destroyPQExpBuffer(delq);
14756 1296 : destroyPQExpBuffer(nameusing);
14757 : }
14758 :
14759 : /*
14760 : * dumpOpfamily
14761 : * write out a single operator family definition
14762 : *
14763 : * Note: this also dumps any "loose" operator members that aren't bound to a
14764 : * specific opclass within the opfamily.
14765 : */
14766 : static void
14767 1110 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14768 : {
14769 1110 : DumpOptions *dopt = fout->dopt;
14770 : PQExpBuffer query;
14771 : PQExpBuffer q;
14772 : PQExpBuffer delq;
14773 : PQExpBuffer nameusing;
14774 : PGresult *res;
14775 : PGresult *res_ops;
14776 : PGresult *res_procs;
14777 : int ntups;
14778 : int i_amname;
14779 : int i_amopstrategy;
14780 : int i_amopopr;
14781 : int i_sortfamily;
14782 : int i_sortfamilynsp;
14783 : int i_amprocnum;
14784 : int i_amproc;
14785 : int i_amproclefttype;
14786 : int i_amprocrighttype;
14787 : char *amname;
14788 : char *amopstrategy;
14789 : char *amopopr;
14790 : char *sortfamily;
14791 : char *sortfamilynsp;
14792 : char *amprocnum;
14793 : char *amproc;
14794 : char *amproclefttype;
14795 : char *amprocrighttype;
14796 : bool needComma;
14797 : int i;
14798 :
14799 : /* Do nothing if not dumping schema */
14800 1110 : if (!dopt->dumpSchema)
14801 24 : return;
14802 :
14803 1086 : query = createPQExpBuffer();
14804 1086 : q = createPQExpBuffer();
14805 1086 : delq = createPQExpBuffer();
14806 1086 : nameusing = createPQExpBuffer();
14807 :
14808 : /*
14809 : * Fetch only those opfamily members that are tied directly to the
14810 : * opfamily by pg_depend entries.
14811 : */
14812 1086 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14813 : "amopopr::pg_catalog.regoperator, "
14814 : "opfname AS sortfamily, "
14815 : "nspname AS sortfamilynsp "
14816 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14817 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14818 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14819 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14820 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14821 : "AND refobjid = '%u'::pg_catalog.oid "
14822 : "AND amopfamily = '%u'::pg_catalog.oid "
14823 : "ORDER BY amopstrategy",
14824 1086 : opfinfo->dobj.catId.oid,
14825 1086 : opfinfo->dobj.catId.oid);
14826 :
14827 1086 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14828 :
14829 1086 : resetPQExpBuffer(query);
14830 :
14831 1086 : appendPQExpBuffer(query, "SELECT amprocnum, "
14832 : "amproc::pg_catalog.regprocedure, "
14833 : "amproclefttype::pg_catalog.regtype, "
14834 : "amprocrighttype::pg_catalog.regtype "
14835 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14836 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14837 : "AND refobjid = '%u'::pg_catalog.oid "
14838 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14839 : "AND objid = ap.oid "
14840 : "ORDER BY amprocnum",
14841 1086 : opfinfo->dobj.catId.oid);
14842 :
14843 1086 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14844 :
14845 : /* Get additional fields from the pg_opfamily row */
14846 1086 : resetPQExpBuffer(query);
14847 :
14848 1086 : appendPQExpBuffer(query, "SELECT "
14849 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14850 : "FROM pg_catalog.pg_opfamily "
14851 : "WHERE oid = '%u'::pg_catalog.oid",
14852 1086 : opfinfo->dobj.catId.oid);
14853 :
14854 1086 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14855 :
14856 1086 : i_amname = PQfnumber(res, "amname");
14857 :
14858 : /* amname will still be needed after we PQclear res */
14859 1086 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14860 :
14861 1086 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14862 1086 : fmtQualifiedDumpable(opfinfo));
14863 1086 : appendPQExpBuffer(delq, " USING %s;\n",
14864 : fmtId(amname));
14865 :
14866 : /* Build the fixed portion of the CREATE command */
14867 1086 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14868 1086 : fmtQualifiedDumpable(opfinfo));
14869 1086 : appendPQExpBuffer(q, " USING %s;\n",
14870 : fmtId(amname));
14871 :
14872 1086 : PQclear(res);
14873 :
14874 : /* Do we need an ALTER to add loose members? */
14875 1086 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14876 : {
14877 94 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14878 94 : fmtQualifiedDumpable(opfinfo));
14879 94 : appendPQExpBuffer(q, " USING %s ADD\n ",
14880 : fmtId(amname));
14881 :
14882 94 : needComma = false;
14883 :
14884 : /*
14885 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14886 : */
14887 94 : ntups = PQntuples(res_ops);
14888 :
14889 94 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14890 94 : i_amopopr = PQfnumber(res_ops, "amopopr");
14891 94 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14892 94 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14893 :
14894 414 : for (i = 0; i < ntups; i++)
14895 : {
14896 320 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14897 320 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14898 320 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14899 320 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14900 :
14901 320 : if (needComma)
14902 256 : appendPQExpBufferStr(q, " ,\n ");
14903 :
14904 320 : appendPQExpBuffer(q, "OPERATOR %s %s",
14905 : amopstrategy, amopopr);
14906 :
14907 320 : if (strlen(sortfamily) > 0)
14908 : {
14909 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14910 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14911 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14912 : }
14913 :
14914 320 : needComma = true;
14915 : }
14916 :
14917 : /*
14918 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14919 : */
14920 94 : ntups = PQntuples(res_procs);
14921 :
14922 94 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14923 94 : i_amproc = PQfnumber(res_procs, "amproc");
14924 94 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14925 94 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14926 :
14927 444 : for (i = 0; i < ntups; i++)
14928 : {
14929 350 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14930 350 : amproc = PQgetvalue(res_procs, i, i_amproc);
14931 350 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14932 350 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14933 :
14934 350 : if (needComma)
14935 320 : appendPQExpBufferStr(q, " ,\n ");
14936 :
14937 350 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14938 : amprocnum, amproclefttype, amprocrighttype,
14939 : amproc);
14940 :
14941 350 : needComma = true;
14942 : }
14943 :
14944 94 : appendPQExpBufferStr(q, ";\n");
14945 : }
14946 :
14947 1086 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14948 1086 : appendPQExpBuffer(nameusing, " USING %s",
14949 : fmtId(amname));
14950 :
14951 1086 : if (dopt->binary_upgrade)
14952 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14953 18 : "OPERATOR FAMILY", nameusing->data,
14954 18 : opfinfo->dobj.namespace->dobj.name);
14955 :
14956 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14957 1086 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14958 1086 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14959 : .namespace = opfinfo->dobj.namespace->dobj.name,
14960 : .owner = opfinfo->rolname,
14961 : .description = "OPERATOR FAMILY",
14962 : .section = SECTION_PRE_DATA,
14963 : .createStmt = q->data,
14964 : .dropStmt = delq->data));
14965 :
14966 : /* Dump Operator Family Comments */
14967 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14968 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14969 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14970 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14971 :
14972 1086 : free(amname);
14973 1086 : PQclear(res_ops);
14974 1086 : PQclear(res_procs);
14975 1086 : destroyPQExpBuffer(query);
14976 1086 : destroyPQExpBuffer(q);
14977 1086 : destroyPQExpBuffer(delq);
14978 1086 : destroyPQExpBuffer(nameusing);
14979 : }
14980 :
14981 : /*
14982 : * dumpCollation
14983 : * write out a single collation definition
14984 : */
14985 : static void
14986 5074 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14987 : {
14988 5074 : DumpOptions *dopt = fout->dopt;
14989 : PQExpBuffer query;
14990 : PQExpBuffer q;
14991 : PQExpBuffer delq;
14992 : char *qcollname;
14993 : PGresult *res;
14994 : int i_collprovider;
14995 : int i_collisdeterministic;
14996 : int i_collcollate;
14997 : int i_collctype;
14998 : int i_colllocale;
14999 : int i_collicurules;
15000 : const char *collprovider;
15001 : const char *collcollate;
15002 : const char *collctype;
15003 : const char *colllocale;
15004 : const char *collicurules;
15005 :
15006 : /* Do nothing if not dumping schema */
15007 5074 : if (!dopt->dumpSchema)
15008 24 : return;
15009 :
15010 5050 : query = createPQExpBuffer();
15011 5050 : q = createPQExpBuffer();
15012 5050 : delq = createPQExpBuffer();
15013 :
15014 5050 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15015 :
15016 : /* Get collation-specific details */
15017 5050 : appendPQExpBufferStr(query, "SELECT ");
15018 :
15019 5050 : if (fout->remoteVersion >= 100000)
15020 5050 : appendPQExpBufferStr(query,
15021 : "collprovider, "
15022 : "collversion, ");
15023 : else
15024 0 : appendPQExpBufferStr(query,
15025 : "'c' AS collprovider, "
15026 : "NULL AS collversion, ");
15027 :
15028 5050 : if (fout->remoteVersion >= 120000)
15029 5050 : appendPQExpBufferStr(query,
15030 : "collisdeterministic, ");
15031 : else
15032 0 : appendPQExpBufferStr(query,
15033 : "true AS collisdeterministic, ");
15034 :
15035 5050 : if (fout->remoteVersion >= 170000)
15036 5050 : appendPQExpBufferStr(query,
15037 : "colllocale, ");
15038 0 : else if (fout->remoteVersion >= 150000)
15039 0 : appendPQExpBufferStr(query,
15040 : "colliculocale AS colllocale, ");
15041 : else
15042 0 : appendPQExpBufferStr(query,
15043 : "NULL AS colllocale, ");
15044 :
15045 5050 : if (fout->remoteVersion >= 160000)
15046 5050 : appendPQExpBufferStr(query,
15047 : "collicurules, ");
15048 : else
15049 0 : appendPQExpBufferStr(query,
15050 : "NULL AS collicurules, ");
15051 :
15052 5050 : appendPQExpBuffer(query,
15053 : "collcollate, "
15054 : "collctype "
15055 : "FROM pg_catalog.pg_collation c "
15056 : "WHERE c.oid = '%u'::pg_catalog.oid",
15057 5050 : collinfo->dobj.catId.oid);
15058 :
15059 5050 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15060 :
15061 5050 : i_collprovider = PQfnumber(res, "collprovider");
15062 5050 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15063 5050 : i_collcollate = PQfnumber(res, "collcollate");
15064 5050 : i_collctype = PQfnumber(res, "collctype");
15065 5050 : i_colllocale = PQfnumber(res, "colllocale");
15066 5050 : i_collicurules = PQfnumber(res, "collicurules");
15067 :
15068 5050 : collprovider = PQgetvalue(res, 0, i_collprovider);
15069 :
15070 5050 : if (!PQgetisnull(res, 0, i_collcollate))
15071 92 : collcollate = PQgetvalue(res, 0, i_collcollate);
15072 : else
15073 4958 : collcollate = NULL;
15074 :
15075 5050 : if (!PQgetisnull(res, 0, i_collctype))
15076 92 : collctype = PQgetvalue(res, 0, i_collctype);
15077 : else
15078 4958 : collctype = NULL;
15079 :
15080 : /*
15081 : * Before version 15, collcollate and collctype were of type NAME and
15082 : * non-nullable. Treat empty strings as NULL for consistency.
15083 : */
15084 5050 : if (fout->remoteVersion < 150000)
15085 : {
15086 0 : if (collcollate[0] == '\0')
15087 0 : collcollate = NULL;
15088 0 : if (collctype[0] == '\0')
15089 0 : collctype = NULL;
15090 : }
15091 :
15092 5050 : if (!PQgetisnull(res, 0, i_colllocale))
15093 4952 : colllocale = PQgetvalue(res, 0, i_colllocale);
15094 : else
15095 98 : colllocale = NULL;
15096 :
15097 5050 : if (!PQgetisnull(res, 0, i_collicurules))
15098 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15099 : else
15100 5050 : collicurules = NULL;
15101 :
15102 5050 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15103 5050 : fmtQualifiedDumpable(collinfo));
15104 :
15105 5050 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15106 5050 : fmtQualifiedDumpable(collinfo));
15107 :
15108 5050 : appendPQExpBufferStr(q, "provider = ");
15109 5050 : if (collprovider[0] == 'b')
15110 38 : appendPQExpBufferStr(q, "builtin");
15111 5012 : else if (collprovider[0] == 'c')
15112 92 : appendPQExpBufferStr(q, "libc");
15113 4920 : else if (collprovider[0] == 'i')
15114 4914 : appendPQExpBufferStr(q, "icu");
15115 6 : else if (collprovider[0] == 'd')
15116 : /* to allow dumping pg_catalog; not accepted on input */
15117 6 : appendPQExpBufferStr(q, "default");
15118 : else
15119 0 : pg_fatal("unrecognized collation provider: %s",
15120 : collprovider);
15121 :
15122 5050 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15123 0 : appendPQExpBufferStr(q, ", deterministic = false");
15124 :
15125 5050 : if (collprovider[0] == 'd')
15126 : {
15127 6 : if (collcollate || collctype || colllocale || collicurules)
15128 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15129 :
15130 : /* no locale -- the default collation cannot be reloaded anyway */
15131 : }
15132 5044 : else if (collprovider[0] == 'b')
15133 : {
15134 38 : if (collcollate || collctype || !colllocale || collicurules)
15135 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15136 :
15137 38 : appendPQExpBufferStr(q, ", locale = ");
15138 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15139 : fout);
15140 : }
15141 5006 : else if (collprovider[0] == 'i')
15142 : {
15143 4914 : if (fout->remoteVersion >= 150000)
15144 : {
15145 4914 : if (collcollate || collctype || !colllocale)
15146 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15147 :
15148 4914 : appendPQExpBufferStr(q, ", locale = ");
15149 4914 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15150 : fout);
15151 : }
15152 : else
15153 : {
15154 0 : if (!collcollate || !collctype || colllocale ||
15155 0 : strcmp(collcollate, collctype) != 0)
15156 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15157 :
15158 0 : appendPQExpBufferStr(q, ", locale = ");
15159 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15160 : }
15161 :
15162 4914 : if (collicurules)
15163 : {
15164 0 : appendPQExpBufferStr(q, ", rules = ");
15165 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15166 : }
15167 : }
15168 92 : else if (collprovider[0] == 'c')
15169 : {
15170 92 : if (colllocale || collicurules || !collcollate || !collctype)
15171 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15172 :
15173 92 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15174 : {
15175 92 : appendPQExpBufferStr(q, ", locale = ");
15176 92 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15177 : }
15178 : else
15179 : {
15180 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15181 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15182 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15183 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15184 : }
15185 : }
15186 : else
15187 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15188 :
15189 : /*
15190 : * For binary upgrade, carry over the collation version. For normal
15191 : * dump/restore, omit the version, so that it is computed upon restore.
15192 : */
15193 5050 : if (dopt->binary_upgrade)
15194 : {
15195 : int i_collversion;
15196 :
15197 10 : i_collversion = PQfnumber(res, "collversion");
15198 10 : if (!PQgetisnull(res, 0, i_collversion))
15199 : {
15200 8 : appendPQExpBufferStr(q, ", version = ");
15201 8 : appendStringLiteralAH(q,
15202 : PQgetvalue(res, 0, i_collversion),
15203 : fout);
15204 : }
15205 : }
15206 :
15207 5050 : appendPQExpBufferStr(q, ");\n");
15208 :
15209 5050 : if (dopt->binary_upgrade)
15210 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15211 : "COLLATION", qcollname,
15212 10 : collinfo->dobj.namespace->dobj.name);
15213 :
15214 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15215 5050 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15216 5050 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15217 : .namespace = collinfo->dobj.namespace->dobj.name,
15218 : .owner = collinfo->rolname,
15219 : .description = "COLLATION",
15220 : .section = SECTION_PRE_DATA,
15221 : .createStmt = q->data,
15222 : .dropStmt = delq->data));
15223 :
15224 : /* Dump Collation Comments */
15225 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15226 4862 : dumpComment(fout, "COLLATION", qcollname,
15227 4862 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15228 4862 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15229 :
15230 5050 : PQclear(res);
15231 :
15232 5050 : destroyPQExpBuffer(query);
15233 5050 : destroyPQExpBuffer(q);
15234 5050 : destroyPQExpBuffer(delq);
15235 5050 : free(qcollname);
15236 : }
15237 :
15238 : /*
15239 : * dumpConversion
15240 : * write out a single conversion definition
15241 : */
15242 : static void
15243 844 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15244 : {
15245 844 : DumpOptions *dopt = fout->dopt;
15246 : PQExpBuffer query;
15247 : PQExpBuffer q;
15248 : PQExpBuffer delq;
15249 : char *qconvname;
15250 : PGresult *res;
15251 : int i_conforencoding;
15252 : int i_contoencoding;
15253 : int i_conproc;
15254 : int i_condefault;
15255 : const char *conforencoding;
15256 : const char *contoencoding;
15257 : const char *conproc;
15258 : bool condefault;
15259 :
15260 : /* Do nothing if not dumping schema */
15261 844 : if (!dopt->dumpSchema)
15262 12 : return;
15263 :
15264 832 : query = createPQExpBuffer();
15265 832 : q = createPQExpBuffer();
15266 832 : delq = createPQExpBuffer();
15267 :
15268 832 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15269 :
15270 : /* Get conversion-specific details */
15271 832 : appendPQExpBuffer(query, "SELECT "
15272 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15273 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15274 : "conproc, condefault "
15275 : "FROM pg_catalog.pg_conversion c "
15276 : "WHERE c.oid = '%u'::pg_catalog.oid",
15277 832 : convinfo->dobj.catId.oid);
15278 :
15279 832 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15280 :
15281 832 : i_conforencoding = PQfnumber(res, "conforencoding");
15282 832 : i_contoencoding = PQfnumber(res, "contoencoding");
15283 832 : i_conproc = PQfnumber(res, "conproc");
15284 832 : i_condefault = PQfnumber(res, "condefault");
15285 :
15286 832 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15287 832 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15288 832 : conproc = PQgetvalue(res, 0, i_conproc);
15289 832 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15290 :
15291 832 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15292 832 : fmtQualifiedDumpable(convinfo));
15293 :
15294 832 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15295 : (condefault) ? "DEFAULT " : "",
15296 832 : fmtQualifiedDumpable(convinfo));
15297 832 : appendStringLiteralAH(q, conforencoding, fout);
15298 832 : appendPQExpBufferStr(q, " TO ");
15299 832 : appendStringLiteralAH(q, contoencoding, fout);
15300 : /* regproc output is already sufficiently quoted */
15301 832 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15302 :
15303 832 : if (dopt->binary_upgrade)
15304 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15305 : "CONVERSION", qconvname,
15306 2 : convinfo->dobj.namespace->dobj.name);
15307 :
15308 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15309 832 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15310 832 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15311 : .namespace = convinfo->dobj.namespace->dobj.name,
15312 : .owner = convinfo->rolname,
15313 : .description = "CONVERSION",
15314 : .section = SECTION_PRE_DATA,
15315 : .createStmt = q->data,
15316 : .dropStmt = delq->data));
15317 :
15318 : /* Dump Conversion Comments */
15319 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15320 832 : dumpComment(fout, "CONVERSION", qconvname,
15321 832 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15322 832 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15323 :
15324 832 : PQclear(res);
15325 :
15326 832 : destroyPQExpBuffer(query);
15327 832 : destroyPQExpBuffer(q);
15328 832 : destroyPQExpBuffer(delq);
15329 832 : free(qconvname);
15330 : }
15331 :
15332 : /*
15333 : * format_aggregate_signature: generate aggregate name and argument list
15334 : *
15335 : * The argument type names are qualified if needed. The aggregate name
15336 : * is never qualified.
15337 : */
15338 : static char *
15339 570 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15340 : {
15341 : PQExpBufferData buf;
15342 : int j;
15343 :
15344 570 : initPQExpBuffer(&buf);
15345 570 : if (honor_quotes)
15346 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15347 : else
15348 570 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15349 :
15350 570 : if (agginfo->aggfn.nargs == 0)
15351 80 : appendPQExpBufferStr(&buf, "(*)");
15352 : else
15353 : {
15354 490 : appendPQExpBufferChar(&buf, '(');
15355 1070 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15356 580 : appendPQExpBuffer(&buf, "%s%s",
15357 : (j > 0) ? ", " : "",
15358 : getFormattedTypeName(fout,
15359 580 : agginfo->aggfn.argtypes[j],
15360 : zeroIsError));
15361 490 : appendPQExpBufferChar(&buf, ')');
15362 : }
15363 570 : return buf.data;
15364 : }
15365 :
15366 : /*
15367 : * dumpAgg
15368 : * write out a single aggregate definition
15369 : */
15370 : static void
15371 584 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15372 : {
15373 584 : DumpOptions *dopt = fout->dopt;
15374 : PQExpBuffer query;
15375 : PQExpBuffer q;
15376 : PQExpBuffer delq;
15377 : PQExpBuffer details;
15378 : char *aggsig; /* identity signature */
15379 584 : char *aggfullsig = NULL; /* full signature */
15380 : char *aggsig_tag;
15381 : PGresult *res;
15382 : int i_agginitval;
15383 : int i_aggminitval;
15384 : const char *aggtransfn;
15385 : const char *aggfinalfn;
15386 : const char *aggcombinefn;
15387 : const char *aggserialfn;
15388 : const char *aggdeserialfn;
15389 : const char *aggmtransfn;
15390 : const char *aggminvtransfn;
15391 : const char *aggmfinalfn;
15392 : bool aggfinalextra;
15393 : bool aggmfinalextra;
15394 : char aggfinalmodify;
15395 : char aggmfinalmodify;
15396 : const char *aggsortop;
15397 : char *aggsortconvop;
15398 : char aggkind;
15399 : const char *aggtranstype;
15400 : const char *aggtransspace;
15401 : const char *aggmtranstype;
15402 : const char *aggmtransspace;
15403 : const char *agginitval;
15404 : const char *aggminitval;
15405 : const char *proparallel;
15406 : char defaultfinalmodify;
15407 :
15408 : /* Do nothing if not dumping schema */
15409 584 : if (!dopt->dumpSchema)
15410 14 : return;
15411 :
15412 570 : query = createPQExpBuffer();
15413 570 : q = createPQExpBuffer();
15414 570 : delq = createPQExpBuffer();
15415 570 : details = createPQExpBuffer();
15416 :
15417 570 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15418 : {
15419 : /* Set up query for aggregate-specific details */
15420 110 : appendPQExpBufferStr(query,
15421 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15422 :
15423 110 : appendPQExpBufferStr(query,
15424 : "SELECT "
15425 : "aggtransfn,\n"
15426 : "aggfinalfn,\n"
15427 : "aggtranstype::pg_catalog.regtype,\n"
15428 : "agginitval,\n"
15429 : "aggsortop,\n"
15430 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15431 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15432 :
15433 110 : if (fout->remoteVersion >= 90400)
15434 110 : appendPQExpBufferStr(query,
15435 : "aggkind,\n"
15436 : "aggmtransfn,\n"
15437 : "aggminvtransfn,\n"
15438 : "aggmfinalfn,\n"
15439 : "aggmtranstype::pg_catalog.regtype,\n"
15440 : "aggfinalextra,\n"
15441 : "aggmfinalextra,\n"
15442 : "aggtransspace,\n"
15443 : "aggmtransspace,\n"
15444 : "aggminitval,\n");
15445 : else
15446 0 : appendPQExpBufferStr(query,
15447 : "'n' AS aggkind,\n"
15448 : "'-' AS aggmtransfn,\n"
15449 : "'-' AS aggminvtransfn,\n"
15450 : "'-' AS aggmfinalfn,\n"
15451 : "0 AS aggmtranstype,\n"
15452 : "false AS aggfinalextra,\n"
15453 : "false AS aggmfinalextra,\n"
15454 : "0 AS aggtransspace,\n"
15455 : "0 AS aggmtransspace,\n"
15456 : "NULL AS aggminitval,\n");
15457 :
15458 110 : if (fout->remoteVersion >= 90600)
15459 110 : appendPQExpBufferStr(query,
15460 : "aggcombinefn,\n"
15461 : "aggserialfn,\n"
15462 : "aggdeserialfn,\n"
15463 : "proparallel,\n");
15464 : else
15465 0 : appendPQExpBufferStr(query,
15466 : "'-' AS aggcombinefn,\n"
15467 : "'-' AS aggserialfn,\n"
15468 : "'-' AS aggdeserialfn,\n"
15469 : "'u' AS proparallel,\n");
15470 :
15471 110 : if (fout->remoteVersion >= 110000)
15472 110 : appendPQExpBufferStr(query,
15473 : "aggfinalmodify,\n"
15474 : "aggmfinalmodify\n");
15475 : else
15476 0 : appendPQExpBufferStr(query,
15477 : "'0' AS aggfinalmodify,\n"
15478 : "'0' AS aggmfinalmodify\n");
15479 :
15480 110 : appendPQExpBufferStr(query,
15481 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15482 : "WHERE a.aggfnoid = p.oid "
15483 : "AND p.oid = $1");
15484 :
15485 110 : ExecuteSqlStatement(fout, query->data);
15486 :
15487 110 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15488 : }
15489 :
15490 570 : printfPQExpBuffer(query,
15491 : "EXECUTE dumpAgg('%u')",
15492 570 : agginfo->aggfn.dobj.catId.oid);
15493 :
15494 570 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15495 :
15496 570 : i_agginitval = PQfnumber(res, "agginitval");
15497 570 : i_aggminitval = PQfnumber(res, "aggminitval");
15498 :
15499 570 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15500 570 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15501 570 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15502 570 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15503 570 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15504 570 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15505 570 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15506 570 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15507 570 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15508 570 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15509 570 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15510 570 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15511 570 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15512 570 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15513 570 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15514 570 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15515 570 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15516 570 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15517 570 : agginitval = PQgetvalue(res, 0, i_agginitval);
15518 570 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15519 570 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15520 :
15521 : {
15522 : char *funcargs;
15523 : char *funciargs;
15524 :
15525 570 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15526 570 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15527 570 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15528 570 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15529 : }
15530 :
15531 570 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15532 :
15533 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15534 570 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15535 : /* replace omitted flags for old versions */
15536 570 : if (aggfinalmodify == '0')
15537 0 : aggfinalmodify = defaultfinalmodify;
15538 570 : if (aggmfinalmodify == '0')
15539 0 : aggmfinalmodify = defaultfinalmodify;
15540 :
15541 : /* regproc and regtype output is already sufficiently quoted */
15542 570 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15543 : aggtransfn, aggtranstype);
15544 :
15545 570 : if (strcmp(aggtransspace, "0") != 0)
15546 : {
15547 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15548 : aggtransspace);
15549 : }
15550 :
15551 570 : if (!PQgetisnull(res, 0, i_agginitval))
15552 : {
15553 414 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15554 414 : appendStringLiteralAH(details, agginitval, fout);
15555 : }
15556 :
15557 570 : if (strcmp(aggfinalfn, "-") != 0)
15558 : {
15559 264 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15560 : aggfinalfn);
15561 264 : if (aggfinalextra)
15562 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15563 264 : if (aggfinalmodify != defaultfinalmodify)
15564 : {
15565 64 : switch (aggfinalmodify)
15566 : {
15567 0 : case AGGMODIFY_READ_ONLY:
15568 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15569 0 : break;
15570 64 : case AGGMODIFY_SHAREABLE:
15571 64 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15572 64 : break;
15573 0 : case AGGMODIFY_READ_WRITE:
15574 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15575 0 : break;
15576 0 : default:
15577 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15578 : agginfo->aggfn.dobj.name);
15579 : break;
15580 : }
15581 : }
15582 : }
15583 :
15584 570 : if (strcmp(aggcombinefn, "-") != 0)
15585 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15586 :
15587 570 : if (strcmp(aggserialfn, "-") != 0)
15588 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15589 :
15590 570 : if (strcmp(aggdeserialfn, "-") != 0)
15591 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15592 :
15593 570 : if (strcmp(aggmtransfn, "-") != 0)
15594 : {
15595 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15596 : aggmtransfn,
15597 : aggminvtransfn,
15598 : aggmtranstype);
15599 : }
15600 :
15601 570 : if (strcmp(aggmtransspace, "0") != 0)
15602 : {
15603 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15604 : aggmtransspace);
15605 : }
15606 :
15607 570 : if (!PQgetisnull(res, 0, i_aggminitval))
15608 : {
15609 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15610 20 : appendStringLiteralAH(details, aggminitval, fout);
15611 : }
15612 :
15613 570 : if (strcmp(aggmfinalfn, "-") != 0)
15614 : {
15615 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15616 : aggmfinalfn);
15617 0 : if (aggmfinalextra)
15618 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15619 0 : if (aggmfinalmodify != defaultfinalmodify)
15620 : {
15621 0 : switch (aggmfinalmodify)
15622 : {
15623 0 : case AGGMODIFY_READ_ONLY:
15624 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15625 0 : break;
15626 0 : case AGGMODIFY_SHAREABLE:
15627 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15628 0 : break;
15629 0 : case AGGMODIFY_READ_WRITE:
15630 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15631 0 : break;
15632 0 : default:
15633 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15634 : agginfo->aggfn.dobj.name);
15635 : break;
15636 : }
15637 : }
15638 : }
15639 :
15640 570 : aggsortconvop = getFormattedOperatorName(aggsortop);
15641 570 : if (aggsortconvop)
15642 : {
15643 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15644 : aggsortconvop);
15645 0 : free(aggsortconvop);
15646 : }
15647 :
15648 570 : if (aggkind == AGGKIND_HYPOTHETICAL)
15649 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15650 :
15651 570 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15652 : {
15653 10 : if (proparallel[0] == PROPARALLEL_SAFE)
15654 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15655 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15656 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15657 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15658 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15659 : agginfo->aggfn.dobj.name);
15660 : }
15661 :
15662 570 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15663 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15664 : aggsig);
15665 :
15666 1140 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15667 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15668 : aggfullsig ? aggfullsig : aggsig, details->data);
15669 :
15670 570 : if (dopt->binary_upgrade)
15671 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15672 : "AGGREGATE", aggsig,
15673 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15674 :
15675 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15676 536 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15677 536 : agginfo->aggfn.dobj.dumpId,
15678 536 : ARCHIVE_OPTS(.tag = aggsig_tag,
15679 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15680 : .owner = agginfo->aggfn.rolname,
15681 : .description = "AGGREGATE",
15682 : .section = SECTION_PRE_DATA,
15683 : .createStmt = q->data,
15684 : .dropStmt = delq->data));
15685 :
15686 : /* Dump Aggregate Comments */
15687 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15688 20 : dumpComment(fout, "AGGREGATE", aggsig,
15689 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15690 20 : agginfo->aggfn.rolname,
15691 20 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15692 :
15693 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15694 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15695 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15696 0 : agginfo->aggfn.rolname,
15697 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15698 :
15699 : /*
15700 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15701 : * command look like a function's GRANT; in particular this affects the
15702 : * syntax for zero-argument aggregates and ordered-set aggregates.
15703 : */
15704 570 : free(aggsig);
15705 :
15706 570 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15707 :
15708 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15709 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15710 : "FUNCTION", aggsig, NULL,
15711 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15712 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15713 :
15714 570 : free(aggsig);
15715 570 : free(aggfullsig);
15716 570 : free(aggsig_tag);
15717 :
15718 570 : PQclear(res);
15719 :
15720 570 : destroyPQExpBuffer(query);
15721 570 : destroyPQExpBuffer(q);
15722 570 : destroyPQExpBuffer(delq);
15723 570 : destroyPQExpBuffer(details);
15724 : }
15725 :
15726 : /*
15727 : * dumpTSParser
15728 : * write out a single text search parser
15729 : */
15730 : static void
15731 82 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15732 : {
15733 82 : DumpOptions *dopt = fout->dopt;
15734 : PQExpBuffer q;
15735 : PQExpBuffer delq;
15736 : char *qprsname;
15737 :
15738 : /* Do nothing if not dumping schema */
15739 82 : if (!dopt->dumpSchema)
15740 12 : return;
15741 :
15742 70 : q = createPQExpBuffer();
15743 70 : delq = createPQExpBuffer();
15744 :
15745 70 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15746 :
15747 70 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15748 70 : fmtQualifiedDumpable(prsinfo));
15749 :
15750 70 : appendPQExpBuffer(q, " START = %s,\n",
15751 70 : convertTSFunction(fout, prsinfo->prsstart));
15752 70 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15753 70 : convertTSFunction(fout, prsinfo->prstoken));
15754 70 : appendPQExpBuffer(q, " END = %s,\n",
15755 70 : convertTSFunction(fout, prsinfo->prsend));
15756 70 : if (prsinfo->prsheadline != InvalidOid)
15757 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15758 6 : convertTSFunction(fout, prsinfo->prsheadline));
15759 70 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15760 70 : convertTSFunction(fout, prsinfo->prslextype));
15761 :
15762 70 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15763 70 : fmtQualifiedDumpable(prsinfo));
15764 :
15765 70 : if (dopt->binary_upgrade)
15766 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15767 : "TEXT SEARCH PARSER", qprsname,
15768 2 : prsinfo->dobj.namespace->dobj.name);
15769 :
15770 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15771 70 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15772 70 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15773 : .namespace = prsinfo->dobj.namespace->dobj.name,
15774 : .description = "TEXT SEARCH PARSER",
15775 : .section = SECTION_PRE_DATA,
15776 : .createStmt = q->data,
15777 : .dropStmt = delq->data));
15778 :
15779 : /* Dump Parser Comments */
15780 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15781 70 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15782 70 : prsinfo->dobj.namespace->dobj.name, "",
15783 70 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15784 :
15785 70 : destroyPQExpBuffer(q);
15786 70 : destroyPQExpBuffer(delq);
15787 70 : free(qprsname);
15788 : }
15789 :
15790 : /*
15791 : * dumpTSDictionary
15792 : * write out a single text search dictionary
15793 : */
15794 : static void
15795 358 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15796 : {
15797 358 : DumpOptions *dopt = fout->dopt;
15798 : PQExpBuffer q;
15799 : PQExpBuffer delq;
15800 : PQExpBuffer query;
15801 : char *qdictname;
15802 : PGresult *res;
15803 : char *nspname;
15804 : char *tmplname;
15805 :
15806 : /* Do nothing if not dumping schema */
15807 358 : if (!dopt->dumpSchema)
15808 12 : return;
15809 :
15810 346 : q = createPQExpBuffer();
15811 346 : delq = createPQExpBuffer();
15812 346 : query = createPQExpBuffer();
15813 :
15814 346 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15815 :
15816 : /* Fetch name and namespace of the dictionary's template */
15817 346 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15818 : "FROM pg_ts_template p, pg_namespace n "
15819 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15820 346 : dictinfo->dicttemplate);
15821 346 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15822 346 : nspname = PQgetvalue(res, 0, 0);
15823 346 : tmplname = PQgetvalue(res, 0, 1);
15824 :
15825 346 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15826 346 : fmtQualifiedDumpable(dictinfo));
15827 :
15828 346 : appendPQExpBufferStr(q, " TEMPLATE = ");
15829 346 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15830 346 : appendPQExpBufferStr(q, fmtId(tmplname));
15831 :
15832 346 : PQclear(res);
15833 :
15834 : /* the dictinitoption can be dumped straight into the command */
15835 346 : if (dictinfo->dictinitoption)
15836 276 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15837 :
15838 346 : appendPQExpBufferStr(q, " );\n");
15839 :
15840 346 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15841 346 : fmtQualifiedDumpable(dictinfo));
15842 :
15843 346 : if (dopt->binary_upgrade)
15844 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15845 : "TEXT SEARCH DICTIONARY", qdictname,
15846 20 : dictinfo->dobj.namespace->dobj.name);
15847 :
15848 346 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15849 346 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15850 346 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15851 : .namespace = dictinfo->dobj.namespace->dobj.name,
15852 : .owner = dictinfo->rolname,
15853 : .description = "TEXT SEARCH DICTIONARY",
15854 : .section = SECTION_PRE_DATA,
15855 : .createStmt = q->data,
15856 : .dropStmt = delq->data));
15857 :
15858 : /* Dump Dictionary Comments */
15859 346 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15860 256 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15861 256 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15862 256 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15863 :
15864 346 : destroyPQExpBuffer(q);
15865 346 : destroyPQExpBuffer(delq);
15866 346 : destroyPQExpBuffer(query);
15867 346 : free(qdictname);
15868 : }
15869 :
15870 : /*
15871 : * dumpTSTemplate
15872 : * write out a single text search template
15873 : */
15874 : static void
15875 106 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15876 : {
15877 106 : DumpOptions *dopt = fout->dopt;
15878 : PQExpBuffer q;
15879 : PQExpBuffer delq;
15880 : char *qtmplname;
15881 :
15882 : /* Do nothing if not dumping schema */
15883 106 : if (!dopt->dumpSchema)
15884 12 : return;
15885 :
15886 94 : q = createPQExpBuffer();
15887 94 : delq = createPQExpBuffer();
15888 :
15889 94 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15890 :
15891 94 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15892 94 : fmtQualifiedDumpable(tmplinfo));
15893 :
15894 94 : if (tmplinfo->tmplinit != InvalidOid)
15895 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15896 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15897 94 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15898 94 : convertTSFunction(fout, tmplinfo->tmpllexize));
15899 :
15900 94 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15901 94 : fmtQualifiedDumpable(tmplinfo));
15902 :
15903 94 : if (dopt->binary_upgrade)
15904 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15905 : "TEXT SEARCH TEMPLATE", qtmplname,
15906 2 : tmplinfo->dobj.namespace->dobj.name);
15907 :
15908 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15909 94 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15910 94 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15911 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15912 : .description = "TEXT SEARCH TEMPLATE",
15913 : .section = SECTION_PRE_DATA,
15914 : .createStmt = q->data,
15915 : .dropStmt = delq->data));
15916 :
15917 : /* Dump Template Comments */
15918 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15919 94 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15920 94 : tmplinfo->dobj.namespace->dobj.name, "",
15921 94 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15922 :
15923 94 : destroyPQExpBuffer(q);
15924 94 : destroyPQExpBuffer(delq);
15925 94 : free(qtmplname);
15926 : }
15927 :
15928 : /*
15929 : * dumpTSConfig
15930 : * write out a single text search configuration
15931 : */
15932 : static void
15933 308 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15934 : {
15935 308 : DumpOptions *dopt = fout->dopt;
15936 : PQExpBuffer q;
15937 : PQExpBuffer delq;
15938 : PQExpBuffer query;
15939 : char *qcfgname;
15940 : PGresult *res;
15941 : char *nspname;
15942 : char *prsname;
15943 : int ntups,
15944 : i;
15945 : int i_tokenname;
15946 : int i_dictname;
15947 :
15948 : /* Do nothing if not dumping schema */
15949 308 : if (!dopt->dumpSchema)
15950 12 : return;
15951 :
15952 296 : q = createPQExpBuffer();
15953 296 : delq = createPQExpBuffer();
15954 296 : query = createPQExpBuffer();
15955 :
15956 296 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15957 :
15958 : /* Fetch name and namespace of the config's parser */
15959 296 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15960 : "FROM pg_ts_parser p, pg_namespace n "
15961 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15962 296 : cfginfo->cfgparser);
15963 296 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15964 296 : nspname = PQgetvalue(res, 0, 0);
15965 296 : prsname = PQgetvalue(res, 0, 1);
15966 :
15967 296 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15968 296 : fmtQualifiedDumpable(cfginfo));
15969 :
15970 296 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15971 296 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15972 :
15973 296 : PQclear(res);
15974 :
15975 296 : resetPQExpBuffer(query);
15976 296 : appendPQExpBuffer(query,
15977 : "SELECT\n"
15978 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15979 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15980 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15981 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15982 : "WHERE m.mapcfg = '%u'\n"
15983 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15984 296 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15985 :
15986 296 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15987 296 : ntups = PQntuples(res);
15988 :
15989 296 : i_tokenname = PQfnumber(res, "tokenname");
15990 296 : i_dictname = PQfnumber(res, "dictname");
15991 :
15992 6190 : for (i = 0; i < ntups; i++)
15993 : {
15994 5894 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15995 5894 : char *dictname = PQgetvalue(res, i, i_dictname);
15996 :
15997 5894 : if (i == 0 ||
15998 5598 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15999 : {
16000 : /* starting a new token type, so start a new command */
16001 5624 : if (i > 0)
16002 5328 : appendPQExpBufferStr(q, ";\n");
16003 5624 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16004 5624 : fmtQualifiedDumpable(cfginfo));
16005 : /* tokenname needs quoting, dictname does NOT */
16006 5624 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16007 : fmtId(tokenname), dictname);
16008 : }
16009 : else
16010 270 : appendPQExpBuffer(q, ", %s", dictname);
16011 : }
16012 :
16013 296 : if (ntups > 0)
16014 296 : appendPQExpBufferStr(q, ";\n");
16015 :
16016 296 : PQclear(res);
16017 :
16018 296 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16019 296 : fmtQualifiedDumpable(cfginfo));
16020 :
16021 296 : if (dopt->binary_upgrade)
16022 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16023 : "TEXT SEARCH CONFIGURATION", qcfgname,
16024 10 : cfginfo->dobj.namespace->dobj.name);
16025 :
16026 296 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16027 296 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16028 296 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16029 : .namespace = cfginfo->dobj.namespace->dobj.name,
16030 : .owner = cfginfo->rolname,
16031 : .description = "TEXT SEARCH CONFIGURATION",
16032 : .section = SECTION_PRE_DATA,
16033 : .createStmt = q->data,
16034 : .dropStmt = delq->data));
16035 :
16036 : /* Dump Configuration Comments */
16037 296 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16038 256 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16039 256 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16040 256 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16041 :
16042 296 : destroyPQExpBuffer(q);
16043 296 : destroyPQExpBuffer(delq);
16044 296 : destroyPQExpBuffer(query);
16045 296 : free(qcfgname);
16046 : }
16047 :
16048 : /*
16049 : * dumpForeignDataWrapper
16050 : * write out a single foreign-data wrapper definition
16051 : */
16052 : static void
16053 104 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16054 : {
16055 104 : DumpOptions *dopt = fout->dopt;
16056 : PQExpBuffer q;
16057 : PQExpBuffer delq;
16058 : char *qfdwname;
16059 :
16060 : /* Do nothing if not dumping schema */
16061 104 : if (!dopt->dumpSchema)
16062 14 : return;
16063 :
16064 90 : q = createPQExpBuffer();
16065 90 : delq = createPQExpBuffer();
16066 :
16067 90 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16068 :
16069 90 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16070 : qfdwname);
16071 :
16072 90 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16073 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16074 :
16075 90 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16076 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16077 :
16078 90 : if (strlen(fdwinfo->fdwoptions) > 0)
16079 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16080 :
16081 90 : appendPQExpBufferStr(q, ";\n");
16082 :
16083 90 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16084 : qfdwname);
16085 :
16086 90 : if (dopt->binary_upgrade)
16087 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16088 : "FOREIGN DATA WRAPPER", qfdwname,
16089 : NULL);
16090 :
16091 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16092 90 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16093 90 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16094 : .owner = fdwinfo->rolname,
16095 : .description = "FOREIGN DATA WRAPPER",
16096 : .section = SECTION_PRE_DATA,
16097 : .createStmt = q->data,
16098 : .dropStmt = delq->data));
16099 :
16100 : /* Dump Foreign Data Wrapper Comments */
16101 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16102 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16103 0 : NULL, fdwinfo->rolname,
16104 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16105 :
16106 : /* Handle the ACL */
16107 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16108 62 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16109 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16110 62 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16111 :
16112 90 : free(qfdwname);
16113 :
16114 90 : destroyPQExpBuffer(q);
16115 90 : destroyPQExpBuffer(delq);
16116 : }
16117 :
16118 : /*
16119 : * dumpForeignServer
16120 : * write out a foreign server definition
16121 : */
16122 : static void
16123 112 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16124 : {
16125 112 : DumpOptions *dopt = fout->dopt;
16126 : PQExpBuffer q;
16127 : PQExpBuffer delq;
16128 : PQExpBuffer query;
16129 : PGresult *res;
16130 : char *qsrvname;
16131 : char *fdwname;
16132 :
16133 : /* Do nothing if not dumping schema */
16134 112 : if (!dopt->dumpSchema)
16135 18 : return;
16136 :
16137 94 : q = createPQExpBuffer();
16138 94 : delq = createPQExpBuffer();
16139 94 : query = createPQExpBuffer();
16140 :
16141 94 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16142 :
16143 : /* look up the foreign-data wrapper */
16144 94 : appendPQExpBuffer(query, "SELECT fdwname "
16145 : "FROM pg_foreign_data_wrapper w "
16146 : "WHERE w.oid = '%u'",
16147 94 : srvinfo->srvfdw);
16148 94 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16149 94 : fdwname = PQgetvalue(res, 0, 0);
16150 :
16151 94 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16152 94 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16153 : {
16154 0 : appendPQExpBufferStr(q, " TYPE ");
16155 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16156 : }
16157 94 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16158 : {
16159 0 : appendPQExpBufferStr(q, " VERSION ");
16160 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16161 : }
16162 :
16163 94 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16164 94 : appendPQExpBufferStr(q, fmtId(fdwname));
16165 :
16166 94 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16167 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16168 :
16169 94 : appendPQExpBufferStr(q, ";\n");
16170 :
16171 94 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16172 : qsrvname);
16173 :
16174 94 : if (dopt->binary_upgrade)
16175 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16176 : "SERVER", qsrvname, NULL);
16177 :
16178 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16179 94 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16180 94 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16181 : .owner = srvinfo->rolname,
16182 : .description = "SERVER",
16183 : .section = SECTION_PRE_DATA,
16184 : .createStmt = q->data,
16185 : .dropStmt = delq->data));
16186 :
16187 : /* Dump Foreign Server Comments */
16188 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16189 0 : dumpComment(fout, "SERVER", qsrvname,
16190 0 : NULL, srvinfo->rolname,
16191 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16192 :
16193 : /* Handle the ACL */
16194 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16195 62 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16196 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16197 62 : NULL, srvinfo->rolname, &srvinfo->dacl);
16198 :
16199 : /* Dump user mappings */
16200 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16201 94 : dumpUserMappings(fout,
16202 94 : srvinfo->dobj.name, NULL,
16203 94 : srvinfo->rolname,
16204 94 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16205 :
16206 94 : PQclear(res);
16207 :
16208 94 : free(qsrvname);
16209 :
16210 94 : destroyPQExpBuffer(q);
16211 94 : destroyPQExpBuffer(delq);
16212 94 : destroyPQExpBuffer(query);
16213 : }
16214 :
16215 : /*
16216 : * dumpUserMappings
16217 : *
16218 : * This routine is used to dump any user mappings associated with the
16219 : * server handed to this routine. Should be called after ArchiveEntry()
16220 : * for the server.
16221 : */
16222 : static void
16223 94 : dumpUserMappings(Archive *fout,
16224 : const char *servername, const char *namespace,
16225 : const char *owner,
16226 : CatalogId catalogId, DumpId dumpId)
16227 : {
16228 : PQExpBuffer q;
16229 : PQExpBuffer delq;
16230 : PQExpBuffer query;
16231 : PQExpBuffer tag;
16232 : PGresult *res;
16233 : int ntups;
16234 : int i_usename;
16235 : int i_umoptions;
16236 : int i;
16237 :
16238 94 : q = createPQExpBuffer();
16239 94 : tag = createPQExpBuffer();
16240 94 : delq = createPQExpBuffer();
16241 94 : query = createPQExpBuffer();
16242 :
16243 : /*
16244 : * We read from the publicly accessible view pg_user_mappings, so as not
16245 : * to fail if run by a non-superuser. Note that the view will show
16246 : * umoptions as null if the user hasn't got privileges for the associated
16247 : * server; this means that pg_dump will dump such a mapping, but with no
16248 : * OPTIONS clause. A possible alternative is to skip such mappings
16249 : * altogether, but it's not clear that that's an improvement.
16250 : */
16251 94 : appendPQExpBuffer(query,
16252 : "SELECT usename, "
16253 : "array_to_string(ARRAY("
16254 : "SELECT quote_ident(option_name) || ' ' || "
16255 : "quote_literal(option_value) "
16256 : "FROM pg_options_to_table(umoptions) "
16257 : "ORDER BY option_name"
16258 : "), E',\n ') AS umoptions "
16259 : "FROM pg_user_mappings "
16260 : "WHERE srvid = '%u' "
16261 : "ORDER BY usename",
16262 : catalogId.oid);
16263 :
16264 94 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16265 :
16266 94 : ntups = PQntuples(res);
16267 94 : i_usename = PQfnumber(res, "usename");
16268 94 : i_umoptions = PQfnumber(res, "umoptions");
16269 :
16270 156 : for (i = 0; i < ntups; i++)
16271 : {
16272 : char *usename;
16273 : char *umoptions;
16274 :
16275 62 : usename = PQgetvalue(res, i, i_usename);
16276 62 : umoptions = PQgetvalue(res, i, i_umoptions);
16277 :
16278 62 : resetPQExpBuffer(q);
16279 62 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16280 62 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16281 :
16282 62 : if (umoptions && strlen(umoptions) > 0)
16283 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16284 :
16285 62 : appendPQExpBufferStr(q, ";\n");
16286 :
16287 62 : resetPQExpBuffer(delq);
16288 62 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16289 62 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16290 :
16291 62 : resetPQExpBuffer(tag);
16292 62 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16293 : usename, servername);
16294 :
16295 62 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16296 62 : ARCHIVE_OPTS(.tag = tag->data,
16297 : .namespace = namespace,
16298 : .owner = owner,
16299 : .description = "USER MAPPING",
16300 : .section = SECTION_PRE_DATA,
16301 : .createStmt = q->data,
16302 : .dropStmt = delq->data));
16303 : }
16304 :
16305 94 : PQclear(res);
16306 :
16307 94 : destroyPQExpBuffer(query);
16308 94 : destroyPQExpBuffer(delq);
16309 94 : destroyPQExpBuffer(tag);
16310 94 : destroyPQExpBuffer(q);
16311 94 : }
16312 :
16313 : /*
16314 : * Write out default privileges information
16315 : */
16316 : static void
16317 320 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16318 : {
16319 320 : DumpOptions *dopt = fout->dopt;
16320 : PQExpBuffer q;
16321 : PQExpBuffer tag;
16322 : const char *type;
16323 :
16324 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16325 320 : if (!dopt->dumpSchema || dopt->aclsSkip)
16326 56 : return;
16327 :
16328 264 : q = createPQExpBuffer();
16329 264 : tag = createPQExpBuffer();
16330 :
16331 264 : switch (daclinfo->defaclobjtype)
16332 : {
16333 122 : case DEFACLOBJ_RELATION:
16334 122 : type = "TABLES";
16335 122 : break;
16336 0 : case DEFACLOBJ_SEQUENCE:
16337 0 : type = "SEQUENCES";
16338 0 : break;
16339 122 : case DEFACLOBJ_FUNCTION:
16340 122 : type = "FUNCTIONS";
16341 122 : break;
16342 20 : case DEFACLOBJ_TYPE:
16343 20 : type = "TYPES";
16344 20 : break;
16345 0 : case DEFACLOBJ_NAMESPACE:
16346 0 : type = "SCHEMAS";
16347 0 : break;
16348 0 : case DEFACLOBJ_LARGEOBJECT:
16349 0 : type = "LARGE OBJECTS";
16350 0 : break;
16351 0 : default:
16352 : /* shouldn't get here */
16353 0 : pg_fatal("unrecognized object type in default privileges: %d",
16354 : (int) daclinfo->defaclobjtype);
16355 : type = ""; /* keep compiler quiet */
16356 : }
16357 :
16358 264 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16359 :
16360 : /* build the actual command(s) for this tuple */
16361 264 : if (!buildDefaultACLCommands(type,
16362 264 : daclinfo->dobj.namespace != NULL ?
16363 124 : daclinfo->dobj.namespace->dobj.name : NULL,
16364 264 : daclinfo->dacl.acl,
16365 264 : daclinfo->dacl.acldefault,
16366 264 : daclinfo->defaclrole,
16367 : fout->remoteVersion,
16368 : q))
16369 0 : pg_fatal("could not parse default ACL list (%s)",
16370 : daclinfo->dacl.acl);
16371 :
16372 264 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16373 264 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16374 264 : ARCHIVE_OPTS(.tag = tag->data,
16375 : .namespace = daclinfo->dobj.namespace ?
16376 : daclinfo->dobj.namespace->dobj.name : NULL,
16377 : .owner = daclinfo->defaclrole,
16378 : .description = "DEFAULT ACL",
16379 : .section = SECTION_POST_DATA,
16380 : .createStmt = q->data));
16381 :
16382 264 : destroyPQExpBuffer(tag);
16383 264 : destroyPQExpBuffer(q);
16384 : }
16385 :
16386 : /*----------
16387 : * Write out grant/revoke information
16388 : *
16389 : * 'objDumpId' is the dump ID of the underlying object.
16390 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16391 : * or InvalidDumpId if there is no need for a second dependency.
16392 : * 'type' must be one of
16393 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16394 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16395 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16396 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16397 : * (Currently we assume that subname is only provided for table columns.)
16398 : * 'nspname' is the namespace the object is in (NULL if none).
16399 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16400 : * to use the default for the object type.
16401 : * 'owner' is the owner, NULL if there is no owner (for languages).
16402 : * 'dacl' is the DumpableAcl struct for the object.
16403 : *
16404 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16405 : * no ACL entry was created.
16406 : *----------
16407 : */
16408 : static DumpId
16409 58024 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16410 : const char *type, const char *name, const char *subname,
16411 : const char *nspname, const char *tag, const char *owner,
16412 : const DumpableAcl *dacl)
16413 : {
16414 58024 : DumpId aclDumpId = InvalidDumpId;
16415 58024 : DumpOptions *dopt = fout->dopt;
16416 58024 : const char *acls = dacl->acl;
16417 58024 : const char *acldefault = dacl->acldefault;
16418 58024 : char privtype = dacl->privtype;
16419 58024 : const char *initprivs = dacl->initprivs;
16420 : const char *baseacls;
16421 : PQExpBuffer sql;
16422 :
16423 : /* Do nothing if ACL dump is not enabled */
16424 58024 : if (dopt->aclsSkip)
16425 652 : return InvalidDumpId;
16426 :
16427 : /* --data-only skips ACLs *except* large object ACLs */
16428 57372 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16429 0 : return InvalidDumpId;
16430 :
16431 57372 : sql = createPQExpBuffer();
16432 :
16433 : /*
16434 : * In binary upgrade mode, we don't run an extension's script but instead
16435 : * dump out the objects independently and then recreate them. To preserve
16436 : * any initial privileges which were set on extension objects, we need to
16437 : * compute the set of GRANT and REVOKE commands necessary to get from the
16438 : * default privileges of an object to its initial privileges as recorded
16439 : * in pg_init_privs.
16440 : *
16441 : * At restore time, we apply these commands after having called
16442 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16443 : * copy the results into pg_init_privs. This is how we preserve the
16444 : * contents of that catalog across binary upgrades.
16445 : */
16446 57372 : if (dopt->binary_upgrade && privtype == 'e' &&
16447 26 : initprivs && *initprivs != '\0')
16448 : {
16449 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16450 26 : if (!buildACLCommands(name, subname, nspname, type,
16451 : initprivs, acldefault, owner,
16452 : "", fout->remoteVersion, sql))
16453 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16454 : initprivs, acldefault, name, type);
16455 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16456 : }
16457 :
16458 : /*
16459 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16460 : * actual current ACL, starting from the initprivs if given, else from the
16461 : * object-type-specific default. Also, while buildACLCommands will assume
16462 : * that a NULL/empty acls string means it needn't do anything, what that
16463 : * actually represents is the object-type-specific default; so we need to
16464 : * substitute the acldefault string to get the right results in that case.
16465 : */
16466 57372 : if (initprivs && *initprivs != '\0')
16467 : {
16468 53916 : baseacls = initprivs;
16469 53916 : if (acls == NULL || *acls == '\0')
16470 34 : acls = acldefault;
16471 : }
16472 : else
16473 3456 : baseacls = acldefault;
16474 :
16475 57372 : if (!buildACLCommands(name, subname, nspname, type,
16476 : acls, baseacls, owner,
16477 : "", fout->remoteVersion, sql))
16478 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16479 : acls, baseacls, name, type);
16480 :
16481 57372 : if (sql->len > 0)
16482 : {
16483 3570 : PQExpBuffer tagbuf = createPQExpBuffer();
16484 : DumpId aclDeps[2];
16485 3570 : int nDeps = 0;
16486 :
16487 3570 : if (tag)
16488 0 : appendPQExpBufferStr(tagbuf, tag);
16489 3570 : else if (subname)
16490 2094 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16491 : else
16492 1476 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16493 :
16494 3570 : aclDeps[nDeps++] = objDumpId;
16495 3570 : if (altDumpId != InvalidDumpId)
16496 1926 : aclDeps[nDeps++] = altDumpId;
16497 :
16498 3570 : aclDumpId = createDumpId();
16499 :
16500 3570 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16501 3570 : ARCHIVE_OPTS(.tag = tagbuf->data,
16502 : .namespace = nspname,
16503 : .owner = owner,
16504 : .description = "ACL",
16505 : .section = SECTION_NONE,
16506 : .createStmt = sql->data,
16507 : .deps = aclDeps,
16508 : .nDeps = nDeps));
16509 :
16510 3570 : destroyPQExpBuffer(tagbuf);
16511 : }
16512 :
16513 57372 : destroyPQExpBuffer(sql);
16514 :
16515 57372 : return aclDumpId;
16516 : }
16517 :
16518 : /*
16519 : * dumpSecLabel
16520 : *
16521 : * This routine is used to dump any security labels associated with the
16522 : * object handed to this routine. The routine takes the object type
16523 : * and object name (ready to print, except for schema decoration), plus
16524 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16525 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16526 : * plus the dump ID for the object (for setting a dependency).
16527 : * If a matching pg_seclabel entry is found, it is dumped.
16528 : *
16529 : * Note: although this routine takes a dumpId for dependency purposes,
16530 : * that purpose is just to mark the dependency in the emitted dump file
16531 : * for possible future use by pg_restore. We do NOT use it for determining
16532 : * ordering of the label in the dump file, because this routine is called
16533 : * after dependency sorting occurs. This routine should be called just after
16534 : * calling ArchiveEntry() for the specified object.
16535 : */
16536 : static void
16537 20 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16538 : const char *namespace, const char *owner,
16539 : CatalogId catalogId, int subid, DumpId dumpId)
16540 : {
16541 20 : DumpOptions *dopt = fout->dopt;
16542 : SecLabelItem *labels;
16543 : int nlabels;
16544 : int i;
16545 : PQExpBuffer query;
16546 :
16547 : /* do nothing, if --no-security-labels is supplied */
16548 20 : if (dopt->no_security_labels)
16549 0 : return;
16550 :
16551 : /*
16552 : * Security labels are schema not data ... except large object labels are
16553 : * data
16554 : */
16555 20 : if (strcmp(type, "LARGE OBJECT") != 0)
16556 : {
16557 0 : if (!dopt->dumpSchema)
16558 0 : return;
16559 : }
16560 : else
16561 : {
16562 : /* We do dump large object security labels in binary-upgrade mode */
16563 20 : if (!dopt->dumpData && !dopt->binary_upgrade)
16564 0 : return;
16565 : }
16566 :
16567 : /* Search for security labels associated with catalogId, using table */
16568 20 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16569 :
16570 20 : query = createPQExpBuffer();
16571 :
16572 30 : for (i = 0; i < nlabels; i++)
16573 : {
16574 : /*
16575 : * Ignore label entries for which the subid doesn't match.
16576 : */
16577 10 : if (labels[i].objsubid != subid)
16578 0 : continue;
16579 :
16580 10 : appendPQExpBuffer(query,
16581 : "SECURITY LABEL FOR %s ON %s ",
16582 10 : fmtId(labels[i].provider), type);
16583 10 : if (namespace && *namespace)
16584 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16585 10 : appendPQExpBuffer(query, "%s IS ", name);
16586 10 : appendStringLiteralAH(query, labels[i].label, fout);
16587 10 : appendPQExpBufferStr(query, ";\n");
16588 : }
16589 :
16590 20 : if (query->len > 0)
16591 : {
16592 10 : PQExpBuffer tag = createPQExpBuffer();
16593 :
16594 10 : appendPQExpBuffer(tag, "%s %s", type, name);
16595 10 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16596 10 : ARCHIVE_OPTS(.tag = tag->data,
16597 : .namespace = namespace,
16598 : .owner = owner,
16599 : .description = "SECURITY LABEL",
16600 : .section = SECTION_NONE,
16601 : .createStmt = query->data,
16602 : .deps = &dumpId,
16603 : .nDeps = 1));
16604 10 : destroyPQExpBuffer(tag);
16605 : }
16606 :
16607 20 : destroyPQExpBuffer(query);
16608 : }
16609 :
16610 : /*
16611 : * dumpTableSecLabel
16612 : *
16613 : * As above, but dump security label for both the specified table (or view)
16614 : * and its columns.
16615 : */
16616 : static void
16617 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16618 : {
16619 0 : DumpOptions *dopt = fout->dopt;
16620 : SecLabelItem *labels;
16621 : int nlabels;
16622 : int i;
16623 : PQExpBuffer query;
16624 : PQExpBuffer target;
16625 :
16626 : /* do nothing, if --no-security-labels is supplied */
16627 0 : if (dopt->no_security_labels)
16628 0 : return;
16629 :
16630 : /* SecLabel are SCHEMA not data */
16631 0 : if (!dopt->dumpSchema)
16632 0 : return;
16633 :
16634 : /* Search for comments associated with relation, using table */
16635 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16636 0 : tbinfo->dobj.catId.oid,
16637 : &labels);
16638 :
16639 : /* If security labels exist, build SECURITY LABEL statements */
16640 0 : if (nlabels <= 0)
16641 0 : return;
16642 :
16643 0 : query = createPQExpBuffer();
16644 0 : target = createPQExpBuffer();
16645 :
16646 0 : for (i = 0; i < nlabels; i++)
16647 : {
16648 : const char *colname;
16649 0 : const char *provider = labels[i].provider;
16650 0 : const char *label = labels[i].label;
16651 0 : int objsubid = labels[i].objsubid;
16652 :
16653 0 : resetPQExpBuffer(target);
16654 0 : if (objsubid == 0)
16655 : {
16656 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16657 0 : fmtQualifiedDumpable(tbinfo));
16658 : }
16659 : else
16660 : {
16661 0 : colname = getAttrName(objsubid, tbinfo);
16662 : /* first fmtXXX result must be consumed before calling again */
16663 0 : appendPQExpBuffer(target, "COLUMN %s",
16664 0 : fmtQualifiedDumpable(tbinfo));
16665 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16666 : }
16667 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16668 : fmtId(provider), target->data);
16669 0 : appendStringLiteralAH(query, label, fout);
16670 0 : appendPQExpBufferStr(query, ";\n");
16671 : }
16672 0 : if (query->len > 0)
16673 : {
16674 0 : resetPQExpBuffer(target);
16675 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16676 0 : fmtId(tbinfo->dobj.name));
16677 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16678 0 : ARCHIVE_OPTS(.tag = target->data,
16679 : .namespace = tbinfo->dobj.namespace->dobj.name,
16680 : .owner = tbinfo->rolname,
16681 : .description = "SECURITY LABEL",
16682 : .section = SECTION_NONE,
16683 : .createStmt = query->data,
16684 : .deps = &(tbinfo->dobj.dumpId),
16685 : .nDeps = 1));
16686 : }
16687 0 : destroyPQExpBuffer(query);
16688 0 : destroyPQExpBuffer(target);
16689 : }
16690 :
16691 : /*
16692 : * findSecLabels
16693 : *
16694 : * Find the security label(s), if any, associated with the given object.
16695 : * All the objsubid values associated with the given classoid/objoid are
16696 : * found with one search.
16697 : */
16698 : static int
16699 20 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16700 : {
16701 20 : SecLabelItem *middle = NULL;
16702 : SecLabelItem *low;
16703 : SecLabelItem *high;
16704 : int nmatch;
16705 :
16706 20 : if (nseclabels <= 0) /* no labels, so no match is possible */
16707 : {
16708 0 : *items = NULL;
16709 0 : return 0;
16710 : }
16711 :
16712 : /*
16713 : * Do binary search to find some item matching the object.
16714 : */
16715 20 : low = &seclabels[0];
16716 20 : high = &seclabels[nseclabels - 1];
16717 30 : while (low <= high)
16718 : {
16719 20 : middle = low + (high - low) / 2;
16720 :
16721 20 : if (classoid < middle->classoid)
16722 0 : high = middle - 1;
16723 20 : else if (classoid > middle->classoid)
16724 0 : low = middle + 1;
16725 20 : else if (objoid < middle->objoid)
16726 10 : high = middle - 1;
16727 10 : else if (objoid > middle->objoid)
16728 0 : low = middle + 1;
16729 : else
16730 10 : break; /* found a match */
16731 : }
16732 :
16733 20 : if (low > high) /* no matches */
16734 : {
16735 10 : *items = NULL;
16736 10 : return 0;
16737 : }
16738 :
16739 : /*
16740 : * Now determine how many items match the object. The search loop
16741 : * invariant still holds: only items between low and high inclusive could
16742 : * match.
16743 : */
16744 10 : nmatch = 1;
16745 10 : while (middle > low)
16746 : {
16747 0 : if (classoid != middle[-1].classoid ||
16748 0 : objoid != middle[-1].objoid)
16749 : break;
16750 0 : middle--;
16751 0 : nmatch++;
16752 : }
16753 :
16754 10 : *items = middle;
16755 :
16756 10 : middle += nmatch;
16757 10 : while (middle <= high)
16758 : {
16759 0 : if (classoid != middle->classoid ||
16760 0 : objoid != middle->objoid)
16761 : break;
16762 0 : middle++;
16763 0 : nmatch++;
16764 : }
16765 :
16766 10 : return nmatch;
16767 : }
16768 :
16769 : /*
16770 : * collectSecLabels
16771 : *
16772 : * Construct a table of all security labels available for database objects;
16773 : * also set the has-seclabel component flag for each relevant object.
16774 : *
16775 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16776 : */
16777 : static void
16778 376 : collectSecLabels(Archive *fout)
16779 : {
16780 : PGresult *res;
16781 : PQExpBuffer query;
16782 : int i_label;
16783 : int i_provider;
16784 : int i_classoid;
16785 : int i_objoid;
16786 : int i_objsubid;
16787 : int ntups;
16788 : int i;
16789 : DumpableObject *dobj;
16790 :
16791 376 : query = createPQExpBuffer();
16792 :
16793 376 : appendPQExpBufferStr(query,
16794 : "SELECT label, provider, classoid, objoid, objsubid "
16795 : "FROM pg_catalog.pg_seclabels "
16796 : "ORDER BY classoid, objoid, objsubid");
16797 :
16798 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16799 :
16800 : /* Construct lookup table containing OIDs in numeric form */
16801 376 : i_label = PQfnumber(res, "label");
16802 376 : i_provider = PQfnumber(res, "provider");
16803 376 : i_classoid = PQfnumber(res, "classoid");
16804 376 : i_objoid = PQfnumber(res, "objoid");
16805 376 : i_objsubid = PQfnumber(res, "objsubid");
16806 :
16807 376 : ntups = PQntuples(res);
16808 :
16809 376 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16810 376 : nseclabels = 0;
16811 376 : dobj = NULL;
16812 :
16813 386 : for (i = 0; i < ntups; i++)
16814 : {
16815 : CatalogId objId;
16816 : int subid;
16817 :
16818 10 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16819 10 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16820 10 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16821 :
16822 : /* We needn't remember labels that don't match any dumpable object */
16823 10 : if (dobj == NULL ||
16824 0 : dobj->catId.tableoid != objId.tableoid ||
16825 0 : dobj->catId.oid != objId.oid)
16826 10 : dobj = findObjectByCatalogId(objId);
16827 10 : if (dobj == NULL)
16828 0 : continue;
16829 :
16830 : /*
16831 : * Labels on columns of composite types are linked to the type's
16832 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16833 : * in the type's own DumpableObject.
16834 : */
16835 10 : if (subid != 0 && dobj->objType == DO_TABLE &&
16836 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16837 0 : {
16838 : TypeInfo *cTypeInfo;
16839 :
16840 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16841 0 : if (cTypeInfo)
16842 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16843 : }
16844 : else
16845 10 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16846 :
16847 10 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16848 10 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16849 10 : seclabels[nseclabels].classoid = objId.tableoid;
16850 10 : seclabels[nseclabels].objoid = objId.oid;
16851 10 : seclabels[nseclabels].objsubid = subid;
16852 10 : nseclabels++;
16853 : }
16854 :
16855 376 : PQclear(res);
16856 376 : destroyPQExpBuffer(query);
16857 376 : }
16858 :
16859 : /*
16860 : * dumpTable
16861 : * write out to fout the declarations (not data) of a user-defined table
16862 : */
16863 : static void
16864 62652 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16865 : {
16866 62652 : DumpOptions *dopt = fout->dopt;
16867 62652 : DumpId tableAclDumpId = InvalidDumpId;
16868 : char *namecopy;
16869 :
16870 : /* Do nothing if not dumping schema */
16871 62652 : if (!dopt->dumpSchema)
16872 3016 : return;
16873 :
16874 59636 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16875 : {
16876 13194 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16877 750 : dumpSequence(fout, tbinfo);
16878 : else
16879 12444 : dumpTableSchema(fout, tbinfo);
16880 : }
16881 :
16882 : /* Handle the ACL here */
16883 59636 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16884 59636 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16885 : {
16886 47884 : const char *objtype =
16887 47884 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16888 :
16889 : tableAclDumpId =
16890 47884 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16891 : objtype, namecopy, NULL,
16892 47884 : tbinfo->dobj.namespace->dobj.name,
16893 47884 : NULL, tbinfo->rolname, &tbinfo->dacl);
16894 : }
16895 :
16896 : /*
16897 : * Handle column ACLs, if any. Note: we pull these with a separate query
16898 : * rather than trying to fetch them during getTableAttrs, so that we won't
16899 : * miss ACLs on system columns. Doing it this way also allows us to dump
16900 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16901 : */
16902 59636 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16903 : {
16904 566 : PQExpBuffer query = createPQExpBuffer();
16905 : PGresult *res;
16906 : int i;
16907 :
16908 566 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16909 : {
16910 : /* Set up query for column ACLs */
16911 324 : appendPQExpBufferStr(query,
16912 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16913 :
16914 324 : if (fout->remoteVersion >= 90600)
16915 : {
16916 : /*
16917 : * In principle we should call acldefault('c', relowner) to
16918 : * get the default ACL for a column. However, we don't
16919 : * currently store the numeric OID of the relowner in
16920 : * TableInfo. We could convert the owner name using regrole,
16921 : * but that creates a risk of failure due to concurrent role
16922 : * renames. Given that the default ACL for columns is empty
16923 : * and is likely to stay that way, it's not worth extra cycles
16924 : * and risk to avoid hard-wiring that knowledge here.
16925 : */
16926 324 : appendPQExpBufferStr(query,
16927 : "SELECT at.attname, "
16928 : "at.attacl, "
16929 : "'{}' AS acldefault, "
16930 : "pip.privtype, pip.initprivs "
16931 : "FROM pg_catalog.pg_attribute at "
16932 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16933 : "(at.attrelid = pip.objoid "
16934 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16935 : "AND at.attnum = pip.objsubid) "
16936 : "WHERE at.attrelid = $1 AND "
16937 : "NOT at.attisdropped "
16938 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16939 : "ORDER BY at.attnum");
16940 : }
16941 : else
16942 : {
16943 0 : appendPQExpBufferStr(query,
16944 : "SELECT attname, attacl, '{}' AS acldefault, "
16945 : "NULL AS privtype, NULL AS initprivs "
16946 : "FROM pg_catalog.pg_attribute "
16947 : "WHERE attrelid = $1 AND NOT attisdropped "
16948 : "AND attacl IS NOT NULL "
16949 : "ORDER BY attnum");
16950 : }
16951 :
16952 324 : ExecuteSqlStatement(fout, query->data);
16953 :
16954 324 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16955 : }
16956 :
16957 566 : printfPQExpBuffer(query,
16958 : "EXECUTE getColumnACLs('%u')",
16959 566 : tbinfo->dobj.catId.oid);
16960 :
16961 566 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16962 :
16963 9020 : for (i = 0; i < PQntuples(res); i++)
16964 : {
16965 8454 : char *attname = PQgetvalue(res, i, 0);
16966 8454 : char *attacl = PQgetvalue(res, i, 1);
16967 8454 : char *acldefault = PQgetvalue(res, i, 2);
16968 8454 : char privtype = *(PQgetvalue(res, i, 3));
16969 8454 : char *initprivs = PQgetvalue(res, i, 4);
16970 : DumpableAcl coldacl;
16971 : char *attnamecopy;
16972 :
16973 8454 : coldacl.acl = attacl;
16974 8454 : coldacl.acldefault = acldefault;
16975 8454 : coldacl.privtype = privtype;
16976 8454 : coldacl.initprivs = initprivs;
16977 8454 : attnamecopy = pg_strdup(fmtId(attname));
16978 :
16979 : /*
16980 : * Column's GRANT type is always TABLE. Each column ACL depends
16981 : * on the table-level ACL, since we can restore column ACLs in
16982 : * parallel but the table-level ACL has to be done first.
16983 : */
16984 8454 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16985 : "TABLE", namecopy, attnamecopy,
16986 8454 : tbinfo->dobj.namespace->dobj.name,
16987 8454 : NULL, tbinfo->rolname, &coldacl);
16988 8454 : free(attnamecopy);
16989 : }
16990 566 : PQclear(res);
16991 566 : destroyPQExpBuffer(query);
16992 : }
16993 :
16994 59636 : free(namecopy);
16995 : }
16996 :
16997 : /*
16998 : * Create the AS clause for a view or materialized view. The semicolon is
16999 : * stripped because a materialized view must add a WITH NO DATA clause.
17000 : *
17001 : * This returns a new buffer which must be freed by the caller.
17002 : */
17003 : static PQExpBuffer
17004 1736 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
17005 : {
17006 1736 : PQExpBuffer query = createPQExpBuffer();
17007 1736 : PQExpBuffer result = createPQExpBuffer();
17008 : PGresult *res;
17009 : int len;
17010 :
17011 : /* Fetch the view definition */
17012 1736 : appendPQExpBuffer(query,
17013 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17014 1736 : tbinfo->dobj.catId.oid);
17015 :
17016 1736 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17017 :
17018 1736 : if (PQntuples(res) != 1)
17019 : {
17020 0 : if (PQntuples(res) < 1)
17021 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17022 : tbinfo->dobj.name);
17023 : else
17024 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17025 : tbinfo->dobj.name);
17026 : }
17027 :
17028 1736 : len = PQgetlength(res, 0, 0);
17029 :
17030 1736 : if (len == 0)
17031 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17032 : tbinfo->dobj.name);
17033 :
17034 : /* Strip off the trailing semicolon so that other things may follow. */
17035 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17036 1736 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17037 :
17038 1736 : PQclear(res);
17039 1736 : destroyPQExpBuffer(query);
17040 :
17041 1736 : return result;
17042 : }
17043 :
17044 : /*
17045 : * Create a dummy AS clause for a view. This is used when the real view
17046 : * definition has to be postponed because of circular dependencies.
17047 : * We must duplicate the view's external properties -- column names and types
17048 : * (including collation) -- so that it works for subsequent references.
17049 : *
17050 : * This returns a new buffer which must be freed by the caller.
17051 : */
17052 : static PQExpBuffer
17053 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17054 : {
17055 40 : PQExpBuffer result = createPQExpBuffer();
17056 : int j;
17057 :
17058 40 : appendPQExpBufferStr(result, "SELECT");
17059 :
17060 80 : for (j = 0; j < tbinfo->numatts; j++)
17061 : {
17062 40 : if (j > 0)
17063 20 : appendPQExpBufferChar(result, ',');
17064 40 : appendPQExpBufferStr(result, "\n ");
17065 :
17066 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17067 :
17068 : /*
17069 : * Must add collation if not default for the type, because CREATE OR
17070 : * REPLACE VIEW won't change it
17071 : */
17072 40 : if (OidIsValid(tbinfo->attcollation[j]))
17073 : {
17074 : CollInfo *coll;
17075 :
17076 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17077 0 : if (coll)
17078 0 : appendPQExpBuffer(result, " COLLATE %s",
17079 0 : fmtQualifiedDumpable(coll));
17080 : }
17081 :
17082 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17083 : }
17084 :
17085 40 : return result;
17086 : }
17087 :
17088 : /*
17089 : * dumpTableSchema
17090 : * write the declaration (not data) of one user-defined table or view
17091 : */
17092 : static void
17093 12444 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17094 : {
17095 12444 : DumpOptions *dopt = fout->dopt;
17096 12444 : PQExpBuffer q = createPQExpBuffer();
17097 12444 : PQExpBuffer delq = createPQExpBuffer();
17098 12444 : PQExpBuffer extra = createPQExpBuffer();
17099 : char *qrelname;
17100 : char *qualrelname;
17101 : int numParents;
17102 : TableInfo **parents;
17103 : int actual_atts; /* number of attrs in this CREATE statement */
17104 : const char *reltypename;
17105 : char *storage;
17106 : int j,
17107 : k;
17108 :
17109 : /* We had better have loaded per-column details about this table */
17110 : Assert(tbinfo->interesting);
17111 :
17112 12444 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17113 12444 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17114 :
17115 12444 : if (tbinfo->hasoids)
17116 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17117 : qrelname);
17118 :
17119 12444 : if (dopt->binary_upgrade)
17120 1742 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17121 :
17122 : /* Is it a table or a view? */
17123 12444 : if (tbinfo->relkind == RELKIND_VIEW)
17124 : {
17125 : PQExpBuffer result;
17126 :
17127 : /*
17128 : * Note: keep this code in sync with the is_view case in dumpRule()
17129 : */
17130 :
17131 1066 : reltypename = "VIEW";
17132 :
17133 1066 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17134 :
17135 1066 : if (dopt->binary_upgrade)
17136 104 : binary_upgrade_set_pg_class_oids(fout, q,
17137 104 : tbinfo->dobj.catId.oid);
17138 :
17139 1066 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17140 :
17141 1066 : if (tbinfo->dummy_view)
17142 20 : result = createDummyViewAsClause(fout, tbinfo);
17143 : else
17144 : {
17145 1046 : if (nonemptyReloptions(tbinfo->reloptions))
17146 : {
17147 122 : appendPQExpBufferStr(q, " WITH (");
17148 122 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17149 122 : appendPQExpBufferChar(q, ')');
17150 : }
17151 1046 : result = createViewAsClause(fout, tbinfo);
17152 : }
17153 1066 : appendPQExpBuffer(q, " AS\n%s", result->data);
17154 1066 : destroyPQExpBuffer(result);
17155 :
17156 1066 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17157 64 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17158 1066 : appendPQExpBufferStr(q, ";\n");
17159 : }
17160 : else
17161 : {
17162 11378 : char *partkeydef = NULL;
17163 11378 : char *ftoptions = NULL;
17164 11378 : char *srvname = NULL;
17165 11378 : const char *foreign = "";
17166 :
17167 : /*
17168 : * Set reltypename, and collect any relkind-specific data that we
17169 : * didn't fetch during getTables().
17170 : */
17171 11378 : switch (tbinfo->relkind)
17172 : {
17173 1152 : case RELKIND_PARTITIONED_TABLE:
17174 : {
17175 1152 : PQExpBuffer query = createPQExpBuffer();
17176 : PGresult *res;
17177 :
17178 1152 : reltypename = "TABLE";
17179 :
17180 : /* retrieve partition key definition */
17181 1152 : appendPQExpBuffer(query,
17182 : "SELECT pg_get_partkeydef('%u')",
17183 1152 : tbinfo->dobj.catId.oid);
17184 1152 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17185 1152 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17186 1152 : PQclear(res);
17187 1152 : destroyPQExpBuffer(query);
17188 1152 : break;
17189 : }
17190 68 : case RELKIND_FOREIGN_TABLE:
17191 : {
17192 68 : PQExpBuffer query = createPQExpBuffer();
17193 : PGresult *res;
17194 : int i_srvname;
17195 : int i_ftoptions;
17196 :
17197 68 : reltypename = "FOREIGN TABLE";
17198 :
17199 : /* retrieve name of foreign server and generic options */
17200 68 : appendPQExpBuffer(query,
17201 : "SELECT fs.srvname, "
17202 : "pg_catalog.array_to_string(ARRAY("
17203 : "SELECT pg_catalog.quote_ident(option_name) || "
17204 : "' ' || pg_catalog.quote_literal(option_value) "
17205 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17206 : "ORDER BY option_name"
17207 : "), E',\n ') AS ftoptions "
17208 : "FROM pg_catalog.pg_foreign_table ft "
17209 : "JOIN pg_catalog.pg_foreign_server fs "
17210 : "ON (fs.oid = ft.ftserver) "
17211 : "WHERE ft.ftrelid = '%u'",
17212 68 : tbinfo->dobj.catId.oid);
17213 68 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17214 68 : i_srvname = PQfnumber(res, "srvname");
17215 68 : i_ftoptions = PQfnumber(res, "ftoptions");
17216 68 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17217 68 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17218 68 : PQclear(res);
17219 68 : destroyPQExpBuffer(query);
17220 :
17221 68 : foreign = "FOREIGN ";
17222 68 : break;
17223 : }
17224 670 : case RELKIND_MATVIEW:
17225 670 : reltypename = "MATERIALIZED VIEW";
17226 670 : break;
17227 9488 : default:
17228 9488 : reltypename = "TABLE";
17229 9488 : break;
17230 : }
17231 :
17232 11378 : numParents = tbinfo->numParents;
17233 11378 : parents = tbinfo->parents;
17234 :
17235 11378 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17236 :
17237 11378 : if (dopt->binary_upgrade)
17238 1638 : binary_upgrade_set_pg_class_oids(fout, q,
17239 1638 : tbinfo->dobj.catId.oid);
17240 :
17241 : /*
17242 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17243 : * ignore it when dumping if it was set in this case.
17244 : */
17245 11378 : appendPQExpBuffer(q, "CREATE %s%s %s",
17246 11378 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17247 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17248 : "UNLOGGED " : "",
17249 : reltypename,
17250 : qualrelname);
17251 :
17252 : /*
17253 : * Attach to type, if reloftype; except in case of a binary upgrade,
17254 : * we dump the table normally and attach it to the type afterward.
17255 : */
17256 11378 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17257 48 : appendPQExpBuffer(q, " OF %s",
17258 48 : getFormattedTypeName(fout, tbinfo->reloftype,
17259 : zeroIsError));
17260 :
17261 11378 : if (tbinfo->relkind != RELKIND_MATVIEW)
17262 : {
17263 : /* Dump the attributes */
17264 10708 : actual_atts = 0;
17265 50326 : for (j = 0; j < tbinfo->numatts; j++)
17266 : {
17267 : /*
17268 : * Normally, dump if it's locally defined in this table, and
17269 : * not dropped. But for binary upgrade, we'll dump all the
17270 : * columns, and then fix up the dropped and nonlocal cases
17271 : * below.
17272 : */
17273 39618 : if (shouldPrintColumn(dopt, tbinfo, j))
17274 : {
17275 : bool print_default;
17276 : bool print_notnull;
17277 :
17278 : /*
17279 : * Default value --- suppress if to be printed separately
17280 : * or not at all.
17281 : */
17282 77390 : print_default = (tbinfo->attrdefs[j] != NULL &&
17283 39644 : tbinfo->attrdefs[j]->dobj.dump &&
17284 1992 : !tbinfo->attrdefs[j]->separate);
17285 :
17286 : /*
17287 : * Not Null constraint --- print it if it is locally
17288 : * defined, or if binary upgrade. (In the latter case, we
17289 : * reset conislocal below.)
17290 : */
17291 42114 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17292 4462 : (tbinfo->notnull_islocal[j] ||
17293 1234 : dopt->binary_upgrade ||
17294 1062 : tbinfo->ispartition));
17295 :
17296 : /*
17297 : * Skip column if fully defined by reloftype, except in
17298 : * binary upgrade
17299 : */
17300 37652 : if (OidIsValid(tbinfo->reloftype) &&
17301 100 : !print_default && !print_notnull &&
17302 60 : !dopt->binary_upgrade)
17303 48 : continue;
17304 :
17305 : /* Format properly if not first attr */
17306 37604 : if (actual_atts == 0)
17307 10056 : appendPQExpBufferStr(q, " (");
17308 : else
17309 27548 : appendPQExpBufferChar(q, ',');
17310 37604 : appendPQExpBufferStr(q, "\n ");
17311 37604 : actual_atts++;
17312 :
17313 : /* Attribute name */
17314 37604 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17315 :
17316 37604 : if (tbinfo->attisdropped[j])
17317 : {
17318 : /*
17319 : * ALTER TABLE DROP COLUMN clears
17320 : * pg_attribute.atttypid, so we will not have gotten a
17321 : * valid type name; insert INTEGER as a stopgap. We'll
17322 : * clean things up later.
17323 : */
17324 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17325 : /* and skip to the next column */
17326 168 : continue;
17327 : }
17328 :
17329 : /*
17330 : * Attribute type; print it except when creating a typed
17331 : * table ('OF type_name'), but in binary-upgrade mode,
17332 : * print it in that case too.
17333 : */
17334 37436 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17335 : {
17336 37404 : appendPQExpBuffer(q, " %s",
17337 37404 : tbinfo->atttypnames[j]);
17338 : }
17339 :
17340 37436 : if (print_default)
17341 : {
17342 1732 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17343 554 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17344 554 : tbinfo->attrdefs[j]->adef_expr);
17345 1178 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17346 446 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17347 446 : tbinfo->attrdefs[j]->adef_expr);
17348 : else
17349 732 : appendPQExpBuffer(q, " DEFAULT %s",
17350 732 : tbinfo->attrdefs[j]->adef_expr);
17351 : }
17352 :
17353 37436 : if (print_notnull)
17354 : {
17355 4400 : if (tbinfo->notnull_constrs[j][0] == '\0')
17356 3112 : appendPQExpBufferStr(q, " NOT NULL");
17357 : else
17358 1288 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17359 1288 : fmtId(tbinfo->notnull_constrs[j]));
17360 :
17361 4400 : if (tbinfo->notnull_noinh[j])
17362 0 : appendPQExpBufferStr(q, " NO INHERIT");
17363 : }
17364 :
17365 : /* Add collation if not default for the type */
17366 37436 : if (OidIsValid(tbinfo->attcollation[j]))
17367 : {
17368 : CollInfo *coll;
17369 :
17370 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
17371 394 : if (coll)
17372 394 : appendPQExpBuffer(q, " COLLATE %s",
17373 394 : fmtQualifiedDumpable(coll));
17374 : }
17375 : }
17376 :
17377 : /*
17378 : * On the other hand, if we choose not to print a column
17379 : * (likely because it is created by inheritance), but the
17380 : * column has a locally-defined not-null constraint, we need
17381 : * to dump the constraint as a standalone object.
17382 : *
17383 : * This syntax isn't SQL-conforming, but if you wanted
17384 : * standard output you wouldn't be creating non-standard
17385 : * objects to begin with.
17386 : */
17387 39402 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17388 1966 : !tbinfo->attisdropped[j] &&
17389 1240 : tbinfo->notnull_constrs[j] != NULL &&
17390 354 : tbinfo->notnull_islocal[j])
17391 : {
17392 : /* Format properly if not first attr */
17393 118 : if (actual_atts == 0)
17394 110 : appendPQExpBufferStr(q, " (");
17395 : else
17396 8 : appendPQExpBufferChar(q, ',');
17397 118 : appendPQExpBufferStr(q, "\n ");
17398 118 : actual_atts++;
17399 :
17400 118 : if (tbinfo->notnull_constrs[j][0] == '\0')
17401 16 : appendPQExpBuffer(q, "NOT NULL %s",
17402 16 : fmtId(tbinfo->attnames[j]));
17403 : else
17404 204 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17405 102 : tbinfo->notnull_constrs[j],
17406 102 : fmtId(tbinfo->attnames[j]));
17407 : }
17408 : }
17409 :
17410 : /*
17411 : * Add non-inherited CHECK constraints, if any.
17412 : *
17413 : * For partitions, we need to include check constraints even if
17414 : * they're not defined locally, because the ALTER TABLE ATTACH
17415 : * PARTITION that we'll emit later expects the constraint to be
17416 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17417 : */
17418 11854 : for (j = 0; j < tbinfo->ncheck; j++)
17419 : {
17420 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17421 :
17422 1146 : if (constr->separate ||
17423 1006 : (!constr->conislocal && !tbinfo->ispartition))
17424 214 : continue;
17425 :
17426 932 : if (actual_atts == 0)
17427 32 : appendPQExpBufferStr(q, " (\n ");
17428 : else
17429 900 : appendPQExpBufferStr(q, ",\n ");
17430 :
17431 932 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17432 932 : fmtId(constr->dobj.name));
17433 932 : appendPQExpBufferStr(q, constr->condef);
17434 :
17435 932 : actual_atts++;
17436 : }
17437 :
17438 10708 : if (actual_atts)
17439 10198 : appendPQExpBufferStr(q, "\n)");
17440 510 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17441 : {
17442 : /*
17443 : * No attributes? we must have a parenthesized attribute list,
17444 : * even though empty, when not using the OF TYPE syntax.
17445 : */
17446 486 : appendPQExpBufferStr(q, " (\n)");
17447 : }
17448 :
17449 : /*
17450 : * Emit the INHERITS clause (not for partitions), except in
17451 : * binary-upgrade mode.
17452 : */
17453 10708 : if (numParents > 0 && !tbinfo->ispartition &&
17454 978 : !dopt->binary_upgrade)
17455 : {
17456 850 : appendPQExpBufferStr(q, "\nINHERITS (");
17457 1842 : for (k = 0; k < numParents; k++)
17458 : {
17459 992 : TableInfo *parentRel = parents[k];
17460 :
17461 992 : if (k > 0)
17462 142 : appendPQExpBufferStr(q, ", ");
17463 992 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17464 : }
17465 850 : appendPQExpBufferChar(q, ')');
17466 : }
17467 :
17468 10708 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17469 1152 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17470 :
17471 10708 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17472 68 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17473 : }
17474 :
17475 22470 : if (nonemptyReloptions(tbinfo->reloptions) ||
17476 11092 : nonemptyReloptions(tbinfo->toast_reloptions))
17477 : {
17478 286 : bool addcomma = false;
17479 :
17480 286 : appendPQExpBufferStr(q, "\nWITH (");
17481 286 : if (nonemptyReloptions(tbinfo->reloptions))
17482 : {
17483 286 : addcomma = true;
17484 286 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17485 : }
17486 286 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17487 : {
17488 10 : if (addcomma)
17489 10 : appendPQExpBufferStr(q, ", ");
17490 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17491 : fout);
17492 : }
17493 286 : appendPQExpBufferChar(q, ')');
17494 : }
17495 :
17496 : /* Dump generic options if any */
17497 11378 : if (ftoptions && ftoptions[0])
17498 64 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17499 :
17500 : /*
17501 : * For materialized views, create the AS clause just like a view. At
17502 : * this point, we always mark the view as not populated.
17503 : */
17504 11378 : if (tbinfo->relkind == RELKIND_MATVIEW)
17505 : {
17506 : PQExpBuffer result;
17507 :
17508 670 : result = createViewAsClause(fout, tbinfo);
17509 670 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17510 : result->data);
17511 670 : destroyPQExpBuffer(result);
17512 : }
17513 : else
17514 10708 : appendPQExpBufferStr(q, ";\n");
17515 :
17516 : /* Materialized views can depend on extensions */
17517 11378 : if (tbinfo->relkind == RELKIND_MATVIEW)
17518 670 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17519 : "pg_catalog.pg_class",
17520 : "MATERIALIZED VIEW",
17521 : qualrelname);
17522 :
17523 : /*
17524 : * in binary upgrade mode, update the catalog with any missing values
17525 : * that might be present.
17526 : */
17527 11378 : if (dopt->binary_upgrade)
17528 : {
17529 7930 : for (j = 0; j < tbinfo->numatts; j++)
17530 : {
17531 6292 : if (tbinfo->attmissingval[j][0] != '\0')
17532 : {
17533 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17534 4 : appendPQExpBufferStr(q,
17535 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17536 4 : appendStringLiteralAH(q, qualrelname, fout);
17537 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17538 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17539 4 : appendPQExpBufferChar(q, ',');
17540 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17541 4 : appendPQExpBufferStr(q, ");\n\n");
17542 : }
17543 : }
17544 : }
17545 :
17546 : /*
17547 : * To create binary-compatible heap files, we have to ensure the same
17548 : * physical column order, including dropped columns, as in the
17549 : * original. Therefore, we create dropped columns above and drop them
17550 : * here, also updating their attlen/attalign values so that the
17551 : * dropped column can be skipped properly. (We do not bother with
17552 : * restoring the original attbyval setting.) Also, inheritance
17553 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17554 : * using an INHERITS clause --- the latter would possibly mess up the
17555 : * column order. That also means we have to take care about setting
17556 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17557 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17558 : *
17559 : * We process foreign and partitioned tables here, even though they
17560 : * lack heap storage, because they can participate in inheritance
17561 : * relationships and we want this stuff to be consistent across the
17562 : * inheritance tree. We can exclude indexes, toast tables, sequences
17563 : * and matviews, even though they have storage, because we don't
17564 : * support altering or dropping columns in them, nor can they be part
17565 : * of inheritance trees.
17566 : */
17567 11378 : if (dopt->binary_upgrade &&
17568 1638 : (tbinfo->relkind == RELKIND_RELATION ||
17569 222 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17570 220 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17571 : {
17572 : bool firstitem;
17573 : bool firstitem_extra;
17574 :
17575 : /*
17576 : * Drop any dropped columns. Merge the pg_attribute manipulations
17577 : * into a single SQL command, so that we don't cause repeated
17578 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17579 : * relcache bloat while dropping N columns.
17580 : */
17581 1604 : resetPQExpBuffer(extra);
17582 1604 : firstitem = true;
17583 7854 : for (j = 0; j < tbinfo->numatts; j++)
17584 : {
17585 6250 : if (tbinfo->attisdropped[j])
17586 : {
17587 168 : if (firstitem)
17588 : {
17589 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17590 : "UPDATE pg_catalog.pg_attribute\n"
17591 : "SET attlen = v.dlen, "
17592 : "attalign = v.dalign, "
17593 : "attbyval = false\n"
17594 : "FROM (VALUES ");
17595 76 : firstitem = false;
17596 : }
17597 : else
17598 92 : appendPQExpBufferStr(q, ",\n ");
17599 168 : appendPQExpBufferChar(q, '(');
17600 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17601 168 : appendPQExpBuffer(q, ", %d, '%c')",
17602 168 : tbinfo->attlen[j],
17603 168 : tbinfo->attalign[j]);
17604 : /* The ALTER ... DROP COLUMN commands must come after */
17605 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17606 : foreign, qualrelname);
17607 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17608 168 : fmtId(tbinfo->attnames[j]));
17609 : }
17610 : }
17611 1604 : if (!firstitem)
17612 : {
17613 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17614 : "WHERE attrelid = ");
17615 76 : appendStringLiteralAH(q, qualrelname, fout);
17616 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17617 : " AND attname = v.dname;\n");
17618 : /* Now we can issue the actual DROP COLUMN commands */
17619 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17620 : }
17621 :
17622 : /*
17623 : * Fix up inherited columns. As above, do the pg_attribute
17624 : * manipulations in a single SQL command.
17625 : */
17626 1604 : firstitem = true;
17627 7854 : for (j = 0; j < tbinfo->numatts; j++)
17628 : {
17629 6250 : if (!tbinfo->attisdropped[j] &&
17630 6082 : !tbinfo->attislocal[j])
17631 : {
17632 1212 : if (firstitem)
17633 : {
17634 538 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17635 538 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17636 : "SET attislocal = false\n"
17637 : "WHERE attrelid = ");
17638 538 : appendStringLiteralAH(q, qualrelname, fout);
17639 538 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17640 : " AND attname IN (");
17641 538 : firstitem = false;
17642 : }
17643 : else
17644 674 : appendPQExpBufferStr(q, ", ");
17645 1212 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17646 : }
17647 : }
17648 1604 : if (!firstitem)
17649 538 : appendPQExpBufferStr(q, ");\n");
17650 :
17651 : /*
17652 : * Fix up not-null constraints that come from inheritance. As
17653 : * above, do the pg_constraint manipulations in a single SQL
17654 : * command. (Actually, two in special cases, if we're doing an
17655 : * upgrade from < 18).
17656 : */
17657 1604 : firstitem = true;
17658 1604 : firstitem_extra = true;
17659 1604 : resetPQExpBuffer(extra);
17660 7854 : for (j = 0; j < tbinfo->numatts; j++)
17661 : {
17662 : /*
17663 : * If a not-null constraint comes from inheritance, reset
17664 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17665 : * below. Special hack: in versions < 18, columns with no
17666 : * local definition need their constraint to be matched by
17667 : * column number in conkeys instead of by constraint name,
17668 : * because the latter is not available. (We distinguish the
17669 : * case because the constraint name is the empty string.)
17670 : */
17671 6250 : if (tbinfo->notnull_constrs[j] != NULL &&
17672 590 : !tbinfo->notnull_islocal[j])
17673 : {
17674 172 : if (tbinfo->notnull_constrs[j][0] != '\0')
17675 : {
17676 146 : if (firstitem)
17677 : {
17678 126 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17679 : "SET conislocal = false\n"
17680 : "WHERE contype = 'n' AND conrelid = ");
17681 126 : appendStringLiteralAH(q, qualrelname, fout);
17682 126 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17683 : "conname IN (");
17684 126 : firstitem = false;
17685 : }
17686 : else
17687 20 : appendPQExpBufferStr(q, ", ");
17688 146 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17689 : }
17690 : else
17691 : {
17692 26 : if (firstitem_extra)
17693 : {
17694 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17695 : "SET conislocal = false\n"
17696 : "WHERE contype = 'n' AND conrelid = ");
17697 26 : appendStringLiteralAH(extra, qualrelname, fout);
17698 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17699 : "conkey IN (");
17700 26 : firstitem_extra = false;
17701 : }
17702 : else
17703 0 : appendPQExpBufferStr(extra, ", ");
17704 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17705 : }
17706 : }
17707 : }
17708 1604 : if (!firstitem)
17709 126 : appendPQExpBufferStr(q, ");\n");
17710 1604 : if (!firstitem_extra)
17711 26 : appendPQExpBufferStr(extra, ");\n");
17712 :
17713 1604 : if (extra->len > 0)
17714 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17715 :
17716 : /*
17717 : * Add inherited CHECK constraints, if any.
17718 : *
17719 : * For partitions, they were already dumped, and conislocal
17720 : * doesn't need fixing.
17721 : *
17722 : * As above, issue only one direct manipulation of pg_constraint.
17723 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17724 : * commands into one as well, refrain for now due to concern about
17725 : * possible backend memory bloat if there are many such
17726 : * constraints.
17727 : */
17728 1604 : resetPQExpBuffer(extra);
17729 1604 : firstitem = true;
17730 1728 : for (k = 0; k < tbinfo->ncheck; k++)
17731 : {
17732 124 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17733 :
17734 124 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17735 120 : continue;
17736 :
17737 4 : if (firstitem)
17738 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17739 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17740 : foreign, qualrelname,
17741 4 : fmtId(constr->dobj.name),
17742 : constr->condef);
17743 : /* Update pg_constraint after all the ALTER TABLEs */
17744 4 : if (firstitem)
17745 : {
17746 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17747 : "SET conislocal = false\n"
17748 : "WHERE contype = 'c' AND conrelid = ");
17749 4 : appendStringLiteralAH(extra, qualrelname, fout);
17750 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17751 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17752 4 : firstitem = false;
17753 : }
17754 : else
17755 0 : appendPQExpBufferStr(extra, ", ");
17756 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17757 : }
17758 1604 : if (!firstitem)
17759 : {
17760 4 : appendPQExpBufferStr(extra, ");\n");
17761 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17762 : }
17763 :
17764 1604 : if (numParents > 0 && !tbinfo->ispartition)
17765 : {
17766 128 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17767 278 : for (k = 0; k < numParents; k++)
17768 : {
17769 150 : TableInfo *parentRel = parents[k];
17770 :
17771 150 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17772 : qualrelname,
17773 150 : fmtQualifiedDumpable(parentRel));
17774 : }
17775 : }
17776 :
17777 1604 : if (OidIsValid(tbinfo->reloftype))
17778 : {
17779 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17780 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17781 : qualrelname,
17782 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17783 : zeroIsError));
17784 : }
17785 : }
17786 :
17787 : /*
17788 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17789 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17790 : * TOAST tables semi-independently, here we see them only as children
17791 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17792 : * child toast table is handled below.)
17793 : */
17794 11378 : if (dopt->binary_upgrade &&
17795 1638 : (tbinfo->relkind == RELKIND_RELATION ||
17796 222 : tbinfo->relkind == RELKIND_MATVIEW))
17797 : {
17798 1450 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17799 1450 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17800 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17801 : "WHERE oid = ",
17802 1450 : tbinfo->frozenxid, tbinfo->minmxid);
17803 1450 : appendStringLiteralAH(q, qualrelname, fout);
17804 1450 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17805 :
17806 1450 : if (tbinfo->toast_oid)
17807 : {
17808 : /*
17809 : * The toast table will have the same OID at restore, so we
17810 : * can safely target it by OID.
17811 : */
17812 554 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17813 554 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17814 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17815 : "WHERE oid = '%u';\n",
17816 554 : tbinfo->toast_frozenxid,
17817 554 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17818 : }
17819 : }
17820 :
17821 : /*
17822 : * In binary_upgrade mode, restore matviews' populated status by
17823 : * poking pg_class directly. This is pretty ugly, but we can't use
17824 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17825 : * matview is not populated even though this matview is; in any case,
17826 : * we want to transfer the matview's heap storage, not run REFRESH.
17827 : */
17828 11378 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17829 34 : tbinfo->relispopulated)
17830 : {
17831 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17832 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17833 : "SET relispopulated = 't'\n"
17834 : "WHERE oid = ");
17835 30 : appendStringLiteralAH(q, qualrelname, fout);
17836 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17837 : }
17838 :
17839 : /*
17840 : * Dump additional per-column properties that we can't handle in the
17841 : * main CREATE TABLE command.
17842 : */
17843 51814 : for (j = 0; j < tbinfo->numatts; j++)
17844 : {
17845 : /* None of this applies to dropped columns */
17846 40436 : if (tbinfo->attisdropped[j])
17847 894 : continue;
17848 :
17849 : /*
17850 : * Dump per-column statistics information. We only issue an ALTER
17851 : * TABLE statement if the attstattarget entry for this column is
17852 : * not the default value.
17853 : */
17854 39542 : if (tbinfo->attstattarget[j] >= 0)
17855 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17856 : foreign, qualrelname,
17857 64 : fmtId(tbinfo->attnames[j]),
17858 64 : tbinfo->attstattarget[j]);
17859 :
17860 : /*
17861 : * Dump per-column storage information. The statement is only
17862 : * dumped if the storage has been changed from the type's default.
17863 : */
17864 39542 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17865 : {
17866 158 : switch (tbinfo->attstorage[j])
17867 : {
17868 20 : case TYPSTORAGE_PLAIN:
17869 20 : storage = "PLAIN";
17870 20 : break;
17871 74 : case TYPSTORAGE_EXTERNAL:
17872 74 : storage = "EXTERNAL";
17873 74 : break;
17874 0 : case TYPSTORAGE_EXTENDED:
17875 0 : storage = "EXTENDED";
17876 0 : break;
17877 64 : case TYPSTORAGE_MAIN:
17878 64 : storage = "MAIN";
17879 64 : break;
17880 0 : default:
17881 0 : storage = NULL;
17882 : }
17883 :
17884 : /*
17885 : * Only dump the statement if it's a storage type we recognize
17886 : */
17887 158 : if (storage != NULL)
17888 158 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17889 : foreign, qualrelname,
17890 158 : fmtId(tbinfo->attnames[j]),
17891 : storage);
17892 : }
17893 :
17894 : /*
17895 : * Dump per-column compression, if it's been set.
17896 : */
17897 39542 : if (!dopt->no_toast_compression)
17898 : {
17899 : const char *cmname;
17900 :
17901 39354 : switch (tbinfo->attcompression[j])
17902 : {
17903 142 : case 'p':
17904 142 : cmname = "pglz";
17905 142 : break;
17906 78 : case 'l':
17907 78 : cmname = "lz4";
17908 78 : break;
17909 39134 : default:
17910 39134 : cmname = NULL;
17911 39134 : break;
17912 : }
17913 :
17914 39354 : if (cmname != NULL)
17915 220 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17916 : foreign, qualrelname,
17917 220 : fmtId(tbinfo->attnames[j]),
17918 : cmname);
17919 : }
17920 :
17921 : /*
17922 : * Dump per-column attributes.
17923 : */
17924 39542 : if (tbinfo->attoptions[j][0] != '\0')
17925 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17926 : foreign, qualrelname,
17927 64 : fmtId(tbinfo->attnames[j]),
17928 64 : tbinfo->attoptions[j]);
17929 :
17930 : /*
17931 : * Dump per-column fdw options.
17932 : */
17933 39542 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17934 68 : tbinfo->attfdwoptions[j][0] != '\0')
17935 64 : appendPQExpBuffer(q,
17936 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17937 : " %s\n"
17938 : ");\n",
17939 : qualrelname,
17940 64 : fmtId(tbinfo->attnames[j]),
17941 64 : tbinfo->attfdwoptions[j]);
17942 : } /* end loop over columns */
17943 :
17944 11378 : free(partkeydef);
17945 11378 : free(ftoptions);
17946 11378 : free(srvname);
17947 : }
17948 :
17949 : /*
17950 : * dump properties we only have ALTER TABLE syntax for
17951 : */
17952 12444 : if ((tbinfo->relkind == RELKIND_RELATION ||
17953 2956 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17954 1804 : tbinfo->relkind == RELKIND_MATVIEW) &&
17955 11310 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17956 : {
17957 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17958 : {
17959 : /* nothing to do, will be set when the index is dumped */
17960 : }
17961 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17962 : {
17963 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17964 : qualrelname);
17965 : }
17966 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17967 : {
17968 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17969 : qualrelname);
17970 : }
17971 : }
17972 :
17973 12444 : if (tbinfo->forcerowsec)
17974 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17975 : qualrelname);
17976 :
17977 12444 : if (dopt->binary_upgrade)
17978 1742 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17979 : reltypename, qrelname,
17980 1742 : tbinfo->dobj.namespace->dobj.name);
17981 :
17982 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17983 : {
17984 12444 : char *tablespace = NULL;
17985 12444 : char *tableam = NULL;
17986 :
17987 : /*
17988 : * _selectTablespace() relies on tablespace-enabled objects in the
17989 : * default tablespace to have a tablespace of "" (empty string) versus
17990 : * non-tablespace-enabled objects to have a tablespace of NULL.
17991 : * getTables() sets tbinfo->reltablespace to "" for the default
17992 : * tablespace (not NULL).
17993 : */
17994 12444 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17995 11310 : tablespace = tbinfo->reltablespace;
17996 :
17997 12444 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17998 2286 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17999 11310 : tableam = tbinfo->amname;
18000 :
18001 12444 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18002 12444 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18003 : .namespace = tbinfo->dobj.namespace->dobj.name,
18004 : .tablespace = tablespace,
18005 : .tableam = tableam,
18006 : .relkind = tbinfo->relkind,
18007 : .owner = tbinfo->rolname,
18008 : .description = reltypename,
18009 : .section = tbinfo->postponed_def ?
18010 : SECTION_POST_DATA : SECTION_PRE_DATA,
18011 : .createStmt = q->data,
18012 : .dropStmt = delq->data));
18013 : }
18014 :
18015 : /* Dump Table Comments */
18016 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18017 148 : dumpTableComment(fout, tbinfo, reltypename);
18018 :
18019 : /* Dump Table Security Labels */
18020 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18021 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18022 :
18023 : /*
18024 : * Dump comments for not-null constraints that aren't to be dumped
18025 : * separately (those are processed by collectComments/dumpComment).
18026 : */
18027 12444 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18028 12444 : fout->remoteVersion >= 180000)
18029 : {
18030 12444 : PQExpBuffer comment = NULL;
18031 12444 : PQExpBuffer tag = NULL;
18032 :
18033 59738 : for (j = 0; j < tbinfo->numatts; j++)
18034 : {
18035 47294 : if (tbinfo->notnull_constrs[j] != NULL &&
18036 4816 : tbinfo->notnull_comment[j] != NULL)
18037 : {
18038 84 : if (comment == NULL)
18039 : {
18040 84 : comment = createPQExpBuffer();
18041 84 : tag = createPQExpBuffer();
18042 : }
18043 : else
18044 : {
18045 0 : resetPQExpBuffer(comment);
18046 0 : resetPQExpBuffer(tag);
18047 : }
18048 :
18049 84 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18050 84 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18051 84 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18052 84 : appendPQExpBufferStr(comment, ";\n");
18053 :
18054 84 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18055 84 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18056 :
18057 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18058 84 : ARCHIVE_OPTS(.tag = tag->data,
18059 : .namespace = tbinfo->dobj.namespace->dobj.name,
18060 : .owner = tbinfo->rolname,
18061 : .description = "COMMENT",
18062 : .section = SECTION_NONE,
18063 : .createStmt = comment->data,
18064 : .deps = &(tbinfo->dobj.dumpId),
18065 : .nDeps = 1));
18066 : }
18067 : }
18068 :
18069 12444 : destroyPQExpBuffer(comment);
18070 12444 : destroyPQExpBuffer(tag);
18071 : }
18072 :
18073 : /* Dump comments on inlined table constraints */
18074 13590 : for (j = 0; j < tbinfo->ncheck; j++)
18075 : {
18076 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18077 :
18078 1146 : if (constr->separate || !constr->conislocal)
18079 488 : continue;
18080 :
18081 658 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18082 74 : dumpTableConstraintComment(fout, constr);
18083 : }
18084 :
18085 12444 : destroyPQExpBuffer(q);
18086 12444 : destroyPQExpBuffer(delq);
18087 12444 : destroyPQExpBuffer(extra);
18088 12444 : free(qrelname);
18089 12444 : free(qualrelname);
18090 12444 : }
18091 :
18092 : /*
18093 : * dumpTableAttach
18094 : * write to fout the commands to attach a child partition
18095 : *
18096 : * Child partitions are always made by creating them separately
18097 : * and then using ATTACH PARTITION, rather than using
18098 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18099 : * any possible discrepancy in column layout, to allow assigning the
18100 : * correct tablespace if different, and so that it's possible to restore
18101 : * a partition without restoring its parent. (You'll get an error from
18102 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18103 : * using "pg_restore -L" if you prefer.) The last point motivates
18104 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18105 : * rather than emitting it within the child partition's ArchiveEntry.
18106 : */
18107 : static void
18108 2782 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18109 : {
18110 2782 : DumpOptions *dopt = fout->dopt;
18111 : PQExpBuffer q;
18112 : PGresult *res;
18113 : char *partbound;
18114 :
18115 : /* Do nothing if not dumping schema */
18116 2782 : if (!dopt->dumpSchema)
18117 108 : return;
18118 :
18119 2674 : q = createPQExpBuffer();
18120 :
18121 2674 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18122 : {
18123 : /* Set up query for partbound details */
18124 86 : appendPQExpBufferStr(q,
18125 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18126 :
18127 86 : appendPQExpBufferStr(q,
18128 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18129 : "FROM pg_class c "
18130 : "WHERE c.oid = $1");
18131 :
18132 86 : ExecuteSqlStatement(fout, q->data);
18133 :
18134 86 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18135 : }
18136 :
18137 2674 : printfPQExpBuffer(q,
18138 : "EXECUTE dumpTableAttach('%u')",
18139 2674 : attachinfo->partitionTbl->dobj.catId.oid);
18140 :
18141 2674 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18142 2674 : partbound = PQgetvalue(res, 0, 0);
18143 :
18144 : /* Perform ALTER TABLE on the parent */
18145 2674 : printfPQExpBuffer(q,
18146 : "ALTER TABLE ONLY %s ",
18147 2674 : fmtQualifiedDumpable(attachinfo->parentTbl));
18148 2674 : appendPQExpBuffer(q,
18149 : "ATTACH PARTITION %s %s;\n",
18150 2674 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18151 : partbound);
18152 :
18153 : /*
18154 : * There is no point in creating a drop query as the drop is done by table
18155 : * drop. (If you think to change this, see also _printTocEntry().)
18156 : * Although this object doesn't really have ownership as such, set the
18157 : * owner field anyway to ensure that the command is run by the correct
18158 : * role at restore time.
18159 : */
18160 2674 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18161 2674 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18162 : .namespace = attachinfo->dobj.namespace->dobj.name,
18163 : .owner = attachinfo->partitionTbl->rolname,
18164 : .description = "TABLE ATTACH",
18165 : .section = SECTION_PRE_DATA,
18166 : .createStmt = q->data));
18167 :
18168 2674 : PQclear(res);
18169 2674 : destroyPQExpBuffer(q);
18170 : }
18171 :
18172 : /*
18173 : * dumpAttrDef --- dump an attribute's default-value declaration
18174 : */
18175 : static void
18176 2064 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18177 : {
18178 2064 : DumpOptions *dopt = fout->dopt;
18179 2064 : TableInfo *tbinfo = adinfo->adtable;
18180 2064 : int adnum = adinfo->adnum;
18181 : PQExpBuffer q;
18182 : PQExpBuffer delq;
18183 : char *qualrelname;
18184 : char *tag;
18185 : char *foreign;
18186 :
18187 : /* Do nothing if not dumping schema */
18188 2064 : if (!dopt->dumpSchema)
18189 0 : return;
18190 :
18191 : /* Skip if not "separate"; it was dumped in the table's definition */
18192 2064 : if (!adinfo->separate)
18193 1732 : return;
18194 :
18195 332 : q = createPQExpBuffer();
18196 332 : delq = createPQExpBuffer();
18197 :
18198 332 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18199 :
18200 332 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18201 :
18202 332 : appendPQExpBuffer(q,
18203 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18204 332 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18205 332 : adinfo->adef_expr);
18206 :
18207 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18208 : foreign, qualrelname,
18209 332 : fmtId(tbinfo->attnames[adnum - 1]));
18210 :
18211 332 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18212 :
18213 332 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18214 332 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18215 332 : ARCHIVE_OPTS(.tag = tag,
18216 : .namespace = tbinfo->dobj.namespace->dobj.name,
18217 : .owner = tbinfo->rolname,
18218 : .description = "DEFAULT",
18219 : .section = SECTION_PRE_DATA,
18220 : .createStmt = q->data,
18221 : .dropStmt = delq->data));
18222 :
18223 332 : free(tag);
18224 332 : destroyPQExpBuffer(q);
18225 332 : destroyPQExpBuffer(delq);
18226 332 : free(qualrelname);
18227 : }
18228 :
18229 : /*
18230 : * getAttrName: extract the correct name for an attribute
18231 : *
18232 : * The array tblInfo->attnames[] only provides names of user attributes;
18233 : * if a system attribute number is supplied, we have to fake it.
18234 : * We also do a little bit of bounds checking for safety's sake.
18235 : */
18236 : static const char *
18237 4110 : getAttrName(int attrnum, const TableInfo *tblInfo)
18238 : {
18239 4110 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18240 4110 : return tblInfo->attnames[attrnum - 1];
18241 0 : switch (attrnum)
18242 : {
18243 0 : case SelfItemPointerAttributeNumber:
18244 0 : return "ctid";
18245 0 : case MinTransactionIdAttributeNumber:
18246 0 : return "xmin";
18247 0 : case MinCommandIdAttributeNumber:
18248 0 : return "cmin";
18249 0 : case MaxTransactionIdAttributeNumber:
18250 0 : return "xmax";
18251 0 : case MaxCommandIdAttributeNumber:
18252 0 : return "cmax";
18253 0 : case TableOidAttributeNumber:
18254 0 : return "tableoid";
18255 : }
18256 0 : pg_fatal("invalid column number %d for table \"%s\"",
18257 : attrnum, tblInfo->dobj.name);
18258 : return NULL; /* keep compiler quiet */
18259 : }
18260 :
18261 : /*
18262 : * dumpIndex
18263 : * write out to fout a user-defined index
18264 : */
18265 : static void
18266 5200 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18267 : {
18268 5200 : DumpOptions *dopt = fout->dopt;
18269 5200 : TableInfo *tbinfo = indxinfo->indextable;
18270 5200 : bool is_constraint = (indxinfo->indexconstraint != 0);
18271 : PQExpBuffer q;
18272 : PQExpBuffer delq;
18273 : char *qindxname;
18274 : char *qqindxname;
18275 :
18276 : /* Do nothing if not dumping schema */
18277 5200 : if (!dopt->dumpSchema)
18278 234 : return;
18279 :
18280 4966 : q = createPQExpBuffer();
18281 4966 : delq = createPQExpBuffer();
18282 :
18283 4966 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18284 4966 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18285 :
18286 : /*
18287 : * If there's an associated constraint, don't dump the index per se, but
18288 : * do dump any comment for it. (This is safe because dependency ordering
18289 : * will have ensured the constraint is emitted first.) Note that the
18290 : * emitted comment has to be shown as depending on the constraint, not the
18291 : * index, in such cases.
18292 : */
18293 4966 : if (!is_constraint)
18294 : {
18295 2090 : char *indstatcols = indxinfo->indstatcols;
18296 2090 : char *indstatvals = indxinfo->indstatvals;
18297 2090 : char **indstatcolsarray = NULL;
18298 2090 : char **indstatvalsarray = NULL;
18299 2090 : int nstatcols = 0;
18300 2090 : int nstatvals = 0;
18301 :
18302 2090 : if (dopt->binary_upgrade)
18303 316 : binary_upgrade_set_pg_class_oids(fout, q,
18304 316 : indxinfo->dobj.catId.oid);
18305 :
18306 : /* Plain secondary index */
18307 2090 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18308 :
18309 : /*
18310 : * Append ALTER TABLE commands as needed to set properties that we
18311 : * only have ALTER TABLE syntax for. Keep this in sync with the
18312 : * similar code in dumpConstraint!
18313 : */
18314 :
18315 : /* If the index is clustered, we need to record that. */
18316 2090 : if (indxinfo->indisclustered)
18317 : {
18318 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18319 0 : fmtQualifiedDumpable(tbinfo));
18320 : /* index name is not qualified in this syntax */
18321 0 : appendPQExpBuffer(q, " ON %s;\n",
18322 : qindxname);
18323 : }
18324 :
18325 : /*
18326 : * If the index has any statistics on some of its columns, generate
18327 : * the associated ALTER INDEX queries.
18328 : */
18329 2090 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18330 : {
18331 : int j;
18332 :
18333 64 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18334 0 : pg_fatal("could not parse index statistic columns");
18335 64 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18336 0 : pg_fatal("could not parse index statistic values");
18337 64 : if (nstatcols != nstatvals)
18338 0 : pg_fatal("mismatched number of columns and values for index statistics");
18339 :
18340 192 : for (j = 0; j < nstatcols; j++)
18341 : {
18342 128 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18343 :
18344 : /*
18345 : * Note that this is a column number, so no quotes should be
18346 : * used.
18347 : */
18348 128 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18349 128 : indstatcolsarray[j]);
18350 128 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18351 128 : indstatvalsarray[j]);
18352 : }
18353 : }
18354 :
18355 : /* Indexes can depend on extensions */
18356 2090 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18357 : "pg_catalog.pg_class",
18358 : "INDEX", qqindxname);
18359 :
18360 : /* If the index defines identity, we need to record that. */
18361 2090 : if (indxinfo->indisreplident)
18362 : {
18363 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18364 0 : fmtQualifiedDumpable(tbinfo));
18365 : /* index name is not qualified in this syntax */
18366 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18367 : qindxname);
18368 : }
18369 :
18370 : /*
18371 : * If this index is a member of a partitioned index, the backend will
18372 : * not allow us to drop it separately, so don't try. It will go away
18373 : * automatically when we drop either the index's table or the
18374 : * partitioned index. (If, in a selective restore with --clean, we
18375 : * drop neither of those, then this index will not be dropped either.
18376 : * But that's fine, and even if you think it's not, the backend won't
18377 : * let us do differently.)
18378 : */
18379 2090 : if (indxinfo->parentidx == 0)
18380 1726 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18381 :
18382 2090 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18383 2090 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18384 2090 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18385 : .namespace = tbinfo->dobj.namespace->dobj.name,
18386 : .tablespace = indxinfo->tablespace,
18387 : .owner = tbinfo->rolname,
18388 : .description = "INDEX",
18389 : .section = SECTION_POST_DATA,
18390 : .createStmt = q->data,
18391 : .dropStmt = delq->data));
18392 :
18393 2090 : free(indstatcolsarray);
18394 2090 : free(indstatvalsarray);
18395 : }
18396 :
18397 : /* Dump Index Comments */
18398 4966 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18399 30 : dumpComment(fout, "INDEX", qindxname,
18400 30 : tbinfo->dobj.namespace->dobj.name,
18401 : tbinfo->rolname,
18402 : indxinfo->dobj.catId, 0,
18403 : is_constraint ? indxinfo->indexconstraint :
18404 : indxinfo->dobj.dumpId);
18405 :
18406 4966 : destroyPQExpBuffer(q);
18407 4966 : destroyPQExpBuffer(delq);
18408 4966 : free(qindxname);
18409 4966 : free(qqindxname);
18410 : }
18411 :
18412 : /*
18413 : * dumpIndexAttach
18414 : * write out to fout a partitioned-index attachment clause
18415 : */
18416 : static void
18417 1148 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18418 : {
18419 : /* Do nothing if not dumping schema */
18420 1148 : if (!fout->dopt->dumpSchema)
18421 96 : return;
18422 :
18423 1052 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18424 : {
18425 1052 : PQExpBuffer q = createPQExpBuffer();
18426 :
18427 1052 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18428 1052 : fmtQualifiedDumpable(attachinfo->parentIdx));
18429 1052 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18430 1052 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18431 :
18432 : /*
18433 : * There is no need for a dropStmt since the drop is done implicitly
18434 : * when we drop either the index's table or the partitioned index.
18435 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18436 : * there's no way to do it anyway. (If you think to change this,
18437 : * consider also what to do with --if-exists.)
18438 : *
18439 : * Although this object doesn't really have ownership as such, set the
18440 : * owner field anyway to ensure that the command is run by the correct
18441 : * role at restore time.
18442 : */
18443 1052 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18444 1052 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18445 : .namespace = attachinfo->dobj.namespace->dobj.name,
18446 : .owner = attachinfo->parentIdx->indextable->rolname,
18447 : .description = "INDEX ATTACH",
18448 : .section = SECTION_POST_DATA,
18449 : .createStmt = q->data));
18450 :
18451 1052 : destroyPQExpBuffer(q);
18452 : }
18453 : }
18454 :
18455 : /*
18456 : * dumpStatisticsExt
18457 : * write out to fout an extended statistics object
18458 : */
18459 : static void
18460 266 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18461 : {
18462 266 : DumpOptions *dopt = fout->dopt;
18463 : PQExpBuffer q;
18464 : PQExpBuffer delq;
18465 : PQExpBuffer query;
18466 : char *qstatsextname;
18467 : PGresult *res;
18468 : char *stxdef;
18469 :
18470 : /* Do nothing if not dumping schema */
18471 266 : if (!dopt->dumpSchema)
18472 36 : return;
18473 :
18474 230 : q = createPQExpBuffer();
18475 230 : delq = createPQExpBuffer();
18476 230 : query = createPQExpBuffer();
18477 :
18478 230 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18479 :
18480 230 : appendPQExpBuffer(query, "SELECT "
18481 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18482 230 : statsextinfo->dobj.catId.oid);
18483 :
18484 230 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18485 :
18486 230 : stxdef = PQgetvalue(res, 0, 0);
18487 :
18488 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18489 230 : appendPQExpBuffer(q, "%s;\n", stxdef);
18490 :
18491 : /*
18492 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18493 : * for this statistics object is not the default value.
18494 : */
18495 230 : if (statsextinfo->stattarget >= 0)
18496 : {
18497 64 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18498 64 : fmtQualifiedDumpable(statsextinfo));
18499 64 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18500 64 : statsextinfo->stattarget);
18501 : }
18502 :
18503 230 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18504 230 : fmtQualifiedDumpable(statsextinfo));
18505 :
18506 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18507 230 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18508 230 : statsextinfo->dobj.dumpId,
18509 230 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18510 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18511 : .owner = statsextinfo->rolname,
18512 : .description = "STATISTICS",
18513 : .section = SECTION_POST_DATA,
18514 : .createStmt = q->data,
18515 : .dropStmt = delq->data));
18516 :
18517 : /* Dump Statistics Comments */
18518 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18519 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18520 0 : statsextinfo->dobj.namespace->dobj.name,
18521 0 : statsextinfo->rolname,
18522 : statsextinfo->dobj.catId, 0,
18523 0 : statsextinfo->dobj.dumpId);
18524 :
18525 230 : PQclear(res);
18526 230 : destroyPQExpBuffer(q);
18527 230 : destroyPQExpBuffer(delq);
18528 230 : destroyPQExpBuffer(query);
18529 230 : free(qstatsextname);
18530 : }
18531 :
18532 : /*
18533 : * dumpConstraint
18534 : * write out to fout a user-defined constraint
18535 : */
18536 : static void
18537 4978 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18538 : {
18539 4978 : DumpOptions *dopt = fout->dopt;
18540 4978 : TableInfo *tbinfo = coninfo->contable;
18541 : PQExpBuffer q;
18542 : PQExpBuffer delq;
18543 4978 : char *tag = NULL;
18544 : char *foreign;
18545 :
18546 : /* Do nothing if not dumping schema */
18547 4978 : if (!dopt->dumpSchema)
18548 196 : return;
18549 :
18550 4782 : q = createPQExpBuffer();
18551 4782 : delq = createPQExpBuffer();
18552 :
18553 9256 : foreign = tbinfo &&
18554 4782 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18555 :
18556 4782 : if (coninfo->contype == 'p' ||
18557 2384 : coninfo->contype == 'u' ||
18558 1926 : coninfo->contype == 'x')
18559 2876 : {
18560 : /* Index-related constraint */
18561 : IndxInfo *indxinfo;
18562 : int k;
18563 :
18564 2876 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18565 :
18566 2876 : if (indxinfo == NULL)
18567 0 : pg_fatal("missing index for constraint \"%s\"",
18568 : coninfo->dobj.name);
18569 :
18570 2876 : if (dopt->binary_upgrade)
18571 300 : binary_upgrade_set_pg_class_oids(fout, q,
18572 : indxinfo->dobj.catId.oid);
18573 :
18574 2876 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18575 2876 : fmtQualifiedDumpable(tbinfo));
18576 2876 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18577 2876 : fmtId(coninfo->dobj.name));
18578 :
18579 2876 : if (coninfo->condef)
18580 : {
18581 : /* pg_get_constraintdef should have provided everything */
18582 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18583 : }
18584 : else
18585 : {
18586 2856 : appendPQExpBufferStr(q,
18587 2856 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18588 :
18589 : /*
18590 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18591 : * indexes. Being able to create this was fixed, but we need to
18592 : * make the index distinct in order to be able to restore the
18593 : * dump.
18594 : */
18595 2856 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18596 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18597 2856 : appendPQExpBufferStr(q, " (");
18598 6886 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18599 : {
18600 4030 : int indkey = (int) indxinfo->indkeys[k];
18601 : const char *attname;
18602 :
18603 4030 : if (indkey == InvalidAttrNumber)
18604 0 : break;
18605 4030 : attname = getAttrName(indkey, tbinfo);
18606 :
18607 4030 : appendPQExpBuffer(q, "%s%s",
18608 : (k == 0) ? "" : ", ",
18609 : fmtId(attname));
18610 : }
18611 2856 : if (coninfo->conperiod)
18612 208 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18613 :
18614 2856 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18615 40 : appendPQExpBufferStr(q, ") INCLUDE (");
18616 :
18617 2936 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18618 : {
18619 80 : int indkey = (int) indxinfo->indkeys[k];
18620 : const char *attname;
18621 :
18622 80 : if (indkey == InvalidAttrNumber)
18623 0 : break;
18624 80 : attname = getAttrName(indkey, tbinfo);
18625 :
18626 160 : appendPQExpBuffer(q, "%s%s",
18627 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18628 : fmtId(attname));
18629 : }
18630 :
18631 2856 : appendPQExpBufferChar(q, ')');
18632 :
18633 2856 : if (nonemptyReloptions(indxinfo->indreloptions))
18634 : {
18635 0 : appendPQExpBufferStr(q, " WITH (");
18636 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18637 0 : appendPQExpBufferChar(q, ')');
18638 : }
18639 :
18640 2856 : if (coninfo->condeferrable)
18641 : {
18642 50 : appendPQExpBufferStr(q, " DEFERRABLE");
18643 50 : if (coninfo->condeferred)
18644 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18645 : }
18646 :
18647 2856 : appendPQExpBufferStr(q, ";\n");
18648 : }
18649 :
18650 : /*
18651 : * Append ALTER TABLE commands as needed to set properties that we
18652 : * only have ALTER TABLE syntax for. Keep this in sync with the
18653 : * similar code in dumpIndex!
18654 : */
18655 :
18656 : /* If the index is clustered, we need to record that. */
18657 2876 : if (indxinfo->indisclustered)
18658 : {
18659 64 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18660 64 : fmtQualifiedDumpable(tbinfo));
18661 : /* index name is not qualified in this syntax */
18662 64 : appendPQExpBuffer(q, " ON %s;\n",
18663 64 : fmtId(indxinfo->dobj.name));
18664 : }
18665 :
18666 : /* If the index defines identity, we need to record that. */
18667 2876 : if (indxinfo->indisreplident)
18668 : {
18669 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18670 0 : fmtQualifiedDumpable(tbinfo));
18671 : /* index name is not qualified in this syntax */
18672 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18673 0 : fmtId(indxinfo->dobj.name));
18674 : }
18675 :
18676 : /* Indexes can depend on extensions */
18677 2876 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18678 : "pg_catalog.pg_class", "INDEX",
18679 2876 : fmtQualifiedDumpable(indxinfo));
18680 :
18681 2876 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18682 2876 : fmtQualifiedDumpable(tbinfo));
18683 2876 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18684 2876 : fmtId(coninfo->dobj.name));
18685 :
18686 2876 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18687 :
18688 2876 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18689 2876 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18690 2876 : ARCHIVE_OPTS(.tag = tag,
18691 : .namespace = tbinfo->dobj.namespace->dobj.name,
18692 : .tablespace = indxinfo->tablespace,
18693 : .owner = tbinfo->rolname,
18694 : .description = "CONSTRAINT",
18695 : .section = SECTION_POST_DATA,
18696 : .createStmt = q->data,
18697 : .dropStmt = delq->data));
18698 : }
18699 1906 : else if (coninfo->contype == 'f')
18700 : {
18701 : char *only;
18702 :
18703 : /*
18704 : * Foreign keys on partitioned tables are always declared as
18705 : * inheriting to partitions; for all other cases, emit them as
18706 : * applying ONLY directly to the named table, because that's how they
18707 : * work for regular inherited tables.
18708 : */
18709 318 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18710 :
18711 : /*
18712 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18713 : * current table data is not processed
18714 : */
18715 318 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18716 318 : only, fmtQualifiedDumpable(tbinfo));
18717 318 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18718 318 : fmtId(coninfo->dobj.name),
18719 318 : coninfo->condef);
18720 :
18721 318 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18722 318 : only, fmtQualifiedDumpable(tbinfo));
18723 318 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18724 318 : fmtId(coninfo->dobj.name));
18725 :
18726 318 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18727 :
18728 318 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18729 318 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18730 318 : ARCHIVE_OPTS(.tag = tag,
18731 : .namespace = tbinfo->dobj.namespace->dobj.name,
18732 : .owner = tbinfo->rolname,
18733 : .description = "FK CONSTRAINT",
18734 : .section = SECTION_POST_DATA,
18735 : .createStmt = q->data,
18736 : .dropStmt = delq->data));
18737 : }
18738 1588 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18739 : {
18740 : /* CHECK or invalid not-null constraint on a table */
18741 :
18742 : /* Ignore if not to be dumped separately, or if it was inherited */
18743 1280 : if (coninfo->separate && coninfo->conislocal)
18744 : {
18745 : const char *keyword;
18746 :
18747 214 : if (coninfo->contype == 'c')
18748 90 : keyword = "CHECK CONSTRAINT";
18749 : else
18750 124 : keyword = "CONSTRAINT";
18751 :
18752 : /* not ONLY since we want it to propagate to children */
18753 214 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18754 214 : fmtQualifiedDumpable(tbinfo));
18755 214 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18756 214 : fmtId(coninfo->dobj.name),
18757 214 : coninfo->condef);
18758 :
18759 214 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18760 214 : fmtQualifiedDumpable(tbinfo));
18761 214 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18762 214 : fmtId(coninfo->dobj.name));
18763 :
18764 214 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18765 :
18766 214 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18767 214 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18768 214 : ARCHIVE_OPTS(.tag = tag,
18769 : .namespace = tbinfo->dobj.namespace->dobj.name,
18770 : .owner = tbinfo->rolname,
18771 : .description = keyword,
18772 : .section = SECTION_POST_DATA,
18773 : .createStmt = q->data,
18774 : .dropStmt = delq->data));
18775 : }
18776 : }
18777 308 : else if (tbinfo == NULL)
18778 : {
18779 : /* CHECK, NOT NULL constraint on a domain */
18780 308 : TypeInfo *tyinfo = coninfo->condomain;
18781 :
18782 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18783 :
18784 : /* Ignore if not to be dumped separately */
18785 308 : if (coninfo->separate)
18786 : {
18787 : const char *keyword;
18788 :
18789 10 : if (coninfo->contype == 'c')
18790 10 : keyword = "CHECK CONSTRAINT";
18791 : else
18792 0 : keyword = "CONSTRAINT";
18793 :
18794 10 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18795 10 : fmtQualifiedDumpable(tyinfo));
18796 10 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18797 10 : fmtId(coninfo->dobj.name),
18798 10 : coninfo->condef);
18799 :
18800 10 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18801 10 : fmtQualifiedDumpable(tyinfo));
18802 10 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18803 10 : fmtId(coninfo->dobj.name));
18804 :
18805 10 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18806 :
18807 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18808 10 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18809 10 : ARCHIVE_OPTS(.tag = tag,
18810 : .namespace = tyinfo->dobj.namespace->dobj.name,
18811 : .owner = tyinfo->rolname,
18812 : .description = keyword,
18813 : .section = SECTION_POST_DATA,
18814 : .createStmt = q->data,
18815 : .dropStmt = delq->data));
18816 :
18817 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18818 : {
18819 10 : PQExpBuffer conprefix = createPQExpBuffer();
18820 10 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18821 :
18822 10 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18823 10 : fmtId(coninfo->dobj.name));
18824 :
18825 10 : dumpComment(fout, conprefix->data, qtypname,
18826 10 : tyinfo->dobj.namespace->dobj.name,
18827 : tyinfo->rolname,
18828 10 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18829 10 : destroyPQExpBuffer(conprefix);
18830 10 : free(qtypname);
18831 : }
18832 : }
18833 : }
18834 : else
18835 : {
18836 0 : pg_fatal("unrecognized constraint type: %c",
18837 : coninfo->contype);
18838 : }
18839 :
18840 : /* Dump Constraint Comments --- only works for table constraints */
18841 4782 : if (tbinfo && coninfo->separate &&
18842 3468 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18843 94 : dumpTableConstraintComment(fout, coninfo);
18844 :
18845 4782 : free(tag);
18846 4782 : destroyPQExpBuffer(q);
18847 4782 : destroyPQExpBuffer(delq);
18848 : }
18849 :
18850 : /*
18851 : * dumpTableConstraintComment --- dump a constraint's comment if any
18852 : *
18853 : * This is split out because we need the function in two different places
18854 : * depending on whether the constraint is dumped as part of CREATE TABLE
18855 : * or as a separate ALTER command.
18856 : */
18857 : static void
18858 168 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18859 : {
18860 168 : TableInfo *tbinfo = coninfo->contable;
18861 168 : PQExpBuffer conprefix = createPQExpBuffer();
18862 : char *qtabname;
18863 :
18864 168 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18865 :
18866 168 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18867 168 : fmtId(coninfo->dobj.name));
18868 :
18869 168 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18870 168 : dumpComment(fout, conprefix->data, qtabname,
18871 168 : tbinfo->dobj.namespace->dobj.name,
18872 : tbinfo->rolname,
18873 : coninfo->dobj.catId, 0,
18874 168 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18875 :
18876 168 : destroyPQExpBuffer(conprefix);
18877 168 : free(qtabname);
18878 168 : }
18879 :
18880 : static inline SeqType
18881 1276 : parse_sequence_type(const char *name)
18882 : {
18883 2850 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18884 : {
18885 2850 : if (strcmp(SeqTypeNames[i], name) == 0)
18886 1276 : return (SeqType) i;
18887 : }
18888 :
18889 0 : pg_fatal("unrecognized sequence type: %s", name);
18890 : return (SeqType) 0; /* keep compiler quiet */
18891 : }
18892 :
18893 : /*
18894 : * bsearch() comparator for SequenceItem
18895 : */
18896 : static int
18897 5912 : SequenceItemCmp(const void *p1, const void *p2)
18898 : {
18899 5912 : SequenceItem v1 = *((const SequenceItem *) p1);
18900 5912 : SequenceItem v2 = *((const SequenceItem *) p2);
18901 :
18902 5912 : return pg_cmp_u32(v1.oid, v2.oid);
18903 : }
18904 :
18905 : /*
18906 : * collectSequences
18907 : *
18908 : * Construct a table of sequence information. This table is sorted by OID for
18909 : * speed in lookup.
18910 : */
18911 : static void
18912 376 : collectSequences(Archive *fout)
18913 : {
18914 : PGresult *res;
18915 : const char *query;
18916 :
18917 : /*
18918 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18919 : * some extra effort, we might be able to use the sorted table for those
18920 : * versions, but for now it seems unlikely to be worth it.
18921 : *
18922 : * Since version 18, we can gather the sequence data in this query with
18923 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18924 : */
18925 376 : if (fout->remoteVersion < 100000)
18926 0 : return;
18927 376 : else if (fout->remoteVersion < 180000 ||
18928 376 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18929 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18930 : "seqstart, seqincrement, "
18931 : "seqmax, seqmin, "
18932 : "seqcache, seqcycle, "
18933 : "NULL, 'f' "
18934 : "FROM pg_catalog.pg_sequence "
18935 : "ORDER BY seqrelid";
18936 : else
18937 360 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18938 : "seqstart, seqincrement, "
18939 : "seqmax, seqmin, "
18940 : "seqcache, seqcycle, "
18941 : "last_value, is_called "
18942 : "FROM pg_catalog.pg_sequence, "
18943 : "pg_get_sequence_data(seqrelid) "
18944 : "ORDER BY seqrelid;";
18945 :
18946 376 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18947 :
18948 376 : nsequences = PQntuples(res);
18949 376 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18950 :
18951 1652 : for (int i = 0; i < nsequences; i++)
18952 : {
18953 1276 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18954 1276 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18955 1276 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18956 1276 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18957 1276 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18958 1276 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18959 1276 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18960 1276 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18961 1276 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18962 1276 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18963 1276 : sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
18964 : }
18965 :
18966 376 : PQclear(res);
18967 : }
18968 :
18969 : /*
18970 : * dumpSequence
18971 : * write the declaration (not data) of one user-defined sequence
18972 : */
18973 : static void
18974 750 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18975 : {
18976 750 : DumpOptions *dopt = fout->dopt;
18977 : SequenceItem *seq;
18978 : bool is_ascending;
18979 : int64 default_minv,
18980 : default_maxv;
18981 750 : PQExpBuffer query = createPQExpBuffer();
18982 750 : PQExpBuffer delqry = createPQExpBuffer();
18983 : char *qseqname;
18984 750 : TableInfo *owning_tab = NULL;
18985 :
18986 750 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18987 :
18988 : /*
18989 : * For versions >= 10, the sequence information is gathered in a sorted
18990 : * table before any calls to dumpSequence(). See collectSequences() for
18991 : * more information.
18992 : */
18993 750 : if (fout->remoteVersion >= 100000)
18994 : {
18995 750 : SequenceItem key = {0};
18996 :
18997 : Assert(sequences);
18998 :
18999 750 : key.oid = tbinfo->dobj.catId.oid;
19000 750 : seq = bsearch(&key, sequences, nsequences,
19001 : sizeof(SequenceItem), SequenceItemCmp);
19002 : }
19003 : else
19004 : {
19005 : PGresult *res;
19006 :
19007 : /*
19008 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19009 : *
19010 : * Note: it might seem that 'bigint' potentially needs to be
19011 : * schema-qualified, but actually that's a keyword.
19012 : */
19013 0 : appendPQExpBuffer(query,
19014 : "SELECT 'bigint' AS sequence_type, "
19015 : "start_value, increment_by, max_value, min_value, "
19016 : "cache_value, is_cycled FROM %s",
19017 0 : fmtQualifiedDumpable(tbinfo));
19018 :
19019 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19020 :
19021 0 : if (PQntuples(res) != 1)
19022 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19023 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19024 : PQntuples(res)),
19025 : tbinfo->dobj.name, PQntuples(res));
19026 :
19027 0 : seq = pg_malloc0(sizeof(SequenceItem));
19028 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19029 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19030 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19031 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19032 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19033 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19034 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19035 :
19036 0 : PQclear(res);
19037 : }
19038 :
19039 : /* Calculate default limits for a sequence of this type */
19040 750 : is_ascending = (seq->incby >= 0);
19041 750 : if (seq->seqtype == SEQTYPE_SMALLINT)
19042 : {
19043 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19044 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19045 : }
19046 700 : else if (seq->seqtype == SEQTYPE_INTEGER)
19047 : {
19048 568 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19049 568 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19050 : }
19051 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
19052 : {
19053 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19054 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19055 : }
19056 : else
19057 : {
19058 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19059 : default_minv = default_maxv = 0; /* keep compiler quiet */
19060 : }
19061 :
19062 : /*
19063 : * Identity sequences are not to be dropped separately.
19064 : */
19065 750 : if (!tbinfo->is_identity_sequence)
19066 : {
19067 466 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19068 466 : fmtQualifiedDumpable(tbinfo));
19069 : }
19070 :
19071 750 : resetPQExpBuffer(query);
19072 :
19073 750 : if (dopt->binary_upgrade)
19074 : {
19075 132 : binary_upgrade_set_pg_class_oids(fout, query,
19076 132 : tbinfo->dobj.catId.oid);
19077 :
19078 : /*
19079 : * In older PG versions a sequence will have a pg_type entry, but v14
19080 : * and up don't use that, so don't attempt to preserve the type OID.
19081 : */
19082 : }
19083 :
19084 750 : if (tbinfo->is_identity_sequence)
19085 : {
19086 284 : owning_tab = findTableByOid(tbinfo->owning_tab);
19087 :
19088 284 : appendPQExpBuffer(query,
19089 : "ALTER TABLE %s ",
19090 284 : fmtQualifiedDumpable(owning_tab));
19091 284 : appendPQExpBuffer(query,
19092 : "ALTER COLUMN %s ADD GENERATED ",
19093 284 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19094 284 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19095 204 : appendPQExpBufferStr(query, "ALWAYS");
19096 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19097 80 : appendPQExpBufferStr(query, "BY DEFAULT");
19098 284 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19099 284 : fmtQualifiedDumpable(tbinfo));
19100 :
19101 : /*
19102 : * Emit persistence option only if it's different from the owning
19103 : * table's. This avoids using this new syntax unnecessarily.
19104 : */
19105 284 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19106 20 : appendPQExpBuffer(query, " %s\n",
19107 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19108 : "UNLOGGED" : "LOGGED");
19109 : }
19110 : else
19111 : {
19112 466 : appendPQExpBuffer(query,
19113 : "CREATE %sSEQUENCE %s\n",
19114 466 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19115 : "UNLOGGED " : "",
19116 466 : fmtQualifiedDumpable(tbinfo));
19117 :
19118 466 : if (seq->seqtype != SEQTYPE_BIGINT)
19119 364 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19120 : }
19121 :
19122 750 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19123 :
19124 750 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19125 :
19126 750 : if (seq->minv != default_minv)
19127 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19128 : else
19129 720 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19130 :
19131 750 : if (seq->maxv != default_maxv)
19132 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19133 : else
19134 720 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19135 :
19136 750 : appendPQExpBuffer(query,
19137 : " CACHE " INT64_FORMAT "%s",
19138 750 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19139 :
19140 750 : if (tbinfo->is_identity_sequence)
19141 284 : appendPQExpBufferStr(query, "\n);\n");
19142 : else
19143 466 : appendPQExpBufferStr(query, ";\n");
19144 :
19145 : /* binary_upgrade: no need to clear TOAST table oid */
19146 :
19147 750 : if (dopt->binary_upgrade)
19148 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19149 : "SEQUENCE", qseqname,
19150 132 : tbinfo->dobj.namespace->dobj.name);
19151 :
19152 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19153 750 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19154 750 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19155 : .namespace = tbinfo->dobj.namespace->dobj.name,
19156 : .owner = tbinfo->rolname,
19157 : .description = "SEQUENCE",
19158 : .section = SECTION_PRE_DATA,
19159 : .createStmt = query->data,
19160 : .dropStmt = delqry->data));
19161 :
19162 : /*
19163 : * If the sequence is owned by a table column, emit the ALTER for it as a
19164 : * separate TOC entry immediately following the sequence's own entry. It's
19165 : * OK to do this rather than using full sorting logic, because the
19166 : * dependency that tells us it's owned will have forced the table to be
19167 : * created first. We can't just include the ALTER in the TOC entry
19168 : * because it will fail if we haven't reassigned the sequence owner to
19169 : * match the table's owner.
19170 : *
19171 : * We need not schema-qualify the table reference because both sequence
19172 : * and table must be in the same schema.
19173 : */
19174 750 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19175 : {
19176 274 : owning_tab = findTableByOid(tbinfo->owning_tab);
19177 :
19178 274 : if (owning_tab == NULL)
19179 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19180 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19181 :
19182 274 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19183 : {
19184 270 : resetPQExpBuffer(query);
19185 270 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19186 270 : fmtQualifiedDumpable(tbinfo));
19187 270 : appendPQExpBuffer(query, " OWNED BY %s",
19188 270 : fmtQualifiedDumpable(owning_tab));
19189 270 : appendPQExpBuffer(query, ".%s;\n",
19190 270 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19191 :
19192 270 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19193 270 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19194 270 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19195 : .namespace = tbinfo->dobj.namespace->dobj.name,
19196 : .owner = tbinfo->rolname,
19197 : .description = "SEQUENCE OWNED BY",
19198 : .section = SECTION_PRE_DATA,
19199 : .createStmt = query->data,
19200 : .deps = &(tbinfo->dobj.dumpId),
19201 : .nDeps = 1));
19202 : }
19203 : }
19204 :
19205 : /* Dump Sequence Comments and Security Labels */
19206 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19207 0 : dumpComment(fout, "SEQUENCE", qseqname,
19208 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19209 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19210 :
19211 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19212 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19213 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19214 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19215 :
19216 750 : if (fout->remoteVersion < 100000)
19217 0 : pg_free(seq);
19218 750 : destroyPQExpBuffer(query);
19219 750 : destroyPQExpBuffer(delqry);
19220 750 : free(qseqname);
19221 750 : }
19222 :
19223 : /*
19224 : * dumpSequenceData
19225 : * write the data of one user-defined sequence
19226 : */
19227 : static void
19228 786 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19229 : {
19230 786 : TableInfo *tbinfo = tdinfo->tdtable;
19231 : int64 last;
19232 : bool called;
19233 : PQExpBuffer query;
19234 :
19235 : /* needn't bother if not dumping sequence data */
19236 786 : if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19237 2 : return;
19238 :
19239 784 : query = createPQExpBuffer();
19240 :
19241 : /*
19242 : * For versions >= 18, the sequence information is gathered in the sorted
19243 : * array before any calls to dumpSequenceData(). See collectSequences()
19244 : * for more information.
19245 : *
19246 : * For older versions, we have to query the sequence relations
19247 : * individually.
19248 : */
19249 784 : if (fout->remoteVersion < 180000)
19250 : {
19251 : PGresult *res;
19252 :
19253 0 : appendPQExpBuffer(query,
19254 : "SELECT last_value, is_called FROM %s",
19255 0 : fmtQualifiedDumpable(tbinfo));
19256 :
19257 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19258 :
19259 0 : if (PQntuples(res) != 1)
19260 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19261 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19262 : PQntuples(res)),
19263 : tbinfo->dobj.name, PQntuples(res));
19264 :
19265 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19266 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19267 :
19268 0 : PQclear(res);
19269 : }
19270 : else
19271 : {
19272 784 : SequenceItem key = {0};
19273 : SequenceItem *entry;
19274 :
19275 : Assert(sequences);
19276 : Assert(tbinfo->dobj.catId.oid);
19277 :
19278 784 : key.oid = tbinfo->dobj.catId.oid;
19279 784 : entry = bsearch(&key, sequences, nsequences,
19280 : sizeof(SequenceItem), SequenceItemCmp);
19281 :
19282 784 : if (entry->null_seqtuple)
19283 0 : pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19284 : "SELECT privilege on the sequence or the sequence may "
19285 : "have been concurrently dropped",
19286 : tbinfo->dobj.name);
19287 :
19288 784 : last = entry->last_value;
19289 784 : called = entry->is_called;
19290 : }
19291 :
19292 784 : resetPQExpBuffer(query);
19293 784 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19294 784 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19295 784 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19296 : last, (called ? "true" : "false"));
19297 :
19298 784 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19299 784 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19300 784 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19301 : .namespace = tbinfo->dobj.namespace->dobj.name,
19302 : .owner = tbinfo->rolname,
19303 : .description = "SEQUENCE SET",
19304 : .section = SECTION_DATA,
19305 : .createStmt = query->data,
19306 : .deps = &(tbinfo->dobj.dumpId),
19307 : .nDeps = 1));
19308 :
19309 784 : destroyPQExpBuffer(query);
19310 : }
19311 :
19312 : /*
19313 : * dumpTrigger
19314 : * write the declaration of one user-defined table trigger
19315 : */
19316 : static void
19317 1046 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19318 : {
19319 1046 : DumpOptions *dopt = fout->dopt;
19320 1046 : TableInfo *tbinfo = tginfo->tgtable;
19321 : PQExpBuffer query;
19322 : PQExpBuffer delqry;
19323 : PQExpBuffer trigprefix;
19324 : PQExpBuffer trigidentity;
19325 : char *qtabname;
19326 : char *tag;
19327 :
19328 : /* Do nothing if not dumping schema */
19329 1046 : if (!dopt->dumpSchema)
19330 62 : return;
19331 :
19332 984 : query = createPQExpBuffer();
19333 984 : delqry = createPQExpBuffer();
19334 984 : trigprefix = createPQExpBuffer();
19335 984 : trigidentity = createPQExpBuffer();
19336 :
19337 984 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19338 :
19339 984 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19340 984 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19341 :
19342 984 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19343 984 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19344 :
19345 : /* Triggers can depend on extensions */
19346 984 : append_depends_on_extension(fout, query, &tginfo->dobj,
19347 : "pg_catalog.pg_trigger", "TRIGGER",
19348 984 : trigidentity->data);
19349 :
19350 984 : if (tginfo->tgispartition)
19351 : {
19352 : Assert(tbinfo->ispartition);
19353 :
19354 : /*
19355 : * Partition triggers only appear here because their 'tgenabled' flag
19356 : * differs from its parent's. The trigger is created already, so
19357 : * remove the CREATE and replace it with an ALTER. (Clear out the
19358 : * DROP query too, so that pg_dump --create does not cause errors.)
19359 : */
19360 218 : resetPQExpBuffer(query);
19361 218 : resetPQExpBuffer(delqry);
19362 218 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19363 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19364 218 : fmtQualifiedDumpable(tbinfo));
19365 218 : switch (tginfo->tgenabled)
19366 : {
19367 76 : case 'f':
19368 : case 'D':
19369 76 : appendPQExpBufferStr(query, "DISABLE");
19370 76 : break;
19371 0 : case 't':
19372 : case 'O':
19373 0 : appendPQExpBufferStr(query, "ENABLE");
19374 0 : break;
19375 66 : case 'R':
19376 66 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19377 66 : break;
19378 76 : case 'A':
19379 76 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19380 76 : break;
19381 : }
19382 218 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19383 218 : fmtId(tginfo->dobj.name));
19384 : }
19385 766 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19386 : {
19387 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19388 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19389 0 : fmtQualifiedDumpable(tbinfo));
19390 0 : switch (tginfo->tgenabled)
19391 : {
19392 0 : case 'D':
19393 : case 'f':
19394 0 : appendPQExpBufferStr(query, "DISABLE");
19395 0 : break;
19396 0 : case 'A':
19397 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19398 0 : break;
19399 0 : case 'R':
19400 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19401 0 : break;
19402 0 : default:
19403 0 : appendPQExpBufferStr(query, "ENABLE");
19404 0 : break;
19405 : }
19406 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19407 0 : fmtId(tginfo->dobj.name));
19408 : }
19409 :
19410 984 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19411 984 : fmtId(tginfo->dobj.name));
19412 :
19413 984 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19414 :
19415 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19416 984 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19417 984 : ARCHIVE_OPTS(.tag = tag,
19418 : .namespace = tbinfo->dobj.namespace->dobj.name,
19419 : .owner = tbinfo->rolname,
19420 : .description = "TRIGGER",
19421 : .section = SECTION_POST_DATA,
19422 : .createStmt = query->data,
19423 : .dropStmt = delqry->data));
19424 :
19425 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19426 0 : dumpComment(fout, trigprefix->data, qtabname,
19427 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19428 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19429 :
19430 984 : free(tag);
19431 984 : destroyPQExpBuffer(query);
19432 984 : destroyPQExpBuffer(delqry);
19433 984 : destroyPQExpBuffer(trigprefix);
19434 984 : destroyPQExpBuffer(trigidentity);
19435 984 : free(qtabname);
19436 : }
19437 :
19438 : /*
19439 : * dumpEventTrigger
19440 : * write the declaration of one user-defined event trigger
19441 : */
19442 : static void
19443 84 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19444 : {
19445 84 : DumpOptions *dopt = fout->dopt;
19446 : PQExpBuffer query;
19447 : PQExpBuffer delqry;
19448 : char *qevtname;
19449 :
19450 : /* Do nothing if not dumping schema */
19451 84 : if (!dopt->dumpSchema)
19452 12 : return;
19453 :
19454 72 : query = createPQExpBuffer();
19455 72 : delqry = createPQExpBuffer();
19456 :
19457 72 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19458 :
19459 72 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19460 72 : appendPQExpBufferStr(query, qevtname);
19461 72 : appendPQExpBufferStr(query, " ON ");
19462 72 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19463 :
19464 72 : if (strcmp("", evtinfo->evttags) != 0)
19465 : {
19466 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19467 10 : appendPQExpBufferStr(query, evtinfo->evttags);
19468 10 : appendPQExpBufferChar(query, ')');
19469 : }
19470 :
19471 72 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19472 72 : appendPQExpBufferStr(query, evtinfo->evtfname);
19473 72 : appendPQExpBufferStr(query, "();\n");
19474 :
19475 72 : if (evtinfo->evtenabled != 'O')
19476 : {
19477 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19478 : qevtname);
19479 0 : switch (evtinfo->evtenabled)
19480 : {
19481 0 : case 'D':
19482 0 : appendPQExpBufferStr(query, "DISABLE");
19483 0 : break;
19484 0 : case 'A':
19485 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19486 0 : break;
19487 0 : case 'R':
19488 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19489 0 : break;
19490 0 : default:
19491 0 : appendPQExpBufferStr(query, "ENABLE");
19492 0 : break;
19493 : }
19494 0 : appendPQExpBufferStr(query, ";\n");
19495 : }
19496 :
19497 72 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19498 : qevtname);
19499 :
19500 72 : if (dopt->binary_upgrade)
19501 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19502 : "EVENT TRIGGER", qevtname, NULL);
19503 :
19504 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19505 72 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19506 72 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19507 : .owner = evtinfo->evtowner,
19508 : .description = "EVENT TRIGGER",
19509 : .section = SECTION_POST_DATA,
19510 : .createStmt = query->data,
19511 : .dropStmt = delqry->data));
19512 :
19513 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19514 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19515 0 : NULL, evtinfo->evtowner,
19516 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19517 :
19518 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19519 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19520 0 : NULL, evtinfo->evtowner,
19521 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19522 :
19523 72 : destroyPQExpBuffer(query);
19524 72 : destroyPQExpBuffer(delqry);
19525 72 : free(qevtname);
19526 : }
19527 :
19528 : /*
19529 : * dumpRule
19530 : * Dump a rule
19531 : */
19532 : static void
19533 2258 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19534 : {
19535 2258 : DumpOptions *dopt = fout->dopt;
19536 2258 : TableInfo *tbinfo = rinfo->ruletable;
19537 : bool is_view;
19538 : PQExpBuffer query;
19539 : PQExpBuffer cmd;
19540 : PQExpBuffer delcmd;
19541 : PQExpBuffer ruleprefix;
19542 : char *qtabname;
19543 : PGresult *res;
19544 : char *tag;
19545 :
19546 : /* Do nothing if not dumping schema */
19547 2258 : if (!dopt->dumpSchema)
19548 120 : return;
19549 :
19550 : /*
19551 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19552 : * we do not want to dump it as a separate object.
19553 : */
19554 2138 : if (!rinfo->separate)
19555 1716 : return;
19556 :
19557 : /*
19558 : * If it's an ON SELECT rule, we want to print it as a view definition,
19559 : * instead of a rule.
19560 : */
19561 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19562 :
19563 422 : query = createPQExpBuffer();
19564 422 : cmd = createPQExpBuffer();
19565 422 : delcmd = createPQExpBuffer();
19566 422 : ruleprefix = createPQExpBuffer();
19567 :
19568 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19569 :
19570 422 : if (is_view)
19571 : {
19572 : PQExpBuffer result;
19573 :
19574 : /*
19575 : * We need OR REPLACE here because we'll be replacing a dummy view.
19576 : * Otherwise this should look largely like the regular view dump code.
19577 : */
19578 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19579 20 : fmtQualifiedDumpable(tbinfo));
19580 20 : if (nonemptyReloptions(tbinfo->reloptions))
19581 : {
19582 0 : appendPQExpBufferStr(cmd, " WITH (");
19583 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19584 0 : appendPQExpBufferChar(cmd, ')');
19585 : }
19586 20 : result = createViewAsClause(fout, tbinfo);
19587 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19588 20 : destroyPQExpBuffer(result);
19589 20 : if (tbinfo->checkoption != NULL)
19590 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19591 : tbinfo->checkoption);
19592 20 : appendPQExpBufferStr(cmd, ";\n");
19593 : }
19594 : else
19595 : {
19596 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19597 402 : appendPQExpBuffer(query,
19598 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19599 402 : rinfo->dobj.catId.oid);
19600 :
19601 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19602 :
19603 402 : if (PQntuples(res) != 1)
19604 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19605 : rinfo->dobj.name, tbinfo->dobj.name);
19606 :
19607 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19608 :
19609 402 : PQclear(res);
19610 : }
19611 :
19612 : /*
19613 : * Add the command to alter the rules replication firing semantics if it
19614 : * differs from the default.
19615 : */
19616 422 : if (rinfo->ev_enabled != 'O')
19617 : {
19618 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19619 30 : switch (rinfo->ev_enabled)
19620 : {
19621 0 : case 'A':
19622 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19623 0 : fmtId(rinfo->dobj.name));
19624 0 : break;
19625 0 : case 'R':
19626 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19627 0 : fmtId(rinfo->dobj.name));
19628 0 : break;
19629 30 : case 'D':
19630 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19631 30 : fmtId(rinfo->dobj.name));
19632 30 : break;
19633 : }
19634 : }
19635 :
19636 422 : if (is_view)
19637 : {
19638 : /*
19639 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19640 : * REPLACE VIEW to replace the rule with something with minimal
19641 : * dependencies.
19642 : */
19643 : PQExpBuffer result;
19644 :
19645 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19646 20 : fmtQualifiedDumpable(tbinfo));
19647 20 : result = createDummyViewAsClause(fout, tbinfo);
19648 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19649 20 : destroyPQExpBuffer(result);
19650 : }
19651 : else
19652 : {
19653 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19654 402 : fmtId(rinfo->dobj.name));
19655 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
19656 402 : fmtQualifiedDumpable(tbinfo));
19657 : }
19658 :
19659 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19660 422 : fmtId(rinfo->dobj.name));
19661 :
19662 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19663 :
19664 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19665 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19666 422 : ARCHIVE_OPTS(.tag = tag,
19667 : .namespace = tbinfo->dobj.namespace->dobj.name,
19668 : .owner = tbinfo->rolname,
19669 : .description = "RULE",
19670 : .section = SECTION_POST_DATA,
19671 : .createStmt = cmd->data,
19672 : .dropStmt = delcmd->data));
19673 :
19674 : /* Dump rule comments */
19675 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19676 0 : dumpComment(fout, ruleprefix->data, qtabname,
19677 0 : tbinfo->dobj.namespace->dobj.name,
19678 : tbinfo->rolname,
19679 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19680 :
19681 422 : free(tag);
19682 422 : destroyPQExpBuffer(query);
19683 422 : destroyPQExpBuffer(cmd);
19684 422 : destroyPQExpBuffer(delcmd);
19685 422 : destroyPQExpBuffer(ruleprefix);
19686 422 : free(qtabname);
19687 : }
19688 :
19689 : /*
19690 : * getExtensionMembership --- obtain extension membership data
19691 : *
19692 : * We need to identify objects that are extension members as soon as they're
19693 : * loaded, so that we can correctly determine whether they need to be dumped.
19694 : * Generally speaking, extension member objects will get marked as *not* to
19695 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19696 : * command. However, in binary upgrade mode we still need to dump the members
19697 : * individually.
19698 : */
19699 : void
19700 378 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19701 : int numExtensions)
19702 : {
19703 : PQExpBuffer query;
19704 : PGresult *res;
19705 : int ntups,
19706 : i;
19707 : int i_classid,
19708 : i_objid,
19709 : i_refobjid;
19710 : ExtensionInfo *ext;
19711 :
19712 : /* Nothing to do if no extensions */
19713 378 : if (numExtensions == 0)
19714 0 : return;
19715 :
19716 378 : query = createPQExpBuffer();
19717 :
19718 : /* refclassid constraint is redundant but may speed the search */
19719 378 : appendPQExpBufferStr(query, "SELECT "
19720 : "classid, objid, refobjid "
19721 : "FROM pg_depend "
19722 : "WHERE refclassid = 'pg_extension'::regclass "
19723 : "AND deptype = 'e' "
19724 : "ORDER BY 3");
19725 :
19726 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19727 :
19728 378 : ntups = PQntuples(res);
19729 :
19730 378 : i_classid = PQfnumber(res, "classid");
19731 378 : i_objid = PQfnumber(res, "objid");
19732 378 : i_refobjid = PQfnumber(res, "refobjid");
19733 :
19734 : /*
19735 : * Since we ordered the SELECT by referenced ID, we can expect that
19736 : * multiple entries for the same extension will appear together; this
19737 : * saves on searches.
19738 : */
19739 378 : ext = NULL;
19740 :
19741 3100 : for (i = 0; i < ntups; i++)
19742 : {
19743 : CatalogId objId;
19744 : Oid extId;
19745 :
19746 2722 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19747 2722 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19748 2722 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19749 :
19750 2722 : if (ext == NULL ||
19751 2344 : ext->dobj.catId.oid != extId)
19752 438 : ext = findExtensionByOid(extId);
19753 :
19754 2722 : if (ext == NULL)
19755 : {
19756 : /* shouldn't happen */
19757 0 : pg_log_warning("could not find referenced extension %u", extId);
19758 0 : continue;
19759 : }
19760 :
19761 2722 : recordExtensionMembership(objId, ext);
19762 : }
19763 :
19764 378 : PQclear(res);
19765 :
19766 378 : destroyPQExpBuffer(query);
19767 : }
19768 :
19769 : /*
19770 : * processExtensionTables --- deal with extension configuration tables
19771 : *
19772 : * There are two parts to this process:
19773 : *
19774 : * 1. Identify and create dump records for extension configuration tables.
19775 : *
19776 : * Extensions can mark tables as "configuration", which means that the user
19777 : * is able and expected to modify those tables after the extension has been
19778 : * loaded. For these tables, we dump out only the data- the structure is
19779 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19780 : * foreign keys, which brings us to-
19781 : *
19782 : * 2. Record FK dependencies between configuration tables.
19783 : *
19784 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19785 : * the data is loaded, we have to work out what the best order for reloading
19786 : * the data is, to avoid FK violations when the tables are restored. This is
19787 : * not perfect- we can't handle circular dependencies and if any exist they
19788 : * will cause an invalid dump to be produced (though at least all of the data
19789 : * is included for a user to manually restore). This is currently documented
19790 : * but perhaps we can provide a better solution in the future.
19791 : */
19792 : void
19793 376 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19794 : int numExtensions)
19795 : {
19796 376 : DumpOptions *dopt = fout->dopt;
19797 : PQExpBuffer query;
19798 : PGresult *res;
19799 : int ntups,
19800 : i;
19801 : int i_conrelid,
19802 : i_confrelid;
19803 :
19804 : /* Nothing to do if no extensions */
19805 376 : if (numExtensions == 0)
19806 0 : return;
19807 :
19808 : /*
19809 : * Identify extension configuration tables and create TableDataInfo
19810 : * objects for them, ensuring their data will be dumped even though the
19811 : * tables themselves won't be.
19812 : *
19813 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19814 : * user data in a configuration table is treated like schema data. This
19815 : * seems appropriate since system data in a config table would get
19816 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19817 : * list of extensions to be included, none of its data is dumped.
19818 : */
19819 812 : for (i = 0; i < numExtensions; i++)
19820 : {
19821 436 : ExtensionInfo *curext = &(extinfo[i]);
19822 436 : char *extconfig = curext->extconfig;
19823 436 : char *extcondition = curext->extcondition;
19824 436 : char **extconfigarray = NULL;
19825 436 : char **extconditionarray = NULL;
19826 436 : int nconfigitems = 0;
19827 436 : int nconditionitems = 0;
19828 :
19829 : /*
19830 : * Check if this extension is listed as to include in the dump. If
19831 : * not, any table data associated with it is discarded.
19832 : */
19833 436 : if (extension_include_oids.head != NULL &&
19834 16 : !simple_oid_list_member(&extension_include_oids,
19835 : curext->dobj.catId.oid))
19836 12 : continue;
19837 :
19838 : /*
19839 : * Check if this extension is listed as to exclude in the dump. If
19840 : * yes, any table data associated with it is discarded.
19841 : */
19842 436 : if (extension_exclude_oids.head != NULL &&
19843 8 : simple_oid_list_member(&extension_exclude_oids,
19844 : curext->dobj.catId.oid))
19845 4 : continue;
19846 :
19847 424 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19848 : {
19849 : int j;
19850 :
19851 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19852 0 : pg_fatal("could not parse %s array", "extconfig");
19853 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19854 0 : pg_fatal("could not parse %s array", "extcondition");
19855 40 : if (nconfigitems != nconditionitems)
19856 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19857 :
19858 120 : for (j = 0; j < nconfigitems; j++)
19859 : {
19860 : TableInfo *configtbl;
19861 80 : Oid configtbloid = atooid(extconfigarray[j]);
19862 80 : bool dumpobj =
19863 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19864 :
19865 80 : configtbl = findTableByOid(configtbloid);
19866 80 : if (configtbl == NULL)
19867 0 : continue;
19868 :
19869 : /*
19870 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19871 : * unless the table or its schema is explicitly included
19872 : */
19873 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19874 : {
19875 : /* check table explicitly requested */
19876 4 : if (table_include_oids.head != NULL &&
19877 0 : simple_oid_list_member(&table_include_oids,
19878 : configtbloid))
19879 0 : dumpobj = true;
19880 :
19881 : /* check table's schema explicitly requested */
19882 4 : if (configtbl->dobj.namespace->dobj.dump &
19883 : DUMP_COMPONENT_DATA)
19884 4 : dumpobj = true;
19885 : }
19886 :
19887 : /* check table excluded by an exclusion switch */
19888 88 : if (table_exclude_oids.head != NULL &&
19889 8 : simple_oid_list_member(&table_exclude_oids,
19890 : configtbloid))
19891 2 : dumpobj = false;
19892 :
19893 : /* check schema excluded by an exclusion switch */
19894 80 : if (simple_oid_list_member(&schema_exclude_oids,
19895 80 : configtbl->dobj.namespace->dobj.catId.oid))
19896 0 : dumpobj = false;
19897 :
19898 80 : if (dumpobj)
19899 : {
19900 78 : makeTableDataInfo(dopt, configtbl);
19901 78 : if (configtbl->dataObj != NULL)
19902 : {
19903 78 : if (strlen(extconditionarray[j]) > 0)
19904 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19905 : }
19906 : }
19907 : }
19908 : }
19909 424 : if (extconfigarray)
19910 40 : free(extconfigarray);
19911 424 : if (extconditionarray)
19912 40 : free(extconditionarray);
19913 : }
19914 :
19915 : /*
19916 : * Now that all the TableDataInfo objects have been created for all the
19917 : * extensions, check their FK dependencies and register them to try and
19918 : * dump the data out in an order that they can be restored in.
19919 : *
19920 : * Note that this is not a problem for user tables as their FKs are
19921 : * recreated after the data has been loaded.
19922 : */
19923 :
19924 376 : query = createPQExpBuffer();
19925 :
19926 376 : printfPQExpBuffer(query,
19927 : "SELECT conrelid, confrelid "
19928 : "FROM pg_constraint "
19929 : "JOIN pg_depend ON (objid = confrelid) "
19930 : "WHERE contype = 'f' "
19931 : "AND refclassid = 'pg_extension'::regclass "
19932 : "AND classid = 'pg_class'::regclass;");
19933 :
19934 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19935 376 : ntups = PQntuples(res);
19936 :
19937 376 : i_conrelid = PQfnumber(res, "conrelid");
19938 376 : i_confrelid = PQfnumber(res, "confrelid");
19939 :
19940 : /* Now get the dependencies and register them */
19941 376 : for (i = 0; i < ntups; i++)
19942 : {
19943 : Oid conrelid,
19944 : confrelid;
19945 : TableInfo *reftable,
19946 : *contable;
19947 :
19948 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19949 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19950 0 : contable = findTableByOid(conrelid);
19951 0 : reftable = findTableByOid(confrelid);
19952 :
19953 0 : if (reftable == NULL ||
19954 0 : reftable->dataObj == NULL ||
19955 0 : contable == NULL ||
19956 0 : contable->dataObj == NULL)
19957 0 : continue;
19958 :
19959 : /*
19960 : * Make referencing TABLE_DATA object depend on the referenced table's
19961 : * TABLE_DATA object.
19962 : */
19963 0 : addObjectDependency(&contable->dataObj->dobj,
19964 0 : reftable->dataObj->dobj.dumpId);
19965 : }
19966 376 : PQclear(res);
19967 376 : destroyPQExpBuffer(query);
19968 : }
19969 :
19970 : /*
19971 : * getDependencies --- obtain available dependency data
19972 : */
19973 : static void
19974 376 : getDependencies(Archive *fout)
19975 : {
19976 : PQExpBuffer query;
19977 : PGresult *res;
19978 : int ntups,
19979 : i;
19980 : int i_classid,
19981 : i_objid,
19982 : i_refclassid,
19983 : i_refobjid,
19984 : i_deptype;
19985 : DumpableObject *dobj,
19986 : *refdobj;
19987 :
19988 376 : pg_log_info("reading dependency data");
19989 :
19990 376 : query = createPQExpBuffer();
19991 :
19992 : /*
19993 : * Messy query to collect the dependency data we need. Note that we
19994 : * ignore the sub-object column, so that dependencies of or on a column
19995 : * look the same as dependencies of or on a whole table.
19996 : *
19997 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19998 : * already processed by getExtensionMembership.
19999 : */
20000 376 : appendPQExpBufferStr(query, "SELECT "
20001 : "classid, objid, refclassid, refobjid, deptype "
20002 : "FROM pg_depend "
20003 : "WHERE deptype != 'p' AND deptype != 'e'\n");
20004 :
20005 : /*
20006 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
20007 : * have to translate their dependencies into dependencies of their parent
20008 : * opfamily. Ignore internal dependencies though, as those will point to
20009 : * their parent opclass, which we needn't consider here (and if we did,
20010 : * it'd just result in circular dependencies). Also, "loose" opfamily
20011 : * entries will have dependencies on their parent opfamily, which we
20012 : * should drop since they'd likewise become useless self-dependencies.
20013 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20014 : */
20015 376 : appendPQExpBufferStr(query, "UNION ALL\n"
20016 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20017 : "FROM pg_depend d, pg_amop o "
20018 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20019 : "classid = 'pg_amop'::regclass AND objid = o.oid "
20020 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20021 :
20022 : /* Likewise for pg_amproc entries */
20023 376 : appendPQExpBufferStr(query, "UNION ALL\n"
20024 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20025 : "FROM pg_depend d, pg_amproc p "
20026 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20027 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20028 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20029 :
20030 : /* Sort the output for efficiency below */
20031 376 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20032 :
20033 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20034 :
20035 376 : ntups = PQntuples(res);
20036 :
20037 376 : i_classid = PQfnumber(res, "classid");
20038 376 : i_objid = PQfnumber(res, "objid");
20039 376 : i_refclassid = PQfnumber(res, "refclassid");
20040 376 : i_refobjid = PQfnumber(res, "refobjid");
20041 376 : i_deptype = PQfnumber(res, "deptype");
20042 :
20043 : /*
20044 : * Since we ordered the SELECT by referencing ID, we can expect that
20045 : * multiple entries for the same object will appear together; this saves
20046 : * on searches.
20047 : */
20048 376 : dobj = NULL;
20049 :
20050 812750 : for (i = 0; i < ntups; i++)
20051 : {
20052 : CatalogId objId;
20053 : CatalogId refobjId;
20054 : char deptype;
20055 :
20056 812374 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20057 812374 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20058 812374 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20059 812374 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20060 812374 : deptype = *(PQgetvalue(res, i, i_deptype));
20061 :
20062 812374 : if (dobj == NULL ||
20063 763096 : dobj->catId.tableoid != objId.tableoid ||
20064 758858 : dobj->catId.oid != objId.oid)
20065 355142 : dobj = findObjectByCatalogId(objId);
20066 :
20067 : /*
20068 : * Failure to find objects mentioned in pg_depend is not unexpected,
20069 : * since for example we don't collect info about TOAST tables.
20070 : */
20071 812374 : if (dobj == NULL)
20072 : {
20073 : #ifdef NOT_USED
20074 : pg_log_warning("no referencing object %u %u",
20075 : objId.tableoid, objId.oid);
20076 : #endif
20077 50618 : continue;
20078 : }
20079 :
20080 763472 : refdobj = findObjectByCatalogId(refobjId);
20081 :
20082 763472 : if (refdobj == NULL)
20083 : {
20084 : #ifdef NOT_USED
20085 : pg_log_warning("no referenced object %u %u",
20086 : refobjId.tableoid, refobjId.oid);
20087 : #endif
20088 1716 : continue;
20089 : }
20090 :
20091 : /*
20092 : * For 'x' dependencies, mark the object for later; we still add the
20093 : * normal dependency, for possible ordering purposes. Currently
20094 : * pg_dump_sort.c knows to put extensions ahead of all object types
20095 : * that could possibly depend on them, but this is safer.
20096 : */
20097 761756 : if (deptype == 'x')
20098 88 : dobj->depends_on_ext = true;
20099 :
20100 : /*
20101 : * Ordinarily, table rowtypes have implicit dependencies on their
20102 : * tables. However, for a composite type the implicit dependency goes
20103 : * the other way in pg_depend; which is the right thing for DROP but
20104 : * it doesn't produce the dependency ordering we need. So in that one
20105 : * case, we reverse the direction of the dependency.
20106 : */
20107 761756 : if (deptype == 'i' &&
20108 213460 : dobj->objType == DO_TABLE &&
20109 2516 : refdobj->objType == DO_TYPE)
20110 364 : addObjectDependency(refdobj, dobj->dumpId);
20111 : else
20112 : /* normal case */
20113 761392 : addObjectDependency(dobj, refdobj->dumpId);
20114 : }
20115 :
20116 376 : PQclear(res);
20117 :
20118 376 : destroyPQExpBuffer(query);
20119 376 : }
20120 :
20121 :
20122 : /*
20123 : * createBoundaryObjects - create dummy DumpableObjects to represent
20124 : * dump section boundaries.
20125 : */
20126 : static DumpableObject *
20127 376 : createBoundaryObjects(void)
20128 : {
20129 : DumpableObject *dobjs;
20130 :
20131 376 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20132 :
20133 376 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20134 376 : dobjs[0].catId = nilCatalogId;
20135 376 : AssignDumpId(dobjs + 0);
20136 376 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20137 :
20138 376 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20139 376 : dobjs[1].catId = nilCatalogId;
20140 376 : AssignDumpId(dobjs + 1);
20141 376 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20142 :
20143 376 : return dobjs;
20144 : }
20145 :
20146 : /*
20147 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20148 : * section boundaries.
20149 : */
20150 : static void
20151 376 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20152 : DumpableObject *boundaryObjs)
20153 : {
20154 376 : DumpableObject *preDataBound = boundaryObjs + 0;
20155 376 : DumpableObject *postDataBound = boundaryObjs + 1;
20156 : int i;
20157 :
20158 1401708 : for (i = 0; i < numObjs; i++)
20159 : {
20160 1401332 : DumpableObject *dobj = dobjs[i];
20161 :
20162 : /*
20163 : * The classification of object types here must match the SECTION_xxx
20164 : * values assigned during subsequent ArchiveEntry calls!
20165 : */
20166 1401332 : switch (dobj->objType)
20167 : {
20168 1309526 : case DO_NAMESPACE:
20169 : case DO_EXTENSION:
20170 : case DO_TYPE:
20171 : case DO_SHELL_TYPE:
20172 : case DO_FUNC:
20173 : case DO_AGG:
20174 : case DO_OPERATOR:
20175 : case DO_ACCESS_METHOD:
20176 : case DO_OPCLASS:
20177 : case DO_OPFAMILY:
20178 : case DO_COLLATION:
20179 : case DO_CONVERSION:
20180 : case DO_TABLE:
20181 : case DO_TABLE_ATTACH:
20182 : case DO_ATTRDEF:
20183 : case DO_PROCLANG:
20184 : case DO_CAST:
20185 : case DO_DUMMY_TYPE:
20186 : case DO_TSPARSER:
20187 : case DO_TSDICT:
20188 : case DO_TSTEMPLATE:
20189 : case DO_TSCONFIG:
20190 : case DO_FDW:
20191 : case DO_FOREIGN_SERVER:
20192 : case DO_TRANSFORM:
20193 : /* Pre-data objects: must come before the pre-data boundary */
20194 1309526 : addObjectDependency(preDataBound, dobj->dumpId);
20195 1309526 : break;
20196 9598 : case DO_TABLE_DATA:
20197 : case DO_SEQUENCE_SET:
20198 : case DO_LARGE_OBJECT:
20199 : case DO_LARGE_OBJECT_DATA:
20200 : /* Data objects: must come between the boundaries */
20201 9598 : addObjectDependency(dobj, preDataBound->dumpId);
20202 9598 : addObjectDependency(postDataBound, dobj->dumpId);
20203 9598 : break;
20204 11486 : case DO_INDEX:
20205 : case DO_INDEX_ATTACH:
20206 : case DO_STATSEXT:
20207 : case DO_REFRESH_MATVIEW:
20208 : case DO_TRIGGER:
20209 : case DO_EVENT_TRIGGER:
20210 : case DO_DEFAULT_ACL:
20211 : case DO_POLICY:
20212 : case DO_PUBLICATION:
20213 : case DO_PUBLICATION_REL:
20214 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20215 : case DO_SUBSCRIPTION:
20216 : case DO_SUBSCRIPTION_REL:
20217 : /* Post-data objects: must come after the post-data boundary */
20218 11486 : addObjectDependency(dobj, postDataBound->dumpId);
20219 11486 : break;
20220 58310 : case DO_RULE:
20221 : /* Rules are post-data, but only if dumped separately */
20222 58310 : if (((RuleInfo *) dobj)->separate)
20223 1298 : addObjectDependency(dobj, postDataBound->dumpId);
20224 58310 : break;
20225 5066 : case DO_CONSTRAINT:
20226 : case DO_FK_CONSTRAINT:
20227 : /* Constraints are post-data, but only if dumped separately */
20228 5066 : if (((ConstraintInfo *) dobj)->separate)
20229 3650 : addObjectDependency(dobj, postDataBound->dumpId);
20230 5066 : break;
20231 376 : case DO_PRE_DATA_BOUNDARY:
20232 : /* nothing to do */
20233 376 : break;
20234 376 : case DO_POST_DATA_BOUNDARY:
20235 : /* must come after the pre-data boundary */
20236 376 : addObjectDependency(dobj, preDataBound->dumpId);
20237 376 : break;
20238 6594 : case DO_REL_STATS:
20239 : /* stats section varies by parent object type, DATA or POST */
20240 6594 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20241 : {
20242 4234 : addObjectDependency(dobj, preDataBound->dumpId);
20243 4234 : addObjectDependency(postDataBound, dobj->dumpId);
20244 : }
20245 : else
20246 2360 : addObjectDependency(dobj, postDataBound->dumpId);
20247 6594 : break;
20248 : }
20249 : }
20250 376 : }
20251 :
20252 :
20253 : /*
20254 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20255 : *
20256 : * The raw dependency data obtained by getDependencies() is not terribly
20257 : * useful in an archive dump, because in many cases there are dependency
20258 : * chains linking through objects that don't appear explicitly in the dump.
20259 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20260 : * will depend on other objects --- but the rule will not appear as a separate
20261 : * object in the dump. We need to adjust the view's dependencies to include
20262 : * whatever the rule depends on that is included in the dump.
20263 : *
20264 : * Just to make things more complicated, there are also "special" dependencies
20265 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20266 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20267 : * its table. In these cases we must leave the dependencies strictly as-is
20268 : * even if they refer to not-to-be-dumped objects.
20269 : *
20270 : * To handle this, the convention is that "special" dependencies are created
20271 : * during ArchiveEntry calls, and an archive TOC item that has any such
20272 : * entries will not be touched here. Otherwise, we recursively search the
20273 : * DumpableObject data structures to build the correct dependencies for each
20274 : * archive TOC item.
20275 : */
20276 : static void
20277 116 : BuildArchiveDependencies(Archive *fout)
20278 : {
20279 116 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20280 : TocEntry *te;
20281 :
20282 : /* Scan all TOC entries in the archive */
20283 13324 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20284 : {
20285 : DumpableObject *dobj;
20286 : DumpId *dependencies;
20287 : int nDeps;
20288 : int allocDeps;
20289 :
20290 : /* No need to process entries that will not be dumped */
20291 13208 : if (te->reqs == 0)
20292 6468 : continue;
20293 : /* Ignore entries that already have "special" dependencies */
20294 13182 : if (te->nDeps > 0)
20295 5558 : continue;
20296 : /* Otherwise, look up the item's original DumpableObject, if any */
20297 7624 : dobj = findObjectByDumpId(te->dumpId);
20298 7624 : if (dobj == NULL)
20299 700 : continue;
20300 : /* No work if it has no dependencies */
20301 6924 : if (dobj->nDeps <= 0)
20302 184 : continue;
20303 : /* Set up work array */
20304 6740 : allocDeps = 64;
20305 6740 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20306 6740 : nDeps = 0;
20307 : /* Recursively find all dumpable dependencies */
20308 6740 : findDumpableDependencies(AH, dobj,
20309 : &dependencies, &nDeps, &allocDeps);
20310 : /* And save 'em ... */
20311 6740 : if (nDeps > 0)
20312 : {
20313 5006 : dependencies = (DumpId *) pg_realloc(dependencies,
20314 : nDeps * sizeof(DumpId));
20315 5006 : te->dependencies = dependencies;
20316 5006 : te->nDeps = nDeps;
20317 : }
20318 : else
20319 1734 : free(dependencies);
20320 : }
20321 116 : }
20322 :
20323 : /* Recursive search subroutine for BuildArchiveDependencies */
20324 : static void
20325 16568 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20326 : DumpId **dependencies, int *nDeps, int *allocDeps)
20327 : {
20328 : int i;
20329 :
20330 : /*
20331 : * Ignore section boundary objects: if we search through them, we'll
20332 : * report lots of bogus dependencies.
20333 : */
20334 16568 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20335 16534 : dobj->objType == DO_POST_DATA_BOUNDARY)
20336 2804 : return;
20337 :
20338 34178 : for (i = 0; i < dobj->nDeps; i++)
20339 : {
20340 20414 : DumpId depid = dobj->dependencies[i];
20341 :
20342 20414 : if (TocIDRequired(AH, depid) != 0)
20343 : {
20344 : /* Object will be dumped, so just reference it as a dependency */
20345 10586 : if (*nDeps >= *allocDeps)
20346 : {
20347 0 : *allocDeps *= 2;
20348 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20349 0 : *allocDeps * sizeof(DumpId));
20350 : }
20351 10586 : (*dependencies)[*nDeps] = depid;
20352 10586 : (*nDeps)++;
20353 : }
20354 : else
20355 : {
20356 : /*
20357 : * Object will not be dumped, so recursively consider its deps. We
20358 : * rely on the assumption that sortDumpableObjects already broke
20359 : * any dependency loops, else we might recurse infinitely.
20360 : */
20361 9828 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20362 :
20363 9828 : if (otherdobj)
20364 9828 : findDumpableDependencies(AH, otherdobj,
20365 : dependencies, nDeps, allocDeps);
20366 : }
20367 : }
20368 : }
20369 :
20370 :
20371 : /*
20372 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20373 : * given type OID.
20374 : *
20375 : * This does not guarantee to schema-qualify the output, so it should not
20376 : * be used to create the target object name for CREATE or ALTER commands.
20377 : *
20378 : * Note that the result is cached and must not be freed by the caller.
20379 : */
20380 : static const char *
20381 4610 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20382 : {
20383 : TypeInfo *typeInfo;
20384 : char *result;
20385 : PQExpBuffer query;
20386 : PGresult *res;
20387 :
20388 4610 : if (oid == 0)
20389 : {
20390 0 : if ((opts & zeroAsStar) != 0)
20391 0 : return "*";
20392 0 : else if ((opts & zeroAsNone) != 0)
20393 0 : return "NONE";
20394 : }
20395 :
20396 : /* see if we have the result cached in the type's TypeInfo record */
20397 4610 : typeInfo = findTypeByOid(oid);
20398 4610 : if (typeInfo && typeInfo->ftypname)
20399 3676 : return typeInfo->ftypname;
20400 :
20401 934 : query = createPQExpBuffer();
20402 934 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20403 : oid);
20404 :
20405 934 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20406 :
20407 : /* result of format_type is already quoted */
20408 934 : result = pg_strdup(PQgetvalue(res, 0, 0));
20409 :
20410 934 : PQclear(res);
20411 934 : destroyPQExpBuffer(query);
20412 :
20413 : /*
20414 : * Cache the result for re-use in later requests, if possible. If we
20415 : * don't have a TypeInfo for the type, the string will be leaked once the
20416 : * caller is done with it ... but that case really should not happen, so
20417 : * leaking if it does seems acceptable.
20418 : */
20419 934 : if (typeInfo)
20420 934 : typeInfo->ftypname = result;
20421 :
20422 934 : return result;
20423 : }
20424 :
20425 : /*
20426 : * Return a column list clause for the given relation.
20427 : *
20428 : * Special case: if there are no undropped columns in the relation, return
20429 : * "", not an invalid "()" column list.
20430 : */
20431 : static const char *
20432 16420 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20433 : {
20434 16420 : int numatts = ti->numatts;
20435 16420 : char **attnames = ti->attnames;
20436 16420 : bool *attisdropped = ti->attisdropped;
20437 16420 : char *attgenerated = ti->attgenerated;
20438 : bool needComma;
20439 : int i;
20440 :
20441 16420 : appendPQExpBufferChar(buffer, '(');
20442 16420 : needComma = false;
20443 80120 : for (i = 0; i < numatts; i++)
20444 : {
20445 63700 : if (attisdropped[i])
20446 1196 : continue;
20447 62504 : if (attgenerated[i])
20448 2208 : continue;
20449 60296 : if (needComma)
20450 44324 : appendPQExpBufferStr(buffer, ", ");
20451 60296 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20452 60296 : needComma = true;
20453 : }
20454 :
20455 16420 : if (!needComma)
20456 448 : return ""; /* no undropped columns */
20457 :
20458 15972 : appendPQExpBufferChar(buffer, ')');
20459 15972 : return buffer->data;
20460 : }
20461 :
20462 : /*
20463 : * Check if a reloptions array is nonempty.
20464 : */
20465 : static bool
20466 26964 : nonemptyReloptions(const char *reloptions)
20467 : {
20468 : /* Don't want to print it if it's just "{}" */
20469 26964 : return (reloptions != NULL && strlen(reloptions) > 2);
20470 : }
20471 :
20472 : /*
20473 : * Format a reloptions array and append it to the given buffer.
20474 : *
20475 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20476 : */
20477 : static void
20478 418 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20479 : const char *prefix, Archive *fout)
20480 : {
20481 : bool res;
20482 :
20483 418 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20484 418 : fout->std_strings);
20485 418 : if (!res)
20486 0 : pg_log_warning("could not parse %s array", "reloptions");
20487 418 : }
20488 :
20489 : /*
20490 : * read_dump_filters - retrieve object identifier patterns from file
20491 : *
20492 : * Parse the specified filter file for include and exclude patterns, and add
20493 : * them to the relevant lists. If the filename is "-" then filters will be
20494 : * read from STDIN rather than a file.
20495 : */
20496 : static void
20497 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20498 : {
20499 : FilterStateData fstate;
20500 : char *objname;
20501 : FilterCommandType comtype;
20502 : FilterObjectType objtype;
20503 :
20504 52 : filter_init(&fstate, filename, exit_nicely);
20505 :
20506 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20507 : {
20508 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20509 : {
20510 34 : switch (objtype)
20511 : {
20512 0 : case FILTER_OBJECT_TYPE_NONE:
20513 0 : break;
20514 0 : case FILTER_OBJECT_TYPE_DATABASE:
20515 : case FILTER_OBJECT_TYPE_FUNCTION:
20516 : case FILTER_OBJECT_TYPE_INDEX:
20517 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20518 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20519 : case FILTER_OBJECT_TYPE_TRIGGER:
20520 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20521 : "include",
20522 : filter_object_type_name(objtype));
20523 0 : exit_nicely(1);
20524 : break; /* unreachable */
20525 :
20526 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20527 2 : simple_string_list_append(&extension_include_patterns, objname);
20528 2 : break;
20529 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20530 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20531 2 : break;
20532 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20533 2 : simple_string_list_append(&schema_include_patterns, objname);
20534 2 : dopt->include_everything = false;
20535 2 : break;
20536 26 : case FILTER_OBJECT_TYPE_TABLE:
20537 26 : simple_string_list_append(&table_include_patterns, objname);
20538 26 : dopt->include_everything = false;
20539 26 : break;
20540 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20541 2 : simple_string_list_append(&table_include_patterns_and_children,
20542 : objname);
20543 2 : dopt->include_everything = false;
20544 2 : break;
20545 : }
20546 : }
20547 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20548 : {
20549 18 : switch (objtype)
20550 : {
20551 0 : case FILTER_OBJECT_TYPE_NONE:
20552 0 : break;
20553 2 : case FILTER_OBJECT_TYPE_DATABASE:
20554 : case FILTER_OBJECT_TYPE_FUNCTION:
20555 : case FILTER_OBJECT_TYPE_INDEX:
20556 : case FILTER_OBJECT_TYPE_TRIGGER:
20557 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20558 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20559 : "exclude",
20560 : filter_object_type_name(objtype));
20561 2 : exit_nicely(1);
20562 : break;
20563 :
20564 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20565 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20566 2 : break;
20567 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20568 2 : simple_string_list_append(&tabledata_exclude_patterns,
20569 : objname);
20570 2 : break;
20571 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20572 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20573 : objname);
20574 2 : break;
20575 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20576 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20577 4 : break;
20578 4 : case FILTER_OBJECT_TYPE_TABLE:
20579 4 : simple_string_list_append(&table_exclude_patterns, objname);
20580 4 : break;
20581 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20582 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20583 : objname);
20584 2 : break;
20585 : }
20586 : }
20587 : else
20588 : {
20589 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20590 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20591 : }
20592 :
20593 64 : if (objname)
20594 50 : free(objname);
20595 : }
20596 :
20597 44 : filter_free(&fstate);
20598 44 : }
|