Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_dump.c
4 : * pg_dump is a utility for dumping out a postgres database
5 : * into a script file.
6 : *
7 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_constraint_d.h"
51 : #include "catalog/pg_default_acl_d.h"
52 : #include "catalog/pg_largeobject_d.h"
53 : #include "catalog/pg_largeobject_metadata_d.h"
54 : #include "catalog/pg_proc_d.h"
55 : #include "catalog/pg_publication_d.h"
56 : #include "catalog/pg_shdepend_d.h"
57 : #include "catalog/pg_subscription_d.h"
58 : #include "catalog/pg_type_d.h"
59 : #include "common/connect.h"
60 : #include "common/int.h"
61 : #include "common/relpath.h"
62 : #include "common/shortest_dec.h"
63 : #include "compress_io.h"
64 : #include "dumputils.h"
65 : #include "fe_utils/option_utils.h"
66 : #include "fe_utils/string_utils.h"
67 : #include "filter.h"
68 : #include "getopt_long.h"
69 : #include "libpq/libpq-fs.h"
70 : #include "parallel.h"
71 : #include "pg_backup_db.h"
72 : #include "pg_backup_utils.h"
73 : #include "pg_dump.h"
74 : #include "storage/block.h"
75 :
76 : typedef struct
77 : {
78 : Oid roleoid; /* role's OID */
79 : const char *rolename; /* role's name */
80 : } RoleNameItem;
81 :
82 : typedef struct
83 : {
84 : const char *descr; /* comment for an object */
85 : Oid classoid; /* object class (catalog OID) */
86 : Oid objoid; /* object OID */
87 : int objsubid; /* subobject (table column #) */
88 : } CommentItem;
89 :
90 : typedef struct
91 : {
92 : const char *provider; /* label provider of this security label */
93 : const char *label; /* security label for an object */
94 : Oid classoid; /* object class (catalog OID) */
95 : Oid objoid; /* object OID */
96 : int objsubid; /* subobject (table column #) */
97 : } SecLabelItem;
98 :
99 : typedef struct
100 : {
101 : Oid oid; /* object OID */
102 : char relkind; /* object kind */
103 : RelFileNumber relfilenumber; /* object filenode */
104 : Oid toast_oid; /* toast table OID */
105 : RelFileNumber toast_relfilenumber; /* toast table filenode */
106 : Oid toast_index_oid; /* toast table index OID */
107 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
108 : } BinaryUpgradeClassOidItem;
109 :
110 : /* sequence types */
111 : typedef enum SeqType
112 : {
113 : SEQTYPE_SMALLINT,
114 : SEQTYPE_INTEGER,
115 : SEQTYPE_BIGINT,
116 : } SeqType;
117 :
118 : static const char *const SeqTypeNames[] =
119 : {
120 : [SEQTYPE_SMALLINT] = "smallint",
121 : [SEQTYPE_INTEGER] = "integer",
122 : [SEQTYPE_BIGINT] = "bigint",
123 : };
124 :
125 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
126 : "array length mismatch");
127 :
128 : typedef struct
129 : {
130 : Oid oid; /* sequence OID */
131 : SeqType seqtype; /* data type of sequence */
132 : bool cycled; /* whether sequence cycles */
133 : int64 minv; /* minimum value */
134 : int64 maxv; /* maximum value */
135 : int64 startv; /* start value */
136 : int64 incby; /* increment value */
137 : int64 cache; /* cache size */
138 : int64 last_value; /* last value of sequence */
139 : bool is_called; /* whether nextval advances before returning */
140 : } SequenceItem;
141 :
142 : typedef enum OidOptions
143 : {
144 : zeroIsError = 1,
145 : zeroAsStar = 2,
146 : zeroAsNone = 4,
147 : } OidOptions;
148 :
149 : /* global decls */
150 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
151 :
152 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
153 :
154 : /* The specified names/patterns should to match at least one entity */
155 : static int strict_names = 0;
156 :
157 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
158 :
159 : /*
160 : * Object inclusion/exclusion lists
161 : *
162 : * The string lists record the patterns given by command-line switches,
163 : * which we then convert to lists of OIDs of matching objects.
164 : */
165 : static SimpleStringList schema_include_patterns = {NULL, NULL};
166 : static SimpleOidList schema_include_oids = {NULL, NULL};
167 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
168 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
169 :
170 : static SimpleStringList table_include_patterns = {NULL, NULL};
171 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
172 : static SimpleOidList table_include_oids = {NULL, NULL};
173 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
174 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
175 : static SimpleOidList table_exclude_oids = {NULL, NULL};
176 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
177 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
178 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
179 :
180 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
181 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
182 :
183 : static SimpleStringList extension_include_patterns = {NULL, NULL};
184 : static SimpleOidList extension_include_oids = {NULL, NULL};
185 :
186 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
187 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
188 :
189 : static const CatalogId nilCatalogId = {0, 0};
190 :
191 : /* override for standard extra_float_digits setting */
192 : static bool have_extra_float_digits = false;
193 : static int extra_float_digits;
194 :
195 : /* sorted table of role names */
196 : static RoleNameItem *rolenames = NULL;
197 : static int nrolenames = 0;
198 :
199 : /* sorted table of comments */
200 : static CommentItem *comments = NULL;
201 : static int ncomments = 0;
202 :
203 : /* sorted table of security labels */
204 : static SecLabelItem *seclabels = NULL;
205 : static int nseclabels = 0;
206 :
207 : /* sorted table of pg_class information for binary upgrade */
208 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
209 : static int nbinaryUpgradeClassOids = 0;
210 :
211 : /* sorted table of sequences */
212 : static SequenceItem *sequences = NULL;
213 : static int nsequences = 0;
214 :
215 : /*
216 : * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
217 : * as a dependency for pg_shdepend and any large object comments/seclabels.
218 : */
219 : static DumpId lo_metadata_dumpId;
220 :
221 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
222 : #define MAX_ATTR_STATS_RELS 64
223 :
224 : /*
225 : * The default number of rows per INSERT when
226 : * --inserts is specified without --rows-per-insert
227 : */
228 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
229 :
230 : /*
231 : * Maximum number of large objects to group into a single ArchiveEntry.
232 : * At some point we might want to make this user-controllable, but for now
233 : * a hard-wired setting will suffice.
234 : */
235 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
236 :
237 : /*
238 : * Macro for producing quoted, schema-qualified name of a dumpable object.
239 : */
240 : #define fmtQualifiedDumpable(obj) \
241 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
242 : (obj)->dobj.name)
243 :
244 : static void help(const char *progname);
245 : static void setup_connection(Archive *AH,
246 : const char *dumpencoding, const char *dumpsnapshot,
247 : char *use_role);
248 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
249 : static void expand_schema_name_patterns(Archive *fout,
250 : SimpleStringList *patterns,
251 : SimpleOidList *oids,
252 : bool strict_names);
253 : static void expand_extension_name_patterns(Archive *fout,
254 : SimpleStringList *patterns,
255 : SimpleOidList *oids,
256 : bool strict_names);
257 : static void expand_foreign_server_name_patterns(Archive *fout,
258 : SimpleStringList *patterns,
259 : SimpleOidList *oids);
260 : static void expand_table_name_patterns(Archive *fout,
261 : SimpleStringList *patterns,
262 : SimpleOidList *oids,
263 : bool strict_names,
264 : bool with_child_tables);
265 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
266 : const char *pattern);
267 :
268 : static NamespaceInfo *findNamespace(Oid nsoid);
269 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
270 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
271 : static const char *getRoleName(const char *roleoid_str);
272 : static void collectRoleNames(Archive *fout);
273 : static void getAdditionalACLs(Archive *fout);
274 : static void dumpCommentExtended(Archive *fout, const char *type,
275 : const char *name, const char *namespace,
276 : const char *owner, CatalogId catalogId,
277 : int subid, DumpId dumpId,
278 : const char *initdb_comment);
279 : static inline void dumpComment(Archive *fout, const char *type,
280 : const char *name, const char *namespace,
281 : const char *owner, CatalogId catalogId,
282 : int subid, DumpId dumpId);
283 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
284 : static void collectComments(Archive *fout);
285 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
286 : const char *namespace, const char *owner,
287 : CatalogId catalogId, int subid, DumpId dumpId);
288 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
289 : static void collectSecLabels(Archive *fout);
290 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
291 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
292 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
293 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
294 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
295 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
296 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
297 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
298 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
299 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
300 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
301 : PGresult *res);
302 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
303 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
304 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
305 : static void dumpCast(Archive *fout, const CastInfo *cast);
306 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
307 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
308 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
309 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
310 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
311 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
312 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
313 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
314 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
315 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
316 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
317 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
318 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
319 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
320 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
321 : static void collectSequences(Archive *fout);
322 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
323 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
324 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
325 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
326 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
327 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
328 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
329 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
330 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
331 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
332 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
333 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
334 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
335 : static void dumpUserMappings(Archive *fout,
336 : const char *servername, const char *namespace,
337 : const char *owner, CatalogId catalogId, DumpId dumpId);
338 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
339 :
340 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
341 : const char *type, const char *name, const char *subname,
342 : const char *nspname, const char *tag, const char *owner,
343 : const DumpableAcl *dacl);
344 :
345 : static void getDependencies(Archive *fout);
346 : static void BuildArchiveDependencies(Archive *fout);
347 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
348 : DumpId **dependencies, int *nDeps, int *allocDeps);
349 :
350 : static DumpableObject *createBoundaryObjects(void);
351 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
352 : DumpableObject *boundaryObjs);
353 :
354 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
355 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
356 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
357 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
358 : static void buildMatViewRefreshDependencies(Archive *fout);
359 : static void getTableDataFKConstraints(void);
360 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
361 : TableInfo *tbinfo, int j,
362 : int i_notnull_name,
363 : int i_notnull_comment,
364 : int i_notnull_invalidoid,
365 : int i_notnull_noinherit,
366 : int i_notnull_islocal,
367 : PQExpBuffer *invalidnotnulloids);
368 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
369 : bool is_agg);
370 : static char *format_function_signature(Archive *fout,
371 : const FuncInfo *finfo, bool honor_quotes);
372 : static char *convertRegProcReference(const char *proc);
373 : static char *getFormattedOperatorName(const char *oproid);
374 : static char *convertTSFunction(Archive *fout, Oid funcOid);
375 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
376 : static void getLOs(Archive *fout);
377 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
378 : static int dumpLOs(Archive *fout, const void *arg);
379 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
380 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
381 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
382 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
383 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
384 : static void dumpDatabase(Archive *fout);
385 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
386 : const char *dbname, Oid dboid);
387 : static void dumpEncoding(Archive *AH);
388 : static void dumpStdStrings(Archive *AH);
389 : static void dumpSearchPath(Archive *AH);
390 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
391 : PQExpBuffer upgrade_buffer,
392 : Oid pg_type_oid,
393 : bool force_array_type,
394 : bool include_multirange_type);
395 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
396 : PQExpBuffer upgrade_buffer,
397 : const TableInfo *tbinfo);
398 : static void collectBinaryUpgradeClassOids(Archive *fout);
399 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
400 : PQExpBuffer upgrade_buffer,
401 : Oid pg_class_oid);
402 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
403 : const DumpableObject *dobj,
404 : const char *objtype,
405 : const char *objname,
406 : const char *objnamespace);
407 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
408 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
409 : static bool nonemptyReloptions(const char *reloptions);
410 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
411 : const char *prefix, Archive *fout);
412 : static char *get_synchronized_snapshot(Archive *fout);
413 : static void set_restrict_relation_kind(Archive *AH, const char *value);
414 : static void setupDumpWorker(Archive *AH);
415 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
416 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
417 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
418 :
419 :
420 : int
421 580 : main(int argc, char **argv)
422 : {
423 : int c;
424 580 : const char *filename = NULL;
425 580 : const char *format = "p";
426 : TableInfo *tblinfo;
427 : int numTables;
428 : DumpableObject **dobjs;
429 : int numObjs;
430 : DumpableObject *boundaryObjs;
431 : int i;
432 : int optindex;
433 : RestoreOptions *ropt;
434 : Archive *fout; /* the script file */
435 580 : bool g_verbose = false;
436 580 : const char *dumpencoding = NULL;
437 580 : const char *dumpsnapshot = NULL;
438 580 : char *use_role = NULL;
439 580 : int numWorkers = 1;
440 580 : int plainText = 0;
441 580 : ArchiveFormat archiveFormat = archUnknown;
442 : ArchiveMode archiveMode;
443 580 : pg_compress_specification compression_spec = {0};
444 580 : char *compression_detail = NULL;
445 580 : char *compression_algorithm_str = "none";
446 580 : char *error_detail = NULL;
447 580 : bool user_compression_defined = false;
448 580 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
449 580 : bool data_only = false;
450 580 : bool schema_only = false;
451 580 : bool statistics_only = false;
452 580 : bool with_statistics = false;
453 580 : bool no_data = false;
454 580 : bool no_schema = false;
455 580 : bool no_statistics = false;
456 :
457 : static DumpOptions dopt;
458 :
459 : static struct option long_options[] = {
460 : {"data-only", no_argument, NULL, 'a'},
461 : {"blobs", no_argument, NULL, 'b'},
462 : {"large-objects", no_argument, NULL, 'b'},
463 : {"no-blobs", no_argument, NULL, 'B'},
464 : {"no-large-objects", no_argument, NULL, 'B'},
465 : {"clean", no_argument, NULL, 'c'},
466 : {"create", no_argument, NULL, 'C'},
467 : {"dbname", required_argument, NULL, 'd'},
468 : {"extension", required_argument, NULL, 'e'},
469 : {"file", required_argument, NULL, 'f'},
470 : {"format", required_argument, NULL, 'F'},
471 : {"host", required_argument, NULL, 'h'},
472 : {"jobs", 1, NULL, 'j'},
473 : {"no-reconnect", no_argument, NULL, 'R'},
474 : {"no-owner", no_argument, NULL, 'O'},
475 : {"port", required_argument, NULL, 'p'},
476 : {"schema", required_argument, NULL, 'n'},
477 : {"exclude-schema", required_argument, NULL, 'N'},
478 : {"schema-only", no_argument, NULL, 's'},
479 : {"superuser", required_argument, NULL, 'S'},
480 : {"table", required_argument, NULL, 't'},
481 : {"exclude-table", required_argument, NULL, 'T'},
482 : {"no-password", no_argument, NULL, 'w'},
483 : {"password", no_argument, NULL, 'W'},
484 : {"username", required_argument, NULL, 'U'},
485 : {"verbose", no_argument, NULL, 'v'},
486 : {"no-privileges", no_argument, NULL, 'x'},
487 : {"no-acl", no_argument, NULL, 'x'},
488 : {"compress", required_argument, NULL, 'Z'},
489 : {"encoding", required_argument, NULL, 'E'},
490 : {"help", no_argument, NULL, '?'},
491 : {"version", no_argument, NULL, 'V'},
492 :
493 : /*
494 : * the following options don't have an equivalent short option letter
495 : */
496 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
497 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
498 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
499 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
500 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
501 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
502 : {"exclude-table-data", required_argument, NULL, 4},
503 : {"extra-float-digits", required_argument, NULL, 8},
504 : {"if-exists", no_argument, &dopt.if_exists, 1},
505 : {"inserts", no_argument, NULL, 9},
506 : {"lock-wait-timeout", required_argument, NULL, 2},
507 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
508 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
509 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
510 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
511 : {"role", required_argument, NULL, 3},
512 : {"section", required_argument, NULL, 5},
513 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
514 : {"snapshot", required_argument, NULL, 6},
515 : {"statistics", no_argument, NULL, 22},
516 : {"statistics-only", no_argument, NULL, 18},
517 : {"strict-names", no_argument, &strict_names, 1},
518 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
519 : {"no-comments", no_argument, &dopt.no_comments, 1},
520 : {"no-data", no_argument, NULL, 19},
521 : {"no-policies", no_argument, &dopt.no_policies, 1},
522 : {"no-publications", no_argument, &dopt.no_publications, 1},
523 : {"no-schema", no_argument, NULL, 20},
524 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
525 : {"no-statistics", no_argument, NULL, 21},
526 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
527 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
528 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
529 : {"no-sync", no_argument, NULL, 7},
530 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
531 : {"rows-per-insert", required_argument, NULL, 10},
532 : {"include-foreign-data", required_argument, NULL, 11},
533 : {"table-and-children", required_argument, NULL, 12},
534 : {"exclude-table-and-children", required_argument, NULL, 13},
535 : {"exclude-table-data-and-children", required_argument, NULL, 14},
536 : {"sync-method", required_argument, NULL, 15},
537 : {"filter", required_argument, NULL, 16},
538 : {"exclude-extension", required_argument, NULL, 17},
539 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
540 : {"restrict-key", required_argument, NULL, 25},
541 :
542 : {NULL, 0, NULL, 0}
543 : };
544 :
545 580 : pg_logging_init(argv[0]);
546 580 : pg_logging_set_level(PG_LOG_WARNING);
547 580 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
548 :
549 : /*
550 : * Initialize what we need for parallel execution, especially for thread
551 : * support on Windows.
552 : */
553 580 : init_parallel_dump_utils();
554 :
555 580 : progname = get_progname(argv[0]);
556 :
557 580 : if (argc > 1)
558 : {
559 580 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 : {
561 2 : help(progname);
562 2 : exit_nicely(0);
563 : }
564 578 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 : {
566 124 : puts("pg_dump (PostgreSQL) " PG_VERSION);
567 124 : exit_nicely(0);
568 : }
569 : }
570 :
571 454 : InitDumpOptions(&dopt);
572 :
573 2520 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 2520 : long_options, &optindex)) != -1)
575 : {
576 2082 : switch (c)
577 : {
578 18 : case 'a': /* Dump data only */
579 18 : data_only = true;
580 18 : break;
581 :
582 2 : case 'b': /* Dump LOs */
583 2 : dopt.outputLOs = true;
584 2 : break;
585 :
586 4 : case 'B': /* Don't dump LOs */
587 4 : dopt.dontOutputLOs = true;
588 4 : break;
589 :
590 12 : case 'c': /* clean (i.e., drop) schema prior to create */
591 12 : dopt.outputClean = 1;
592 12 : break;
593 :
594 58 : case 'C': /* Create DB */
595 58 : dopt.outputCreateDB = 1;
596 58 : break;
597 :
598 10 : case 'd': /* database name */
599 10 : dopt.cparams.dbname = pg_strdup(optarg);
600 10 : break;
601 :
602 8 : case 'e': /* include extension(s) */
603 8 : simple_string_list_append(&extension_include_patterns, optarg);
604 8 : dopt.include_everything = false;
605 8 : break;
606 :
607 4 : case 'E': /* Dump encoding */
608 4 : dumpencoding = pg_strdup(optarg);
609 4 : break;
610 :
611 360 : case 'f':
612 360 : filename = pg_strdup(optarg);
613 360 : break;
614 :
615 216 : case 'F':
616 216 : format = pg_strdup(optarg);
617 216 : break;
618 :
619 68 : case 'h': /* server host */
620 68 : dopt.cparams.pghost = pg_strdup(optarg);
621 68 : break;
622 :
623 22 : case 'j': /* number of dump jobs */
624 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
625 : PG_MAX_JOBS,
626 : &numWorkers))
627 2 : exit_nicely(1);
628 20 : break;
629 :
630 34 : case 'n': /* include schema(s) */
631 34 : simple_string_list_append(&schema_include_patterns, optarg);
632 34 : dopt.include_everything = false;
633 34 : break;
634 :
635 2 : case 'N': /* exclude schema(s) */
636 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
637 2 : break;
638 :
639 4 : case 'O': /* Don't reconnect to match owner */
640 4 : dopt.outputNoOwner = 1;
641 4 : break;
642 :
643 146 : case 'p': /* server port */
644 146 : dopt.cparams.pgport = pg_strdup(optarg);
645 146 : break;
646 :
647 4 : case 'R':
648 : /* no-op, still accepted for backwards compatibility */
649 4 : break;
650 :
651 14 : case 's': /* dump schema only */
652 14 : schema_only = true;
653 14 : break;
654 :
655 2 : case 'S': /* Username for superuser in plain text output */
656 2 : dopt.outputSuperuser = pg_strdup(optarg);
657 2 : break;
658 :
659 16 : case 't': /* include table(s) */
660 16 : simple_string_list_append(&table_include_patterns, optarg);
661 16 : dopt.include_everything = false;
662 16 : break;
663 :
664 8 : case 'T': /* exclude table(s) */
665 8 : simple_string_list_append(&table_exclude_patterns, optarg);
666 8 : break;
667 :
668 72 : case 'U':
669 72 : dopt.cparams.username = pg_strdup(optarg);
670 72 : break;
671 :
672 12 : case 'v': /* verbose */
673 12 : g_verbose = true;
674 12 : pg_logging_increase_verbosity();
675 12 : break;
676 :
677 2 : case 'w':
678 2 : dopt.cparams.promptPassword = TRI_NO;
679 2 : break;
680 :
681 0 : case 'W':
682 0 : dopt.cparams.promptPassword = TRI_YES;
683 0 : break;
684 :
685 4 : case 'x': /* skip ACL dump */
686 4 : dopt.aclsSkip = true;
687 4 : break;
688 :
689 24 : case 'Z': /* Compression */
690 24 : parse_compress_options(optarg, &compression_algorithm_str,
691 : &compression_detail);
692 24 : user_compression_defined = true;
693 24 : break;
694 :
695 256 : case 0:
696 : /* This covers the long options. */
697 256 : break;
698 :
699 4 : case 2: /* lock-wait-timeout */
700 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
701 4 : break;
702 :
703 6 : case 3: /* SET ROLE */
704 6 : use_role = pg_strdup(optarg);
705 6 : break;
706 :
707 2 : case 4: /* exclude table(s) data */
708 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
709 2 : break;
710 :
711 12 : case 5: /* section */
712 12 : set_dump_section(optarg, &dopt.dumpSections);
713 12 : break;
714 :
715 0 : case 6: /* snapshot */
716 0 : dumpsnapshot = pg_strdup(optarg);
717 0 : break;
718 :
719 276 : case 7: /* no-sync */
720 276 : dosync = false;
721 276 : break;
722 :
723 2 : case 8:
724 2 : have_extra_float_digits = true;
725 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
726 : &extra_float_digits))
727 2 : exit_nicely(1);
728 0 : break;
729 :
730 4 : case 9: /* inserts */
731 :
732 : /*
733 : * dump_inserts also stores --rows-per-insert, careful not to
734 : * overwrite that.
735 : */
736 4 : if (dopt.dump_inserts == 0)
737 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
738 4 : break;
739 :
740 4 : case 10: /* rows per insert */
741 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
742 : &dopt.dump_inserts))
743 2 : exit_nicely(1);
744 2 : break;
745 :
746 8 : case 11: /* include foreign data */
747 8 : simple_string_list_append(&foreign_servers_include_patterns,
748 : optarg);
749 8 : break;
750 :
751 2 : case 12: /* include table(s) and their children */
752 2 : simple_string_list_append(&table_include_patterns_and_children,
753 : optarg);
754 2 : dopt.include_everything = false;
755 2 : break;
756 :
757 2 : case 13: /* exclude table(s) and their children */
758 2 : simple_string_list_append(&table_exclude_patterns_and_children,
759 : optarg);
760 2 : break;
761 :
762 2 : case 14: /* exclude data of table(s) and children */
763 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
764 : optarg);
765 2 : break;
766 :
767 0 : case 15:
768 0 : if (!parse_sync_method(optarg, &sync_method))
769 0 : exit_nicely(1);
770 0 : break;
771 :
772 52 : case 16: /* read object filters from file */
773 52 : read_dump_filters(optarg, &dopt);
774 44 : break;
775 :
776 2 : case 17: /* exclude extension(s) */
777 2 : simple_string_list_append(&extension_exclude_patterns,
778 : optarg);
779 2 : break;
780 :
781 8 : case 18:
782 8 : statistics_only = true;
783 8 : break;
784 :
785 72 : case 19:
786 72 : no_data = true;
787 72 : break;
788 :
789 4 : case 20:
790 4 : no_schema = true;
791 4 : break;
792 :
793 16 : case 21:
794 16 : no_statistics = true;
795 16 : break;
796 :
797 168 : case 22:
798 168 : with_statistics = true;
799 168 : break;
800 :
801 52 : case 25:
802 52 : dopt.restrict_key = pg_strdup(optarg);
803 52 : break;
804 :
805 2 : default:
806 : /* getopt_long already emitted a complaint */
807 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
808 2 : exit_nicely(1);
809 : }
810 : }
811 :
812 : /*
813 : * Non-option argument specifies database name as long as it wasn't
814 : * already specified with -d / --dbname
815 : */
816 438 : if (optind < argc && dopt.cparams.dbname == NULL)
817 366 : dopt.cparams.dbname = argv[optind++];
818 :
819 : /* Complain if any arguments remain */
820 438 : if (optind < argc)
821 : {
822 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
823 : argv[optind]);
824 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
825 2 : exit_nicely(1);
826 : }
827 :
828 : /* --column-inserts implies --inserts */
829 436 : if (dopt.column_inserts && dopt.dump_inserts == 0)
830 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
831 :
832 : /* reject conflicting "-only" options */
833 436 : if (data_only && schema_only)
834 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
835 434 : if (schema_only && statistics_only)
836 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
837 432 : if (data_only && statistics_only)
838 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
839 :
840 : /* reject conflicting "-only" and "no-" options */
841 430 : if (data_only && no_data)
842 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
843 430 : if (schema_only && no_schema)
844 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
845 430 : if (statistics_only && no_statistics)
846 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
847 :
848 : /* reject conflicting "no-" options */
849 428 : if (with_statistics && no_statistics)
850 0 : pg_fatal("options --statistics and --no-statistics cannot be used together");
851 :
852 : /* reject conflicting "-only" options */
853 428 : if (data_only && with_statistics)
854 0 : pg_fatal("options %s and %s cannot be used together",
855 : "-a/--data-only", "--statistics");
856 428 : if (schema_only && with_statistics)
857 2 : pg_fatal("options %s and %s cannot be used together",
858 : "-s/--schema-only", "--statistics");
859 :
860 426 : if (schema_only && foreign_servers_include_patterns.head != NULL)
861 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
862 :
863 424 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
864 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
865 :
866 422 : if (data_only && dopt.outputClean)
867 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
868 :
869 420 : if (dopt.if_exists && !dopt.outputClean)
870 2 : pg_fatal("option --if-exists requires option -c/--clean");
871 :
872 : /*
873 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
874 : * "--schema-only --no-schema", will have already caused an error in one
875 : * of the checks above.
876 : */
877 418 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
878 836 : data_only) && !no_data;
879 418 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
880 836 : schema_only) && !no_schema;
881 418 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
882 836 : (statistics_only || with_statistics)) && !no_statistics;
883 :
884 :
885 : /*
886 : * --inserts are already implied above if --column-inserts or
887 : * --rows-per-insert were specified.
888 : */
889 418 : if (dopt.do_nothing && dopt.dump_inserts == 0)
890 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
891 :
892 : /* Identify archive format to emit */
893 416 : archiveFormat = parseArchiveFormat(format, &archiveMode);
894 :
895 : /* archiveFormat specific setup */
896 414 : if (archiveFormat == archNull)
897 : {
898 306 : plainText = 1;
899 :
900 : /*
901 : * If you don't provide a restrict key, one will be appointed for you.
902 : */
903 306 : if (!dopt.restrict_key)
904 254 : dopt.restrict_key = generate_restrict_key();
905 306 : if (!dopt.restrict_key)
906 0 : pg_fatal("could not generate restrict key");
907 306 : if (!valid_restrict_key(dopt.restrict_key))
908 0 : pg_fatal("invalid restrict key");
909 : }
910 108 : else if (dopt.restrict_key)
911 0 : pg_fatal("option --restrict-key can only be used with --format=plain");
912 :
913 : /*
914 : * Custom and directory formats are compressed by default with gzip when
915 : * available, not the others. If gzip is not available, no compression is
916 : * done by default.
917 : */
918 414 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
919 102 : !user_compression_defined)
920 : {
921 : #ifdef HAVE_LIBZ
922 92 : compression_algorithm_str = "gzip";
923 : #else
924 : compression_algorithm_str = "none";
925 : #endif
926 : }
927 :
928 : /*
929 : * Compression options
930 : */
931 414 : if (!parse_compress_algorithm(compression_algorithm_str,
932 : &compression_algorithm))
933 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
934 : compression_algorithm_str);
935 :
936 412 : parse_compress_specification(compression_algorithm, compression_detail,
937 : &compression_spec);
938 412 : error_detail = validate_compress_specification(&compression_spec);
939 412 : if (error_detail != NULL)
940 6 : pg_fatal("invalid compression specification: %s",
941 : error_detail);
942 :
943 406 : error_detail = supports_compression(compression_spec);
944 406 : if (error_detail != NULL)
945 0 : pg_fatal("%s", error_detail);
946 :
947 : /*
948 : * Disable support for zstd workers for now - these are based on
949 : * threading, and it's unclear how it interacts with parallel dumps on
950 : * platforms where that relies on threads too (e.g. Windows).
951 : */
952 406 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
953 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
954 : "workers");
955 :
956 : /*
957 : * If emitting an archive format, we always want to emit a DATABASE item,
958 : * in case --create is specified at pg_restore time.
959 : */
960 406 : if (!plainText)
961 108 : dopt.outputCreateDB = 1;
962 :
963 : /* Parallel backup only in the directory archive format so far */
964 406 : if (archiveFormat != archDirectory && numWorkers > 1)
965 2 : pg_fatal("parallel backup only supported by the directory format");
966 :
967 : /* Open the output file */
968 404 : fout = CreateArchive(filename, archiveFormat, compression_spec,
969 : dosync, archiveMode, setupDumpWorker, sync_method);
970 :
971 : /* Make dump options accessible right away */
972 402 : SetArchiveOptions(fout, &dopt, NULL);
973 :
974 : /* Register the cleanup hook */
975 402 : on_exit_close_archive(fout);
976 :
977 : /* Let the archiver know how noisy to be */
978 402 : fout->verbose = g_verbose;
979 :
980 :
981 : /*
982 : * We allow the server to be back to 9.2, and up to any minor release of
983 : * our own major version. (See also version check in pg_dumpall.c.)
984 : */
985 402 : fout->minRemoteVersion = 90200;
986 402 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
987 :
988 402 : fout->numWorkers = numWorkers;
989 :
990 : /*
991 : * Open the database using the Archiver, so it knows about it. Errors mean
992 : * death.
993 : */
994 402 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
995 398 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
996 :
997 : /*
998 : * On hot standbys, never try to dump unlogged table data, since it will
999 : * just throw an error.
1000 : */
1001 398 : if (fout->isStandby)
1002 8 : dopt.no_unlogged_table_data = true;
1003 :
1004 : /*
1005 : * Find the last built-in OID, if needed (prior to 8.1)
1006 : *
1007 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1008 : */
1009 398 : g_last_builtin_oid = FirstNormalObjectId - 1;
1010 :
1011 398 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1012 :
1013 : /* Expand schema selection patterns into OID lists */
1014 398 : if (schema_include_patterns.head != NULL)
1015 : {
1016 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1017 : &schema_include_oids,
1018 : strict_names);
1019 24 : if (schema_include_oids.head == NULL)
1020 2 : pg_fatal("no matching schemas were found");
1021 : }
1022 384 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1023 : &schema_exclude_oids,
1024 : false);
1025 : /* non-matching exclusion patterns aren't an error */
1026 :
1027 : /* Expand table selection patterns into OID lists */
1028 384 : expand_table_name_patterns(fout, &table_include_patterns,
1029 : &table_include_oids,
1030 : strict_names, false);
1031 374 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1032 : &table_include_oids,
1033 : strict_names, true);
1034 374 : if ((table_include_patterns.head != NULL ||
1035 352 : table_include_patterns_and_children.head != NULL) &&
1036 26 : table_include_oids.head == NULL)
1037 4 : pg_fatal("no matching tables were found");
1038 :
1039 370 : expand_table_name_patterns(fout, &table_exclude_patterns,
1040 : &table_exclude_oids,
1041 : false, false);
1042 370 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1043 : &table_exclude_oids,
1044 : false, true);
1045 :
1046 370 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1047 : &tabledata_exclude_oids,
1048 : false, false);
1049 370 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1050 : &tabledata_exclude_oids,
1051 : false, true);
1052 :
1053 370 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1054 : &foreign_servers_include_oids);
1055 :
1056 : /* non-matching exclusion patterns aren't an error */
1057 :
1058 : /* Expand extension selection patterns into OID lists */
1059 368 : if (extension_include_patterns.head != NULL)
1060 : {
1061 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1062 : &extension_include_oids,
1063 : strict_names);
1064 10 : if (extension_include_oids.head == NULL)
1065 2 : pg_fatal("no matching extensions were found");
1066 : }
1067 366 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1068 : &extension_exclude_oids,
1069 : false);
1070 : /* non-matching exclusion patterns aren't an error */
1071 :
1072 : /*
1073 : * Dumping LOs is the default for dumps where an inclusion switch is not
1074 : * used (an "include everything" dump). -B can be used to exclude LOs
1075 : * from those dumps. -b can be used to include LOs even when an inclusion
1076 : * switch is used.
1077 : *
1078 : * -s means "schema only" and LOs are data, not schema, so we never
1079 : * include LOs when -s is used.
1080 : */
1081 366 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1082 236 : dopt.outputLOs = true;
1083 :
1084 : /*
1085 : * Collect role names so we can map object owner OIDs to names.
1086 : */
1087 366 : collectRoleNames(fout);
1088 :
1089 : /*
1090 : * Now scan the database and create DumpableObject structs for all the
1091 : * objects we intend to dump.
1092 : */
1093 366 : tblinfo = getSchemaData(fout, &numTables);
1094 :
1095 364 : if (dopt.dumpData)
1096 : {
1097 284 : getTableData(&dopt, tblinfo, numTables, 0);
1098 284 : buildMatViewRefreshDependencies(fout);
1099 284 : if (!dopt.dumpSchema)
1100 14 : getTableDataFKConstraints();
1101 : }
1102 :
1103 364 : if (!dopt.dumpData && dopt.sequence_data)
1104 64 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1105 :
1106 : /*
1107 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1108 : * associated pg_shdepend rows. This is faster to restore than the
1109 : * equivalent set of large object commands. We can only do this for
1110 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1111 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1112 : */
1113 364 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1114 : {
1115 72 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1116 72 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1117 :
1118 72 : makeTableDataInfo(&dopt, lo_metadata);
1119 72 : makeTableDataInfo(&dopt, shdepend);
1120 :
1121 : /*
1122 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1123 : * pg_shdepend and any large object comments/seclabels.
1124 : */
1125 72 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1126 72 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1127 :
1128 : /*
1129 : * Only dump large object shdepend rows for this database.
1130 : */
1131 72 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1132 : "AND dbid = (SELECT oid FROM pg_database "
1133 : " WHERE datname = current_database())";
1134 :
1135 : /*
1136 : * If upgrading from v16 or newer, only dump large objects with
1137 : * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1138 : * pg_largeobject_metadata's files (which is usually faster) but we
1139 : * still need to dump LOs with comments/seclabels here so that the
1140 : * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1141 : * can't copy/link the files from older versions because aclitem
1142 : * (needed by pg_largeobject_metadata.lomacl) changed its storage
1143 : * format in v16.
1144 : */
1145 72 : if (fout->remoteVersion >= 160000)
1146 72 : lo_metadata->dataObj->filtercond = "WHERE oid IN "
1147 : "(SELECT objoid FROM pg_description "
1148 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1149 : "UNION SELECT objoid FROM pg_seclabel "
1150 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1151 : }
1152 :
1153 : /*
1154 : * In binary-upgrade mode, we do not have to worry about the actual LO
1155 : * data or the associated metadata that resides in the pg_largeobject and
1156 : * pg_largeobject_metadata tables, respectively.
1157 : *
1158 : * However, we do need to collect LO information as there may be comments
1159 : * or other information on LOs that we do need to dump out.
1160 : */
1161 364 : if (dopt.outputLOs || dopt.binary_upgrade)
1162 308 : getLOs(fout);
1163 :
1164 : /*
1165 : * Collect dependency data to assist in ordering the objects.
1166 : */
1167 364 : getDependencies(fout);
1168 :
1169 : /*
1170 : * Collect ACLs, comments, and security labels, if wanted.
1171 : */
1172 364 : if (!dopt.aclsSkip)
1173 360 : getAdditionalACLs(fout);
1174 364 : if (!dopt.no_comments)
1175 364 : collectComments(fout);
1176 364 : if (!dopt.no_security_labels)
1177 364 : collectSecLabels(fout);
1178 :
1179 : /* For binary upgrade mode, collect required pg_class information. */
1180 364 : if (dopt.binary_upgrade)
1181 72 : collectBinaryUpgradeClassOids(fout);
1182 :
1183 : /* Collect sequence information. */
1184 364 : collectSequences(fout);
1185 :
1186 : /* Lastly, create dummy objects to represent the section boundaries */
1187 364 : boundaryObjs = createBoundaryObjects();
1188 :
1189 : /* Get pointers to all the known DumpableObjects */
1190 364 : getDumpableObjects(&dobjs, &numObjs);
1191 :
1192 : /*
1193 : * Add dummy dependencies to enforce the dump section ordering.
1194 : */
1195 364 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1196 :
1197 : /*
1198 : * Sort the objects into a safe dump order (no forward references).
1199 : *
1200 : * We rely on dependency information to help us determine a safe order, so
1201 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1202 : * ensure that logically identical schemas will dump identically.
1203 : */
1204 364 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1205 :
1206 364 : sortDumpableObjects(dobjs, numObjs,
1207 364 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1208 :
1209 : /*
1210 : * Create archive TOC entries for all the objects to be dumped, in a safe
1211 : * order.
1212 : */
1213 :
1214 : /*
1215 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1216 : */
1217 364 : dumpEncoding(fout);
1218 364 : dumpStdStrings(fout);
1219 364 : dumpSearchPath(fout);
1220 :
1221 : /* The database items are always next, unless we don't want them at all */
1222 364 : if (dopt.outputCreateDB)
1223 164 : dumpDatabase(fout);
1224 :
1225 : /* Now the rearrangeable objects. */
1226 1355542 : for (i = 0; i < numObjs; i++)
1227 1355178 : dumpDumpableObject(fout, dobjs[i]);
1228 :
1229 : /*
1230 : * Set up options info to ensure we dump what we want.
1231 : */
1232 364 : ropt = NewRestoreOptions();
1233 364 : ropt->filename = filename;
1234 :
1235 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1236 364 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1237 364 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1238 364 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1239 364 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1240 364 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1241 364 : ropt->dropSchema = dopt.outputClean;
1242 364 : ropt->dumpData = dopt.dumpData;
1243 364 : ropt->dumpSchema = dopt.dumpSchema;
1244 364 : ropt->dumpStatistics = dopt.dumpStatistics;
1245 364 : ropt->if_exists = dopt.if_exists;
1246 364 : ropt->column_inserts = dopt.column_inserts;
1247 364 : ropt->dumpSections = dopt.dumpSections;
1248 364 : ropt->aclsSkip = dopt.aclsSkip;
1249 364 : ropt->superuser = dopt.outputSuperuser;
1250 364 : ropt->createDB = dopt.outputCreateDB;
1251 364 : ropt->noOwner = dopt.outputNoOwner;
1252 364 : ropt->noTableAm = dopt.outputNoTableAm;
1253 364 : ropt->noTablespace = dopt.outputNoTablespaces;
1254 364 : ropt->disable_triggers = dopt.disable_triggers;
1255 364 : ropt->use_setsessauth = dopt.use_setsessauth;
1256 364 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1257 364 : ropt->dump_inserts = dopt.dump_inserts;
1258 364 : ropt->no_comments = dopt.no_comments;
1259 364 : ropt->no_policies = dopt.no_policies;
1260 364 : ropt->no_publications = dopt.no_publications;
1261 364 : ropt->no_security_labels = dopt.no_security_labels;
1262 364 : ropt->no_subscriptions = dopt.no_subscriptions;
1263 364 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1264 364 : ropt->include_everything = dopt.include_everything;
1265 364 : ropt->enable_row_security = dopt.enable_row_security;
1266 364 : ropt->sequence_data = dopt.sequence_data;
1267 364 : ropt->binary_upgrade = dopt.binary_upgrade;
1268 364 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1269 :
1270 364 : ropt->compression_spec = compression_spec;
1271 :
1272 364 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1273 :
1274 364 : SetArchiveOptions(fout, &dopt, ropt);
1275 :
1276 : /* Mark which entries should be output */
1277 364 : ProcessArchiveRestoreOptions(fout);
1278 :
1279 : /*
1280 : * The archive's TOC entries are now marked as to which ones will actually
1281 : * be output, so we can set up their dependency lists properly. This isn't
1282 : * necessary for plain-text output, though.
1283 : */
1284 364 : if (!plainText)
1285 106 : BuildArchiveDependencies(fout);
1286 :
1287 : /*
1288 : * And finally we can do the actual output.
1289 : *
1290 : * Note: for non-plain-text output formats, the output file is written
1291 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1292 : * right now.
1293 : */
1294 364 : if (plainText)
1295 258 : RestoreArchive(fout);
1296 :
1297 362 : CloseArchive(fout);
1298 :
1299 362 : exit_nicely(0);
1300 : }
1301 :
1302 :
1303 : static void
1304 2 : help(const char *progname)
1305 : {
1306 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1307 2 : printf(_("Usage:\n"));
1308 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1309 :
1310 2 : printf(_("\nGeneral options:\n"));
1311 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1312 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1313 : " plain text (default))\n"));
1314 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1315 2 : printf(_(" -v, --verbose verbose mode\n"));
1316 2 : printf(_(" -V, --version output version information, then exit\n"));
1317 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1318 : " compress as specified\n"));
1319 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1320 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1321 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1322 2 : printf(_(" -?, --help show this help, then exit\n"));
1323 :
1324 2 : printf(_("\nOptions controlling the output content:\n"));
1325 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1326 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1327 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1328 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1329 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1330 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1331 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1332 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1333 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1334 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1335 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1336 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1337 : " plain-text format\n"));
1338 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1339 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1340 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1341 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1342 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1343 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1344 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1345 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1346 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1347 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1348 : " access to)\n"));
1349 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1350 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1351 : " do NOT dump the specified table(s), including\n"
1352 : " child and partition tables\n"));
1353 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1354 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1355 : " do NOT dump data for the specified table(s),\n"
1356 : " including child and partition tables\n"));
1357 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1358 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1359 : " based on expressions in FILENAME\n"));
1360 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1361 2 : printf(_(" --include-foreign-data=PATTERN\n"
1362 : " include data of foreign tables on foreign\n"
1363 : " servers matching PATTERN\n"));
1364 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1365 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1366 2 : printf(_(" --no-comments do not dump comment commands\n"));
1367 2 : printf(_(" --no-data do not dump data\n"));
1368 2 : printf(_(" --no-policies do not dump row security policies\n"));
1369 2 : printf(_(" --no-publications do not dump publications\n"));
1370 2 : printf(_(" --no-schema do not dump schema\n"));
1371 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1372 2 : printf(_(" --no-statistics do not dump statistics\n"));
1373 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1374 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1375 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1376 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1377 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1378 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1379 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1380 2 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1381 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1382 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1383 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1384 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1385 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1386 2 : printf(_(" --statistics dump the statistics\n"));
1387 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1388 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1389 : " match at least one entity each\n"));
1390 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1391 : " child and partition tables\n"));
1392 2 : printf(_(" --use-set-session-authorization\n"
1393 : " use SET SESSION AUTHORIZATION commands instead of\n"
1394 : " ALTER OWNER commands to set ownership\n"));
1395 :
1396 2 : printf(_("\nConnection options:\n"));
1397 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1398 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1399 2 : printf(_(" -p, --port=PORT database server port number\n"));
1400 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1401 2 : printf(_(" -w, --no-password never prompt for password\n"));
1402 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1403 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1404 :
1405 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1406 : "variable value is used.\n\n"));
1407 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1408 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1409 2 : }
1410 :
1411 : static void
1412 430 : setup_connection(Archive *AH, const char *dumpencoding,
1413 : const char *dumpsnapshot, char *use_role)
1414 : {
1415 430 : DumpOptions *dopt = AH->dopt;
1416 430 : PGconn *conn = GetConnection(AH);
1417 : const char *std_strings;
1418 :
1419 430 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1420 :
1421 : /*
1422 : * Set the client encoding if requested.
1423 : */
1424 430 : if (dumpencoding)
1425 : {
1426 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1427 0 : pg_fatal("invalid client encoding \"%s\" specified",
1428 : dumpencoding);
1429 : }
1430 :
1431 : /*
1432 : * Get the active encoding and the standard_conforming_strings setting, so
1433 : * we know how to escape strings.
1434 : */
1435 430 : AH->encoding = PQclientEncoding(conn);
1436 430 : setFmtEncoding(AH->encoding);
1437 :
1438 430 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1439 430 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1440 :
1441 : /*
1442 : * Set the role if requested. In a parallel dump worker, we'll be passed
1443 : * use_role == NULL, but AH->use_role is already set (if user specified it
1444 : * originally) and we should use that.
1445 : */
1446 430 : if (!use_role && AH->use_role)
1447 4 : use_role = AH->use_role;
1448 :
1449 : /* Set the role if requested */
1450 430 : if (use_role)
1451 : {
1452 10 : PQExpBuffer query = createPQExpBuffer();
1453 :
1454 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1455 10 : ExecuteSqlStatement(AH, query->data);
1456 10 : destroyPQExpBuffer(query);
1457 :
1458 : /* save it for possible later use by parallel workers */
1459 10 : if (!AH->use_role)
1460 6 : AH->use_role = pg_strdup(use_role);
1461 : }
1462 :
1463 : /* Set the datestyle to ISO to ensure the dump's portability */
1464 430 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1465 :
1466 : /* Likewise, avoid using sql_standard intervalstyle */
1467 430 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1468 :
1469 : /*
1470 : * Use an explicitly specified extra_float_digits if it has been provided.
1471 : * Otherwise, set extra_float_digits so that we can dump float data
1472 : * exactly (given correctly implemented float I/O code, anyway).
1473 : */
1474 430 : if (have_extra_float_digits)
1475 : {
1476 0 : PQExpBuffer q = createPQExpBuffer();
1477 :
1478 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1479 : extra_float_digits);
1480 0 : ExecuteSqlStatement(AH, q->data);
1481 0 : destroyPQExpBuffer(q);
1482 : }
1483 : else
1484 430 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1485 :
1486 : /*
1487 : * Disable synchronized scanning, to prevent unpredictable changes in row
1488 : * ordering across a dump and reload.
1489 : */
1490 430 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1491 :
1492 : /*
1493 : * Disable timeouts if supported.
1494 : */
1495 430 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1496 430 : if (AH->remoteVersion >= 90300)
1497 430 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1498 430 : if (AH->remoteVersion >= 90600)
1499 430 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1500 430 : if (AH->remoteVersion >= 170000)
1501 430 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1502 :
1503 : /*
1504 : * Quote all identifiers, if requested.
1505 : */
1506 430 : if (quote_all_identifiers)
1507 68 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1508 :
1509 : /*
1510 : * Adjust row-security mode, if supported.
1511 : */
1512 430 : if (AH->remoteVersion >= 90500)
1513 : {
1514 430 : if (dopt->enable_row_security)
1515 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1516 : else
1517 430 : ExecuteSqlStatement(AH, "SET row_security = off");
1518 : }
1519 :
1520 : /*
1521 : * For security reasons, we restrict the expansion of non-system views and
1522 : * access to foreign tables during the pg_dump process. This restriction
1523 : * is adjusted when dumping foreign table data.
1524 : */
1525 430 : set_restrict_relation_kind(AH, "view, foreign-table");
1526 :
1527 : /*
1528 : * Initialize prepared-query state to "nothing prepared". We do this here
1529 : * so that a parallel dump worker will have its own state.
1530 : */
1531 430 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1532 :
1533 : /*
1534 : * Start transaction-snapshot mode transaction to dump consistent data.
1535 : */
1536 430 : ExecuteSqlStatement(AH, "BEGIN");
1537 :
1538 : /*
1539 : * To support the combination of serializable_deferrable with the jobs
1540 : * option we use REPEATABLE READ for the worker connections that are
1541 : * passed a snapshot. As long as the snapshot is acquired in a
1542 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1543 : * REPEATABLE READ transaction provides the appropriate integrity
1544 : * guarantees. This is a kluge, but safe for back-patching.
1545 : */
1546 430 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1547 0 : ExecuteSqlStatement(AH,
1548 : "SET TRANSACTION ISOLATION LEVEL "
1549 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1550 : else
1551 430 : ExecuteSqlStatement(AH,
1552 : "SET TRANSACTION ISOLATION LEVEL "
1553 : "REPEATABLE READ, READ ONLY");
1554 :
1555 : /*
1556 : * If user specified a snapshot to use, select that. In a parallel dump
1557 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1558 : * is already set (if the server can handle it) and we should use that.
1559 : */
1560 430 : if (dumpsnapshot)
1561 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1562 :
1563 430 : if (AH->sync_snapshot_id)
1564 : {
1565 32 : PQExpBuffer query = createPQExpBuffer();
1566 :
1567 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1568 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1569 32 : ExecuteSqlStatement(AH, query->data);
1570 32 : destroyPQExpBuffer(query);
1571 : }
1572 398 : else if (AH->numWorkers > 1)
1573 : {
1574 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1575 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1576 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1577 : }
1578 430 : }
1579 :
1580 : /* Set up connection for a parallel worker process */
1581 : static void
1582 32 : setupDumpWorker(Archive *AH)
1583 : {
1584 : /*
1585 : * We want to re-select all the same values the leader connection is
1586 : * using. We'll have inherited directly-usable values in
1587 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1588 : * inherited encoding value back to a string to pass to setup_connection.
1589 : */
1590 32 : setup_connection(AH,
1591 : pg_encoding_to_char(AH->encoding),
1592 : NULL,
1593 : NULL);
1594 32 : }
1595 :
1596 : static char *
1597 16 : get_synchronized_snapshot(Archive *fout)
1598 : {
1599 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1600 : char *result;
1601 : PGresult *res;
1602 :
1603 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1604 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1605 16 : PQclear(res);
1606 :
1607 16 : return result;
1608 : }
1609 :
1610 : static ArchiveFormat
1611 416 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1612 : {
1613 : ArchiveFormat archiveFormat;
1614 :
1615 416 : *mode = archModeWrite;
1616 :
1617 416 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1618 : {
1619 : /* This is used by pg_dumpall, and is not documented */
1620 86 : archiveFormat = archNull;
1621 86 : *mode = archModeAppend;
1622 : }
1623 330 : else if (pg_strcasecmp(format, "c") == 0)
1624 0 : archiveFormat = archCustom;
1625 330 : else if (pg_strcasecmp(format, "custom") == 0)
1626 82 : archiveFormat = archCustom;
1627 248 : else if (pg_strcasecmp(format, "d") == 0)
1628 0 : archiveFormat = archDirectory;
1629 248 : else if (pg_strcasecmp(format, "directory") == 0)
1630 20 : archiveFormat = archDirectory;
1631 228 : else if (pg_strcasecmp(format, "p") == 0)
1632 214 : archiveFormat = archNull;
1633 14 : else if (pg_strcasecmp(format, "plain") == 0)
1634 6 : archiveFormat = archNull;
1635 8 : else if (pg_strcasecmp(format, "t") == 0)
1636 0 : archiveFormat = archTar;
1637 8 : else if (pg_strcasecmp(format, "tar") == 0)
1638 6 : archiveFormat = archTar;
1639 : else
1640 2 : pg_fatal("invalid output format \"%s\" specified", format);
1641 414 : return archiveFormat;
1642 : }
1643 :
1644 : /*
1645 : * Find the OIDs of all schemas matching the given list of patterns,
1646 : * and append them to the given OID list.
1647 : */
1648 : static void
1649 420 : expand_schema_name_patterns(Archive *fout,
1650 : SimpleStringList *patterns,
1651 : SimpleOidList *oids,
1652 : bool strict_names)
1653 : {
1654 : PQExpBuffer query;
1655 : PGresult *res;
1656 : SimpleStringListCell *cell;
1657 : int i;
1658 :
1659 420 : if (patterns->head == NULL)
1660 378 : return; /* nothing to do */
1661 :
1662 42 : query = createPQExpBuffer();
1663 :
1664 : /*
1665 : * The loop below runs multiple SELECTs might sometimes result in
1666 : * duplicate entries in the OID list, but we don't care.
1667 : */
1668 :
1669 72 : for (cell = patterns->head; cell; cell = cell->next)
1670 : {
1671 : PQExpBufferData dbbuf;
1672 : int dotcnt;
1673 :
1674 42 : appendPQExpBufferStr(query,
1675 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1676 42 : initPQExpBuffer(&dbbuf);
1677 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1678 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1679 : &dotcnt);
1680 42 : if (dotcnt > 1)
1681 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1682 : cell->val);
1683 38 : else if (dotcnt == 1)
1684 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1685 32 : termPQExpBuffer(&dbbuf);
1686 :
1687 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1688 32 : if (strict_names && PQntuples(res) == 0)
1689 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1690 :
1691 58 : for (i = 0; i < PQntuples(res); i++)
1692 : {
1693 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1694 : }
1695 :
1696 30 : PQclear(res);
1697 30 : resetPQExpBuffer(query);
1698 : }
1699 :
1700 30 : destroyPQExpBuffer(query);
1701 : }
1702 :
1703 : /*
1704 : * Find the OIDs of all extensions matching the given list of patterns,
1705 : * and append them to the given OID list.
1706 : */
1707 : static void
1708 376 : expand_extension_name_patterns(Archive *fout,
1709 : SimpleStringList *patterns,
1710 : SimpleOidList *oids,
1711 : bool strict_names)
1712 : {
1713 : PQExpBuffer query;
1714 : PGresult *res;
1715 : SimpleStringListCell *cell;
1716 : int i;
1717 :
1718 376 : if (patterns->head == NULL)
1719 362 : return; /* nothing to do */
1720 :
1721 14 : query = createPQExpBuffer();
1722 :
1723 : /*
1724 : * The loop below runs multiple SELECTs might sometimes result in
1725 : * duplicate entries in the OID list, but we don't care.
1726 : */
1727 28 : for (cell = patterns->head; cell; cell = cell->next)
1728 : {
1729 : int dotcnt;
1730 :
1731 14 : appendPQExpBufferStr(query,
1732 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1733 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1734 : false, NULL, "e.extname", NULL, NULL, NULL,
1735 : &dotcnt);
1736 14 : if (dotcnt > 0)
1737 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1738 : cell->val);
1739 :
1740 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1741 14 : if (strict_names && PQntuples(res) == 0)
1742 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1743 :
1744 26 : for (i = 0; i < PQntuples(res); i++)
1745 : {
1746 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1747 : }
1748 :
1749 14 : PQclear(res);
1750 14 : resetPQExpBuffer(query);
1751 : }
1752 :
1753 14 : destroyPQExpBuffer(query);
1754 : }
1755 :
1756 : /*
1757 : * Find the OIDs of all foreign servers matching the given list of patterns,
1758 : * and append them to the given OID list.
1759 : */
1760 : static void
1761 370 : expand_foreign_server_name_patterns(Archive *fout,
1762 : SimpleStringList *patterns,
1763 : SimpleOidList *oids)
1764 : {
1765 : PQExpBuffer query;
1766 : PGresult *res;
1767 : SimpleStringListCell *cell;
1768 : int i;
1769 :
1770 370 : if (patterns->head == NULL)
1771 364 : return; /* nothing to do */
1772 :
1773 6 : query = createPQExpBuffer();
1774 :
1775 : /*
1776 : * The loop below runs multiple SELECTs might sometimes result in
1777 : * duplicate entries in the OID list, but we don't care.
1778 : */
1779 :
1780 10 : for (cell = patterns->head; cell; cell = cell->next)
1781 : {
1782 : int dotcnt;
1783 :
1784 6 : appendPQExpBufferStr(query,
1785 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1786 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1787 : false, NULL, "s.srvname", NULL, NULL, NULL,
1788 : &dotcnt);
1789 6 : if (dotcnt > 0)
1790 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1791 : cell->val);
1792 :
1793 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1794 6 : if (PQntuples(res) == 0)
1795 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1796 :
1797 8 : for (i = 0; i < PQntuples(res); i++)
1798 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1799 :
1800 4 : PQclear(res);
1801 4 : resetPQExpBuffer(query);
1802 : }
1803 :
1804 4 : destroyPQExpBuffer(query);
1805 : }
1806 :
1807 : /*
1808 : * Find the OIDs of all tables matching the given list of patterns,
1809 : * and append them to the given OID list. See also expand_dbname_patterns()
1810 : * in pg_dumpall.c
1811 : */
1812 : static void
1813 2238 : expand_table_name_patterns(Archive *fout,
1814 : SimpleStringList *patterns, SimpleOidList *oids,
1815 : bool strict_names, bool with_child_tables)
1816 : {
1817 : PQExpBuffer query;
1818 : PGresult *res;
1819 : SimpleStringListCell *cell;
1820 : int i;
1821 :
1822 2238 : if (patterns->head == NULL)
1823 2180 : return; /* nothing to do */
1824 :
1825 58 : query = createPQExpBuffer();
1826 :
1827 : /*
1828 : * this might sometimes result in duplicate entries in the OID list, but
1829 : * we don't care.
1830 : */
1831 :
1832 118 : for (cell = patterns->head; cell; cell = cell->next)
1833 : {
1834 : PQExpBufferData dbbuf;
1835 : int dotcnt;
1836 :
1837 : /*
1838 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1839 : * would be unnecessary given a pg_table_is_visible() variant taking a
1840 : * search_path argument.
1841 : *
1842 : * For with_child_tables, we start with the basic query's results and
1843 : * recursively search the inheritance tree to add child tables.
1844 : */
1845 70 : if (with_child_tables)
1846 : {
1847 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1848 : }
1849 :
1850 70 : appendPQExpBuffer(query,
1851 : "SELECT c.oid"
1852 : "\nFROM pg_catalog.pg_class c"
1853 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1854 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1855 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1856 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1857 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1858 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1859 : RELKIND_PARTITIONED_TABLE);
1860 70 : initPQExpBuffer(&dbbuf);
1861 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1862 : false, "n.nspname", "c.relname", NULL,
1863 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1864 : &dotcnt);
1865 70 : if (dotcnt > 2)
1866 2 : pg_fatal("improper relation name (too many dotted names): %s",
1867 : cell->val);
1868 68 : else if (dotcnt == 2)
1869 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1870 64 : termPQExpBuffer(&dbbuf);
1871 :
1872 64 : if (with_child_tables)
1873 : {
1874 12 : appendPQExpBufferStr(query, "UNION"
1875 : "\nSELECT i.inhrelid"
1876 : "\nFROM partition_tree p"
1877 : "\n JOIN pg_catalog.pg_inherits i"
1878 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1879 : "\n)"
1880 : "\nSELECT relid FROM partition_tree");
1881 : }
1882 :
1883 64 : ExecuteSqlStatement(fout, "RESET search_path");
1884 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1885 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1886 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1887 64 : if (strict_names && PQntuples(res) == 0)
1888 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1889 :
1890 148 : for (i = 0; i < PQntuples(res); i++)
1891 : {
1892 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1893 : }
1894 :
1895 60 : PQclear(res);
1896 60 : resetPQExpBuffer(query);
1897 : }
1898 :
1899 48 : destroyPQExpBuffer(query);
1900 : }
1901 :
1902 : /*
1903 : * Verifies that the connected database name matches the given database name,
1904 : * and if not, dies with an error about the given pattern.
1905 : *
1906 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1907 : */
1908 : static void
1909 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1910 : {
1911 : const char *db;
1912 :
1913 10 : db = PQdb(conn);
1914 10 : if (db == NULL)
1915 0 : pg_fatal("You are currently not connected to a database.");
1916 :
1917 10 : if (strcmp(db, dbname) != 0)
1918 10 : pg_fatal("cross-database references are not implemented: %s",
1919 : pattern);
1920 0 : }
1921 :
1922 : /*
1923 : * checkExtensionMembership
1924 : * Determine whether object is an extension member, and if so,
1925 : * record an appropriate dependency and set the object's dump flag.
1926 : *
1927 : * It's important to call this for each object that could be an extension
1928 : * member. Generally, we integrate this with determining the object's
1929 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1930 : *
1931 : * Returns true if object is an extension member, else false.
1932 : */
1933 : static bool
1934 1149866 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1935 : {
1936 1149866 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1937 :
1938 1149866 : if (ext == NULL)
1939 1148276 : return false;
1940 :
1941 1590 : dobj->ext_member = true;
1942 :
1943 : /* Record dependency so that getDependencies needn't deal with that */
1944 1590 : addObjectDependency(dobj, ext->dobj.dumpId);
1945 :
1946 : /*
1947 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1948 : * dumped. (Any initial ACLs will be removed later, using data from
1949 : * pg_init_privs, so that we'll dump only the delta from the extension's
1950 : * initial setup.)
1951 : *
1952 : * Prior to 9.6, we do not include any extension member components.
1953 : *
1954 : * In binary upgrades, we still dump all components of the members
1955 : * individually, since the idea is to exactly reproduce the database
1956 : * contents rather than replace the extension contents with something
1957 : * different.
1958 : *
1959 : * Note: it might be interesting someday to implement storage and delta
1960 : * dumping of extension members' RLS policies and/or security labels.
1961 : * However there is a pitfall for RLS policies: trying to dump them
1962 : * requires getting a lock on their tables, and the calling user might not
1963 : * have privileges for that. We need no lock to examine a table's ACLs,
1964 : * so the current feature doesn't have a problem of that sort.
1965 : */
1966 1590 : if (fout->dopt->binary_upgrade)
1967 338 : dobj->dump = ext->dobj.dump;
1968 : else
1969 : {
1970 1252 : if (fout->remoteVersion < 90600)
1971 0 : dobj->dump = DUMP_COMPONENT_NONE;
1972 : else
1973 1252 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1974 : }
1975 :
1976 1590 : return true;
1977 : }
1978 :
1979 : /*
1980 : * selectDumpableNamespace: policy-setting subroutine
1981 : * Mark a namespace as to be dumped or not
1982 : */
1983 : static void
1984 2832 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1985 : {
1986 : /*
1987 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1988 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1989 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1990 : */
1991 2832 : nsinfo->create = true;
1992 :
1993 : /*
1994 : * If specific tables are being dumped, do not dump any complete
1995 : * namespaces. If specific namespaces are being dumped, dump just those
1996 : * namespaces. Otherwise, dump all non-system namespaces.
1997 : */
1998 2832 : if (table_include_oids.head != NULL)
1999 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2000 2732 : else if (schema_include_oids.head != NULL)
2001 374 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2002 374 : simple_oid_list_member(&schema_include_oids,
2003 : nsinfo->dobj.catId.oid) ?
2004 374 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2005 2358 : else if (fout->remoteVersion >= 90600 &&
2006 2358 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2007 : {
2008 : /*
2009 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2010 : * they are interesting (and not the original ACLs which were set at
2011 : * initdb time, see pg_init_privs).
2012 : */
2013 322 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2014 : }
2015 2036 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2016 1002 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2017 : {
2018 : /* Other system schemas don't get dumped */
2019 1356 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2020 : }
2021 680 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2022 : {
2023 : /*
2024 : * The public schema is a strange beast that sits in a sort of
2025 : * no-mans-land between being a system object and a user object.
2026 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2027 : * a comment and an indication of ownership. If the owner is the
2028 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2029 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2030 : */
2031 314 : nsinfo->create = false;
2032 314 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2033 314 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2034 224 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2035 314 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2036 :
2037 : /*
2038 : * Also, make like it has a comment even if it doesn't; this is so
2039 : * that we'll emit a command to drop the comment, if appropriate.
2040 : * (Without this, we'd not call dumpCommentExtended for it.)
2041 : */
2042 314 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2043 : }
2044 : else
2045 366 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2046 :
2047 : /*
2048 : * In any case, a namespace can be excluded by an exclusion switch
2049 : */
2050 3856 : if (nsinfo->dobj.dump_contains &&
2051 1024 : simple_oid_list_member(&schema_exclude_oids,
2052 : nsinfo->dobj.catId.oid))
2053 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2054 :
2055 : /*
2056 : * If the schema belongs to an extension, allow extension membership to
2057 : * override the dump decision for the schema itself. However, this does
2058 : * not change dump_contains, so this won't change what we do with objects
2059 : * within the schema. (If they belong to the extension, they'll get
2060 : * suppressed by it, otherwise not.)
2061 : */
2062 2832 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2063 2832 : }
2064 :
2065 : /*
2066 : * selectDumpableTable: policy-setting subroutine
2067 : * Mark a table as to be dumped or not
2068 : */
2069 : static void
2070 96626 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2071 : {
2072 96626 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2073 450 : return; /* extension membership overrides all else */
2074 :
2075 : /*
2076 : * If specific tables are being dumped, dump just those tables; else, dump
2077 : * according to the parent namespace's dump flag.
2078 : */
2079 96176 : if (table_include_oids.head != NULL)
2080 10356 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2081 : tbinfo->dobj.catId.oid) ?
2082 5178 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2083 : else
2084 90998 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2085 :
2086 : /*
2087 : * In any case, a table can be excluded by an exclusion switch
2088 : */
2089 157314 : if (tbinfo->dobj.dump &&
2090 61138 : simple_oid_list_member(&table_exclude_oids,
2091 : tbinfo->dobj.catId.oid))
2092 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2093 : }
2094 :
2095 : /*
2096 : * selectDumpableType: policy-setting subroutine
2097 : * Mark a type as to be dumped or not
2098 : *
2099 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2100 : * special type code to facilitate sorting into the desired order. (We don't
2101 : * want to consider those to be ordinary types because that would bring tables
2102 : * up into the datatype part of the dump order.) We still set the object's
2103 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2104 : * need it so that casts involving such types will be dumped correctly -- see
2105 : * dumpCast. This means the flag should be set the same as for the underlying
2106 : * object (the table or base type).
2107 : */
2108 : static void
2109 264998 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2110 : {
2111 : /* skip complex types, except for standalone composite types */
2112 264998 : if (OidIsValid(tyinfo->typrelid) &&
2113 95160 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2114 : {
2115 94790 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2116 :
2117 94790 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2118 94790 : if (tytable != NULL)
2119 94790 : tyinfo->dobj.dump = tytable->dobj.dump;
2120 : else
2121 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2122 94790 : return;
2123 : }
2124 :
2125 : /* skip auto-generated array and multirange types */
2126 170208 : if (tyinfo->isArray || tyinfo->isMultirange)
2127 : {
2128 129626 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2129 :
2130 : /*
2131 : * Fall through to set the dump flag; we assume that the subsequent
2132 : * rules will do the same thing as they would for the array's base
2133 : * type or multirange's range type. (We cannot reliably look up the
2134 : * base type here, since getTypes may not have processed it yet.)
2135 : */
2136 : }
2137 :
2138 170208 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2139 300 : return; /* extension membership overrides all else */
2140 :
2141 : /* Dump based on if the contents of the namespace are being dumped */
2142 169908 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2143 : }
2144 :
2145 : /*
2146 : * selectDumpableDefaultACL: policy-setting subroutine
2147 : * Mark a default ACL as to be dumped or not
2148 : *
2149 : * For per-schema default ACLs, dump if the schema is to be dumped.
2150 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2151 : * and aclsSkip are checked separately.
2152 : */
2153 : static void
2154 412 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2155 : {
2156 : /* Default ACLs can't be extension members */
2157 :
2158 412 : if (dinfo->dobj.namespace)
2159 : /* default ACLs are considered part of the namespace */
2160 192 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2161 : else
2162 220 : dinfo->dobj.dump = dopt->include_everything ?
2163 220 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2164 412 : }
2165 :
2166 : /*
2167 : * selectDumpableCast: policy-setting subroutine
2168 : * Mark a cast as to be dumped or not
2169 : *
2170 : * Casts do not belong to any particular namespace (since they haven't got
2171 : * names), nor do they have identifiable owners. To distinguish user-defined
2172 : * casts from built-in ones, we must resort to checking whether the cast's
2173 : * OID is in the range reserved for initdb.
2174 : */
2175 : static void
2176 86084 : selectDumpableCast(CastInfo *cast, Archive *fout)
2177 : {
2178 86084 : if (checkExtensionMembership(&cast->dobj, fout))
2179 0 : return; /* extension membership overrides all else */
2180 :
2181 : /*
2182 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2183 : * support ACLs currently.
2184 : */
2185 86084 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2186 85904 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2187 : else
2188 180 : cast->dobj.dump = fout->dopt->include_everything ?
2189 180 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2190 : }
2191 :
2192 : /*
2193 : * selectDumpableProcLang: policy-setting subroutine
2194 : * Mark a procedural language as to be dumped or not
2195 : *
2196 : * Procedural languages do not belong to any particular namespace. To
2197 : * identify built-in languages, we must resort to checking whether the
2198 : * language's OID is in the range reserved for initdb.
2199 : */
2200 : static void
2201 460 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2202 : {
2203 460 : if (checkExtensionMembership(&plang->dobj, fout))
2204 364 : return; /* extension membership overrides all else */
2205 :
2206 : /*
2207 : * Only include procedural languages when we are dumping everything.
2208 : *
2209 : * For from-initdb procedural languages, only include ACLs, as we do for
2210 : * the pg_catalog namespace. We need this because procedural languages do
2211 : * not live in any namespace.
2212 : */
2213 96 : if (!fout->dopt->include_everything)
2214 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2215 : else
2216 : {
2217 80 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2218 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2219 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2220 : else
2221 80 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2222 : }
2223 : }
2224 :
2225 : /*
2226 : * selectDumpableAccessMethod: policy-setting subroutine
2227 : * Mark an access method as to be dumped or not
2228 : *
2229 : * Access methods do not belong to any particular namespace. To identify
2230 : * built-in access methods, we must resort to checking whether the
2231 : * method's OID is in the range reserved for initdb.
2232 : */
2233 : static void
2234 2804 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2235 : {
2236 : /* see getAccessMethods() comment about v9.6. */
2237 2804 : if (fout->remoteVersion < 90600)
2238 : {
2239 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2240 0 : return;
2241 : }
2242 :
2243 2804 : if (checkExtensionMembership(&method->dobj, fout))
2244 50 : return; /* extension membership overrides all else */
2245 :
2246 : /*
2247 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2248 : * they do not support ACLs currently.
2249 : */
2250 2754 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2251 2548 : method->dobj.dump = DUMP_COMPONENT_NONE;
2252 : else
2253 206 : method->dobj.dump = fout->dopt->include_everything ?
2254 206 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2255 : }
2256 :
2257 : /*
2258 : * selectDumpableExtension: policy-setting subroutine
2259 : * Mark an extension as to be dumped or not
2260 : *
2261 : * Built-in extensions should be skipped except for checking ACLs, since we
2262 : * assume those will already be installed in the target database. We identify
2263 : * such extensions by their having OIDs in the range reserved for initdb.
2264 : * We dump all user-added extensions by default. No extensions are dumped
2265 : * if include_everything is false (i.e., a --schema or --table switch was
2266 : * given), except if --extension specifies a list of extensions to dump.
2267 : */
2268 : static void
2269 426 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2270 : {
2271 : /*
2272 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2273 : * change permissions on their member objects, if they wish to, and have
2274 : * those changes preserved.
2275 : */
2276 426 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2277 366 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2278 : else
2279 : {
2280 : /* check if there is a list of extensions to dump */
2281 60 : if (extension_include_oids.head != NULL)
2282 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2283 8 : simple_oid_list_member(&extension_include_oids,
2284 : extinfo->dobj.catId.oid) ?
2285 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2286 : else
2287 52 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2288 52 : dopt->include_everything ?
2289 52 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2290 :
2291 : /* check that the extension is not explicitly excluded */
2292 112 : if (extinfo->dobj.dump &&
2293 52 : simple_oid_list_member(&extension_exclude_oids,
2294 : extinfo->dobj.catId.oid))
2295 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2296 : }
2297 426 : }
2298 :
2299 : /*
2300 : * selectDumpablePublicationObject: policy-setting subroutine
2301 : * Mark a publication object as to be dumped or not
2302 : *
2303 : * A publication can have schemas and tables which have schemas, but those are
2304 : * ignored in decision making, because publications are only dumped when we are
2305 : * dumping everything.
2306 : */
2307 : static void
2308 1004 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2309 : {
2310 1004 : if (checkExtensionMembership(dobj, fout))
2311 0 : return; /* extension membership overrides all else */
2312 :
2313 1004 : dobj->dump = fout->dopt->include_everything ?
2314 1004 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2315 : }
2316 :
2317 : /*
2318 : * selectDumpableStatisticsObject: policy-setting subroutine
2319 : * Mark an extended statistics object as to be dumped or not
2320 : *
2321 : * We dump an extended statistics object if the schema it's in and the table
2322 : * it's for are being dumped. (This'll need more thought if statistics
2323 : * objects ever support cross-table stats.)
2324 : */
2325 : static void
2326 344 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2327 : {
2328 344 : if (checkExtensionMembership(&sobj->dobj, fout))
2329 0 : return; /* extension membership overrides all else */
2330 :
2331 344 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2332 344 : if (sobj->stattable == NULL ||
2333 344 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2334 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2335 : }
2336 :
2337 : /*
2338 : * selectDumpableObject: policy-setting subroutine
2339 : * Mark a generic dumpable object as to be dumped or not
2340 : *
2341 : * Use this only for object types without a special-case routine above.
2342 : */
2343 : static void
2344 789504 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2345 : {
2346 789504 : if (checkExtensionMembership(dobj, fout))
2347 376 : return; /* extension membership overrides all else */
2348 :
2349 : /*
2350 : * Default policy is to dump if parent namespace is dumpable, or for
2351 : * non-namespace-associated items, dump if we're dumping "everything".
2352 : */
2353 789128 : if (dobj->namespace)
2354 787768 : dobj->dump = dobj->namespace->dobj.dump_contains;
2355 : else
2356 1360 : dobj->dump = fout->dopt->include_everything ?
2357 1360 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2358 : }
2359 :
2360 : /*
2361 : * Dump a table's contents for loading using the COPY command
2362 : * - this routine is called by the Archiver when it wants the table
2363 : * to be dumped.
2364 : */
2365 : static int
2366 8426 : dumpTableData_copy(Archive *fout, const void *dcontext)
2367 : {
2368 8426 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2369 8426 : TableInfo *tbinfo = tdinfo->tdtable;
2370 8426 : const char *classname = tbinfo->dobj.name;
2371 8426 : PQExpBuffer q = createPQExpBuffer();
2372 :
2373 : /*
2374 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2375 : * which uses it already.
2376 : */
2377 8426 : PQExpBuffer clistBuf = createPQExpBuffer();
2378 8426 : PGconn *conn = GetConnection(fout);
2379 : PGresult *res;
2380 : int ret;
2381 : char *copybuf;
2382 : const char *column_list;
2383 :
2384 8426 : pg_log_info("dumping contents of table \"%s.%s\"",
2385 : tbinfo->dobj.namespace->dobj.name, classname);
2386 :
2387 : /*
2388 : * Specify the column list explicitly so that we have no possibility of
2389 : * retrieving data in the wrong column order. (The default column
2390 : * ordering of COPY will not be what we want in certain corner cases
2391 : * involving ADD COLUMN and inheritance.)
2392 : */
2393 8426 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2394 :
2395 : /*
2396 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2397 : * a filter condition was specified. For other cases a simple COPY
2398 : * suffices.
2399 : */
2400 8426 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2401 : {
2402 : /* Temporary allows to access to foreign tables to dump data */
2403 146 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2404 2 : set_restrict_relation_kind(fout, "view");
2405 :
2406 146 : appendPQExpBufferStr(q, "COPY (SELECT ");
2407 : /* klugery to get rid of parens in column list */
2408 146 : if (strlen(column_list) > 2)
2409 : {
2410 146 : appendPQExpBufferStr(q, column_list + 1);
2411 146 : q->data[q->len - 1] = ' ';
2412 : }
2413 : else
2414 0 : appendPQExpBufferStr(q, "* ");
2415 :
2416 292 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2417 146 : fmtQualifiedDumpable(tbinfo),
2418 146 : tdinfo->filtercond ? tdinfo->filtercond : "");
2419 : }
2420 : else
2421 : {
2422 8280 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2423 8280 : fmtQualifiedDumpable(tbinfo),
2424 : column_list);
2425 : }
2426 8426 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2427 8424 : PQclear(res);
2428 8424 : destroyPQExpBuffer(clistBuf);
2429 :
2430 : for (;;)
2431 : {
2432 3621086 : ret = PQgetCopyData(conn, ©buf, 0);
2433 :
2434 3621086 : if (ret < 0)
2435 8424 : break; /* done or error */
2436 :
2437 3612662 : if (copybuf)
2438 : {
2439 3612662 : WriteData(fout, copybuf, ret);
2440 3612662 : PQfreemem(copybuf);
2441 : }
2442 :
2443 : /* ----------
2444 : * THROTTLE:
2445 : *
2446 : * There was considerable discussion in late July, 2000 regarding
2447 : * slowing down pg_dump when backing up large tables. Users with both
2448 : * slow & fast (multi-processor) machines experienced performance
2449 : * degradation when doing a backup.
2450 : *
2451 : * Initial attempts based on sleeping for a number of ms for each ms
2452 : * of work were deemed too complex, then a simple 'sleep in each loop'
2453 : * implementation was suggested. The latter failed because the loop
2454 : * was too tight. Finally, the following was implemented:
2455 : *
2456 : * If throttle is non-zero, then
2457 : * See how long since the last sleep.
2458 : * Work out how long to sleep (based on ratio).
2459 : * If sleep is more than 100ms, then
2460 : * sleep
2461 : * reset timer
2462 : * EndIf
2463 : * EndIf
2464 : *
2465 : * where the throttle value was the number of ms to sleep per ms of
2466 : * work. The calculation was done in each loop.
2467 : *
2468 : * Most of the hard work is done in the backend, and this solution
2469 : * still did not work particularly well: on slow machines, the ratio
2470 : * was 50:1, and on medium paced machines, 1:1, and on fast
2471 : * multi-processor machines, it had little or no effect, for reasons
2472 : * that were unclear.
2473 : *
2474 : * Further discussion ensued, and the proposal was dropped.
2475 : *
2476 : * For those people who want this feature, it can be implemented using
2477 : * gettimeofday in each loop, calculating the time since last sleep,
2478 : * multiplying that by the sleep ratio, then if the result is more
2479 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2480 : * function to sleep for a subsecond period ie.
2481 : *
2482 : * select(0, NULL, NULL, NULL, &tvi);
2483 : *
2484 : * This will return after the interval specified in the structure tvi.
2485 : * Finally, call gettimeofday again to save the 'last sleep time'.
2486 : * ----------
2487 : */
2488 : }
2489 8424 : archprintf(fout, "\\.\n\n\n");
2490 :
2491 8424 : if (ret == -2)
2492 : {
2493 : /* copy data transfer failed */
2494 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2495 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2496 0 : pg_log_error_detail("Command was: %s", q->data);
2497 0 : exit_nicely(1);
2498 : }
2499 :
2500 : /* Check command status and return to normal libpq state */
2501 8424 : res = PQgetResult(conn);
2502 8424 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2503 : {
2504 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2505 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2506 0 : pg_log_error_detail("Command was: %s", q->data);
2507 0 : exit_nicely(1);
2508 : }
2509 8424 : PQclear(res);
2510 :
2511 : /* Do this to ensure we've pumped libpq back to idle state */
2512 8424 : if (PQgetResult(conn) != NULL)
2513 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2514 : classname);
2515 :
2516 8424 : destroyPQExpBuffer(q);
2517 :
2518 : /* Revert back the setting */
2519 8424 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2520 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2521 :
2522 8424 : return 1;
2523 : }
2524 :
2525 : /*
2526 : * Dump table data using INSERT commands.
2527 : *
2528 : * Caution: when we restore from an archive file direct to database, the
2529 : * INSERT commands emitted by this function have to be parsed by
2530 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2531 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2532 : */
2533 : static int
2534 170 : dumpTableData_insert(Archive *fout, const void *dcontext)
2535 : {
2536 170 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2537 170 : TableInfo *tbinfo = tdinfo->tdtable;
2538 170 : DumpOptions *dopt = fout->dopt;
2539 170 : PQExpBuffer q = createPQExpBuffer();
2540 170 : PQExpBuffer insertStmt = NULL;
2541 : char *attgenerated;
2542 : PGresult *res;
2543 : int nfields,
2544 : i;
2545 170 : int rows_per_statement = dopt->dump_inserts;
2546 170 : int rows_this_statement = 0;
2547 :
2548 : /* Temporary allows to access to foreign tables to dump data */
2549 170 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2550 0 : set_restrict_relation_kind(fout, "view");
2551 :
2552 : /*
2553 : * If we're going to emit INSERTs with column names, the most efficient
2554 : * way to deal with generated columns is to exclude them entirely. For
2555 : * INSERTs without column names, we have to emit DEFAULT rather than the
2556 : * actual column value --- but we can save a few cycles by fetching nulls
2557 : * rather than the uninteresting-to-us value.
2558 : */
2559 170 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2560 170 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2561 170 : nfields = 0;
2562 522 : for (i = 0; i < tbinfo->numatts; i++)
2563 : {
2564 352 : if (tbinfo->attisdropped[i])
2565 4 : continue;
2566 348 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2567 16 : continue;
2568 332 : if (nfields > 0)
2569 176 : appendPQExpBufferStr(q, ", ");
2570 332 : if (tbinfo->attgenerated[i])
2571 16 : appendPQExpBufferStr(q, "NULL");
2572 : else
2573 316 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2574 332 : attgenerated[nfields] = tbinfo->attgenerated[i];
2575 332 : nfields++;
2576 : }
2577 : /* Servers before 9.4 will complain about zero-column SELECT */
2578 170 : if (nfields == 0)
2579 14 : appendPQExpBufferStr(q, "NULL");
2580 170 : appendPQExpBuffer(q, " FROM ONLY %s",
2581 170 : fmtQualifiedDumpable(tbinfo));
2582 170 : if (tdinfo->filtercond)
2583 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2584 :
2585 170 : ExecuteSqlStatement(fout, q->data);
2586 :
2587 : while (1)
2588 : {
2589 274 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2590 : PGRES_TUPLES_OK);
2591 :
2592 : /* cross-check field count, allowing for dummy NULL if any */
2593 274 : if (nfields != PQnfields(res) &&
2594 20 : !(nfields == 0 && PQnfields(res) == 1))
2595 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2596 : tbinfo->dobj.name);
2597 :
2598 : /*
2599 : * First time through, we build as much of the INSERT statement as
2600 : * possible in "insertStmt", which we can then just print for each
2601 : * statement. If the table happens to have zero dumpable columns then
2602 : * this will be a complete statement, otherwise it will end in
2603 : * "VALUES" and be ready to have the row's column values printed.
2604 : */
2605 274 : if (insertStmt == NULL)
2606 : {
2607 : TableInfo *targettab;
2608 :
2609 170 : insertStmt = createPQExpBuffer();
2610 :
2611 : /*
2612 : * When load-via-partition-root is set or forced, get the root
2613 : * table name for the partition table, so that we can reload data
2614 : * through the root table.
2615 : */
2616 170 : if (tbinfo->ispartition &&
2617 96 : (dopt->load_via_partition_root ||
2618 48 : forcePartitionRootLoad(tbinfo)))
2619 14 : targettab = getRootTableInfo(tbinfo);
2620 : else
2621 156 : targettab = tbinfo;
2622 :
2623 170 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2624 170 : fmtQualifiedDumpable(targettab));
2625 :
2626 : /* corner case for zero-column table */
2627 170 : if (nfields == 0)
2628 : {
2629 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2630 : }
2631 : else
2632 : {
2633 : /* append the list of column names if required */
2634 156 : if (dopt->column_inserts)
2635 : {
2636 70 : appendPQExpBufferChar(insertStmt, '(');
2637 210 : for (int field = 0; field < nfields; field++)
2638 : {
2639 140 : if (field > 0)
2640 70 : appendPQExpBufferStr(insertStmt, ", ");
2641 140 : appendPQExpBufferStr(insertStmt,
2642 140 : fmtId(PQfname(res, field)));
2643 : }
2644 70 : appendPQExpBufferStr(insertStmt, ") ");
2645 : }
2646 :
2647 156 : if (tbinfo->needs_override)
2648 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2649 :
2650 156 : appendPQExpBufferStr(insertStmt, "VALUES");
2651 : }
2652 : }
2653 :
2654 6816 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2655 : {
2656 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2657 6542 : if (rows_this_statement == 0)
2658 6530 : archputs(insertStmt->data, fout);
2659 :
2660 : /*
2661 : * If it is zero-column table then we've already written the
2662 : * complete statement, which will mean we've disobeyed
2663 : * --rows-per-insert when it's set greater than 1. We do support
2664 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2665 : * UNION ALL ... but that's non-standard so we should avoid it
2666 : * given that using INSERTs is mostly only ever needed for
2667 : * cross-database exports.
2668 : */
2669 6542 : if (nfields == 0)
2670 12 : continue;
2671 :
2672 : /* Emit a row heading */
2673 6530 : if (rows_per_statement == 1)
2674 6512 : archputs(" (", fout);
2675 18 : else if (rows_this_statement > 0)
2676 12 : archputs(",\n\t(", fout);
2677 : else
2678 6 : archputs("\n\t(", fout);
2679 :
2680 19698 : for (int field = 0; field < nfields; field++)
2681 : {
2682 13168 : if (field > 0)
2683 6638 : archputs(", ", fout);
2684 13168 : if (attgenerated[field])
2685 : {
2686 4 : archputs("DEFAULT", fout);
2687 4 : continue;
2688 : }
2689 13164 : if (PQgetisnull(res, tuple, field))
2690 : {
2691 166 : archputs("NULL", fout);
2692 166 : continue;
2693 : }
2694 :
2695 : /* XXX This code is partially duplicated in ruleutils.c */
2696 12998 : switch (PQftype(res, field))
2697 : {
2698 8938 : case INT2OID:
2699 : case INT4OID:
2700 : case INT8OID:
2701 : case OIDOID:
2702 : case FLOAT4OID:
2703 : case FLOAT8OID:
2704 : case NUMERICOID:
2705 : {
2706 : /*
2707 : * These types are printed without quotes unless
2708 : * they contain values that aren't accepted by the
2709 : * scanner unquoted (e.g., 'NaN'). Note that
2710 : * strtod() and friends might accept NaN, so we
2711 : * can't use that to test.
2712 : *
2713 : * In reality we only need to defend against
2714 : * infinity and NaN, so we need not get too crazy
2715 : * about pattern matching here.
2716 : */
2717 8938 : const char *s = PQgetvalue(res, tuple, field);
2718 :
2719 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2720 8934 : archputs(s, fout);
2721 : else
2722 4 : archprintf(fout, "'%s'", s);
2723 : }
2724 8938 : break;
2725 :
2726 4 : case BITOID:
2727 : case VARBITOID:
2728 4 : archprintf(fout, "B'%s'",
2729 : PQgetvalue(res, tuple, field));
2730 4 : break;
2731 :
2732 8 : case BOOLOID:
2733 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2734 4 : archputs("true", fout);
2735 : else
2736 4 : archputs("false", fout);
2737 8 : break;
2738 :
2739 4048 : default:
2740 : /* All other types are printed as string literals. */
2741 4048 : resetPQExpBuffer(q);
2742 4048 : appendStringLiteralAH(q,
2743 : PQgetvalue(res, tuple, field),
2744 : fout);
2745 4048 : archputs(q->data, fout);
2746 4048 : break;
2747 : }
2748 : }
2749 :
2750 : /* Terminate the row ... */
2751 6530 : archputs(")", fout);
2752 :
2753 : /* ... and the statement, if the target no. of rows is reached */
2754 6530 : if (++rows_this_statement >= rows_per_statement)
2755 : {
2756 6516 : if (dopt->do_nothing)
2757 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2758 : else
2759 6516 : archputs(";\n", fout);
2760 : /* Reset the row counter */
2761 6516 : rows_this_statement = 0;
2762 : }
2763 : }
2764 :
2765 274 : if (PQntuples(res) <= 0)
2766 : {
2767 170 : PQclear(res);
2768 170 : break;
2769 : }
2770 104 : PQclear(res);
2771 : }
2772 :
2773 : /* Terminate any statements that didn't make the row count. */
2774 170 : if (rows_this_statement > 0)
2775 : {
2776 2 : if (dopt->do_nothing)
2777 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2778 : else
2779 2 : archputs(";\n", fout);
2780 : }
2781 :
2782 170 : archputs("\n\n", fout);
2783 :
2784 170 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2785 :
2786 170 : destroyPQExpBuffer(q);
2787 170 : if (insertStmt != NULL)
2788 170 : destroyPQExpBuffer(insertStmt);
2789 170 : free(attgenerated);
2790 :
2791 : /* Revert back the setting */
2792 170 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2793 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2794 :
2795 170 : return 1;
2796 : }
2797 :
2798 : /*
2799 : * getRootTableInfo:
2800 : * get the root TableInfo for the given partition table.
2801 : */
2802 : static TableInfo *
2803 170 : getRootTableInfo(const TableInfo *tbinfo)
2804 : {
2805 : TableInfo *parentTbinfo;
2806 :
2807 : Assert(tbinfo->ispartition);
2808 : Assert(tbinfo->numParents == 1);
2809 :
2810 170 : parentTbinfo = tbinfo->parents[0];
2811 170 : while (parentTbinfo->ispartition)
2812 : {
2813 : Assert(parentTbinfo->numParents == 1);
2814 0 : parentTbinfo = parentTbinfo->parents[0];
2815 : }
2816 :
2817 170 : return parentTbinfo;
2818 : }
2819 :
2820 : /*
2821 : * forcePartitionRootLoad
2822 : * Check if we must force load_via_partition_root for this partition.
2823 : *
2824 : * This is required if any level of ancestral partitioned table has an
2825 : * unsafe partitioning scheme.
2826 : */
2827 : static bool
2828 2150 : forcePartitionRootLoad(const TableInfo *tbinfo)
2829 : {
2830 : TableInfo *parentTbinfo;
2831 :
2832 : Assert(tbinfo->ispartition);
2833 : Assert(tbinfo->numParents == 1);
2834 :
2835 2150 : parentTbinfo = tbinfo->parents[0];
2836 2150 : if (parentTbinfo->unsafe_partitions)
2837 170 : return true;
2838 2412 : while (parentTbinfo->ispartition)
2839 : {
2840 : Assert(parentTbinfo->numParents == 1);
2841 432 : parentTbinfo = parentTbinfo->parents[0];
2842 432 : if (parentTbinfo->unsafe_partitions)
2843 0 : return true;
2844 : }
2845 :
2846 1980 : return false;
2847 : }
2848 :
2849 : /*
2850 : * dumpTableData -
2851 : * dump the contents of a single table
2852 : *
2853 : * Actually, this just makes an ArchiveEntry for the table contents.
2854 : */
2855 : static void
2856 8764 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2857 : {
2858 8764 : DumpOptions *dopt = fout->dopt;
2859 8764 : TableInfo *tbinfo = tdinfo->tdtable;
2860 8764 : PQExpBuffer copyBuf = createPQExpBuffer();
2861 8764 : PQExpBuffer clistBuf = createPQExpBuffer();
2862 : DataDumperPtr dumpFn;
2863 8764 : char *tdDefn = NULL;
2864 : char *copyStmt;
2865 : const char *copyFrom;
2866 :
2867 : /* We had better have loaded per-column details about this table */
2868 : Assert(tbinfo->interesting);
2869 :
2870 : /*
2871 : * When load-via-partition-root is set or forced, get the root table name
2872 : * for the partition table, so that we can reload data through the root
2873 : * table. Then construct a comment to be inserted into the TOC entry's
2874 : * defn field, so that such cases can be identified reliably.
2875 : */
2876 8764 : if (tbinfo->ispartition &&
2877 4204 : (dopt->load_via_partition_root ||
2878 2102 : forcePartitionRootLoad(tbinfo)))
2879 156 : {
2880 : TableInfo *parentTbinfo;
2881 : char *sanitized;
2882 :
2883 156 : parentTbinfo = getRootTableInfo(tbinfo);
2884 156 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2885 156 : sanitized = sanitize_line(copyFrom, true);
2886 156 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2887 : sanitized);
2888 156 : free(sanitized);
2889 156 : tdDefn = pg_strdup(copyBuf->data);
2890 : }
2891 : else
2892 8608 : copyFrom = fmtQualifiedDumpable(tbinfo);
2893 :
2894 8764 : if (dopt->dump_inserts == 0)
2895 : {
2896 : /* Dump/restore using COPY */
2897 8594 : dumpFn = dumpTableData_copy;
2898 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2899 8594 : printfPQExpBuffer(copyBuf, "COPY %s ",
2900 : copyFrom);
2901 8594 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2902 : fmtCopyColumnList(tbinfo, clistBuf));
2903 8594 : copyStmt = copyBuf->data;
2904 : }
2905 : else
2906 : {
2907 : /* Restore using INSERT */
2908 170 : dumpFn = dumpTableData_insert;
2909 170 : copyStmt = NULL;
2910 : }
2911 :
2912 : /*
2913 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2914 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2915 : * See comments for BuildArchiveDependencies.
2916 : */
2917 8764 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2918 : {
2919 : TocEntry *te;
2920 :
2921 8764 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2922 8764 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2923 : .namespace = tbinfo->dobj.namespace->dobj.name,
2924 : .owner = tbinfo->rolname,
2925 : .description = "TABLE DATA",
2926 : .section = SECTION_DATA,
2927 : .createStmt = tdDefn,
2928 : .copyStmt = copyStmt,
2929 : .deps = &(tbinfo->dobj.dumpId),
2930 : .nDeps = 1,
2931 : .dumpFn = dumpFn,
2932 : .dumpArg = tdinfo));
2933 :
2934 : /*
2935 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2936 : * and want to order dump jobs by table size. We choose to measure
2937 : * dataLength in table pages (including TOAST pages) during dump, so
2938 : * no scaling is needed.
2939 : *
2940 : * However, relpages is declared as "integer" in pg_class, and hence
2941 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2942 : * Cast so that we get the right interpretation of table sizes
2943 : * exceeding INT_MAX pages.
2944 : */
2945 8764 : te->dataLength = (BlockNumber) tbinfo->relpages;
2946 8764 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2947 :
2948 : /*
2949 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2950 : * and instead we'd better worry about integer overflow. Clamp to
2951 : * INT_MAX if the correct result exceeds that.
2952 : */
2953 : if (sizeof(te->dataLength) == 4 &&
2954 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2955 : te->dataLength < 0))
2956 : te->dataLength = INT_MAX;
2957 : }
2958 :
2959 8764 : destroyPQExpBuffer(copyBuf);
2960 8764 : destroyPQExpBuffer(clistBuf);
2961 8764 : }
2962 :
2963 : /*
2964 : * refreshMatViewData -
2965 : * load or refresh the contents of a single materialized view
2966 : *
2967 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2968 : * statement.
2969 : */
2970 : static void
2971 804 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2972 : {
2973 804 : TableInfo *tbinfo = tdinfo->tdtable;
2974 : PQExpBuffer q;
2975 :
2976 : /* If the materialized view is not flagged as populated, skip this. */
2977 804 : if (!tbinfo->relispopulated)
2978 148 : return;
2979 :
2980 656 : q = createPQExpBuffer();
2981 :
2982 656 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2983 656 : fmtQualifiedDumpable(tbinfo));
2984 :
2985 656 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2986 656 : ArchiveEntry(fout,
2987 : tdinfo->dobj.catId, /* catalog ID */
2988 656 : tdinfo->dobj.dumpId, /* dump ID */
2989 656 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2990 : .namespace = tbinfo->dobj.namespace->dobj.name,
2991 : .owner = tbinfo->rolname,
2992 : .description = "MATERIALIZED VIEW DATA",
2993 : .section = SECTION_POST_DATA,
2994 : .createStmt = q->data,
2995 : .deps = tdinfo->dobj.dependencies,
2996 : .nDeps = tdinfo->dobj.nDeps));
2997 :
2998 656 : destroyPQExpBuffer(q);
2999 : }
3000 :
3001 : /*
3002 : * getTableData -
3003 : * set up dumpable objects representing the contents of tables
3004 : */
3005 : static void
3006 348 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3007 : {
3008 : int i;
3009 :
3010 92996 : for (i = 0; i < numTables; i++)
3011 : {
3012 92648 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3013 1858 : (!relkind || tblinfo[i].relkind == relkind))
3014 12250 : makeTableDataInfo(dopt, &(tblinfo[i]));
3015 : }
3016 348 : }
3017 :
3018 : /*
3019 : * Make a dumpable object for the data of this specific table
3020 : *
3021 : * Note: we make a TableDataInfo if and only if we are going to dump the
3022 : * table data; the "dump" field in such objects isn't very interesting.
3023 : */
3024 : static void
3025 12472 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3026 : {
3027 : TableDataInfo *tdinfo;
3028 :
3029 : /*
3030 : * Nothing to do if we already decided to dump the table. This will
3031 : * happen for "config" tables.
3032 : */
3033 12472 : if (tbinfo->dataObj != NULL)
3034 2 : return;
3035 :
3036 : /* Skip VIEWs (no data to dump) */
3037 12470 : if (tbinfo->relkind == RELKIND_VIEW)
3038 968 : return;
3039 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3040 11502 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3041 82 : (foreign_servers_include_oids.head == NULL ||
3042 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3043 : tbinfo->foreign_server)))
3044 80 : return;
3045 : /* Skip partitioned tables (data in partitions) */
3046 11422 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3047 998 : return;
3048 :
3049 : /* Don't dump data in unlogged tables, if so requested */
3050 10424 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3051 82 : dopt->no_unlogged_table_data)
3052 36 : return;
3053 :
3054 : /* Check that the data is not explicitly excluded */
3055 10388 : if (simple_oid_list_member(&tabledata_exclude_oids,
3056 : tbinfo->dobj.catId.oid))
3057 16 : return;
3058 :
3059 : /* OK, let's dump it */
3060 10372 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3061 :
3062 10372 : if (tbinfo->relkind == RELKIND_MATVIEW)
3063 804 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3064 9568 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3065 804 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3066 : else
3067 8764 : tdinfo->dobj.objType = DO_TABLE_DATA;
3068 :
3069 : /*
3070 : * Note: use tableoid 0 so that this object won't be mistaken for
3071 : * something that pg_depend entries apply to.
3072 : */
3073 10372 : tdinfo->dobj.catId.tableoid = 0;
3074 10372 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3075 10372 : AssignDumpId(&tdinfo->dobj);
3076 10372 : tdinfo->dobj.name = tbinfo->dobj.name;
3077 10372 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3078 10372 : tdinfo->tdtable = tbinfo;
3079 10372 : tdinfo->filtercond = NULL; /* might get set later */
3080 10372 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3081 :
3082 : /* A TableDataInfo contains data, of course */
3083 10372 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3084 :
3085 10372 : tbinfo->dataObj = tdinfo;
3086 :
3087 : /*
3088 : * Materialized view statistics must be restored after the data, because
3089 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3090 : *
3091 : * The dependency is added here because the statistics objects are created
3092 : * first.
3093 : */
3094 10372 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3095 : {
3096 640 : tbinfo->stats->section = SECTION_POST_DATA;
3097 640 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3098 : }
3099 :
3100 : /* Make sure that we'll collect per-column info for this table. */
3101 10372 : tbinfo->interesting = true;
3102 : }
3103 :
3104 : /*
3105 : * The refresh for a materialized view must be dependent on the refresh for
3106 : * any materialized view that this one is dependent on.
3107 : *
3108 : * This must be called after all the objects are created, but before they are
3109 : * sorted.
3110 : */
3111 : static void
3112 284 : buildMatViewRefreshDependencies(Archive *fout)
3113 : {
3114 : PQExpBuffer query;
3115 : PGresult *res;
3116 : int ntups,
3117 : i;
3118 : int i_classid,
3119 : i_objid,
3120 : i_refobjid;
3121 :
3122 : /* No Mat Views before 9.3. */
3123 284 : if (fout->remoteVersion < 90300)
3124 0 : return;
3125 :
3126 284 : query = createPQExpBuffer();
3127 :
3128 284 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3129 : "( "
3130 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3131 : "FROM pg_depend d1 "
3132 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3133 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3134 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3135 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3136 : "AND d2.objid = r1.oid "
3137 : "AND d2.refobjid <> d1.objid "
3138 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3139 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3140 : CppAsString2(RELKIND_VIEW) ") "
3141 : "WHERE d1.classid = 'pg_class'::regclass "
3142 : "UNION "
3143 : "SELECT w.objid, d3.refobjid, c3.relkind "
3144 : "FROM w "
3145 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3146 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3147 : "AND d3.objid = r3.oid "
3148 : "AND d3.refobjid <> w.refobjid "
3149 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3150 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3151 : CppAsString2(RELKIND_VIEW) ") "
3152 : ") "
3153 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3154 : "FROM w "
3155 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3156 :
3157 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3158 :
3159 284 : ntups = PQntuples(res);
3160 :
3161 284 : i_classid = PQfnumber(res, "classid");
3162 284 : i_objid = PQfnumber(res, "objid");
3163 284 : i_refobjid = PQfnumber(res, "refobjid");
3164 :
3165 848 : for (i = 0; i < ntups; i++)
3166 : {
3167 : CatalogId objId;
3168 : CatalogId refobjId;
3169 : DumpableObject *dobj;
3170 : DumpableObject *refdobj;
3171 : TableInfo *tbinfo;
3172 : TableInfo *reftbinfo;
3173 :
3174 564 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3175 564 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3176 564 : refobjId.tableoid = objId.tableoid;
3177 564 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3178 :
3179 564 : dobj = findObjectByCatalogId(objId);
3180 564 : if (dobj == NULL)
3181 96 : continue;
3182 :
3183 : Assert(dobj->objType == DO_TABLE);
3184 564 : tbinfo = (TableInfo *) dobj;
3185 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3186 564 : dobj = (DumpableObject *) tbinfo->dataObj;
3187 564 : if (dobj == NULL)
3188 96 : continue;
3189 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3190 :
3191 468 : refdobj = findObjectByCatalogId(refobjId);
3192 468 : if (refdobj == NULL)
3193 0 : continue;
3194 :
3195 : Assert(refdobj->objType == DO_TABLE);
3196 468 : reftbinfo = (TableInfo *) refdobj;
3197 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3198 468 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3199 468 : if (refdobj == NULL)
3200 0 : continue;
3201 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3202 :
3203 468 : addObjectDependency(dobj, refdobj->dumpId);
3204 :
3205 468 : if (!reftbinfo->relispopulated)
3206 74 : tbinfo->relispopulated = false;
3207 : }
3208 :
3209 284 : PQclear(res);
3210 :
3211 284 : destroyPQExpBuffer(query);
3212 : }
3213 :
3214 : /*
3215 : * getTableDataFKConstraints -
3216 : * add dump-order dependencies reflecting foreign key constraints
3217 : *
3218 : * This code is executed only in a data-only dump --- in schema+data dumps
3219 : * we handle foreign key issues by not creating the FK constraints until
3220 : * after the data is loaded. In a data-only dump, however, we want to
3221 : * order the table data objects in such a way that a table's referenced
3222 : * tables are restored first. (In the presence of circular references or
3223 : * self-references this may be impossible; we'll detect and complain about
3224 : * that during the dependency sorting step.)
3225 : */
3226 : static void
3227 14 : getTableDataFKConstraints(void)
3228 : {
3229 : DumpableObject **dobjs;
3230 : int numObjs;
3231 : int i;
3232 :
3233 : /* Search through all the dumpable objects for FK constraints */
3234 14 : getDumpableObjects(&dobjs, &numObjs);
3235 51614 : for (i = 0; i < numObjs; i++)
3236 : {
3237 51600 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3238 : {
3239 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3240 : TableInfo *ftable;
3241 :
3242 : /* Not interesting unless both tables are to be dumped */
3243 16 : if (cinfo->contable == NULL ||
3244 16 : cinfo->contable->dataObj == NULL)
3245 8 : continue;
3246 8 : ftable = findTableByOid(cinfo->confrelid);
3247 8 : if (ftable == NULL ||
3248 8 : ftable->dataObj == NULL)
3249 0 : continue;
3250 :
3251 : /*
3252 : * Okay, make referencing table's TABLE_DATA object depend on the
3253 : * referenced table's TABLE_DATA object.
3254 : */
3255 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3256 8 : ftable->dataObj->dobj.dumpId);
3257 : }
3258 : }
3259 14 : free(dobjs);
3260 14 : }
3261 :
3262 :
3263 : /*
3264 : * dumpDatabase:
3265 : * dump the database definition
3266 : */
3267 : static void
3268 164 : dumpDatabase(Archive *fout)
3269 : {
3270 164 : DumpOptions *dopt = fout->dopt;
3271 164 : PQExpBuffer dbQry = createPQExpBuffer();
3272 164 : PQExpBuffer delQry = createPQExpBuffer();
3273 164 : PQExpBuffer creaQry = createPQExpBuffer();
3274 164 : PQExpBuffer labelq = createPQExpBuffer();
3275 164 : PGconn *conn = GetConnection(fout);
3276 : PGresult *res;
3277 : int i_tableoid,
3278 : i_oid,
3279 : i_datname,
3280 : i_datdba,
3281 : i_encoding,
3282 : i_datlocprovider,
3283 : i_collate,
3284 : i_ctype,
3285 : i_datlocale,
3286 : i_daticurules,
3287 : i_frozenxid,
3288 : i_minmxid,
3289 : i_datacl,
3290 : i_acldefault,
3291 : i_datistemplate,
3292 : i_datconnlimit,
3293 : i_datcollversion,
3294 : i_tablespace;
3295 : CatalogId dbCatId;
3296 : DumpId dbDumpId;
3297 : DumpableAcl dbdacl;
3298 : const char *datname,
3299 : *dba,
3300 : *encoding,
3301 : *datlocprovider,
3302 : *collate,
3303 : *ctype,
3304 : *locale,
3305 : *icurules,
3306 : *datistemplate,
3307 : *datconnlimit,
3308 : *tablespace;
3309 : uint32 frozenxid,
3310 : minmxid;
3311 : char *qdatname;
3312 :
3313 164 : pg_log_info("saving database definition");
3314 :
3315 : /*
3316 : * Fetch the database-level properties for this database.
3317 : */
3318 164 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3319 : "datdba, "
3320 : "pg_encoding_to_char(encoding) AS encoding, "
3321 : "datcollate, datctype, datfrozenxid, "
3322 : "datacl, acldefault('d', datdba) AS acldefault, "
3323 : "datistemplate, datconnlimit, ");
3324 164 : if (fout->remoteVersion >= 90300)
3325 164 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3326 : else
3327 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3328 164 : if (fout->remoteVersion >= 170000)
3329 164 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3330 0 : else if (fout->remoteVersion >= 150000)
3331 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3332 : else
3333 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3334 164 : if (fout->remoteVersion >= 160000)
3335 164 : appendPQExpBufferStr(dbQry, "daticurules, ");
3336 : else
3337 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3338 164 : appendPQExpBufferStr(dbQry,
3339 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3340 : "shobj_description(oid, 'pg_database') AS description "
3341 : "FROM pg_database "
3342 : "WHERE datname = current_database()");
3343 :
3344 164 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3345 :
3346 164 : i_tableoid = PQfnumber(res, "tableoid");
3347 164 : i_oid = PQfnumber(res, "oid");
3348 164 : i_datname = PQfnumber(res, "datname");
3349 164 : i_datdba = PQfnumber(res, "datdba");
3350 164 : i_encoding = PQfnumber(res, "encoding");
3351 164 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3352 164 : i_collate = PQfnumber(res, "datcollate");
3353 164 : i_ctype = PQfnumber(res, "datctype");
3354 164 : i_datlocale = PQfnumber(res, "datlocale");
3355 164 : i_daticurules = PQfnumber(res, "daticurules");
3356 164 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3357 164 : i_minmxid = PQfnumber(res, "datminmxid");
3358 164 : i_datacl = PQfnumber(res, "datacl");
3359 164 : i_acldefault = PQfnumber(res, "acldefault");
3360 164 : i_datistemplate = PQfnumber(res, "datistemplate");
3361 164 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3362 164 : i_datcollversion = PQfnumber(res, "datcollversion");
3363 164 : i_tablespace = PQfnumber(res, "tablespace");
3364 :
3365 164 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3366 164 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3367 164 : datname = PQgetvalue(res, 0, i_datname);
3368 164 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3369 164 : encoding = PQgetvalue(res, 0, i_encoding);
3370 164 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3371 164 : collate = PQgetvalue(res, 0, i_collate);
3372 164 : ctype = PQgetvalue(res, 0, i_ctype);
3373 164 : if (!PQgetisnull(res, 0, i_datlocale))
3374 28 : locale = PQgetvalue(res, 0, i_datlocale);
3375 : else
3376 136 : locale = NULL;
3377 164 : if (!PQgetisnull(res, 0, i_daticurules))
3378 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3379 : else
3380 164 : icurules = NULL;
3381 164 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3382 164 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3383 164 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3384 164 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3385 164 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3386 164 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3387 164 : tablespace = PQgetvalue(res, 0, i_tablespace);
3388 :
3389 164 : qdatname = pg_strdup(fmtId(datname));
3390 :
3391 : /*
3392 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3393 : * to preserve that), as well as the encoding, locale, and tablespace
3394 : * since those can't be altered later. Other DB properties are left to
3395 : * the DATABASE PROPERTIES entry, so that they can be applied after
3396 : * reconnecting to the target DB.
3397 : *
3398 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3399 : * shown it to be faster. When the server is in binary upgrade mode, it
3400 : * will also skip the checkpoints this strategy ordinarily performs.
3401 : */
3402 164 : if (dopt->binary_upgrade)
3403 : {
3404 70 : appendPQExpBuffer(creaQry,
3405 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3406 : "OID = %u STRATEGY = FILE_COPY",
3407 : qdatname, dbCatId.oid);
3408 : }
3409 : else
3410 : {
3411 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3412 : qdatname);
3413 : }
3414 164 : if (strlen(encoding) > 0)
3415 : {
3416 164 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3417 164 : appendStringLiteralAH(creaQry, encoding, fout);
3418 : }
3419 :
3420 164 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3421 164 : if (datlocprovider[0] == 'b')
3422 28 : appendPQExpBufferStr(creaQry, "builtin");
3423 136 : else if (datlocprovider[0] == 'c')
3424 136 : appendPQExpBufferStr(creaQry, "libc");
3425 0 : else if (datlocprovider[0] == 'i')
3426 0 : appendPQExpBufferStr(creaQry, "icu");
3427 : else
3428 0 : pg_fatal("unrecognized locale provider: %s",
3429 : datlocprovider);
3430 :
3431 164 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3432 : {
3433 164 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3434 164 : appendStringLiteralAH(creaQry, collate, fout);
3435 : }
3436 : else
3437 : {
3438 0 : if (strlen(collate) > 0)
3439 : {
3440 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3441 0 : appendStringLiteralAH(creaQry, collate, fout);
3442 : }
3443 0 : if (strlen(ctype) > 0)
3444 : {
3445 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3446 0 : appendStringLiteralAH(creaQry, ctype, fout);
3447 : }
3448 : }
3449 164 : if (locale)
3450 : {
3451 28 : if (datlocprovider[0] == 'b')
3452 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3453 : else
3454 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3455 :
3456 28 : appendStringLiteralAH(creaQry, locale, fout);
3457 : }
3458 :
3459 164 : if (icurules)
3460 : {
3461 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3462 0 : appendStringLiteralAH(creaQry, icurules, fout);
3463 : }
3464 :
3465 : /*
3466 : * For binary upgrade, carry over the collation version. For normal
3467 : * dump/restore, omit the version, so that it is computed upon restore.
3468 : */
3469 164 : if (dopt->binary_upgrade)
3470 : {
3471 70 : if (!PQgetisnull(res, 0, i_datcollversion))
3472 : {
3473 70 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3474 70 : appendStringLiteralAH(creaQry,
3475 : PQgetvalue(res, 0, i_datcollversion),
3476 : fout);
3477 : }
3478 : }
3479 :
3480 : /*
3481 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3482 : * thing; the decision whether to specify a tablespace should be left till
3483 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3484 : * label the DATABASE entry with the tablespace and let the normal
3485 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3486 : * attention to default_tablespace, so that won't work.
3487 : */
3488 164 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3489 10 : !dopt->outputNoTablespaces)
3490 10 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3491 : fmtId(tablespace));
3492 164 : appendPQExpBufferStr(creaQry, ";\n");
3493 :
3494 164 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3495 : qdatname);
3496 :
3497 164 : dbDumpId = createDumpId();
3498 :
3499 164 : ArchiveEntry(fout,
3500 : dbCatId, /* catalog ID */
3501 : dbDumpId, /* dump ID */
3502 164 : ARCHIVE_OPTS(.tag = datname,
3503 : .owner = dba,
3504 : .description = "DATABASE",
3505 : .section = SECTION_PRE_DATA,
3506 : .createStmt = creaQry->data,
3507 : .dropStmt = delQry->data));
3508 :
3509 : /* Compute correct tag for archive entry */
3510 164 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3511 :
3512 : /* Dump DB comment if any */
3513 : {
3514 : /*
3515 : * 8.2 and up keep comments on shared objects in a shared table, so we
3516 : * cannot use the dumpComment() code used for other database objects.
3517 : * Be careful that the ArchiveEntry parameters match that function.
3518 : */
3519 164 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3520 :
3521 164 : if (comment && *comment && !dopt->no_comments)
3522 : {
3523 74 : resetPQExpBuffer(dbQry);
3524 :
3525 : /*
3526 : * Generates warning when loaded into a differently-named
3527 : * database.
3528 : */
3529 74 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3530 74 : appendStringLiteralAH(dbQry, comment, fout);
3531 74 : appendPQExpBufferStr(dbQry, ";\n");
3532 :
3533 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3534 74 : ARCHIVE_OPTS(.tag = labelq->data,
3535 : .owner = dba,
3536 : .description = "COMMENT",
3537 : .section = SECTION_NONE,
3538 : .createStmt = dbQry->data,
3539 : .deps = &dbDumpId,
3540 : .nDeps = 1));
3541 : }
3542 : }
3543 :
3544 : /* Dump DB security label, if enabled */
3545 164 : if (!dopt->no_security_labels)
3546 : {
3547 : PGresult *shres;
3548 : PQExpBuffer seclabelQry;
3549 :
3550 164 : seclabelQry = createPQExpBuffer();
3551 :
3552 164 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3553 164 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3554 164 : resetPQExpBuffer(seclabelQry);
3555 164 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3556 164 : if (seclabelQry->len > 0)
3557 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3558 0 : ARCHIVE_OPTS(.tag = labelq->data,
3559 : .owner = dba,
3560 : .description = "SECURITY LABEL",
3561 : .section = SECTION_NONE,
3562 : .createStmt = seclabelQry->data,
3563 : .deps = &dbDumpId,
3564 : .nDeps = 1));
3565 164 : destroyPQExpBuffer(seclabelQry);
3566 164 : PQclear(shres);
3567 : }
3568 :
3569 : /*
3570 : * Dump ACL if any. Note that we do not support initial privileges
3571 : * (pg_init_privs) on databases.
3572 : */
3573 164 : dbdacl.privtype = 0;
3574 164 : dbdacl.initprivs = NULL;
3575 :
3576 164 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3577 : qdatname, NULL, NULL,
3578 : NULL, dba, &dbdacl);
3579 :
3580 : /*
3581 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3582 : * non-default database-level properties. (The reason this must be
3583 : * separate is that we cannot put any additional commands into the TOC
3584 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3585 : * in an implicit transaction block, and the backend won't allow CREATE
3586 : * DATABASE in that context.)
3587 : */
3588 164 : resetPQExpBuffer(creaQry);
3589 164 : resetPQExpBuffer(delQry);
3590 :
3591 164 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3592 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3593 : qdatname, datconnlimit);
3594 :
3595 164 : if (strcmp(datistemplate, "t") == 0)
3596 : {
3597 20 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3598 : qdatname);
3599 :
3600 : /*
3601 : * The backend won't accept DROP DATABASE on a template database. We
3602 : * can deal with that by removing the template marking before the DROP
3603 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3604 : * since no such command is currently supported, fake it with a direct
3605 : * UPDATE on pg_database.
3606 : */
3607 20 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3608 : "SET datistemplate = false WHERE datname = ");
3609 20 : appendStringLiteralAH(delQry, datname, fout);
3610 20 : appendPQExpBufferStr(delQry, ";\n");
3611 : }
3612 :
3613 : /*
3614 : * We do not restore pg_database.dathasloginevt because it is set
3615 : * automatically on login event trigger creation.
3616 : */
3617 :
3618 : /* Add database-specific SET options */
3619 164 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3620 :
3621 : /*
3622 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3623 : * entry, too, for lack of a better place.
3624 : */
3625 164 : if (dopt->binary_upgrade)
3626 : {
3627 70 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3628 70 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3629 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3630 : "WHERE datname = ",
3631 : frozenxid, minmxid);
3632 70 : appendStringLiteralAH(creaQry, datname, fout);
3633 70 : appendPQExpBufferStr(creaQry, ";\n");
3634 : }
3635 :
3636 164 : if (creaQry->len > 0)
3637 78 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3638 78 : ARCHIVE_OPTS(.tag = datname,
3639 : .owner = dba,
3640 : .description = "DATABASE PROPERTIES",
3641 : .section = SECTION_PRE_DATA,
3642 : .createStmt = creaQry->data,
3643 : .dropStmt = delQry->data,
3644 : .deps = &dbDumpId));
3645 :
3646 : /*
3647 : * pg_largeobject comes from the old system intact, so set its
3648 : * relfrozenxids, relminmxids and relfilenode.
3649 : *
3650 : * pg_largeobject_metadata also comes from the old system intact for
3651 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3652 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3653 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3654 : * changed its storage format in v16.
3655 : */
3656 164 : if (dopt->binary_upgrade)
3657 : {
3658 : PGresult *lo_res;
3659 70 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3660 70 : PQExpBuffer loOutQry = createPQExpBuffer();
3661 70 : PQExpBuffer lomOutQry = createPQExpBuffer();
3662 70 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3663 70 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3664 : int ii_relfrozenxid,
3665 : ii_relfilenode,
3666 : ii_oid,
3667 : ii_relminmxid;
3668 :
3669 70 : if (fout->remoteVersion >= 90300)
3670 70 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3671 : "FROM pg_catalog.pg_class\n"
3672 : "WHERE oid IN (%u, %u, %u, %u);\n",
3673 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3674 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3675 : else
3676 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3677 : "FROM pg_catalog.pg_class\n"
3678 : "WHERE oid IN (%u, %u);\n",
3679 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3680 :
3681 70 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3682 :
3683 70 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3684 70 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3685 70 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3686 70 : ii_oid = PQfnumber(lo_res, "oid");
3687 :
3688 70 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3689 70 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3690 70 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3691 70 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3692 350 : for (int i = 0; i < PQntuples(lo_res); ++i)
3693 : {
3694 : Oid oid;
3695 : RelFileNumber relfilenumber;
3696 : PQExpBuffer horizonQry;
3697 : PQExpBuffer outQry;
3698 :
3699 280 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3700 280 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3701 :
3702 280 : if (oid == LargeObjectRelationId ||
3703 : oid == LargeObjectLOidPNIndexId)
3704 : {
3705 140 : horizonQry = loHorizonQry;
3706 140 : outQry = loOutQry;
3707 : }
3708 : else
3709 : {
3710 140 : horizonQry = lomHorizonQry;
3711 140 : outQry = lomOutQry;
3712 : }
3713 :
3714 280 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3715 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3716 : "WHERE oid = %u;\n",
3717 280 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3718 280 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3719 280 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3720 :
3721 280 : if (oid == LargeObjectRelationId ||
3722 : oid == LargeObjectMetadataRelationId)
3723 140 : appendPQExpBuffer(outQry,
3724 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3725 : relfilenumber);
3726 140 : else if (oid == LargeObjectLOidPNIndexId ||
3727 : oid == LargeObjectMetadataOidIndexId)
3728 140 : appendPQExpBuffer(outQry,
3729 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3730 : relfilenumber);
3731 : }
3732 :
3733 70 : appendPQExpBufferStr(loOutQry,
3734 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3735 70 : appendPQExpBufferStr(lomOutQry,
3736 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3737 :
3738 70 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3739 70 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3740 :
3741 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3742 70 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3743 : .description = "pg_largeobject",
3744 : .section = SECTION_PRE_DATA,
3745 : .createStmt = loOutQry->data));
3746 :
3747 70 : if (fout->remoteVersion >= 160000)
3748 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3749 70 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3750 : .description = "pg_largeobject_metadata",
3751 : .section = SECTION_PRE_DATA,
3752 : .createStmt = lomOutQry->data));
3753 :
3754 70 : PQclear(lo_res);
3755 :
3756 70 : destroyPQExpBuffer(loFrozenQry);
3757 70 : destroyPQExpBuffer(loHorizonQry);
3758 70 : destroyPQExpBuffer(lomHorizonQry);
3759 70 : destroyPQExpBuffer(loOutQry);
3760 70 : destroyPQExpBuffer(lomOutQry);
3761 : }
3762 :
3763 164 : PQclear(res);
3764 :
3765 164 : free(qdatname);
3766 164 : destroyPQExpBuffer(dbQry);
3767 164 : destroyPQExpBuffer(delQry);
3768 164 : destroyPQExpBuffer(creaQry);
3769 164 : destroyPQExpBuffer(labelq);
3770 164 : }
3771 :
3772 : /*
3773 : * Collect any database-specific or role-and-database-specific SET options
3774 : * for this database, and append them to outbuf.
3775 : */
3776 : static void
3777 164 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3778 : const char *dbname, Oid dboid)
3779 : {
3780 164 : PGconn *conn = GetConnection(AH);
3781 164 : PQExpBuffer buf = createPQExpBuffer();
3782 : PGresult *res;
3783 :
3784 : /* First collect database-specific options */
3785 164 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3786 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3787 : dboid);
3788 :
3789 164 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3790 :
3791 224 : for (int i = 0; i < PQntuples(res); i++)
3792 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3793 : "DATABASE", dbname, NULL, NULL,
3794 : outbuf);
3795 :
3796 164 : PQclear(res);
3797 :
3798 : /* Now look for role-and-database-specific options */
3799 164 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3800 : "FROM pg_db_role_setting s, pg_roles r "
3801 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3802 : dboid);
3803 :
3804 164 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3805 :
3806 164 : for (int i = 0; i < PQntuples(res); i++)
3807 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3808 0 : "ROLE", PQgetvalue(res, i, 0),
3809 : "DATABASE", dbname,
3810 : outbuf);
3811 :
3812 164 : PQclear(res);
3813 :
3814 164 : destroyPQExpBuffer(buf);
3815 164 : }
3816 :
3817 : /*
3818 : * dumpEncoding: put the correct encoding into the archive
3819 : */
3820 : static void
3821 364 : dumpEncoding(Archive *AH)
3822 : {
3823 364 : const char *encname = pg_encoding_to_char(AH->encoding);
3824 364 : PQExpBuffer qry = createPQExpBuffer();
3825 :
3826 364 : pg_log_info("saving encoding = %s", encname);
3827 :
3828 364 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3829 364 : appendStringLiteralAH(qry, encname, AH);
3830 364 : appendPQExpBufferStr(qry, ";\n");
3831 :
3832 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3833 364 : ARCHIVE_OPTS(.tag = "ENCODING",
3834 : .description = "ENCODING",
3835 : .section = SECTION_PRE_DATA,
3836 : .createStmt = qry->data));
3837 :
3838 364 : destroyPQExpBuffer(qry);
3839 364 : }
3840 :
3841 :
3842 : /*
3843 : * dumpStdStrings: put the correct escape string behavior into the archive
3844 : */
3845 : static void
3846 364 : dumpStdStrings(Archive *AH)
3847 : {
3848 364 : const char *stdstrings = AH->std_strings ? "on" : "off";
3849 364 : PQExpBuffer qry = createPQExpBuffer();
3850 :
3851 364 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3852 : stdstrings);
3853 :
3854 364 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3855 : stdstrings);
3856 :
3857 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3858 364 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3859 : .description = "STDSTRINGS",
3860 : .section = SECTION_PRE_DATA,
3861 : .createStmt = qry->data));
3862 :
3863 364 : destroyPQExpBuffer(qry);
3864 364 : }
3865 :
3866 : /*
3867 : * dumpSearchPath: record the active search_path in the archive
3868 : */
3869 : static void
3870 364 : dumpSearchPath(Archive *AH)
3871 : {
3872 364 : PQExpBuffer qry = createPQExpBuffer();
3873 364 : PQExpBuffer path = createPQExpBuffer();
3874 : PGresult *res;
3875 364 : char **schemanames = NULL;
3876 364 : int nschemanames = 0;
3877 : int i;
3878 :
3879 : /*
3880 : * We use the result of current_schemas(), not the search_path GUC,
3881 : * because that might contain wildcards such as "$user", which won't
3882 : * necessarily have the same value during restore. Also, this way avoids
3883 : * listing schemas that may appear in search_path but not actually exist,
3884 : * which seems like a prudent exclusion.
3885 : */
3886 364 : res = ExecuteSqlQueryForSingleRow(AH,
3887 : "SELECT pg_catalog.current_schemas(false)");
3888 :
3889 364 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3890 0 : pg_fatal("could not parse result of current_schemas()");
3891 :
3892 : /*
3893 : * We use set_config(), not a simple "SET search_path" command, because
3894 : * the latter has less-clean behavior if the search path is empty. While
3895 : * that's likely to get fixed at some point, it seems like a good idea to
3896 : * be as backwards-compatible as possible in what we put into archives.
3897 : */
3898 364 : for (i = 0; i < nschemanames; i++)
3899 : {
3900 0 : if (i > 0)
3901 0 : appendPQExpBufferStr(path, ", ");
3902 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3903 : }
3904 :
3905 364 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3906 364 : appendStringLiteralAH(qry, path->data, AH);
3907 364 : appendPQExpBufferStr(qry, ", false);\n");
3908 :
3909 364 : pg_log_info("saving \"search_path = %s\"", path->data);
3910 :
3911 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3912 364 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3913 : .description = "SEARCHPATH",
3914 : .section = SECTION_PRE_DATA,
3915 : .createStmt = qry->data));
3916 :
3917 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3918 364 : AH->searchpath = pg_strdup(qry->data);
3919 :
3920 364 : free(schemanames);
3921 364 : PQclear(res);
3922 364 : destroyPQExpBuffer(qry);
3923 364 : destroyPQExpBuffer(path);
3924 364 : }
3925 :
3926 :
3927 : /*
3928 : * getLOs:
3929 : * Collect schema-level data about large objects
3930 : */
3931 : static void
3932 308 : getLOs(Archive *fout)
3933 : {
3934 308 : DumpOptions *dopt = fout->dopt;
3935 308 : PQExpBuffer loQry = createPQExpBuffer();
3936 : PGresult *res;
3937 : int ntups;
3938 : int i;
3939 : int n;
3940 : int i_oid;
3941 : int i_lomowner;
3942 : int i_lomacl;
3943 : int i_acldefault;
3944 :
3945 308 : pg_log_info("reading large objects");
3946 :
3947 : /*
3948 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3949 : * with the same owner/ACL appear together.
3950 : */
3951 308 : appendPQExpBufferStr(loQry,
3952 : "SELECT oid, lomowner, lomacl, "
3953 : "acldefault('L', lomowner) AS acldefault "
3954 : "FROM pg_largeobject_metadata "
3955 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3956 :
3957 308 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3958 :
3959 308 : i_oid = PQfnumber(res, "oid");
3960 308 : i_lomowner = PQfnumber(res, "lomowner");
3961 308 : i_lomacl = PQfnumber(res, "lomacl");
3962 308 : i_acldefault = PQfnumber(res, "acldefault");
3963 :
3964 308 : ntups = PQntuples(res);
3965 :
3966 : /*
3967 : * Group the blobs into suitably-sized groups that have the same owner and
3968 : * ACL setting, and build a metadata and a data DumpableObject for each
3969 : * group. (If we supported initprivs for blobs, we'd have to insist that
3970 : * groups also share initprivs settings, since the DumpableObject only has
3971 : * room for one.) i is the index of the first tuple in the current group,
3972 : * and n is the number of tuples we include in the group.
3973 : */
3974 486 : for (i = 0; i < ntups; i += n)
3975 : {
3976 178 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3977 178 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3978 178 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3979 : LoInfo *loinfo;
3980 : DumpableObject *lodata;
3981 : char namebuf[64];
3982 :
3983 : /* Scan to find first tuple not to be included in group */
3984 178 : n = 1;
3985 208 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3986 : {
3987 114 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3988 104 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3989 : break;
3990 30 : n++;
3991 : }
3992 :
3993 : /* Build the metadata DumpableObject */
3994 178 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3995 :
3996 178 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3997 178 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3998 178 : loinfo->dobj.catId.oid = thisoid;
3999 178 : AssignDumpId(&loinfo->dobj);
4000 :
4001 178 : if (n > 1)
4002 20 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4003 20 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4004 : else
4005 158 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4006 178 : loinfo->dobj.name = pg_strdup(namebuf);
4007 178 : loinfo->dacl.acl = pg_strdup(thisacl);
4008 178 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4009 178 : loinfo->dacl.privtype = 0;
4010 178 : loinfo->dacl.initprivs = NULL;
4011 178 : loinfo->rolname = getRoleName(thisowner);
4012 178 : loinfo->numlos = n;
4013 178 : loinfo->looids[0] = thisoid;
4014 : /* Collect OIDs of the remaining blobs in this group */
4015 208 : for (int k = 1; k < n; k++)
4016 : {
4017 : CatalogId extraID;
4018 :
4019 30 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4020 :
4021 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4022 30 : extraID.tableoid = LargeObjectRelationId;
4023 30 : extraID.oid = loinfo->looids[k];
4024 30 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4025 : }
4026 :
4027 : /* LOs have data */
4028 178 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4029 :
4030 : /* Mark whether LO group has a non-empty ACL */
4031 178 : if (!PQgetisnull(res, i, i_lomacl))
4032 84 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4033 :
4034 : /*
4035 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4036 : * as it will be copied by pg_upgrade, which simply copies the
4037 : * pg_largeobject table. We *do* however dump out anything but the
4038 : * data, as pg_upgrade copies just pg_largeobject, but not
4039 : * pg_largeobject_metadata, after the dump is restored. In versions
4040 : * before v12, this is done via proper large object commands. In
4041 : * newer versions, we dump the content of pg_largeobject_metadata and
4042 : * any associated pg_shdepend rows, which is faster to restore. (On
4043 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4044 : * column is hidden and won't be dumped.)
4045 : */
4046 178 : if (dopt->binary_upgrade)
4047 : {
4048 26 : if (fout->remoteVersion >= 120000)
4049 : {
4050 : /*
4051 : * We should've saved pg_largeobject_metadata's dump ID before
4052 : * this point.
4053 : */
4054 : Assert(lo_metadata_dumpId);
4055 :
4056 26 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4057 :
4058 : /*
4059 : * Mark the large object as dependent on
4060 : * pg_largeobject_metadata so that any large object
4061 : * comments/seclables are dumped after it.
4062 : */
4063 26 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4064 26 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4065 26 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4066 : }
4067 : else
4068 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4069 : }
4070 :
4071 : /*
4072 : * Create a "BLOBS" data item for the group, too. This is just a
4073 : * placeholder for sorting; it carries no data now.
4074 : */
4075 178 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4076 178 : lodata->objType = DO_LARGE_OBJECT_DATA;
4077 178 : lodata->catId = nilCatalogId;
4078 178 : AssignDumpId(lodata);
4079 178 : lodata->name = pg_strdup(namebuf);
4080 178 : lodata->components |= DUMP_COMPONENT_DATA;
4081 : /* Set up explicit dependency from data to metadata */
4082 178 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4083 178 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4084 178 : lodata->nDeps = lodata->allocDeps = 1;
4085 : }
4086 :
4087 308 : PQclear(res);
4088 308 : destroyPQExpBuffer(loQry);
4089 308 : }
4090 :
4091 : /*
4092 : * dumpLO
4093 : *
4094 : * dump the definition (metadata) of the given large object group
4095 : */
4096 : static void
4097 166 : dumpLO(Archive *fout, const LoInfo *loinfo)
4098 : {
4099 166 : PQExpBuffer cquery = createPQExpBuffer();
4100 :
4101 : /*
4102 : * The "definition" is just a newline-separated list of OIDs. We need to
4103 : * put something into the dropStmt too, but it can just be a comment.
4104 : */
4105 362 : for (int i = 0; i < loinfo->numlos; i++)
4106 196 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4107 :
4108 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4109 152 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4110 152 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4111 : .owner = loinfo->rolname,
4112 : .description = "BLOB METADATA",
4113 : .section = SECTION_DATA,
4114 : .createStmt = cquery->data,
4115 : .dropStmt = "-- dummy"));
4116 :
4117 : /*
4118 : * Dump per-blob comments and seclabels if any. We assume these are rare
4119 : * enough that it's okay to generate retail TOC entries for them.
4120 : */
4121 166 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4122 : DUMP_COMPONENT_SECLABEL))
4123 : {
4124 218 : for (int i = 0; i < loinfo->numlos; i++)
4125 : {
4126 : CatalogId catId;
4127 : char namebuf[32];
4128 :
4129 : /* Build identifying info for this blob */
4130 124 : catId.tableoid = loinfo->dobj.catId.tableoid;
4131 124 : catId.oid = loinfo->looids[i];
4132 124 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4133 :
4134 124 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4135 124 : dumpComment(fout, "LARGE OBJECT", namebuf,
4136 124 : NULL, loinfo->rolname,
4137 124 : catId, 0, loinfo->dobj.dumpId);
4138 :
4139 124 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4140 20 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4141 20 : NULL, loinfo->rolname,
4142 20 : catId, 0, loinfo->dobj.dumpId);
4143 : }
4144 : }
4145 :
4146 : /*
4147 : * Dump the ACLs if any (remember that all blobs in the group will have
4148 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4149 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4150 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4151 : * string to emit a mutated version for each blob.
4152 : */
4153 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4154 : {
4155 : char namebuf[32];
4156 :
4157 : /* Build identifying info for the first blob */
4158 72 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4159 :
4160 72 : if (loinfo->numlos > 1)
4161 : {
4162 : char tagbuf[64];
4163 :
4164 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4165 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4166 :
4167 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4168 : "LARGE OBJECT", namebuf, NULL, NULL,
4169 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4170 : }
4171 : else
4172 : {
4173 72 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4174 : "LARGE OBJECT", namebuf, NULL, NULL,
4175 72 : NULL, loinfo->rolname, &loinfo->dacl);
4176 : }
4177 : }
4178 :
4179 166 : destroyPQExpBuffer(cquery);
4180 166 : }
4181 :
4182 : /*
4183 : * dumpLOs:
4184 : * dump the data contents of the large objects in the given group
4185 : */
4186 : static int
4187 144 : dumpLOs(Archive *fout, const void *arg)
4188 : {
4189 144 : const LoInfo *loinfo = (const LoInfo *) arg;
4190 144 : PGconn *conn = GetConnection(fout);
4191 : char buf[LOBBUFSIZE];
4192 :
4193 144 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4194 :
4195 304 : for (int i = 0; i < loinfo->numlos; i++)
4196 : {
4197 160 : Oid loOid = loinfo->looids[i];
4198 : int loFd;
4199 : int cnt;
4200 :
4201 : /* Open the LO */
4202 160 : loFd = lo_open(conn, loOid, INV_READ);
4203 160 : if (loFd == -1)
4204 0 : pg_fatal("could not open large object %u: %s",
4205 : loOid, PQerrorMessage(conn));
4206 :
4207 160 : StartLO(fout, loOid);
4208 :
4209 : /* Now read it in chunks, sending data to archive */
4210 : do
4211 : {
4212 244 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4213 244 : if (cnt < 0)
4214 0 : pg_fatal("error reading large object %u: %s",
4215 : loOid, PQerrorMessage(conn));
4216 :
4217 244 : WriteData(fout, buf, cnt);
4218 244 : } while (cnt > 0);
4219 :
4220 160 : lo_close(conn, loFd);
4221 :
4222 160 : EndLO(fout, loOid);
4223 : }
4224 :
4225 144 : return 1;
4226 : }
4227 :
4228 : /*
4229 : * getPolicies
4230 : * get information about all RLS policies on dumpable tables.
4231 : */
4232 : void
4233 364 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4234 : {
4235 364 : DumpOptions *dopt = fout->dopt;
4236 : PQExpBuffer query;
4237 : PQExpBuffer tbloids;
4238 : PGresult *res;
4239 : PolicyInfo *polinfo;
4240 : int i_oid;
4241 : int i_tableoid;
4242 : int i_polrelid;
4243 : int i_polname;
4244 : int i_polcmd;
4245 : int i_polpermissive;
4246 : int i_polroles;
4247 : int i_polqual;
4248 : int i_polwithcheck;
4249 : int i,
4250 : j,
4251 : ntups;
4252 :
4253 : /* No policies before 9.5 */
4254 364 : if (fout->remoteVersion < 90500)
4255 0 : return;
4256 :
4257 : /* Skip if --no-policies was specified */
4258 364 : if (dopt->no_policies)
4259 2 : return;
4260 :
4261 362 : query = createPQExpBuffer();
4262 362 : tbloids = createPQExpBuffer();
4263 :
4264 : /*
4265 : * Identify tables of interest, and check which ones have RLS enabled.
4266 : */
4267 362 : appendPQExpBufferChar(tbloids, '{');
4268 96272 : for (i = 0; i < numTables; i++)
4269 : {
4270 95910 : TableInfo *tbinfo = &tblinfo[i];
4271 :
4272 : /* Ignore row security on tables not to be dumped */
4273 95910 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4274 81690 : continue;
4275 :
4276 : /* It can't have RLS or policies if it's not a table */
4277 14220 : if (tbinfo->relkind != RELKIND_RELATION &&
4278 4040 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4279 2844 : continue;
4280 :
4281 : /* Add it to the list of table OIDs to be probed below */
4282 11376 : if (tbloids->len > 1) /* do we have more than the '{'? */
4283 11142 : appendPQExpBufferChar(tbloids, ',');
4284 11376 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4285 :
4286 : /* Is RLS enabled? (That's separate from whether it has policies) */
4287 11376 : if (tbinfo->rowsec)
4288 : {
4289 112 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4290 :
4291 : /*
4292 : * We represent RLS being enabled on a table by creating a
4293 : * PolicyInfo object with null polname.
4294 : *
4295 : * Note: use tableoid 0 so that this object won't be mistaken for
4296 : * something that pg_depend entries apply to.
4297 : */
4298 112 : polinfo = pg_malloc(sizeof(PolicyInfo));
4299 112 : polinfo->dobj.objType = DO_POLICY;
4300 112 : polinfo->dobj.catId.tableoid = 0;
4301 112 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4302 112 : AssignDumpId(&polinfo->dobj);
4303 112 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4304 112 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4305 112 : polinfo->poltable = tbinfo;
4306 112 : polinfo->polname = NULL;
4307 112 : polinfo->polcmd = '\0';
4308 112 : polinfo->polpermissive = 0;
4309 112 : polinfo->polroles = NULL;
4310 112 : polinfo->polqual = NULL;
4311 112 : polinfo->polwithcheck = NULL;
4312 : }
4313 : }
4314 362 : appendPQExpBufferChar(tbloids, '}');
4315 :
4316 : /*
4317 : * Now, read all RLS policies belonging to the tables of interest, and
4318 : * create PolicyInfo objects for them. (Note that we must filter the
4319 : * results server-side not locally, because we dare not apply pg_get_expr
4320 : * to tables we don't have lock on.)
4321 : */
4322 362 : pg_log_info("reading row-level security policies");
4323 :
4324 362 : printfPQExpBuffer(query,
4325 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4326 362 : if (fout->remoteVersion >= 100000)
4327 362 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4328 : else
4329 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4330 362 : appendPQExpBuffer(query,
4331 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4332 : " pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
4333 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4334 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4335 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4336 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4337 : tbloids->data);
4338 :
4339 362 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4340 :
4341 362 : ntups = PQntuples(res);
4342 362 : if (ntups > 0)
4343 : {
4344 92 : i_oid = PQfnumber(res, "oid");
4345 92 : i_tableoid = PQfnumber(res, "tableoid");
4346 92 : i_polrelid = PQfnumber(res, "polrelid");
4347 92 : i_polname = PQfnumber(res, "polname");
4348 92 : i_polcmd = PQfnumber(res, "polcmd");
4349 92 : i_polpermissive = PQfnumber(res, "polpermissive");
4350 92 : i_polroles = PQfnumber(res, "polroles");
4351 92 : i_polqual = PQfnumber(res, "polqual");
4352 92 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4353 :
4354 92 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4355 :
4356 674 : for (j = 0; j < ntups; j++)
4357 : {
4358 582 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4359 582 : TableInfo *tbinfo = findTableByOid(polrelid);
4360 :
4361 582 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4362 :
4363 582 : polinfo[j].dobj.objType = DO_POLICY;
4364 582 : polinfo[j].dobj.catId.tableoid =
4365 582 : atooid(PQgetvalue(res, j, i_tableoid));
4366 582 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4367 582 : AssignDumpId(&polinfo[j].dobj);
4368 582 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4369 582 : polinfo[j].poltable = tbinfo;
4370 582 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4371 582 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4372 :
4373 582 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4374 582 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4375 :
4376 582 : if (PQgetisnull(res, j, i_polroles))
4377 254 : polinfo[j].polroles = NULL;
4378 : else
4379 328 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4380 :
4381 582 : if (PQgetisnull(res, j, i_polqual))
4382 82 : polinfo[j].polqual = NULL;
4383 : else
4384 500 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4385 :
4386 582 : if (PQgetisnull(res, j, i_polwithcheck))
4387 306 : polinfo[j].polwithcheck = NULL;
4388 : else
4389 276 : polinfo[j].polwithcheck
4390 276 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4391 : }
4392 : }
4393 :
4394 362 : PQclear(res);
4395 :
4396 362 : destroyPQExpBuffer(query);
4397 362 : destroyPQExpBuffer(tbloids);
4398 : }
4399 :
4400 : /*
4401 : * dumpPolicy
4402 : * dump the definition of the given policy
4403 : */
4404 : static void
4405 694 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4406 : {
4407 694 : DumpOptions *dopt = fout->dopt;
4408 694 : TableInfo *tbinfo = polinfo->poltable;
4409 : PQExpBuffer query;
4410 : PQExpBuffer delqry;
4411 : PQExpBuffer polprefix;
4412 : char *qtabname;
4413 : const char *cmd;
4414 : char *tag;
4415 :
4416 : /* Do nothing if not dumping schema */
4417 694 : if (!dopt->dumpSchema)
4418 98 : return;
4419 :
4420 : /*
4421 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4422 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4423 : * ROW LEVEL SECURITY.
4424 : */
4425 596 : if (polinfo->polname == NULL)
4426 : {
4427 98 : query = createPQExpBuffer();
4428 :
4429 98 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4430 98 : fmtQualifiedDumpable(tbinfo));
4431 :
4432 : /*
4433 : * We must emit the ROW SECURITY object's dependency on its table
4434 : * explicitly, because it will not match anything in pg_depend (unlike
4435 : * the case for other PolicyInfo objects).
4436 : */
4437 98 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4438 98 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4439 98 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4440 : .namespace = polinfo->dobj.namespace->dobj.name,
4441 : .owner = tbinfo->rolname,
4442 : .description = "ROW SECURITY",
4443 : .section = SECTION_POST_DATA,
4444 : .createStmt = query->data,
4445 : .deps = &(tbinfo->dobj.dumpId),
4446 : .nDeps = 1));
4447 :
4448 98 : destroyPQExpBuffer(query);
4449 98 : return;
4450 : }
4451 :
4452 498 : if (polinfo->polcmd == '*')
4453 166 : cmd = "";
4454 332 : else if (polinfo->polcmd == 'r')
4455 88 : cmd = " FOR SELECT";
4456 244 : else if (polinfo->polcmd == 'a')
4457 68 : cmd = " FOR INSERT";
4458 176 : else if (polinfo->polcmd == 'w')
4459 88 : cmd = " FOR UPDATE";
4460 88 : else if (polinfo->polcmd == 'd')
4461 88 : cmd = " FOR DELETE";
4462 : else
4463 0 : pg_fatal("unexpected policy command type: %c",
4464 : polinfo->polcmd);
4465 :
4466 498 : query = createPQExpBuffer();
4467 498 : delqry = createPQExpBuffer();
4468 498 : polprefix = createPQExpBuffer();
4469 :
4470 498 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4471 :
4472 498 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4473 :
4474 498 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4475 498 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4476 :
4477 498 : if (polinfo->polroles != NULL)
4478 272 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4479 :
4480 498 : if (polinfo->polqual != NULL)
4481 430 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4482 :
4483 498 : if (polinfo->polwithcheck != NULL)
4484 234 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4485 :
4486 498 : appendPQExpBufferStr(query, ";\n");
4487 :
4488 498 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4489 498 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4490 :
4491 498 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4492 498 : fmtId(polinfo->polname));
4493 :
4494 498 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4495 :
4496 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4497 498 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4498 498 : ARCHIVE_OPTS(.tag = tag,
4499 : .namespace = polinfo->dobj.namespace->dobj.name,
4500 : .owner = tbinfo->rolname,
4501 : .description = "POLICY",
4502 : .section = SECTION_POST_DATA,
4503 : .createStmt = query->data,
4504 : .dropStmt = delqry->data));
4505 :
4506 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4507 0 : dumpComment(fout, polprefix->data, qtabname,
4508 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4509 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4510 :
4511 498 : free(tag);
4512 498 : destroyPQExpBuffer(query);
4513 498 : destroyPQExpBuffer(delqry);
4514 498 : destroyPQExpBuffer(polprefix);
4515 498 : free(qtabname);
4516 : }
4517 :
4518 : /*
4519 : * getPublications
4520 : * get information about publications
4521 : */
4522 : void
4523 364 : getPublications(Archive *fout)
4524 : {
4525 364 : DumpOptions *dopt = fout->dopt;
4526 : PQExpBuffer query;
4527 : PGresult *res;
4528 : PublicationInfo *pubinfo;
4529 : int i_tableoid;
4530 : int i_oid;
4531 : int i_pubname;
4532 : int i_pubowner;
4533 : int i_puballtables;
4534 : int i_pubinsert;
4535 : int i_pubupdate;
4536 : int i_pubdelete;
4537 : int i_pubtruncate;
4538 : int i_pubviaroot;
4539 : int i_pubgencols;
4540 : int i,
4541 : ntups;
4542 :
4543 364 : if (dopt->no_publications || fout->remoteVersion < 100000)
4544 0 : return;
4545 :
4546 364 : query = createPQExpBuffer();
4547 :
4548 : /* Get the publications. */
4549 364 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4550 : "p.pubowner, p.puballtables, p.pubinsert, "
4551 : "p.pubupdate, p.pubdelete, ");
4552 :
4553 364 : if (fout->remoteVersion >= 110000)
4554 364 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4555 : else
4556 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4557 :
4558 364 : if (fout->remoteVersion >= 130000)
4559 364 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4560 : else
4561 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4562 :
4563 364 : if (fout->remoteVersion >= 180000)
4564 364 : appendPQExpBufferStr(query, "p.pubgencols ");
4565 : else
4566 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4567 :
4568 364 : appendPQExpBufferStr(query, "FROM pg_publication p");
4569 :
4570 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4571 :
4572 364 : ntups = PQntuples(res);
4573 :
4574 364 : if (ntups == 0)
4575 252 : goto cleanup;
4576 :
4577 112 : i_tableoid = PQfnumber(res, "tableoid");
4578 112 : i_oid = PQfnumber(res, "oid");
4579 112 : i_pubname = PQfnumber(res, "pubname");
4580 112 : i_pubowner = PQfnumber(res, "pubowner");
4581 112 : i_puballtables = PQfnumber(res, "puballtables");
4582 112 : i_pubinsert = PQfnumber(res, "pubinsert");
4583 112 : i_pubupdate = PQfnumber(res, "pubupdate");
4584 112 : i_pubdelete = PQfnumber(res, "pubdelete");
4585 112 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4586 112 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4587 112 : i_pubgencols = PQfnumber(res, "pubgencols");
4588 :
4589 112 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4590 :
4591 664 : for (i = 0; i < ntups; i++)
4592 : {
4593 552 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4594 552 : pubinfo[i].dobj.catId.tableoid =
4595 552 : atooid(PQgetvalue(res, i, i_tableoid));
4596 552 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4597 552 : AssignDumpId(&pubinfo[i].dobj);
4598 552 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4599 552 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4600 552 : pubinfo[i].puballtables =
4601 552 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4602 552 : pubinfo[i].pubinsert =
4603 552 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4604 552 : pubinfo[i].pubupdate =
4605 552 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4606 552 : pubinfo[i].pubdelete =
4607 552 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4608 552 : pubinfo[i].pubtruncate =
4609 552 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4610 552 : pubinfo[i].pubviaroot =
4611 552 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4612 552 : pubinfo[i].pubgencols_type =
4613 552 : *(PQgetvalue(res, i, i_pubgencols));
4614 :
4615 : /* Decide whether we want to dump it */
4616 552 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4617 : }
4618 :
4619 112 : cleanup:
4620 364 : PQclear(res);
4621 :
4622 364 : destroyPQExpBuffer(query);
4623 : }
4624 :
4625 : /*
4626 : * dumpPublication
4627 : * dump the definition of the given publication
4628 : */
4629 : static void
4630 452 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4631 : {
4632 452 : DumpOptions *dopt = fout->dopt;
4633 : PQExpBuffer delq;
4634 : PQExpBuffer query;
4635 : char *qpubname;
4636 452 : bool first = true;
4637 :
4638 : /* Do nothing if not dumping schema */
4639 452 : if (!dopt->dumpSchema)
4640 60 : return;
4641 :
4642 392 : delq = createPQExpBuffer();
4643 392 : query = createPQExpBuffer();
4644 :
4645 392 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4646 :
4647 392 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4648 : qpubname);
4649 :
4650 392 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4651 : qpubname);
4652 :
4653 392 : if (pubinfo->puballtables)
4654 70 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4655 :
4656 392 : appendPQExpBufferStr(query, " WITH (publish = '");
4657 392 : if (pubinfo->pubinsert)
4658 : {
4659 324 : appendPQExpBufferStr(query, "insert");
4660 324 : first = false;
4661 : }
4662 :
4663 392 : if (pubinfo->pubupdate)
4664 : {
4665 324 : if (!first)
4666 324 : appendPQExpBufferStr(query, ", ");
4667 :
4668 324 : appendPQExpBufferStr(query, "update");
4669 324 : first = false;
4670 : }
4671 :
4672 392 : if (pubinfo->pubdelete)
4673 : {
4674 324 : if (!first)
4675 324 : appendPQExpBufferStr(query, ", ");
4676 :
4677 324 : appendPQExpBufferStr(query, "delete");
4678 324 : first = false;
4679 : }
4680 :
4681 392 : if (pubinfo->pubtruncate)
4682 : {
4683 324 : if (!first)
4684 324 : appendPQExpBufferStr(query, ", ");
4685 :
4686 324 : appendPQExpBufferStr(query, "truncate");
4687 324 : first = false;
4688 : }
4689 :
4690 392 : appendPQExpBufferChar(query, '\'');
4691 :
4692 392 : if (pubinfo->pubviaroot)
4693 10 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4694 :
4695 392 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4696 68 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4697 :
4698 392 : appendPQExpBufferStr(query, ");\n");
4699 :
4700 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4701 392 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4702 392 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4703 : .owner = pubinfo->rolname,
4704 : .description = "PUBLICATION",
4705 : .section = SECTION_POST_DATA,
4706 : .createStmt = query->data,
4707 : .dropStmt = delq->data));
4708 :
4709 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4710 68 : dumpComment(fout, "PUBLICATION", qpubname,
4711 68 : NULL, pubinfo->rolname,
4712 68 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4713 :
4714 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4715 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4716 0 : NULL, pubinfo->rolname,
4717 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4718 :
4719 392 : destroyPQExpBuffer(delq);
4720 392 : destroyPQExpBuffer(query);
4721 392 : free(qpubname);
4722 : }
4723 :
4724 : /*
4725 : * getPublicationNamespaces
4726 : * get information about publication membership for dumpable schemas.
4727 : */
4728 : void
4729 364 : getPublicationNamespaces(Archive *fout)
4730 : {
4731 : PQExpBuffer query;
4732 : PGresult *res;
4733 : PublicationSchemaInfo *pubsinfo;
4734 364 : DumpOptions *dopt = fout->dopt;
4735 : int i_tableoid;
4736 : int i_oid;
4737 : int i_pnpubid;
4738 : int i_pnnspid;
4739 : int i,
4740 : j,
4741 : ntups;
4742 :
4743 364 : if (dopt->no_publications || fout->remoteVersion < 150000)
4744 0 : return;
4745 :
4746 364 : query = createPQExpBuffer();
4747 :
4748 : /* Collect all publication membership info. */
4749 364 : appendPQExpBufferStr(query,
4750 : "SELECT tableoid, oid, pnpubid, pnnspid "
4751 : "FROM pg_catalog.pg_publication_namespace");
4752 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4753 :
4754 364 : ntups = PQntuples(res);
4755 :
4756 364 : i_tableoid = PQfnumber(res, "tableoid");
4757 364 : i_oid = PQfnumber(res, "oid");
4758 364 : i_pnpubid = PQfnumber(res, "pnpubid");
4759 364 : i_pnnspid = PQfnumber(res, "pnnspid");
4760 :
4761 : /* this allocation may be more than we need */
4762 364 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4763 364 : j = 0;
4764 :
4765 626 : for (i = 0; i < ntups; i++)
4766 : {
4767 262 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4768 262 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4769 : PublicationInfo *pubinfo;
4770 : NamespaceInfo *nspinfo;
4771 :
4772 : /*
4773 : * Ignore any entries for which we aren't interested in either the
4774 : * publication or the rel.
4775 : */
4776 262 : pubinfo = findPublicationByOid(pnpubid);
4777 262 : if (pubinfo == NULL)
4778 0 : continue;
4779 262 : nspinfo = findNamespaceByOid(pnnspid);
4780 262 : if (nspinfo == NULL)
4781 0 : continue;
4782 :
4783 : /* OK, make a DumpableObject for this relationship */
4784 262 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4785 262 : pubsinfo[j].dobj.catId.tableoid =
4786 262 : atooid(PQgetvalue(res, i, i_tableoid));
4787 262 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4788 262 : AssignDumpId(&pubsinfo[j].dobj);
4789 262 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4790 262 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4791 262 : pubsinfo[j].publication = pubinfo;
4792 262 : pubsinfo[j].pubschema = nspinfo;
4793 :
4794 : /* Decide whether we want to dump it */
4795 262 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4796 :
4797 262 : j++;
4798 : }
4799 :
4800 364 : PQclear(res);
4801 364 : destroyPQExpBuffer(query);
4802 : }
4803 :
4804 : /*
4805 : * getPublicationTables
4806 : * get information about publication membership for dumpable tables.
4807 : */
4808 : void
4809 364 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4810 : {
4811 : PQExpBuffer query;
4812 : PGresult *res;
4813 : PublicationRelInfo *pubrinfo;
4814 364 : DumpOptions *dopt = fout->dopt;
4815 : int i_tableoid;
4816 : int i_oid;
4817 : int i_prpubid;
4818 : int i_prrelid;
4819 : int i_prrelqual;
4820 : int i_prattrs;
4821 : int i,
4822 : j,
4823 : ntups;
4824 :
4825 364 : if (dopt->no_publications || fout->remoteVersion < 100000)
4826 0 : return;
4827 :
4828 364 : query = createPQExpBuffer();
4829 :
4830 : /* Collect all publication membership info. */
4831 364 : if (fout->remoteVersion >= 150000)
4832 364 : appendPQExpBufferStr(query,
4833 : "SELECT tableoid, oid, prpubid, prrelid, "
4834 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4835 : "(CASE\n"
4836 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4837 : " (SELECT array_agg(attname)\n"
4838 : " FROM\n"
4839 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4840 : " pg_catalog.pg_attribute\n"
4841 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4842 : " ELSE NULL END) prattrs "
4843 : "FROM pg_catalog.pg_publication_rel pr");
4844 : else
4845 0 : appendPQExpBufferStr(query,
4846 : "SELECT tableoid, oid, prpubid, prrelid, "
4847 : "NULL AS prrelqual, NULL AS prattrs "
4848 : "FROM pg_catalog.pg_publication_rel");
4849 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4850 :
4851 364 : ntups = PQntuples(res);
4852 :
4853 364 : i_tableoid = PQfnumber(res, "tableoid");
4854 364 : i_oid = PQfnumber(res, "oid");
4855 364 : i_prpubid = PQfnumber(res, "prpubid");
4856 364 : i_prrelid = PQfnumber(res, "prrelid");
4857 364 : i_prrelqual = PQfnumber(res, "prrelqual");
4858 364 : i_prattrs = PQfnumber(res, "prattrs");
4859 :
4860 : /* this allocation may be more than we need */
4861 364 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4862 364 : j = 0;
4863 :
4864 1106 : for (i = 0; i < ntups; i++)
4865 : {
4866 742 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4867 742 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4868 : PublicationInfo *pubinfo;
4869 : TableInfo *tbinfo;
4870 :
4871 : /*
4872 : * Ignore any entries for which we aren't interested in either the
4873 : * publication or the rel.
4874 : */
4875 742 : pubinfo = findPublicationByOid(prpubid);
4876 742 : if (pubinfo == NULL)
4877 0 : continue;
4878 742 : tbinfo = findTableByOid(prrelid);
4879 742 : if (tbinfo == NULL)
4880 0 : continue;
4881 :
4882 : /* OK, make a DumpableObject for this relationship */
4883 742 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4884 742 : pubrinfo[j].dobj.catId.tableoid =
4885 742 : atooid(PQgetvalue(res, i, i_tableoid));
4886 742 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4887 742 : AssignDumpId(&pubrinfo[j].dobj);
4888 742 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4889 742 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4890 742 : pubrinfo[j].publication = pubinfo;
4891 742 : pubrinfo[j].pubtable = tbinfo;
4892 742 : if (PQgetisnull(res, i, i_prrelqual))
4893 412 : pubrinfo[j].pubrelqual = NULL;
4894 : else
4895 330 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4896 :
4897 742 : if (!PQgetisnull(res, i, i_prattrs))
4898 : {
4899 : char **attnames;
4900 : int nattnames;
4901 : PQExpBuffer attribs;
4902 :
4903 234 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4904 : &attnames, &nattnames))
4905 0 : pg_fatal("could not parse %s array", "prattrs");
4906 234 : attribs = createPQExpBuffer();
4907 674 : for (int k = 0; k < nattnames; k++)
4908 : {
4909 440 : if (k > 0)
4910 206 : appendPQExpBufferStr(attribs, ", ");
4911 :
4912 440 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4913 : }
4914 234 : pubrinfo[j].pubrattrs = attribs->data;
4915 234 : free(attribs); /* but not attribs->data */
4916 234 : free(attnames);
4917 : }
4918 : else
4919 508 : pubrinfo[j].pubrattrs = NULL;
4920 :
4921 : /* Decide whether we want to dump it */
4922 742 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4923 :
4924 742 : j++;
4925 : }
4926 :
4927 364 : PQclear(res);
4928 364 : destroyPQExpBuffer(query);
4929 : }
4930 :
4931 : /*
4932 : * dumpPublicationNamespace
4933 : * dump the definition of the given publication schema mapping.
4934 : */
4935 : static void
4936 210 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4937 : {
4938 210 : DumpOptions *dopt = fout->dopt;
4939 210 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4940 210 : PublicationInfo *pubinfo = pubsinfo->publication;
4941 : PQExpBuffer query;
4942 : char *tag;
4943 :
4944 : /* Do nothing if not dumping schema */
4945 210 : if (!dopt->dumpSchema)
4946 24 : return;
4947 :
4948 186 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4949 :
4950 186 : query = createPQExpBuffer();
4951 :
4952 186 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4953 186 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4954 :
4955 : /*
4956 : * There is no point in creating drop query as the drop is done by schema
4957 : * drop.
4958 : */
4959 186 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4960 186 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4961 186 : ARCHIVE_OPTS(.tag = tag,
4962 : .namespace = schemainfo->dobj.name,
4963 : .owner = pubinfo->rolname,
4964 : .description = "PUBLICATION TABLES IN SCHEMA",
4965 : .section = SECTION_POST_DATA,
4966 : .createStmt = query->data));
4967 :
4968 : /* These objects can't currently have comments or seclabels */
4969 :
4970 186 : free(tag);
4971 186 : destroyPQExpBuffer(query);
4972 : }
4973 :
4974 : /*
4975 : * dumpPublicationTable
4976 : * dump the definition of the given publication table mapping
4977 : */
4978 : static void
4979 610 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4980 : {
4981 610 : DumpOptions *dopt = fout->dopt;
4982 610 : PublicationInfo *pubinfo = pubrinfo->publication;
4983 610 : TableInfo *tbinfo = pubrinfo->pubtable;
4984 : PQExpBuffer query;
4985 : char *tag;
4986 :
4987 : /* Do nothing if not dumping schema */
4988 610 : if (!dopt->dumpSchema)
4989 84 : return;
4990 :
4991 526 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4992 :
4993 526 : query = createPQExpBuffer();
4994 :
4995 526 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4996 526 : fmtId(pubinfo->dobj.name));
4997 526 : appendPQExpBuffer(query, " %s",
4998 526 : fmtQualifiedDumpable(tbinfo));
4999 :
5000 526 : if (pubrinfo->pubrattrs)
5001 166 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5002 :
5003 526 : if (pubrinfo->pubrelqual)
5004 : {
5005 : /*
5006 : * It's necessary to add parentheses around the expression because
5007 : * pg_get_expr won't supply the parentheses for things like WHERE
5008 : * TRUE.
5009 : */
5010 234 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5011 : }
5012 526 : appendPQExpBufferStr(query, ";\n");
5013 :
5014 : /*
5015 : * There is no point in creating a drop query as the drop is done by table
5016 : * drop. (If you think to change this, see also _printTocEntry().)
5017 : * Although this object doesn't really have ownership as such, set the
5018 : * owner field anyway to ensure that the command is run by the correct
5019 : * role at restore time.
5020 : */
5021 526 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5022 526 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5023 526 : ARCHIVE_OPTS(.tag = tag,
5024 : .namespace = tbinfo->dobj.namespace->dobj.name,
5025 : .owner = pubinfo->rolname,
5026 : .description = "PUBLICATION TABLE",
5027 : .section = SECTION_POST_DATA,
5028 : .createStmt = query->data));
5029 :
5030 : /* These objects can't currently have comments or seclabels */
5031 :
5032 526 : free(tag);
5033 526 : destroyPQExpBuffer(query);
5034 : }
5035 :
5036 : /*
5037 : * Is the currently connected user a superuser?
5038 : */
5039 : static bool
5040 364 : is_superuser(Archive *fout)
5041 : {
5042 364 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5043 : const char *val;
5044 :
5045 364 : val = PQparameterStatus(AH->connection, "is_superuser");
5046 :
5047 364 : if (val && strcmp(val, "on") == 0)
5048 358 : return true;
5049 :
5050 6 : return false;
5051 : }
5052 :
5053 : /*
5054 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5055 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5056 : * the setting query is effective only where available.
5057 : */
5058 : static void
5059 432 : set_restrict_relation_kind(Archive *AH, const char *value)
5060 : {
5061 432 : PQExpBuffer query = createPQExpBuffer();
5062 : PGresult *res;
5063 :
5064 432 : appendPQExpBuffer(query,
5065 : "SELECT set_config(name, '%s', false) "
5066 : "FROM pg_settings "
5067 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5068 : value);
5069 432 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5070 :
5071 432 : PQclear(res);
5072 432 : destroyPQExpBuffer(query);
5073 432 : }
5074 :
5075 : /*
5076 : * getSubscriptions
5077 : * get information about subscriptions
5078 : */
5079 : void
5080 364 : getSubscriptions(Archive *fout)
5081 : {
5082 364 : DumpOptions *dopt = fout->dopt;
5083 : PQExpBuffer query;
5084 : PGresult *res;
5085 : SubscriptionInfo *subinfo;
5086 : int i_tableoid;
5087 : int i_oid;
5088 : int i_subname;
5089 : int i_subowner;
5090 : int i_subbinary;
5091 : int i_substream;
5092 : int i_subtwophasestate;
5093 : int i_subdisableonerr;
5094 : int i_subpasswordrequired;
5095 : int i_subrunasowner;
5096 : int i_subconninfo;
5097 : int i_subslotname;
5098 : int i_subsynccommit;
5099 : int i_subpublications;
5100 : int i_suborigin;
5101 : int i_suboriginremotelsn;
5102 : int i_subenabled;
5103 : int i_subfailover;
5104 : int i_subretaindeadtuples;
5105 : int i_submaxretention;
5106 : int i,
5107 : ntups;
5108 :
5109 364 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5110 0 : return;
5111 :
5112 364 : if (!is_superuser(fout))
5113 : {
5114 : int n;
5115 :
5116 6 : res = ExecuteSqlQuery(fout,
5117 : "SELECT count(*) FROM pg_subscription "
5118 : "WHERE subdbid = (SELECT oid FROM pg_database"
5119 : " WHERE datname = current_database())",
5120 : PGRES_TUPLES_OK);
5121 6 : n = atoi(PQgetvalue(res, 0, 0));
5122 6 : if (n > 0)
5123 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5124 6 : PQclear(res);
5125 6 : return;
5126 : }
5127 :
5128 358 : query = createPQExpBuffer();
5129 :
5130 : /* Get the subscriptions in current database. */
5131 358 : appendPQExpBufferStr(query,
5132 : "SELECT s.tableoid, s.oid, s.subname,\n"
5133 : " s.subowner,\n"
5134 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5135 : " s.subpublications,\n");
5136 :
5137 358 : if (fout->remoteVersion >= 140000)
5138 358 : appendPQExpBufferStr(query, " s.subbinary,\n");
5139 : else
5140 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5141 :
5142 358 : if (fout->remoteVersion >= 140000)
5143 358 : appendPQExpBufferStr(query, " s.substream,\n");
5144 : else
5145 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5146 :
5147 358 : if (fout->remoteVersion >= 150000)
5148 358 : appendPQExpBufferStr(query,
5149 : " s.subtwophasestate,\n"
5150 : " s.subdisableonerr,\n");
5151 : else
5152 0 : appendPQExpBuffer(query,
5153 : " '%c' AS subtwophasestate,\n"
5154 : " false AS subdisableonerr,\n",
5155 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5156 :
5157 358 : if (fout->remoteVersion >= 160000)
5158 358 : appendPQExpBufferStr(query,
5159 : " s.subpasswordrequired,\n"
5160 : " s.subrunasowner,\n"
5161 : " s.suborigin,\n");
5162 : else
5163 0 : appendPQExpBuffer(query,
5164 : " 't' AS subpasswordrequired,\n"
5165 : " 't' AS subrunasowner,\n"
5166 : " '%s' AS suborigin,\n",
5167 : LOGICALREP_ORIGIN_ANY);
5168 :
5169 358 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5170 72 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5171 : " s.subenabled,\n");
5172 : else
5173 286 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5174 : " false AS subenabled,\n");
5175 :
5176 358 : if (fout->remoteVersion >= 170000)
5177 358 : appendPQExpBufferStr(query,
5178 : " s.subfailover,\n");
5179 : else
5180 0 : appendPQExpBufferStr(query,
5181 : " false AS subfailover,\n");
5182 :
5183 358 : if (fout->remoteVersion >= 190000)
5184 358 : appendPQExpBufferStr(query,
5185 : " s.subretaindeadtuples,\n");
5186 : else
5187 0 : appendPQExpBufferStr(query,
5188 : " false AS subretaindeadtuples,\n");
5189 :
5190 358 : if (fout->remoteVersion >= 190000)
5191 358 : appendPQExpBufferStr(query,
5192 : " s.submaxretention\n");
5193 : else
5194 0 : appendPQExpBuffer(query,
5195 : " 0 AS submaxretention\n");
5196 :
5197 358 : appendPQExpBufferStr(query,
5198 : "FROM pg_subscription s\n");
5199 :
5200 358 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5201 72 : appendPQExpBufferStr(query,
5202 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5203 : " ON o.external_id = 'pg_' || s.oid::text \n");
5204 :
5205 358 : appendPQExpBufferStr(query,
5206 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5207 : " WHERE datname = current_database())");
5208 :
5209 358 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5210 :
5211 358 : ntups = PQntuples(res);
5212 :
5213 : /*
5214 : * Get subscription fields. We don't include subskiplsn in the dump as
5215 : * after restoring the dump this value may no longer be relevant.
5216 : */
5217 358 : i_tableoid = PQfnumber(res, "tableoid");
5218 358 : i_oid = PQfnumber(res, "oid");
5219 358 : i_subname = PQfnumber(res, "subname");
5220 358 : i_subowner = PQfnumber(res, "subowner");
5221 358 : i_subenabled = PQfnumber(res, "subenabled");
5222 358 : i_subbinary = PQfnumber(res, "subbinary");
5223 358 : i_substream = PQfnumber(res, "substream");
5224 358 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5225 358 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5226 358 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5227 358 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5228 358 : i_subfailover = PQfnumber(res, "subfailover");
5229 358 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5230 358 : i_submaxretention = PQfnumber(res, "submaxretention");
5231 358 : i_subconninfo = PQfnumber(res, "subconninfo");
5232 358 : i_subslotname = PQfnumber(res, "subslotname");
5233 358 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5234 358 : i_subpublications = PQfnumber(res, "subpublications");
5235 358 : i_suborigin = PQfnumber(res, "suborigin");
5236 358 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5237 :
5238 358 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5239 :
5240 638 : for (i = 0; i < ntups; i++)
5241 : {
5242 280 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5243 280 : subinfo[i].dobj.catId.tableoid =
5244 280 : atooid(PQgetvalue(res, i, i_tableoid));
5245 280 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5246 280 : AssignDumpId(&subinfo[i].dobj);
5247 280 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5248 280 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5249 :
5250 280 : subinfo[i].subenabled =
5251 280 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5252 280 : subinfo[i].subbinary =
5253 280 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5254 280 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5255 280 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5256 280 : subinfo[i].subdisableonerr =
5257 280 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5258 280 : subinfo[i].subpasswordrequired =
5259 280 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5260 280 : subinfo[i].subrunasowner =
5261 280 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5262 280 : subinfo[i].subfailover =
5263 280 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5264 280 : subinfo[i].subretaindeadtuples =
5265 280 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5266 280 : subinfo[i].submaxretention =
5267 280 : atoi(PQgetvalue(res, i, i_submaxretention));
5268 560 : subinfo[i].subconninfo =
5269 280 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5270 280 : if (PQgetisnull(res, i, i_subslotname))
5271 0 : subinfo[i].subslotname = NULL;
5272 : else
5273 280 : subinfo[i].subslotname =
5274 280 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5275 560 : subinfo[i].subsynccommit =
5276 280 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5277 560 : subinfo[i].subpublications =
5278 280 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5279 280 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5280 280 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5281 278 : subinfo[i].suboriginremotelsn = NULL;
5282 : else
5283 2 : subinfo[i].suboriginremotelsn =
5284 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5285 :
5286 : /* Decide whether we want to dump it */
5287 280 : selectDumpableObject(&(subinfo[i].dobj), fout);
5288 : }
5289 358 : PQclear(res);
5290 :
5291 358 : destroyPQExpBuffer(query);
5292 : }
5293 :
5294 : /*
5295 : * getSubscriptionTables
5296 : * Get information about subscription membership for dumpable tables. This
5297 : * will be used only in binary-upgrade mode for PG17 or later versions.
5298 : */
5299 : void
5300 364 : getSubscriptionTables(Archive *fout)
5301 : {
5302 364 : DumpOptions *dopt = fout->dopt;
5303 364 : SubscriptionInfo *subinfo = NULL;
5304 : SubRelInfo *subrinfo;
5305 : PGresult *res;
5306 : int i_srsubid;
5307 : int i_srrelid;
5308 : int i_srsubstate;
5309 : int i_srsublsn;
5310 : int ntups;
5311 364 : Oid last_srsubid = InvalidOid;
5312 :
5313 364 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5314 72 : fout->remoteVersion < 170000)
5315 292 : return;
5316 :
5317 72 : res = ExecuteSqlQuery(fout,
5318 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5319 : "FROM pg_catalog.pg_subscription_rel "
5320 : "ORDER BY srsubid",
5321 : PGRES_TUPLES_OK);
5322 72 : ntups = PQntuples(res);
5323 72 : if (ntups == 0)
5324 70 : goto cleanup;
5325 :
5326 : /* Get pg_subscription_rel attributes */
5327 2 : i_srsubid = PQfnumber(res, "srsubid");
5328 2 : i_srrelid = PQfnumber(res, "srrelid");
5329 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5330 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5331 :
5332 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5333 6 : for (int i = 0; i < ntups; i++)
5334 : {
5335 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5336 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5337 : TableInfo *tblinfo;
5338 :
5339 : /*
5340 : * If we switched to a new subscription, check if the subscription
5341 : * exists.
5342 : */
5343 4 : if (cur_srsubid != last_srsubid)
5344 : {
5345 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5346 4 : if (subinfo == NULL)
5347 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5348 :
5349 4 : last_srsubid = cur_srsubid;
5350 : }
5351 :
5352 4 : tblinfo = findTableByOid(relid);
5353 4 : if (tblinfo == NULL)
5354 0 : pg_fatal("failed sanity check, table with OID %u not found",
5355 : relid);
5356 :
5357 : /* OK, make a DumpableObject for this relationship */
5358 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5359 4 : subrinfo[i].dobj.catId.tableoid = relid;
5360 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5361 4 : AssignDumpId(&subrinfo[i].dobj);
5362 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5363 4 : subrinfo[i].tblinfo = tblinfo;
5364 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5365 4 : if (PQgetisnull(res, i, i_srsublsn))
5366 2 : subrinfo[i].srsublsn = NULL;
5367 : else
5368 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5369 :
5370 4 : subrinfo[i].subinfo = subinfo;
5371 :
5372 : /* Decide whether we want to dump it */
5373 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5374 : }
5375 :
5376 2 : cleanup:
5377 72 : PQclear(res);
5378 : }
5379 :
5380 : /*
5381 : * dumpSubscriptionTable
5382 : * Dump the definition of the given subscription table mapping. This will be
5383 : * used only in binary-upgrade mode for PG17 or later versions.
5384 : */
5385 : static void
5386 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5387 : {
5388 4 : DumpOptions *dopt = fout->dopt;
5389 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5390 : PQExpBuffer query;
5391 : char *tag;
5392 :
5393 : /* Do nothing if not dumping schema */
5394 4 : if (!dopt->dumpSchema)
5395 0 : return;
5396 :
5397 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5398 :
5399 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5400 :
5401 4 : query = createPQExpBuffer();
5402 :
5403 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5404 : {
5405 : /*
5406 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5407 : * to pg_subscription_rel table. This will be used only in
5408 : * binary-upgrade mode.
5409 : */
5410 4 : appendPQExpBufferStr(query,
5411 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5412 4 : appendPQExpBufferStr(query,
5413 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5414 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5415 4 : appendPQExpBuffer(query,
5416 : ", %u, '%c'",
5417 4 : subrinfo->tblinfo->dobj.catId.oid,
5418 4 : subrinfo->srsubstate);
5419 :
5420 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5421 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5422 : else
5423 2 : appendPQExpBufferStr(query, ", NULL");
5424 :
5425 4 : appendPQExpBufferStr(query, ");\n");
5426 : }
5427 :
5428 : /*
5429 : * There is no point in creating a drop query as the drop is done by table
5430 : * drop. (If you think to change this, see also _printTocEntry().)
5431 : * Although this object doesn't really have ownership as such, set the
5432 : * owner field anyway to ensure that the command is run by the correct
5433 : * role at restore time.
5434 : */
5435 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5436 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5437 4 : ARCHIVE_OPTS(.tag = tag,
5438 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5439 : .owner = subinfo->rolname,
5440 : .description = "SUBSCRIPTION TABLE",
5441 : .section = SECTION_POST_DATA,
5442 : .createStmt = query->data));
5443 :
5444 : /* These objects can't currently have comments or seclabels */
5445 :
5446 4 : free(tag);
5447 4 : destroyPQExpBuffer(query);
5448 : }
5449 :
5450 : /*
5451 : * dumpSubscription
5452 : * dump the definition of the given subscription
5453 : */
5454 : static void
5455 244 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5456 : {
5457 244 : DumpOptions *dopt = fout->dopt;
5458 : PQExpBuffer delq;
5459 : PQExpBuffer query;
5460 : PQExpBuffer publications;
5461 : char *qsubname;
5462 244 : char **pubnames = NULL;
5463 244 : int npubnames = 0;
5464 : int i;
5465 :
5466 : /* Do nothing if not dumping schema */
5467 244 : if (!dopt->dumpSchema)
5468 36 : return;
5469 :
5470 208 : delq = createPQExpBuffer();
5471 208 : query = createPQExpBuffer();
5472 :
5473 208 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5474 :
5475 208 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5476 : qsubname);
5477 :
5478 208 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5479 : qsubname);
5480 208 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5481 :
5482 : /* Build list of quoted publications and append them to query. */
5483 208 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5484 0 : pg_fatal("could not parse %s array", "subpublications");
5485 :
5486 208 : publications = createPQExpBuffer();
5487 416 : for (i = 0; i < npubnames; i++)
5488 : {
5489 208 : if (i > 0)
5490 0 : appendPQExpBufferStr(publications, ", ");
5491 :
5492 208 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5493 : }
5494 :
5495 208 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5496 208 : if (subinfo->subslotname)
5497 208 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5498 : else
5499 0 : appendPQExpBufferStr(query, "NONE");
5500 :
5501 208 : if (subinfo->subbinary)
5502 0 : appendPQExpBufferStr(query, ", binary = true");
5503 :
5504 208 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5505 68 : appendPQExpBufferStr(query, ", streaming = on");
5506 140 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5507 72 : appendPQExpBufferStr(query, ", streaming = parallel");
5508 : else
5509 68 : appendPQExpBufferStr(query, ", streaming = off");
5510 :
5511 208 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5512 0 : appendPQExpBufferStr(query, ", two_phase = on");
5513 :
5514 208 : if (subinfo->subdisableonerr)
5515 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5516 :
5517 208 : if (!subinfo->subpasswordrequired)
5518 0 : appendPQExpBufferStr(query, ", password_required = false");
5519 :
5520 208 : if (subinfo->subrunasowner)
5521 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5522 :
5523 208 : if (subinfo->subfailover)
5524 2 : appendPQExpBufferStr(query, ", failover = true");
5525 :
5526 208 : if (subinfo->subretaindeadtuples)
5527 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5528 :
5529 208 : if (subinfo->submaxretention)
5530 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5531 :
5532 208 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5533 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5534 :
5535 208 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5536 68 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5537 :
5538 208 : appendPQExpBufferStr(query, ");\n");
5539 :
5540 : /*
5541 : * In binary-upgrade mode, we allow the replication to continue after the
5542 : * upgrade.
5543 : */
5544 208 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5545 : {
5546 10 : if (subinfo->suboriginremotelsn)
5547 : {
5548 : /*
5549 : * Preserve the remote_lsn for the subscriber's replication
5550 : * origin. This value is required to start the replication from
5551 : * the position before the upgrade. This value will be stale if
5552 : * the publisher gets upgraded before the subscriber node.
5553 : * However, this shouldn't be a problem as the upgrade of the
5554 : * publisher ensures that all the transactions were replicated
5555 : * before upgrading it.
5556 : */
5557 2 : appendPQExpBufferStr(query,
5558 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5559 2 : appendPQExpBufferStr(query,
5560 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5561 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5562 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5563 : }
5564 :
5565 10 : if (subinfo->subenabled)
5566 : {
5567 : /*
5568 : * Enable the subscription to allow the replication to continue
5569 : * after the upgrade.
5570 : */
5571 2 : appendPQExpBufferStr(query,
5572 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5573 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5574 : }
5575 : }
5576 :
5577 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5578 208 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5579 208 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5580 : .owner = subinfo->rolname,
5581 : .description = "SUBSCRIPTION",
5582 : .section = SECTION_POST_DATA,
5583 : .createStmt = query->data,
5584 : .dropStmt = delq->data));
5585 :
5586 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5587 68 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5588 68 : NULL, subinfo->rolname,
5589 68 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5590 :
5591 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5592 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5593 0 : NULL, subinfo->rolname,
5594 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5595 :
5596 208 : destroyPQExpBuffer(publications);
5597 208 : free(pubnames);
5598 :
5599 208 : destroyPQExpBuffer(delq);
5600 208 : destroyPQExpBuffer(query);
5601 208 : free(qsubname);
5602 : }
5603 :
5604 : /*
5605 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5606 : * the object needs.
5607 : */
5608 : static void
5609 10394 : append_depends_on_extension(Archive *fout,
5610 : PQExpBuffer create,
5611 : const DumpableObject *dobj,
5612 : const char *catalog,
5613 : const char *keyword,
5614 : const char *objname)
5615 : {
5616 10394 : if (dobj->depends_on_ext)
5617 : {
5618 : char *nm;
5619 : PGresult *res;
5620 : PQExpBuffer query;
5621 : int ntups;
5622 : int i_extname;
5623 : int i;
5624 :
5625 : /* dodge fmtId() non-reentrancy */
5626 84 : nm = pg_strdup(objname);
5627 :
5628 84 : query = createPQExpBuffer();
5629 84 : appendPQExpBuffer(query,
5630 : "SELECT e.extname "
5631 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5632 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5633 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5634 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5635 : catalog,
5636 84 : dobj->catId.oid);
5637 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5638 84 : ntups = PQntuples(res);
5639 84 : i_extname = PQfnumber(res, "extname");
5640 168 : for (i = 0; i < ntups; i++)
5641 : {
5642 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5643 : keyword, nm,
5644 84 : fmtId(PQgetvalue(res, i, i_extname)));
5645 : }
5646 :
5647 84 : PQclear(res);
5648 84 : destroyPQExpBuffer(query);
5649 84 : pg_free(nm);
5650 : }
5651 10394 : }
5652 :
5653 : static Oid
5654 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5655 : {
5656 : /*
5657 : * If the old version didn't assign an array type, but the new version
5658 : * does, we must select an unused type OID to assign. This currently only
5659 : * happens for domains, when upgrading pre-v11 to v11 and up.
5660 : *
5661 : * Note: local state here is kind of ugly, but we must have some, since we
5662 : * mustn't choose the same unused OID more than once.
5663 : */
5664 : static Oid next_possible_free_oid = FirstNormalObjectId;
5665 : PGresult *res;
5666 : bool is_dup;
5667 :
5668 : do
5669 : {
5670 0 : ++next_possible_free_oid;
5671 0 : printfPQExpBuffer(upgrade_query,
5672 : "SELECT EXISTS(SELECT 1 "
5673 : "FROM pg_catalog.pg_type "
5674 : "WHERE oid = '%u'::pg_catalog.oid);",
5675 : next_possible_free_oid);
5676 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5677 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5678 0 : PQclear(res);
5679 0 : } while (is_dup);
5680 :
5681 0 : return next_possible_free_oid;
5682 : }
5683 :
5684 : static void
5685 1886 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5686 : PQExpBuffer upgrade_buffer,
5687 : Oid pg_type_oid,
5688 : bool force_array_type,
5689 : bool include_multirange_type)
5690 : {
5691 1886 : PQExpBuffer upgrade_query = createPQExpBuffer();
5692 : PGresult *res;
5693 : Oid pg_type_array_oid;
5694 : Oid pg_type_multirange_oid;
5695 : Oid pg_type_multirange_array_oid;
5696 : TypeInfo *tinfo;
5697 :
5698 1886 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5699 1886 : appendPQExpBuffer(upgrade_buffer,
5700 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5701 : pg_type_oid);
5702 :
5703 1886 : tinfo = findTypeByOid(pg_type_oid);
5704 1886 : if (tinfo)
5705 1886 : pg_type_array_oid = tinfo->typarray;
5706 : else
5707 0 : pg_type_array_oid = InvalidOid;
5708 :
5709 1886 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5710 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5711 :
5712 1886 : if (OidIsValid(pg_type_array_oid))
5713 : {
5714 1882 : appendPQExpBufferStr(upgrade_buffer,
5715 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5716 1882 : appendPQExpBuffer(upgrade_buffer,
5717 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5718 : pg_type_array_oid);
5719 : }
5720 :
5721 : /*
5722 : * Pre-set the multirange type oid and its own array type oid.
5723 : */
5724 1886 : if (include_multirange_type)
5725 : {
5726 16 : if (fout->remoteVersion >= 140000)
5727 : {
5728 16 : printfPQExpBuffer(upgrade_query,
5729 : "SELECT t.oid, t.typarray "
5730 : "FROM pg_catalog.pg_type t "
5731 : "JOIN pg_catalog.pg_range r "
5732 : "ON t.oid = r.rngmultitypid "
5733 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5734 : pg_type_oid);
5735 :
5736 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5737 :
5738 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5739 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5740 :
5741 16 : PQclear(res);
5742 : }
5743 : else
5744 : {
5745 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5746 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5747 : }
5748 :
5749 16 : appendPQExpBufferStr(upgrade_buffer,
5750 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5751 16 : appendPQExpBuffer(upgrade_buffer,
5752 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5753 : pg_type_multirange_oid);
5754 16 : appendPQExpBufferStr(upgrade_buffer,
5755 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5756 16 : appendPQExpBuffer(upgrade_buffer,
5757 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5758 : pg_type_multirange_array_oid);
5759 : }
5760 :
5761 1886 : destroyPQExpBuffer(upgrade_query);
5762 1886 : }
5763 :
5764 : static void
5765 1736 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5766 : PQExpBuffer upgrade_buffer,
5767 : const TableInfo *tbinfo)
5768 : {
5769 1736 : Oid pg_type_oid = tbinfo->reltype;
5770 :
5771 1736 : if (OidIsValid(pg_type_oid))
5772 1736 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5773 : pg_type_oid, false, false);
5774 1736 : }
5775 :
5776 : /*
5777 : * bsearch() comparator for BinaryUpgradeClassOidItem
5778 : */
5779 : static int
5780 24746 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5781 : {
5782 24746 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5783 24746 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5784 :
5785 24746 : return pg_cmp_u32(v1.oid, v2.oid);
5786 : }
5787 :
5788 : /*
5789 : * collectBinaryUpgradeClassOids
5790 : *
5791 : * Construct a table of pg_class information required for
5792 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5793 : * lookup.
5794 : */
5795 : static void
5796 72 : collectBinaryUpgradeClassOids(Archive *fout)
5797 : {
5798 : PGresult *res;
5799 : const char *query;
5800 :
5801 72 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5802 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5803 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5804 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5805 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5806 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5807 : "ORDER BY c.oid;";
5808 :
5809 72 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5810 :
5811 72 : nbinaryUpgradeClassOids = PQntuples(res);
5812 72 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5813 72 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5814 :
5815 33652 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5816 : {
5817 33580 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5818 33580 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5819 33580 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5820 33580 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5821 33580 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5822 33580 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5823 33580 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5824 : }
5825 :
5826 72 : PQclear(res);
5827 72 : }
5828 :
5829 : static void
5830 2508 : binary_upgrade_set_pg_class_oids(Archive *fout,
5831 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5832 : {
5833 2508 : BinaryUpgradeClassOidItem key = {0};
5834 : BinaryUpgradeClassOidItem *entry;
5835 :
5836 : Assert(binaryUpgradeClassOids);
5837 :
5838 : /*
5839 : * Preserve the OID and relfilenumber of the table, table's index, table's
5840 : * toast table and toast table's index if any.
5841 : *
5842 : * One complexity is that the current table definition might not require
5843 : * the creation of a TOAST table, but the old database might have a TOAST
5844 : * table that was created earlier, before some wide columns were dropped.
5845 : * By setting the TOAST oid we force creation of the TOAST heap and index
5846 : * by the new backend, so we can copy the files during binary upgrade
5847 : * without worrying about this case.
5848 : */
5849 2508 : key.oid = pg_class_oid;
5850 2508 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5851 : sizeof(BinaryUpgradeClassOidItem),
5852 : BinaryUpgradeClassOidItemCmp);
5853 :
5854 2508 : appendPQExpBufferStr(upgrade_buffer,
5855 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5856 :
5857 2508 : if (entry->relkind != RELKIND_INDEX &&
5858 1954 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5859 : {
5860 1904 : appendPQExpBuffer(upgrade_buffer,
5861 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5862 : pg_class_oid);
5863 :
5864 : /*
5865 : * Not every relation has storage. Also, in a pre-v12 database,
5866 : * partitioned tables have a relfilenumber, which should not be
5867 : * preserved when upgrading.
5868 : */
5869 1904 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5870 1580 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5871 1580 : appendPQExpBuffer(upgrade_buffer,
5872 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5873 : entry->relfilenumber);
5874 :
5875 : /*
5876 : * In a pre-v12 database, partitioned tables might be marked as having
5877 : * toast tables, but we should ignore them if so.
5878 : */
5879 1904 : if (OidIsValid(entry->toast_oid) &&
5880 560 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5881 : {
5882 560 : appendPQExpBuffer(upgrade_buffer,
5883 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5884 : entry->toast_oid);
5885 560 : appendPQExpBuffer(upgrade_buffer,
5886 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5887 : entry->toast_relfilenumber);
5888 :
5889 : /* every toast table has an index */
5890 560 : appendPQExpBuffer(upgrade_buffer,
5891 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5892 : entry->toast_index_oid);
5893 560 : appendPQExpBuffer(upgrade_buffer,
5894 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5895 : entry->toast_index_relfilenumber);
5896 : }
5897 : }
5898 : else
5899 : {
5900 : /* Preserve the OID and relfilenumber of the index */
5901 604 : appendPQExpBuffer(upgrade_buffer,
5902 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5903 : pg_class_oid);
5904 604 : appendPQExpBuffer(upgrade_buffer,
5905 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5906 : entry->relfilenumber);
5907 : }
5908 :
5909 2508 : appendPQExpBufferChar(upgrade_buffer, '\n');
5910 2508 : }
5911 :
5912 : /*
5913 : * If the DumpableObject is a member of an extension, add a suitable
5914 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5915 : *
5916 : * For somewhat historical reasons, objname should already be quoted,
5917 : * but not objnamespace (if any).
5918 : */
5919 : static void
5920 3008 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5921 : const DumpableObject *dobj,
5922 : const char *objtype,
5923 : const char *objname,
5924 : const char *objnamespace)
5925 : {
5926 3008 : DumpableObject *extobj = NULL;
5927 : int i;
5928 :
5929 3008 : if (!dobj->ext_member)
5930 2966 : return;
5931 :
5932 : /*
5933 : * Find the parent extension. We could avoid this search if we wanted to
5934 : * add a link field to DumpableObject, but the space costs of that would
5935 : * be considerable. We assume that member objects could only have a
5936 : * direct dependency on their own extension, not any others.
5937 : */
5938 42 : for (i = 0; i < dobj->nDeps; i++)
5939 : {
5940 42 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5941 42 : if (extobj && extobj->objType == DO_EXTENSION)
5942 42 : break;
5943 0 : extobj = NULL;
5944 : }
5945 42 : if (extobj == NULL)
5946 0 : pg_fatal("could not find parent extension for %s %s",
5947 : objtype, objname);
5948 :
5949 42 : appendPQExpBufferStr(upgrade_buffer,
5950 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5951 42 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5952 42 : fmtId(extobj->name),
5953 : objtype);
5954 42 : if (objnamespace && *objnamespace)
5955 36 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5956 42 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5957 : }
5958 :
5959 : /*
5960 : * getNamespaces:
5961 : * get information about all namespaces in the system catalogs
5962 : */
5963 : void
5964 366 : getNamespaces(Archive *fout)
5965 : {
5966 : PGresult *res;
5967 : int ntups;
5968 : int i;
5969 : PQExpBuffer query;
5970 : NamespaceInfo *nsinfo;
5971 : int i_tableoid;
5972 : int i_oid;
5973 : int i_nspname;
5974 : int i_nspowner;
5975 : int i_nspacl;
5976 : int i_acldefault;
5977 :
5978 366 : query = createPQExpBuffer();
5979 :
5980 : /*
5981 : * we fetch all namespaces including system ones, so that every object we
5982 : * read in can be linked to a containing namespace.
5983 : */
5984 366 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5985 : "n.nspowner, "
5986 : "n.nspacl, "
5987 : "acldefault('n', n.nspowner) AS acldefault "
5988 : "FROM pg_namespace n");
5989 :
5990 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5991 :
5992 366 : ntups = PQntuples(res);
5993 :
5994 366 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5995 :
5996 366 : i_tableoid = PQfnumber(res, "tableoid");
5997 366 : i_oid = PQfnumber(res, "oid");
5998 366 : i_nspname = PQfnumber(res, "nspname");
5999 366 : i_nspowner = PQfnumber(res, "nspowner");
6000 366 : i_nspacl = PQfnumber(res, "nspacl");
6001 366 : i_acldefault = PQfnumber(res, "acldefault");
6002 :
6003 3198 : for (i = 0; i < ntups; i++)
6004 : {
6005 : const char *nspowner;
6006 :
6007 2832 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6008 2832 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6009 2832 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6010 2832 : AssignDumpId(&nsinfo[i].dobj);
6011 2832 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6012 2832 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6013 2832 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6014 2832 : nsinfo[i].dacl.privtype = 0;
6015 2832 : nsinfo[i].dacl.initprivs = NULL;
6016 2832 : nspowner = PQgetvalue(res, i, i_nspowner);
6017 2832 : nsinfo[i].nspowner = atooid(nspowner);
6018 2832 : nsinfo[i].rolname = getRoleName(nspowner);
6019 :
6020 : /* Decide whether to dump this namespace */
6021 2832 : selectDumpableNamespace(&nsinfo[i], fout);
6022 :
6023 : /* Mark whether namespace has an ACL */
6024 2832 : if (!PQgetisnull(res, i, i_nspacl))
6025 1228 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6026 :
6027 : /*
6028 : * We ignore any pg_init_privs.initprivs entry for the public schema
6029 : * and assume a predetermined default, for several reasons. First,
6030 : * dropping and recreating the schema removes its pg_init_privs entry,
6031 : * but an empty destination database starts with this ACL nonetheless.
6032 : * Second, we support dump/reload of public schema ownership changes.
6033 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6034 : * initprivs continues to reflect the initial owner. Hence,
6035 : * synthesize the value that nspacl will have after the restore's
6036 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6037 : * match the source's ACL, even if the latter was an initdb-default
6038 : * ACL, which changed in v15. An upgrade pulls in changes to most
6039 : * system object ACLs that the DBA had not customized. We've made the
6040 : * public schema depart from that, because changing its ACL so easily
6041 : * breaks applications.
6042 : */
6043 2832 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6044 : {
6045 358 : PQExpBuffer aclarray = createPQExpBuffer();
6046 358 : PQExpBuffer aclitem = createPQExpBuffer();
6047 :
6048 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6049 358 : appendPQExpBufferChar(aclarray, '{');
6050 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6051 358 : appendPQExpBufferStr(aclitem, "=UC/");
6052 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6053 358 : appendPGArray(aclarray, aclitem->data);
6054 358 : resetPQExpBuffer(aclitem);
6055 358 : appendPQExpBufferStr(aclitem, "=U/");
6056 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6057 358 : appendPGArray(aclarray, aclitem->data);
6058 358 : appendPQExpBufferChar(aclarray, '}');
6059 :
6060 358 : nsinfo[i].dacl.privtype = 'i';
6061 358 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6062 358 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6063 :
6064 358 : destroyPQExpBuffer(aclarray);
6065 358 : destroyPQExpBuffer(aclitem);
6066 : }
6067 : }
6068 :
6069 366 : PQclear(res);
6070 366 : destroyPQExpBuffer(query);
6071 366 : }
6072 :
6073 : /*
6074 : * findNamespace:
6075 : * given a namespace OID, look up the info read by getNamespaces
6076 : */
6077 : static NamespaceInfo *
6078 1150676 : findNamespace(Oid nsoid)
6079 : {
6080 : NamespaceInfo *nsinfo;
6081 :
6082 1150676 : nsinfo = findNamespaceByOid(nsoid);
6083 1150676 : if (nsinfo == NULL)
6084 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6085 1150676 : return nsinfo;
6086 : }
6087 :
6088 : /*
6089 : * getExtensions:
6090 : * read all extensions in the system catalogs and return them in the
6091 : * ExtensionInfo* structure
6092 : *
6093 : * numExtensions is set to the number of extensions read in
6094 : */
6095 : ExtensionInfo *
6096 366 : getExtensions(Archive *fout, int *numExtensions)
6097 : {
6098 366 : DumpOptions *dopt = fout->dopt;
6099 : PGresult *res;
6100 : int ntups;
6101 : int i;
6102 : PQExpBuffer query;
6103 366 : ExtensionInfo *extinfo = NULL;
6104 : int i_tableoid;
6105 : int i_oid;
6106 : int i_extname;
6107 : int i_nspname;
6108 : int i_extrelocatable;
6109 : int i_extversion;
6110 : int i_extconfig;
6111 : int i_extcondition;
6112 :
6113 366 : query = createPQExpBuffer();
6114 :
6115 366 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6116 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6117 : "FROM pg_extension x "
6118 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6119 :
6120 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6121 :
6122 366 : ntups = PQntuples(res);
6123 366 : if (ntups == 0)
6124 0 : goto cleanup;
6125 :
6126 366 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6127 :
6128 366 : i_tableoid = PQfnumber(res, "tableoid");
6129 366 : i_oid = PQfnumber(res, "oid");
6130 366 : i_extname = PQfnumber(res, "extname");
6131 366 : i_nspname = PQfnumber(res, "nspname");
6132 366 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6133 366 : i_extversion = PQfnumber(res, "extversion");
6134 366 : i_extconfig = PQfnumber(res, "extconfig");
6135 366 : i_extcondition = PQfnumber(res, "extcondition");
6136 :
6137 792 : for (i = 0; i < ntups; i++)
6138 : {
6139 426 : extinfo[i].dobj.objType = DO_EXTENSION;
6140 426 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6141 426 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6142 426 : AssignDumpId(&extinfo[i].dobj);
6143 426 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6144 426 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6145 426 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6146 426 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6147 426 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6148 426 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6149 :
6150 : /* Decide whether we want to dump it */
6151 426 : selectDumpableExtension(&(extinfo[i]), dopt);
6152 : }
6153 :
6154 366 : cleanup:
6155 366 : PQclear(res);
6156 366 : destroyPQExpBuffer(query);
6157 :
6158 366 : *numExtensions = ntups;
6159 :
6160 366 : return extinfo;
6161 : }
6162 :
6163 : /*
6164 : * getTypes:
6165 : * get information about all types in the system catalogs
6166 : *
6167 : * NB: this must run after getFuncs() because we assume we can do
6168 : * findFuncByOid().
6169 : */
6170 : void
6171 364 : getTypes(Archive *fout)
6172 : {
6173 : PGresult *res;
6174 : int ntups;
6175 : int i;
6176 364 : PQExpBuffer query = createPQExpBuffer();
6177 : TypeInfo *tyinfo;
6178 : ShellTypeInfo *stinfo;
6179 : int i_tableoid;
6180 : int i_oid;
6181 : int i_typname;
6182 : int i_typnamespace;
6183 : int i_typacl;
6184 : int i_acldefault;
6185 : int i_typowner;
6186 : int i_typelem;
6187 : int i_typrelid;
6188 : int i_typrelkind;
6189 : int i_typtype;
6190 : int i_typisdefined;
6191 : int i_isarray;
6192 : int i_typarray;
6193 :
6194 : /*
6195 : * we include even the built-in types because those may be used as array
6196 : * elements by user-defined types
6197 : *
6198 : * we filter out the built-in types when we dump out the types
6199 : *
6200 : * same approach for undefined (shell) types and array types
6201 : *
6202 : * Note: as of 8.3 we can reliably detect whether a type is an
6203 : * auto-generated array type by checking the element type's typarray.
6204 : * (Before that the test is capable of generating false positives.) We
6205 : * still check for name beginning with '_', though, so as to avoid the
6206 : * cost of the subselect probe for all standard types. This would have to
6207 : * be revisited if the backend ever allows renaming of array types.
6208 : */
6209 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6210 : "typnamespace, typacl, "
6211 : "acldefault('T', typowner) AS acldefault, "
6212 : "typowner, "
6213 : "typelem, typrelid, typarray, "
6214 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6215 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6216 : "typtype, typisdefined, "
6217 : "typname[0] = '_' AND typelem != 0 AND "
6218 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6219 : "FROM pg_type");
6220 :
6221 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6222 :
6223 364 : ntups = PQntuples(res);
6224 :
6225 364 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6226 :
6227 364 : i_tableoid = PQfnumber(res, "tableoid");
6228 364 : i_oid = PQfnumber(res, "oid");
6229 364 : i_typname = PQfnumber(res, "typname");
6230 364 : i_typnamespace = PQfnumber(res, "typnamespace");
6231 364 : i_typacl = PQfnumber(res, "typacl");
6232 364 : i_acldefault = PQfnumber(res, "acldefault");
6233 364 : i_typowner = PQfnumber(res, "typowner");
6234 364 : i_typelem = PQfnumber(res, "typelem");
6235 364 : i_typrelid = PQfnumber(res, "typrelid");
6236 364 : i_typrelkind = PQfnumber(res, "typrelkind");
6237 364 : i_typtype = PQfnumber(res, "typtype");
6238 364 : i_typisdefined = PQfnumber(res, "typisdefined");
6239 364 : i_isarray = PQfnumber(res, "isarray");
6240 364 : i_typarray = PQfnumber(res, "typarray");
6241 :
6242 265362 : for (i = 0; i < ntups; i++)
6243 : {
6244 264998 : tyinfo[i].dobj.objType = DO_TYPE;
6245 264998 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6246 264998 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6247 264998 : AssignDumpId(&tyinfo[i].dobj);
6248 264998 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6249 529996 : tyinfo[i].dobj.namespace =
6250 264998 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6251 264998 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6252 264998 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6253 264998 : tyinfo[i].dacl.privtype = 0;
6254 264998 : tyinfo[i].dacl.initprivs = NULL;
6255 264998 : tyinfo[i].ftypname = NULL; /* may get filled later */
6256 264998 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6257 264998 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6258 264998 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6259 264998 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6260 264998 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6261 264998 : tyinfo[i].shellType = NULL;
6262 :
6263 264998 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6264 264888 : tyinfo[i].isDefined = true;
6265 : else
6266 110 : tyinfo[i].isDefined = false;
6267 :
6268 264998 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6269 127166 : tyinfo[i].isArray = true;
6270 : else
6271 137832 : tyinfo[i].isArray = false;
6272 :
6273 264998 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6274 :
6275 264998 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6276 2460 : tyinfo[i].isMultirange = true;
6277 : else
6278 262538 : tyinfo[i].isMultirange = false;
6279 :
6280 : /* Decide whether we want to dump it */
6281 264998 : selectDumpableType(&tyinfo[i], fout);
6282 :
6283 : /* Mark whether type has an ACL */
6284 264998 : if (!PQgetisnull(res, i, i_typacl))
6285 434 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6286 :
6287 : /*
6288 : * If it's a domain, fetch info about its constraints, if any
6289 : */
6290 264998 : tyinfo[i].nDomChecks = 0;
6291 264998 : tyinfo[i].domChecks = NULL;
6292 264998 : tyinfo[i].notnull = NULL;
6293 264998 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6294 30996 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6295 322 : getDomainConstraints(fout, &(tyinfo[i]));
6296 :
6297 : /*
6298 : * If it's a base type, make a DumpableObject representing a shell
6299 : * definition of the type. We will need to dump that ahead of the I/O
6300 : * functions for the type. Similarly, range types need a shell
6301 : * definition in case they have a canonicalize function.
6302 : *
6303 : * Note: the shell type doesn't have a catId. You might think it
6304 : * should copy the base type's catId, but then it might capture the
6305 : * pg_depend entries for the type, which we don't want.
6306 : */
6307 264998 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6308 30996 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6309 15068 : tyinfo[i].typtype == TYPTYPE_RANGE))
6310 : {
6311 16188 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6312 16188 : stinfo->dobj.objType = DO_SHELL_TYPE;
6313 16188 : stinfo->dobj.catId = nilCatalogId;
6314 16188 : AssignDumpId(&stinfo->dobj);
6315 16188 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6316 16188 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6317 16188 : stinfo->baseType = &(tyinfo[i]);
6318 16188 : tyinfo[i].shellType = stinfo;
6319 :
6320 : /*
6321 : * Initially mark the shell type as not to be dumped. We'll only
6322 : * dump it if the I/O or canonicalize functions need to be dumped;
6323 : * this is taken care of while sorting dependencies.
6324 : */
6325 16188 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6326 : }
6327 : }
6328 :
6329 364 : PQclear(res);
6330 :
6331 364 : destroyPQExpBuffer(query);
6332 364 : }
6333 :
6334 : /*
6335 : * getOperators:
6336 : * get information about all operators in the system catalogs
6337 : */
6338 : void
6339 364 : getOperators(Archive *fout)
6340 : {
6341 : PGresult *res;
6342 : int ntups;
6343 : int i;
6344 364 : PQExpBuffer query = createPQExpBuffer();
6345 : OprInfo *oprinfo;
6346 : int i_tableoid;
6347 : int i_oid;
6348 : int i_oprname;
6349 : int i_oprnamespace;
6350 : int i_oprowner;
6351 : int i_oprkind;
6352 : int i_oprleft;
6353 : int i_oprright;
6354 : int i_oprcode;
6355 :
6356 : /*
6357 : * find all operators, including builtin operators; we filter out
6358 : * system-defined operators at dump-out time.
6359 : */
6360 :
6361 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6362 : "oprnamespace, "
6363 : "oprowner, "
6364 : "oprkind, "
6365 : "oprleft, "
6366 : "oprright, "
6367 : "oprcode::oid AS oprcode "
6368 : "FROM pg_operator");
6369 :
6370 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6371 :
6372 364 : ntups = PQntuples(res);
6373 :
6374 364 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6375 :
6376 364 : i_tableoid = PQfnumber(res, "tableoid");
6377 364 : i_oid = PQfnumber(res, "oid");
6378 364 : i_oprname = PQfnumber(res, "oprname");
6379 364 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6380 364 : i_oprowner = PQfnumber(res, "oprowner");
6381 364 : i_oprkind = PQfnumber(res, "oprkind");
6382 364 : i_oprleft = PQfnumber(res, "oprleft");
6383 364 : i_oprright = PQfnumber(res, "oprright");
6384 364 : i_oprcode = PQfnumber(res, "oprcode");
6385 :
6386 291490 : for (i = 0; i < ntups; i++)
6387 : {
6388 291126 : oprinfo[i].dobj.objType = DO_OPERATOR;
6389 291126 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6390 291126 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6391 291126 : AssignDumpId(&oprinfo[i].dobj);
6392 291126 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6393 582252 : oprinfo[i].dobj.namespace =
6394 291126 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6395 291126 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6396 291126 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6397 291126 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6398 291126 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6399 291126 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6400 :
6401 : /* Decide whether we want to dump it */
6402 291126 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6403 : }
6404 :
6405 364 : PQclear(res);
6406 :
6407 364 : destroyPQExpBuffer(query);
6408 364 : }
6409 :
6410 : /*
6411 : * getCollations:
6412 : * get information about all collations in the system catalogs
6413 : */
6414 : void
6415 364 : getCollations(Archive *fout)
6416 : {
6417 : PGresult *res;
6418 : int ntups;
6419 : int i;
6420 : PQExpBuffer query;
6421 : CollInfo *collinfo;
6422 : int i_tableoid;
6423 : int i_oid;
6424 : int i_collname;
6425 : int i_collnamespace;
6426 : int i_collowner;
6427 : int i_collencoding;
6428 :
6429 364 : query = createPQExpBuffer();
6430 :
6431 : /*
6432 : * find all collations, including builtin collations; we filter out
6433 : * system-defined collations at dump-out time.
6434 : */
6435 :
6436 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6437 : "collnamespace, "
6438 : "collowner, "
6439 : "collencoding "
6440 : "FROM pg_collation");
6441 :
6442 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6443 :
6444 364 : ntups = PQntuples(res);
6445 :
6446 364 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6447 :
6448 364 : i_tableoid = PQfnumber(res, "tableoid");
6449 364 : i_oid = PQfnumber(res, "oid");
6450 364 : i_collname = PQfnumber(res, "collname");
6451 364 : i_collnamespace = PQfnumber(res, "collnamespace");
6452 364 : i_collowner = PQfnumber(res, "collowner");
6453 364 : i_collencoding = PQfnumber(res, "collencoding");
6454 :
6455 297622 : for (i = 0; i < ntups; i++)
6456 : {
6457 297258 : collinfo[i].dobj.objType = DO_COLLATION;
6458 297258 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6459 297258 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6460 297258 : AssignDumpId(&collinfo[i].dobj);
6461 297258 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6462 594516 : collinfo[i].dobj.namespace =
6463 297258 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6464 297258 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6465 297258 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6466 :
6467 : /* Decide whether we want to dump it */
6468 297258 : selectDumpableObject(&(collinfo[i].dobj), fout);
6469 : }
6470 :
6471 364 : PQclear(res);
6472 :
6473 364 : destroyPQExpBuffer(query);
6474 364 : }
6475 :
6476 : /*
6477 : * getConversions:
6478 : * get information about all conversions in the system catalogs
6479 : */
6480 : void
6481 364 : getConversions(Archive *fout)
6482 : {
6483 : PGresult *res;
6484 : int ntups;
6485 : int i;
6486 : PQExpBuffer query;
6487 : ConvInfo *convinfo;
6488 : int i_tableoid;
6489 : int i_oid;
6490 : int i_conname;
6491 : int i_connamespace;
6492 : int i_conowner;
6493 :
6494 364 : query = createPQExpBuffer();
6495 :
6496 : /*
6497 : * find all conversions, including builtin conversions; we filter out
6498 : * system-defined conversions at dump-out time.
6499 : */
6500 :
6501 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6502 : "connamespace, "
6503 : "conowner "
6504 : "FROM pg_conversion");
6505 :
6506 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6507 :
6508 364 : ntups = PQntuples(res);
6509 :
6510 364 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6511 :
6512 364 : i_tableoid = PQfnumber(res, "tableoid");
6513 364 : i_oid = PQfnumber(res, "oid");
6514 364 : i_conname = PQfnumber(res, "conname");
6515 364 : i_connamespace = PQfnumber(res, "connamespace");
6516 364 : i_conowner = PQfnumber(res, "conowner");
6517 :
6518 47052 : for (i = 0; i < ntups; i++)
6519 : {
6520 46688 : convinfo[i].dobj.objType = DO_CONVERSION;
6521 46688 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6522 46688 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6523 46688 : AssignDumpId(&convinfo[i].dobj);
6524 46688 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6525 93376 : convinfo[i].dobj.namespace =
6526 46688 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6527 46688 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6528 :
6529 : /* Decide whether we want to dump it */
6530 46688 : selectDumpableObject(&(convinfo[i].dobj), fout);
6531 : }
6532 :
6533 364 : PQclear(res);
6534 :
6535 364 : destroyPQExpBuffer(query);
6536 364 : }
6537 :
6538 : /*
6539 : * getAccessMethods:
6540 : * get information about all user-defined access methods
6541 : */
6542 : void
6543 364 : getAccessMethods(Archive *fout)
6544 : {
6545 : PGresult *res;
6546 : int ntups;
6547 : int i;
6548 : PQExpBuffer query;
6549 : AccessMethodInfo *aminfo;
6550 : int i_tableoid;
6551 : int i_oid;
6552 : int i_amname;
6553 : int i_amhandler;
6554 : int i_amtype;
6555 :
6556 364 : query = createPQExpBuffer();
6557 :
6558 : /*
6559 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6560 : * ACCESS METHOD, so earlier versions usually have only built-in access
6561 : * methods. v9.6 also changed the access method API, replacing dozens of
6562 : * pg_am columns with amhandler. Even if a user created an access method
6563 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6564 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6565 : * pg_am just to facilitate findAccessMethodByOid() providing the
6566 : * OID-to-name mapping.
6567 : */
6568 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6569 364 : if (fout->remoteVersion >= 90600)
6570 364 : appendPQExpBufferStr(query,
6571 : "amtype, "
6572 : "amhandler::pg_catalog.regproc AS amhandler ");
6573 : else
6574 0 : appendPQExpBufferStr(query,
6575 : "'i'::pg_catalog.\"char\" AS amtype, "
6576 : "'-'::pg_catalog.regproc AS amhandler ");
6577 364 : appendPQExpBufferStr(query, "FROM pg_am");
6578 :
6579 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6580 :
6581 364 : ntups = PQntuples(res);
6582 :
6583 364 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6584 :
6585 364 : i_tableoid = PQfnumber(res, "tableoid");
6586 364 : i_oid = PQfnumber(res, "oid");
6587 364 : i_amname = PQfnumber(res, "amname");
6588 364 : i_amhandler = PQfnumber(res, "amhandler");
6589 364 : i_amtype = PQfnumber(res, "amtype");
6590 :
6591 3168 : for (i = 0; i < ntups; i++)
6592 : {
6593 2804 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6594 2804 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6595 2804 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6596 2804 : AssignDumpId(&aminfo[i].dobj);
6597 2804 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6598 2804 : aminfo[i].dobj.namespace = NULL;
6599 2804 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6600 2804 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6601 :
6602 : /* Decide whether we want to dump it */
6603 2804 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6604 : }
6605 :
6606 364 : PQclear(res);
6607 :
6608 364 : destroyPQExpBuffer(query);
6609 364 : }
6610 :
6611 :
6612 : /*
6613 : * getOpclasses:
6614 : * get information about all opclasses in the system catalogs
6615 : */
6616 : void
6617 364 : getOpclasses(Archive *fout)
6618 : {
6619 : PGresult *res;
6620 : int ntups;
6621 : int i;
6622 364 : PQExpBuffer query = createPQExpBuffer();
6623 : OpclassInfo *opcinfo;
6624 : int i_tableoid;
6625 : int i_oid;
6626 : int i_opcmethod;
6627 : int i_opcname;
6628 : int i_opcnamespace;
6629 : int i_opcowner;
6630 :
6631 : /*
6632 : * find all opclasses, including builtin opclasses; we filter out
6633 : * system-defined opclasses at dump-out time.
6634 : */
6635 :
6636 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6637 : "opcnamespace, "
6638 : "opcowner "
6639 : "FROM pg_opclass");
6640 :
6641 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6642 :
6643 364 : ntups = PQntuples(res);
6644 :
6645 364 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6646 :
6647 364 : i_tableoid = PQfnumber(res, "tableoid");
6648 364 : i_oid = PQfnumber(res, "oid");
6649 364 : i_opcmethod = PQfnumber(res, "opcmethod");
6650 364 : i_opcname = PQfnumber(res, "opcname");
6651 364 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6652 364 : i_opcowner = PQfnumber(res, "opcowner");
6653 :
6654 65122 : for (i = 0; i < ntups; i++)
6655 : {
6656 64758 : opcinfo[i].dobj.objType = DO_OPCLASS;
6657 64758 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6658 64758 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6659 64758 : AssignDumpId(&opcinfo[i].dobj);
6660 64758 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6661 129516 : opcinfo[i].dobj.namespace =
6662 64758 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6663 64758 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6664 64758 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6665 :
6666 : /* Decide whether we want to dump it */
6667 64758 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6668 : }
6669 :
6670 364 : PQclear(res);
6671 :
6672 364 : destroyPQExpBuffer(query);
6673 364 : }
6674 :
6675 : /*
6676 : * getOpfamilies:
6677 : * get information about all opfamilies in the system catalogs
6678 : */
6679 : void
6680 364 : getOpfamilies(Archive *fout)
6681 : {
6682 : PGresult *res;
6683 : int ntups;
6684 : int i;
6685 : PQExpBuffer query;
6686 : OpfamilyInfo *opfinfo;
6687 : int i_tableoid;
6688 : int i_oid;
6689 : int i_opfmethod;
6690 : int i_opfname;
6691 : int i_opfnamespace;
6692 : int i_opfowner;
6693 :
6694 364 : query = createPQExpBuffer();
6695 :
6696 : /*
6697 : * find all opfamilies, including builtin opfamilies; we filter out
6698 : * system-defined opfamilies at dump-out time.
6699 : */
6700 :
6701 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6702 : "opfnamespace, "
6703 : "opfowner "
6704 : "FROM pg_opfamily");
6705 :
6706 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6707 :
6708 364 : ntups = PQntuples(res);
6709 :
6710 364 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6711 :
6712 364 : i_tableoid = PQfnumber(res, "tableoid");
6713 364 : i_oid = PQfnumber(res, "oid");
6714 364 : i_opfname = PQfnumber(res, "opfname");
6715 364 : i_opfmethod = PQfnumber(res, "opfmethod");
6716 364 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6717 364 : i_opfowner = PQfnumber(res, "opfowner");
6718 :
6719 53798 : for (i = 0; i < ntups; i++)
6720 : {
6721 53434 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6722 53434 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6723 53434 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6724 53434 : AssignDumpId(&opfinfo[i].dobj);
6725 53434 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6726 106868 : opfinfo[i].dobj.namespace =
6727 53434 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6728 53434 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6729 53434 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6730 :
6731 : /* Decide whether we want to dump it */
6732 53434 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6733 : }
6734 :
6735 364 : PQclear(res);
6736 :
6737 364 : destroyPQExpBuffer(query);
6738 364 : }
6739 :
6740 : /*
6741 : * getAggregates:
6742 : * get information about all user-defined aggregates in the system catalogs
6743 : */
6744 : void
6745 364 : getAggregates(Archive *fout)
6746 : {
6747 364 : DumpOptions *dopt = fout->dopt;
6748 : PGresult *res;
6749 : int ntups;
6750 : int i;
6751 364 : PQExpBuffer query = createPQExpBuffer();
6752 : AggInfo *agginfo;
6753 : int i_tableoid;
6754 : int i_oid;
6755 : int i_aggname;
6756 : int i_aggnamespace;
6757 : int i_pronargs;
6758 : int i_proargtypes;
6759 : int i_proowner;
6760 : int i_aggacl;
6761 : int i_acldefault;
6762 :
6763 : /*
6764 : * Find all interesting aggregates. See comment in getFuncs() for the
6765 : * rationale behind the filtering logic.
6766 : */
6767 364 : if (fout->remoteVersion >= 90600)
6768 : {
6769 : const char *agg_check;
6770 :
6771 728 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6772 364 : : "p.proisagg");
6773 :
6774 364 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6775 : "p.proname AS aggname, "
6776 : "p.pronamespace AS aggnamespace, "
6777 : "p.pronargs, p.proargtypes, "
6778 : "p.proowner, "
6779 : "p.proacl AS aggacl, "
6780 : "acldefault('f', p.proowner) AS acldefault "
6781 : "FROM pg_proc p "
6782 : "LEFT JOIN pg_init_privs pip ON "
6783 : "(p.oid = pip.objoid "
6784 : "AND pip.classoid = 'pg_proc'::regclass "
6785 : "AND pip.objsubid = 0) "
6786 : "WHERE %s AND ("
6787 : "p.pronamespace != "
6788 : "(SELECT oid FROM pg_namespace "
6789 : "WHERE nspname = 'pg_catalog') OR "
6790 : "p.proacl IS DISTINCT FROM pip.initprivs",
6791 : agg_check);
6792 364 : if (dopt->binary_upgrade)
6793 72 : appendPQExpBufferStr(query,
6794 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6795 : "classid = 'pg_proc'::regclass AND "
6796 : "objid = p.oid AND "
6797 : "refclassid = 'pg_extension'::regclass AND "
6798 : "deptype = 'e')");
6799 364 : appendPQExpBufferChar(query, ')');
6800 : }
6801 : else
6802 : {
6803 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6804 : "pronamespace AS aggnamespace, "
6805 : "pronargs, proargtypes, "
6806 : "proowner, "
6807 : "proacl AS aggacl, "
6808 : "acldefault('f', proowner) AS acldefault "
6809 : "FROM pg_proc p "
6810 : "WHERE proisagg AND ("
6811 : "pronamespace != "
6812 : "(SELECT oid FROM pg_namespace "
6813 : "WHERE nspname = 'pg_catalog')");
6814 0 : if (dopt->binary_upgrade)
6815 0 : appendPQExpBufferStr(query,
6816 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6817 : "classid = 'pg_proc'::regclass AND "
6818 : "objid = p.oid AND "
6819 : "refclassid = 'pg_extension'::regclass AND "
6820 : "deptype = 'e')");
6821 0 : appendPQExpBufferChar(query, ')');
6822 : }
6823 :
6824 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6825 :
6826 364 : ntups = PQntuples(res);
6827 :
6828 364 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6829 :
6830 364 : i_tableoid = PQfnumber(res, "tableoid");
6831 364 : i_oid = PQfnumber(res, "oid");
6832 364 : i_aggname = PQfnumber(res, "aggname");
6833 364 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6834 364 : i_pronargs = PQfnumber(res, "pronargs");
6835 364 : i_proargtypes = PQfnumber(res, "proargtypes");
6836 364 : i_proowner = PQfnumber(res, "proowner");
6837 364 : i_aggacl = PQfnumber(res, "aggacl");
6838 364 : i_acldefault = PQfnumber(res, "acldefault");
6839 :
6840 1168 : for (i = 0; i < ntups; i++)
6841 : {
6842 804 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6843 804 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6844 804 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6845 804 : AssignDumpId(&agginfo[i].aggfn.dobj);
6846 804 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6847 1608 : agginfo[i].aggfn.dobj.namespace =
6848 804 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6849 804 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6850 804 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6851 804 : agginfo[i].aggfn.dacl.privtype = 0;
6852 804 : agginfo[i].aggfn.dacl.initprivs = NULL;
6853 804 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6854 804 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6855 804 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6856 804 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6857 804 : if (agginfo[i].aggfn.nargs == 0)
6858 112 : agginfo[i].aggfn.argtypes = NULL;
6859 : else
6860 : {
6861 692 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6862 692 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6863 692 : agginfo[i].aggfn.argtypes,
6864 692 : agginfo[i].aggfn.nargs);
6865 : }
6866 804 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6867 :
6868 : /* Decide whether we want to dump it */
6869 804 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6870 :
6871 : /* Mark whether aggregate has an ACL */
6872 804 : if (!PQgetisnull(res, i, i_aggacl))
6873 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6874 : }
6875 :
6876 364 : PQclear(res);
6877 :
6878 364 : destroyPQExpBuffer(query);
6879 364 : }
6880 :
6881 : /*
6882 : * getFuncs:
6883 : * get information about all user-defined functions in the system catalogs
6884 : */
6885 : void
6886 364 : getFuncs(Archive *fout)
6887 : {
6888 364 : DumpOptions *dopt = fout->dopt;
6889 : PGresult *res;
6890 : int ntups;
6891 : int i;
6892 364 : PQExpBuffer query = createPQExpBuffer();
6893 : FuncInfo *finfo;
6894 : int i_tableoid;
6895 : int i_oid;
6896 : int i_proname;
6897 : int i_pronamespace;
6898 : int i_proowner;
6899 : int i_prolang;
6900 : int i_pronargs;
6901 : int i_proargtypes;
6902 : int i_prorettype;
6903 : int i_proacl;
6904 : int i_acldefault;
6905 :
6906 : /*
6907 : * Find all interesting functions. This is a bit complicated:
6908 : *
6909 : * 1. Always exclude aggregates; those are handled elsewhere.
6910 : *
6911 : * 2. Always exclude functions that are internally dependent on something
6912 : * else, since presumably those will be created as a result of creating
6913 : * the something else. This currently acts only to suppress constructor
6914 : * functions for range types. Note this is OK only because the
6915 : * constructors don't have any dependencies the range type doesn't have;
6916 : * otherwise we might not get creation ordering correct.
6917 : *
6918 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6919 : * they're members of extensions and we are in binary-upgrade mode then
6920 : * include them, since we want to dump extension members individually in
6921 : * that mode. Also, if they are used by casts or transforms then we need
6922 : * to gather the information about them, though they won't be dumped if
6923 : * they are built-in. Also, in 9.6 and up, include functions in
6924 : * pg_catalog if they have an ACL different from what's shown in
6925 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6926 : */
6927 364 : if (fout->remoteVersion >= 90600)
6928 : {
6929 : const char *not_agg_check;
6930 :
6931 728 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6932 364 : : "NOT p.proisagg");
6933 :
6934 364 : appendPQExpBuffer(query,
6935 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6936 : "p.pronargs, p.proargtypes, p.prorettype, "
6937 : "p.proacl, "
6938 : "acldefault('f', p.proowner) AS acldefault, "
6939 : "p.pronamespace, "
6940 : "p.proowner "
6941 : "FROM pg_proc p "
6942 : "LEFT JOIN pg_init_privs pip ON "
6943 : "(p.oid = pip.objoid "
6944 : "AND pip.classoid = 'pg_proc'::regclass "
6945 : "AND pip.objsubid = 0) "
6946 : "WHERE %s"
6947 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6948 : "WHERE classid = 'pg_proc'::regclass AND "
6949 : "objid = p.oid AND deptype = 'i')"
6950 : "\n AND ("
6951 : "\n pronamespace != "
6952 : "(SELECT oid FROM pg_namespace "
6953 : "WHERE nspname = 'pg_catalog')"
6954 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6955 : "\n WHERE pg_cast.oid > %u "
6956 : "\n AND p.oid = pg_cast.castfunc)"
6957 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6958 : "\n WHERE pg_transform.oid > %u AND "
6959 : "\n (p.oid = pg_transform.trffromsql"
6960 : "\n OR p.oid = pg_transform.trftosql))",
6961 : not_agg_check,
6962 : g_last_builtin_oid,
6963 : g_last_builtin_oid);
6964 364 : if (dopt->binary_upgrade)
6965 72 : appendPQExpBufferStr(query,
6966 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6967 : "classid = 'pg_proc'::regclass AND "
6968 : "objid = p.oid AND "
6969 : "refclassid = 'pg_extension'::regclass AND "
6970 : "deptype = 'e')");
6971 364 : appendPQExpBufferStr(query,
6972 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6973 364 : appendPQExpBufferChar(query, ')');
6974 : }
6975 : else
6976 : {
6977 0 : appendPQExpBuffer(query,
6978 : "SELECT tableoid, oid, proname, prolang, "
6979 : "pronargs, proargtypes, prorettype, proacl, "
6980 : "acldefault('f', proowner) AS acldefault, "
6981 : "pronamespace, "
6982 : "proowner "
6983 : "FROM pg_proc p "
6984 : "WHERE NOT proisagg"
6985 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6986 : "WHERE classid = 'pg_proc'::regclass AND "
6987 : "objid = p.oid AND deptype = 'i')"
6988 : "\n AND ("
6989 : "\n pronamespace != "
6990 : "(SELECT oid FROM pg_namespace "
6991 : "WHERE nspname = 'pg_catalog')"
6992 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6993 : "\n WHERE pg_cast.oid > '%u'::oid"
6994 : "\n AND p.oid = pg_cast.castfunc)",
6995 : g_last_builtin_oid);
6996 :
6997 0 : if (fout->remoteVersion >= 90500)
6998 0 : appendPQExpBuffer(query,
6999 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7000 : "\n WHERE pg_transform.oid > '%u'::oid"
7001 : "\n AND (p.oid = pg_transform.trffromsql"
7002 : "\n OR p.oid = pg_transform.trftosql))",
7003 : g_last_builtin_oid);
7004 :
7005 0 : if (dopt->binary_upgrade)
7006 0 : appendPQExpBufferStr(query,
7007 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7008 : "classid = 'pg_proc'::regclass AND "
7009 : "objid = p.oid AND "
7010 : "refclassid = 'pg_extension'::regclass AND "
7011 : "deptype = 'e')");
7012 0 : appendPQExpBufferChar(query, ')');
7013 : }
7014 :
7015 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7016 :
7017 364 : ntups = PQntuples(res);
7018 :
7019 364 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7020 :
7021 364 : i_tableoid = PQfnumber(res, "tableoid");
7022 364 : i_oid = PQfnumber(res, "oid");
7023 364 : i_proname = PQfnumber(res, "proname");
7024 364 : i_pronamespace = PQfnumber(res, "pronamespace");
7025 364 : i_proowner = PQfnumber(res, "proowner");
7026 364 : i_prolang = PQfnumber(res, "prolang");
7027 364 : i_pronargs = PQfnumber(res, "pronargs");
7028 364 : i_proargtypes = PQfnumber(res, "proargtypes");
7029 364 : i_prorettype = PQfnumber(res, "prorettype");
7030 364 : i_proacl = PQfnumber(res, "proacl");
7031 364 : i_acldefault = PQfnumber(res, "acldefault");
7032 :
7033 9850 : for (i = 0; i < ntups; i++)
7034 : {
7035 9486 : finfo[i].dobj.objType = DO_FUNC;
7036 9486 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7037 9486 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7038 9486 : AssignDumpId(&finfo[i].dobj);
7039 9486 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7040 18972 : finfo[i].dobj.namespace =
7041 9486 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7042 9486 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7043 9486 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7044 9486 : finfo[i].dacl.privtype = 0;
7045 9486 : finfo[i].dacl.initprivs = NULL;
7046 9486 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7047 9486 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7048 9486 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7049 9486 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7050 9486 : if (finfo[i].nargs == 0)
7051 2162 : finfo[i].argtypes = NULL;
7052 : else
7053 : {
7054 7324 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7055 7324 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7056 7324 : finfo[i].argtypes, finfo[i].nargs);
7057 : }
7058 9486 : finfo[i].postponed_def = false; /* might get set during sort */
7059 :
7060 : /* Decide whether we want to dump it */
7061 9486 : selectDumpableObject(&(finfo[i].dobj), fout);
7062 :
7063 : /* Mark whether function has an ACL */
7064 9486 : if (!PQgetisnull(res, i, i_proacl))
7065 292 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7066 : }
7067 :
7068 364 : PQclear(res);
7069 :
7070 364 : destroyPQExpBuffer(query);
7071 364 : }
7072 :
7073 : /*
7074 : * getRelationStatistics
7075 : * register the statistics object as a dependent of the relation.
7076 : *
7077 : * reltuples is passed as a string to avoid complexities in converting from/to
7078 : * floating point.
7079 : */
7080 : static RelStatsInfo *
7081 19722 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7082 : char *reltuples, int32 relallvisible,
7083 : int32 relallfrozen, char relkind,
7084 : char **indAttNames, int nindAttNames)
7085 : {
7086 19722 : if (!fout->dopt->dumpStatistics)
7087 12072 : return NULL;
7088 :
7089 7650 : if ((relkind == RELKIND_RELATION) ||
7090 3210 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7091 1956 : (relkind == RELKIND_INDEX) ||
7092 1284 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7093 568 : (relkind == RELKIND_MATVIEW ||
7094 : relkind == RELKIND_FOREIGN_TABLE))
7095 : {
7096 7152 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7097 7152 : DumpableObject *dobj = &info->dobj;
7098 :
7099 7152 : dobj->objType = DO_REL_STATS;
7100 7152 : dobj->catId.tableoid = 0;
7101 7152 : dobj->catId.oid = 0;
7102 7152 : AssignDumpId(dobj);
7103 7152 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7104 7152 : dobj->dependencies[0] = rel->dumpId;
7105 7152 : dobj->nDeps = 1;
7106 7152 : dobj->allocDeps = 1;
7107 7152 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7108 7152 : dobj->name = pg_strdup(rel->name);
7109 7152 : dobj->namespace = rel->namespace;
7110 7152 : info->relpages = relpages;
7111 7152 : info->reltuples = pstrdup(reltuples);
7112 7152 : info->relallvisible = relallvisible;
7113 7152 : info->relallfrozen = relallfrozen;
7114 7152 : info->relkind = relkind;
7115 7152 : info->indAttNames = indAttNames;
7116 7152 : info->nindAttNames = nindAttNames;
7117 :
7118 : /*
7119 : * Ordinarily, stats go in SECTION_DATA for tables and
7120 : * SECTION_POST_DATA for indexes.
7121 : *
7122 : * However, the section may be updated later for materialized view
7123 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7124 : * the stats, so the stats must be restored after the data. Also, the
7125 : * materialized view definition may be postponed to SECTION_POST_DATA
7126 : * (see repairMatViewBoundaryMultiLoop()).
7127 : */
7128 7152 : switch (info->relkind)
7129 : {
7130 5226 : case RELKIND_RELATION:
7131 : case RELKIND_PARTITIONED_TABLE:
7132 : case RELKIND_MATVIEW:
7133 : case RELKIND_FOREIGN_TABLE:
7134 5226 : info->section = SECTION_DATA;
7135 5226 : break;
7136 1926 : case RELKIND_INDEX:
7137 : case RELKIND_PARTITIONED_INDEX:
7138 1926 : info->section = SECTION_POST_DATA;
7139 1926 : break;
7140 0 : default:
7141 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7142 : info->relkind);
7143 : }
7144 :
7145 7152 : return info;
7146 : }
7147 498 : return NULL;
7148 : }
7149 :
7150 : /*
7151 : * getTables
7152 : * read all the tables (no indexes) in the system catalogs,
7153 : * and return them as an array of TableInfo structures
7154 : *
7155 : * *numTables is set to the number of tables read in
7156 : */
7157 : TableInfo *
7158 366 : getTables(Archive *fout, int *numTables)
7159 : {
7160 366 : DumpOptions *dopt = fout->dopt;
7161 : PGresult *res;
7162 : int ntups;
7163 : int i;
7164 366 : PQExpBuffer query = createPQExpBuffer();
7165 : TableInfo *tblinfo;
7166 : int i_reltableoid;
7167 : int i_reloid;
7168 : int i_relname;
7169 : int i_relnamespace;
7170 : int i_relkind;
7171 : int i_reltype;
7172 : int i_relowner;
7173 : int i_relchecks;
7174 : int i_relhasindex;
7175 : int i_relhasrules;
7176 : int i_relpages;
7177 : int i_reltuples;
7178 : int i_relallvisible;
7179 : int i_relallfrozen;
7180 : int i_toastpages;
7181 : int i_owning_tab;
7182 : int i_owning_col;
7183 : int i_reltablespace;
7184 : int i_relhasoids;
7185 : int i_relhastriggers;
7186 : int i_relpersistence;
7187 : int i_relispopulated;
7188 : int i_relreplident;
7189 : int i_relrowsec;
7190 : int i_relforcerowsec;
7191 : int i_relfrozenxid;
7192 : int i_toastfrozenxid;
7193 : int i_toastoid;
7194 : int i_relminmxid;
7195 : int i_toastminmxid;
7196 : int i_reloptions;
7197 : int i_checkoption;
7198 : int i_toastreloptions;
7199 : int i_reloftype;
7200 : int i_foreignserver;
7201 : int i_amname;
7202 : int i_is_identity_sequence;
7203 : int i_relacl;
7204 : int i_acldefault;
7205 : int i_ispartition;
7206 :
7207 : /*
7208 : * Find all the tables and table-like objects.
7209 : *
7210 : * We must fetch all tables in this phase because otherwise we cannot
7211 : * correctly identify inherited columns, owned sequences, etc.
7212 : *
7213 : * We include system catalogs, so that we can work if a user table is
7214 : * defined to inherit from a system catalog (pretty weird, but...)
7215 : *
7216 : * Note: in this phase we should collect only a minimal amount of
7217 : * information about each table, basically just enough to decide if it is
7218 : * interesting. In particular, since we do not yet have lock on any user
7219 : * table, we MUST NOT invoke any server-side data collection functions
7220 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7221 : * wrong answers if any concurrent DDL is happening.
7222 : */
7223 :
7224 366 : appendPQExpBufferStr(query,
7225 : "SELECT c.tableoid, c.oid, c.relname, "
7226 : "c.relnamespace, c.relkind, c.reltype, "
7227 : "c.relowner, "
7228 : "c.relchecks, "
7229 : "c.relhasindex, c.relhasrules, c.relpages, "
7230 : "c.reltuples, c.relallvisible, ");
7231 :
7232 366 : if (fout->remoteVersion >= 180000)
7233 366 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7234 : else
7235 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7236 :
7237 366 : appendPQExpBufferStr(query,
7238 : "c.relhastriggers, c.relpersistence, "
7239 : "c.reloftype, "
7240 : "c.relacl, "
7241 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7242 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7243 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7244 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7245 : "ELSE 0 END AS foreignserver, "
7246 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7247 : "tc.oid AS toid, "
7248 : "tc.relpages AS toastpages, "
7249 : "tc.reloptions AS toast_reloptions, "
7250 : "d.refobjid AS owning_tab, "
7251 : "d.refobjsubid AS owning_col, "
7252 : "tsp.spcname AS reltablespace, ");
7253 :
7254 366 : if (fout->remoteVersion >= 120000)
7255 366 : appendPQExpBufferStr(query,
7256 : "false AS relhasoids, ");
7257 : else
7258 0 : appendPQExpBufferStr(query,
7259 : "c.relhasoids, ");
7260 :
7261 366 : if (fout->remoteVersion >= 90300)
7262 366 : appendPQExpBufferStr(query,
7263 : "c.relispopulated, ");
7264 : else
7265 0 : appendPQExpBufferStr(query,
7266 : "'t' as relispopulated, ");
7267 :
7268 366 : if (fout->remoteVersion >= 90400)
7269 366 : appendPQExpBufferStr(query,
7270 : "c.relreplident, ");
7271 : else
7272 0 : appendPQExpBufferStr(query,
7273 : "'d' AS relreplident, ");
7274 :
7275 366 : if (fout->remoteVersion >= 90500)
7276 366 : appendPQExpBufferStr(query,
7277 : "c.relrowsecurity, c.relforcerowsecurity, ");
7278 : else
7279 0 : appendPQExpBufferStr(query,
7280 : "false AS relrowsecurity, "
7281 : "false AS relforcerowsecurity, ");
7282 :
7283 366 : if (fout->remoteVersion >= 90300)
7284 366 : appendPQExpBufferStr(query,
7285 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7286 : else
7287 0 : appendPQExpBufferStr(query,
7288 : "0 AS relminmxid, 0 AS tminmxid, ");
7289 :
7290 366 : if (fout->remoteVersion >= 90300)
7291 366 : appendPQExpBufferStr(query,
7292 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7293 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7294 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7295 : else
7296 0 : appendPQExpBufferStr(query,
7297 : "c.reloptions, NULL AS checkoption, ");
7298 :
7299 366 : if (fout->remoteVersion >= 90600)
7300 366 : appendPQExpBufferStr(query,
7301 : "am.amname, ");
7302 : else
7303 0 : appendPQExpBufferStr(query,
7304 : "NULL AS amname, ");
7305 :
7306 366 : if (fout->remoteVersion >= 90600)
7307 366 : appendPQExpBufferStr(query,
7308 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7309 : else
7310 0 : appendPQExpBufferStr(query,
7311 : "false AS is_identity_sequence, ");
7312 :
7313 366 : if (fout->remoteVersion >= 100000)
7314 366 : appendPQExpBufferStr(query,
7315 : "c.relispartition AS ispartition ");
7316 : else
7317 0 : appendPQExpBufferStr(query,
7318 : "false AS ispartition ");
7319 :
7320 : /*
7321 : * Left join to pg_depend to pick up dependency info linking sequences to
7322 : * their owning column, if any (note this dependency is AUTO except for
7323 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7324 : * collect the spcname.
7325 : */
7326 366 : appendPQExpBufferStr(query,
7327 : "\nFROM pg_class c\n"
7328 : "LEFT JOIN pg_depend d ON "
7329 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7330 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7331 : "d.objsubid = 0 AND "
7332 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7333 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7334 :
7335 : /*
7336 : * In 9.6 and up, left join to pg_am to pick up the amname.
7337 : */
7338 366 : if (fout->remoteVersion >= 90600)
7339 366 : appendPQExpBufferStr(query,
7340 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7341 :
7342 : /*
7343 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7344 : * that versions 10 and 11 have them, but later versions do not, so
7345 : * emitting them causes the upgrade to fail.
7346 : */
7347 366 : appendPQExpBufferStr(query,
7348 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7349 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7350 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7351 :
7352 : /*
7353 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7354 : * relkinds are possible in older servers, but it's not worth the trouble
7355 : * to emit a version-dependent list.
7356 : *
7357 : * Composite-type table entries won't be dumped as such, but we have to
7358 : * make a DumpableObject for them so that we can track dependencies of the
7359 : * composite type (pg_depend entries for columns of the composite type
7360 : * link to the pg_class entry not the pg_type entry).
7361 : */
7362 366 : appendPQExpBufferStr(query,
7363 : "WHERE c.relkind IN ("
7364 : CppAsString2(RELKIND_RELATION) ", "
7365 : CppAsString2(RELKIND_SEQUENCE) ", "
7366 : CppAsString2(RELKIND_VIEW) ", "
7367 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7368 : CppAsString2(RELKIND_MATVIEW) ", "
7369 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7370 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7371 : "ORDER BY c.oid");
7372 :
7373 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7374 :
7375 366 : ntups = PQntuples(res);
7376 :
7377 366 : *numTables = ntups;
7378 :
7379 : /*
7380 : * Extract data from result and lock dumpable tables. We do the locking
7381 : * before anything else, to minimize the window wherein a table could
7382 : * disappear under us.
7383 : *
7384 : * Note that we have to save info about all tables here, even when dumping
7385 : * only one, because we don't yet know which tables might be inheritance
7386 : * ancestors of the target table.
7387 : */
7388 366 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7389 :
7390 366 : i_reltableoid = PQfnumber(res, "tableoid");
7391 366 : i_reloid = PQfnumber(res, "oid");
7392 366 : i_relname = PQfnumber(res, "relname");
7393 366 : i_relnamespace = PQfnumber(res, "relnamespace");
7394 366 : i_relkind = PQfnumber(res, "relkind");
7395 366 : i_reltype = PQfnumber(res, "reltype");
7396 366 : i_relowner = PQfnumber(res, "relowner");
7397 366 : i_relchecks = PQfnumber(res, "relchecks");
7398 366 : i_relhasindex = PQfnumber(res, "relhasindex");
7399 366 : i_relhasrules = PQfnumber(res, "relhasrules");
7400 366 : i_relpages = PQfnumber(res, "relpages");
7401 366 : i_reltuples = PQfnumber(res, "reltuples");
7402 366 : i_relallvisible = PQfnumber(res, "relallvisible");
7403 366 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7404 366 : i_toastpages = PQfnumber(res, "toastpages");
7405 366 : i_owning_tab = PQfnumber(res, "owning_tab");
7406 366 : i_owning_col = PQfnumber(res, "owning_col");
7407 366 : i_reltablespace = PQfnumber(res, "reltablespace");
7408 366 : i_relhasoids = PQfnumber(res, "relhasoids");
7409 366 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7410 366 : i_relpersistence = PQfnumber(res, "relpersistence");
7411 366 : i_relispopulated = PQfnumber(res, "relispopulated");
7412 366 : i_relreplident = PQfnumber(res, "relreplident");
7413 366 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7414 366 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7415 366 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7416 366 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7417 366 : i_toastoid = PQfnumber(res, "toid");
7418 366 : i_relminmxid = PQfnumber(res, "relminmxid");
7419 366 : i_toastminmxid = PQfnumber(res, "tminmxid");
7420 366 : i_reloptions = PQfnumber(res, "reloptions");
7421 366 : i_checkoption = PQfnumber(res, "checkoption");
7422 366 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7423 366 : i_reloftype = PQfnumber(res, "reloftype");
7424 366 : i_foreignserver = PQfnumber(res, "foreignserver");
7425 366 : i_amname = PQfnumber(res, "amname");
7426 366 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7427 366 : i_relacl = PQfnumber(res, "relacl");
7428 366 : i_acldefault = PQfnumber(res, "acldefault");
7429 366 : i_ispartition = PQfnumber(res, "ispartition");
7430 :
7431 366 : if (dopt->lockWaitTimeout)
7432 : {
7433 : /*
7434 : * Arrange to fail instead of waiting forever for a table lock.
7435 : *
7436 : * NB: this coding assumes that the only queries issued within the
7437 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7438 : * applied to other things too.
7439 : */
7440 4 : resetPQExpBuffer(query);
7441 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7442 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7443 4 : ExecuteSqlStatement(fout, query->data);
7444 : }
7445 :
7446 366 : resetPQExpBuffer(query);
7447 :
7448 97364 : for (i = 0; i < ntups; i++)
7449 : {
7450 96998 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7451 96998 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7452 :
7453 96998 : tblinfo[i].dobj.objType = DO_TABLE;
7454 96998 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7455 96998 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7456 96998 : AssignDumpId(&tblinfo[i].dobj);
7457 96998 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7458 193996 : tblinfo[i].dobj.namespace =
7459 96998 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7460 96998 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7461 96998 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7462 96998 : tblinfo[i].dacl.privtype = 0;
7463 96998 : tblinfo[i].dacl.initprivs = NULL;
7464 96998 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7465 96998 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7466 96998 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7467 96998 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7468 96998 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7469 96998 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7470 96998 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7471 96998 : if (PQgetisnull(res, i, i_toastpages))
7472 77690 : tblinfo[i].toastpages = 0;
7473 : else
7474 19308 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7475 96998 : if (PQgetisnull(res, i, i_owning_tab))
7476 : {
7477 96150 : tblinfo[i].owning_tab = InvalidOid;
7478 96150 : tblinfo[i].owning_col = 0;
7479 : }
7480 : else
7481 : {
7482 848 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7483 848 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7484 : }
7485 96998 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7486 96998 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7487 96998 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7488 96998 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7489 96998 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7490 96998 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7491 96998 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7492 96998 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7493 96998 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7494 96998 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7495 96998 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7496 96998 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7497 96998 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7498 96998 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7499 96998 : if (PQgetisnull(res, i, i_checkoption))
7500 96900 : tblinfo[i].checkoption = NULL;
7501 : else
7502 98 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7503 96998 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7504 96998 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7505 96998 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7506 96998 : if (PQgetisnull(res, i, i_amname))
7507 57546 : tblinfo[i].amname = NULL;
7508 : else
7509 39452 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7510 96998 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7511 96998 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7512 :
7513 : /* other fields were zeroed above */
7514 :
7515 : /*
7516 : * Decide whether we want to dump this table.
7517 : */
7518 96998 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7519 372 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7520 : else
7521 96626 : selectDumpableTable(&tblinfo[i], fout);
7522 :
7523 : /*
7524 : * Now, consider the table "interesting" if we need to dump its
7525 : * definition, data or its statistics. Later on, we'll skip a lot of
7526 : * data collection for uninteresting tables.
7527 : *
7528 : * Note: the "interesting" flag will also be set by flagInhTables for
7529 : * parents of interesting tables, so that we collect necessary
7530 : * inheritance info even when the parents are not themselves being
7531 : * dumped. This is the main reason why we need an "interesting" flag
7532 : * that's separate from the components-to-dump bitmask.
7533 : */
7534 96998 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7535 : (DUMP_COMPONENT_DEFINITION |
7536 : DUMP_COMPONENT_DATA |
7537 96998 : DUMP_COMPONENT_STATISTICS)) != 0;
7538 :
7539 96998 : tblinfo[i].dummy_view = false; /* might get set during sort */
7540 96998 : tblinfo[i].postponed_def = false; /* might get set during sort */
7541 :
7542 : /* Tables have data */
7543 96998 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7544 :
7545 : /* Mark whether table has an ACL */
7546 96998 : if (!PQgetisnull(res, i, i_relacl))
7547 76934 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7548 96998 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7549 :
7550 : /* Add statistics */
7551 96998 : if (tblinfo[i].interesting)
7552 : {
7553 : RelStatsInfo *stats;
7554 :
7555 28884 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7556 14442 : tblinfo[i].relpages,
7557 : PQgetvalue(res, i, i_reltuples),
7558 : relallvisible, relallfrozen,
7559 14442 : tblinfo[i].relkind, NULL, 0);
7560 14442 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7561 920 : tblinfo[i].stats = stats;
7562 : }
7563 :
7564 : /*
7565 : * Read-lock target tables to make sure they aren't DROPPED or altered
7566 : * in schema before we get around to dumping them.
7567 : *
7568 : * Note that we don't explicitly lock parents of the target tables; we
7569 : * assume our lock on the child is enough to prevent schema
7570 : * alterations to parent tables.
7571 : *
7572 : * NOTE: it'd be kinda nice to lock other relations too, not only
7573 : * plain or partitioned tables, but the backend doesn't presently
7574 : * allow that.
7575 : *
7576 : * We only need to lock the table for certain components; see
7577 : * pg_dump.h
7578 : */
7579 96998 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7580 14442 : (tblinfo[i].relkind == RELKIND_RELATION ||
7581 4110 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7582 : {
7583 : /*
7584 : * Tables are locked in batches. When dumping from a remote
7585 : * server this can save a significant amount of time by reducing
7586 : * the number of round trips.
7587 : */
7588 11544 : if (query->len == 0)
7589 238 : appendPQExpBuffer(query, "LOCK TABLE %s",
7590 238 : fmtQualifiedDumpable(&tblinfo[i]));
7591 : else
7592 : {
7593 11306 : appendPQExpBuffer(query, ", %s",
7594 11306 : fmtQualifiedDumpable(&tblinfo[i]));
7595 :
7596 : /* Arbitrarily end a batch when query length reaches 100K. */
7597 11306 : if (query->len >= 100000)
7598 : {
7599 : /* Lock another batch of tables. */
7600 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7601 0 : ExecuteSqlStatement(fout, query->data);
7602 0 : resetPQExpBuffer(query);
7603 : }
7604 : }
7605 : }
7606 : }
7607 :
7608 366 : if (query->len != 0)
7609 : {
7610 : /* Lock the tables in the last batch. */
7611 238 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7612 238 : ExecuteSqlStatement(fout, query->data);
7613 : }
7614 :
7615 364 : if (dopt->lockWaitTimeout)
7616 : {
7617 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7618 : }
7619 :
7620 364 : PQclear(res);
7621 :
7622 364 : destroyPQExpBuffer(query);
7623 :
7624 364 : return tblinfo;
7625 : }
7626 :
7627 : /*
7628 : * getOwnedSeqs
7629 : * identify owned sequences and mark them as dumpable if owning table is
7630 : *
7631 : * We used to do this in getTables(), but it's better to do it after the
7632 : * index used by findTableByOid() has been set up.
7633 : */
7634 : void
7635 364 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7636 : {
7637 : int i;
7638 :
7639 : /*
7640 : * Force sequences that are "owned" by table columns to be dumped whenever
7641 : * their owning table is being dumped.
7642 : */
7643 96818 : for (i = 0; i < numTables; i++)
7644 : {
7645 96454 : TableInfo *seqinfo = &tblinfo[i];
7646 : TableInfo *owning_tab;
7647 :
7648 96454 : if (!OidIsValid(seqinfo->owning_tab))
7649 95612 : continue; /* not an owned sequence */
7650 :
7651 842 : owning_tab = findTableByOid(seqinfo->owning_tab);
7652 842 : if (owning_tab == NULL)
7653 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7654 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7655 :
7656 : /*
7657 : * For an identity sequence, dump exactly the same components for the
7658 : * sequence as for the owning table. This is important because we
7659 : * treat the identity sequence as an integral part of the table. For
7660 : * example, there is not any DDL command that allows creation of such
7661 : * a sequence independently of the table.
7662 : *
7663 : * For other owned sequences such as serial sequences, we need to dump
7664 : * the components that are being dumped for the table and any
7665 : * components that the sequence is explicitly marked with.
7666 : *
7667 : * We can't simply use the set of components which are being dumped
7668 : * for the table as the table might be in an extension (and only the
7669 : * non-extension components, eg: ACLs if changed, security labels, and
7670 : * policies, are being dumped) while the sequence is not (and
7671 : * therefore the definition and other components should also be
7672 : * dumped).
7673 : *
7674 : * If the sequence is part of the extension then it should be properly
7675 : * marked by checkExtensionMembership() and this will be a no-op as
7676 : * the table will be equivalently marked.
7677 : */
7678 842 : if (seqinfo->is_identity_sequence)
7679 404 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7680 : else
7681 438 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7682 :
7683 : /* Make sure that necessary data is available if we're dumping it */
7684 842 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7685 : {
7686 650 : seqinfo->interesting = true;
7687 650 : owning_tab->interesting = true;
7688 : }
7689 : }
7690 364 : }
7691 :
7692 : /*
7693 : * getInherits
7694 : * read all the inheritance information
7695 : * from the system catalogs return them in the InhInfo* structure
7696 : *
7697 : * numInherits is set to the number of pairs read in
7698 : */
7699 : InhInfo *
7700 364 : getInherits(Archive *fout, int *numInherits)
7701 : {
7702 : PGresult *res;
7703 : int ntups;
7704 : int i;
7705 364 : PQExpBuffer query = createPQExpBuffer();
7706 : InhInfo *inhinfo;
7707 :
7708 : int i_inhrelid;
7709 : int i_inhparent;
7710 :
7711 : /* find all the inheritance information */
7712 364 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7713 :
7714 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7715 :
7716 364 : ntups = PQntuples(res);
7717 :
7718 364 : *numInherits = ntups;
7719 :
7720 364 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7721 :
7722 364 : i_inhrelid = PQfnumber(res, "inhrelid");
7723 364 : i_inhparent = PQfnumber(res, "inhparent");
7724 :
7725 7230 : for (i = 0; i < ntups; i++)
7726 : {
7727 6866 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7728 6866 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7729 : }
7730 :
7731 364 : PQclear(res);
7732 :
7733 364 : destroyPQExpBuffer(query);
7734 :
7735 364 : return inhinfo;
7736 : }
7737 :
7738 : /*
7739 : * getPartitioningInfo
7740 : * get information about partitioning
7741 : *
7742 : * For the most part, we only collect partitioning info about tables we
7743 : * intend to dump. However, this function has to consider all partitioned
7744 : * tables in the database, because we need to know about parents of partitions
7745 : * we are going to dump even if the parents themselves won't be dumped.
7746 : *
7747 : * Specifically, what we need to know is whether each partitioned table
7748 : * has an "unsafe" partitioning scheme that requires us to force
7749 : * load-via-partition-root mode for its children. Currently the only case
7750 : * for which we force that is hash partitioning on enum columns, since the
7751 : * hash codes depend on enum value OIDs which won't be replicated across
7752 : * dump-and-reload. There are other cases in which load-via-partition-root
7753 : * might be necessary, but we expect users to cope with them.
7754 : */
7755 : void
7756 364 : getPartitioningInfo(Archive *fout)
7757 : {
7758 : PQExpBuffer query;
7759 : PGresult *res;
7760 : int ntups;
7761 :
7762 : /* hash partitioning didn't exist before v11 */
7763 364 : if (fout->remoteVersion < 110000)
7764 0 : return;
7765 : /* needn't bother if not dumping data */
7766 364 : if (!fout->dopt->dumpData)
7767 80 : return;
7768 :
7769 284 : query = createPQExpBuffer();
7770 :
7771 : /*
7772 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7773 : * appears among the partition opclasses. We needn't check partstrat.
7774 : *
7775 : * Note that this query may well retrieve info about tables we aren't
7776 : * going to dump and hence have no lock on. That's okay since we need not
7777 : * invoke any unsafe server-side functions.
7778 : */
7779 284 : appendPQExpBufferStr(query,
7780 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7781 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7782 : "ON c.opcmethod = a.oid\n"
7783 : "WHERE opcname = 'enum_ops' "
7784 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7785 : "AND amname = 'hash') = ANY(partclass)");
7786 :
7787 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7788 :
7789 284 : ntups = PQntuples(res);
7790 :
7791 376 : for (int i = 0; i < ntups; i++)
7792 : {
7793 92 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7794 : TableInfo *tbinfo;
7795 :
7796 92 : tbinfo = findTableByOid(tabrelid);
7797 92 : if (tbinfo == NULL)
7798 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7799 : tabrelid);
7800 92 : tbinfo->unsafe_partitions = true;
7801 : }
7802 :
7803 284 : PQclear(res);
7804 :
7805 284 : destroyPQExpBuffer(query);
7806 : }
7807 :
7808 : /*
7809 : * getIndexes
7810 : * get information about every index on a dumpable table
7811 : *
7812 : * Note: index data is not returned directly to the caller, but it
7813 : * does get entered into the DumpableObject tables.
7814 : */
7815 : void
7816 364 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7817 : {
7818 364 : PQExpBuffer query = createPQExpBuffer();
7819 364 : PQExpBuffer tbloids = createPQExpBuffer();
7820 : PGresult *res;
7821 : int ntups;
7822 : int curtblindx;
7823 : IndxInfo *indxinfo;
7824 : int i_tableoid,
7825 : i_oid,
7826 : i_indrelid,
7827 : i_indexname,
7828 : i_relpages,
7829 : i_reltuples,
7830 : i_relallvisible,
7831 : i_relallfrozen,
7832 : i_parentidx,
7833 : i_indexdef,
7834 : i_indnkeyatts,
7835 : i_indnatts,
7836 : i_indkey,
7837 : i_indisclustered,
7838 : i_indisreplident,
7839 : i_indnullsnotdistinct,
7840 : i_contype,
7841 : i_conname,
7842 : i_condeferrable,
7843 : i_condeferred,
7844 : i_conperiod,
7845 : i_contableoid,
7846 : i_conoid,
7847 : i_condef,
7848 : i_indattnames,
7849 : i_tablespace,
7850 : i_indreloptions,
7851 : i_indstatcols,
7852 : i_indstatvals;
7853 :
7854 : /*
7855 : * We want to perform just one query against pg_index. However, we
7856 : * mustn't try to select every row of the catalog and then sort it out on
7857 : * the client side, because some of the server-side functions we need
7858 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7859 : * build an array of the OIDs of tables we care about (and now have lock
7860 : * on!), and use a WHERE clause to constrain which rows are selected.
7861 : */
7862 364 : appendPQExpBufferChar(tbloids, '{');
7863 96818 : for (int i = 0; i < numTables; i++)
7864 : {
7865 96454 : TableInfo *tbinfo = &tblinfo[i];
7866 :
7867 96454 : if (!tbinfo->hasindex)
7868 68334 : continue;
7869 :
7870 : /*
7871 : * We can ignore indexes of uninteresting tables.
7872 : */
7873 28120 : if (!tbinfo->interesting)
7874 24062 : continue;
7875 :
7876 : /* OK, we need info for this table */
7877 4058 : if (tbloids->len > 1) /* do we have more than the '{'? */
7878 3896 : appendPQExpBufferChar(tbloids, ',');
7879 4058 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7880 : }
7881 364 : appendPQExpBufferChar(tbloids, '}');
7882 :
7883 364 : appendPQExpBufferStr(query,
7884 : "SELECT t.tableoid, t.oid, i.indrelid, "
7885 : "t.relname AS indexname, "
7886 : "t.relpages, t.reltuples, t.relallvisible, ");
7887 :
7888 364 : if (fout->remoteVersion >= 180000)
7889 364 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7890 : else
7891 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7892 :
7893 364 : appendPQExpBufferStr(query,
7894 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7895 : "i.indkey, i.indisclustered, "
7896 : "c.contype, c.conname, "
7897 : "c.condeferrable, c.condeferred, "
7898 : "c.tableoid AS contableoid, "
7899 : "c.oid AS conoid, "
7900 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7901 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7902 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7903 : " FROM pg_catalog.pg_attribute "
7904 : " WHERE attrelid = i.indexrelid) "
7905 : "ELSE NULL END AS indattnames, "
7906 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7907 : "t.reloptions AS indreloptions, ");
7908 :
7909 :
7910 364 : if (fout->remoteVersion >= 90400)
7911 364 : appendPQExpBufferStr(query,
7912 : "i.indisreplident, ");
7913 : else
7914 0 : appendPQExpBufferStr(query,
7915 : "false AS indisreplident, ");
7916 :
7917 364 : if (fout->remoteVersion >= 110000)
7918 364 : appendPQExpBufferStr(query,
7919 : "inh.inhparent AS parentidx, "
7920 : "i.indnkeyatts AS indnkeyatts, "
7921 : "i.indnatts AS indnatts, "
7922 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7923 : " FROM pg_catalog.pg_attribute "
7924 : " WHERE attrelid = i.indexrelid AND "
7925 : " attstattarget >= 0) AS indstatcols, "
7926 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7927 : " FROM pg_catalog.pg_attribute "
7928 : " WHERE attrelid = i.indexrelid AND "
7929 : " attstattarget >= 0) AS indstatvals, ");
7930 : else
7931 0 : appendPQExpBufferStr(query,
7932 : "0 AS parentidx, "
7933 : "i.indnatts AS indnkeyatts, "
7934 : "i.indnatts AS indnatts, "
7935 : "'' AS indstatcols, "
7936 : "'' AS indstatvals, ");
7937 :
7938 364 : if (fout->remoteVersion >= 150000)
7939 364 : appendPQExpBufferStr(query,
7940 : "i.indnullsnotdistinct, ");
7941 : else
7942 0 : appendPQExpBufferStr(query,
7943 : "false AS indnullsnotdistinct, ");
7944 :
7945 364 : if (fout->remoteVersion >= 180000)
7946 364 : appendPQExpBufferStr(query,
7947 : "c.conperiod ");
7948 : else
7949 0 : appendPQExpBufferStr(query,
7950 : "NULL AS conperiod ");
7951 :
7952 : /*
7953 : * The point of the messy-looking outer join is to find a constraint that
7954 : * is related by an internal dependency link to the index. If we find one,
7955 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7956 : * index won't have more than one internal dependency.
7957 : *
7958 : * Note: the check on conrelid is redundant, but useful because that
7959 : * column is indexed while conindid is not.
7960 : */
7961 364 : if (fout->remoteVersion >= 110000)
7962 : {
7963 364 : appendPQExpBuffer(query,
7964 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7965 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7966 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7967 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7968 : "LEFT JOIN pg_catalog.pg_constraint c "
7969 : "ON (i.indrelid = c.conrelid AND "
7970 : "i.indexrelid = c.conindid AND "
7971 : "c.contype IN ('p','u','x')) "
7972 : "LEFT JOIN pg_catalog.pg_inherits inh "
7973 : "ON (inh.inhrelid = indexrelid) "
7974 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7975 : "AND i.indisready "
7976 : "ORDER BY i.indrelid, indexname",
7977 : tbloids->data);
7978 : }
7979 : else
7980 : {
7981 : /*
7982 : * the test on indisready is necessary in 9.2, and harmless in
7983 : * earlier/later versions
7984 : */
7985 0 : appendPQExpBuffer(query,
7986 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7987 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7988 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7989 : "LEFT JOIN pg_catalog.pg_constraint c "
7990 : "ON (i.indrelid = c.conrelid AND "
7991 : "i.indexrelid = c.conindid AND "
7992 : "c.contype IN ('p','u','x')) "
7993 : "WHERE i.indisvalid AND i.indisready "
7994 : "ORDER BY i.indrelid, indexname",
7995 : tbloids->data);
7996 : }
7997 :
7998 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7999 :
8000 364 : ntups = PQntuples(res);
8001 :
8002 364 : i_tableoid = PQfnumber(res, "tableoid");
8003 364 : i_oid = PQfnumber(res, "oid");
8004 364 : i_indrelid = PQfnumber(res, "indrelid");
8005 364 : i_indexname = PQfnumber(res, "indexname");
8006 364 : i_relpages = PQfnumber(res, "relpages");
8007 364 : i_reltuples = PQfnumber(res, "reltuples");
8008 364 : i_relallvisible = PQfnumber(res, "relallvisible");
8009 364 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8010 364 : i_parentidx = PQfnumber(res, "parentidx");
8011 364 : i_indexdef = PQfnumber(res, "indexdef");
8012 364 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8013 364 : i_indnatts = PQfnumber(res, "indnatts");
8014 364 : i_indkey = PQfnumber(res, "indkey");
8015 364 : i_indisclustered = PQfnumber(res, "indisclustered");
8016 364 : i_indisreplident = PQfnumber(res, "indisreplident");
8017 364 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8018 364 : i_contype = PQfnumber(res, "contype");
8019 364 : i_conname = PQfnumber(res, "conname");
8020 364 : i_condeferrable = PQfnumber(res, "condeferrable");
8021 364 : i_condeferred = PQfnumber(res, "condeferred");
8022 364 : i_conperiod = PQfnumber(res, "conperiod");
8023 364 : i_contableoid = PQfnumber(res, "contableoid");
8024 364 : i_conoid = PQfnumber(res, "conoid");
8025 364 : i_condef = PQfnumber(res, "condef");
8026 364 : i_indattnames = PQfnumber(res, "indattnames");
8027 364 : i_tablespace = PQfnumber(res, "tablespace");
8028 364 : i_indreloptions = PQfnumber(res, "indreloptions");
8029 364 : i_indstatcols = PQfnumber(res, "indstatcols");
8030 364 : i_indstatvals = PQfnumber(res, "indstatvals");
8031 :
8032 364 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8033 :
8034 : /*
8035 : * Outer loop iterates once per table, not once per row. Incrementing of
8036 : * j is handled by the inner loop.
8037 : */
8038 364 : curtblindx = -1;
8039 4390 : for (int j = 0; j < ntups;)
8040 : {
8041 4026 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8042 4026 : TableInfo *tbinfo = NULL;
8043 4026 : char **indAttNames = NULL;
8044 4026 : int nindAttNames = 0;
8045 : int numinds;
8046 :
8047 : /* Count rows for this table */
8048 5280 : for (numinds = 1; numinds < ntups - j; numinds++)
8049 5118 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8050 3864 : break;
8051 :
8052 : /*
8053 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8054 : * order.
8055 : */
8056 47904 : while (++curtblindx < numTables)
8057 : {
8058 47904 : tbinfo = &tblinfo[curtblindx];
8059 47904 : if (tbinfo->dobj.catId.oid == indrelid)
8060 4026 : break;
8061 : }
8062 4026 : if (curtblindx >= numTables)
8063 0 : pg_fatal("unrecognized table OID %u", indrelid);
8064 : /* cross-check that we only got requested tables */
8065 4026 : if (!tbinfo->hasindex ||
8066 4026 : !tbinfo->interesting)
8067 0 : pg_fatal("unexpected index data for table \"%s\"",
8068 : tbinfo->dobj.name);
8069 :
8070 : /* Save data for this table */
8071 4026 : tbinfo->indexes = indxinfo + j;
8072 4026 : tbinfo->numIndexes = numinds;
8073 :
8074 9306 : for (int c = 0; c < numinds; c++, j++)
8075 : {
8076 : char contype;
8077 : char indexkind;
8078 : RelStatsInfo *relstats;
8079 5280 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8080 5280 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8081 5280 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8082 :
8083 5280 : indxinfo[j].dobj.objType = DO_INDEX;
8084 5280 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8085 5280 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8086 5280 : AssignDumpId(&indxinfo[j].dobj);
8087 5280 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8088 5280 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8089 5280 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8090 5280 : indxinfo[j].indextable = tbinfo;
8091 5280 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8092 5280 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8093 5280 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8094 5280 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8095 5280 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8096 5280 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8097 5280 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8098 5280 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8099 5280 : parseOidArray(PQgetvalue(res, j, i_indkey),
8100 5280 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8101 5280 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8102 5280 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8103 5280 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8104 5280 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8105 5280 : indxinfo[j].partattaches = (SimplePtrList)
8106 : {
8107 : NULL, NULL
8108 : };
8109 :
8110 5280 : if (indxinfo[j].parentidx == 0)
8111 4104 : indexkind = RELKIND_INDEX;
8112 : else
8113 1176 : indexkind = RELKIND_PARTITIONED_INDEX;
8114 :
8115 5280 : if (!PQgetisnull(res, j, i_indattnames))
8116 : {
8117 304 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8118 : &indAttNames, &nindAttNames))
8119 0 : pg_fatal("could not parse %s array", "indattnames");
8120 : }
8121 :
8122 5280 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8123 : PQgetvalue(res, j, i_reltuples),
8124 : relallvisible, relallfrozen, indexkind,
8125 : indAttNames, nindAttNames);
8126 :
8127 5280 : contype = *(PQgetvalue(res, j, i_contype));
8128 5280 : if (contype == 'p' || contype == 'u' || contype == 'x')
8129 3074 : {
8130 : /*
8131 : * If we found a constraint matching the index, create an
8132 : * entry for it.
8133 : */
8134 : ConstraintInfo *constrinfo;
8135 :
8136 3074 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8137 3074 : constrinfo->dobj.objType = DO_CONSTRAINT;
8138 3074 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8139 3074 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8140 3074 : AssignDumpId(&constrinfo->dobj);
8141 3074 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8142 3074 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8143 3074 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8144 3074 : constrinfo->contable = tbinfo;
8145 3074 : constrinfo->condomain = NULL;
8146 3074 : constrinfo->contype = contype;
8147 3074 : if (contype == 'x')
8148 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8149 : else
8150 3054 : constrinfo->condef = NULL;
8151 3074 : constrinfo->confrelid = InvalidOid;
8152 3074 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8153 3074 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8154 3074 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8155 3074 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8156 3074 : constrinfo->conislocal = true;
8157 3074 : constrinfo->separate = true;
8158 :
8159 3074 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8160 3074 : if (relstats != NULL)
8161 1124 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8162 : }
8163 : else
8164 : {
8165 : /* Plain secondary index */
8166 2206 : indxinfo[j].indexconstraint = 0;
8167 : }
8168 : }
8169 : }
8170 :
8171 364 : PQclear(res);
8172 :
8173 364 : destroyPQExpBuffer(query);
8174 364 : destroyPQExpBuffer(tbloids);
8175 364 : }
8176 :
8177 : /*
8178 : * getExtendedStatistics
8179 : * get information about extended-statistics objects.
8180 : *
8181 : * Note: extended statistics data is not returned directly to the caller, but
8182 : * it does get entered into the DumpableObject tables.
8183 : */
8184 : void
8185 364 : getExtendedStatistics(Archive *fout)
8186 : {
8187 : PQExpBuffer query;
8188 : PGresult *res;
8189 : StatsExtInfo *statsextinfo;
8190 : int ntups;
8191 : int i_tableoid;
8192 : int i_oid;
8193 : int i_stxname;
8194 : int i_stxnamespace;
8195 : int i_stxowner;
8196 : int i_stxrelid;
8197 : int i_stattarget;
8198 : int i;
8199 :
8200 : /* Extended statistics were new in v10 */
8201 364 : if (fout->remoteVersion < 100000)
8202 0 : return;
8203 :
8204 364 : query = createPQExpBuffer();
8205 :
8206 364 : if (fout->remoteVersion < 130000)
8207 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8208 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8209 : "FROM pg_catalog.pg_statistic_ext");
8210 : else
8211 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8212 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8213 : "FROM pg_catalog.pg_statistic_ext");
8214 :
8215 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8216 :
8217 364 : ntups = PQntuples(res);
8218 :
8219 364 : i_tableoid = PQfnumber(res, "tableoid");
8220 364 : i_oid = PQfnumber(res, "oid");
8221 364 : i_stxname = PQfnumber(res, "stxname");
8222 364 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8223 364 : i_stxowner = PQfnumber(res, "stxowner");
8224 364 : i_stxrelid = PQfnumber(res, "stxrelid");
8225 364 : i_stattarget = PQfnumber(res, "stxstattarget");
8226 :
8227 364 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8228 :
8229 708 : for (i = 0; i < ntups; i++)
8230 : {
8231 344 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8232 344 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8233 344 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8234 344 : AssignDumpId(&statsextinfo[i].dobj);
8235 344 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8236 688 : statsextinfo[i].dobj.namespace =
8237 344 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8238 344 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8239 688 : statsextinfo[i].stattable =
8240 344 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8241 344 : if (PQgetisnull(res, i, i_stattarget))
8242 248 : statsextinfo[i].stattarget = -1;
8243 : else
8244 96 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8245 :
8246 : /* Decide whether we want to dump it */
8247 344 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8248 : }
8249 :
8250 364 : PQclear(res);
8251 364 : destroyPQExpBuffer(query);
8252 : }
8253 :
8254 : /*
8255 : * getConstraints
8256 : *
8257 : * Get info about constraints on dumpable tables.
8258 : *
8259 : * Currently handles foreign keys only.
8260 : * Unique and primary key constraints are handled with indexes,
8261 : * while check constraints are processed in getTableAttrs().
8262 : */
8263 : void
8264 364 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8265 : {
8266 364 : PQExpBuffer query = createPQExpBuffer();
8267 364 : PQExpBuffer tbloids = createPQExpBuffer();
8268 : PGresult *res;
8269 : int ntups;
8270 : int curtblindx;
8271 364 : TableInfo *tbinfo = NULL;
8272 : ConstraintInfo *constrinfo;
8273 : int i_contableoid,
8274 : i_conoid,
8275 : i_conrelid,
8276 : i_conname,
8277 : i_confrelid,
8278 : i_conindid,
8279 : i_condef;
8280 :
8281 : /*
8282 : * We want to perform just one query against pg_constraint. However, we
8283 : * mustn't try to select every row of the catalog and then sort it out on
8284 : * the client side, because some of the server-side functions we need
8285 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8286 : * build an array of the OIDs of tables we care about (and now have lock
8287 : * on!), and use a WHERE clause to constrain which rows are selected.
8288 : */
8289 364 : appendPQExpBufferChar(tbloids, '{');
8290 96818 : for (int i = 0; i < numTables; i++)
8291 : {
8292 96454 : TableInfo *tinfo = &tblinfo[i];
8293 :
8294 96454 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8295 82120 : continue;
8296 :
8297 : /* OK, we need info for this table */
8298 14334 : if (tbloids->len > 1) /* do we have more than the '{'? */
8299 14094 : appendPQExpBufferChar(tbloids, ',');
8300 14334 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8301 : }
8302 364 : appendPQExpBufferChar(tbloids, '}');
8303 :
8304 364 : appendPQExpBufferStr(query,
8305 : "SELECT c.tableoid, c.oid, "
8306 : "conrelid, conname, confrelid, ");
8307 364 : if (fout->remoteVersion >= 110000)
8308 364 : appendPQExpBufferStr(query, "conindid, ");
8309 : else
8310 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8311 364 : appendPQExpBuffer(query,
8312 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8313 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8314 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8315 : "WHERE contype = 'f' ",
8316 : tbloids->data);
8317 364 : if (fout->remoteVersion >= 110000)
8318 364 : appendPQExpBufferStr(query,
8319 : "AND conparentid = 0 ");
8320 364 : appendPQExpBufferStr(query,
8321 : "ORDER BY conrelid, conname");
8322 :
8323 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8324 :
8325 364 : ntups = PQntuples(res);
8326 :
8327 364 : i_contableoid = PQfnumber(res, "tableoid");
8328 364 : i_conoid = PQfnumber(res, "oid");
8329 364 : i_conrelid = PQfnumber(res, "conrelid");
8330 364 : i_conname = PQfnumber(res, "conname");
8331 364 : i_confrelid = PQfnumber(res, "confrelid");
8332 364 : i_conindid = PQfnumber(res, "conindid");
8333 364 : i_condef = PQfnumber(res, "condef");
8334 :
8335 364 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8336 :
8337 364 : curtblindx = -1;
8338 718 : for (int j = 0; j < ntups; j++)
8339 : {
8340 354 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8341 : TableInfo *reftable;
8342 :
8343 : /*
8344 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8345 : * order.
8346 : */
8347 354 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8348 : {
8349 28482 : while (++curtblindx < numTables)
8350 : {
8351 28482 : tbinfo = &tblinfo[curtblindx];
8352 28482 : if (tbinfo->dobj.catId.oid == conrelid)
8353 334 : break;
8354 : }
8355 334 : if (curtblindx >= numTables)
8356 0 : pg_fatal("unrecognized table OID %u", conrelid);
8357 : }
8358 :
8359 354 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8360 354 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8361 354 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8362 354 : AssignDumpId(&constrinfo[j].dobj);
8363 354 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8364 354 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8365 354 : constrinfo[j].contable = tbinfo;
8366 354 : constrinfo[j].condomain = NULL;
8367 354 : constrinfo[j].contype = 'f';
8368 354 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8369 354 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8370 354 : constrinfo[j].conindex = 0;
8371 354 : constrinfo[j].condeferrable = false;
8372 354 : constrinfo[j].condeferred = false;
8373 354 : constrinfo[j].conislocal = true;
8374 354 : constrinfo[j].separate = true;
8375 :
8376 : /*
8377 : * Restoring an FK that points to a partitioned table requires that
8378 : * all partition indexes have been attached beforehand. Ensure that
8379 : * happens by making the constraint depend on each index partition
8380 : * attach object.
8381 : */
8382 354 : reftable = findTableByOid(constrinfo[j].confrelid);
8383 354 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8384 : {
8385 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8386 :
8387 40 : if (indexOid != InvalidOid)
8388 : {
8389 40 : for (int k = 0; k < reftable->numIndexes; k++)
8390 : {
8391 : IndxInfo *refidx;
8392 :
8393 : /* not our index? */
8394 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8395 0 : continue;
8396 :
8397 40 : refidx = &reftable->indexes[k];
8398 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8399 40 : break;
8400 : }
8401 : }
8402 : }
8403 : }
8404 :
8405 364 : PQclear(res);
8406 :
8407 364 : destroyPQExpBuffer(query);
8408 364 : destroyPQExpBuffer(tbloids);
8409 364 : }
8410 :
8411 : /*
8412 : * addConstrChildIdxDeps
8413 : *
8414 : * Recursive subroutine for getConstraints
8415 : *
8416 : * Given an object representing a foreign key constraint and an index on the
8417 : * partitioned table it references, mark the constraint object as dependent
8418 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8419 : * drilling down to their partitions if any. This ensures that the FK is not
8420 : * restored until the index is fully marked valid.
8421 : */
8422 : static void
8423 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8424 : {
8425 : SimplePtrListCell *cell;
8426 :
8427 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8428 :
8429 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8430 : {
8431 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8432 :
8433 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8434 :
8435 220 : if (attach->partitionIdx->partattaches.head != NULL)
8436 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8437 : }
8438 90 : }
8439 :
8440 : /*
8441 : * getDomainConstraints
8442 : *
8443 : * Get info about constraints on a domain.
8444 : */
8445 : static void
8446 322 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8447 : {
8448 : ConstraintInfo *constrinfo;
8449 322 : PQExpBuffer query = createPQExpBuffer();
8450 : PGresult *res;
8451 : int i_tableoid,
8452 : i_oid,
8453 : i_conname,
8454 : i_consrc,
8455 : i_convalidated,
8456 : i_contype;
8457 : int ntups;
8458 :
8459 322 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8460 : {
8461 : /*
8462 : * Set up query for constraint-specific details. For servers 17 and
8463 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8464 : * just the latter.
8465 : */
8466 92 : appendPQExpBuffer(query,
8467 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8468 : "SELECT tableoid, oid, conname, "
8469 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8470 : "convalidated, contype "
8471 : "FROM pg_catalog.pg_constraint "
8472 : "WHERE contypid = $1 AND contype IN (%s) "
8473 : "ORDER BY conname",
8474 92 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8475 :
8476 92 : ExecuteSqlStatement(fout, query->data);
8477 :
8478 92 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8479 : }
8480 :
8481 322 : printfPQExpBuffer(query,
8482 : "EXECUTE getDomainConstraints('%u')",
8483 : tyinfo->dobj.catId.oid);
8484 :
8485 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8486 :
8487 322 : ntups = PQntuples(res);
8488 :
8489 322 : i_tableoid = PQfnumber(res, "tableoid");
8490 322 : i_oid = PQfnumber(res, "oid");
8491 322 : i_conname = PQfnumber(res, "conname");
8492 322 : i_consrc = PQfnumber(res, "consrc");
8493 322 : i_convalidated = PQfnumber(res, "convalidated");
8494 322 : i_contype = PQfnumber(res, "contype");
8495 :
8496 322 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8497 322 : tyinfo->domChecks = constrinfo;
8498 :
8499 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8500 666 : for (int i = 0, j = 0; i < ntups; i++)
8501 : {
8502 344 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8503 344 : char contype = (PQgetvalue(res, i, i_contype))[0];
8504 : ConstraintInfo *constraint;
8505 :
8506 344 : if (contype == CONSTRAINT_CHECK)
8507 : {
8508 232 : constraint = &constrinfo[j++];
8509 232 : tyinfo->nDomChecks++;
8510 : }
8511 : else
8512 : {
8513 : Assert(contype == CONSTRAINT_NOTNULL);
8514 : Assert(tyinfo->notnull == NULL);
8515 : /* use last item in array for the not-null constraint */
8516 112 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8517 112 : constraint = tyinfo->notnull;
8518 : }
8519 :
8520 344 : constraint->dobj.objType = DO_CONSTRAINT;
8521 344 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8522 344 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8523 344 : AssignDumpId(&(constraint->dobj));
8524 344 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8525 344 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8526 344 : constraint->contable = NULL;
8527 344 : constraint->condomain = tyinfo;
8528 344 : constraint->contype = contype;
8529 344 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8530 344 : constraint->confrelid = InvalidOid;
8531 344 : constraint->conindex = 0;
8532 344 : constraint->condeferrable = false;
8533 344 : constraint->condeferred = false;
8534 344 : constraint->conislocal = true;
8535 :
8536 344 : constraint->separate = !validated;
8537 :
8538 : /*
8539 : * Make the domain depend on the constraint, ensuring it won't be
8540 : * output till any constraint dependencies are OK. If the constraint
8541 : * has not been validated, it's going to be dumped after the domain
8542 : * anyway, so this doesn't matter.
8543 : */
8544 344 : if (validated)
8545 334 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8546 : }
8547 :
8548 322 : PQclear(res);
8549 :
8550 322 : destroyPQExpBuffer(query);
8551 322 : }
8552 :
8553 : /*
8554 : * getRules
8555 : * get basic information about every rule in the system
8556 : */
8557 : void
8558 364 : getRules(Archive *fout)
8559 : {
8560 : PGresult *res;
8561 : int ntups;
8562 : int i;
8563 364 : PQExpBuffer query = createPQExpBuffer();
8564 : RuleInfo *ruleinfo;
8565 : int i_tableoid;
8566 : int i_oid;
8567 : int i_rulename;
8568 : int i_ruletable;
8569 : int i_ev_type;
8570 : int i_is_instead;
8571 : int i_ev_enabled;
8572 :
8573 364 : appendPQExpBufferStr(query, "SELECT "
8574 : "tableoid, oid, rulename, "
8575 : "ev_class AS ruletable, ev_type, is_instead, "
8576 : "ev_enabled "
8577 : "FROM pg_rewrite "
8578 : "ORDER BY oid");
8579 :
8580 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8581 :
8582 364 : ntups = PQntuples(res);
8583 :
8584 364 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8585 :
8586 364 : i_tableoid = PQfnumber(res, "tableoid");
8587 364 : i_oid = PQfnumber(res, "oid");
8588 364 : i_rulename = PQfnumber(res, "rulename");
8589 364 : i_ruletable = PQfnumber(res, "ruletable");
8590 364 : i_ev_type = PQfnumber(res, "ev_type");
8591 364 : i_is_instead = PQfnumber(res, "is_instead");
8592 364 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8593 :
8594 56664 : for (i = 0; i < ntups; i++)
8595 : {
8596 : Oid ruletableoid;
8597 :
8598 56300 : ruleinfo[i].dobj.objType = DO_RULE;
8599 56300 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8600 56300 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8601 56300 : AssignDumpId(&ruleinfo[i].dobj);
8602 56300 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8603 56300 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8604 56300 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8605 56300 : if (ruleinfo[i].ruletable == NULL)
8606 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8607 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8608 56300 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8609 56300 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8610 56300 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8611 56300 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8612 56300 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8613 56300 : if (ruleinfo[i].ruletable)
8614 : {
8615 : /*
8616 : * If the table is a view or materialized view, force its ON
8617 : * SELECT rule to be sorted before the view itself --- this
8618 : * ensures that any dependencies for the rule affect the table's
8619 : * positioning. Other rules are forced to appear after their
8620 : * table.
8621 : */
8622 56300 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8623 1534 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8624 55838 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8625 : {
8626 55026 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8627 55026 : ruleinfo[i].dobj.dumpId);
8628 : /* We'll merge the rule into CREATE VIEW, if possible */
8629 55026 : ruleinfo[i].separate = false;
8630 : }
8631 : else
8632 : {
8633 1274 : addObjectDependency(&ruleinfo[i].dobj,
8634 1274 : ruleinfo[i].ruletable->dobj.dumpId);
8635 1274 : ruleinfo[i].separate = true;
8636 : }
8637 : }
8638 : else
8639 0 : ruleinfo[i].separate = true;
8640 : }
8641 :
8642 364 : PQclear(res);
8643 :
8644 364 : destroyPQExpBuffer(query);
8645 364 : }
8646 :
8647 : /*
8648 : * getTriggers
8649 : * get information about every trigger on a dumpable table
8650 : *
8651 : * Note: trigger data is not returned directly to the caller, but it
8652 : * does get entered into the DumpableObject tables.
8653 : */
8654 : void
8655 364 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8656 : {
8657 364 : PQExpBuffer query = createPQExpBuffer();
8658 364 : PQExpBuffer tbloids = createPQExpBuffer();
8659 : PGresult *res;
8660 : int ntups;
8661 : int curtblindx;
8662 : TriggerInfo *tginfo;
8663 : int i_tableoid,
8664 : i_oid,
8665 : i_tgrelid,
8666 : i_tgname,
8667 : i_tgenabled,
8668 : i_tgispartition,
8669 : i_tgdef;
8670 :
8671 : /*
8672 : * We want to perform just one query against pg_trigger. However, we
8673 : * mustn't try to select every row of the catalog and then sort it out on
8674 : * the client side, because some of the server-side functions we need
8675 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8676 : * build an array of the OIDs of tables we care about (and now have lock
8677 : * on!), and use a WHERE clause to constrain which rows are selected.
8678 : */
8679 364 : appendPQExpBufferChar(tbloids, '{');
8680 96818 : for (int i = 0; i < numTables; i++)
8681 : {
8682 96454 : TableInfo *tbinfo = &tblinfo[i];
8683 :
8684 96454 : if (!tbinfo->hastriggers ||
8685 2282 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8686 94696 : continue;
8687 :
8688 : /* OK, we need info for this table */
8689 1758 : if (tbloids->len > 1) /* do we have more than the '{'? */
8690 1650 : appendPQExpBufferChar(tbloids, ',');
8691 1758 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8692 : }
8693 364 : appendPQExpBufferChar(tbloids, '}');
8694 :
8695 364 : if (fout->remoteVersion >= 150000)
8696 : {
8697 : /*
8698 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8699 : * result in non-forward-compatible dumps of WHEN clauses due to
8700 : * under-parenthesization.
8701 : *
8702 : * NB: We need to see partition triggers in case the tgenabled flag
8703 : * has been changed from the parent.
8704 : */
8705 364 : appendPQExpBuffer(query,
8706 : "SELECT t.tgrelid, t.tgname, "
8707 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8708 : "t.tgenabled, t.tableoid, t.oid, "
8709 : "t.tgparentid <> 0 AS tgispartition\n"
8710 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8711 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8712 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8713 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8714 : "OR t.tgenabled != u.tgenabled) "
8715 : "ORDER BY t.tgrelid, t.tgname",
8716 : tbloids->data);
8717 : }
8718 0 : else if (fout->remoteVersion >= 130000)
8719 : {
8720 : /*
8721 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8722 : * result in non-forward-compatible dumps of WHEN clauses due to
8723 : * under-parenthesization.
8724 : *
8725 : * NB: We need to see tgisinternal triggers in partitions, in case the
8726 : * tgenabled flag has been changed from the parent.
8727 : */
8728 0 : appendPQExpBuffer(query,
8729 : "SELECT t.tgrelid, t.tgname, "
8730 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8731 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8732 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8733 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8734 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8735 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8736 : "ORDER BY t.tgrelid, t.tgname",
8737 : tbloids->data);
8738 : }
8739 0 : else if (fout->remoteVersion >= 110000)
8740 : {
8741 : /*
8742 : * NB: We need to see tgisinternal triggers in partitions, in case the
8743 : * tgenabled flag has been changed from the parent. No tgparentid in
8744 : * version 11-12, so we have to match them via pg_depend.
8745 : *
8746 : * See above about pretty=true in pg_get_triggerdef.
8747 : */
8748 0 : appendPQExpBuffer(query,
8749 : "SELECT t.tgrelid, t.tgname, "
8750 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8751 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8752 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8753 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8754 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8755 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8756 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8757 : " d.objid = t.oid "
8758 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8759 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8760 : "ORDER BY t.tgrelid, t.tgname",
8761 : tbloids->data);
8762 : }
8763 : else
8764 : {
8765 : /* See above about pretty=true in pg_get_triggerdef */
8766 0 : appendPQExpBuffer(query,
8767 : "SELECT t.tgrelid, t.tgname, "
8768 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8769 : "t.tgenabled, false as tgispartition, "
8770 : "t.tableoid, t.oid "
8771 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8772 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8773 : "WHERE NOT tgisinternal "
8774 : "ORDER BY t.tgrelid, t.tgname",
8775 : tbloids->data);
8776 : }
8777 :
8778 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8779 :
8780 364 : ntups = PQntuples(res);
8781 :
8782 364 : i_tableoid = PQfnumber(res, "tableoid");
8783 364 : i_oid = PQfnumber(res, "oid");
8784 364 : i_tgrelid = PQfnumber(res, "tgrelid");
8785 364 : i_tgname = PQfnumber(res, "tgname");
8786 364 : i_tgenabled = PQfnumber(res, "tgenabled");
8787 364 : i_tgispartition = PQfnumber(res, "tgispartition");
8788 364 : i_tgdef = PQfnumber(res, "tgdef");
8789 :
8790 364 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8791 :
8792 : /*
8793 : * Outer loop iterates once per table, not once per row. Incrementing of
8794 : * j is handled by the inner loop.
8795 : */
8796 364 : curtblindx = -1;
8797 1006 : for (int j = 0; j < ntups;)
8798 : {
8799 642 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8800 642 : TableInfo *tbinfo = NULL;
8801 : int numtrigs;
8802 :
8803 : /* Count rows for this table */
8804 1076 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8805 968 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8806 534 : break;
8807 :
8808 : /*
8809 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8810 : * order.
8811 : */
8812 33692 : while (++curtblindx < numTables)
8813 : {
8814 33692 : tbinfo = &tblinfo[curtblindx];
8815 33692 : if (tbinfo->dobj.catId.oid == tgrelid)
8816 642 : break;
8817 : }
8818 642 : if (curtblindx >= numTables)
8819 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8820 :
8821 : /* Save data for this table */
8822 642 : tbinfo->triggers = tginfo + j;
8823 642 : tbinfo->numTriggers = numtrigs;
8824 :
8825 1718 : for (int c = 0; c < numtrigs; c++, j++)
8826 : {
8827 1076 : tginfo[j].dobj.objType = DO_TRIGGER;
8828 1076 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8829 1076 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8830 1076 : AssignDumpId(&tginfo[j].dobj);
8831 1076 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8832 1076 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8833 1076 : tginfo[j].tgtable = tbinfo;
8834 1076 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8835 1076 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8836 1076 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8837 : }
8838 : }
8839 :
8840 364 : PQclear(res);
8841 :
8842 364 : destroyPQExpBuffer(query);
8843 364 : destroyPQExpBuffer(tbloids);
8844 364 : }
8845 :
8846 : /*
8847 : * getEventTriggers
8848 : * get information about event triggers
8849 : */
8850 : void
8851 364 : getEventTriggers(Archive *fout)
8852 : {
8853 : int i;
8854 : PQExpBuffer query;
8855 : PGresult *res;
8856 : EventTriggerInfo *evtinfo;
8857 : int i_tableoid,
8858 : i_oid,
8859 : i_evtname,
8860 : i_evtevent,
8861 : i_evtowner,
8862 : i_evttags,
8863 : i_evtfname,
8864 : i_evtenabled;
8865 : int ntups;
8866 :
8867 : /* Before 9.3, there are no event triggers */
8868 364 : if (fout->remoteVersion < 90300)
8869 0 : return;
8870 :
8871 364 : query = createPQExpBuffer();
8872 :
8873 364 : appendPQExpBufferStr(query,
8874 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8875 : "evtevent, evtowner, "
8876 : "array_to_string(array("
8877 : "select quote_literal(x) "
8878 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8879 : "e.evtfoid::regproc as evtfname "
8880 : "FROM pg_event_trigger e "
8881 : "ORDER BY e.oid");
8882 :
8883 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8884 :
8885 364 : ntups = PQntuples(res);
8886 :
8887 364 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8888 :
8889 364 : i_tableoid = PQfnumber(res, "tableoid");
8890 364 : i_oid = PQfnumber(res, "oid");
8891 364 : i_evtname = PQfnumber(res, "evtname");
8892 364 : i_evtevent = PQfnumber(res, "evtevent");
8893 364 : i_evtowner = PQfnumber(res, "evtowner");
8894 364 : i_evttags = PQfnumber(res, "evttags");
8895 364 : i_evtfname = PQfnumber(res, "evtfname");
8896 364 : i_evtenabled = PQfnumber(res, "evtenabled");
8897 :
8898 474 : for (i = 0; i < ntups; i++)
8899 : {
8900 110 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8901 110 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8902 110 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8903 110 : AssignDumpId(&evtinfo[i].dobj);
8904 110 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8905 110 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8906 110 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8907 110 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8908 110 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8909 110 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8910 110 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8911 :
8912 : /* Decide whether we want to dump it */
8913 110 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8914 : }
8915 :
8916 364 : PQclear(res);
8917 :
8918 364 : destroyPQExpBuffer(query);
8919 : }
8920 :
8921 : /*
8922 : * getProcLangs
8923 : * get basic information about every procedural language in the system
8924 : *
8925 : * NB: this must run after getFuncs() because we assume we can do
8926 : * findFuncByOid().
8927 : */
8928 : void
8929 364 : getProcLangs(Archive *fout)
8930 : {
8931 : PGresult *res;
8932 : int ntups;
8933 : int i;
8934 364 : PQExpBuffer query = createPQExpBuffer();
8935 : ProcLangInfo *planginfo;
8936 : int i_tableoid;
8937 : int i_oid;
8938 : int i_lanname;
8939 : int i_lanpltrusted;
8940 : int i_lanplcallfoid;
8941 : int i_laninline;
8942 : int i_lanvalidator;
8943 : int i_lanacl;
8944 : int i_acldefault;
8945 : int i_lanowner;
8946 :
8947 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8948 : "lanname, lanpltrusted, lanplcallfoid, "
8949 : "laninline, lanvalidator, "
8950 : "lanacl, "
8951 : "acldefault('l', lanowner) AS acldefault, "
8952 : "lanowner "
8953 : "FROM pg_language "
8954 : "WHERE lanispl "
8955 : "ORDER BY oid");
8956 :
8957 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8958 :
8959 364 : ntups = PQntuples(res);
8960 :
8961 364 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8962 :
8963 364 : i_tableoid = PQfnumber(res, "tableoid");
8964 364 : i_oid = PQfnumber(res, "oid");
8965 364 : i_lanname = PQfnumber(res, "lanname");
8966 364 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8967 364 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8968 364 : i_laninline = PQfnumber(res, "laninline");
8969 364 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8970 364 : i_lanacl = PQfnumber(res, "lanacl");
8971 364 : i_acldefault = PQfnumber(res, "acldefault");
8972 364 : i_lanowner = PQfnumber(res, "lanowner");
8973 :
8974 824 : for (i = 0; i < ntups; i++)
8975 : {
8976 460 : planginfo[i].dobj.objType = DO_PROCLANG;
8977 460 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8978 460 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8979 460 : AssignDumpId(&planginfo[i].dobj);
8980 :
8981 460 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8982 460 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8983 460 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8984 460 : planginfo[i].dacl.privtype = 0;
8985 460 : planginfo[i].dacl.initprivs = NULL;
8986 460 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8987 460 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8988 460 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8989 460 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8990 460 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8991 :
8992 : /* Decide whether we want to dump it */
8993 460 : selectDumpableProcLang(&(planginfo[i]), fout);
8994 :
8995 : /* Mark whether language has an ACL */
8996 460 : if (!PQgetisnull(res, i, i_lanacl))
8997 96 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8998 : }
8999 :
9000 364 : PQclear(res);
9001 :
9002 364 : destroyPQExpBuffer(query);
9003 364 : }
9004 :
9005 : /*
9006 : * getCasts
9007 : * get basic information about most casts in the system
9008 : *
9009 : * Skip casts from a range to its multirange, since we'll create those
9010 : * automatically.
9011 : */
9012 : void
9013 364 : getCasts(Archive *fout)
9014 : {
9015 : PGresult *res;
9016 : int ntups;
9017 : int i;
9018 364 : PQExpBuffer query = createPQExpBuffer();
9019 : CastInfo *castinfo;
9020 : int i_tableoid;
9021 : int i_oid;
9022 : int i_castsource;
9023 : int i_casttarget;
9024 : int i_castfunc;
9025 : int i_castcontext;
9026 : int i_castmethod;
9027 :
9028 364 : if (fout->remoteVersion >= 140000)
9029 : {
9030 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9031 : "castsource, casttarget, castfunc, castcontext, "
9032 : "castmethod "
9033 : "FROM pg_cast c "
9034 : "WHERE NOT EXISTS ( "
9035 : "SELECT 1 FROM pg_range r "
9036 : "WHERE c.castsource = r.rngtypid "
9037 : "AND c.casttarget = r.rngmultitypid "
9038 : ") "
9039 : "ORDER BY 3,4");
9040 : }
9041 : else
9042 : {
9043 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9044 : "castsource, casttarget, castfunc, castcontext, "
9045 : "castmethod "
9046 : "FROM pg_cast ORDER BY 3,4");
9047 : }
9048 :
9049 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9050 :
9051 364 : ntups = PQntuples(res);
9052 :
9053 364 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9054 :
9055 364 : i_tableoid = PQfnumber(res, "tableoid");
9056 364 : i_oid = PQfnumber(res, "oid");
9057 364 : i_castsource = PQfnumber(res, "castsource");
9058 364 : i_casttarget = PQfnumber(res, "casttarget");
9059 364 : i_castfunc = PQfnumber(res, "castfunc");
9060 364 : i_castcontext = PQfnumber(res, "castcontext");
9061 364 : i_castmethod = PQfnumber(res, "castmethod");
9062 :
9063 86448 : for (i = 0; i < ntups; i++)
9064 : {
9065 : PQExpBufferData namebuf;
9066 : TypeInfo *sTypeInfo;
9067 : TypeInfo *tTypeInfo;
9068 :
9069 86084 : castinfo[i].dobj.objType = DO_CAST;
9070 86084 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9071 86084 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9072 86084 : AssignDumpId(&castinfo[i].dobj);
9073 86084 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9074 86084 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9075 86084 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9076 86084 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9077 86084 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9078 :
9079 : /*
9080 : * Try to name cast as concatenation of typnames. This is only used
9081 : * for purposes of sorting. If we fail to find either type, the name
9082 : * will be an empty string.
9083 : */
9084 86084 : initPQExpBuffer(&namebuf);
9085 86084 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9086 86084 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9087 86084 : if (sTypeInfo && tTypeInfo)
9088 86084 : appendPQExpBuffer(&namebuf, "%s %s",
9089 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9090 86084 : castinfo[i].dobj.name = namebuf.data;
9091 :
9092 : /* Decide whether we want to dump it */
9093 86084 : selectDumpableCast(&(castinfo[i]), fout);
9094 : }
9095 :
9096 364 : PQclear(res);
9097 :
9098 364 : destroyPQExpBuffer(query);
9099 364 : }
9100 :
9101 : static char *
9102 188 : get_language_name(Archive *fout, Oid langid)
9103 : {
9104 : PQExpBuffer query;
9105 : PGresult *res;
9106 : char *lanname;
9107 :
9108 188 : query = createPQExpBuffer();
9109 188 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9110 188 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9111 188 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9112 188 : destroyPQExpBuffer(query);
9113 188 : PQclear(res);
9114 :
9115 188 : return lanname;
9116 : }
9117 :
9118 : /*
9119 : * getTransforms
9120 : * get basic information about every transform in the system
9121 : */
9122 : void
9123 364 : getTransforms(Archive *fout)
9124 : {
9125 : PGresult *res;
9126 : int ntups;
9127 : int i;
9128 : PQExpBuffer query;
9129 : TransformInfo *transforminfo;
9130 : int i_tableoid;
9131 : int i_oid;
9132 : int i_trftype;
9133 : int i_trflang;
9134 : int i_trffromsql;
9135 : int i_trftosql;
9136 :
9137 : /* Transforms didn't exist pre-9.5 */
9138 364 : if (fout->remoteVersion < 90500)
9139 0 : return;
9140 :
9141 364 : query = createPQExpBuffer();
9142 :
9143 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9144 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9145 : "FROM pg_transform "
9146 : "ORDER BY 3,4");
9147 :
9148 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9149 :
9150 364 : ntups = PQntuples(res);
9151 :
9152 364 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9153 :
9154 364 : i_tableoid = PQfnumber(res, "tableoid");
9155 364 : i_oid = PQfnumber(res, "oid");
9156 364 : i_trftype = PQfnumber(res, "trftype");
9157 364 : i_trflang = PQfnumber(res, "trflang");
9158 364 : i_trffromsql = PQfnumber(res, "trffromsql");
9159 364 : i_trftosql = PQfnumber(res, "trftosql");
9160 :
9161 474 : for (i = 0; i < ntups; i++)
9162 : {
9163 : PQExpBufferData namebuf;
9164 : TypeInfo *typeInfo;
9165 : char *lanname;
9166 :
9167 110 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9168 110 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9169 110 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9170 110 : AssignDumpId(&transforminfo[i].dobj);
9171 110 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9172 110 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9173 110 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9174 110 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9175 :
9176 : /*
9177 : * Try to name transform as concatenation of type and language name.
9178 : * This is only used for purposes of sorting. If we fail to find
9179 : * either, the name will be an empty string.
9180 : */
9181 110 : initPQExpBuffer(&namebuf);
9182 110 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9183 110 : lanname = get_language_name(fout, transforminfo[i].trflang);
9184 110 : if (typeInfo && lanname)
9185 110 : appendPQExpBuffer(&namebuf, "%s %s",
9186 : typeInfo->dobj.name, lanname);
9187 110 : transforminfo[i].dobj.name = namebuf.data;
9188 110 : free(lanname);
9189 :
9190 : /* Decide whether we want to dump it */
9191 110 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9192 : }
9193 :
9194 364 : PQclear(res);
9195 :
9196 364 : destroyPQExpBuffer(query);
9197 : }
9198 :
9199 : /*
9200 : * getTableAttrs -
9201 : * for each interesting table, read info about its attributes
9202 : * (names, types, default values, CHECK constraints, etc)
9203 : *
9204 : * modifies tblinfo
9205 : */
9206 : void
9207 364 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9208 : {
9209 364 : DumpOptions *dopt = fout->dopt;
9210 364 : PQExpBuffer q = createPQExpBuffer();
9211 364 : PQExpBuffer tbloids = createPQExpBuffer();
9212 364 : PQExpBuffer checkoids = createPQExpBuffer();
9213 364 : PQExpBuffer invalidnotnulloids = NULL;
9214 : PGresult *res;
9215 : int ntups;
9216 : int curtblindx;
9217 : int i_attrelid;
9218 : int i_attnum;
9219 : int i_attname;
9220 : int i_atttypname;
9221 : int i_attstattarget;
9222 : int i_attstorage;
9223 : int i_typstorage;
9224 : int i_attidentity;
9225 : int i_attgenerated;
9226 : int i_attisdropped;
9227 : int i_attlen;
9228 : int i_attalign;
9229 : int i_attislocal;
9230 : int i_notnull_name;
9231 : int i_notnull_comment;
9232 : int i_notnull_noinherit;
9233 : int i_notnull_islocal;
9234 : int i_notnull_invalidoid;
9235 : int i_attoptions;
9236 : int i_attcollation;
9237 : int i_attcompression;
9238 : int i_attfdwoptions;
9239 : int i_attmissingval;
9240 : int i_atthasdef;
9241 :
9242 : /*
9243 : * We want to perform just one query against pg_attribute, and then just
9244 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9245 : * (for CHECK constraints and for NOT NULL constraints). However, we
9246 : * mustn't try to select every row of those catalogs and then sort it out
9247 : * on the client side, because some of the server-side functions we need
9248 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9249 : * build an array of the OIDs of tables we care about (and now have lock
9250 : * on!), and use a WHERE clause to constrain which rows are selected.
9251 : */
9252 364 : appendPQExpBufferChar(tbloids, '{');
9253 364 : appendPQExpBufferChar(checkoids, '{');
9254 96818 : for (int i = 0; i < numTables; i++)
9255 : {
9256 96454 : TableInfo *tbinfo = &tblinfo[i];
9257 :
9258 : /* Don't bother to collect info for sequences */
9259 96454 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9260 1294 : continue;
9261 :
9262 : /*
9263 : * Don't bother with uninteresting tables, either. For binary
9264 : * upgrades, this is bypassed for pg_largeobject_metadata and
9265 : * pg_shdepend so that the columns names are collected for the
9266 : * corresponding COPY commands. Restoring the data for those catalogs
9267 : * is faster than restoring the equivalent set of large object
9268 : * commands. We can only do this for upgrades from v12 and newer; in
9269 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9270 : * the OID column is hidden and won't be dumped.
9271 : */
9272 95160 : if (!tbinfo->interesting &&
9273 81550 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9274 15444 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9275 15372 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9276 81406 : continue;
9277 :
9278 : /* OK, we need info for this table */
9279 13754 : if (tbloids->len > 1) /* do we have more than the '{'? */
9280 13478 : appendPQExpBufferChar(tbloids, ',');
9281 13754 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9282 :
9283 13754 : if (tbinfo->ncheck > 0)
9284 : {
9285 : /* Also make a list of the ones with check constraints */
9286 1124 : if (checkoids->len > 1) /* do we have more than the '{'? */
9287 980 : appendPQExpBufferChar(checkoids, ',');
9288 1124 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9289 : }
9290 : }
9291 364 : appendPQExpBufferChar(tbloids, '}');
9292 364 : appendPQExpBufferChar(checkoids, '}');
9293 :
9294 : /*
9295 : * Find all the user attributes and their types.
9296 : *
9297 : * Since we only want to dump COLLATE clauses for attributes whose
9298 : * collation is different from their type's default, we use a CASE here to
9299 : * suppress uninteresting attcollations cheaply.
9300 : */
9301 364 : appendPQExpBufferStr(q,
9302 : "SELECT\n"
9303 : "a.attrelid,\n"
9304 : "a.attnum,\n"
9305 : "a.attname,\n"
9306 : "a.attstattarget,\n"
9307 : "a.attstorage,\n"
9308 : "t.typstorage,\n"
9309 : "a.atthasdef,\n"
9310 : "a.attisdropped,\n"
9311 : "a.attlen,\n"
9312 : "a.attalign,\n"
9313 : "a.attislocal,\n"
9314 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9315 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9316 : "CASE WHEN a.attcollation <> t.typcollation "
9317 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9318 : "pg_catalog.array_to_string(ARRAY("
9319 : "SELECT pg_catalog.quote_ident(option_name) || "
9320 : "' ' || pg_catalog.quote_literal(option_value) "
9321 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9322 : "ORDER BY option_name"
9323 : "), E',\n ') AS attfdwoptions,\n");
9324 :
9325 : /*
9326 : * Find out any NOT NULL markings for each column. In 18 and up we read
9327 : * pg_constraint to obtain the constraint name, and for valid constraints
9328 : * also pg_description to obtain its comment. notnull_noinherit is set
9329 : * according to the NO INHERIT property. For versions prior to 18, we
9330 : * store an empty string as the name when a constraint is marked as
9331 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9332 : * without a name); also, such cases are never NO INHERIT.
9333 : *
9334 : * For invalid constraints, we need to store their OIDs for processing
9335 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9336 : * is invalid, and NULL otherwise. Their comments are handled not here
9337 : * but by collectComments, because they're their own dumpable object.
9338 : *
9339 : * We track in notnull_islocal whether the constraint was defined directly
9340 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9341 : * might modify this later; that routine is also in charge of determining
9342 : * the correct inhcount.
9343 : */
9344 364 : if (fout->remoteVersion >= 180000)
9345 364 : appendPQExpBufferStr(q,
9346 : "co.conname AS notnull_name,\n"
9347 : "CASE WHEN co.convalidated THEN pt.description"
9348 : " ELSE NULL END AS notnull_comment,\n"
9349 : "CASE WHEN NOT co.convalidated THEN co.oid "
9350 : "ELSE NULL END AS notnull_invalidoid,\n"
9351 : "co.connoinherit AS notnull_noinherit,\n"
9352 : "co.conislocal AS notnull_islocal,\n");
9353 : else
9354 0 : appendPQExpBufferStr(q,
9355 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9356 : "NULL AS notnull_comment,\n"
9357 : "NULL AS notnull_invalidoid,\n"
9358 : "false AS notnull_noinherit,\n"
9359 : "a.attislocal AS notnull_islocal,\n");
9360 :
9361 364 : if (fout->remoteVersion >= 140000)
9362 364 : appendPQExpBufferStr(q,
9363 : "a.attcompression AS attcompression,\n");
9364 : else
9365 0 : appendPQExpBufferStr(q,
9366 : "'' AS attcompression,\n");
9367 :
9368 364 : if (fout->remoteVersion >= 100000)
9369 364 : appendPQExpBufferStr(q,
9370 : "a.attidentity,\n");
9371 : else
9372 0 : appendPQExpBufferStr(q,
9373 : "'' AS attidentity,\n");
9374 :
9375 364 : if (fout->remoteVersion >= 110000)
9376 364 : appendPQExpBufferStr(q,
9377 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9378 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9379 : else
9380 0 : appendPQExpBufferStr(q,
9381 : "NULL AS attmissingval,\n");
9382 :
9383 364 : if (fout->remoteVersion >= 120000)
9384 364 : appendPQExpBufferStr(q,
9385 : "a.attgenerated\n");
9386 : else
9387 0 : appendPQExpBufferStr(q,
9388 : "'' AS attgenerated\n");
9389 :
9390 : /* need left join to pg_type to not fail on dropped columns ... */
9391 364 : appendPQExpBuffer(q,
9392 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9393 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9394 : "LEFT JOIN pg_catalog.pg_type t "
9395 : "ON (a.atttypid = t.oid)\n",
9396 : tbloids->data);
9397 :
9398 : /*
9399 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9400 : * entries and pg_description to get their comments.
9401 : */
9402 364 : if (fout->remoteVersion >= 180000)
9403 364 : appendPQExpBufferStr(q,
9404 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9405 : "(a.attrelid = co.conrelid\n"
9406 : " AND co.contype = 'n' AND "
9407 : "co.conkey = array[a.attnum])\n"
9408 : " LEFT JOIN pg_catalog.pg_description pt ON "
9409 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9410 :
9411 364 : appendPQExpBufferStr(q,
9412 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9413 : "ORDER BY a.attrelid, a.attnum");
9414 :
9415 364 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9416 :
9417 364 : ntups = PQntuples(res);
9418 :
9419 364 : i_attrelid = PQfnumber(res, "attrelid");
9420 364 : i_attnum = PQfnumber(res, "attnum");
9421 364 : i_attname = PQfnumber(res, "attname");
9422 364 : i_atttypname = PQfnumber(res, "atttypname");
9423 364 : i_attstattarget = PQfnumber(res, "attstattarget");
9424 364 : i_attstorage = PQfnumber(res, "attstorage");
9425 364 : i_typstorage = PQfnumber(res, "typstorage");
9426 364 : i_attidentity = PQfnumber(res, "attidentity");
9427 364 : i_attgenerated = PQfnumber(res, "attgenerated");
9428 364 : i_attisdropped = PQfnumber(res, "attisdropped");
9429 364 : i_attlen = PQfnumber(res, "attlen");
9430 364 : i_attalign = PQfnumber(res, "attalign");
9431 364 : i_attislocal = PQfnumber(res, "attislocal");
9432 364 : i_notnull_name = PQfnumber(res, "notnull_name");
9433 364 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9434 364 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9435 364 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9436 364 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9437 364 : i_attoptions = PQfnumber(res, "attoptions");
9438 364 : i_attcollation = PQfnumber(res, "attcollation");
9439 364 : i_attcompression = PQfnumber(res, "attcompression");
9440 364 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9441 364 : i_attmissingval = PQfnumber(res, "attmissingval");
9442 364 : i_atthasdef = PQfnumber(res, "atthasdef");
9443 :
9444 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9445 364 : resetPQExpBuffer(tbloids);
9446 364 : appendPQExpBufferChar(tbloids, '{');
9447 :
9448 : /*
9449 : * Outer loop iterates once per table, not once per row. Incrementing of
9450 : * r is handled by the inner loop.
9451 : */
9452 364 : curtblindx = -1;
9453 13824 : for (int r = 0; r < ntups;)
9454 : {
9455 13460 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9456 13460 : TableInfo *tbinfo = NULL;
9457 : int numatts;
9458 : bool hasdefaults;
9459 :
9460 : /* Count rows for this table */
9461 49956 : for (numatts = 1; numatts < ntups - r; numatts++)
9462 49686 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9463 13190 : break;
9464 :
9465 : /*
9466 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9467 : * order.
9468 : */
9469 66316 : while (++curtblindx < numTables)
9470 : {
9471 66316 : tbinfo = &tblinfo[curtblindx];
9472 66316 : if (tbinfo->dobj.catId.oid == attrelid)
9473 13460 : break;
9474 : }
9475 13460 : if (curtblindx >= numTables)
9476 0 : pg_fatal("unrecognized table OID %u", attrelid);
9477 : /* cross-check that we only got requested tables */
9478 13460 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9479 13460 : (!tbinfo->interesting &&
9480 144 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9481 144 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9482 72 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9483 0 : pg_fatal("unexpected column data for table \"%s\"",
9484 : tbinfo->dobj.name);
9485 :
9486 : /* Save data for this table */
9487 13460 : tbinfo->numatts = numatts;
9488 13460 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9489 13460 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9490 13460 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9491 13460 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9492 13460 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9493 13460 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9494 13460 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9495 13460 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9496 13460 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9497 13460 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9498 13460 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9499 13460 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9500 13460 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9501 13460 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9502 13460 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9503 13460 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9504 13460 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9505 13460 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9506 13460 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9507 13460 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9508 13460 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9509 13460 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9510 13460 : hasdefaults = false;
9511 :
9512 63416 : for (int j = 0; j < numatts; j++, r++)
9513 : {
9514 49956 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9515 0 : pg_fatal("invalid column numbering in table \"%s\"",
9516 : tbinfo->dobj.name);
9517 49956 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9518 49956 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9519 49956 : if (PQgetisnull(res, r, i_attstattarget))
9520 49870 : tbinfo->attstattarget[j] = -1;
9521 : else
9522 86 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9523 49956 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9524 49956 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9525 49956 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9526 49956 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9527 49956 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9528 49956 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9529 49956 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9530 49956 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9531 49956 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9532 :
9533 : /* Handle not-null constraint name and flags */
9534 49956 : determineNotNullFlags(fout, res, r,
9535 : tbinfo, j,
9536 : i_notnull_name,
9537 : i_notnull_comment,
9538 : i_notnull_invalidoid,
9539 : i_notnull_noinherit,
9540 : i_notnull_islocal,
9541 : &invalidnotnulloids);
9542 :
9543 49956 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9544 49956 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9545 49956 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9546 49956 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9547 49956 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9548 49956 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9549 49956 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9550 49956 : tbinfo->attrdefs[j] = NULL; /* fix below */
9551 49956 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9552 2632 : hasdefaults = true;
9553 : }
9554 :
9555 13460 : if (hasdefaults)
9556 : {
9557 : /* Collect OIDs of interesting tables that have defaults */
9558 1970 : if (tbloids->len > 1) /* do we have more than the '{'? */
9559 1828 : appendPQExpBufferChar(tbloids, ',');
9560 1970 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9561 : }
9562 : }
9563 :
9564 : /* If invalidnotnulloids has any data, finalize it */
9565 364 : if (invalidnotnulloids != NULL)
9566 92 : appendPQExpBufferChar(invalidnotnulloids, '}');
9567 :
9568 364 : PQclear(res);
9569 :
9570 : /*
9571 : * Now get info about column defaults. This is skipped for a data-only
9572 : * dump, as it is only needed for table schemas.
9573 : */
9574 364 : if (dopt->dumpSchema && tbloids->len > 1)
9575 : {
9576 : AttrDefInfo *attrdefs;
9577 : int numDefaults;
9578 126 : TableInfo *tbinfo = NULL;
9579 :
9580 126 : pg_log_info("finding table default expressions");
9581 :
9582 126 : appendPQExpBufferChar(tbloids, '}');
9583 :
9584 126 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9585 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9586 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9587 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9588 : "ORDER BY a.adrelid, a.adnum",
9589 : tbloids->data);
9590 :
9591 126 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9592 :
9593 126 : numDefaults = PQntuples(res);
9594 126 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9595 :
9596 126 : curtblindx = -1;
9597 2562 : for (int j = 0; j < numDefaults; j++)
9598 : {
9599 2436 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9600 2436 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9601 2436 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9602 2436 : int adnum = atoi(PQgetvalue(res, j, 3));
9603 2436 : char *adsrc = PQgetvalue(res, j, 4);
9604 :
9605 : /*
9606 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9607 : * OID order.
9608 : */
9609 2436 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9610 : {
9611 38984 : while (++curtblindx < numTables)
9612 : {
9613 38984 : tbinfo = &tblinfo[curtblindx];
9614 38984 : if (tbinfo->dobj.catId.oid == adrelid)
9615 1834 : break;
9616 : }
9617 1834 : if (curtblindx >= numTables)
9618 0 : pg_fatal("unrecognized table OID %u", adrelid);
9619 : }
9620 :
9621 2436 : if (adnum <= 0 || adnum > tbinfo->numatts)
9622 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9623 : adnum, tbinfo->dobj.name);
9624 :
9625 : /*
9626 : * dropped columns shouldn't have defaults, but just in case,
9627 : * ignore 'em
9628 : */
9629 2436 : if (tbinfo->attisdropped[adnum - 1])
9630 0 : continue;
9631 :
9632 2436 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9633 2436 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9634 2436 : attrdefs[j].dobj.catId.oid = adoid;
9635 2436 : AssignDumpId(&attrdefs[j].dobj);
9636 2436 : attrdefs[j].adtable = tbinfo;
9637 2436 : attrdefs[j].adnum = adnum;
9638 2436 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9639 :
9640 2436 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9641 2436 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9642 :
9643 2436 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9644 :
9645 : /*
9646 : * Figure out whether the default/generation expression should be
9647 : * dumped as part of the main CREATE TABLE (or similar) command or
9648 : * as a separate ALTER TABLE (or similar) command. The preference
9649 : * is to put it into the CREATE command, but in some cases that's
9650 : * not possible.
9651 : */
9652 2436 : if (tbinfo->attgenerated[adnum - 1])
9653 : {
9654 : /*
9655 : * Column generation expressions cannot be dumped separately,
9656 : * because there is no syntax for it. By setting separate to
9657 : * false here we prevent the "default" from being processed as
9658 : * its own dumpable object. Later, flagInhAttrs() will mark
9659 : * it as not to be dumped at all, if possible (that is, if it
9660 : * can be inherited from a parent).
9661 : */
9662 1360 : attrdefs[j].separate = false;
9663 : }
9664 1076 : else if (tbinfo->relkind == RELKIND_VIEW)
9665 : {
9666 : /*
9667 : * Defaults on a VIEW must always be dumped as separate ALTER
9668 : * TABLE commands.
9669 : */
9670 70 : attrdefs[j].separate = true;
9671 : }
9672 1006 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9673 : {
9674 : /* column will be suppressed, print default separately */
9675 8 : attrdefs[j].separate = true;
9676 : }
9677 : else
9678 : {
9679 998 : attrdefs[j].separate = false;
9680 : }
9681 :
9682 2436 : if (!attrdefs[j].separate)
9683 : {
9684 : /*
9685 : * Mark the default as needing to appear before the table, so
9686 : * that any dependencies it has must be emitted before the
9687 : * CREATE TABLE. If this is not possible, we'll change to
9688 : * "separate" mode while sorting dependencies.
9689 : */
9690 2358 : addObjectDependency(&tbinfo->dobj,
9691 2358 : attrdefs[j].dobj.dumpId);
9692 : }
9693 :
9694 2436 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9695 : }
9696 :
9697 126 : PQclear(res);
9698 : }
9699 :
9700 : /*
9701 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9702 : * data-only dump, as it is only needed for table schemas.
9703 : */
9704 364 : if (dopt->dumpSchema && invalidnotnulloids)
9705 : {
9706 : ConstraintInfo *constrs;
9707 : int numConstrs;
9708 : int i_tableoid;
9709 : int i_oid;
9710 : int i_conrelid;
9711 : int i_conname;
9712 : int i_consrc;
9713 : int i_conislocal;
9714 :
9715 80 : pg_log_info("finding invalid not-null constraints");
9716 :
9717 80 : resetPQExpBuffer(q);
9718 80 : appendPQExpBuffer(q,
9719 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9720 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9721 : "conislocal, convalidated "
9722 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9723 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9724 : "ORDER BY c.conrelid, c.conname",
9725 80 : invalidnotnulloids->data);
9726 :
9727 80 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9728 :
9729 80 : numConstrs = PQntuples(res);
9730 80 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9731 :
9732 80 : i_tableoid = PQfnumber(res, "tableoid");
9733 80 : i_oid = PQfnumber(res, "oid");
9734 80 : i_conrelid = PQfnumber(res, "conrelid");
9735 80 : i_conname = PQfnumber(res, "conname");
9736 80 : i_consrc = PQfnumber(res, "consrc");
9737 80 : i_conislocal = PQfnumber(res, "conislocal");
9738 :
9739 : /* As above, this loop iterates once per table, not once per row */
9740 80 : curtblindx = -1;
9741 220 : for (int j = 0; j < numConstrs;)
9742 : {
9743 140 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9744 140 : TableInfo *tbinfo = NULL;
9745 : int numcons;
9746 :
9747 : /* Count rows for this table */
9748 140 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9749 60 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9750 60 : break;
9751 :
9752 : /*
9753 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9754 : * OID order.
9755 : */
9756 26744 : while (++curtblindx < numTables)
9757 : {
9758 26744 : tbinfo = &tblinfo[curtblindx];
9759 26744 : if (tbinfo->dobj.catId.oid == conrelid)
9760 140 : break;
9761 : }
9762 140 : if (curtblindx >= numTables)
9763 0 : pg_fatal("unrecognized table OID %u", conrelid);
9764 :
9765 280 : for (int c = 0; c < numcons; c++, j++)
9766 : {
9767 140 : constrs[j].dobj.objType = DO_CONSTRAINT;
9768 140 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9769 140 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9770 140 : AssignDumpId(&constrs[j].dobj);
9771 140 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9772 140 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9773 140 : constrs[j].contable = tbinfo;
9774 140 : constrs[j].condomain = NULL;
9775 140 : constrs[j].contype = 'n';
9776 140 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9777 140 : constrs[j].confrelid = InvalidOid;
9778 140 : constrs[j].conindex = 0;
9779 140 : constrs[j].condeferrable = false;
9780 140 : constrs[j].condeferred = false;
9781 140 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9782 :
9783 : /*
9784 : * All invalid not-null constraints must be dumped separately,
9785 : * because CREATE TABLE would not create them as invalid, and
9786 : * also because they must be created after potentially
9787 : * violating data has been loaded.
9788 : */
9789 140 : constrs[j].separate = true;
9790 :
9791 140 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9792 : }
9793 : }
9794 80 : PQclear(res);
9795 : }
9796 :
9797 : /*
9798 : * Get info about table CHECK constraints. This is skipped for a
9799 : * data-only dump, as it is only needed for table schemas.
9800 : */
9801 364 : if (dopt->dumpSchema && checkoids->len > 2)
9802 : {
9803 : ConstraintInfo *constrs;
9804 : int numConstrs;
9805 : int i_tableoid;
9806 : int i_oid;
9807 : int i_conrelid;
9808 : int i_conname;
9809 : int i_consrc;
9810 : int i_conislocal;
9811 : int i_convalidated;
9812 :
9813 128 : pg_log_info("finding table check constraints");
9814 :
9815 128 : resetPQExpBuffer(q);
9816 128 : appendPQExpBuffer(q,
9817 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9818 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9819 : "conislocal, convalidated "
9820 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9821 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9822 : "WHERE contype = 'c' "
9823 : "ORDER BY c.conrelid, c.conname",
9824 : checkoids->data);
9825 :
9826 128 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9827 :
9828 128 : numConstrs = PQntuples(res);
9829 128 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9830 :
9831 128 : i_tableoid = PQfnumber(res, "tableoid");
9832 128 : i_oid = PQfnumber(res, "oid");
9833 128 : i_conrelid = PQfnumber(res, "conrelid");
9834 128 : i_conname = PQfnumber(res, "conname");
9835 128 : i_consrc = PQfnumber(res, "consrc");
9836 128 : i_conislocal = PQfnumber(res, "conislocal");
9837 128 : i_convalidated = PQfnumber(res, "convalidated");
9838 :
9839 : /* As above, this loop iterates once per table, not once per row */
9840 128 : curtblindx = -1;
9841 1150 : for (int j = 0; j < numConstrs;)
9842 : {
9843 1022 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9844 1022 : TableInfo *tbinfo = NULL;
9845 : int numcons;
9846 :
9847 : /* Count rows for this table */
9848 1304 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9849 1176 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9850 894 : break;
9851 :
9852 : /*
9853 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9854 : * OID order.
9855 : */
9856 37646 : while (++curtblindx < numTables)
9857 : {
9858 37646 : tbinfo = &tblinfo[curtblindx];
9859 37646 : if (tbinfo->dobj.catId.oid == conrelid)
9860 1022 : break;
9861 : }
9862 1022 : if (curtblindx >= numTables)
9863 0 : pg_fatal("unrecognized table OID %u", conrelid);
9864 :
9865 1022 : if (numcons != tbinfo->ncheck)
9866 : {
9867 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9868 : "expected %d check constraints on table \"%s\" but found %d",
9869 : tbinfo->ncheck),
9870 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9871 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9872 0 : exit_nicely(1);
9873 : }
9874 :
9875 1022 : tbinfo->checkexprs = constrs + j;
9876 :
9877 2326 : for (int c = 0; c < numcons; c++, j++)
9878 : {
9879 1304 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9880 :
9881 1304 : constrs[j].dobj.objType = DO_CONSTRAINT;
9882 1304 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9883 1304 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9884 1304 : AssignDumpId(&constrs[j].dobj);
9885 1304 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9886 1304 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9887 1304 : constrs[j].contable = tbinfo;
9888 1304 : constrs[j].condomain = NULL;
9889 1304 : constrs[j].contype = 'c';
9890 1304 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9891 1304 : constrs[j].confrelid = InvalidOid;
9892 1304 : constrs[j].conindex = 0;
9893 1304 : constrs[j].condeferrable = false;
9894 1304 : constrs[j].condeferred = false;
9895 1304 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9896 :
9897 : /*
9898 : * An unvalidated constraint needs to be dumped separately, so
9899 : * that potentially-violating existing data is loaded before
9900 : * the constraint.
9901 : */
9902 1304 : constrs[j].separate = !validated;
9903 :
9904 1304 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9905 :
9906 : /*
9907 : * Mark the constraint as needing to appear before the table
9908 : * --- this is so that any other dependencies of the
9909 : * constraint will be emitted before we try to create the
9910 : * table. If the constraint is to be dumped separately, it
9911 : * will be dumped after data is loaded anyway, so don't do it.
9912 : * (There's an automatic dependency in the opposite direction
9913 : * anyway, so don't need to add one manually here.)
9914 : */
9915 1304 : if (!constrs[j].separate)
9916 1174 : addObjectDependency(&tbinfo->dobj,
9917 1174 : constrs[j].dobj.dumpId);
9918 :
9919 : /*
9920 : * We will detect later whether the constraint must be split
9921 : * out from the table definition.
9922 : */
9923 : }
9924 : }
9925 :
9926 128 : PQclear(res);
9927 : }
9928 :
9929 364 : destroyPQExpBuffer(q);
9930 364 : destroyPQExpBuffer(tbloids);
9931 364 : destroyPQExpBuffer(checkoids);
9932 364 : }
9933 :
9934 : /*
9935 : * Based on the getTableAttrs query's row corresponding to one column, set
9936 : * the name and flags to handle a not-null constraint for that column in
9937 : * the tbinfo struct.
9938 : *
9939 : * Result row 'r' is for tbinfo's attribute 'j'.
9940 : *
9941 : * There are four possibilities:
9942 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9943 : * (the constraint name) remains NULL.
9944 : * 2) The column has a constraint with no name (this is the case when
9945 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9946 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9947 : * 3) The column has an invalid not-null constraint. This must be treated
9948 : * as a separate object (because it must be created after the table data
9949 : * is loaded). So we add its OID to invalidnotnulloids for processing
9950 : * elsewhere and do nothing further with it here. We distinguish this
9951 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9952 : * value, which is the constraint OID. Valid constraints have a null OID.
9953 : * 4) The column has a constraint with a known name; in that case
9954 : * notnull_constrs carries that name and dumpTableSchema will print
9955 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9956 : * (table_column_not_null) and there's no comment on the constraint,
9957 : * there's no need to print that name in the dump, so notnull_constrs
9958 : * is set to the empty string and it behaves as case 2.
9959 : *
9960 : * In a child table that inherits from a parent already containing NOT NULL
9961 : * constraints and the columns in the child don't have their own NOT NULL
9962 : * declarations, we suppress printing constraints in the child: the
9963 : * constraints are acquired at the point where the child is attached to the
9964 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9965 : * set not here but in flagInhAttrs. That flag is also used when the
9966 : * constraint was validated in a child but all its parent have it as NOT
9967 : * VALID.
9968 : *
9969 : * Any of these constraints might have the NO INHERIT bit. If so we set
9970 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9971 : *
9972 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9973 : * to do the right thing in all but the trivial case. However, the downside
9974 : * of getting it wrong is simply that the name is printed rather than
9975 : * suppressed, so it's not a big deal.
9976 : *
9977 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9978 : * constraints are found, it is initialized and filled with the array of
9979 : * OIDs of such constraints, for later processing.
9980 : */
9981 : static void
9982 49956 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9983 : TableInfo *tbinfo, int j,
9984 : int i_notnull_name,
9985 : int i_notnull_comment,
9986 : int i_notnull_invalidoid,
9987 : int i_notnull_noinherit,
9988 : int i_notnull_islocal,
9989 : PQExpBuffer *invalidnotnulloids)
9990 : {
9991 49956 : DumpOptions *dopt = fout->dopt;
9992 :
9993 : /*
9994 : * If this not-null constraint is not valid, list its OID in
9995 : * invalidnotnulloids and do nothing further. It'll be processed
9996 : * elsewhere later.
9997 : *
9998 : * Because invalid not-null constraints are rare, we don't want to malloc
9999 : * invalidnotnulloids until we're sure we're going it need it, which
10000 : * happens here.
10001 : */
10002 49956 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10003 : {
10004 152 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10005 :
10006 152 : if (*invalidnotnulloids == NULL)
10007 : {
10008 92 : *invalidnotnulloids = createPQExpBuffer();
10009 92 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10010 92 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10011 : }
10012 : else
10013 60 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10014 :
10015 : /*
10016 : * Track when a parent constraint is invalid for the cases where a
10017 : * child constraint has been validated independenly.
10018 : */
10019 152 : tbinfo->notnull_invalid[j] = true;
10020 :
10021 : /* nothing else to do */
10022 152 : tbinfo->notnull_constrs[j] = NULL;
10023 152 : return;
10024 : }
10025 :
10026 : /*
10027 : * notnull_noinh is straight from the query result. notnull_islocal also,
10028 : * though flagInhAttrs may change that one later.
10029 : */
10030 49804 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10031 49804 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10032 49804 : tbinfo->notnull_invalid[j] = false;
10033 :
10034 : /*
10035 : * Determine a constraint name to use. If the column is not marked not-
10036 : * null, we set NULL which cues ... to do nothing. An empty string says
10037 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10038 : * use.
10039 : */
10040 49804 : if (fout->remoteVersion < 180000)
10041 : {
10042 : /*
10043 : * < 18 doesn't have not-null names, so an unnamed constraint is
10044 : * sufficient.
10045 : */
10046 0 : if (PQgetisnull(res, r, i_notnull_name))
10047 0 : tbinfo->notnull_constrs[j] = NULL;
10048 : else
10049 0 : tbinfo->notnull_constrs[j] = "";
10050 : }
10051 : else
10052 : {
10053 49804 : if (PQgetisnull(res, r, i_notnull_name))
10054 44450 : tbinfo->notnull_constrs[j] = NULL;
10055 : else
10056 : {
10057 : /*
10058 : * In binary upgrade of inheritance child tables, must have a
10059 : * constraint name that we can UPDATE later; same if there's a
10060 : * comment on the constraint.
10061 : */
10062 5354 : if ((dopt->binary_upgrade &&
10063 652 : !tbinfo->ispartition &&
10064 5850 : !tbinfo->notnull_islocal) ||
10065 5354 : !PQgetisnull(res, r, i_notnull_comment))
10066 : {
10067 102 : tbinfo->notnull_constrs[j] =
10068 102 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10069 : }
10070 : else
10071 : {
10072 : char *default_name;
10073 :
10074 : /* XXX should match ChooseConstraintName better */
10075 5252 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10076 5252 : tbinfo->attnames[j]);
10077 5252 : if (strcmp(default_name,
10078 5252 : PQgetvalue(res, r, i_notnull_name)) == 0)
10079 3452 : tbinfo->notnull_constrs[j] = "";
10080 : else
10081 : {
10082 1800 : tbinfo->notnull_constrs[j] =
10083 1800 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10084 : }
10085 5252 : free(default_name);
10086 : }
10087 : }
10088 : }
10089 : }
10090 :
10091 : /*
10092 : * Test whether a column should be printed as part of table's CREATE TABLE.
10093 : * Column number is zero-based.
10094 : *
10095 : * Normally this is always true, but it's false for dropped columns, as well
10096 : * as those that were inherited without any local definition. (If we print
10097 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10098 : * For partitions, it's always true, because we want the partitions to be
10099 : * created independently and ATTACH PARTITION used afterwards.
10100 : *
10101 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10102 : * attisdropped state later, so as to keep control of the physical column
10103 : * order.
10104 : *
10105 : * This function exists because there are scattered nonobvious places that
10106 : * must be kept in sync with this decision.
10107 : */
10108 : bool
10109 81326 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10110 : {
10111 81326 : if (dopt->binary_upgrade)
10112 12436 : return true;
10113 68890 : if (tbinfo->attisdropped[colno])
10114 1464 : return false;
10115 67426 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10116 : }
10117 :
10118 :
10119 : /*
10120 : * getTSParsers:
10121 : * get information about all text search parsers in the system catalogs
10122 : */
10123 : void
10124 364 : getTSParsers(Archive *fout)
10125 : {
10126 : PGresult *res;
10127 : int ntups;
10128 : int i;
10129 : PQExpBuffer query;
10130 : TSParserInfo *prsinfo;
10131 : int i_tableoid;
10132 : int i_oid;
10133 : int i_prsname;
10134 : int i_prsnamespace;
10135 : int i_prsstart;
10136 : int i_prstoken;
10137 : int i_prsend;
10138 : int i_prsheadline;
10139 : int i_prslextype;
10140 :
10141 364 : query = createPQExpBuffer();
10142 :
10143 : /*
10144 : * find all text search objects, including builtin ones; we filter out
10145 : * system-defined objects at dump-out time.
10146 : */
10147 :
10148 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10149 : "prsstart::oid, prstoken::oid, "
10150 : "prsend::oid, prsheadline::oid, prslextype::oid "
10151 : "FROM pg_ts_parser");
10152 :
10153 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10154 :
10155 364 : ntups = PQntuples(res);
10156 :
10157 364 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10158 :
10159 364 : i_tableoid = PQfnumber(res, "tableoid");
10160 364 : i_oid = PQfnumber(res, "oid");
10161 364 : i_prsname = PQfnumber(res, "prsname");
10162 364 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10163 364 : i_prsstart = PQfnumber(res, "prsstart");
10164 364 : i_prstoken = PQfnumber(res, "prstoken");
10165 364 : i_prsend = PQfnumber(res, "prsend");
10166 364 : i_prsheadline = PQfnumber(res, "prsheadline");
10167 364 : i_prslextype = PQfnumber(res, "prslextype");
10168 :
10169 824 : for (i = 0; i < ntups; i++)
10170 : {
10171 460 : prsinfo[i].dobj.objType = DO_TSPARSER;
10172 460 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10173 460 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10174 460 : AssignDumpId(&prsinfo[i].dobj);
10175 460 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10176 920 : prsinfo[i].dobj.namespace =
10177 460 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10178 460 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10179 460 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10180 460 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10181 460 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10182 460 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10183 :
10184 : /* Decide whether we want to dump it */
10185 460 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10186 : }
10187 :
10188 364 : PQclear(res);
10189 :
10190 364 : destroyPQExpBuffer(query);
10191 364 : }
10192 :
10193 : /*
10194 : * getTSDictionaries:
10195 : * get information about all text search dictionaries in the system catalogs
10196 : */
10197 : void
10198 364 : getTSDictionaries(Archive *fout)
10199 : {
10200 : PGresult *res;
10201 : int ntups;
10202 : int i;
10203 : PQExpBuffer query;
10204 : TSDictInfo *dictinfo;
10205 : int i_tableoid;
10206 : int i_oid;
10207 : int i_dictname;
10208 : int i_dictnamespace;
10209 : int i_dictowner;
10210 : int i_dicttemplate;
10211 : int i_dictinitoption;
10212 :
10213 364 : query = createPQExpBuffer();
10214 :
10215 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10216 : "dictnamespace, dictowner, "
10217 : "dicttemplate, dictinitoption "
10218 : "FROM pg_ts_dict");
10219 :
10220 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10221 :
10222 364 : ntups = PQntuples(res);
10223 :
10224 364 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10225 :
10226 364 : i_tableoid = PQfnumber(res, "tableoid");
10227 364 : i_oid = PQfnumber(res, "oid");
10228 364 : i_dictname = PQfnumber(res, "dictname");
10229 364 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10230 364 : i_dictowner = PQfnumber(res, "dictowner");
10231 364 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10232 364 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10233 :
10234 11506 : for (i = 0; i < ntups; i++)
10235 : {
10236 11142 : dictinfo[i].dobj.objType = DO_TSDICT;
10237 11142 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10238 11142 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10239 11142 : AssignDumpId(&dictinfo[i].dobj);
10240 11142 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10241 22284 : dictinfo[i].dobj.namespace =
10242 11142 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10243 11142 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10244 11142 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10245 11142 : if (PQgetisnull(res, i, i_dictinitoption))
10246 460 : dictinfo[i].dictinitoption = NULL;
10247 : else
10248 10682 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10249 :
10250 : /* Decide whether we want to dump it */
10251 11142 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10252 : }
10253 :
10254 364 : PQclear(res);
10255 :
10256 364 : destroyPQExpBuffer(query);
10257 364 : }
10258 :
10259 : /*
10260 : * getTSTemplates:
10261 : * get information about all text search templates in the system catalogs
10262 : */
10263 : void
10264 364 : getTSTemplates(Archive *fout)
10265 : {
10266 : PGresult *res;
10267 : int ntups;
10268 : int i;
10269 : PQExpBuffer query;
10270 : TSTemplateInfo *tmplinfo;
10271 : int i_tableoid;
10272 : int i_oid;
10273 : int i_tmplname;
10274 : int i_tmplnamespace;
10275 : int i_tmplinit;
10276 : int i_tmpllexize;
10277 :
10278 364 : query = createPQExpBuffer();
10279 :
10280 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10281 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10282 : "FROM pg_ts_template");
10283 :
10284 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10285 :
10286 364 : ntups = PQntuples(res);
10287 :
10288 364 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10289 :
10290 364 : i_tableoid = PQfnumber(res, "tableoid");
10291 364 : i_oid = PQfnumber(res, "oid");
10292 364 : i_tmplname = PQfnumber(res, "tmplname");
10293 364 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10294 364 : i_tmplinit = PQfnumber(res, "tmplinit");
10295 364 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10296 :
10297 2280 : for (i = 0; i < ntups; i++)
10298 : {
10299 1916 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10300 1916 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10301 1916 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10302 1916 : AssignDumpId(&tmplinfo[i].dobj);
10303 1916 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10304 3832 : tmplinfo[i].dobj.namespace =
10305 1916 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10306 1916 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10307 1916 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10308 :
10309 : /* Decide whether we want to dump it */
10310 1916 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10311 : }
10312 :
10313 364 : PQclear(res);
10314 :
10315 364 : destroyPQExpBuffer(query);
10316 364 : }
10317 :
10318 : /*
10319 : * getTSConfigurations:
10320 : * get information about all text search configurations
10321 : */
10322 : void
10323 364 : getTSConfigurations(Archive *fout)
10324 : {
10325 : PGresult *res;
10326 : int ntups;
10327 : int i;
10328 : PQExpBuffer query;
10329 : TSConfigInfo *cfginfo;
10330 : int i_tableoid;
10331 : int i_oid;
10332 : int i_cfgname;
10333 : int i_cfgnamespace;
10334 : int i_cfgowner;
10335 : int i_cfgparser;
10336 :
10337 364 : query = createPQExpBuffer();
10338 :
10339 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10340 : "cfgnamespace, cfgowner, cfgparser "
10341 : "FROM pg_ts_config");
10342 :
10343 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10344 :
10345 364 : ntups = PQntuples(res);
10346 :
10347 364 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10348 :
10349 364 : i_tableoid = PQfnumber(res, "tableoid");
10350 364 : i_oid = PQfnumber(res, "oid");
10351 364 : i_cfgname = PQfnumber(res, "cfgname");
10352 364 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10353 364 : i_cfgowner = PQfnumber(res, "cfgowner");
10354 364 : i_cfgparser = PQfnumber(res, "cfgparser");
10355 :
10356 11436 : for (i = 0; i < ntups; i++)
10357 : {
10358 11072 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10359 11072 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10360 11072 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10361 11072 : AssignDumpId(&cfginfo[i].dobj);
10362 11072 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10363 22144 : cfginfo[i].dobj.namespace =
10364 11072 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10365 11072 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10366 11072 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10367 :
10368 : /* Decide whether we want to dump it */
10369 11072 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10370 : }
10371 :
10372 364 : PQclear(res);
10373 :
10374 364 : destroyPQExpBuffer(query);
10375 364 : }
10376 :
10377 : /*
10378 : * getForeignDataWrappers:
10379 : * get information about all foreign-data wrappers in the system catalogs
10380 : */
10381 : void
10382 364 : getForeignDataWrappers(Archive *fout)
10383 : {
10384 : PGresult *res;
10385 : int ntups;
10386 : int i;
10387 : PQExpBuffer query;
10388 : FdwInfo *fdwinfo;
10389 : int i_tableoid;
10390 : int i_oid;
10391 : int i_fdwname;
10392 : int i_fdwowner;
10393 : int i_fdwhandler;
10394 : int i_fdwvalidator;
10395 : int i_fdwacl;
10396 : int i_acldefault;
10397 : int i_fdwoptions;
10398 :
10399 364 : query = createPQExpBuffer();
10400 :
10401 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10402 : "fdwowner, "
10403 : "fdwhandler::pg_catalog.regproc, "
10404 : "fdwvalidator::pg_catalog.regproc, "
10405 : "fdwacl, "
10406 : "acldefault('F', fdwowner) AS acldefault, "
10407 : "array_to_string(ARRAY("
10408 : "SELECT quote_ident(option_name) || ' ' || "
10409 : "quote_literal(option_value) "
10410 : "FROM pg_options_to_table(fdwoptions) "
10411 : "ORDER BY option_name"
10412 : "), E',\n ') AS fdwoptions "
10413 : "FROM pg_foreign_data_wrapper");
10414 :
10415 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10416 :
10417 364 : ntups = PQntuples(res);
10418 :
10419 364 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10420 :
10421 364 : i_tableoid = PQfnumber(res, "tableoid");
10422 364 : i_oid = PQfnumber(res, "oid");
10423 364 : i_fdwname = PQfnumber(res, "fdwname");
10424 364 : i_fdwowner = PQfnumber(res, "fdwowner");
10425 364 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10426 364 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10427 364 : i_fdwacl = PQfnumber(res, "fdwacl");
10428 364 : i_acldefault = PQfnumber(res, "acldefault");
10429 364 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10430 :
10431 512 : for (i = 0; i < ntups; i++)
10432 : {
10433 148 : fdwinfo[i].dobj.objType = DO_FDW;
10434 148 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10435 148 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10436 148 : AssignDumpId(&fdwinfo[i].dobj);
10437 148 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10438 148 : fdwinfo[i].dobj.namespace = NULL;
10439 148 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10440 148 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10441 148 : fdwinfo[i].dacl.privtype = 0;
10442 148 : fdwinfo[i].dacl.initprivs = NULL;
10443 148 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10444 148 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10445 148 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10446 148 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10447 :
10448 : /* Decide whether we want to dump it */
10449 148 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10450 :
10451 : /* Mark whether FDW has an ACL */
10452 148 : if (!PQgetisnull(res, i, i_fdwacl))
10453 96 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10454 : }
10455 :
10456 364 : PQclear(res);
10457 :
10458 364 : destroyPQExpBuffer(query);
10459 364 : }
10460 :
10461 : /*
10462 : * getForeignServers:
10463 : * get information about all foreign servers in the system catalogs
10464 : */
10465 : void
10466 364 : getForeignServers(Archive *fout)
10467 : {
10468 : PGresult *res;
10469 : int ntups;
10470 : int i;
10471 : PQExpBuffer query;
10472 : ForeignServerInfo *srvinfo;
10473 : int i_tableoid;
10474 : int i_oid;
10475 : int i_srvname;
10476 : int i_srvowner;
10477 : int i_srvfdw;
10478 : int i_srvtype;
10479 : int i_srvversion;
10480 : int i_srvacl;
10481 : int i_acldefault;
10482 : int i_srvoptions;
10483 :
10484 364 : query = createPQExpBuffer();
10485 :
10486 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10487 : "srvowner, "
10488 : "srvfdw, srvtype, srvversion, srvacl, "
10489 : "acldefault('S', srvowner) AS acldefault, "
10490 : "array_to_string(ARRAY("
10491 : "SELECT quote_ident(option_name) || ' ' || "
10492 : "quote_literal(option_value) "
10493 : "FROM pg_options_to_table(srvoptions) "
10494 : "ORDER BY option_name"
10495 : "), E',\n ') AS srvoptions "
10496 : "FROM pg_foreign_server");
10497 :
10498 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10499 :
10500 364 : ntups = PQntuples(res);
10501 :
10502 364 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10503 :
10504 364 : i_tableoid = PQfnumber(res, "tableoid");
10505 364 : i_oid = PQfnumber(res, "oid");
10506 364 : i_srvname = PQfnumber(res, "srvname");
10507 364 : i_srvowner = PQfnumber(res, "srvowner");
10508 364 : i_srvfdw = PQfnumber(res, "srvfdw");
10509 364 : i_srvtype = PQfnumber(res, "srvtype");
10510 364 : i_srvversion = PQfnumber(res, "srvversion");
10511 364 : i_srvacl = PQfnumber(res, "srvacl");
10512 364 : i_acldefault = PQfnumber(res, "acldefault");
10513 364 : i_srvoptions = PQfnumber(res, "srvoptions");
10514 :
10515 520 : for (i = 0; i < ntups; i++)
10516 : {
10517 156 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10518 156 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10519 156 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10520 156 : AssignDumpId(&srvinfo[i].dobj);
10521 156 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10522 156 : srvinfo[i].dobj.namespace = NULL;
10523 156 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10524 156 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10525 156 : srvinfo[i].dacl.privtype = 0;
10526 156 : srvinfo[i].dacl.initprivs = NULL;
10527 156 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10528 156 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10529 156 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10530 156 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10531 156 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10532 :
10533 : /* Decide whether we want to dump it */
10534 156 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10535 :
10536 : /* Servers have user mappings */
10537 156 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10538 :
10539 : /* Mark whether server has an ACL */
10540 156 : if (!PQgetisnull(res, i, i_srvacl))
10541 96 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10542 : }
10543 :
10544 364 : PQclear(res);
10545 :
10546 364 : destroyPQExpBuffer(query);
10547 364 : }
10548 :
10549 : /*
10550 : * getDefaultACLs:
10551 : * get information about all default ACL information in the system catalogs
10552 : */
10553 : void
10554 364 : getDefaultACLs(Archive *fout)
10555 : {
10556 364 : DumpOptions *dopt = fout->dopt;
10557 : DefaultACLInfo *daclinfo;
10558 : PQExpBuffer query;
10559 : PGresult *res;
10560 : int i_oid;
10561 : int i_tableoid;
10562 : int i_defaclrole;
10563 : int i_defaclnamespace;
10564 : int i_defaclobjtype;
10565 : int i_defaclacl;
10566 : int i_acldefault;
10567 : int i,
10568 : ntups;
10569 :
10570 364 : query = createPQExpBuffer();
10571 :
10572 : /*
10573 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10574 : * ACL for their object type. We should dump them as deltas from the
10575 : * default ACL, since that will be used as a starting point for
10576 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10577 : * non-global entries can only add privileges not revoke them. We must
10578 : * dump those as-is (i.e., as deltas from an empty ACL).
10579 : *
10580 : * We can use defaclobjtype as the object type for acldefault(), except
10581 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10582 : * 's'.
10583 : */
10584 364 : appendPQExpBufferStr(query,
10585 : "SELECT oid, tableoid, "
10586 : "defaclrole, "
10587 : "defaclnamespace, "
10588 : "defaclobjtype, "
10589 : "defaclacl, "
10590 : "CASE WHEN defaclnamespace = 0 THEN "
10591 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10592 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10593 : "defaclrole) ELSE '{}' END AS acldefault "
10594 : "FROM pg_default_acl");
10595 :
10596 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10597 :
10598 364 : ntups = PQntuples(res);
10599 :
10600 364 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10601 :
10602 364 : i_oid = PQfnumber(res, "oid");
10603 364 : i_tableoid = PQfnumber(res, "tableoid");
10604 364 : i_defaclrole = PQfnumber(res, "defaclrole");
10605 364 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10606 364 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10607 364 : i_defaclacl = PQfnumber(res, "defaclacl");
10608 364 : i_acldefault = PQfnumber(res, "acldefault");
10609 :
10610 776 : for (i = 0; i < ntups; i++)
10611 : {
10612 412 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10613 :
10614 412 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10615 412 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10616 412 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10617 412 : AssignDumpId(&daclinfo[i].dobj);
10618 : /* cheesy ... is it worth coming up with a better object name? */
10619 412 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10620 :
10621 412 : if (nspid != InvalidOid)
10622 192 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10623 : else
10624 220 : daclinfo[i].dobj.namespace = NULL;
10625 :
10626 412 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10627 412 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10628 412 : daclinfo[i].dacl.privtype = 0;
10629 412 : daclinfo[i].dacl.initprivs = NULL;
10630 412 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10631 412 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10632 :
10633 : /* Default ACLs are ACLs, of course */
10634 412 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10635 :
10636 : /* Decide whether we want to dump it */
10637 412 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10638 : }
10639 :
10640 364 : PQclear(res);
10641 :
10642 364 : destroyPQExpBuffer(query);
10643 364 : }
10644 :
10645 : /*
10646 : * getRoleName -- look up the name of a role, given its OID
10647 : *
10648 : * In current usage, we don't expect failures, so error out for a bad OID.
10649 : */
10650 : static const char *
10651 1153400 : getRoleName(const char *roleoid_str)
10652 : {
10653 1153400 : Oid roleoid = atooid(roleoid_str);
10654 :
10655 : /*
10656 : * Do binary search to find the appropriate item.
10657 : */
10658 1153400 : if (nrolenames > 0)
10659 : {
10660 1153400 : RoleNameItem *low = &rolenames[0];
10661 1153400 : RoleNameItem *high = &rolenames[nrolenames - 1];
10662 :
10663 4613376 : while (low <= high)
10664 : {
10665 4613376 : RoleNameItem *middle = low + (high - low) / 2;
10666 :
10667 4613376 : if (roleoid < middle->roleoid)
10668 3457798 : high = middle - 1;
10669 1155578 : else if (roleoid > middle->roleoid)
10670 2178 : low = middle + 1;
10671 : else
10672 1153400 : return middle->rolename; /* found a match */
10673 : }
10674 : }
10675 :
10676 0 : pg_fatal("role with OID %u does not exist", roleoid);
10677 : return NULL; /* keep compiler quiet */
10678 : }
10679 :
10680 : /*
10681 : * collectRoleNames --
10682 : *
10683 : * Construct a table of all known roles.
10684 : * The table is sorted by OID for speed in lookup.
10685 : */
10686 : static void
10687 366 : collectRoleNames(Archive *fout)
10688 : {
10689 : PGresult *res;
10690 : const char *query;
10691 : int i;
10692 :
10693 366 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10694 :
10695 366 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10696 :
10697 366 : nrolenames = PQntuples(res);
10698 :
10699 366 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10700 :
10701 7216 : for (i = 0; i < nrolenames; i++)
10702 : {
10703 6850 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10704 6850 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10705 : }
10706 :
10707 366 : PQclear(res);
10708 366 : }
10709 :
10710 : /*
10711 : * getAdditionalACLs
10712 : *
10713 : * We have now created all the DumpableObjects, and collected the ACL data
10714 : * that appears in the directly-associated catalog entries. However, there's
10715 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10716 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10717 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10718 : * Also, in versions having the pg_init_privs catalog, read that and load the
10719 : * information into the relevant DumpableObjects.
10720 : */
10721 : static void
10722 360 : getAdditionalACLs(Archive *fout)
10723 : {
10724 360 : PQExpBuffer query = createPQExpBuffer();
10725 : PGresult *res;
10726 : int ntups,
10727 : i;
10728 :
10729 : /* Check for per-column ACLs */
10730 360 : appendPQExpBufferStr(query,
10731 : "SELECT DISTINCT attrelid FROM pg_attribute "
10732 : "WHERE attacl IS NOT NULL");
10733 :
10734 360 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10735 :
10736 360 : ntups = PQntuples(res);
10737 1078 : for (i = 0; i < ntups; i++)
10738 : {
10739 718 : Oid relid = atooid(PQgetvalue(res, i, 0));
10740 : TableInfo *tblinfo;
10741 :
10742 718 : tblinfo = findTableByOid(relid);
10743 : /* OK to ignore tables we haven't got a DumpableObject for */
10744 718 : if (tblinfo)
10745 : {
10746 718 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10747 718 : tblinfo->hascolumnACLs = true;
10748 : }
10749 : }
10750 360 : PQclear(res);
10751 :
10752 : /* Fetch initial-privileges data */
10753 360 : if (fout->remoteVersion >= 90600)
10754 : {
10755 360 : printfPQExpBuffer(query,
10756 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10757 : "FROM pg_init_privs");
10758 :
10759 360 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10760 :
10761 360 : ntups = PQntuples(res);
10762 85216 : for (i = 0; i < ntups; i++)
10763 : {
10764 84856 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10765 84856 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10766 84856 : int objsubid = atoi(PQgetvalue(res, i, 2));
10767 84856 : char privtype = *(PQgetvalue(res, i, 3));
10768 84856 : char *initprivs = PQgetvalue(res, i, 4);
10769 : CatalogId objId;
10770 : DumpableObject *dobj;
10771 :
10772 84856 : objId.tableoid = classoid;
10773 84856 : objId.oid = objoid;
10774 84856 : dobj = findObjectByCatalogId(objId);
10775 : /* OK to ignore entries we haven't got a DumpableObject for */
10776 84856 : if (dobj)
10777 : {
10778 : /* Cope with sub-object initprivs */
10779 60830 : if (objsubid != 0)
10780 : {
10781 7248 : if (dobj->objType == DO_TABLE)
10782 : {
10783 : /* For a column initprivs, set the table's ACL flags */
10784 7248 : dobj->components |= DUMP_COMPONENT_ACL;
10785 7248 : ((TableInfo *) dobj)->hascolumnACLs = true;
10786 : }
10787 : else
10788 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10789 : classoid, objoid, objsubid);
10790 7600 : continue;
10791 : }
10792 :
10793 : /*
10794 : * We ignore any pg_init_privs.initprivs entry for the public
10795 : * schema, as explained in getNamespaces().
10796 : */
10797 53582 : if (dobj->objType == DO_NAMESPACE &&
10798 712 : strcmp(dobj->name, "public") == 0)
10799 352 : continue;
10800 :
10801 : /* Else it had better be of a type we think has ACLs */
10802 53230 : if (dobj->objType == DO_NAMESPACE ||
10803 52870 : dobj->objType == DO_TYPE ||
10804 52822 : dobj->objType == DO_FUNC ||
10805 52632 : dobj->objType == DO_AGG ||
10806 52584 : dobj->objType == DO_TABLE ||
10807 0 : dobj->objType == DO_PROCLANG ||
10808 0 : dobj->objType == DO_FDW ||
10809 0 : dobj->objType == DO_FOREIGN_SERVER)
10810 53230 : {
10811 53230 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10812 :
10813 53230 : daobj->dacl.privtype = privtype;
10814 53230 : daobj->dacl.initprivs = pstrdup(initprivs);
10815 : }
10816 : else
10817 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10818 : classoid, objoid, objsubid);
10819 : }
10820 : }
10821 360 : PQclear(res);
10822 : }
10823 :
10824 360 : destroyPQExpBuffer(query);
10825 360 : }
10826 :
10827 : /*
10828 : * dumpCommentExtended --
10829 : *
10830 : * This routine is used to dump any comments associated with the
10831 : * object handed to this routine. The routine takes the object type
10832 : * and object name (ready to print, except for schema decoration), plus
10833 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10834 : * plus catalog ID and subid which are the lookup key for pg_description,
10835 : * plus the dump ID for the object (for setting a dependency).
10836 : * If a matching pg_description entry is found, it is dumped.
10837 : *
10838 : * Note: in some cases, such as comments for triggers and rules, the "type"
10839 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10840 : * but it doesn't seem worth complicating the API for all callers to make
10841 : * it cleaner.
10842 : *
10843 : * Note: although this routine takes a dumpId for dependency purposes,
10844 : * that purpose is just to mark the dependency in the emitted dump file
10845 : * for possible future use by pg_restore. We do NOT use it for determining
10846 : * ordering of the comment in the dump file, because this routine is called
10847 : * after dependency sorting occurs. This routine should be called just after
10848 : * calling ArchiveEntry() for the specified object.
10849 : */
10850 : static void
10851 12996 : dumpCommentExtended(Archive *fout, const char *type,
10852 : const char *name, const char *namespace,
10853 : const char *owner, CatalogId catalogId,
10854 : int subid, DumpId dumpId,
10855 : const char *initdb_comment)
10856 : {
10857 12996 : DumpOptions *dopt = fout->dopt;
10858 : CommentItem *comments;
10859 : int ncomments;
10860 :
10861 : /* do nothing, if --no-comments is supplied */
10862 12996 : if (dopt->no_comments)
10863 0 : return;
10864 :
10865 : /* Comments are schema not data ... except LO comments are data */
10866 12996 : if (strcmp(type, "LARGE OBJECT") != 0)
10867 : {
10868 12872 : if (!dopt->dumpSchema)
10869 0 : return;
10870 : }
10871 : else
10872 : {
10873 : /* We do dump LO comments in binary-upgrade mode */
10874 124 : if (!dopt->dumpData && !dopt->binary_upgrade)
10875 0 : return;
10876 : }
10877 :
10878 : /* Search for comments associated with catalogId, using table */
10879 12996 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10880 : &comments);
10881 :
10882 : /* Is there one matching the subid? */
10883 12996 : while (ncomments > 0)
10884 : {
10885 12898 : if (comments->objsubid == subid)
10886 12898 : break;
10887 0 : comments++;
10888 0 : ncomments--;
10889 : }
10890 :
10891 12996 : if (initdb_comment != NULL)
10892 : {
10893 : static CommentItem empty_comment = {.descr = ""};
10894 :
10895 : /*
10896 : * initdb creates this object with a comment. Skip dumping the
10897 : * initdb-provided comment, which would complicate matters for
10898 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10899 : * comment, replicate that.
10900 : */
10901 226 : if (ncomments == 0)
10902 : {
10903 8 : comments = &empty_comment;
10904 8 : ncomments = 1;
10905 : }
10906 218 : else if (strcmp(comments->descr, initdb_comment) == 0)
10907 218 : ncomments = 0;
10908 : }
10909 :
10910 : /* If a comment exists, build COMMENT ON statement */
10911 12996 : if (ncomments > 0)
10912 : {
10913 12688 : PQExpBuffer query = createPQExpBuffer();
10914 12688 : PQExpBuffer tag = createPQExpBuffer();
10915 :
10916 12688 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10917 12688 : if (namespace && *namespace)
10918 12316 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10919 12688 : appendPQExpBuffer(query, "%s IS ", name);
10920 12688 : appendStringLiteralAH(query, comments->descr, fout);
10921 12688 : appendPQExpBufferStr(query, ";\n");
10922 :
10923 12688 : appendPQExpBuffer(tag, "%s %s", type, name);
10924 :
10925 : /*
10926 : * We mark comments as SECTION_NONE because they really belong in the
10927 : * same section as their parent, whether that is pre-data or
10928 : * post-data.
10929 : */
10930 12688 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10931 12688 : ARCHIVE_OPTS(.tag = tag->data,
10932 : .namespace = namespace,
10933 : .owner = owner,
10934 : .description = "COMMENT",
10935 : .section = SECTION_NONE,
10936 : .createStmt = query->data,
10937 : .deps = &dumpId,
10938 : .nDeps = 1));
10939 :
10940 12688 : destroyPQExpBuffer(query);
10941 12688 : destroyPQExpBuffer(tag);
10942 : }
10943 : }
10944 :
10945 : /*
10946 : * dumpComment --
10947 : *
10948 : * Typical simplification of the above function.
10949 : */
10950 : static inline void
10951 12692 : dumpComment(Archive *fout, const char *type,
10952 : const char *name, const char *namespace,
10953 : const char *owner, CatalogId catalogId,
10954 : int subid, DumpId dumpId)
10955 : {
10956 12692 : dumpCommentExtended(fout, type, name, namespace, owner,
10957 : catalogId, subid, dumpId, NULL);
10958 12692 : }
10959 :
10960 : /*
10961 : * appendNamedArgument --
10962 : *
10963 : * Convenience routine for constructing parameters of the form:
10964 : * 'paraname', 'value'::type
10965 : */
10966 : static void
10967 10370 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10968 : const char *argtype, const char *argval)
10969 : {
10970 10370 : appendPQExpBufferStr(out, ",\n\t");
10971 :
10972 10370 : appendStringLiteralAH(out, argname, fout);
10973 10370 : appendPQExpBufferStr(out, ", ");
10974 :
10975 10370 : appendStringLiteralAH(out, argval, fout);
10976 10370 : appendPQExpBuffer(out, "::%s", argtype);
10977 10370 : }
10978 :
10979 : /*
10980 : * fetchAttributeStats --
10981 : *
10982 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10983 : */
10984 : static PGresult *
10985 2234 : fetchAttributeStats(Archive *fout)
10986 : {
10987 2234 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10988 2234 : PQExpBuffer nspnames = createPQExpBuffer();
10989 2234 : PQExpBuffer relnames = createPQExpBuffer();
10990 2234 : int count = 0;
10991 2234 : PGresult *res = NULL;
10992 : static TocEntry *te;
10993 : static bool restarted;
10994 2234 : int max_rels = MAX_ATTR_STATS_RELS;
10995 :
10996 : /*
10997 : * Our query for retrieving statistics for multiple relations uses WITH
10998 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10999 : * in v9.4. For older versions, we resort to gathering statistics for a
11000 : * single relation at a time.
11001 : */
11002 2234 : if (fout->remoteVersion < 90400)
11003 0 : max_rels = 1;
11004 :
11005 : /* If we're just starting, set our TOC pointer. */
11006 2234 : if (!te)
11007 114 : te = AH->toc->next;
11008 :
11009 : /*
11010 : * We can't easily avoid a second TOC scan for the tar format because it
11011 : * writes restore.sql separately, which means we must execute the queries
11012 : * twice. This feels risky, but there is no known reason it should
11013 : * generate different output than the first pass. Even if it does, the
11014 : * worst-case scenario is that restore.sql might have different statistics
11015 : * data than the archive.
11016 : */
11017 2234 : if (!restarted && te == AH->toc && AH->format == archTar)
11018 : {
11019 2 : te = AH->toc->next;
11020 2 : restarted = true;
11021 : }
11022 :
11023 2234 : appendPQExpBufferChar(nspnames, '{');
11024 2234 : appendPQExpBufferChar(relnames, '{');
11025 :
11026 : /*
11027 : * Scan the TOC for the next set of relevant stats entries. We assume
11028 : * that statistics are dumped in the order they are listed in the TOC.
11029 : * This is perhaps not the sturdiest assumption, so we verify it matches
11030 : * reality in dumpRelationStats_dumper().
11031 : */
11032 33864 : for (; te != AH->toc && count < max_rels; te = te->next)
11033 : {
11034 31630 : if ((te->reqs & REQ_STATS) != 0 &&
11035 7008 : strcmp(te->desc, "STATISTICS DATA") == 0)
11036 : {
11037 7008 : appendPGArray(nspnames, te->namespace);
11038 7008 : appendPGArray(relnames, te->tag);
11039 7008 : count++;
11040 : }
11041 : }
11042 :
11043 2234 : appendPQExpBufferChar(nspnames, '}');
11044 2234 : appendPQExpBufferChar(relnames, '}');
11045 :
11046 : /* Execute the query for the next batch of relations. */
11047 2234 : if (count > 0)
11048 : {
11049 206 : PQExpBuffer query = createPQExpBuffer();
11050 :
11051 206 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11052 206 : appendStringLiteralAH(query, nspnames->data, fout);
11053 206 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11054 206 : appendStringLiteralAH(query, relnames->data, fout);
11055 206 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11056 206 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11057 206 : destroyPQExpBuffer(query);
11058 : }
11059 :
11060 2234 : destroyPQExpBuffer(nspnames);
11061 2234 : destroyPQExpBuffer(relnames);
11062 2234 : return res;
11063 : }
11064 :
11065 : /*
11066 : * dumpRelationStats_dumper --
11067 : *
11068 : * Generate command to import stats into the relation on the new database.
11069 : * This routine is called by the Archiver when it wants the statistics to be
11070 : * dumped.
11071 : */
11072 : static char *
11073 7008 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11074 : {
11075 7008 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11076 : static PGresult *res;
11077 : static int rownum;
11078 : PQExpBuffer query;
11079 : PQExpBufferData out_data;
11080 7008 : PQExpBuffer out = &out_data;
11081 : int i_schemaname;
11082 : int i_tablename;
11083 : int i_attname;
11084 : int i_inherited;
11085 : int i_null_frac;
11086 : int i_avg_width;
11087 : int i_n_distinct;
11088 : int i_most_common_vals;
11089 : int i_most_common_freqs;
11090 : int i_histogram_bounds;
11091 : int i_correlation;
11092 : int i_most_common_elems;
11093 : int i_most_common_elem_freqs;
11094 : int i_elem_count_histogram;
11095 : int i_range_length_histogram;
11096 : int i_range_empty_frac;
11097 : int i_range_bounds_histogram;
11098 : static TocEntry *expected_te;
11099 :
11100 : /*
11101 : * fetchAttributeStats() assumes that the statistics are dumped in the
11102 : * order they are listed in the TOC. We verify that here for safety.
11103 : */
11104 7008 : if (!expected_te)
11105 114 : expected_te = ((ArchiveHandle *) fout)->toc;
11106 :
11107 7008 : expected_te = expected_te->next;
11108 27938 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11109 7008 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11110 20930 : expected_te = expected_te->next;
11111 :
11112 7008 : if (te != expected_te)
11113 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11114 : te->dumpId, te->desc, te->tag,
11115 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11116 :
11117 7008 : query = createPQExpBuffer();
11118 7008 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11119 : {
11120 114 : appendPQExpBufferStr(query,
11121 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11122 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11123 : "s.null_frac, s.avg_width, s.n_distinct, "
11124 : "s.most_common_vals, s.most_common_freqs, "
11125 : "s.histogram_bounds, s.correlation, "
11126 : "s.most_common_elems, s.most_common_elem_freqs, "
11127 : "s.elem_count_histogram, ");
11128 :
11129 114 : if (fout->remoteVersion >= 170000)
11130 114 : appendPQExpBufferStr(query,
11131 : "s.range_length_histogram, "
11132 : "s.range_empty_frac, "
11133 : "s.range_bounds_histogram ");
11134 : else
11135 0 : appendPQExpBufferStr(query,
11136 : "NULL AS range_length_histogram,"
11137 : "NULL AS range_empty_frac,"
11138 : "NULL AS range_bounds_histogram ");
11139 :
11140 : /*
11141 : * The results must be in the order of the relations supplied in the
11142 : * parameters to ensure we remain in sync as we walk through the TOC.
11143 : * The redundant filter clause on s.tablename = ANY(...) seems
11144 : * sufficient to convince the planner to use
11145 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11146 : * This may not work for all versions.
11147 : *
11148 : * Our query for retrieving statistics for multiple relations uses
11149 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11150 : * introduced in v9.4. For older versions, we resort to gathering
11151 : * statistics for a single relation at a time.
11152 : */
11153 114 : if (fout->remoteVersion >= 90400)
11154 114 : appendPQExpBufferStr(query,
11155 : "FROM pg_catalog.pg_stats s "
11156 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11157 : "ON s.schemaname = u.schemaname "
11158 : "AND s.tablename = u.tablename "
11159 : "WHERE s.tablename = ANY($2) "
11160 : "ORDER BY u.ord, s.attname, s.inherited");
11161 : else
11162 0 : appendPQExpBufferStr(query,
11163 : "FROM pg_catalog.pg_stats s "
11164 : "WHERE s.schemaname = $1[1] "
11165 : "AND s.tablename = $2[1] "
11166 : "ORDER BY s.attname, s.inherited");
11167 :
11168 114 : ExecuteSqlStatement(fout, query->data);
11169 :
11170 114 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11171 114 : resetPQExpBuffer(query);
11172 : }
11173 :
11174 7008 : initPQExpBuffer(out);
11175 :
11176 : /* restore relation stats */
11177 7008 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11178 7008 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11179 : fout->remoteVersion);
11180 7008 : appendPQExpBufferStr(out, "\t'schemaname', ");
11181 7008 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11182 7008 : appendPQExpBufferStr(out, ",\n");
11183 7008 : appendPQExpBufferStr(out, "\t'relname', ");
11184 7008 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11185 7008 : appendPQExpBufferStr(out, ",\n");
11186 7008 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11187 :
11188 : /*
11189 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11190 : * the relation is empty, or it could mean that it hadn't yet been
11191 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11192 : * This ambiguity allegedly can cause the planner to choose inefficient
11193 : * plans after restoring to v18 or newer. To deal with this, let's just
11194 : * set reltuples to -1 in that case.
11195 : */
11196 7008 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11197 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11198 : else
11199 7008 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11200 :
11201 7008 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11202 7008 : rsinfo->relallvisible);
11203 :
11204 7008 : if (fout->remoteVersion >= 180000)
11205 7008 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11206 :
11207 7008 : appendPQExpBufferStr(out, "\n);\n");
11208 :
11209 : /* Fetch the next batch of attribute statistics if needed. */
11210 7008 : if (rownum >= PQntuples(res))
11211 : {
11212 2234 : PQclear(res);
11213 2234 : res = fetchAttributeStats(fout);
11214 2234 : rownum = 0;
11215 : }
11216 :
11217 7008 : i_schemaname = PQfnumber(res, "schemaname");
11218 7008 : i_tablename = PQfnumber(res, "tablename");
11219 7008 : i_attname = PQfnumber(res, "attname");
11220 7008 : i_inherited = PQfnumber(res, "inherited");
11221 7008 : i_null_frac = PQfnumber(res, "null_frac");
11222 7008 : i_avg_width = PQfnumber(res, "avg_width");
11223 7008 : i_n_distinct = PQfnumber(res, "n_distinct");
11224 7008 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11225 7008 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11226 7008 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11227 7008 : i_correlation = PQfnumber(res, "correlation");
11228 7008 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11229 7008 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11230 7008 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11231 7008 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11232 7008 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11233 7008 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11234 :
11235 : /* restore attribute stats */
11236 8582 : for (; rownum < PQntuples(res); rownum++)
11237 : {
11238 : const char *attname;
11239 :
11240 : /* Stop if the next stat row in our cache isn't for this relation. */
11241 6348 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11242 1574 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11243 : break;
11244 :
11245 1574 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11246 1574 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11247 : fout->remoteVersion);
11248 1574 : appendPQExpBufferStr(out, "\t'schemaname', ");
11249 1574 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11250 1574 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11251 1574 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11252 :
11253 1574 : if (PQgetisnull(res, rownum, i_attname))
11254 0 : pg_fatal("unexpected null attname");
11255 1574 : attname = PQgetvalue(res, rownum, i_attname);
11256 :
11257 : /*
11258 : * Indexes look up attname in indAttNames to derive attnum, all others
11259 : * use attname directly. We must specify attnum for indexes, since
11260 : * their attnames are not necessarily stable across dump/reload.
11261 : */
11262 1574 : if (rsinfo->nindAttNames == 0)
11263 : {
11264 1494 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11265 1494 : appendStringLiteralAH(out, attname, fout);
11266 : }
11267 : else
11268 : {
11269 80 : bool found = false;
11270 :
11271 148 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11272 : {
11273 148 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11274 : {
11275 80 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11276 : i + 1);
11277 80 : found = true;
11278 80 : break;
11279 : }
11280 : }
11281 :
11282 80 : if (!found)
11283 0 : pg_fatal("could not find index attname \"%s\"", attname);
11284 : }
11285 :
11286 1574 : if (!PQgetisnull(res, rownum, i_inherited))
11287 1574 : appendNamedArgument(out, fout, "inherited", "boolean",
11288 1574 : PQgetvalue(res, rownum, i_inherited));
11289 1574 : if (!PQgetisnull(res, rownum, i_null_frac))
11290 1574 : appendNamedArgument(out, fout, "null_frac", "real",
11291 1574 : PQgetvalue(res, rownum, i_null_frac));
11292 1574 : if (!PQgetisnull(res, rownum, i_avg_width))
11293 1574 : appendNamedArgument(out, fout, "avg_width", "integer",
11294 1574 : PQgetvalue(res, rownum, i_avg_width));
11295 1574 : if (!PQgetisnull(res, rownum, i_n_distinct))
11296 1574 : appendNamedArgument(out, fout, "n_distinct", "real",
11297 1574 : PQgetvalue(res, rownum, i_n_distinct));
11298 1574 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11299 776 : appendNamedArgument(out, fout, "most_common_vals", "text",
11300 776 : PQgetvalue(res, rownum, i_most_common_vals));
11301 1574 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11302 776 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11303 776 : PQgetvalue(res, rownum, i_most_common_freqs));
11304 1574 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11305 954 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11306 954 : PQgetvalue(res, rownum, i_histogram_bounds));
11307 1574 : if (!PQgetisnull(res, rownum, i_correlation))
11308 1498 : appendNamedArgument(out, fout, "correlation", "real",
11309 1498 : PQgetvalue(res, rownum, i_correlation));
11310 1574 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11311 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11312 16 : PQgetvalue(res, rownum, i_most_common_elems));
11313 1574 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11314 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11315 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11316 1574 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11317 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11318 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11319 1574 : if (fout->remoteVersion >= 170000)
11320 : {
11321 1574 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11322 8 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11323 8 : PQgetvalue(res, rownum, i_range_length_histogram));
11324 1574 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11325 8 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11326 8 : PQgetvalue(res, rownum, i_range_empty_frac));
11327 1574 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11328 8 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11329 8 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11330 : }
11331 1574 : appendPQExpBufferStr(out, "\n);\n");
11332 : }
11333 :
11334 7008 : destroyPQExpBuffer(query);
11335 7008 : return out->data;
11336 : }
11337 :
11338 : /*
11339 : * dumpRelationStats --
11340 : *
11341 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11342 : * care of gathering the statistics and generating the restore commands when
11343 : * they are needed.
11344 : */
11345 : static void
11346 7152 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11347 : {
11348 7152 : const DumpableObject *dobj = &rsinfo->dobj;
11349 :
11350 : /* nothing to do if we are not dumping statistics */
11351 7152 : if (!fout->dopt->dumpStatistics)
11352 0 : return;
11353 :
11354 7152 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11355 7152 : ARCHIVE_OPTS(.tag = dobj->name,
11356 : .namespace = dobj->namespace->dobj.name,
11357 : .description = "STATISTICS DATA",
11358 : .section = rsinfo->section,
11359 : .defnFn = dumpRelationStats_dumper,
11360 : .defnArg = rsinfo,
11361 : .deps = dobj->dependencies,
11362 : .nDeps = dobj->nDeps));
11363 : }
11364 :
11365 : /*
11366 : * dumpTableComment --
11367 : *
11368 : * As above, but dump comments for both the specified table (or view)
11369 : * and its columns.
11370 : */
11371 : static void
11372 160 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11373 : const char *reltypename)
11374 : {
11375 160 : DumpOptions *dopt = fout->dopt;
11376 : CommentItem *comments;
11377 : int ncomments;
11378 : PQExpBuffer query;
11379 : PQExpBuffer tag;
11380 :
11381 : /* do nothing, if --no-comments is supplied */
11382 160 : if (dopt->no_comments)
11383 0 : return;
11384 :
11385 : /* Comments are SCHEMA not data */
11386 160 : if (!dopt->dumpSchema)
11387 0 : return;
11388 :
11389 : /* Search for comments associated with relation, using table */
11390 160 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11391 160 : tbinfo->dobj.catId.oid,
11392 : &comments);
11393 :
11394 : /* If comments exist, build COMMENT ON statements */
11395 160 : if (ncomments <= 0)
11396 0 : return;
11397 :
11398 160 : query = createPQExpBuffer();
11399 160 : tag = createPQExpBuffer();
11400 :
11401 460 : while (ncomments > 0)
11402 : {
11403 300 : const char *descr = comments->descr;
11404 300 : int objsubid = comments->objsubid;
11405 :
11406 300 : if (objsubid == 0)
11407 : {
11408 70 : resetPQExpBuffer(tag);
11409 70 : appendPQExpBuffer(tag, "%s %s", reltypename,
11410 70 : fmtId(tbinfo->dobj.name));
11411 :
11412 70 : resetPQExpBuffer(query);
11413 70 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11414 70 : fmtQualifiedDumpable(tbinfo));
11415 70 : appendStringLiteralAH(query, descr, fout);
11416 70 : appendPQExpBufferStr(query, ";\n");
11417 :
11418 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11419 70 : ARCHIVE_OPTS(.tag = tag->data,
11420 : .namespace = tbinfo->dobj.namespace->dobj.name,
11421 : .owner = tbinfo->rolname,
11422 : .description = "COMMENT",
11423 : .section = SECTION_NONE,
11424 : .createStmt = query->data,
11425 : .deps = &(tbinfo->dobj.dumpId),
11426 : .nDeps = 1));
11427 : }
11428 230 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11429 : {
11430 230 : resetPQExpBuffer(tag);
11431 230 : appendPQExpBuffer(tag, "COLUMN %s.",
11432 230 : fmtId(tbinfo->dobj.name));
11433 230 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11434 :
11435 230 : resetPQExpBuffer(query);
11436 230 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11437 230 : fmtQualifiedDumpable(tbinfo));
11438 230 : appendPQExpBuffer(query, "%s IS ",
11439 230 : fmtId(tbinfo->attnames[objsubid - 1]));
11440 230 : appendStringLiteralAH(query, descr, fout);
11441 230 : appendPQExpBufferStr(query, ";\n");
11442 :
11443 230 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11444 230 : ARCHIVE_OPTS(.tag = tag->data,
11445 : .namespace = tbinfo->dobj.namespace->dobj.name,
11446 : .owner = tbinfo->rolname,
11447 : .description = "COMMENT",
11448 : .section = SECTION_NONE,
11449 : .createStmt = query->data,
11450 : .deps = &(tbinfo->dobj.dumpId),
11451 : .nDeps = 1));
11452 : }
11453 :
11454 300 : comments++;
11455 300 : ncomments--;
11456 : }
11457 :
11458 160 : destroyPQExpBuffer(query);
11459 160 : destroyPQExpBuffer(tag);
11460 : }
11461 :
11462 : /*
11463 : * findComments --
11464 : *
11465 : * Find the comment(s), if any, associated with the given object. All the
11466 : * objsubid values associated with the given classoid/objoid are found with
11467 : * one search.
11468 : */
11469 : static int
11470 13226 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11471 : {
11472 13226 : CommentItem *middle = NULL;
11473 : CommentItem *low;
11474 : CommentItem *high;
11475 : int nmatch;
11476 :
11477 : /*
11478 : * Do binary search to find some item matching the object.
11479 : */
11480 13226 : low = &comments[0];
11481 13226 : high = &comments[ncomments - 1];
11482 131766 : while (low <= high)
11483 : {
11484 131668 : middle = low + (high - low) / 2;
11485 :
11486 131668 : if (classoid < middle->classoid)
11487 15070 : high = middle - 1;
11488 116598 : else if (classoid > middle->classoid)
11489 14446 : low = middle + 1;
11490 102152 : else if (objoid < middle->objoid)
11491 43126 : high = middle - 1;
11492 59026 : else if (objoid > middle->objoid)
11493 45898 : low = middle + 1;
11494 : else
11495 13128 : break; /* found a match */
11496 : }
11497 :
11498 13226 : if (low > high) /* no matches */
11499 : {
11500 98 : *items = NULL;
11501 98 : return 0;
11502 : }
11503 :
11504 : /*
11505 : * Now determine how many items match the object. The search loop
11506 : * invariant still holds: only items between low and high inclusive could
11507 : * match.
11508 : */
11509 13128 : nmatch = 1;
11510 13268 : while (middle > low)
11511 : {
11512 6144 : if (classoid != middle[-1].classoid ||
11513 5886 : objoid != middle[-1].objoid)
11514 : break;
11515 140 : middle--;
11516 140 : nmatch++;
11517 : }
11518 :
11519 13128 : *items = middle;
11520 :
11521 13128 : middle += nmatch;
11522 13128 : while (middle <= high)
11523 : {
11524 7046 : if (classoid != middle->classoid ||
11525 6428 : objoid != middle->objoid)
11526 : break;
11527 0 : middle++;
11528 0 : nmatch++;
11529 : }
11530 :
11531 13128 : return nmatch;
11532 : }
11533 :
11534 : /*
11535 : * collectComments --
11536 : *
11537 : * Construct a table of all comments available for database objects;
11538 : * also set the has-comment component flag for each relevant object.
11539 : *
11540 : * We used to do per-object queries for the comments, but it's much faster
11541 : * to pull them all over at once, and on most databases the memory cost
11542 : * isn't high.
11543 : *
11544 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11545 : */
11546 : static void
11547 364 : collectComments(Archive *fout)
11548 : {
11549 : PGresult *res;
11550 : PQExpBuffer query;
11551 : int i_description;
11552 : int i_classoid;
11553 : int i_objoid;
11554 : int i_objsubid;
11555 : int ntups;
11556 : int i;
11557 : DumpableObject *dobj;
11558 :
11559 364 : query = createPQExpBuffer();
11560 :
11561 364 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11562 : "FROM pg_catalog.pg_description "
11563 : "ORDER BY classoid, objoid, objsubid");
11564 :
11565 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11566 :
11567 : /* Construct lookup table containing OIDs in numeric form */
11568 :
11569 364 : i_description = PQfnumber(res, "description");
11570 364 : i_classoid = PQfnumber(res, "classoid");
11571 364 : i_objoid = PQfnumber(res, "objoid");
11572 364 : i_objsubid = PQfnumber(res, "objsubid");
11573 :
11574 364 : ntups = PQntuples(res);
11575 :
11576 364 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11577 364 : ncomments = 0;
11578 364 : dobj = NULL;
11579 :
11580 1940434 : for (i = 0; i < ntups; i++)
11581 : {
11582 : CatalogId objId;
11583 : int subid;
11584 :
11585 1940070 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11586 1940070 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11587 1940070 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11588 :
11589 : /* We needn't remember comments that don't match any dumpable object */
11590 1940070 : if (dobj == NULL ||
11591 698706 : dobj->catId.tableoid != objId.tableoid ||
11592 694278 : dobj->catId.oid != objId.oid)
11593 1939878 : dobj = findObjectByCatalogId(objId);
11594 1940070 : if (dobj == NULL)
11595 1241012 : continue;
11596 :
11597 : /*
11598 : * Comments on columns of composite types are linked to the type's
11599 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11600 : * in the type's own DumpableObject.
11601 : */
11602 699058 : if (subid != 0 && dobj->objType == DO_TABLE &&
11603 412 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11604 96 : {
11605 : TypeInfo *cTypeInfo;
11606 :
11607 96 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11608 96 : if (cTypeInfo)
11609 96 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11610 : }
11611 : else
11612 698962 : dobj->components |= DUMP_COMPONENT_COMMENT;
11613 :
11614 699058 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11615 699058 : comments[ncomments].classoid = objId.tableoid;
11616 699058 : comments[ncomments].objoid = objId.oid;
11617 699058 : comments[ncomments].objsubid = subid;
11618 699058 : ncomments++;
11619 : }
11620 :
11621 364 : PQclear(res);
11622 364 : destroyPQExpBuffer(query);
11623 364 : }
11624 :
11625 : /*
11626 : * dumpDumpableObject
11627 : *
11628 : * This routine and its subsidiaries are responsible for creating
11629 : * ArchiveEntries (TOC objects) for each object to be dumped.
11630 : */
11631 : static void
11632 1355178 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11633 : {
11634 : /*
11635 : * Clear any dump-request bits for components that don't exist for this
11636 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11637 : * request for every kind of object.)
11638 : */
11639 1355178 : dobj->dump &= dobj->components;
11640 :
11641 : /* Now, short-circuit if there's nothing to be done here. */
11642 1355178 : if (dobj->dump == 0)
11643 1201076 : return;
11644 :
11645 154102 : switch (dobj->objType)
11646 : {
11647 966 : case DO_NAMESPACE:
11648 966 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11649 966 : break;
11650 48 : case DO_EXTENSION:
11651 48 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11652 48 : break;
11653 1888 : case DO_TYPE:
11654 1888 : dumpType(fout, (const TypeInfo *) dobj);
11655 1888 : break;
11656 152 : case DO_SHELL_TYPE:
11657 152 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11658 152 : break;
11659 3696 : case DO_FUNC:
11660 3696 : dumpFunc(fout, (const FuncInfo *) dobj);
11661 3696 : break;
11662 590 : case DO_AGG:
11663 590 : dumpAgg(fout, (const AggInfo *) dobj);
11664 590 : break;
11665 5014 : case DO_OPERATOR:
11666 5014 : dumpOpr(fout, (const OprInfo *) dobj);
11667 5014 : break;
11668 172 : case DO_ACCESS_METHOD:
11669 172 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11670 172 : break;
11671 1338 : case DO_OPCLASS:
11672 1338 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11673 1338 : break;
11674 1110 : case DO_OPFAMILY:
11675 1110 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11676 1110 : break;
11677 5086 : case DO_COLLATION:
11678 5086 : dumpCollation(fout, (const CollInfo *) dobj);
11679 5086 : break;
11680 850 : case DO_CONVERSION:
11681 850 : dumpConversion(fout, (const ConvInfo *) dobj);
11682 850 : break;
11683 61058 : case DO_TABLE:
11684 61058 : dumpTable(fout, (const TableInfo *) dobj);
11685 61058 : break;
11686 2816 : case DO_TABLE_ATTACH:
11687 2816 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11688 2816 : break;
11689 2136 : case DO_ATTRDEF:
11690 2136 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11691 2136 : break;
11692 5262 : case DO_INDEX:
11693 5262 : dumpIndex(fout, (const IndxInfo *) dobj);
11694 5262 : break;
11695 1176 : case DO_INDEX_ATTACH:
11696 1176 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11697 1176 : break;
11698 284 : case DO_STATSEXT:
11699 284 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11700 284 : break;
11701 804 : case DO_REFRESH_MATVIEW:
11702 804 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11703 804 : break;
11704 2380 : case DO_RULE:
11705 2380 : dumpRule(fout, (const RuleInfo *) dobj);
11706 2380 : break;
11707 1076 : case DO_TRIGGER:
11708 1076 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11709 1076 : break;
11710 90 : case DO_EVENT_TRIGGER:
11711 90 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11712 90 : break;
11713 4774 : case DO_CONSTRAINT:
11714 4774 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11715 4774 : break;
11716 354 : case DO_FK_CONSTRAINT:
11717 354 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11718 354 : break;
11719 176 : case DO_PROCLANG:
11720 176 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11721 176 : break;
11722 140 : case DO_CAST:
11723 140 : dumpCast(fout, (const CastInfo *) dobj);
11724 140 : break;
11725 90 : case DO_TRANSFORM:
11726 90 : dumpTransform(fout, (const TransformInfo *) dobj);
11727 90 : break;
11728 804 : case DO_SEQUENCE_SET:
11729 804 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11730 804 : break;
11731 8764 : case DO_TABLE_DATA:
11732 8764 : dumpTableData(fout, (const TableDataInfo *) dobj);
11733 8764 : break;
11734 29144 : case DO_DUMMY_TYPE:
11735 : /* table rowtypes and array types are never dumped separately */
11736 29144 : break;
11737 88 : case DO_TSPARSER:
11738 88 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11739 88 : break;
11740 352 : case DO_TSDICT:
11741 352 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11742 352 : break;
11743 112 : case DO_TSTEMPLATE:
11744 112 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11745 112 : break;
11746 302 : case DO_TSCONFIG:
11747 302 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11748 302 : break;
11749 110 : case DO_FDW:
11750 110 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11751 110 : break;
11752 118 : case DO_FOREIGN_SERVER:
11753 118 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11754 118 : break;
11755 344 : case DO_DEFAULT_ACL:
11756 344 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11757 344 : break;
11758 166 : case DO_LARGE_OBJECT:
11759 166 : dumpLO(fout, (const LoInfo *) dobj);
11760 166 : break;
11761 178 : case DO_LARGE_OBJECT_DATA:
11762 178 : if (dobj->dump & DUMP_COMPONENT_DATA)
11763 : {
11764 : LoInfo *loinfo;
11765 : TocEntry *te;
11766 :
11767 178 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11768 178 : if (loinfo == NULL)
11769 0 : pg_fatal("missing metadata for large objects \"%s\"",
11770 : dobj->name);
11771 :
11772 178 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11773 178 : ARCHIVE_OPTS(.tag = dobj->name,
11774 : .owner = loinfo->rolname,
11775 : .description = "BLOBS",
11776 : .section = SECTION_DATA,
11777 : .deps = dobj->dependencies,
11778 : .nDeps = dobj->nDeps,
11779 : .dumpFn = dumpLOs,
11780 : .dumpArg = loinfo));
11781 :
11782 : /*
11783 : * Set the TocEntry's dataLength in case we are doing a
11784 : * parallel dump and want to order dump jobs by table size.
11785 : * (We need some size estimate for every TocEntry with a
11786 : * DataDumper function.) We don't currently have any cheap
11787 : * way to estimate the size of LOs, but fortunately it doesn't
11788 : * matter too much as long as we get large batches of LOs
11789 : * processed reasonably early. Assume 8K per blob.
11790 : */
11791 178 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11792 : }
11793 178 : break;
11794 694 : case DO_POLICY:
11795 694 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11796 694 : break;
11797 452 : case DO_PUBLICATION:
11798 452 : dumpPublication(fout, (const PublicationInfo *) dobj);
11799 452 : break;
11800 610 : case DO_PUBLICATION_REL:
11801 610 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11802 610 : break;
11803 210 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11804 210 : dumpPublicationNamespace(fout,
11805 : (const PublicationSchemaInfo *) dobj);
11806 210 : break;
11807 244 : case DO_SUBSCRIPTION:
11808 244 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11809 244 : break;
11810 4 : case DO_SUBSCRIPTION_REL:
11811 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11812 4 : break;
11813 7152 : case DO_REL_STATS:
11814 7152 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11815 7152 : break;
11816 728 : case DO_PRE_DATA_BOUNDARY:
11817 : case DO_POST_DATA_BOUNDARY:
11818 : /* never dumped, nothing to do */
11819 728 : break;
11820 : }
11821 : }
11822 :
11823 : /*
11824 : * dumpNamespace
11825 : * writes out to fout the queries to recreate a user-defined namespace
11826 : */
11827 : static void
11828 966 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11829 : {
11830 966 : DumpOptions *dopt = fout->dopt;
11831 : PQExpBuffer q;
11832 : PQExpBuffer delq;
11833 : char *qnspname;
11834 :
11835 : /* Do nothing if not dumping schema */
11836 966 : if (!dopt->dumpSchema)
11837 56 : return;
11838 :
11839 910 : q = createPQExpBuffer();
11840 910 : delq = createPQExpBuffer();
11841 :
11842 910 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11843 :
11844 910 : if (nspinfo->create)
11845 : {
11846 616 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11847 616 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11848 : }
11849 : else
11850 : {
11851 : /* see selectDumpableNamespace() */
11852 294 : appendPQExpBufferStr(delq,
11853 : "-- *not* dropping schema, since initdb creates it\n");
11854 294 : appendPQExpBufferStr(q,
11855 : "-- *not* creating schema, since initdb creates it\n");
11856 : }
11857 :
11858 910 : if (dopt->binary_upgrade)
11859 180 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11860 : "SCHEMA", qnspname, NULL);
11861 :
11862 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11863 388 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11864 388 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11865 : .owner = nspinfo->rolname,
11866 : .description = "SCHEMA",
11867 : .section = SECTION_PRE_DATA,
11868 : .createStmt = q->data,
11869 : .dropStmt = delq->data));
11870 :
11871 : /* Dump Schema Comments and Security Labels */
11872 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11873 : {
11874 304 : const char *initdb_comment = NULL;
11875 :
11876 304 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11877 226 : initdb_comment = "standard public schema";
11878 304 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11879 304 : NULL, nspinfo->rolname,
11880 304 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11881 : initdb_comment);
11882 : }
11883 :
11884 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11885 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11886 0 : NULL, nspinfo->rolname,
11887 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11888 :
11889 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11890 700 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11891 : qnspname, NULL, NULL,
11892 700 : NULL, nspinfo->rolname, &nspinfo->dacl);
11893 :
11894 910 : free(qnspname);
11895 :
11896 910 : destroyPQExpBuffer(q);
11897 910 : destroyPQExpBuffer(delq);
11898 : }
11899 :
11900 : /*
11901 : * dumpExtension
11902 : * writes out to fout the queries to recreate an extension
11903 : */
11904 : static void
11905 48 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11906 : {
11907 48 : DumpOptions *dopt = fout->dopt;
11908 : PQExpBuffer q;
11909 : PQExpBuffer delq;
11910 : char *qextname;
11911 :
11912 : /* Do nothing if not dumping schema */
11913 48 : if (!dopt->dumpSchema)
11914 2 : return;
11915 :
11916 46 : q = createPQExpBuffer();
11917 46 : delq = createPQExpBuffer();
11918 :
11919 46 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11920 :
11921 46 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11922 :
11923 46 : if (!dopt->binary_upgrade)
11924 : {
11925 : /*
11926 : * In a regular dump, we simply create the extension, intentionally
11927 : * not specifying a version, so that the destination installation's
11928 : * default version is used.
11929 : *
11930 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11931 : * types; but there are various scenarios in which it's convenient to
11932 : * manually create the desired extension before restoring, so we
11933 : * prefer to allow it to exist already.
11934 : */
11935 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11936 34 : qextname, fmtId(extinfo->namespace));
11937 : }
11938 : else
11939 : {
11940 : /*
11941 : * In binary-upgrade mode, it's critical to reproduce the state of the
11942 : * database exactly, so our procedure is to create an empty extension,
11943 : * restore all the contained objects normally, and add them to the
11944 : * extension one by one. This function performs just the first of
11945 : * those steps. binary_upgrade_extension_member() takes care of
11946 : * adding member objects as they're created.
11947 : */
11948 : int i;
11949 : int n;
11950 :
11951 12 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11952 :
11953 : /*
11954 : * We unconditionally create the extension, so we must drop it if it
11955 : * exists. This could happen if the user deleted 'plpgsql' and then
11956 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11957 : */
11958 12 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11959 :
11960 12 : appendPQExpBufferStr(q,
11961 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11962 12 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11963 12 : appendPQExpBufferStr(q, ", ");
11964 12 : appendStringLiteralAH(q, extinfo->namespace, fout);
11965 12 : appendPQExpBufferStr(q, ", ");
11966 12 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11967 12 : appendStringLiteralAH(q, extinfo->extversion, fout);
11968 12 : appendPQExpBufferStr(q, ", ");
11969 :
11970 : /*
11971 : * Note that we're pushing extconfig (an OID array) back into
11972 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11973 : * preserved in binary upgrade.
11974 : */
11975 12 : if (strlen(extinfo->extconfig) > 2)
11976 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11977 : else
11978 10 : appendPQExpBufferStr(q, "NULL");
11979 12 : appendPQExpBufferStr(q, ", ");
11980 12 : if (strlen(extinfo->extcondition) > 2)
11981 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11982 : else
11983 10 : appendPQExpBufferStr(q, "NULL");
11984 12 : appendPQExpBufferStr(q, ", ");
11985 12 : appendPQExpBufferStr(q, "ARRAY[");
11986 12 : n = 0;
11987 24 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11988 : {
11989 : DumpableObject *extobj;
11990 :
11991 12 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11992 12 : if (extobj && extobj->objType == DO_EXTENSION)
11993 : {
11994 0 : if (n++ > 0)
11995 0 : appendPQExpBufferChar(q, ',');
11996 0 : appendStringLiteralAH(q, extobj->name, fout);
11997 : }
11998 : }
11999 12 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12000 12 : appendPQExpBufferStr(q, ");\n");
12001 : }
12002 :
12003 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12004 46 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12005 46 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12006 : .description = "EXTENSION",
12007 : .section = SECTION_PRE_DATA,
12008 : .createStmt = q->data,
12009 : .dropStmt = delq->data));
12010 :
12011 : /* Dump Extension Comments and Security Labels */
12012 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12013 46 : dumpComment(fout, "EXTENSION", qextname,
12014 : NULL, "",
12015 46 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12016 :
12017 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12018 0 : dumpSecLabel(fout, "EXTENSION", qextname,
12019 : NULL, "",
12020 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12021 :
12022 46 : free(qextname);
12023 :
12024 46 : destroyPQExpBuffer(q);
12025 46 : destroyPQExpBuffer(delq);
12026 : }
12027 :
12028 : /*
12029 : * dumpType
12030 : * writes out to fout the queries to recreate a user-defined type
12031 : */
12032 : static void
12033 1888 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12034 : {
12035 1888 : DumpOptions *dopt = fout->dopt;
12036 :
12037 : /* Do nothing if not dumping schema */
12038 1888 : if (!dopt->dumpSchema)
12039 98 : return;
12040 :
12041 : /* Dump out in proper style */
12042 1790 : if (tyinfo->typtype == TYPTYPE_BASE)
12043 566 : dumpBaseType(fout, tyinfo);
12044 1224 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12045 310 : dumpDomain(fout, tyinfo);
12046 914 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12047 266 : dumpCompositeType(fout, tyinfo);
12048 648 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12049 182 : dumpEnumType(fout, tyinfo);
12050 466 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12051 236 : dumpRangeType(fout, tyinfo);
12052 230 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12053 80 : dumpUndefinedType(fout, tyinfo);
12054 : else
12055 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12056 : tyinfo->dobj.name);
12057 : }
12058 :
12059 : /*
12060 : * dumpEnumType
12061 : * writes out to fout the queries to recreate a user-defined enum type
12062 : */
12063 : static void
12064 182 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12065 : {
12066 182 : DumpOptions *dopt = fout->dopt;
12067 182 : PQExpBuffer q = createPQExpBuffer();
12068 182 : PQExpBuffer delq = createPQExpBuffer();
12069 182 : PQExpBuffer query = createPQExpBuffer();
12070 : PGresult *res;
12071 : int num,
12072 : i;
12073 : Oid enum_oid;
12074 : char *qtypname;
12075 : char *qualtypname;
12076 : char *label;
12077 : int i_enumlabel;
12078 : int i_oid;
12079 :
12080 182 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12081 : {
12082 : /* Set up query for enum-specific details */
12083 86 : appendPQExpBufferStr(query,
12084 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12085 : "SELECT oid, enumlabel "
12086 : "FROM pg_catalog.pg_enum "
12087 : "WHERE enumtypid = $1 "
12088 : "ORDER BY enumsortorder");
12089 :
12090 86 : ExecuteSqlStatement(fout, query->data);
12091 :
12092 86 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12093 : }
12094 :
12095 182 : printfPQExpBuffer(query,
12096 : "EXECUTE dumpEnumType('%u')",
12097 182 : tyinfo->dobj.catId.oid);
12098 :
12099 182 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12100 :
12101 182 : num = PQntuples(res);
12102 :
12103 182 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12104 182 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12105 :
12106 : /*
12107 : * CASCADE shouldn't be required here as for normal types since the I/O
12108 : * functions are generic and do not get dropped.
12109 : */
12110 182 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12111 :
12112 182 : if (dopt->binary_upgrade)
12113 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12114 12 : tyinfo->dobj.catId.oid,
12115 : false, false);
12116 :
12117 182 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12118 : qualtypname);
12119 :
12120 182 : if (!dopt->binary_upgrade)
12121 : {
12122 170 : i_enumlabel = PQfnumber(res, "enumlabel");
12123 :
12124 : /* Labels with server-assigned oids */
12125 1012 : for (i = 0; i < num; i++)
12126 : {
12127 842 : label = PQgetvalue(res, i, i_enumlabel);
12128 842 : if (i > 0)
12129 672 : appendPQExpBufferChar(q, ',');
12130 842 : appendPQExpBufferStr(q, "\n ");
12131 842 : appendStringLiteralAH(q, label, fout);
12132 : }
12133 : }
12134 :
12135 182 : appendPQExpBufferStr(q, "\n);\n");
12136 :
12137 182 : if (dopt->binary_upgrade)
12138 : {
12139 12 : i_oid = PQfnumber(res, "oid");
12140 12 : i_enumlabel = PQfnumber(res, "enumlabel");
12141 :
12142 : /* Labels with dump-assigned (preserved) oids */
12143 124 : for (i = 0; i < num; i++)
12144 : {
12145 112 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12146 112 : label = PQgetvalue(res, i, i_enumlabel);
12147 :
12148 112 : if (i == 0)
12149 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12150 112 : appendPQExpBuffer(q,
12151 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12152 : enum_oid);
12153 112 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12154 112 : appendStringLiteralAH(q, label, fout);
12155 112 : appendPQExpBufferStr(q, ";\n\n");
12156 : }
12157 : }
12158 :
12159 182 : if (dopt->binary_upgrade)
12160 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12161 : "TYPE", qtypname,
12162 12 : tyinfo->dobj.namespace->dobj.name);
12163 :
12164 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12165 182 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12166 182 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12167 : .namespace = tyinfo->dobj.namespace->dobj.name,
12168 : .owner = tyinfo->rolname,
12169 : .description = "TYPE",
12170 : .section = SECTION_PRE_DATA,
12171 : .createStmt = q->data,
12172 : .dropStmt = delq->data));
12173 :
12174 : /* Dump Type Comments and Security Labels */
12175 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12176 70 : dumpComment(fout, "TYPE", qtypname,
12177 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12178 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12179 :
12180 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12181 0 : dumpSecLabel(fout, "TYPE", qtypname,
12182 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12183 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12184 :
12185 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12186 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12187 : qtypname, NULL,
12188 70 : tyinfo->dobj.namespace->dobj.name,
12189 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12190 :
12191 182 : PQclear(res);
12192 182 : destroyPQExpBuffer(q);
12193 182 : destroyPQExpBuffer(delq);
12194 182 : destroyPQExpBuffer(query);
12195 182 : free(qtypname);
12196 182 : free(qualtypname);
12197 182 : }
12198 :
12199 : /*
12200 : * dumpRangeType
12201 : * writes out to fout the queries to recreate a user-defined range type
12202 : */
12203 : static void
12204 236 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12205 : {
12206 236 : DumpOptions *dopt = fout->dopt;
12207 236 : PQExpBuffer q = createPQExpBuffer();
12208 236 : PQExpBuffer delq = createPQExpBuffer();
12209 236 : PQExpBuffer query = createPQExpBuffer();
12210 : PGresult *res;
12211 : Oid collationOid;
12212 : char *qtypname;
12213 : char *qualtypname;
12214 : char *procname;
12215 :
12216 236 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12217 : {
12218 : /* Set up query for range-specific details */
12219 86 : appendPQExpBufferStr(query,
12220 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12221 :
12222 86 : appendPQExpBufferStr(query,
12223 : "SELECT ");
12224 :
12225 86 : if (fout->remoteVersion >= 140000)
12226 86 : appendPQExpBufferStr(query,
12227 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12228 : else
12229 0 : appendPQExpBufferStr(query,
12230 : "NULL AS rngmultitype, ");
12231 :
12232 86 : appendPQExpBufferStr(query,
12233 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12234 : "opc.opcname AS opcname, "
12235 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12236 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12237 : "opc.opcdefault, "
12238 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12239 : " ELSE rngcollation END AS collation, "
12240 : "rngcanonical, rngsubdiff "
12241 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12242 : " pg_catalog.pg_opclass opc "
12243 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12244 : "rngtypid = $1");
12245 :
12246 86 : ExecuteSqlStatement(fout, query->data);
12247 :
12248 86 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12249 : }
12250 :
12251 236 : printfPQExpBuffer(query,
12252 : "EXECUTE dumpRangeType('%u')",
12253 236 : tyinfo->dobj.catId.oid);
12254 :
12255 236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12256 :
12257 236 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12258 236 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12259 :
12260 : /*
12261 : * CASCADE shouldn't be required here as for normal types since the I/O
12262 : * functions are generic and do not get dropped.
12263 : */
12264 236 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12265 :
12266 236 : if (dopt->binary_upgrade)
12267 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12268 16 : tyinfo->dobj.catId.oid,
12269 : false, true);
12270 :
12271 236 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12272 : qualtypname);
12273 :
12274 236 : appendPQExpBuffer(q, "\n subtype = %s",
12275 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12276 :
12277 236 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12278 236 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12279 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12280 :
12281 : /* print subtype_opclass only if not default for subtype */
12282 236 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12283 : {
12284 70 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12285 70 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12286 :
12287 70 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12288 : fmtId(nspname));
12289 70 : appendPQExpBufferStr(q, fmtId(opcname));
12290 : }
12291 :
12292 236 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12293 236 : if (OidIsValid(collationOid))
12294 : {
12295 80 : CollInfo *coll = findCollationByOid(collationOid);
12296 :
12297 80 : if (coll)
12298 80 : appendPQExpBuffer(q, ",\n collation = %s",
12299 80 : fmtQualifiedDumpable(coll));
12300 : }
12301 :
12302 236 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12303 236 : if (strcmp(procname, "-") != 0)
12304 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12305 :
12306 236 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12307 236 : if (strcmp(procname, "-") != 0)
12308 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12309 :
12310 236 : appendPQExpBufferStr(q, "\n);\n");
12311 :
12312 236 : if (dopt->binary_upgrade)
12313 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12314 : "TYPE", qtypname,
12315 16 : tyinfo->dobj.namespace->dobj.name);
12316 :
12317 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12318 236 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12319 236 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12320 : .namespace = tyinfo->dobj.namespace->dobj.name,
12321 : .owner = tyinfo->rolname,
12322 : .description = "TYPE",
12323 : .section = SECTION_PRE_DATA,
12324 : .createStmt = q->data,
12325 : .dropStmt = delq->data));
12326 :
12327 : /* Dump Type Comments and Security Labels */
12328 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12329 106 : dumpComment(fout, "TYPE", qtypname,
12330 106 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12331 106 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12332 :
12333 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12334 0 : dumpSecLabel(fout, "TYPE", qtypname,
12335 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12336 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12337 :
12338 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12339 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12340 : qtypname, NULL,
12341 70 : tyinfo->dobj.namespace->dobj.name,
12342 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12343 :
12344 236 : PQclear(res);
12345 236 : destroyPQExpBuffer(q);
12346 236 : destroyPQExpBuffer(delq);
12347 236 : destroyPQExpBuffer(query);
12348 236 : free(qtypname);
12349 236 : free(qualtypname);
12350 236 : }
12351 :
12352 : /*
12353 : * dumpUndefinedType
12354 : * writes out to fout the queries to recreate a !typisdefined type
12355 : *
12356 : * This is a shell type, but we use different terminology to distinguish
12357 : * this case from where we have to emit a shell type definition to break
12358 : * circular dependencies. An undefined type shouldn't ever have anything
12359 : * depending on it.
12360 : */
12361 : static void
12362 80 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12363 : {
12364 80 : DumpOptions *dopt = fout->dopt;
12365 80 : PQExpBuffer q = createPQExpBuffer();
12366 80 : PQExpBuffer delq = createPQExpBuffer();
12367 : char *qtypname;
12368 : char *qualtypname;
12369 :
12370 80 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12371 80 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12372 :
12373 80 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12374 :
12375 80 : if (dopt->binary_upgrade)
12376 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12377 4 : tyinfo->dobj.catId.oid,
12378 : false, false);
12379 :
12380 80 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12381 : qualtypname);
12382 :
12383 80 : if (dopt->binary_upgrade)
12384 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12385 : "TYPE", qtypname,
12386 4 : tyinfo->dobj.namespace->dobj.name);
12387 :
12388 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12389 80 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12390 80 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12391 : .namespace = tyinfo->dobj.namespace->dobj.name,
12392 : .owner = tyinfo->rolname,
12393 : .description = "TYPE",
12394 : .section = SECTION_PRE_DATA,
12395 : .createStmt = q->data,
12396 : .dropStmt = delq->data));
12397 :
12398 : /* Dump Type Comments and Security Labels */
12399 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12400 70 : dumpComment(fout, "TYPE", qtypname,
12401 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12402 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12403 :
12404 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12405 0 : dumpSecLabel(fout, "TYPE", qtypname,
12406 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12407 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12408 :
12409 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12410 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12411 : qtypname, NULL,
12412 0 : tyinfo->dobj.namespace->dobj.name,
12413 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12414 :
12415 80 : destroyPQExpBuffer(q);
12416 80 : destroyPQExpBuffer(delq);
12417 80 : free(qtypname);
12418 80 : free(qualtypname);
12419 80 : }
12420 :
12421 : /*
12422 : * dumpBaseType
12423 : * writes out to fout the queries to recreate a user-defined base type
12424 : */
12425 : static void
12426 566 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12427 : {
12428 566 : DumpOptions *dopt = fout->dopt;
12429 566 : PQExpBuffer q = createPQExpBuffer();
12430 566 : PQExpBuffer delq = createPQExpBuffer();
12431 566 : PQExpBuffer query = createPQExpBuffer();
12432 : PGresult *res;
12433 : char *qtypname;
12434 : char *qualtypname;
12435 : char *typlen;
12436 : char *typinput;
12437 : char *typoutput;
12438 : char *typreceive;
12439 : char *typsend;
12440 : char *typmodin;
12441 : char *typmodout;
12442 : char *typanalyze;
12443 : char *typsubscript;
12444 : Oid typreceiveoid;
12445 : Oid typsendoid;
12446 : Oid typmodinoid;
12447 : Oid typmodoutoid;
12448 : Oid typanalyzeoid;
12449 : Oid typsubscriptoid;
12450 : char *typcategory;
12451 : char *typispreferred;
12452 : char *typdelim;
12453 : char *typbyval;
12454 : char *typalign;
12455 : char *typstorage;
12456 : char *typcollatable;
12457 : char *typdefault;
12458 566 : bool typdefault_is_literal = false;
12459 :
12460 566 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12461 : {
12462 : /* Set up query for type-specific details */
12463 86 : appendPQExpBufferStr(query,
12464 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12465 : "SELECT typlen, "
12466 : "typinput, typoutput, typreceive, typsend, "
12467 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12468 : "typsend::pg_catalog.oid AS typsendoid, "
12469 : "typanalyze, "
12470 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12471 : "typdelim, typbyval, typalign, typstorage, "
12472 : "typmodin, typmodout, "
12473 : "typmodin::pg_catalog.oid AS typmodinoid, "
12474 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12475 : "typcategory, typispreferred, "
12476 : "(typcollation <> 0) AS typcollatable, "
12477 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12478 :
12479 86 : if (fout->remoteVersion >= 140000)
12480 86 : appendPQExpBufferStr(query,
12481 : "typsubscript, "
12482 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12483 : else
12484 0 : appendPQExpBufferStr(query,
12485 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12486 :
12487 86 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12488 : "WHERE oid = $1");
12489 :
12490 86 : ExecuteSqlStatement(fout, query->data);
12491 :
12492 86 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12493 : }
12494 :
12495 566 : printfPQExpBuffer(query,
12496 : "EXECUTE dumpBaseType('%u')",
12497 566 : tyinfo->dobj.catId.oid);
12498 :
12499 566 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12500 :
12501 566 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12502 566 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12503 566 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12504 566 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12505 566 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12506 566 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12507 566 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12508 566 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12509 566 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12510 566 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12511 566 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12512 566 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12513 566 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12514 566 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12515 566 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12516 566 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12517 566 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12518 566 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12519 566 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12520 566 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12521 566 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12522 566 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12523 566 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12524 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12525 566 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12526 : {
12527 90 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12528 90 : typdefault_is_literal = true; /* it needs quotes */
12529 : }
12530 : else
12531 476 : typdefault = NULL;
12532 :
12533 566 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12534 566 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12535 :
12536 : /*
12537 : * The reason we include CASCADE is that the circular dependency between
12538 : * the type and its I/O functions makes it impossible to drop the type any
12539 : * other way.
12540 : */
12541 566 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12542 :
12543 : /*
12544 : * We might already have a shell type, but setting pg_type_oid is
12545 : * harmless, and in any case we'd better set the array type OID.
12546 : */
12547 566 : if (dopt->binary_upgrade)
12548 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12549 16 : tyinfo->dobj.catId.oid,
12550 : false, false);
12551 :
12552 566 : appendPQExpBuffer(q,
12553 : "CREATE TYPE %s (\n"
12554 : " INTERNALLENGTH = %s",
12555 : qualtypname,
12556 566 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12557 :
12558 : /* regproc result is sufficiently quoted already */
12559 566 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12560 566 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12561 566 : if (OidIsValid(typreceiveoid))
12562 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12563 566 : if (OidIsValid(typsendoid))
12564 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12565 566 : if (OidIsValid(typmodinoid))
12566 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12567 566 : if (OidIsValid(typmodoutoid))
12568 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12569 566 : if (OidIsValid(typanalyzeoid))
12570 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12571 :
12572 566 : if (strcmp(typcollatable, "t") == 0)
12573 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12574 :
12575 566 : if (typdefault != NULL)
12576 : {
12577 90 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12578 90 : if (typdefault_is_literal)
12579 90 : appendStringLiteralAH(q, typdefault, fout);
12580 : else
12581 0 : appendPQExpBufferStr(q, typdefault);
12582 : }
12583 :
12584 566 : if (OidIsValid(typsubscriptoid))
12585 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12586 :
12587 566 : if (OidIsValid(tyinfo->typelem))
12588 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12589 52 : getFormattedTypeName(fout, tyinfo->typelem,
12590 : zeroIsError));
12591 :
12592 566 : if (strcmp(typcategory, "U") != 0)
12593 : {
12594 316 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12595 316 : appendStringLiteralAH(q, typcategory, fout);
12596 : }
12597 :
12598 566 : if (strcmp(typispreferred, "t") == 0)
12599 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12600 :
12601 566 : if (typdelim && strcmp(typdelim, ",") != 0)
12602 : {
12603 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12604 6 : appendStringLiteralAH(q, typdelim, fout);
12605 : }
12606 :
12607 566 : if (*typalign == TYPALIGN_CHAR)
12608 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12609 542 : else if (*typalign == TYPALIGN_SHORT)
12610 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12611 530 : else if (*typalign == TYPALIGN_INT)
12612 380 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12613 150 : else if (*typalign == TYPALIGN_DOUBLE)
12614 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12615 :
12616 566 : if (*typstorage == TYPSTORAGE_PLAIN)
12617 416 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12618 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12619 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12620 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12621 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12622 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12623 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12624 :
12625 566 : if (strcmp(typbyval, "t") == 0)
12626 274 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12627 :
12628 566 : appendPQExpBufferStr(q, "\n);\n");
12629 :
12630 566 : if (dopt->binary_upgrade)
12631 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12632 : "TYPE", qtypname,
12633 16 : tyinfo->dobj.namespace->dobj.name);
12634 :
12635 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12636 566 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12637 566 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12638 : .namespace = tyinfo->dobj.namespace->dobj.name,
12639 : .owner = tyinfo->rolname,
12640 : .description = "TYPE",
12641 : .section = SECTION_PRE_DATA,
12642 : .createStmt = q->data,
12643 : .dropStmt = delq->data));
12644 :
12645 : /* Dump Type Comments and Security Labels */
12646 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12647 496 : dumpComment(fout, "TYPE", qtypname,
12648 496 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12649 496 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12650 :
12651 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12652 0 : dumpSecLabel(fout, "TYPE", qtypname,
12653 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12654 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12655 :
12656 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12657 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12658 : qtypname, NULL,
12659 70 : tyinfo->dobj.namespace->dobj.name,
12660 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12661 :
12662 566 : PQclear(res);
12663 566 : destroyPQExpBuffer(q);
12664 566 : destroyPQExpBuffer(delq);
12665 566 : destroyPQExpBuffer(query);
12666 566 : free(qtypname);
12667 566 : free(qualtypname);
12668 566 : }
12669 :
12670 : /*
12671 : * dumpDomain
12672 : * writes out to fout the queries to recreate a user-defined domain
12673 : */
12674 : static void
12675 310 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12676 : {
12677 310 : DumpOptions *dopt = fout->dopt;
12678 310 : PQExpBuffer q = createPQExpBuffer();
12679 310 : PQExpBuffer delq = createPQExpBuffer();
12680 310 : PQExpBuffer query = createPQExpBuffer();
12681 : PGresult *res;
12682 : int i;
12683 : char *qtypname;
12684 : char *qualtypname;
12685 : char *typnotnull;
12686 : char *typdefn;
12687 : char *typdefault;
12688 : Oid typcollation;
12689 310 : bool typdefault_is_literal = false;
12690 :
12691 310 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12692 : {
12693 : /* Set up query for domain-specific details */
12694 80 : appendPQExpBufferStr(query,
12695 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12696 :
12697 80 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12698 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12699 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12700 : "t.typdefault, "
12701 : "CASE WHEN t.typcollation <> u.typcollation "
12702 : "THEN t.typcollation ELSE 0 END AS typcollation "
12703 : "FROM pg_catalog.pg_type t "
12704 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12705 : "WHERE t.oid = $1");
12706 :
12707 80 : ExecuteSqlStatement(fout, query->data);
12708 :
12709 80 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12710 : }
12711 :
12712 310 : printfPQExpBuffer(query,
12713 : "EXECUTE dumpDomain('%u')",
12714 310 : tyinfo->dobj.catId.oid);
12715 :
12716 310 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12717 :
12718 310 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12719 310 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12720 310 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12721 80 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12722 230 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12723 : {
12724 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12725 0 : typdefault_is_literal = true; /* it needs quotes */
12726 : }
12727 : else
12728 230 : typdefault = NULL;
12729 310 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12730 :
12731 310 : if (dopt->binary_upgrade)
12732 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12733 50 : tyinfo->dobj.catId.oid,
12734 : true, /* force array type */
12735 : false); /* force multirange type */
12736 :
12737 310 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12738 310 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12739 :
12740 310 : appendPQExpBuffer(q,
12741 : "CREATE DOMAIN %s AS %s",
12742 : qualtypname,
12743 : typdefn);
12744 :
12745 : /* Print collation only if different from base type's collation */
12746 310 : if (OidIsValid(typcollation))
12747 : {
12748 : CollInfo *coll;
12749 :
12750 70 : coll = findCollationByOid(typcollation);
12751 70 : if (coll)
12752 70 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12753 : }
12754 :
12755 : /*
12756 : * Print a not-null constraint if there's one. In servers older than 17
12757 : * these don't have names, so just print it unadorned; in newer ones they
12758 : * do, but most of the time it's going to be the standard generated one,
12759 : * so omit the name in that case also.
12760 : */
12761 310 : if (typnotnull[0] == 't')
12762 : {
12763 100 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12764 0 : appendPQExpBufferStr(q, " NOT NULL");
12765 : else
12766 : {
12767 100 : ConstraintInfo *notnull = tyinfo->notnull;
12768 :
12769 100 : if (!notnull->separate)
12770 : {
12771 : char *default_name;
12772 :
12773 : /* XXX should match ChooseConstraintName better */
12774 100 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12775 :
12776 100 : if (strcmp(default_name, notnull->dobj.name) == 0)
12777 30 : appendPQExpBufferStr(q, " NOT NULL");
12778 : else
12779 70 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12780 70 : fmtId(notnull->dobj.name), notnull->condef);
12781 100 : free(default_name);
12782 : }
12783 : }
12784 : }
12785 :
12786 310 : if (typdefault != NULL)
12787 : {
12788 80 : appendPQExpBufferStr(q, " DEFAULT ");
12789 80 : if (typdefault_is_literal)
12790 0 : appendStringLiteralAH(q, typdefault, fout);
12791 : else
12792 80 : appendPQExpBufferStr(q, typdefault);
12793 : }
12794 :
12795 310 : PQclear(res);
12796 :
12797 : /*
12798 : * Add any CHECK constraints for the domain
12799 : */
12800 530 : for (i = 0; i < tyinfo->nDomChecks; i++)
12801 : {
12802 220 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12803 :
12804 220 : if (!domcheck->separate && domcheck->contype == 'c')
12805 210 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12806 210 : fmtId(domcheck->dobj.name), domcheck->condef);
12807 : }
12808 :
12809 310 : appendPQExpBufferStr(q, ";\n");
12810 :
12811 310 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12812 :
12813 310 : if (dopt->binary_upgrade)
12814 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12815 : "DOMAIN", qtypname,
12816 50 : tyinfo->dobj.namespace->dobj.name);
12817 :
12818 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12819 310 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12820 310 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12821 : .namespace = tyinfo->dobj.namespace->dobj.name,
12822 : .owner = tyinfo->rolname,
12823 : .description = "DOMAIN",
12824 : .section = SECTION_PRE_DATA,
12825 : .createStmt = q->data,
12826 : .dropStmt = delq->data));
12827 :
12828 : /* Dump Domain Comments and Security Labels */
12829 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12830 0 : dumpComment(fout, "DOMAIN", qtypname,
12831 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12832 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12833 :
12834 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12835 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12836 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12837 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12838 :
12839 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12840 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12841 : qtypname, NULL,
12842 70 : tyinfo->dobj.namespace->dobj.name,
12843 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12844 :
12845 : /* Dump any per-constraint comments */
12846 530 : for (i = 0; i < tyinfo->nDomChecks; i++)
12847 : {
12848 220 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12849 : PQExpBuffer conprefix;
12850 :
12851 : /* but only if the constraint itself was dumped here */
12852 220 : if (domcheck->separate)
12853 10 : continue;
12854 :
12855 210 : conprefix = createPQExpBuffer();
12856 210 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12857 210 : fmtId(domcheck->dobj.name));
12858 :
12859 210 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12860 70 : dumpComment(fout, conprefix->data, qtypname,
12861 70 : tyinfo->dobj.namespace->dobj.name,
12862 70 : tyinfo->rolname,
12863 70 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12864 :
12865 210 : destroyPQExpBuffer(conprefix);
12866 : }
12867 :
12868 : /*
12869 : * And a comment on the not-null constraint, if there's one -- but only if
12870 : * the constraint itself was dumped here
12871 : */
12872 310 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12873 : {
12874 100 : PQExpBuffer conprefix = createPQExpBuffer();
12875 :
12876 100 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12877 100 : fmtId(tyinfo->notnull->dobj.name));
12878 :
12879 100 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12880 70 : dumpComment(fout, conprefix->data, qtypname,
12881 70 : tyinfo->dobj.namespace->dobj.name,
12882 70 : tyinfo->rolname,
12883 70 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12884 100 : destroyPQExpBuffer(conprefix);
12885 : }
12886 :
12887 310 : destroyPQExpBuffer(q);
12888 310 : destroyPQExpBuffer(delq);
12889 310 : destroyPQExpBuffer(query);
12890 310 : free(qtypname);
12891 310 : free(qualtypname);
12892 310 : }
12893 :
12894 : /*
12895 : * dumpCompositeType
12896 : * writes out to fout the queries to recreate a user-defined stand-alone
12897 : * composite type
12898 : */
12899 : static void
12900 266 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12901 : {
12902 266 : DumpOptions *dopt = fout->dopt;
12903 266 : PQExpBuffer q = createPQExpBuffer();
12904 266 : PQExpBuffer dropped = createPQExpBuffer();
12905 266 : PQExpBuffer delq = createPQExpBuffer();
12906 266 : PQExpBuffer query = createPQExpBuffer();
12907 : PGresult *res;
12908 : char *qtypname;
12909 : char *qualtypname;
12910 : int ntups;
12911 : int i_attname;
12912 : int i_atttypdefn;
12913 : int i_attlen;
12914 : int i_attalign;
12915 : int i_attisdropped;
12916 : int i_attcollation;
12917 : int i;
12918 : int actual_atts;
12919 :
12920 266 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12921 : {
12922 : /*
12923 : * Set up query for type-specific details.
12924 : *
12925 : * Since we only want to dump COLLATE clauses for attributes whose
12926 : * collation is different from their type's default, we use a CASE
12927 : * here to suppress uninteresting attcollations cheaply. atttypid
12928 : * will be 0 for dropped columns; collation does not matter for those.
12929 : */
12930 116 : appendPQExpBufferStr(query,
12931 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12932 : "SELECT a.attname, a.attnum, "
12933 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12934 : "a.attlen, a.attalign, a.attisdropped, "
12935 : "CASE WHEN a.attcollation <> at.typcollation "
12936 : "THEN a.attcollation ELSE 0 END AS attcollation "
12937 : "FROM pg_catalog.pg_type ct "
12938 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12939 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12940 : "WHERE ct.oid = $1 "
12941 : "ORDER BY a.attnum");
12942 :
12943 116 : ExecuteSqlStatement(fout, query->data);
12944 :
12945 116 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12946 : }
12947 :
12948 266 : printfPQExpBuffer(query,
12949 : "EXECUTE dumpCompositeType('%u')",
12950 266 : tyinfo->dobj.catId.oid);
12951 :
12952 266 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12953 :
12954 266 : ntups = PQntuples(res);
12955 :
12956 266 : i_attname = PQfnumber(res, "attname");
12957 266 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12958 266 : i_attlen = PQfnumber(res, "attlen");
12959 266 : i_attalign = PQfnumber(res, "attalign");
12960 266 : i_attisdropped = PQfnumber(res, "attisdropped");
12961 266 : i_attcollation = PQfnumber(res, "attcollation");
12962 :
12963 266 : if (dopt->binary_upgrade)
12964 : {
12965 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12966 36 : tyinfo->dobj.catId.oid,
12967 : false, false);
12968 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12969 : }
12970 :
12971 266 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12972 266 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12973 :
12974 266 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12975 : qualtypname);
12976 :
12977 266 : actual_atts = 0;
12978 842 : for (i = 0; i < ntups; i++)
12979 : {
12980 : char *attname;
12981 : char *atttypdefn;
12982 : char *attlen;
12983 : char *attalign;
12984 : bool attisdropped;
12985 : Oid attcollation;
12986 :
12987 576 : attname = PQgetvalue(res, i, i_attname);
12988 576 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12989 576 : attlen = PQgetvalue(res, i, i_attlen);
12990 576 : attalign = PQgetvalue(res, i, i_attalign);
12991 576 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12992 576 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12993 :
12994 576 : if (attisdropped && !dopt->binary_upgrade)
12995 16 : continue;
12996 :
12997 : /* Format properly if not first attr */
12998 560 : if (actual_atts++ > 0)
12999 294 : appendPQExpBufferChar(q, ',');
13000 560 : appendPQExpBufferStr(q, "\n\t");
13001 :
13002 560 : if (!attisdropped)
13003 : {
13004 556 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13005 :
13006 : /* Add collation if not default for the column type */
13007 556 : if (OidIsValid(attcollation))
13008 : {
13009 : CollInfo *coll;
13010 :
13011 0 : coll = findCollationByOid(attcollation);
13012 0 : if (coll)
13013 0 : appendPQExpBuffer(q, " COLLATE %s",
13014 0 : fmtQualifiedDumpable(coll));
13015 : }
13016 : }
13017 : else
13018 : {
13019 : /*
13020 : * This is a dropped attribute and we're in binary_upgrade mode.
13021 : * Insert a placeholder for it in the CREATE TYPE command, and set
13022 : * length and alignment with direct UPDATE to the catalogs
13023 : * afterwards. See similar code in dumpTableSchema().
13024 : */
13025 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13026 :
13027 : /* stash separately for insertion after the CREATE TYPE */
13028 4 : appendPQExpBufferStr(dropped,
13029 : "\n-- For binary upgrade, recreate dropped column.\n");
13030 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13031 : "SET attlen = %s, "
13032 : "attalign = '%s', attbyval = false\n"
13033 : "WHERE attname = ", attlen, attalign);
13034 4 : appendStringLiteralAH(dropped, attname, fout);
13035 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13036 4 : appendStringLiteralAH(dropped, qualtypname, fout);
13037 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13038 :
13039 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13040 : qualtypname);
13041 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13042 : fmtId(attname));
13043 : }
13044 : }
13045 266 : appendPQExpBufferStr(q, "\n);\n");
13046 266 : appendPQExpBufferStr(q, dropped->data);
13047 :
13048 266 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13049 :
13050 266 : if (dopt->binary_upgrade)
13051 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13052 : "TYPE", qtypname,
13053 36 : tyinfo->dobj.namespace->dobj.name);
13054 :
13055 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13056 232 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13057 232 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13058 : .namespace = tyinfo->dobj.namespace->dobj.name,
13059 : .owner = tyinfo->rolname,
13060 : .description = "TYPE",
13061 : .section = SECTION_PRE_DATA,
13062 : .createStmt = q->data,
13063 : .dropStmt = delq->data));
13064 :
13065 :
13066 : /* Dump Type Comments and Security Labels */
13067 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13068 70 : dumpComment(fout, "TYPE", qtypname,
13069 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13070 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13071 :
13072 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13073 0 : dumpSecLabel(fout, "TYPE", qtypname,
13074 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13075 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13076 :
13077 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13078 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13079 : qtypname, NULL,
13080 36 : tyinfo->dobj.namespace->dobj.name,
13081 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
13082 :
13083 : /* Dump any per-column comments */
13084 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13085 70 : dumpCompositeTypeColComments(fout, tyinfo, res);
13086 :
13087 266 : PQclear(res);
13088 266 : destroyPQExpBuffer(q);
13089 266 : destroyPQExpBuffer(dropped);
13090 266 : destroyPQExpBuffer(delq);
13091 266 : destroyPQExpBuffer(query);
13092 266 : free(qtypname);
13093 266 : free(qualtypname);
13094 266 : }
13095 :
13096 : /*
13097 : * dumpCompositeTypeColComments
13098 : * writes out to fout the queries to recreate comments on the columns of
13099 : * a user-defined stand-alone composite type.
13100 : *
13101 : * The caller has already made a query to collect the names and attnums
13102 : * of the type's columns, so we just pass that result into here rather
13103 : * than reading them again.
13104 : */
13105 : static void
13106 70 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13107 : PGresult *res)
13108 : {
13109 : CommentItem *comments;
13110 : int ncomments;
13111 : PQExpBuffer query;
13112 : PQExpBuffer target;
13113 : int i;
13114 : int ntups;
13115 : int i_attname;
13116 : int i_attnum;
13117 : int i_attisdropped;
13118 :
13119 : /* do nothing, if --no-comments is supplied */
13120 70 : if (fout->dopt->no_comments)
13121 0 : return;
13122 :
13123 : /* Search for comments associated with type's pg_class OID */
13124 70 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13125 : &comments);
13126 :
13127 : /* If no comments exist, we're done */
13128 70 : if (ncomments <= 0)
13129 0 : return;
13130 :
13131 : /* Build COMMENT ON statements */
13132 70 : query = createPQExpBuffer();
13133 70 : target = createPQExpBuffer();
13134 :
13135 70 : ntups = PQntuples(res);
13136 70 : i_attnum = PQfnumber(res, "attnum");
13137 70 : i_attname = PQfnumber(res, "attname");
13138 70 : i_attisdropped = PQfnumber(res, "attisdropped");
13139 140 : while (ncomments > 0)
13140 : {
13141 : const char *attname;
13142 :
13143 70 : attname = NULL;
13144 70 : for (i = 0; i < ntups; i++)
13145 : {
13146 70 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13147 70 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13148 : {
13149 70 : attname = PQgetvalue(res, i, i_attname);
13150 70 : break;
13151 : }
13152 : }
13153 70 : if (attname) /* just in case we don't find it */
13154 : {
13155 70 : const char *descr = comments->descr;
13156 :
13157 70 : resetPQExpBuffer(target);
13158 70 : appendPQExpBuffer(target, "COLUMN %s.",
13159 70 : fmtId(tyinfo->dobj.name));
13160 70 : appendPQExpBufferStr(target, fmtId(attname));
13161 :
13162 70 : resetPQExpBuffer(query);
13163 70 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13164 70 : fmtQualifiedDumpable(tyinfo));
13165 70 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13166 70 : appendStringLiteralAH(query, descr, fout);
13167 70 : appendPQExpBufferStr(query, ";\n");
13168 :
13169 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13170 70 : ARCHIVE_OPTS(.tag = target->data,
13171 : .namespace = tyinfo->dobj.namespace->dobj.name,
13172 : .owner = tyinfo->rolname,
13173 : .description = "COMMENT",
13174 : .section = SECTION_NONE,
13175 : .createStmt = query->data,
13176 : .deps = &(tyinfo->dobj.dumpId),
13177 : .nDeps = 1));
13178 : }
13179 :
13180 70 : comments++;
13181 70 : ncomments--;
13182 : }
13183 :
13184 70 : destroyPQExpBuffer(query);
13185 70 : destroyPQExpBuffer(target);
13186 : }
13187 :
13188 : /*
13189 : * dumpShellType
13190 : * writes out to fout the queries to create a shell type
13191 : *
13192 : * We dump a shell definition in advance of the I/O functions for the type.
13193 : */
13194 : static void
13195 152 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13196 : {
13197 152 : DumpOptions *dopt = fout->dopt;
13198 : PQExpBuffer q;
13199 :
13200 : /* Do nothing if not dumping schema */
13201 152 : if (!dopt->dumpSchema)
13202 12 : return;
13203 :
13204 140 : q = createPQExpBuffer();
13205 :
13206 : /*
13207 : * Note the lack of a DROP command for the shell type; any required DROP
13208 : * is driven off the base type entry, instead. This interacts with
13209 : * _printTocEntry()'s use of the presence of a DROP command to decide
13210 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13211 : * the shell type's owner immediately on creation; that should happen only
13212 : * after it's filled in, otherwise the backend complains.
13213 : */
13214 :
13215 140 : if (dopt->binary_upgrade)
13216 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13217 16 : stinfo->baseType->dobj.catId.oid,
13218 : false, false);
13219 :
13220 140 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13221 140 : fmtQualifiedDumpable(stinfo));
13222 :
13223 140 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13224 140 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13225 140 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13226 : .namespace = stinfo->dobj.namespace->dobj.name,
13227 : .owner = stinfo->baseType->rolname,
13228 : .description = "SHELL TYPE",
13229 : .section = SECTION_PRE_DATA,
13230 : .createStmt = q->data));
13231 :
13232 140 : destroyPQExpBuffer(q);
13233 : }
13234 :
13235 : /*
13236 : * dumpProcLang
13237 : * writes out to fout the queries to recreate a user-defined
13238 : * procedural language
13239 : */
13240 : static void
13241 176 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13242 : {
13243 176 : DumpOptions *dopt = fout->dopt;
13244 : PQExpBuffer defqry;
13245 : PQExpBuffer delqry;
13246 : bool useParams;
13247 : char *qlanname;
13248 : FuncInfo *funcInfo;
13249 176 : FuncInfo *inlineInfo = NULL;
13250 176 : FuncInfo *validatorInfo = NULL;
13251 :
13252 : /* Do nothing if not dumping schema */
13253 176 : if (!dopt->dumpSchema)
13254 26 : return;
13255 :
13256 : /*
13257 : * Try to find the support function(s). It is not an error if we don't
13258 : * find them --- if the functions are in the pg_catalog schema, as is
13259 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13260 : * we will emit a parameterless CREATE LANGUAGE command, which will
13261 : * require PL template knowledge in the backend to reload.)
13262 : */
13263 :
13264 150 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13265 150 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13266 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13267 :
13268 150 : if (OidIsValid(plang->laninline))
13269 : {
13270 82 : inlineInfo = findFuncByOid(plang->laninline);
13271 82 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13272 2 : inlineInfo = NULL;
13273 : }
13274 :
13275 150 : if (OidIsValid(plang->lanvalidator))
13276 : {
13277 82 : validatorInfo = findFuncByOid(plang->lanvalidator);
13278 82 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13279 2 : validatorInfo = NULL;
13280 : }
13281 :
13282 : /*
13283 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13284 : * parameters. Otherwise, we'll write a parameterless command, which will
13285 : * be interpreted as CREATE EXTENSION.
13286 : */
13287 66 : useParams = (funcInfo != NULL &&
13288 282 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13289 66 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13290 :
13291 150 : defqry = createPQExpBuffer();
13292 150 : delqry = createPQExpBuffer();
13293 :
13294 150 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13295 :
13296 150 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13297 : qlanname);
13298 :
13299 150 : if (useParams)
13300 : {
13301 66 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13302 66 : plang->lanpltrusted ? "TRUSTED " : "",
13303 : qlanname);
13304 66 : appendPQExpBuffer(defqry, " HANDLER %s",
13305 66 : fmtQualifiedDumpable(funcInfo));
13306 66 : if (OidIsValid(plang->laninline))
13307 0 : appendPQExpBuffer(defqry, " INLINE %s",
13308 0 : fmtQualifiedDumpable(inlineInfo));
13309 66 : if (OidIsValid(plang->lanvalidator))
13310 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13311 0 : fmtQualifiedDumpable(validatorInfo));
13312 : }
13313 : else
13314 : {
13315 : /*
13316 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13317 : * command will not fail if the language is preinstalled in the target
13318 : * database.
13319 : *
13320 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13321 : * EXISTS; perhaps we should emit that instead? But it might just add
13322 : * confusion.
13323 : */
13324 84 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13325 : qlanname);
13326 : }
13327 150 : appendPQExpBufferStr(defqry, ";\n");
13328 :
13329 150 : if (dopt->binary_upgrade)
13330 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13331 : "LANGUAGE", qlanname, NULL);
13332 :
13333 150 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13334 68 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13335 68 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13336 : .owner = plang->lanowner,
13337 : .description = "PROCEDURAL LANGUAGE",
13338 : .section = SECTION_PRE_DATA,
13339 : .createStmt = defqry->data,
13340 : .dropStmt = delqry->data,
13341 : ));
13342 :
13343 : /* Dump Proc Lang Comments and Security Labels */
13344 150 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13345 0 : dumpComment(fout, "LANGUAGE", qlanname,
13346 0 : NULL, plang->lanowner,
13347 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13348 :
13349 150 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13350 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13351 0 : NULL, plang->lanowner,
13352 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13353 :
13354 150 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13355 82 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13356 : qlanname, NULL, NULL,
13357 82 : NULL, plang->lanowner, &plang->dacl);
13358 :
13359 150 : free(qlanname);
13360 :
13361 150 : destroyPQExpBuffer(defqry);
13362 150 : destroyPQExpBuffer(delqry);
13363 : }
13364 :
13365 : /*
13366 : * format_function_arguments: generate function name and argument list
13367 : *
13368 : * This is used when we can rely on pg_get_function_arguments to format
13369 : * the argument list. Note, however, that pg_get_function_arguments
13370 : * does not special-case zero-argument aggregates.
13371 : */
13372 : static char *
13373 8296 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13374 : {
13375 : PQExpBufferData fn;
13376 :
13377 8296 : initPQExpBuffer(&fn);
13378 8296 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13379 8296 : if (is_agg && finfo->nargs == 0)
13380 160 : appendPQExpBufferStr(&fn, "(*)");
13381 : else
13382 8136 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13383 8296 : return fn.data;
13384 : }
13385 :
13386 : /*
13387 : * format_function_signature: generate function name and argument list
13388 : *
13389 : * Only a minimal list of input argument types is generated; this is
13390 : * sufficient to reference the function, but not to define it.
13391 : *
13392 : * If honor_quotes is false then the function name is never quoted.
13393 : * This is appropriate for use in TOC tags, but not in SQL commands.
13394 : */
13395 : static char *
13396 4382 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13397 : {
13398 : PQExpBufferData fn;
13399 : int j;
13400 :
13401 4382 : initPQExpBuffer(&fn);
13402 4382 : if (honor_quotes)
13403 810 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13404 : else
13405 3572 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13406 8022 : for (j = 0; j < finfo->nargs; j++)
13407 : {
13408 3640 : if (j > 0)
13409 844 : appendPQExpBufferStr(&fn, ", ");
13410 :
13411 3640 : appendPQExpBufferStr(&fn,
13412 3640 : getFormattedTypeName(fout, finfo->argtypes[j],
13413 : zeroIsError));
13414 : }
13415 4382 : appendPQExpBufferChar(&fn, ')');
13416 4382 : return fn.data;
13417 : }
13418 :
13419 :
13420 : /*
13421 : * dumpFunc:
13422 : * dump out one function
13423 : */
13424 : static void
13425 3696 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13426 : {
13427 3696 : DumpOptions *dopt = fout->dopt;
13428 : PQExpBuffer query;
13429 : PQExpBuffer q;
13430 : PQExpBuffer delqry;
13431 : PQExpBuffer asPart;
13432 : PGresult *res;
13433 : char *funcsig; /* identity signature */
13434 3696 : char *funcfullsig = NULL; /* full signature */
13435 : char *funcsig_tag;
13436 : char *qual_funcsig;
13437 : char *proretset;
13438 : char *prosrc;
13439 : char *probin;
13440 : char *prosqlbody;
13441 : char *funcargs;
13442 : char *funciargs;
13443 : char *funcresult;
13444 : char *protrftypes;
13445 : char *prokind;
13446 : char *provolatile;
13447 : char *proisstrict;
13448 : char *prosecdef;
13449 : char *proleakproof;
13450 : char *proconfig;
13451 : char *procost;
13452 : char *prorows;
13453 : char *prosupport;
13454 : char *proparallel;
13455 : char *lanname;
13456 3696 : char **configitems = NULL;
13457 3696 : int nconfigitems = 0;
13458 : const char *keyword;
13459 :
13460 : /* Do nothing if not dumping schema */
13461 3696 : if (!dopt->dumpSchema)
13462 124 : return;
13463 :
13464 3572 : query = createPQExpBuffer();
13465 3572 : q = createPQExpBuffer();
13466 3572 : delqry = createPQExpBuffer();
13467 3572 : asPart = createPQExpBuffer();
13468 :
13469 3572 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13470 : {
13471 : /* Set up query for function-specific details */
13472 138 : appendPQExpBufferStr(query,
13473 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13474 :
13475 138 : appendPQExpBufferStr(query,
13476 : "SELECT\n"
13477 : "proretset,\n"
13478 : "prosrc,\n"
13479 : "probin,\n"
13480 : "provolatile,\n"
13481 : "proisstrict,\n"
13482 : "prosecdef,\n"
13483 : "lanname,\n"
13484 : "proconfig,\n"
13485 : "procost,\n"
13486 : "prorows,\n"
13487 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13488 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13489 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13490 : "proleakproof,\n");
13491 :
13492 138 : if (fout->remoteVersion >= 90500)
13493 138 : appendPQExpBufferStr(query,
13494 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13495 : else
13496 0 : appendPQExpBufferStr(query,
13497 : "NULL AS protrftypes,\n");
13498 :
13499 138 : if (fout->remoteVersion >= 90600)
13500 138 : appendPQExpBufferStr(query,
13501 : "proparallel,\n");
13502 : else
13503 0 : appendPQExpBufferStr(query,
13504 : "'u' AS proparallel,\n");
13505 :
13506 138 : if (fout->remoteVersion >= 110000)
13507 138 : appendPQExpBufferStr(query,
13508 : "prokind,\n");
13509 : else
13510 0 : appendPQExpBufferStr(query,
13511 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13512 :
13513 138 : if (fout->remoteVersion >= 120000)
13514 138 : appendPQExpBufferStr(query,
13515 : "prosupport,\n");
13516 : else
13517 0 : appendPQExpBufferStr(query,
13518 : "'-' AS prosupport,\n");
13519 :
13520 138 : if (fout->remoteVersion >= 140000)
13521 138 : appendPQExpBufferStr(query,
13522 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13523 : else
13524 0 : appendPQExpBufferStr(query,
13525 : "NULL AS prosqlbody\n");
13526 :
13527 138 : appendPQExpBufferStr(query,
13528 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13529 : "WHERE p.oid = $1 "
13530 : "AND l.oid = p.prolang");
13531 :
13532 138 : ExecuteSqlStatement(fout, query->data);
13533 :
13534 138 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13535 : }
13536 :
13537 3572 : printfPQExpBuffer(query,
13538 : "EXECUTE dumpFunc('%u')",
13539 3572 : finfo->dobj.catId.oid);
13540 :
13541 3572 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13542 :
13543 3572 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13544 3572 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13545 : {
13546 3470 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13547 3470 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13548 3470 : prosqlbody = NULL;
13549 : }
13550 : else
13551 : {
13552 102 : prosrc = NULL;
13553 102 : probin = NULL;
13554 102 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13555 : }
13556 3572 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13557 3572 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13558 3572 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13559 3572 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13560 3572 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13561 3572 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13562 3572 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13563 3572 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13564 3572 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13565 3572 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13566 3572 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13567 3572 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13568 3572 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13569 3572 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13570 3572 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13571 :
13572 : /*
13573 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13574 : * is used.
13575 : */
13576 3572 : if (prosqlbody)
13577 : {
13578 102 : appendPQExpBufferStr(asPart, prosqlbody);
13579 : }
13580 3470 : else if (probin[0] != '\0')
13581 : {
13582 280 : appendPQExpBufferStr(asPart, "AS ");
13583 280 : appendStringLiteralAH(asPart, probin, fout);
13584 280 : if (prosrc[0] != '\0')
13585 : {
13586 280 : appendPQExpBufferStr(asPart, ", ");
13587 :
13588 : /*
13589 : * where we have bin, use dollar quoting if allowed and src
13590 : * contains quote or backslash; else use regular quoting.
13591 : */
13592 280 : if (dopt->disable_dollar_quoting ||
13593 280 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13594 280 : appendStringLiteralAH(asPart, prosrc, fout);
13595 : else
13596 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13597 : }
13598 : }
13599 : else
13600 : {
13601 3190 : appendPQExpBufferStr(asPart, "AS ");
13602 : /* with no bin, dollar quote src unconditionally if allowed */
13603 3190 : if (dopt->disable_dollar_quoting)
13604 0 : appendStringLiteralAH(asPart, prosrc, fout);
13605 : else
13606 3190 : appendStringLiteralDQ(asPart, prosrc, NULL);
13607 : }
13608 :
13609 3572 : if (*proconfig)
13610 : {
13611 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13612 0 : pg_fatal("could not parse %s array", "proconfig");
13613 : }
13614 : else
13615 : {
13616 3542 : configitems = NULL;
13617 3542 : nconfigitems = 0;
13618 : }
13619 :
13620 3572 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13621 3572 : funcsig = format_function_arguments(finfo, funciargs, false);
13622 :
13623 3572 : funcsig_tag = format_function_signature(fout, finfo, false);
13624 :
13625 3572 : qual_funcsig = psprintf("%s.%s",
13626 3572 : fmtId(finfo->dobj.namespace->dobj.name),
13627 : funcsig);
13628 :
13629 3572 : if (prokind[0] == PROKIND_PROCEDURE)
13630 190 : keyword = "PROCEDURE";
13631 : else
13632 3382 : keyword = "FUNCTION"; /* works for window functions too */
13633 :
13634 3572 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13635 : keyword, qual_funcsig);
13636 :
13637 7144 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13638 : keyword,
13639 3572 : fmtId(finfo->dobj.namespace->dobj.name),
13640 : funcfullsig ? funcfullsig :
13641 : funcsig);
13642 :
13643 3572 : if (prokind[0] == PROKIND_PROCEDURE)
13644 : /* no result type to output */ ;
13645 3382 : else if (funcresult)
13646 3382 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13647 : else
13648 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13649 0 : (proretset[0] == 't') ? "SETOF " : "",
13650 0 : getFormattedTypeName(fout, finfo->prorettype,
13651 : zeroIsError));
13652 :
13653 3572 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13654 :
13655 3572 : if (*protrftypes)
13656 : {
13657 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13658 : int i;
13659 :
13660 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13661 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13662 0 : for (i = 0; typeids[i]; i++)
13663 : {
13664 0 : if (i != 0)
13665 0 : appendPQExpBufferStr(q, ", ");
13666 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13667 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13668 : }
13669 :
13670 0 : free(typeids);
13671 : }
13672 :
13673 3572 : if (prokind[0] == PROKIND_WINDOW)
13674 10 : appendPQExpBufferStr(q, " WINDOW");
13675 :
13676 3572 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13677 : {
13678 714 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13679 672 : appendPQExpBufferStr(q, " IMMUTABLE");
13680 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13681 42 : appendPQExpBufferStr(q, " STABLE");
13682 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13683 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13684 : finfo->dobj.name);
13685 : }
13686 :
13687 3572 : if (proisstrict[0] == 't')
13688 724 : appendPQExpBufferStr(q, " STRICT");
13689 :
13690 3572 : if (prosecdef[0] == 't')
13691 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13692 :
13693 3572 : if (proleakproof[0] == 't')
13694 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13695 :
13696 : /*
13697 : * COST and ROWS are emitted only if present and not default, so as not to
13698 : * break backwards-compatibility of the dump without need. Keep this code
13699 : * in sync with the defaults in functioncmds.c.
13700 : */
13701 3572 : if (strcmp(procost, "0") != 0)
13702 : {
13703 3572 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13704 : {
13705 : /* default cost is 1 */
13706 762 : if (strcmp(procost, "1") != 0)
13707 0 : appendPQExpBuffer(q, " COST %s", procost);
13708 : }
13709 : else
13710 : {
13711 : /* default cost is 100 */
13712 2810 : if (strcmp(procost, "100") != 0)
13713 12 : appendPQExpBuffer(q, " COST %s", procost);
13714 : }
13715 : }
13716 3572 : if (proretset[0] == 't' &&
13717 380 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13718 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13719 :
13720 3572 : if (strcmp(prosupport, "-") != 0)
13721 : {
13722 : /* We rely on regprocout to provide quoting and qualification */
13723 90 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13724 : }
13725 :
13726 3572 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13727 : {
13728 244 : if (proparallel[0] == PROPARALLEL_SAFE)
13729 234 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13730 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13731 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13732 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13733 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13734 : finfo->dobj.name);
13735 : }
13736 :
13737 3642 : for (int i = 0; i < nconfigitems; i++)
13738 : {
13739 : /* we feel free to scribble on configitems[] here */
13740 70 : char *configitem = configitems[i];
13741 : char *pos;
13742 :
13743 70 : pos = strchr(configitem, '=');
13744 70 : if (pos == NULL)
13745 0 : continue;
13746 70 : *pos++ = '\0';
13747 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13748 :
13749 : /*
13750 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13751 : * by flatten_set_variable_args() before they were put into the
13752 : * proconfig array. However, because the quoting rules used there
13753 : * aren't exactly like SQL's, we have to break the list value apart
13754 : * and then quote the elements as string literals. (The elements may
13755 : * be double-quoted as-is, but we can't just feed them to the SQL
13756 : * parser; it would do the wrong thing with elements that are
13757 : * zero-length or longer than NAMEDATALEN.)
13758 : *
13759 : * Variables that are not so marked should just be emitted as simple
13760 : * string literals. If the variable is not known to
13761 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13762 : * to use GUC_LIST_QUOTE for extension variables.
13763 : */
13764 70 : if (variable_is_guc_list_quote(configitem))
13765 : {
13766 : char **namelist;
13767 : char **nameptr;
13768 :
13769 : /* Parse string into list of identifiers */
13770 : /* this shouldn't fail really */
13771 20 : if (SplitGUCList(pos, ',', &namelist))
13772 : {
13773 70 : for (nameptr = namelist; *nameptr; nameptr++)
13774 : {
13775 50 : if (nameptr != namelist)
13776 30 : appendPQExpBufferStr(q, ", ");
13777 50 : appendStringLiteralAH(q, *nameptr, fout);
13778 : }
13779 : }
13780 20 : pg_free(namelist);
13781 : }
13782 : else
13783 50 : appendStringLiteralAH(q, pos, fout);
13784 : }
13785 :
13786 3572 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13787 :
13788 3572 : append_depends_on_extension(fout, q, &finfo->dobj,
13789 : "pg_catalog.pg_proc", keyword,
13790 : qual_funcsig);
13791 :
13792 3572 : if (dopt->binary_upgrade)
13793 586 : binary_upgrade_extension_member(q, &finfo->dobj,
13794 : keyword, funcsig,
13795 586 : finfo->dobj.namespace->dobj.name);
13796 :
13797 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13798 3368 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13799 3368 : ARCHIVE_OPTS(.tag = funcsig_tag,
13800 : .namespace = finfo->dobj.namespace->dobj.name,
13801 : .owner = finfo->rolname,
13802 : .description = keyword,
13803 : .section = finfo->postponed_def ?
13804 : SECTION_POST_DATA : SECTION_PRE_DATA,
13805 : .createStmt = q->data,
13806 : .dropStmt = delqry->data));
13807 :
13808 : /* Dump Function Comments and Security Labels */
13809 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13810 18 : dumpComment(fout, keyword, funcsig,
13811 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13812 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13813 :
13814 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13815 0 : dumpSecLabel(fout, keyword, funcsig,
13816 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13817 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13818 :
13819 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13820 212 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13821 : funcsig, NULL,
13822 212 : finfo->dobj.namespace->dobj.name,
13823 212 : NULL, finfo->rolname, &finfo->dacl);
13824 :
13825 3572 : PQclear(res);
13826 :
13827 3572 : destroyPQExpBuffer(query);
13828 3572 : destroyPQExpBuffer(q);
13829 3572 : destroyPQExpBuffer(delqry);
13830 3572 : destroyPQExpBuffer(asPart);
13831 3572 : free(funcsig);
13832 3572 : free(funcfullsig);
13833 3572 : free(funcsig_tag);
13834 3572 : free(qual_funcsig);
13835 3572 : free(configitems);
13836 : }
13837 :
13838 :
13839 : /*
13840 : * Dump a user-defined cast
13841 : */
13842 : static void
13843 140 : dumpCast(Archive *fout, const CastInfo *cast)
13844 : {
13845 140 : DumpOptions *dopt = fout->dopt;
13846 : PQExpBuffer defqry;
13847 : PQExpBuffer delqry;
13848 : PQExpBuffer labelq;
13849 : PQExpBuffer castargs;
13850 140 : FuncInfo *funcInfo = NULL;
13851 : const char *sourceType;
13852 : const char *targetType;
13853 :
13854 : /* Do nothing if not dumping schema */
13855 140 : if (!dopt->dumpSchema)
13856 12 : return;
13857 :
13858 : /* Cannot dump if we don't have the cast function's info */
13859 128 : if (OidIsValid(cast->castfunc))
13860 : {
13861 78 : funcInfo = findFuncByOid(cast->castfunc);
13862 78 : if (funcInfo == NULL)
13863 0 : pg_fatal("could not find function definition for function with OID %u",
13864 : cast->castfunc);
13865 : }
13866 :
13867 128 : defqry = createPQExpBuffer();
13868 128 : delqry = createPQExpBuffer();
13869 128 : labelq = createPQExpBuffer();
13870 128 : castargs = createPQExpBuffer();
13871 :
13872 128 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13873 128 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13874 128 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13875 : sourceType, targetType);
13876 :
13877 128 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13878 : sourceType, targetType);
13879 :
13880 128 : switch (cast->castmethod)
13881 : {
13882 50 : case COERCION_METHOD_BINARY:
13883 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13884 50 : break;
13885 0 : case COERCION_METHOD_INOUT:
13886 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13887 0 : break;
13888 78 : case COERCION_METHOD_FUNCTION:
13889 78 : if (funcInfo)
13890 : {
13891 78 : char *fsig = format_function_signature(fout, funcInfo, true);
13892 :
13893 : /*
13894 : * Always qualify the function name (format_function_signature
13895 : * won't qualify it).
13896 : */
13897 78 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13898 78 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13899 78 : free(fsig);
13900 : }
13901 : else
13902 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13903 78 : break;
13904 0 : default:
13905 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13906 : }
13907 :
13908 128 : if (cast->castcontext == 'a')
13909 68 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13910 60 : else if (cast->castcontext == 'i')
13911 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13912 128 : appendPQExpBufferStr(defqry, ";\n");
13913 :
13914 128 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13915 : sourceType, targetType);
13916 :
13917 128 : appendPQExpBuffer(castargs, "(%s AS %s)",
13918 : sourceType, targetType);
13919 :
13920 128 : if (dopt->binary_upgrade)
13921 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13922 14 : "CAST", castargs->data, NULL);
13923 :
13924 128 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13925 128 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13926 128 : ARCHIVE_OPTS(.tag = labelq->data,
13927 : .description = "CAST",
13928 : .section = SECTION_PRE_DATA,
13929 : .createStmt = defqry->data,
13930 : .dropStmt = delqry->data));
13931 :
13932 : /* Dump Cast Comments */
13933 128 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13934 0 : dumpComment(fout, "CAST", castargs->data,
13935 : NULL, "",
13936 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13937 :
13938 128 : destroyPQExpBuffer(defqry);
13939 128 : destroyPQExpBuffer(delqry);
13940 128 : destroyPQExpBuffer(labelq);
13941 128 : destroyPQExpBuffer(castargs);
13942 : }
13943 :
13944 : /*
13945 : * Dump a transform
13946 : */
13947 : static void
13948 90 : dumpTransform(Archive *fout, const TransformInfo *transform)
13949 : {
13950 90 : DumpOptions *dopt = fout->dopt;
13951 : PQExpBuffer defqry;
13952 : PQExpBuffer delqry;
13953 : PQExpBuffer labelq;
13954 : PQExpBuffer transformargs;
13955 90 : FuncInfo *fromsqlFuncInfo = NULL;
13956 90 : FuncInfo *tosqlFuncInfo = NULL;
13957 : char *lanname;
13958 : const char *transformType;
13959 :
13960 : /* Do nothing if not dumping schema */
13961 90 : if (!dopt->dumpSchema)
13962 12 : return;
13963 :
13964 : /* Cannot dump if we don't have the transform functions' info */
13965 78 : if (OidIsValid(transform->trffromsql))
13966 : {
13967 78 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13968 78 : if (fromsqlFuncInfo == NULL)
13969 0 : pg_fatal("could not find function definition for function with OID %u",
13970 : transform->trffromsql);
13971 : }
13972 78 : if (OidIsValid(transform->trftosql))
13973 : {
13974 78 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13975 78 : if (tosqlFuncInfo == NULL)
13976 0 : pg_fatal("could not find function definition for function with OID %u",
13977 : transform->trftosql);
13978 : }
13979 :
13980 78 : defqry = createPQExpBuffer();
13981 78 : delqry = createPQExpBuffer();
13982 78 : labelq = createPQExpBuffer();
13983 78 : transformargs = createPQExpBuffer();
13984 :
13985 78 : lanname = get_language_name(fout, transform->trflang);
13986 78 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13987 :
13988 78 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13989 : transformType, lanname);
13990 :
13991 78 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13992 : transformType, lanname);
13993 :
13994 78 : if (!transform->trffromsql && !transform->trftosql)
13995 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13996 :
13997 78 : if (transform->trffromsql)
13998 : {
13999 78 : if (fromsqlFuncInfo)
14000 : {
14001 78 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14002 :
14003 : /*
14004 : * Always qualify the function name (format_function_signature
14005 : * won't qualify it).
14006 : */
14007 78 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14008 78 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14009 78 : free(fsig);
14010 : }
14011 : else
14012 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14013 : }
14014 :
14015 78 : if (transform->trftosql)
14016 : {
14017 78 : if (transform->trffromsql)
14018 78 : appendPQExpBufferStr(defqry, ", ");
14019 :
14020 78 : if (tosqlFuncInfo)
14021 : {
14022 78 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14023 :
14024 : /*
14025 : * Always qualify the function name (format_function_signature
14026 : * won't qualify it).
14027 : */
14028 78 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14029 78 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14030 78 : free(fsig);
14031 : }
14032 : else
14033 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14034 : }
14035 :
14036 78 : appendPQExpBufferStr(defqry, ");\n");
14037 :
14038 78 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14039 : transformType, lanname);
14040 :
14041 78 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14042 : transformType, lanname);
14043 :
14044 78 : if (dopt->binary_upgrade)
14045 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
14046 4 : "TRANSFORM", transformargs->data, NULL);
14047 :
14048 78 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14049 78 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14050 78 : ARCHIVE_OPTS(.tag = labelq->data,
14051 : .description = "TRANSFORM",
14052 : .section = SECTION_PRE_DATA,
14053 : .createStmt = defqry->data,
14054 : .dropStmt = delqry->data,
14055 : .deps = transform->dobj.dependencies,
14056 : .nDeps = transform->dobj.nDeps));
14057 :
14058 : /* Dump Transform Comments */
14059 78 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14060 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14061 : NULL, "",
14062 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14063 :
14064 78 : free(lanname);
14065 78 : destroyPQExpBuffer(defqry);
14066 78 : destroyPQExpBuffer(delqry);
14067 78 : destroyPQExpBuffer(labelq);
14068 78 : destroyPQExpBuffer(transformargs);
14069 : }
14070 :
14071 :
14072 : /*
14073 : * dumpOpr
14074 : * write out a single operator definition
14075 : */
14076 : static void
14077 5014 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14078 : {
14079 5014 : DumpOptions *dopt = fout->dopt;
14080 : PQExpBuffer query;
14081 : PQExpBuffer q;
14082 : PQExpBuffer delq;
14083 : PQExpBuffer oprid;
14084 : PQExpBuffer details;
14085 : PGresult *res;
14086 : int i_oprkind;
14087 : int i_oprcode;
14088 : int i_oprleft;
14089 : int i_oprright;
14090 : int i_oprcom;
14091 : int i_oprnegate;
14092 : int i_oprrest;
14093 : int i_oprjoin;
14094 : int i_oprcanmerge;
14095 : int i_oprcanhash;
14096 : char *oprkind;
14097 : char *oprcode;
14098 : char *oprleft;
14099 : char *oprright;
14100 : char *oprcom;
14101 : char *oprnegate;
14102 : char *oprrest;
14103 : char *oprjoin;
14104 : char *oprcanmerge;
14105 : char *oprcanhash;
14106 : char *oprregproc;
14107 : char *oprref;
14108 :
14109 : /* Do nothing if not dumping schema */
14110 5014 : if (!dopt->dumpSchema)
14111 12 : return;
14112 :
14113 : /*
14114 : * some operators are invalid because they were the result of user
14115 : * defining operators before commutators exist
14116 : */
14117 5002 : if (!OidIsValid(oprinfo->oprcode))
14118 28 : return;
14119 :
14120 4974 : query = createPQExpBuffer();
14121 4974 : q = createPQExpBuffer();
14122 4974 : delq = createPQExpBuffer();
14123 4974 : oprid = createPQExpBuffer();
14124 4974 : details = createPQExpBuffer();
14125 :
14126 4974 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14127 : {
14128 : /* Set up query for operator-specific details */
14129 86 : appendPQExpBufferStr(query,
14130 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14131 : "SELECT oprkind, "
14132 : "oprcode::pg_catalog.regprocedure, "
14133 : "oprleft::pg_catalog.regtype, "
14134 : "oprright::pg_catalog.regtype, "
14135 : "oprcom, "
14136 : "oprnegate, "
14137 : "oprrest::pg_catalog.regprocedure, "
14138 : "oprjoin::pg_catalog.regprocedure, "
14139 : "oprcanmerge, oprcanhash "
14140 : "FROM pg_catalog.pg_operator "
14141 : "WHERE oid = $1");
14142 :
14143 86 : ExecuteSqlStatement(fout, query->data);
14144 :
14145 86 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14146 : }
14147 :
14148 4974 : printfPQExpBuffer(query,
14149 : "EXECUTE dumpOpr('%u')",
14150 4974 : oprinfo->dobj.catId.oid);
14151 :
14152 4974 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14153 :
14154 4974 : i_oprkind = PQfnumber(res, "oprkind");
14155 4974 : i_oprcode = PQfnumber(res, "oprcode");
14156 4974 : i_oprleft = PQfnumber(res, "oprleft");
14157 4974 : i_oprright = PQfnumber(res, "oprright");
14158 4974 : i_oprcom = PQfnumber(res, "oprcom");
14159 4974 : i_oprnegate = PQfnumber(res, "oprnegate");
14160 4974 : i_oprrest = PQfnumber(res, "oprrest");
14161 4974 : i_oprjoin = PQfnumber(res, "oprjoin");
14162 4974 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14163 4974 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14164 :
14165 4974 : oprkind = PQgetvalue(res, 0, i_oprkind);
14166 4974 : oprcode = PQgetvalue(res, 0, i_oprcode);
14167 4974 : oprleft = PQgetvalue(res, 0, i_oprleft);
14168 4974 : oprright = PQgetvalue(res, 0, i_oprright);
14169 4974 : oprcom = PQgetvalue(res, 0, i_oprcom);
14170 4974 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14171 4974 : oprrest = PQgetvalue(res, 0, i_oprrest);
14172 4974 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14173 4974 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14174 4974 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14175 :
14176 : /* In PG14 upwards postfix operator support does not exist anymore. */
14177 4974 : if (strcmp(oprkind, "r") == 0)
14178 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14179 : oprcode);
14180 :
14181 4974 : oprregproc = convertRegProcReference(oprcode);
14182 4974 : if (oprregproc)
14183 : {
14184 4974 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14185 4974 : free(oprregproc);
14186 : }
14187 :
14188 4974 : appendPQExpBuffer(oprid, "%s (",
14189 4974 : oprinfo->dobj.name);
14190 :
14191 : /*
14192 : * right unary means there's a left arg and left unary means there's a
14193 : * right arg. (Although the "r" case is dead code for PG14 and later,
14194 : * continue to support it in case we're dumping from an old server.)
14195 : */
14196 4974 : if (strcmp(oprkind, "r") == 0 ||
14197 4974 : strcmp(oprkind, "b") == 0)
14198 : {
14199 4688 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14200 4688 : appendPQExpBufferStr(oprid, oprleft);
14201 : }
14202 : else
14203 286 : appendPQExpBufferStr(oprid, "NONE");
14204 :
14205 4974 : if (strcmp(oprkind, "l") == 0 ||
14206 4688 : strcmp(oprkind, "b") == 0)
14207 : {
14208 4974 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14209 4974 : appendPQExpBuffer(oprid, ", %s)", oprright);
14210 : }
14211 : else
14212 0 : appendPQExpBufferStr(oprid, ", NONE)");
14213 :
14214 4974 : oprref = getFormattedOperatorName(oprcom);
14215 4974 : if (oprref)
14216 : {
14217 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14218 3322 : free(oprref);
14219 : }
14220 :
14221 4974 : oprref = getFormattedOperatorName(oprnegate);
14222 4974 : if (oprref)
14223 : {
14224 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14225 2326 : free(oprref);
14226 : }
14227 :
14228 4974 : if (strcmp(oprcanmerge, "t") == 0)
14229 370 : appendPQExpBufferStr(details, ",\n MERGES");
14230 :
14231 4974 : if (strcmp(oprcanhash, "t") == 0)
14232 276 : appendPQExpBufferStr(details, ",\n HASHES");
14233 :
14234 4974 : oprregproc = convertRegProcReference(oprrest);
14235 4974 : if (oprregproc)
14236 : {
14237 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14238 3028 : free(oprregproc);
14239 : }
14240 :
14241 4974 : oprregproc = convertRegProcReference(oprjoin);
14242 4974 : if (oprregproc)
14243 : {
14244 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14245 3028 : free(oprregproc);
14246 : }
14247 :
14248 4974 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14249 4974 : fmtId(oprinfo->dobj.namespace->dobj.name),
14250 : oprid->data);
14251 :
14252 4974 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14253 4974 : fmtId(oprinfo->dobj.namespace->dobj.name),
14254 4974 : oprinfo->dobj.name, details->data);
14255 :
14256 4974 : if (dopt->binary_upgrade)
14257 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14258 24 : "OPERATOR", oprid->data,
14259 24 : oprinfo->dobj.namespace->dobj.name);
14260 :
14261 4974 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14262 4974 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14263 4974 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14264 : .namespace = oprinfo->dobj.namespace->dobj.name,
14265 : .owner = oprinfo->rolname,
14266 : .description = "OPERATOR",
14267 : .section = SECTION_PRE_DATA,
14268 : .createStmt = q->data,
14269 : .dropStmt = delq->data));
14270 :
14271 : /* Dump Operator Comments */
14272 4974 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14273 4794 : dumpComment(fout, "OPERATOR", oprid->data,
14274 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14275 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14276 :
14277 4974 : PQclear(res);
14278 :
14279 4974 : destroyPQExpBuffer(query);
14280 4974 : destroyPQExpBuffer(q);
14281 4974 : destroyPQExpBuffer(delq);
14282 4974 : destroyPQExpBuffer(oprid);
14283 4974 : destroyPQExpBuffer(details);
14284 : }
14285 :
14286 : /*
14287 : * Convert a function reference obtained from pg_operator
14288 : *
14289 : * Returns allocated string of what to print, or NULL if function references
14290 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14291 : *
14292 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14293 : * part.
14294 : */
14295 : static char *
14296 14922 : convertRegProcReference(const char *proc)
14297 : {
14298 : char *name;
14299 : char *paren;
14300 : bool inquote;
14301 :
14302 : /* In all cases "-" means a null reference */
14303 14922 : if (strcmp(proc, "-") == 0)
14304 3892 : return NULL;
14305 :
14306 11030 : name = pg_strdup(proc);
14307 : /* find non-double-quoted left paren */
14308 11030 : inquote = false;
14309 132906 : for (paren = name; *paren; paren++)
14310 : {
14311 132906 : if (*paren == '(' && !inquote)
14312 : {
14313 11030 : *paren = '\0';
14314 11030 : break;
14315 : }
14316 121876 : if (*paren == '"')
14317 100 : inquote = !inquote;
14318 : }
14319 11030 : return name;
14320 : }
14321 :
14322 : /*
14323 : * getFormattedOperatorName - retrieve the operator name for the
14324 : * given operator OID (presented in string form).
14325 : *
14326 : * Returns an allocated string, or NULL if the given OID is invalid.
14327 : * Caller is responsible for free'ing result string.
14328 : *
14329 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14330 : * useful in commands where the operator's argument types can be inferred from
14331 : * context. We always schema-qualify the name, though. The predecessor to
14332 : * this code tried to skip the schema qualification if possible, but that led
14333 : * to wrong results in corner cases, such as if an operator and its negator
14334 : * are in different schemas.
14335 : */
14336 : static char *
14337 10524 : getFormattedOperatorName(const char *oproid)
14338 : {
14339 : OprInfo *oprInfo;
14340 :
14341 : /* In all cases "0" means a null reference */
14342 10524 : if (strcmp(oproid, "0") == 0)
14343 4876 : return NULL;
14344 :
14345 5648 : oprInfo = findOprByOid(atooid(oproid));
14346 5648 : if (oprInfo == NULL)
14347 : {
14348 0 : pg_log_warning("could not find operator with OID %s",
14349 : oproid);
14350 0 : return NULL;
14351 : }
14352 :
14353 5648 : return psprintf("OPERATOR(%s.%s)",
14354 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
14355 : oprInfo->dobj.name);
14356 : }
14357 :
14358 : /*
14359 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14360 : *
14361 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14362 : * argument lists of these functions are predetermined. Note that the
14363 : * caller should ensure we are in the proper schema, because the results
14364 : * are search path dependent!
14365 : */
14366 : static char *
14367 440 : convertTSFunction(Archive *fout, Oid funcOid)
14368 : {
14369 : char *result;
14370 : char query[128];
14371 : PGresult *res;
14372 :
14373 440 : snprintf(query, sizeof(query),
14374 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14375 440 : res = ExecuteSqlQueryForSingleRow(fout, query);
14376 :
14377 440 : result = pg_strdup(PQgetvalue(res, 0, 0));
14378 :
14379 440 : PQclear(res);
14380 :
14381 440 : return result;
14382 : }
14383 :
14384 : /*
14385 : * dumpAccessMethod
14386 : * write out a single access method definition
14387 : */
14388 : static void
14389 172 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14390 : {
14391 172 : DumpOptions *dopt = fout->dopt;
14392 : PQExpBuffer q;
14393 : PQExpBuffer delq;
14394 : char *qamname;
14395 :
14396 : /* Do nothing if not dumping schema */
14397 172 : if (!dopt->dumpSchema)
14398 24 : return;
14399 :
14400 148 : q = createPQExpBuffer();
14401 148 : delq = createPQExpBuffer();
14402 :
14403 148 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14404 :
14405 148 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14406 :
14407 148 : switch (aminfo->amtype)
14408 : {
14409 70 : case AMTYPE_INDEX:
14410 70 : appendPQExpBufferStr(q, "TYPE INDEX ");
14411 70 : break;
14412 78 : case AMTYPE_TABLE:
14413 78 : appendPQExpBufferStr(q, "TYPE TABLE ");
14414 78 : break;
14415 0 : default:
14416 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14417 : aminfo->amtype, qamname);
14418 0 : destroyPQExpBuffer(q);
14419 0 : destroyPQExpBuffer(delq);
14420 0 : free(qamname);
14421 0 : return;
14422 : }
14423 :
14424 148 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14425 :
14426 148 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14427 : qamname);
14428 :
14429 148 : if (dopt->binary_upgrade)
14430 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14431 : "ACCESS METHOD", qamname, NULL);
14432 :
14433 148 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14434 148 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14435 148 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14436 : .description = "ACCESS METHOD",
14437 : .section = SECTION_PRE_DATA,
14438 : .createStmt = q->data,
14439 : .dropStmt = delq->data));
14440 :
14441 : /* Dump Access Method Comments */
14442 148 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14443 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14444 : NULL, "",
14445 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14446 :
14447 148 : destroyPQExpBuffer(q);
14448 148 : destroyPQExpBuffer(delq);
14449 148 : free(qamname);
14450 : }
14451 :
14452 : /*
14453 : * dumpOpclass
14454 : * write out a single operator class definition
14455 : */
14456 : static void
14457 1338 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14458 : {
14459 1338 : DumpOptions *dopt = fout->dopt;
14460 : PQExpBuffer query;
14461 : PQExpBuffer q;
14462 : PQExpBuffer delq;
14463 : PQExpBuffer nameusing;
14464 : PGresult *res;
14465 : int ntups;
14466 : int i_opcintype;
14467 : int i_opckeytype;
14468 : int i_opcdefault;
14469 : int i_opcfamily;
14470 : int i_opcfamilyname;
14471 : int i_opcfamilynsp;
14472 : int i_amname;
14473 : int i_amopstrategy;
14474 : int i_amopopr;
14475 : int i_sortfamily;
14476 : int i_sortfamilynsp;
14477 : int i_amprocnum;
14478 : int i_amproc;
14479 : int i_amproclefttype;
14480 : int i_amprocrighttype;
14481 : char *opcintype;
14482 : char *opckeytype;
14483 : char *opcdefault;
14484 : char *opcfamily;
14485 : char *opcfamilyname;
14486 : char *opcfamilynsp;
14487 : char *amname;
14488 : char *amopstrategy;
14489 : char *amopopr;
14490 : char *sortfamily;
14491 : char *sortfamilynsp;
14492 : char *amprocnum;
14493 : char *amproc;
14494 : char *amproclefttype;
14495 : char *amprocrighttype;
14496 : bool needComma;
14497 : int i;
14498 :
14499 : /* Do nothing if not dumping schema */
14500 1338 : if (!dopt->dumpSchema)
14501 36 : return;
14502 :
14503 1302 : query = createPQExpBuffer();
14504 1302 : q = createPQExpBuffer();
14505 1302 : delq = createPQExpBuffer();
14506 1302 : nameusing = createPQExpBuffer();
14507 :
14508 : /* Get additional fields from the pg_opclass row */
14509 1302 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14510 : "opckeytype::pg_catalog.regtype, "
14511 : "opcdefault, opcfamily, "
14512 : "opfname AS opcfamilyname, "
14513 : "nspname AS opcfamilynsp, "
14514 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14515 : "FROM pg_catalog.pg_opclass c "
14516 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14517 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14518 : "WHERE c.oid = '%u'::pg_catalog.oid",
14519 1302 : opcinfo->dobj.catId.oid);
14520 :
14521 1302 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14522 :
14523 1302 : i_opcintype = PQfnumber(res, "opcintype");
14524 1302 : i_opckeytype = PQfnumber(res, "opckeytype");
14525 1302 : i_opcdefault = PQfnumber(res, "opcdefault");
14526 1302 : i_opcfamily = PQfnumber(res, "opcfamily");
14527 1302 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14528 1302 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14529 1302 : i_amname = PQfnumber(res, "amname");
14530 :
14531 : /* opcintype may still be needed after we PQclear res */
14532 1302 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14533 1302 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14534 1302 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14535 : /* opcfamily will still be needed after we PQclear res */
14536 1302 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14537 1302 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14538 1302 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14539 : /* amname will still be needed after we PQclear res */
14540 1302 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14541 :
14542 1302 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14543 1302 : fmtQualifiedDumpable(opcinfo));
14544 1302 : appendPQExpBuffer(delq, " USING %s;\n",
14545 : fmtId(amname));
14546 :
14547 : /* Build the fixed portion of the CREATE command */
14548 1302 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14549 1302 : fmtQualifiedDumpable(opcinfo));
14550 1302 : if (strcmp(opcdefault, "t") == 0)
14551 714 : appendPQExpBufferStr(q, "DEFAULT ");
14552 1302 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14553 : opcintype,
14554 : fmtId(amname));
14555 1302 : if (strlen(opcfamilyname) > 0)
14556 : {
14557 1302 : appendPQExpBufferStr(q, " FAMILY ");
14558 1302 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14559 1302 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14560 : }
14561 1302 : appendPQExpBufferStr(q, " AS\n ");
14562 :
14563 1302 : needComma = false;
14564 :
14565 1302 : if (strcmp(opckeytype, "-") != 0)
14566 : {
14567 504 : appendPQExpBuffer(q, "STORAGE %s",
14568 : opckeytype);
14569 504 : needComma = true;
14570 : }
14571 :
14572 1302 : PQclear(res);
14573 :
14574 : /*
14575 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14576 : *
14577 : * Print only those opfamily members that are tied to the opclass by
14578 : * pg_depend entries.
14579 : */
14580 1302 : resetPQExpBuffer(query);
14581 1302 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14582 : "amopopr::pg_catalog.regoperator, "
14583 : "opfname AS sortfamily, "
14584 : "nspname AS sortfamilynsp "
14585 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14586 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14587 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14588 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14589 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14590 : "AND refobjid = '%u'::pg_catalog.oid "
14591 : "AND amopfamily = '%s'::pg_catalog.oid "
14592 : "ORDER BY amopstrategy",
14593 1302 : opcinfo->dobj.catId.oid,
14594 : opcfamily);
14595 :
14596 1302 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14597 :
14598 1302 : ntups = PQntuples(res);
14599 :
14600 1302 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14601 1302 : i_amopopr = PQfnumber(res, "amopopr");
14602 1302 : i_sortfamily = PQfnumber(res, "sortfamily");
14603 1302 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14604 :
14605 1742 : for (i = 0; i < ntups; i++)
14606 : {
14607 440 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14608 440 : amopopr = PQgetvalue(res, i, i_amopopr);
14609 440 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14610 440 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14611 :
14612 440 : if (needComma)
14613 280 : appendPQExpBufferStr(q, " ,\n ");
14614 :
14615 440 : appendPQExpBuffer(q, "OPERATOR %s %s",
14616 : amopstrategy, amopopr);
14617 :
14618 440 : if (strlen(sortfamily) > 0)
14619 : {
14620 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14621 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14622 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14623 : }
14624 :
14625 440 : needComma = true;
14626 : }
14627 :
14628 1302 : PQclear(res);
14629 :
14630 : /*
14631 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14632 : *
14633 : * Print only those opfamily members that are tied to the opclass by
14634 : * pg_depend entries.
14635 : *
14636 : * We print the amproclefttype/amprocrighttype even though in most cases
14637 : * the backend could deduce the right values, because of the corner case
14638 : * of a btree sort support function for a cross-type comparison.
14639 : */
14640 1302 : resetPQExpBuffer(query);
14641 :
14642 1302 : appendPQExpBuffer(query, "SELECT amprocnum, "
14643 : "amproc::pg_catalog.regprocedure, "
14644 : "amproclefttype::pg_catalog.regtype, "
14645 : "amprocrighttype::pg_catalog.regtype "
14646 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14647 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14648 : "AND refobjid = '%u'::pg_catalog.oid "
14649 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14650 : "AND objid = ap.oid "
14651 : "ORDER BY amprocnum",
14652 1302 : opcinfo->dobj.catId.oid);
14653 :
14654 1302 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14655 :
14656 1302 : ntups = PQntuples(res);
14657 :
14658 1302 : i_amprocnum = PQfnumber(res, "amprocnum");
14659 1302 : i_amproc = PQfnumber(res, "amproc");
14660 1302 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14661 1302 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14662 :
14663 1372 : for (i = 0; i < ntups; i++)
14664 : {
14665 70 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14666 70 : amproc = PQgetvalue(res, i, i_amproc);
14667 70 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14668 70 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14669 :
14670 70 : if (needComma)
14671 70 : appendPQExpBufferStr(q, " ,\n ");
14672 :
14673 70 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14674 :
14675 70 : if (*amproclefttype && *amprocrighttype)
14676 70 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14677 :
14678 70 : appendPQExpBuffer(q, " %s", amproc);
14679 :
14680 70 : needComma = true;
14681 : }
14682 :
14683 1302 : PQclear(res);
14684 :
14685 : /*
14686 : * If needComma is still false it means we haven't added anything after
14687 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14688 : * clause with the same datatype. This isn't sanctioned by the
14689 : * documentation, but actually DefineOpClass will treat it as a no-op.
14690 : */
14691 1302 : if (!needComma)
14692 638 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14693 :
14694 1302 : appendPQExpBufferStr(q, ";\n");
14695 :
14696 1302 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14697 1302 : appendPQExpBuffer(nameusing, " USING %s",
14698 : fmtId(amname));
14699 :
14700 1302 : if (dopt->binary_upgrade)
14701 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14702 12 : "OPERATOR CLASS", nameusing->data,
14703 12 : opcinfo->dobj.namespace->dobj.name);
14704 :
14705 1302 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14706 1302 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14707 1302 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14708 : .namespace = opcinfo->dobj.namespace->dobj.name,
14709 : .owner = opcinfo->rolname,
14710 : .description = "OPERATOR CLASS",
14711 : .section = SECTION_PRE_DATA,
14712 : .createStmt = q->data,
14713 : .dropStmt = delq->data));
14714 :
14715 : /* Dump Operator Class Comments */
14716 1302 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14717 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14718 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14719 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14720 :
14721 1302 : free(opcintype);
14722 1302 : free(opcfamily);
14723 1302 : free(amname);
14724 1302 : destroyPQExpBuffer(query);
14725 1302 : destroyPQExpBuffer(q);
14726 1302 : destroyPQExpBuffer(delq);
14727 1302 : destroyPQExpBuffer(nameusing);
14728 : }
14729 :
14730 : /*
14731 : * dumpOpfamily
14732 : * write out a single operator family definition
14733 : *
14734 : * Note: this also dumps any "loose" operator members that aren't bound to a
14735 : * specific opclass within the opfamily.
14736 : */
14737 : static void
14738 1110 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14739 : {
14740 1110 : DumpOptions *dopt = fout->dopt;
14741 : PQExpBuffer query;
14742 : PQExpBuffer q;
14743 : PQExpBuffer delq;
14744 : PQExpBuffer nameusing;
14745 : PGresult *res;
14746 : PGresult *res_ops;
14747 : PGresult *res_procs;
14748 : int ntups;
14749 : int i_amname;
14750 : int i_amopstrategy;
14751 : int i_amopopr;
14752 : int i_sortfamily;
14753 : int i_sortfamilynsp;
14754 : int i_amprocnum;
14755 : int i_amproc;
14756 : int i_amproclefttype;
14757 : int i_amprocrighttype;
14758 : char *amname;
14759 : char *amopstrategy;
14760 : char *amopopr;
14761 : char *sortfamily;
14762 : char *sortfamilynsp;
14763 : char *amprocnum;
14764 : char *amproc;
14765 : char *amproclefttype;
14766 : char *amprocrighttype;
14767 : bool needComma;
14768 : int i;
14769 :
14770 : /* Do nothing if not dumping schema */
14771 1110 : if (!dopt->dumpSchema)
14772 24 : return;
14773 :
14774 1086 : query = createPQExpBuffer();
14775 1086 : q = createPQExpBuffer();
14776 1086 : delq = createPQExpBuffer();
14777 1086 : nameusing = createPQExpBuffer();
14778 :
14779 : /*
14780 : * Fetch only those opfamily members that are tied directly to the
14781 : * opfamily by pg_depend entries.
14782 : */
14783 1086 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14784 : "amopopr::pg_catalog.regoperator, "
14785 : "opfname AS sortfamily, "
14786 : "nspname AS sortfamilynsp "
14787 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14788 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14789 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14790 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14791 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14792 : "AND refobjid = '%u'::pg_catalog.oid "
14793 : "AND amopfamily = '%u'::pg_catalog.oid "
14794 : "ORDER BY amopstrategy",
14795 1086 : opfinfo->dobj.catId.oid,
14796 1086 : opfinfo->dobj.catId.oid);
14797 :
14798 1086 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14799 :
14800 1086 : resetPQExpBuffer(query);
14801 :
14802 1086 : appendPQExpBuffer(query, "SELECT amprocnum, "
14803 : "amproc::pg_catalog.regprocedure, "
14804 : "amproclefttype::pg_catalog.regtype, "
14805 : "amprocrighttype::pg_catalog.regtype "
14806 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14807 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14808 : "AND refobjid = '%u'::pg_catalog.oid "
14809 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14810 : "AND objid = ap.oid "
14811 : "ORDER BY amprocnum",
14812 1086 : opfinfo->dobj.catId.oid);
14813 :
14814 1086 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14815 :
14816 : /* Get additional fields from the pg_opfamily row */
14817 1086 : resetPQExpBuffer(query);
14818 :
14819 1086 : appendPQExpBuffer(query, "SELECT "
14820 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14821 : "FROM pg_catalog.pg_opfamily "
14822 : "WHERE oid = '%u'::pg_catalog.oid",
14823 1086 : opfinfo->dobj.catId.oid);
14824 :
14825 1086 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14826 :
14827 1086 : i_amname = PQfnumber(res, "amname");
14828 :
14829 : /* amname will still be needed after we PQclear res */
14830 1086 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14831 :
14832 1086 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14833 1086 : fmtQualifiedDumpable(opfinfo));
14834 1086 : appendPQExpBuffer(delq, " USING %s;\n",
14835 : fmtId(amname));
14836 :
14837 : /* Build the fixed portion of the CREATE command */
14838 1086 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14839 1086 : fmtQualifiedDumpable(opfinfo));
14840 1086 : appendPQExpBuffer(q, " USING %s;\n",
14841 : fmtId(amname));
14842 :
14843 1086 : PQclear(res);
14844 :
14845 : /* Do we need an ALTER to add loose members? */
14846 1086 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14847 : {
14848 100 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14849 100 : fmtQualifiedDumpable(opfinfo));
14850 100 : appendPQExpBuffer(q, " USING %s ADD\n ",
14851 : fmtId(amname));
14852 :
14853 100 : needComma = false;
14854 :
14855 : /*
14856 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14857 : */
14858 100 : ntups = PQntuples(res_ops);
14859 :
14860 100 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14861 100 : i_amopopr = PQfnumber(res_ops, "amopopr");
14862 100 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14863 100 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14864 :
14865 450 : for (i = 0; i < ntups; i++)
14866 : {
14867 350 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14868 350 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14869 350 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14870 350 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14871 :
14872 350 : if (needComma)
14873 280 : appendPQExpBufferStr(q, " ,\n ");
14874 :
14875 350 : appendPQExpBuffer(q, "OPERATOR %s %s",
14876 : amopstrategy, amopopr);
14877 :
14878 350 : if (strlen(sortfamily) > 0)
14879 : {
14880 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14881 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14882 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14883 : }
14884 :
14885 350 : needComma = true;
14886 : }
14887 :
14888 : /*
14889 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14890 : */
14891 100 : ntups = PQntuples(res_procs);
14892 :
14893 100 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14894 100 : i_amproc = PQfnumber(res_procs, "amproc");
14895 100 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14896 100 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14897 :
14898 480 : for (i = 0; i < ntups; i++)
14899 : {
14900 380 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14901 380 : amproc = PQgetvalue(res_procs, i, i_amproc);
14902 380 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14903 380 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14904 :
14905 380 : if (needComma)
14906 350 : appendPQExpBufferStr(q, " ,\n ");
14907 :
14908 380 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14909 : amprocnum, amproclefttype, amprocrighttype,
14910 : amproc);
14911 :
14912 380 : needComma = true;
14913 : }
14914 :
14915 100 : appendPQExpBufferStr(q, ";\n");
14916 : }
14917 :
14918 1086 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14919 1086 : appendPQExpBuffer(nameusing, " USING %s",
14920 : fmtId(amname));
14921 :
14922 1086 : if (dopt->binary_upgrade)
14923 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14924 18 : "OPERATOR FAMILY", nameusing->data,
14925 18 : opfinfo->dobj.namespace->dobj.name);
14926 :
14927 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14928 1086 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14929 1086 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14930 : .namespace = opfinfo->dobj.namespace->dobj.name,
14931 : .owner = opfinfo->rolname,
14932 : .description = "OPERATOR FAMILY",
14933 : .section = SECTION_PRE_DATA,
14934 : .createStmt = q->data,
14935 : .dropStmt = delq->data));
14936 :
14937 : /* Dump Operator Family Comments */
14938 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14939 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14940 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14941 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14942 :
14943 1086 : free(amname);
14944 1086 : PQclear(res_ops);
14945 1086 : PQclear(res_procs);
14946 1086 : destroyPQExpBuffer(query);
14947 1086 : destroyPQExpBuffer(q);
14948 1086 : destroyPQExpBuffer(delq);
14949 1086 : destroyPQExpBuffer(nameusing);
14950 : }
14951 :
14952 : /*
14953 : * dumpCollation
14954 : * write out a single collation definition
14955 : */
14956 : static void
14957 5086 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14958 : {
14959 5086 : DumpOptions *dopt = fout->dopt;
14960 : PQExpBuffer query;
14961 : PQExpBuffer q;
14962 : PQExpBuffer delq;
14963 : char *qcollname;
14964 : PGresult *res;
14965 : int i_collprovider;
14966 : int i_collisdeterministic;
14967 : int i_collcollate;
14968 : int i_collctype;
14969 : int i_colllocale;
14970 : int i_collicurules;
14971 : const char *collprovider;
14972 : const char *collcollate;
14973 : const char *collctype;
14974 : const char *colllocale;
14975 : const char *collicurules;
14976 :
14977 : /* Do nothing if not dumping schema */
14978 5086 : if (!dopt->dumpSchema)
14979 24 : return;
14980 :
14981 5062 : query = createPQExpBuffer();
14982 5062 : q = createPQExpBuffer();
14983 5062 : delq = createPQExpBuffer();
14984 :
14985 5062 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14986 :
14987 : /* Get collation-specific details */
14988 5062 : appendPQExpBufferStr(query, "SELECT ");
14989 :
14990 5062 : if (fout->remoteVersion >= 100000)
14991 5062 : appendPQExpBufferStr(query,
14992 : "collprovider, "
14993 : "collversion, ");
14994 : else
14995 0 : appendPQExpBufferStr(query,
14996 : "'c' AS collprovider, "
14997 : "NULL AS collversion, ");
14998 :
14999 5062 : if (fout->remoteVersion >= 120000)
15000 5062 : appendPQExpBufferStr(query,
15001 : "collisdeterministic, ");
15002 : else
15003 0 : appendPQExpBufferStr(query,
15004 : "true AS collisdeterministic, ");
15005 :
15006 5062 : if (fout->remoteVersion >= 170000)
15007 5062 : appendPQExpBufferStr(query,
15008 : "colllocale, ");
15009 0 : else if (fout->remoteVersion >= 150000)
15010 0 : appendPQExpBufferStr(query,
15011 : "colliculocale AS colllocale, ");
15012 : else
15013 0 : appendPQExpBufferStr(query,
15014 : "NULL AS colllocale, ");
15015 :
15016 5062 : if (fout->remoteVersion >= 160000)
15017 5062 : appendPQExpBufferStr(query,
15018 : "collicurules, ");
15019 : else
15020 0 : appendPQExpBufferStr(query,
15021 : "NULL AS collicurules, ");
15022 :
15023 5062 : appendPQExpBuffer(query,
15024 : "collcollate, "
15025 : "collctype "
15026 : "FROM pg_catalog.pg_collation c "
15027 : "WHERE c.oid = '%u'::pg_catalog.oid",
15028 5062 : collinfo->dobj.catId.oid);
15029 :
15030 5062 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15031 :
15032 5062 : i_collprovider = PQfnumber(res, "collprovider");
15033 5062 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15034 5062 : i_collcollate = PQfnumber(res, "collcollate");
15035 5062 : i_collctype = PQfnumber(res, "collctype");
15036 5062 : i_colllocale = PQfnumber(res, "colllocale");
15037 5062 : i_collicurules = PQfnumber(res, "collicurules");
15038 :
15039 5062 : collprovider = PQgetvalue(res, 0, i_collprovider);
15040 :
15041 5062 : if (!PQgetisnull(res, 0, i_collcollate))
15042 98 : collcollate = PQgetvalue(res, 0, i_collcollate);
15043 : else
15044 4964 : collcollate = NULL;
15045 :
15046 5062 : if (!PQgetisnull(res, 0, i_collctype))
15047 98 : collctype = PQgetvalue(res, 0, i_collctype);
15048 : else
15049 4964 : collctype = NULL;
15050 :
15051 : /*
15052 : * Before version 15, collcollate and collctype were of type NAME and
15053 : * non-nullable. Treat empty strings as NULL for consistency.
15054 : */
15055 5062 : if (fout->remoteVersion < 150000)
15056 : {
15057 0 : if (collcollate[0] == '\0')
15058 0 : collcollate = NULL;
15059 0 : if (collctype[0] == '\0')
15060 0 : collctype = NULL;
15061 : }
15062 :
15063 5062 : if (!PQgetisnull(res, 0, i_colllocale))
15064 4958 : colllocale = PQgetvalue(res, 0, i_colllocale);
15065 : else
15066 104 : colllocale = NULL;
15067 :
15068 5062 : if (!PQgetisnull(res, 0, i_collicurules))
15069 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15070 : else
15071 5062 : collicurules = NULL;
15072 :
15073 5062 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15074 5062 : fmtQualifiedDumpable(collinfo));
15075 :
15076 5062 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15077 5062 : fmtQualifiedDumpable(collinfo));
15078 :
15079 5062 : appendPQExpBufferStr(q, "provider = ");
15080 5062 : if (collprovider[0] == 'b')
15081 38 : appendPQExpBufferStr(q, "builtin");
15082 5024 : else if (collprovider[0] == 'c')
15083 98 : appendPQExpBufferStr(q, "libc");
15084 4926 : else if (collprovider[0] == 'i')
15085 4920 : appendPQExpBufferStr(q, "icu");
15086 6 : else if (collprovider[0] == 'd')
15087 : /* to allow dumping pg_catalog; not accepted on input */
15088 6 : appendPQExpBufferStr(q, "default");
15089 : else
15090 0 : pg_fatal("unrecognized collation provider: %s",
15091 : collprovider);
15092 :
15093 5062 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15094 0 : appendPQExpBufferStr(q, ", deterministic = false");
15095 :
15096 5062 : if (collprovider[0] == 'd')
15097 : {
15098 6 : if (collcollate || collctype || colllocale || collicurules)
15099 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15100 :
15101 : /* no locale -- the default collation cannot be reloaded anyway */
15102 : }
15103 5056 : else if (collprovider[0] == 'b')
15104 : {
15105 38 : if (collcollate || collctype || !colllocale || collicurules)
15106 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15107 :
15108 38 : appendPQExpBufferStr(q, ", locale = ");
15109 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15110 : fout);
15111 : }
15112 5018 : else if (collprovider[0] == 'i')
15113 : {
15114 4920 : if (fout->remoteVersion >= 150000)
15115 : {
15116 4920 : if (collcollate || collctype || !colllocale)
15117 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15118 :
15119 4920 : appendPQExpBufferStr(q, ", locale = ");
15120 4920 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15121 : fout);
15122 : }
15123 : else
15124 : {
15125 0 : if (!collcollate || !collctype || colllocale ||
15126 0 : strcmp(collcollate, collctype) != 0)
15127 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15128 :
15129 0 : appendPQExpBufferStr(q, ", locale = ");
15130 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15131 : }
15132 :
15133 4920 : if (collicurules)
15134 : {
15135 0 : appendPQExpBufferStr(q, ", rules = ");
15136 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15137 : }
15138 : }
15139 98 : else if (collprovider[0] == 'c')
15140 : {
15141 98 : if (colllocale || collicurules || !collcollate || !collctype)
15142 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15143 :
15144 98 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15145 : {
15146 98 : appendPQExpBufferStr(q, ", locale = ");
15147 98 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15148 : }
15149 : else
15150 : {
15151 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15152 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15153 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15154 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15155 : }
15156 : }
15157 : else
15158 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15159 :
15160 : /*
15161 : * For binary upgrade, carry over the collation version. For normal
15162 : * dump/restore, omit the version, so that it is computed upon restore.
15163 : */
15164 5062 : if (dopt->binary_upgrade)
15165 : {
15166 : int i_collversion;
15167 :
15168 10 : i_collversion = PQfnumber(res, "collversion");
15169 10 : if (!PQgetisnull(res, 0, i_collversion))
15170 : {
15171 8 : appendPQExpBufferStr(q, ", version = ");
15172 8 : appendStringLiteralAH(q,
15173 : PQgetvalue(res, 0, i_collversion),
15174 : fout);
15175 : }
15176 : }
15177 :
15178 5062 : appendPQExpBufferStr(q, ");\n");
15179 :
15180 5062 : if (dopt->binary_upgrade)
15181 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15182 : "COLLATION", qcollname,
15183 10 : collinfo->dobj.namespace->dobj.name);
15184 :
15185 5062 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15186 5062 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15187 5062 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15188 : .namespace = collinfo->dobj.namespace->dobj.name,
15189 : .owner = collinfo->rolname,
15190 : .description = "COLLATION",
15191 : .section = SECTION_PRE_DATA,
15192 : .createStmt = q->data,
15193 : .dropStmt = delq->data));
15194 :
15195 : /* Dump Collation Comments */
15196 5062 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15197 4868 : dumpComment(fout, "COLLATION", qcollname,
15198 4868 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15199 4868 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15200 :
15201 5062 : PQclear(res);
15202 :
15203 5062 : destroyPQExpBuffer(query);
15204 5062 : destroyPQExpBuffer(q);
15205 5062 : destroyPQExpBuffer(delq);
15206 5062 : free(qcollname);
15207 : }
15208 :
15209 : /*
15210 : * dumpConversion
15211 : * write out a single conversion definition
15212 : */
15213 : static void
15214 850 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15215 : {
15216 850 : DumpOptions *dopt = fout->dopt;
15217 : PQExpBuffer query;
15218 : PQExpBuffer q;
15219 : PQExpBuffer delq;
15220 : char *qconvname;
15221 : PGresult *res;
15222 : int i_conforencoding;
15223 : int i_contoencoding;
15224 : int i_conproc;
15225 : int i_condefault;
15226 : const char *conforencoding;
15227 : const char *contoencoding;
15228 : const char *conproc;
15229 : bool condefault;
15230 :
15231 : /* Do nothing if not dumping schema */
15232 850 : if (!dopt->dumpSchema)
15233 12 : return;
15234 :
15235 838 : query = createPQExpBuffer();
15236 838 : q = createPQExpBuffer();
15237 838 : delq = createPQExpBuffer();
15238 :
15239 838 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15240 :
15241 : /* Get conversion-specific details */
15242 838 : appendPQExpBuffer(query, "SELECT "
15243 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15244 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15245 : "conproc, condefault "
15246 : "FROM pg_catalog.pg_conversion c "
15247 : "WHERE c.oid = '%u'::pg_catalog.oid",
15248 838 : convinfo->dobj.catId.oid);
15249 :
15250 838 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15251 :
15252 838 : i_conforencoding = PQfnumber(res, "conforencoding");
15253 838 : i_contoencoding = PQfnumber(res, "contoencoding");
15254 838 : i_conproc = PQfnumber(res, "conproc");
15255 838 : i_condefault = PQfnumber(res, "condefault");
15256 :
15257 838 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15258 838 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15259 838 : conproc = PQgetvalue(res, 0, i_conproc);
15260 838 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15261 :
15262 838 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15263 838 : fmtQualifiedDumpable(convinfo));
15264 :
15265 838 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15266 : (condefault) ? "DEFAULT " : "",
15267 838 : fmtQualifiedDumpable(convinfo));
15268 838 : appendStringLiteralAH(q, conforencoding, fout);
15269 838 : appendPQExpBufferStr(q, " TO ");
15270 838 : appendStringLiteralAH(q, contoencoding, fout);
15271 : /* regproc output is already sufficiently quoted */
15272 838 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15273 :
15274 838 : if (dopt->binary_upgrade)
15275 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15276 : "CONVERSION", qconvname,
15277 2 : convinfo->dobj.namespace->dobj.name);
15278 :
15279 838 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15280 838 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15281 838 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15282 : .namespace = convinfo->dobj.namespace->dobj.name,
15283 : .owner = convinfo->rolname,
15284 : .description = "CONVERSION",
15285 : .section = SECTION_PRE_DATA,
15286 : .createStmt = q->data,
15287 : .dropStmt = delq->data));
15288 :
15289 : /* Dump Conversion Comments */
15290 838 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15291 838 : dumpComment(fout, "CONVERSION", qconvname,
15292 838 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15293 838 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15294 :
15295 838 : PQclear(res);
15296 :
15297 838 : destroyPQExpBuffer(query);
15298 838 : destroyPQExpBuffer(q);
15299 838 : destroyPQExpBuffer(delq);
15300 838 : free(qconvname);
15301 : }
15302 :
15303 : /*
15304 : * format_aggregate_signature: generate aggregate name and argument list
15305 : *
15306 : * The argument type names are qualified if needed. The aggregate name
15307 : * is never qualified.
15308 : */
15309 : static char *
15310 576 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15311 : {
15312 : PQExpBufferData buf;
15313 : int j;
15314 :
15315 576 : initPQExpBuffer(&buf);
15316 576 : if (honor_quotes)
15317 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15318 : else
15319 576 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15320 :
15321 576 : if (agginfo->aggfn.nargs == 0)
15322 80 : appendPQExpBufferStr(&buf, "(*)");
15323 : else
15324 : {
15325 496 : appendPQExpBufferChar(&buf, '(');
15326 1082 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15327 586 : appendPQExpBuffer(&buf, "%s%s",
15328 : (j > 0) ? ", " : "",
15329 : getFormattedTypeName(fout,
15330 586 : agginfo->aggfn.argtypes[j],
15331 : zeroIsError));
15332 496 : appendPQExpBufferChar(&buf, ')');
15333 : }
15334 576 : return buf.data;
15335 : }
15336 :
15337 : /*
15338 : * dumpAgg
15339 : * write out a single aggregate definition
15340 : */
15341 : static void
15342 590 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15343 : {
15344 590 : DumpOptions *dopt = fout->dopt;
15345 : PQExpBuffer query;
15346 : PQExpBuffer q;
15347 : PQExpBuffer delq;
15348 : PQExpBuffer details;
15349 : char *aggsig; /* identity signature */
15350 590 : char *aggfullsig = NULL; /* full signature */
15351 : char *aggsig_tag;
15352 : PGresult *res;
15353 : int i_agginitval;
15354 : int i_aggminitval;
15355 : const char *aggtransfn;
15356 : const char *aggfinalfn;
15357 : const char *aggcombinefn;
15358 : const char *aggserialfn;
15359 : const char *aggdeserialfn;
15360 : const char *aggmtransfn;
15361 : const char *aggminvtransfn;
15362 : const char *aggmfinalfn;
15363 : bool aggfinalextra;
15364 : bool aggmfinalextra;
15365 : char aggfinalmodify;
15366 : char aggmfinalmodify;
15367 : const char *aggsortop;
15368 : char *aggsortconvop;
15369 : char aggkind;
15370 : const char *aggtranstype;
15371 : const char *aggtransspace;
15372 : const char *aggmtranstype;
15373 : const char *aggmtransspace;
15374 : const char *agginitval;
15375 : const char *aggminitval;
15376 : const char *proparallel;
15377 : char defaultfinalmodify;
15378 :
15379 : /* Do nothing if not dumping schema */
15380 590 : if (!dopt->dumpSchema)
15381 14 : return;
15382 :
15383 576 : query = createPQExpBuffer();
15384 576 : q = createPQExpBuffer();
15385 576 : delq = createPQExpBuffer();
15386 576 : details = createPQExpBuffer();
15387 :
15388 576 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15389 : {
15390 : /* Set up query for aggregate-specific details */
15391 116 : appendPQExpBufferStr(query,
15392 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15393 :
15394 116 : appendPQExpBufferStr(query,
15395 : "SELECT "
15396 : "aggtransfn,\n"
15397 : "aggfinalfn,\n"
15398 : "aggtranstype::pg_catalog.regtype,\n"
15399 : "agginitval,\n"
15400 : "aggsortop,\n"
15401 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15402 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15403 :
15404 116 : if (fout->remoteVersion >= 90400)
15405 116 : appendPQExpBufferStr(query,
15406 : "aggkind,\n"
15407 : "aggmtransfn,\n"
15408 : "aggminvtransfn,\n"
15409 : "aggmfinalfn,\n"
15410 : "aggmtranstype::pg_catalog.regtype,\n"
15411 : "aggfinalextra,\n"
15412 : "aggmfinalextra,\n"
15413 : "aggtransspace,\n"
15414 : "aggmtransspace,\n"
15415 : "aggminitval,\n");
15416 : else
15417 0 : appendPQExpBufferStr(query,
15418 : "'n' AS aggkind,\n"
15419 : "'-' AS aggmtransfn,\n"
15420 : "'-' AS aggminvtransfn,\n"
15421 : "'-' AS aggmfinalfn,\n"
15422 : "0 AS aggmtranstype,\n"
15423 : "false AS aggfinalextra,\n"
15424 : "false AS aggmfinalextra,\n"
15425 : "0 AS aggtransspace,\n"
15426 : "0 AS aggmtransspace,\n"
15427 : "NULL AS aggminitval,\n");
15428 :
15429 116 : if (fout->remoteVersion >= 90600)
15430 116 : appendPQExpBufferStr(query,
15431 : "aggcombinefn,\n"
15432 : "aggserialfn,\n"
15433 : "aggdeserialfn,\n"
15434 : "proparallel,\n");
15435 : else
15436 0 : appendPQExpBufferStr(query,
15437 : "'-' AS aggcombinefn,\n"
15438 : "'-' AS aggserialfn,\n"
15439 : "'-' AS aggdeserialfn,\n"
15440 : "'u' AS proparallel,\n");
15441 :
15442 116 : if (fout->remoteVersion >= 110000)
15443 116 : appendPQExpBufferStr(query,
15444 : "aggfinalmodify,\n"
15445 : "aggmfinalmodify\n");
15446 : else
15447 0 : appendPQExpBufferStr(query,
15448 : "'0' AS aggfinalmodify,\n"
15449 : "'0' AS aggmfinalmodify\n");
15450 :
15451 116 : appendPQExpBufferStr(query,
15452 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15453 : "WHERE a.aggfnoid = p.oid "
15454 : "AND p.oid = $1");
15455 :
15456 116 : ExecuteSqlStatement(fout, query->data);
15457 :
15458 116 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15459 : }
15460 :
15461 576 : printfPQExpBuffer(query,
15462 : "EXECUTE dumpAgg('%u')",
15463 576 : agginfo->aggfn.dobj.catId.oid);
15464 :
15465 576 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15466 :
15467 576 : i_agginitval = PQfnumber(res, "agginitval");
15468 576 : i_aggminitval = PQfnumber(res, "aggminitval");
15469 :
15470 576 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15471 576 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15472 576 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15473 576 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15474 576 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15475 576 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15476 576 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15477 576 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15478 576 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15479 576 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15480 576 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15481 576 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15482 576 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15483 576 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15484 576 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15485 576 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15486 576 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15487 576 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15488 576 : agginitval = PQgetvalue(res, 0, i_agginitval);
15489 576 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15490 576 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15491 :
15492 : {
15493 : char *funcargs;
15494 : char *funciargs;
15495 :
15496 576 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15497 576 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15498 576 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15499 576 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15500 : }
15501 :
15502 576 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15503 :
15504 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15505 576 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15506 : /* replace omitted flags for old versions */
15507 576 : if (aggfinalmodify == '0')
15508 0 : aggfinalmodify = defaultfinalmodify;
15509 576 : if (aggmfinalmodify == '0')
15510 0 : aggmfinalmodify = defaultfinalmodify;
15511 :
15512 : /* regproc and regtype output is already sufficiently quoted */
15513 576 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15514 : aggtransfn, aggtranstype);
15515 :
15516 576 : if (strcmp(aggtransspace, "0") != 0)
15517 : {
15518 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15519 : aggtransspace);
15520 : }
15521 :
15522 576 : if (!PQgetisnull(res, 0, i_agginitval))
15523 : {
15524 420 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15525 420 : appendStringLiteralAH(details, agginitval, fout);
15526 : }
15527 :
15528 576 : if (strcmp(aggfinalfn, "-") != 0)
15529 : {
15530 270 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15531 : aggfinalfn);
15532 270 : if (aggfinalextra)
15533 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15534 270 : if (aggfinalmodify != defaultfinalmodify)
15535 : {
15536 70 : switch (aggfinalmodify)
15537 : {
15538 0 : case AGGMODIFY_READ_ONLY:
15539 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15540 0 : break;
15541 70 : case AGGMODIFY_SHAREABLE:
15542 70 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15543 70 : break;
15544 0 : case AGGMODIFY_READ_WRITE:
15545 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15546 0 : break;
15547 0 : default:
15548 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15549 : agginfo->aggfn.dobj.name);
15550 : break;
15551 : }
15552 : }
15553 : }
15554 :
15555 576 : if (strcmp(aggcombinefn, "-") != 0)
15556 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15557 :
15558 576 : if (strcmp(aggserialfn, "-") != 0)
15559 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15560 :
15561 576 : if (strcmp(aggdeserialfn, "-") != 0)
15562 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15563 :
15564 576 : if (strcmp(aggmtransfn, "-") != 0)
15565 : {
15566 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15567 : aggmtransfn,
15568 : aggminvtransfn,
15569 : aggmtranstype);
15570 : }
15571 :
15572 576 : if (strcmp(aggmtransspace, "0") != 0)
15573 : {
15574 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15575 : aggmtransspace);
15576 : }
15577 :
15578 576 : if (!PQgetisnull(res, 0, i_aggminitval))
15579 : {
15580 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15581 20 : appendStringLiteralAH(details, aggminitval, fout);
15582 : }
15583 :
15584 576 : if (strcmp(aggmfinalfn, "-") != 0)
15585 : {
15586 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15587 : aggmfinalfn);
15588 0 : if (aggmfinalextra)
15589 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15590 0 : if (aggmfinalmodify != defaultfinalmodify)
15591 : {
15592 0 : switch (aggmfinalmodify)
15593 : {
15594 0 : case AGGMODIFY_READ_ONLY:
15595 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15596 0 : break;
15597 0 : case AGGMODIFY_SHAREABLE:
15598 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15599 0 : break;
15600 0 : case AGGMODIFY_READ_WRITE:
15601 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15602 0 : break;
15603 0 : default:
15604 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15605 : agginfo->aggfn.dobj.name);
15606 : break;
15607 : }
15608 : }
15609 : }
15610 :
15611 576 : aggsortconvop = getFormattedOperatorName(aggsortop);
15612 576 : if (aggsortconvop)
15613 : {
15614 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15615 : aggsortconvop);
15616 0 : free(aggsortconvop);
15617 : }
15618 :
15619 576 : if (aggkind == AGGKIND_HYPOTHETICAL)
15620 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15621 :
15622 576 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15623 : {
15624 10 : if (proparallel[0] == PROPARALLEL_SAFE)
15625 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15626 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15627 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15628 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15629 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15630 : agginfo->aggfn.dobj.name);
15631 : }
15632 :
15633 576 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15634 576 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15635 : aggsig);
15636 :
15637 1152 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15638 576 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15639 : aggfullsig ? aggfullsig : aggsig, details->data);
15640 :
15641 576 : if (dopt->binary_upgrade)
15642 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15643 : "AGGREGATE", aggsig,
15644 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15645 :
15646 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15647 542 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15648 542 : agginfo->aggfn.dobj.dumpId,
15649 542 : ARCHIVE_OPTS(.tag = aggsig_tag,
15650 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15651 : .owner = agginfo->aggfn.rolname,
15652 : .description = "AGGREGATE",
15653 : .section = SECTION_PRE_DATA,
15654 : .createStmt = q->data,
15655 : .dropStmt = delq->data));
15656 :
15657 : /* Dump Aggregate Comments */
15658 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15659 20 : dumpComment(fout, "AGGREGATE", aggsig,
15660 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15661 20 : agginfo->aggfn.rolname,
15662 20 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15663 :
15664 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15665 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15666 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15667 0 : agginfo->aggfn.rolname,
15668 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15669 :
15670 : /*
15671 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15672 : * command look like a function's GRANT; in particular this affects the
15673 : * syntax for zero-argument aggregates and ordered-set aggregates.
15674 : */
15675 576 : free(aggsig);
15676 :
15677 576 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15678 :
15679 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15680 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15681 : "FUNCTION", aggsig, NULL,
15682 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15683 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15684 :
15685 576 : free(aggsig);
15686 576 : free(aggfullsig);
15687 576 : free(aggsig_tag);
15688 :
15689 576 : PQclear(res);
15690 :
15691 576 : destroyPQExpBuffer(query);
15692 576 : destroyPQExpBuffer(q);
15693 576 : destroyPQExpBuffer(delq);
15694 576 : destroyPQExpBuffer(details);
15695 : }
15696 :
15697 : /*
15698 : * dumpTSParser
15699 : * write out a single text search parser
15700 : */
15701 : static void
15702 88 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15703 : {
15704 88 : DumpOptions *dopt = fout->dopt;
15705 : PQExpBuffer q;
15706 : PQExpBuffer delq;
15707 : char *qprsname;
15708 :
15709 : /* Do nothing if not dumping schema */
15710 88 : if (!dopt->dumpSchema)
15711 12 : return;
15712 :
15713 76 : q = createPQExpBuffer();
15714 76 : delq = createPQExpBuffer();
15715 :
15716 76 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15717 :
15718 76 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15719 76 : fmtQualifiedDumpable(prsinfo));
15720 :
15721 76 : appendPQExpBuffer(q, " START = %s,\n",
15722 76 : convertTSFunction(fout, prsinfo->prsstart));
15723 76 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15724 76 : convertTSFunction(fout, prsinfo->prstoken));
15725 76 : appendPQExpBuffer(q, " END = %s,\n",
15726 76 : convertTSFunction(fout, prsinfo->prsend));
15727 76 : if (prsinfo->prsheadline != InvalidOid)
15728 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15729 6 : convertTSFunction(fout, prsinfo->prsheadline));
15730 76 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15731 76 : convertTSFunction(fout, prsinfo->prslextype));
15732 :
15733 76 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15734 76 : fmtQualifiedDumpable(prsinfo));
15735 :
15736 76 : if (dopt->binary_upgrade)
15737 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15738 : "TEXT SEARCH PARSER", qprsname,
15739 2 : prsinfo->dobj.namespace->dobj.name);
15740 :
15741 76 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15742 76 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15743 76 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15744 : .namespace = prsinfo->dobj.namespace->dobj.name,
15745 : .description = "TEXT SEARCH PARSER",
15746 : .section = SECTION_PRE_DATA,
15747 : .createStmt = q->data,
15748 : .dropStmt = delq->data));
15749 :
15750 : /* Dump Parser Comments */
15751 76 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15752 76 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15753 76 : prsinfo->dobj.namespace->dobj.name, "",
15754 76 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15755 :
15756 76 : destroyPQExpBuffer(q);
15757 76 : destroyPQExpBuffer(delq);
15758 76 : free(qprsname);
15759 : }
15760 :
15761 : /*
15762 : * dumpTSDictionary
15763 : * write out a single text search dictionary
15764 : */
15765 : static void
15766 352 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15767 : {
15768 352 : DumpOptions *dopt = fout->dopt;
15769 : PQExpBuffer q;
15770 : PQExpBuffer delq;
15771 : PQExpBuffer query;
15772 : char *qdictname;
15773 : PGresult *res;
15774 : char *nspname;
15775 : char *tmplname;
15776 :
15777 : /* Do nothing if not dumping schema */
15778 352 : if (!dopt->dumpSchema)
15779 12 : return;
15780 :
15781 340 : q = createPQExpBuffer();
15782 340 : delq = createPQExpBuffer();
15783 340 : query = createPQExpBuffer();
15784 :
15785 340 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15786 :
15787 : /* Fetch name and namespace of the dictionary's template */
15788 340 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15789 : "FROM pg_ts_template p, pg_namespace n "
15790 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15791 340 : dictinfo->dicttemplate);
15792 340 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15793 340 : nspname = PQgetvalue(res, 0, 0);
15794 340 : tmplname = PQgetvalue(res, 0, 1);
15795 :
15796 340 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15797 340 : fmtQualifiedDumpable(dictinfo));
15798 :
15799 340 : appendPQExpBufferStr(q, " TEMPLATE = ");
15800 340 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15801 340 : appendPQExpBufferStr(q, fmtId(tmplname));
15802 :
15803 340 : PQclear(res);
15804 :
15805 : /* the dictinitoption can be dumped straight into the command */
15806 340 : if (dictinfo->dictinitoption)
15807 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15808 :
15809 340 : appendPQExpBufferStr(q, " );\n");
15810 :
15811 340 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15812 340 : fmtQualifiedDumpable(dictinfo));
15813 :
15814 340 : if (dopt->binary_upgrade)
15815 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15816 : "TEXT SEARCH DICTIONARY", qdictname,
15817 20 : dictinfo->dobj.namespace->dobj.name);
15818 :
15819 340 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15820 340 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15821 340 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15822 : .namespace = dictinfo->dobj.namespace->dobj.name,
15823 : .owner = dictinfo->rolname,
15824 : .description = "TEXT SEARCH DICTIONARY",
15825 : .section = SECTION_PRE_DATA,
15826 : .createStmt = q->data,
15827 : .dropStmt = delq->data));
15828 :
15829 : /* Dump Dictionary Comments */
15830 340 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15831 250 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15832 250 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15833 250 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15834 :
15835 340 : destroyPQExpBuffer(q);
15836 340 : destroyPQExpBuffer(delq);
15837 340 : destroyPQExpBuffer(query);
15838 340 : free(qdictname);
15839 : }
15840 :
15841 : /*
15842 : * dumpTSTemplate
15843 : * write out a single text search template
15844 : */
15845 : static void
15846 112 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15847 : {
15848 112 : DumpOptions *dopt = fout->dopt;
15849 : PQExpBuffer q;
15850 : PQExpBuffer delq;
15851 : char *qtmplname;
15852 :
15853 : /* Do nothing if not dumping schema */
15854 112 : if (!dopt->dumpSchema)
15855 12 : return;
15856 :
15857 100 : q = createPQExpBuffer();
15858 100 : delq = createPQExpBuffer();
15859 :
15860 100 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15861 :
15862 100 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15863 100 : fmtQualifiedDumpable(tmplinfo));
15864 :
15865 100 : if (tmplinfo->tmplinit != InvalidOid)
15866 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15867 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15868 100 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15869 100 : convertTSFunction(fout, tmplinfo->tmpllexize));
15870 :
15871 100 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15872 100 : fmtQualifiedDumpable(tmplinfo));
15873 :
15874 100 : if (dopt->binary_upgrade)
15875 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15876 : "TEXT SEARCH TEMPLATE", qtmplname,
15877 2 : tmplinfo->dobj.namespace->dobj.name);
15878 :
15879 100 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15880 100 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15881 100 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15882 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15883 : .description = "TEXT SEARCH TEMPLATE",
15884 : .section = SECTION_PRE_DATA,
15885 : .createStmt = q->data,
15886 : .dropStmt = delq->data));
15887 :
15888 : /* Dump Template Comments */
15889 100 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15890 100 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15891 100 : tmplinfo->dobj.namespace->dobj.name, "",
15892 100 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15893 :
15894 100 : destroyPQExpBuffer(q);
15895 100 : destroyPQExpBuffer(delq);
15896 100 : free(qtmplname);
15897 : }
15898 :
15899 : /*
15900 : * dumpTSConfig
15901 : * write out a single text search configuration
15902 : */
15903 : static void
15904 302 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15905 : {
15906 302 : DumpOptions *dopt = fout->dopt;
15907 : PQExpBuffer q;
15908 : PQExpBuffer delq;
15909 : PQExpBuffer query;
15910 : char *qcfgname;
15911 : PGresult *res;
15912 : char *nspname;
15913 : char *prsname;
15914 : int ntups,
15915 : i;
15916 : int i_tokenname;
15917 : int i_dictname;
15918 :
15919 : /* Do nothing if not dumping schema */
15920 302 : if (!dopt->dumpSchema)
15921 12 : return;
15922 :
15923 290 : q = createPQExpBuffer();
15924 290 : delq = createPQExpBuffer();
15925 290 : query = createPQExpBuffer();
15926 :
15927 290 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15928 :
15929 : /* Fetch name and namespace of the config's parser */
15930 290 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15931 : "FROM pg_ts_parser p, pg_namespace n "
15932 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15933 290 : cfginfo->cfgparser);
15934 290 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15935 290 : nspname = PQgetvalue(res, 0, 0);
15936 290 : prsname = PQgetvalue(res, 0, 1);
15937 :
15938 290 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15939 290 : fmtQualifiedDumpable(cfginfo));
15940 :
15941 290 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15942 290 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15943 :
15944 290 : PQclear(res);
15945 :
15946 290 : resetPQExpBuffer(query);
15947 290 : appendPQExpBuffer(query,
15948 : "SELECT\n"
15949 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15950 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15951 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15952 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15953 : "WHERE m.mapcfg = '%u'\n"
15954 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15955 290 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15956 :
15957 290 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15958 290 : ntups = PQntuples(res);
15959 :
15960 290 : i_tokenname = PQfnumber(res, "tokenname");
15961 290 : i_dictname = PQfnumber(res, "dictname");
15962 :
15963 6070 : for (i = 0; i < ntups; i++)
15964 : {
15965 5780 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15966 5780 : char *dictname = PQgetvalue(res, i, i_dictname);
15967 :
15968 5780 : if (i == 0 ||
15969 5490 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15970 : {
15971 : /* starting a new token type, so start a new command */
15972 5510 : if (i > 0)
15973 5220 : appendPQExpBufferStr(q, ";\n");
15974 5510 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15975 5510 : fmtQualifiedDumpable(cfginfo));
15976 : /* tokenname needs quoting, dictname does NOT */
15977 5510 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15978 : fmtId(tokenname), dictname);
15979 : }
15980 : else
15981 270 : appendPQExpBuffer(q, ", %s", dictname);
15982 : }
15983 :
15984 290 : if (ntups > 0)
15985 290 : appendPQExpBufferStr(q, ";\n");
15986 :
15987 290 : PQclear(res);
15988 :
15989 290 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15990 290 : fmtQualifiedDumpable(cfginfo));
15991 :
15992 290 : if (dopt->binary_upgrade)
15993 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15994 : "TEXT SEARCH CONFIGURATION", qcfgname,
15995 10 : cfginfo->dobj.namespace->dobj.name);
15996 :
15997 290 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15998 290 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15999 290 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16000 : .namespace = cfginfo->dobj.namespace->dobj.name,
16001 : .owner = cfginfo->rolname,
16002 : .description = "TEXT SEARCH CONFIGURATION",
16003 : .section = SECTION_PRE_DATA,
16004 : .createStmt = q->data,
16005 : .dropStmt = delq->data));
16006 :
16007 : /* Dump Configuration Comments */
16008 290 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16009 250 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16010 250 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16011 250 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16012 :
16013 290 : destroyPQExpBuffer(q);
16014 290 : destroyPQExpBuffer(delq);
16015 290 : destroyPQExpBuffer(query);
16016 290 : free(qcfgname);
16017 : }
16018 :
16019 : /*
16020 : * dumpForeignDataWrapper
16021 : * write out a single foreign-data wrapper definition
16022 : */
16023 : static void
16024 110 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16025 : {
16026 110 : DumpOptions *dopt = fout->dopt;
16027 : PQExpBuffer q;
16028 : PQExpBuffer delq;
16029 : char *qfdwname;
16030 :
16031 : /* Do nothing if not dumping schema */
16032 110 : if (!dopt->dumpSchema)
16033 14 : return;
16034 :
16035 96 : q = createPQExpBuffer();
16036 96 : delq = createPQExpBuffer();
16037 :
16038 96 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16039 :
16040 96 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16041 : qfdwname);
16042 :
16043 96 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16044 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16045 :
16046 96 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16047 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16048 :
16049 96 : if (strlen(fdwinfo->fdwoptions) > 0)
16050 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16051 :
16052 96 : appendPQExpBufferStr(q, ";\n");
16053 :
16054 96 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16055 : qfdwname);
16056 :
16057 96 : if (dopt->binary_upgrade)
16058 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16059 : "FOREIGN DATA WRAPPER", qfdwname,
16060 : NULL);
16061 :
16062 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16063 96 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16064 96 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16065 : .owner = fdwinfo->rolname,
16066 : .description = "FOREIGN DATA WRAPPER",
16067 : .section = SECTION_PRE_DATA,
16068 : .createStmt = q->data,
16069 : .dropStmt = delq->data));
16070 :
16071 : /* Dump Foreign Data Wrapper Comments */
16072 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16073 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16074 0 : NULL, fdwinfo->rolname,
16075 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16076 :
16077 : /* Handle the ACL */
16078 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16079 68 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16080 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16081 68 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16082 :
16083 96 : free(qfdwname);
16084 :
16085 96 : destroyPQExpBuffer(q);
16086 96 : destroyPQExpBuffer(delq);
16087 : }
16088 :
16089 : /*
16090 : * dumpForeignServer
16091 : * write out a foreign server definition
16092 : */
16093 : static void
16094 118 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16095 : {
16096 118 : DumpOptions *dopt = fout->dopt;
16097 : PQExpBuffer q;
16098 : PQExpBuffer delq;
16099 : PQExpBuffer query;
16100 : PGresult *res;
16101 : char *qsrvname;
16102 : char *fdwname;
16103 :
16104 : /* Do nothing if not dumping schema */
16105 118 : if (!dopt->dumpSchema)
16106 18 : return;
16107 :
16108 100 : q = createPQExpBuffer();
16109 100 : delq = createPQExpBuffer();
16110 100 : query = createPQExpBuffer();
16111 :
16112 100 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16113 :
16114 : /* look up the foreign-data wrapper */
16115 100 : appendPQExpBuffer(query, "SELECT fdwname "
16116 : "FROM pg_foreign_data_wrapper w "
16117 : "WHERE w.oid = '%u'",
16118 100 : srvinfo->srvfdw);
16119 100 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16120 100 : fdwname = PQgetvalue(res, 0, 0);
16121 :
16122 100 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16123 100 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16124 : {
16125 0 : appendPQExpBufferStr(q, " TYPE ");
16126 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16127 : }
16128 100 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16129 : {
16130 0 : appendPQExpBufferStr(q, " VERSION ");
16131 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16132 : }
16133 :
16134 100 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16135 100 : appendPQExpBufferStr(q, fmtId(fdwname));
16136 :
16137 100 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16138 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16139 :
16140 100 : appendPQExpBufferStr(q, ";\n");
16141 :
16142 100 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16143 : qsrvname);
16144 :
16145 100 : if (dopt->binary_upgrade)
16146 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16147 : "SERVER", qsrvname, NULL);
16148 :
16149 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16150 100 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16151 100 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16152 : .owner = srvinfo->rolname,
16153 : .description = "SERVER",
16154 : .section = SECTION_PRE_DATA,
16155 : .createStmt = q->data,
16156 : .dropStmt = delq->data));
16157 :
16158 : /* Dump Foreign Server Comments */
16159 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16160 0 : dumpComment(fout, "SERVER", qsrvname,
16161 0 : NULL, srvinfo->rolname,
16162 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16163 :
16164 : /* Handle the ACL */
16165 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16166 68 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16167 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16168 68 : NULL, srvinfo->rolname, &srvinfo->dacl);
16169 :
16170 : /* Dump user mappings */
16171 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16172 100 : dumpUserMappings(fout,
16173 100 : srvinfo->dobj.name, NULL,
16174 100 : srvinfo->rolname,
16175 100 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16176 :
16177 100 : PQclear(res);
16178 :
16179 100 : free(qsrvname);
16180 :
16181 100 : destroyPQExpBuffer(q);
16182 100 : destroyPQExpBuffer(delq);
16183 100 : destroyPQExpBuffer(query);
16184 : }
16185 :
16186 : /*
16187 : * dumpUserMappings
16188 : *
16189 : * This routine is used to dump any user mappings associated with the
16190 : * server handed to this routine. Should be called after ArchiveEntry()
16191 : * for the server.
16192 : */
16193 : static void
16194 100 : dumpUserMappings(Archive *fout,
16195 : const char *servername, const char *namespace,
16196 : const char *owner,
16197 : CatalogId catalogId, DumpId dumpId)
16198 : {
16199 : PQExpBuffer q;
16200 : PQExpBuffer delq;
16201 : PQExpBuffer query;
16202 : PQExpBuffer tag;
16203 : PGresult *res;
16204 : int ntups;
16205 : int i_usename;
16206 : int i_umoptions;
16207 : int i;
16208 :
16209 100 : q = createPQExpBuffer();
16210 100 : tag = createPQExpBuffer();
16211 100 : delq = createPQExpBuffer();
16212 100 : query = createPQExpBuffer();
16213 :
16214 : /*
16215 : * We read from the publicly accessible view pg_user_mappings, so as not
16216 : * to fail if run by a non-superuser. Note that the view will show
16217 : * umoptions as null if the user hasn't got privileges for the associated
16218 : * server; this means that pg_dump will dump such a mapping, but with no
16219 : * OPTIONS clause. A possible alternative is to skip such mappings
16220 : * altogether, but it's not clear that that's an improvement.
16221 : */
16222 100 : appendPQExpBuffer(query,
16223 : "SELECT usename, "
16224 : "array_to_string(ARRAY("
16225 : "SELECT quote_ident(option_name) || ' ' || "
16226 : "quote_literal(option_value) "
16227 : "FROM pg_options_to_table(umoptions) "
16228 : "ORDER BY option_name"
16229 : "), E',\n ') AS umoptions "
16230 : "FROM pg_user_mappings "
16231 : "WHERE srvid = '%u' "
16232 : "ORDER BY usename",
16233 : catalogId.oid);
16234 :
16235 100 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16236 :
16237 100 : ntups = PQntuples(res);
16238 100 : i_usename = PQfnumber(res, "usename");
16239 100 : i_umoptions = PQfnumber(res, "umoptions");
16240 :
16241 168 : for (i = 0; i < ntups; i++)
16242 : {
16243 : char *usename;
16244 : char *umoptions;
16245 :
16246 68 : usename = PQgetvalue(res, i, i_usename);
16247 68 : umoptions = PQgetvalue(res, i, i_umoptions);
16248 :
16249 68 : resetPQExpBuffer(q);
16250 68 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16251 68 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16252 :
16253 68 : if (umoptions && strlen(umoptions) > 0)
16254 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16255 :
16256 68 : appendPQExpBufferStr(q, ";\n");
16257 :
16258 68 : resetPQExpBuffer(delq);
16259 68 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16260 68 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16261 :
16262 68 : resetPQExpBuffer(tag);
16263 68 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16264 : usename, servername);
16265 :
16266 68 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16267 68 : ARCHIVE_OPTS(.tag = tag->data,
16268 : .namespace = namespace,
16269 : .owner = owner,
16270 : .description = "USER MAPPING",
16271 : .section = SECTION_PRE_DATA,
16272 : .createStmt = q->data,
16273 : .dropStmt = delq->data));
16274 : }
16275 :
16276 100 : PQclear(res);
16277 :
16278 100 : destroyPQExpBuffer(query);
16279 100 : destroyPQExpBuffer(delq);
16280 100 : destroyPQExpBuffer(tag);
16281 100 : destroyPQExpBuffer(q);
16282 100 : }
16283 :
16284 : /*
16285 : * Write out default privileges information
16286 : */
16287 : static void
16288 344 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16289 : {
16290 344 : DumpOptions *dopt = fout->dopt;
16291 : PQExpBuffer q;
16292 : PQExpBuffer tag;
16293 : const char *type;
16294 :
16295 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16296 344 : if (!dopt->dumpSchema || dopt->aclsSkip)
16297 56 : return;
16298 :
16299 288 : q = createPQExpBuffer();
16300 288 : tag = createPQExpBuffer();
16301 :
16302 288 : switch (daclinfo->defaclobjtype)
16303 : {
16304 134 : case DEFACLOBJ_RELATION:
16305 134 : type = "TABLES";
16306 134 : break;
16307 0 : case DEFACLOBJ_SEQUENCE:
16308 0 : type = "SEQUENCES";
16309 0 : break;
16310 134 : case DEFACLOBJ_FUNCTION:
16311 134 : type = "FUNCTIONS";
16312 134 : break;
16313 20 : case DEFACLOBJ_TYPE:
16314 20 : type = "TYPES";
16315 20 : break;
16316 0 : case DEFACLOBJ_NAMESPACE:
16317 0 : type = "SCHEMAS";
16318 0 : break;
16319 0 : case DEFACLOBJ_LARGEOBJECT:
16320 0 : type = "LARGE OBJECTS";
16321 0 : break;
16322 0 : default:
16323 : /* shouldn't get here */
16324 0 : pg_fatal("unrecognized object type in default privileges: %d",
16325 : (int) daclinfo->defaclobjtype);
16326 : type = ""; /* keep compiler quiet */
16327 : }
16328 :
16329 288 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16330 :
16331 : /* build the actual command(s) for this tuple */
16332 288 : if (!buildDefaultACLCommands(type,
16333 288 : daclinfo->dobj.namespace != NULL ?
16334 136 : daclinfo->dobj.namespace->dobj.name : NULL,
16335 288 : daclinfo->dacl.acl,
16336 288 : daclinfo->dacl.acldefault,
16337 288 : daclinfo->defaclrole,
16338 : fout->remoteVersion,
16339 : q))
16340 0 : pg_fatal("could not parse default ACL list (%s)",
16341 : daclinfo->dacl.acl);
16342 :
16343 288 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16344 288 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16345 288 : ARCHIVE_OPTS(.tag = tag->data,
16346 : .namespace = daclinfo->dobj.namespace ?
16347 : daclinfo->dobj.namespace->dobj.name : NULL,
16348 : .owner = daclinfo->defaclrole,
16349 : .description = "DEFAULT ACL",
16350 : .section = SECTION_POST_DATA,
16351 : .createStmt = q->data));
16352 :
16353 288 : destroyPQExpBuffer(tag);
16354 288 : destroyPQExpBuffer(q);
16355 : }
16356 :
16357 : /*----------
16358 : * Write out grant/revoke information
16359 : *
16360 : * 'objDumpId' is the dump ID of the underlying object.
16361 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16362 : * or InvalidDumpId if there is no need for a second dependency.
16363 : * 'type' must be one of
16364 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16365 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16366 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16367 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16368 : * (Currently we assume that subname is only provided for table columns.)
16369 : * 'nspname' is the namespace the object is in (NULL if none).
16370 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16371 : * to use the default for the object type.
16372 : * 'owner' is the owner, NULL if there is no owner (for languages).
16373 : * 'dacl' is the DumpableAcl struct for the object.
16374 : *
16375 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16376 : * no ACL entry was created.
16377 : *----------
16378 : */
16379 : static DumpId
16380 55988 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16381 : const char *type, const char *name, const char *subname,
16382 : const char *nspname, const char *tag, const char *owner,
16383 : const DumpableAcl *dacl)
16384 : {
16385 55988 : DumpId aclDumpId = InvalidDumpId;
16386 55988 : DumpOptions *dopt = fout->dopt;
16387 55988 : const char *acls = dacl->acl;
16388 55988 : const char *acldefault = dacl->acldefault;
16389 55988 : char privtype = dacl->privtype;
16390 55988 : const char *initprivs = dacl->initprivs;
16391 : const char *baseacls;
16392 : PQExpBuffer sql;
16393 :
16394 : /* Do nothing if ACL dump is not enabled */
16395 55988 : if (dopt->aclsSkip)
16396 648 : return InvalidDumpId;
16397 :
16398 : /* --data-only skips ACLs *except* large object ACLs */
16399 55340 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16400 0 : return InvalidDumpId;
16401 :
16402 55340 : sql = createPQExpBuffer();
16403 :
16404 : /*
16405 : * In binary upgrade mode, we don't run an extension's script but instead
16406 : * dump out the objects independently and then recreate them. To preserve
16407 : * any initial privileges which were set on extension objects, we need to
16408 : * compute the set of GRANT and REVOKE commands necessary to get from the
16409 : * default privileges of an object to its initial privileges as recorded
16410 : * in pg_init_privs.
16411 : *
16412 : * At restore time, we apply these commands after having called
16413 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16414 : * copy the results into pg_init_privs. This is how we preserve the
16415 : * contents of that catalog across binary upgrades.
16416 : */
16417 55340 : if (dopt->binary_upgrade && privtype == 'e' &&
16418 26 : initprivs && *initprivs != '\0')
16419 : {
16420 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16421 26 : if (!buildACLCommands(name, subname, nspname, type,
16422 : initprivs, acldefault, owner,
16423 : "", fout->remoteVersion, sql))
16424 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16425 : initprivs, acldefault, name, type);
16426 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16427 : }
16428 :
16429 : /*
16430 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16431 : * actual current ACL, starting from the initprivs if given, else from the
16432 : * object-type-specific default. Also, while buildACLCommands will assume
16433 : * that a NULL/empty acls string means it needn't do anything, what that
16434 : * actually represents is the object-type-specific default; so we need to
16435 : * substitute the acldefault string to get the right results in that case.
16436 : */
16437 55340 : if (initprivs && *initprivs != '\0')
16438 : {
16439 51600 : baseacls = initprivs;
16440 51600 : if (acls == NULL || *acls == '\0')
16441 34 : acls = acldefault;
16442 : }
16443 : else
16444 3740 : baseacls = acldefault;
16445 :
16446 55340 : if (!buildACLCommands(name, subname, nspname, type,
16447 : acls, baseacls, owner,
16448 : "", fout->remoteVersion, sql))
16449 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16450 : acls, baseacls, name, type);
16451 :
16452 55340 : if (sql->len > 0)
16453 : {
16454 3874 : PQExpBuffer tagbuf = createPQExpBuffer();
16455 : DumpId aclDeps[2];
16456 3874 : int nDeps = 0;
16457 :
16458 3874 : if (tag)
16459 0 : appendPQExpBufferStr(tagbuf, tag);
16460 3874 : else if (subname)
16461 2286 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16462 : else
16463 1588 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16464 :
16465 3874 : aclDeps[nDeps++] = objDumpId;
16466 3874 : if (altDumpId != InvalidDumpId)
16467 2112 : aclDeps[nDeps++] = altDumpId;
16468 :
16469 3874 : aclDumpId = createDumpId();
16470 :
16471 3874 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16472 3874 : ARCHIVE_OPTS(.tag = tagbuf->data,
16473 : .namespace = nspname,
16474 : .owner = owner,
16475 : .description = "ACL",
16476 : .section = SECTION_NONE,
16477 : .createStmt = sql->data,
16478 : .deps = aclDeps,
16479 : .nDeps = nDeps));
16480 :
16481 3874 : destroyPQExpBuffer(tagbuf);
16482 : }
16483 :
16484 55340 : destroyPQExpBuffer(sql);
16485 :
16486 55340 : return aclDumpId;
16487 : }
16488 :
16489 : /*
16490 : * dumpSecLabel
16491 : *
16492 : * This routine is used to dump any security labels associated with the
16493 : * object handed to this routine. The routine takes the object type
16494 : * and object name (ready to print, except for schema decoration), plus
16495 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16496 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16497 : * plus the dump ID for the object (for setting a dependency).
16498 : * If a matching pg_seclabel entry is found, it is dumped.
16499 : *
16500 : * Note: although this routine takes a dumpId for dependency purposes,
16501 : * that purpose is just to mark the dependency in the emitted dump file
16502 : * for possible future use by pg_restore. We do NOT use it for determining
16503 : * ordering of the label in the dump file, because this routine is called
16504 : * after dependency sorting occurs. This routine should be called just after
16505 : * calling ArchiveEntry() for the specified object.
16506 : */
16507 : static void
16508 20 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16509 : const char *namespace, const char *owner,
16510 : CatalogId catalogId, int subid, DumpId dumpId)
16511 : {
16512 20 : DumpOptions *dopt = fout->dopt;
16513 : SecLabelItem *labels;
16514 : int nlabels;
16515 : int i;
16516 : PQExpBuffer query;
16517 :
16518 : /* do nothing, if --no-security-labels is supplied */
16519 20 : if (dopt->no_security_labels)
16520 0 : return;
16521 :
16522 : /*
16523 : * Security labels are schema not data ... except large object labels are
16524 : * data
16525 : */
16526 20 : if (strcmp(type, "LARGE OBJECT") != 0)
16527 : {
16528 0 : if (!dopt->dumpSchema)
16529 0 : return;
16530 : }
16531 : else
16532 : {
16533 : /* We do dump large object security labels in binary-upgrade mode */
16534 20 : if (!dopt->dumpData && !dopt->binary_upgrade)
16535 0 : return;
16536 : }
16537 :
16538 : /* Search for security labels associated with catalogId, using table */
16539 20 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16540 :
16541 20 : query = createPQExpBuffer();
16542 :
16543 30 : for (i = 0; i < nlabels; i++)
16544 : {
16545 : /*
16546 : * Ignore label entries for which the subid doesn't match.
16547 : */
16548 10 : if (labels[i].objsubid != subid)
16549 0 : continue;
16550 :
16551 10 : appendPQExpBuffer(query,
16552 : "SECURITY LABEL FOR %s ON %s ",
16553 10 : fmtId(labels[i].provider), type);
16554 10 : if (namespace && *namespace)
16555 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16556 10 : appendPQExpBuffer(query, "%s IS ", name);
16557 10 : appendStringLiteralAH(query, labels[i].label, fout);
16558 10 : appendPQExpBufferStr(query, ";\n");
16559 : }
16560 :
16561 20 : if (query->len > 0)
16562 : {
16563 10 : PQExpBuffer tag = createPQExpBuffer();
16564 :
16565 10 : appendPQExpBuffer(tag, "%s %s", type, name);
16566 10 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16567 10 : ARCHIVE_OPTS(.tag = tag->data,
16568 : .namespace = namespace,
16569 : .owner = owner,
16570 : .description = "SECURITY LABEL",
16571 : .section = SECTION_NONE,
16572 : .createStmt = query->data,
16573 : .deps = &dumpId,
16574 : .nDeps = 1));
16575 10 : destroyPQExpBuffer(tag);
16576 : }
16577 :
16578 20 : destroyPQExpBuffer(query);
16579 : }
16580 :
16581 : /*
16582 : * dumpTableSecLabel
16583 : *
16584 : * As above, but dump security label for both the specified table (or view)
16585 : * and its columns.
16586 : */
16587 : static void
16588 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16589 : {
16590 0 : DumpOptions *dopt = fout->dopt;
16591 : SecLabelItem *labels;
16592 : int nlabels;
16593 : int i;
16594 : PQExpBuffer query;
16595 : PQExpBuffer target;
16596 :
16597 : /* do nothing, if --no-security-labels is supplied */
16598 0 : if (dopt->no_security_labels)
16599 0 : return;
16600 :
16601 : /* SecLabel are SCHEMA not data */
16602 0 : if (!dopt->dumpSchema)
16603 0 : return;
16604 :
16605 : /* Search for comments associated with relation, using table */
16606 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16607 0 : tbinfo->dobj.catId.oid,
16608 : &labels);
16609 :
16610 : /* If security labels exist, build SECURITY LABEL statements */
16611 0 : if (nlabels <= 0)
16612 0 : return;
16613 :
16614 0 : query = createPQExpBuffer();
16615 0 : target = createPQExpBuffer();
16616 :
16617 0 : for (i = 0; i < nlabels; i++)
16618 : {
16619 : const char *colname;
16620 0 : const char *provider = labels[i].provider;
16621 0 : const char *label = labels[i].label;
16622 0 : int objsubid = labels[i].objsubid;
16623 :
16624 0 : resetPQExpBuffer(target);
16625 0 : if (objsubid == 0)
16626 : {
16627 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16628 0 : fmtQualifiedDumpable(tbinfo));
16629 : }
16630 : else
16631 : {
16632 0 : colname = getAttrName(objsubid, tbinfo);
16633 : /* first fmtXXX result must be consumed before calling again */
16634 0 : appendPQExpBuffer(target, "COLUMN %s",
16635 0 : fmtQualifiedDumpable(tbinfo));
16636 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16637 : }
16638 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16639 : fmtId(provider), target->data);
16640 0 : appendStringLiteralAH(query, label, fout);
16641 0 : appendPQExpBufferStr(query, ";\n");
16642 : }
16643 0 : if (query->len > 0)
16644 : {
16645 0 : resetPQExpBuffer(target);
16646 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16647 0 : fmtId(tbinfo->dobj.name));
16648 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16649 0 : ARCHIVE_OPTS(.tag = target->data,
16650 : .namespace = tbinfo->dobj.namespace->dobj.name,
16651 : .owner = tbinfo->rolname,
16652 : .description = "SECURITY LABEL",
16653 : .section = SECTION_NONE,
16654 : .createStmt = query->data,
16655 : .deps = &(tbinfo->dobj.dumpId),
16656 : .nDeps = 1));
16657 : }
16658 0 : destroyPQExpBuffer(query);
16659 0 : destroyPQExpBuffer(target);
16660 : }
16661 :
16662 : /*
16663 : * findSecLabels
16664 : *
16665 : * Find the security label(s), if any, associated with the given object.
16666 : * All the objsubid values associated with the given classoid/objoid are
16667 : * found with one search.
16668 : */
16669 : static int
16670 20 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16671 : {
16672 20 : SecLabelItem *middle = NULL;
16673 : SecLabelItem *low;
16674 : SecLabelItem *high;
16675 : int nmatch;
16676 :
16677 20 : if (nseclabels <= 0) /* no labels, so no match is possible */
16678 : {
16679 0 : *items = NULL;
16680 0 : return 0;
16681 : }
16682 :
16683 : /*
16684 : * Do binary search to find some item matching the object.
16685 : */
16686 20 : low = &seclabels[0];
16687 20 : high = &seclabels[nseclabels - 1];
16688 30 : while (low <= high)
16689 : {
16690 20 : middle = low + (high - low) / 2;
16691 :
16692 20 : if (classoid < middle->classoid)
16693 0 : high = middle - 1;
16694 20 : else if (classoid > middle->classoid)
16695 0 : low = middle + 1;
16696 20 : else if (objoid < middle->objoid)
16697 10 : high = middle - 1;
16698 10 : else if (objoid > middle->objoid)
16699 0 : low = middle + 1;
16700 : else
16701 10 : break; /* found a match */
16702 : }
16703 :
16704 20 : if (low > high) /* no matches */
16705 : {
16706 10 : *items = NULL;
16707 10 : return 0;
16708 : }
16709 :
16710 : /*
16711 : * Now determine how many items match the object. The search loop
16712 : * invariant still holds: only items between low and high inclusive could
16713 : * match.
16714 : */
16715 10 : nmatch = 1;
16716 10 : while (middle > low)
16717 : {
16718 0 : if (classoid != middle[-1].classoid ||
16719 0 : objoid != middle[-1].objoid)
16720 : break;
16721 0 : middle--;
16722 0 : nmatch++;
16723 : }
16724 :
16725 10 : *items = middle;
16726 :
16727 10 : middle += nmatch;
16728 10 : while (middle <= high)
16729 : {
16730 0 : if (classoid != middle->classoid ||
16731 0 : objoid != middle->objoid)
16732 : break;
16733 0 : middle++;
16734 0 : nmatch++;
16735 : }
16736 :
16737 10 : return nmatch;
16738 : }
16739 :
16740 : /*
16741 : * collectSecLabels
16742 : *
16743 : * Construct a table of all security labels available for database objects;
16744 : * also set the has-seclabel component flag for each relevant object.
16745 : *
16746 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16747 : */
16748 : static void
16749 364 : collectSecLabels(Archive *fout)
16750 : {
16751 : PGresult *res;
16752 : PQExpBuffer query;
16753 : int i_label;
16754 : int i_provider;
16755 : int i_classoid;
16756 : int i_objoid;
16757 : int i_objsubid;
16758 : int ntups;
16759 : int i;
16760 : DumpableObject *dobj;
16761 :
16762 364 : query = createPQExpBuffer();
16763 :
16764 364 : appendPQExpBufferStr(query,
16765 : "SELECT label, provider, classoid, objoid, objsubid "
16766 : "FROM pg_catalog.pg_seclabel "
16767 : "ORDER BY classoid, objoid, objsubid");
16768 :
16769 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16770 :
16771 : /* Construct lookup table containing OIDs in numeric form */
16772 364 : i_label = PQfnumber(res, "label");
16773 364 : i_provider = PQfnumber(res, "provider");
16774 364 : i_classoid = PQfnumber(res, "classoid");
16775 364 : i_objoid = PQfnumber(res, "objoid");
16776 364 : i_objsubid = PQfnumber(res, "objsubid");
16777 :
16778 364 : ntups = PQntuples(res);
16779 :
16780 364 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16781 364 : nseclabels = 0;
16782 364 : dobj = NULL;
16783 :
16784 374 : for (i = 0; i < ntups; i++)
16785 : {
16786 : CatalogId objId;
16787 : int subid;
16788 :
16789 10 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16790 10 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16791 10 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16792 :
16793 : /* We needn't remember labels that don't match any dumpable object */
16794 10 : if (dobj == NULL ||
16795 0 : dobj->catId.tableoid != objId.tableoid ||
16796 0 : dobj->catId.oid != objId.oid)
16797 10 : dobj = findObjectByCatalogId(objId);
16798 10 : if (dobj == NULL)
16799 0 : continue;
16800 :
16801 : /*
16802 : * Labels on columns of composite types are linked to the type's
16803 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16804 : * in the type's own DumpableObject.
16805 : */
16806 10 : if (subid != 0 && dobj->objType == DO_TABLE &&
16807 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16808 0 : {
16809 : TypeInfo *cTypeInfo;
16810 :
16811 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16812 0 : if (cTypeInfo)
16813 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16814 : }
16815 : else
16816 10 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16817 :
16818 10 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16819 10 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16820 10 : seclabels[nseclabels].classoid = objId.tableoid;
16821 10 : seclabels[nseclabels].objoid = objId.oid;
16822 10 : seclabels[nseclabels].objsubid = subid;
16823 10 : nseclabels++;
16824 : }
16825 :
16826 364 : PQclear(res);
16827 364 : destroyPQExpBuffer(query);
16828 364 : }
16829 :
16830 : /*
16831 : * dumpTable
16832 : * write out to fout the declarations (not data) of a user-defined table
16833 : */
16834 : static void
16835 61058 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16836 : {
16837 61058 : DumpOptions *dopt = fout->dopt;
16838 61058 : DumpId tableAclDumpId = InvalidDumpId;
16839 : char *namecopy;
16840 :
16841 : /* Do nothing if not dumping schema */
16842 61058 : if (!dopt->dumpSchema)
16843 3036 : return;
16844 :
16845 58022 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16846 : {
16847 13636 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16848 768 : dumpSequence(fout, tbinfo);
16849 : else
16850 12868 : dumpTableSchema(fout, tbinfo);
16851 : }
16852 :
16853 : /* Handle the ACL here */
16854 58022 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16855 58022 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16856 : {
16857 45864 : const char *objtype =
16858 45864 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16859 :
16860 : tableAclDumpId =
16861 45864 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16862 : objtype, namecopy, NULL,
16863 45864 : tbinfo->dobj.namespace->dobj.name,
16864 45864 : NULL, tbinfo->rolname, &tbinfo->dacl);
16865 : }
16866 :
16867 : /*
16868 : * Handle column ACLs, if any. Note: we pull these with a separate query
16869 : * rather than trying to fetch them during getTableAttrs, so that we won't
16870 : * miss ACLs on system columns. Doing it this way also allows us to dump
16871 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16872 : */
16873 58022 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16874 : {
16875 572 : PQExpBuffer query = createPQExpBuffer();
16876 : PGresult *res;
16877 : int i;
16878 :
16879 572 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16880 : {
16881 : /* Set up query for column ACLs */
16882 312 : appendPQExpBufferStr(query,
16883 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16884 :
16885 312 : if (fout->remoteVersion >= 90600)
16886 : {
16887 : /*
16888 : * In principle we should call acldefault('c', relowner) to
16889 : * get the default ACL for a column. However, we don't
16890 : * currently store the numeric OID of the relowner in
16891 : * TableInfo. We could convert the owner name using regrole,
16892 : * but that creates a risk of failure due to concurrent role
16893 : * renames. Given that the default ACL for columns is empty
16894 : * and is likely to stay that way, it's not worth extra cycles
16895 : * and risk to avoid hard-wiring that knowledge here.
16896 : */
16897 312 : appendPQExpBufferStr(query,
16898 : "SELECT at.attname, "
16899 : "at.attacl, "
16900 : "'{}' AS acldefault, "
16901 : "pip.privtype, pip.initprivs "
16902 : "FROM pg_catalog.pg_attribute at "
16903 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16904 : "(at.attrelid = pip.objoid "
16905 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16906 : "AND at.attnum = pip.objsubid) "
16907 : "WHERE at.attrelid = $1 AND "
16908 : "NOT at.attisdropped "
16909 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16910 : "ORDER BY at.attnum");
16911 : }
16912 : else
16913 : {
16914 0 : appendPQExpBufferStr(query,
16915 : "SELECT attname, attacl, '{}' AS acldefault, "
16916 : "NULL AS privtype, NULL AS initprivs "
16917 : "FROM pg_catalog.pg_attribute "
16918 : "WHERE attrelid = $1 AND NOT attisdropped "
16919 : "AND attacl IS NOT NULL "
16920 : "ORDER BY attnum");
16921 : }
16922 :
16923 312 : ExecuteSqlStatement(fout, query->data);
16924 :
16925 312 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16926 : }
16927 :
16928 572 : printfPQExpBuffer(query,
16929 : "EXECUTE getColumnACLs('%u')",
16930 572 : tbinfo->dobj.catId.oid);
16931 :
16932 572 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16933 :
16934 8978 : for (i = 0; i < PQntuples(res); i++)
16935 : {
16936 8406 : char *attname = PQgetvalue(res, i, 0);
16937 8406 : char *attacl = PQgetvalue(res, i, 1);
16938 8406 : char *acldefault = PQgetvalue(res, i, 2);
16939 8406 : char privtype = *(PQgetvalue(res, i, 3));
16940 8406 : char *initprivs = PQgetvalue(res, i, 4);
16941 : DumpableAcl coldacl;
16942 : char *attnamecopy;
16943 :
16944 8406 : coldacl.acl = attacl;
16945 8406 : coldacl.acldefault = acldefault;
16946 8406 : coldacl.privtype = privtype;
16947 8406 : coldacl.initprivs = initprivs;
16948 8406 : attnamecopy = pg_strdup(fmtId(attname));
16949 :
16950 : /*
16951 : * Column's GRANT type is always TABLE. Each column ACL depends
16952 : * on the table-level ACL, since we can restore column ACLs in
16953 : * parallel but the table-level ACL has to be done first.
16954 : */
16955 8406 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16956 : "TABLE", namecopy, attnamecopy,
16957 8406 : tbinfo->dobj.namespace->dobj.name,
16958 8406 : NULL, tbinfo->rolname, &coldacl);
16959 8406 : free(attnamecopy);
16960 : }
16961 572 : PQclear(res);
16962 572 : destroyPQExpBuffer(query);
16963 : }
16964 :
16965 58022 : free(namecopy);
16966 : }
16967 :
16968 : /*
16969 : * Create the AS clause for a view or materialized view. The semicolon is
16970 : * stripped because a materialized view must add a WITH NO DATA clause.
16971 : *
16972 : * This returns a new buffer which must be freed by the caller.
16973 : */
16974 : static PQExpBuffer
16975 1846 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16976 : {
16977 1846 : PQExpBuffer query = createPQExpBuffer();
16978 1846 : PQExpBuffer result = createPQExpBuffer();
16979 : PGresult *res;
16980 : int len;
16981 :
16982 : /* Fetch the view definition */
16983 1846 : appendPQExpBuffer(query,
16984 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16985 1846 : tbinfo->dobj.catId.oid);
16986 :
16987 1846 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16988 :
16989 1846 : if (PQntuples(res) != 1)
16990 : {
16991 0 : if (PQntuples(res) < 1)
16992 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16993 : tbinfo->dobj.name);
16994 : else
16995 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16996 : tbinfo->dobj.name);
16997 : }
16998 :
16999 1846 : len = PQgetlength(res, 0, 0);
17000 :
17001 1846 : if (len == 0)
17002 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17003 : tbinfo->dobj.name);
17004 :
17005 : /* Strip off the trailing semicolon so that other things may follow. */
17006 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17007 1846 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17008 :
17009 1846 : PQclear(res);
17010 1846 : destroyPQExpBuffer(query);
17011 :
17012 1846 : return result;
17013 : }
17014 :
17015 : /*
17016 : * Create a dummy AS clause for a view. This is used when the real view
17017 : * definition has to be postponed because of circular dependencies.
17018 : * We must duplicate the view's external properties -- column names and types
17019 : * (including collation) -- so that it works for subsequent references.
17020 : *
17021 : * This returns a new buffer which must be freed by the caller.
17022 : */
17023 : static PQExpBuffer
17024 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17025 : {
17026 40 : PQExpBuffer result = createPQExpBuffer();
17027 : int j;
17028 :
17029 40 : appendPQExpBufferStr(result, "SELECT");
17030 :
17031 80 : for (j = 0; j < tbinfo->numatts; j++)
17032 : {
17033 40 : if (j > 0)
17034 20 : appendPQExpBufferChar(result, ',');
17035 40 : appendPQExpBufferStr(result, "\n ");
17036 :
17037 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17038 :
17039 : /*
17040 : * Must add collation if not default for the type, because CREATE OR
17041 : * REPLACE VIEW won't change it
17042 : */
17043 40 : if (OidIsValid(tbinfo->attcollation[j]))
17044 : {
17045 : CollInfo *coll;
17046 :
17047 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17048 0 : if (coll)
17049 0 : appendPQExpBuffer(result, " COLLATE %s",
17050 0 : fmtQualifiedDumpable(coll));
17051 : }
17052 :
17053 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17054 : }
17055 :
17056 40 : return result;
17057 : }
17058 :
17059 : /*
17060 : * dumpTableSchema
17061 : * write the declaration (not data) of one user-defined table or view
17062 : */
17063 : static void
17064 12868 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17065 : {
17066 12868 : DumpOptions *dopt = fout->dopt;
17067 12868 : PQExpBuffer q = createPQExpBuffer();
17068 12868 : PQExpBuffer delq = createPQExpBuffer();
17069 12868 : PQExpBuffer extra = createPQExpBuffer();
17070 : char *qrelname;
17071 : char *qualrelname;
17072 : int numParents;
17073 : TableInfo **parents;
17074 : int actual_atts; /* number of attrs in this CREATE statement */
17075 : const char *reltypename;
17076 : char *storage;
17077 : int j,
17078 : k;
17079 :
17080 : /* We had better have loaded per-column details about this table */
17081 : Assert(tbinfo->interesting);
17082 :
17083 12868 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17084 12868 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17085 :
17086 12868 : if (tbinfo->hasoids)
17087 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17088 : qrelname);
17089 :
17090 12868 : if (dopt->binary_upgrade)
17091 1736 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17092 :
17093 : /* Is it a table or a view? */
17094 12868 : if (tbinfo->relkind == RELKIND_VIEW)
17095 : {
17096 : PQExpBuffer result;
17097 :
17098 : /*
17099 : * Note: keep this code in sync with the is_view case in dumpRule()
17100 : */
17101 :
17102 1066 : reltypename = "VIEW";
17103 :
17104 1066 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17105 :
17106 1066 : if (dopt->binary_upgrade)
17107 104 : binary_upgrade_set_pg_class_oids(fout, q,
17108 104 : tbinfo->dobj.catId.oid);
17109 :
17110 1066 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17111 :
17112 1066 : if (tbinfo->dummy_view)
17113 20 : result = createDummyViewAsClause(fout, tbinfo);
17114 : else
17115 : {
17116 1046 : if (nonemptyReloptions(tbinfo->reloptions))
17117 : {
17118 128 : appendPQExpBufferStr(q, " WITH (");
17119 128 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17120 128 : appendPQExpBufferChar(q, ')');
17121 : }
17122 1046 : result = createViewAsClause(fout, tbinfo);
17123 : }
17124 1066 : appendPQExpBuffer(q, " AS\n%s", result->data);
17125 1066 : destroyPQExpBuffer(result);
17126 :
17127 1066 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17128 70 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17129 1066 : appendPQExpBufferStr(q, ";\n");
17130 : }
17131 : else
17132 : {
17133 11802 : char *partkeydef = NULL;
17134 11802 : char *ftoptions = NULL;
17135 11802 : char *srvname = NULL;
17136 11802 : const char *foreign = "";
17137 :
17138 : /*
17139 : * Set reltypename, and collect any relkind-specific data that we
17140 : * didn't fetch during getTables().
17141 : */
17142 11802 : switch (tbinfo->relkind)
17143 : {
17144 1156 : case RELKIND_PARTITIONED_TABLE:
17145 : {
17146 1156 : PQExpBuffer query = createPQExpBuffer();
17147 : PGresult *res;
17148 :
17149 1156 : reltypename = "TABLE";
17150 :
17151 : /* retrieve partition key definition */
17152 1156 : appendPQExpBuffer(query,
17153 : "SELECT pg_get_partkeydef('%u')",
17154 1156 : tbinfo->dobj.catId.oid);
17155 1156 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17156 1156 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17157 1156 : PQclear(res);
17158 1156 : destroyPQExpBuffer(query);
17159 1156 : break;
17160 : }
17161 74 : case RELKIND_FOREIGN_TABLE:
17162 : {
17163 74 : PQExpBuffer query = createPQExpBuffer();
17164 : PGresult *res;
17165 : int i_srvname;
17166 : int i_ftoptions;
17167 :
17168 74 : reltypename = "FOREIGN TABLE";
17169 :
17170 : /* retrieve name of foreign server and generic options */
17171 74 : appendPQExpBuffer(query,
17172 : "SELECT fs.srvname, "
17173 : "pg_catalog.array_to_string(ARRAY("
17174 : "SELECT pg_catalog.quote_ident(option_name) || "
17175 : "' ' || pg_catalog.quote_literal(option_value) "
17176 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17177 : "ORDER BY option_name"
17178 : "), E',\n ') AS ftoptions "
17179 : "FROM pg_catalog.pg_foreign_table ft "
17180 : "JOIN pg_catalog.pg_foreign_server fs "
17181 : "ON (fs.oid = ft.ftserver) "
17182 : "WHERE ft.ftrelid = '%u'",
17183 74 : tbinfo->dobj.catId.oid);
17184 74 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17185 74 : i_srvname = PQfnumber(res, "srvname");
17186 74 : i_ftoptions = PQfnumber(res, "ftoptions");
17187 74 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17188 74 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17189 74 : PQclear(res);
17190 74 : destroyPQExpBuffer(query);
17191 :
17192 74 : foreign = "FOREIGN ";
17193 74 : break;
17194 : }
17195 780 : case RELKIND_MATVIEW:
17196 780 : reltypename = "MATERIALIZED VIEW";
17197 780 : break;
17198 9792 : default:
17199 9792 : reltypename = "TABLE";
17200 9792 : break;
17201 : }
17202 :
17203 11802 : numParents = tbinfo->numParents;
17204 11802 : parents = tbinfo->parents;
17205 :
17206 11802 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17207 :
17208 11802 : if (dopt->binary_upgrade)
17209 1632 : binary_upgrade_set_pg_class_oids(fout, q,
17210 1632 : tbinfo->dobj.catId.oid);
17211 :
17212 : /*
17213 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17214 : * ignore it when dumping if it was set in this case.
17215 : */
17216 11802 : appendPQExpBuffer(q, "CREATE %s%s %s",
17217 11802 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17218 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17219 : "UNLOGGED " : "",
17220 : reltypename,
17221 : qualrelname);
17222 :
17223 : /*
17224 : * Attach to type, if reloftype; except in case of a binary upgrade,
17225 : * we dump the table normally and attach it to the type afterward.
17226 : */
17227 11802 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17228 48 : appendPQExpBuffer(q, " OF %s",
17229 48 : getFormattedTypeName(fout, tbinfo->reloftype,
17230 : zeroIsError));
17231 :
17232 11802 : if (tbinfo->relkind != RELKIND_MATVIEW)
17233 : {
17234 : /* Dump the attributes */
17235 11022 : actual_atts = 0;
17236 51250 : for (j = 0; j < tbinfo->numatts; j++)
17237 : {
17238 : /*
17239 : * Normally, dump if it's locally defined in this table, and
17240 : * not dropped. But for binary upgrade, we'll dump all the
17241 : * columns, and then fix up the dropped and nonlocal cases
17242 : * below.
17243 : */
17244 40228 : if (shouldPrintColumn(dopt, tbinfo, j))
17245 : {
17246 : bool print_default;
17247 : bool print_notnull;
17248 :
17249 : /*
17250 : * Default value --- suppress if to be printed separately
17251 : * or not at all.
17252 : */
17253 78602 : print_default = (tbinfo->attrdefs[j] != NULL &&
17254 40280 : tbinfo->attrdefs[j]->dobj.dump &&
17255 2058 : !tbinfo->attrdefs[j]->separate);
17256 :
17257 : /*
17258 : * Not Null constraint --- print it if it is locally
17259 : * defined, or if binary upgrade. (In the latter case, we
17260 : * reset conislocal below.)
17261 : */
17262 42782 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17263 4560 : (tbinfo->notnull_islocal[j] ||
17264 1268 : dopt->binary_upgrade ||
17265 1100 : tbinfo->ispartition));
17266 :
17267 : /*
17268 : * Skip column if fully defined by reloftype, except in
17269 : * binary upgrade
17270 : */
17271 38222 : if (OidIsValid(tbinfo->reloftype) &&
17272 100 : !print_default && !print_notnull &&
17273 60 : !dopt->binary_upgrade)
17274 48 : continue;
17275 :
17276 : /* Format properly if not first attr */
17277 38174 : if (actual_atts == 0)
17278 10342 : appendPQExpBufferStr(q, " (");
17279 : else
17280 27832 : appendPQExpBufferChar(q, ',');
17281 38174 : appendPQExpBufferStr(q, "\n ");
17282 38174 : actual_atts++;
17283 :
17284 : /* Attribute name */
17285 38174 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17286 :
17287 38174 : if (tbinfo->attisdropped[j])
17288 : {
17289 : /*
17290 : * ALTER TABLE DROP COLUMN clears
17291 : * pg_attribute.atttypid, so we will not have gotten a
17292 : * valid type name; insert INTEGER as a stopgap. We'll
17293 : * clean things up later.
17294 : */
17295 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17296 : /* and skip to the next column */
17297 168 : continue;
17298 : }
17299 :
17300 : /*
17301 : * Attribute type; print it except when creating a typed
17302 : * table ('OF type_name'), but in binary-upgrade mode,
17303 : * print it in that case too.
17304 : */
17305 38006 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17306 : {
17307 37974 : appendPQExpBuffer(q, " %s",
17308 37974 : tbinfo->atttypnames[j]);
17309 : }
17310 :
17311 38006 : if (print_default)
17312 : {
17313 1786 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17314 572 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17315 572 : tbinfo->attrdefs[j]->adef_expr);
17316 1214 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17317 452 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17318 452 : tbinfo->attrdefs[j]->adef_expr);
17319 : else
17320 762 : appendPQExpBuffer(q, " DEFAULT %s",
17321 762 : tbinfo->attrdefs[j]->adef_expr);
17322 : }
17323 :
17324 38006 : if (print_notnull)
17325 : {
17326 4492 : if (tbinfo->notnull_constrs[j][0] == '\0')
17327 3170 : appendPQExpBufferStr(q, " NOT NULL");
17328 : else
17329 1322 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17330 1322 : fmtId(tbinfo->notnull_constrs[j]));
17331 :
17332 4492 : if (tbinfo->notnull_noinh[j])
17333 0 : appendPQExpBufferStr(q, " NO INHERIT");
17334 : }
17335 :
17336 : /* Add collation if not default for the type */
17337 38006 : if (OidIsValid(tbinfo->attcollation[j]))
17338 : {
17339 : CollInfo *coll;
17340 :
17341 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
17342 394 : if (coll)
17343 394 : appendPQExpBuffer(q, " COLLATE %s",
17344 394 : fmtQualifiedDumpable(coll));
17345 : }
17346 : }
17347 :
17348 : /*
17349 : * On the other hand, if we choose not to print a column
17350 : * (likely because it is created by inheritance), but the
17351 : * column has a locally-defined not-null constraint, we need
17352 : * to dump the constraint as a standalone object.
17353 : *
17354 : * This syntax isn't SQL-conforming, but if you wanted
17355 : * standard output you wouldn't be creating non-standard
17356 : * objects to begin with.
17357 : */
17358 40012 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17359 2006 : !tbinfo->attisdropped[j] &&
17360 1274 : tbinfo->notnull_constrs[j] != NULL &&
17361 364 : tbinfo->notnull_islocal[j])
17362 : {
17363 : /* Format properly if not first attr */
17364 116 : if (actual_atts == 0)
17365 108 : appendPQExpBufferStr(q, " (");
17366 : else
17367 8 : appendPQExpBufferChar(q, ',');
17368 116 : appendPQExpBufferStr(q, "\n ");
17369 116 : actual_atts++;
17370 :
17371 116 : if (tbinfo->notnull_constrs[j][0] == '\0')
17372 8 : appendPQExpBuffer(q, "NOT NULL %s",
17373 8 : fmtId(tbinfo->attnames[j]));
17374 : else
17375 216 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17376 108 : tbinfo->notnull_constrs[j],
17377 108 : fmtId(tbinfo->attnames[j]));
17378 : }
17379 : }
17380 :
17381 : /*
17382 : * Add non-inherited CHECK constraints, if any.
17383 : *
17384 : * For partitions, we need to include check constraints even if
17385 : * they're not defined locally, because the ALTER TABLE ATTACH
17386 : * PARTITION that we'll emit later expects the constraint to be
17387 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17388 : */
17389 12248 : for (j = 0; j < tbinfo->ncheck; j++)
17390 : {
17391 1226 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17392 :
17393 1226 : if (constr->separate ||
17394 1086 : (!constr->conislocal && !tbinfo->ispartition))
17395 220 : continue;
17396 :
17397 1006 : if (actual_atts == 0)
17398 32 : appendPQExpBufferStr(q, " (\n ");
17399 : else
17400 974 : appendPQExpBufferStr(q, ",\n ");
17401 :
17402 1006 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17403 1006 : fmtId(constr->dobj.name));
17404 1006 : appendPQExpBufferStr(q, constr->condef);
17405 :
17406 1006 : actual_atts++;
17407 : }
17408 :
17409 11022 : if (actual_atts)
17410 10482 : appendPQExpBufferStr(q, "\n)");
17411 540 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17412 : {
17413 : /*
17414 : * No attributes? we must have a parenthesized attribute list,
17415 : * even though empty, when not using the OF TYPE syntax.
17416 : */
17417 516 : appendPQExpBufferStr(q, " (\n)");
17418 : }
17419 :
17420 : /*
17421 : * Emit the INHERITS clause (not for partitions), except in
17422 : * binary-upgrade mode.
17423 : */
17424 11022 : if (numParents > 0 && !tbinfo->ispartition &&
17425 1004 : !dopt->binary_upgrade)
17426 : {
17427 878 : appendPQExpBufferStr(q, "\nINHERITS (");
17428 1904 : for (k = 0; k < numParents; k++)
17429 : {
17430 1026 : TableInfo *parentRel = parents[k];
17431 :
17432 1026 : if (k > 0)
17433 148 : appendPQExpBufferStr(q, ", ");
17434 1026 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17435 : }
17436 878 : appendPQExpBufferChar(q, ')');
17437 : }
17438 :
17439 11022 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17440 1156 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17441 :
17442 11022 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17443 74 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17444 : }
17445 :
17446 23314 : if (nonemptyReloptions(tbinfo->reloptions) ||
17447 11512 : nonemptyReloptions(tbinfo->toast_reloptions))
17448 : {
17449 290 : bool addcomma = false;
17450 :
17451 290 : appendPQExpBufferStr(q, "\nWITH (");
17452 290 : if (nonemptyReloptions(tbinfo->reloptions))
17453 : {
17454 290 : addcomma = true;
17455 290 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17456 : }
17457 290 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17458 : {
17459 10 : if (addcomma)
17460 10 : appendPQExpBufferStr(q, ", ");
17461 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17462 : fout);
17463 : }
17464 290 : appendPQExpBufferChar(q, ')');
17465 : }
17466 :
17467 : /* Dump generic options if any */
17468 11802 : if (ftoptions && ftoptions[0])
17469 70 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17470 :
17471 : /*
17472 : * For materialized views, create the AS clause just like a view. At
17473 : * this point, we always mark the view as not populated.
17474 : */
17475 11802 : if (tbinfo->relkind == RELKIND_MATVIEW)
17476 : {
17477 : PQExpBuffer result;
17478 :
17479 780 : result = createViewAsClause(fout, tbinfo);
17480 780 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17481 : result->data);
17482 780 : destroyPQExpBuffer(result);
17483 : }
17484 : else
17485 11022 : appendPQExpBufferStr(q, ";\n");
17486 :
17487 : /* Materialized views can depend on extensions */
17488 11802 : if (tbinfo->relkind == RELKIND_MATVIEW)
17489 780 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17490 : "pg_catalog.pg_class",
17491 : "MATERIALIZED VIEW",
17492 : qualrelname);
17493 :
17494 : /*
17495 : * in binary upgrade mode, update the catalog with any missing values
17496 : * that might be present.
17497 : */
17498 11802 : if (dopt->binary_upgrade)
17499 : {
17500 7916 : for (j = 0; j < tbinfo->numatts; j++)
17501 : {
17502 6284 : if (tbinfo->attmissingval[j][0] != '\0')
17503 : {
17504 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17505 4 : appendPQExpBufferStr(q,
17506 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17507 4 : appendStringLiteralAH(q, qualrelname, fout);
17508 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17509 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17510 4 : appendPQExpBufferChar(q, ',');
17511 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17512 4 : appendPQExpBufferStr(q, ");\n\n");
17513 : }
17514 : }
17515 : }
17516 :
17517 : /*
17518 : * To create binary-compatible heap files, we have to ensure the same
17519 : * physical column order, including dropped columns, as in the
17520 : * original. Therefore, we create dropped columns above and drop them
17521 : * here, also updating their attlen/attalign values so that the
17522 : * dropped column can be skipped properly. (We do not bother with
17523 : * restoring the original attbyval setting.) Also, inheritance
17524 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17525 : * using an INHERITS clause --- the latter would possibly mess up the
17526 : * column order. That also means we have to take care about setting
17527 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17528 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17529 : *
17530 : * We process foreign and partitioned tables here, even though they
17531 : * lack heap storage, because they can participate in inheritance
17532 : * relationships and we want this stuff to be consistent across the
17533 : * inheritance tree. We can exclude indexes, toast tables, sequences
17534 : * and matviews, even though they have storage, because we don't
17535 : * support altering or dropping columns in them, nor can they be part
17536 : * of inheritance trees.
17537 : */
17538 11802 : if (dopt->binary_upgrade &&
17539 1632 : (tbinfo->relkind == RELKIND_RELATION ||
17540 220 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17541 218 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17542 : {
17543 : bool firstitem;
17544 : bool firstitem_extra;
17545 :
17546 : /*
17547 : * Drop any dropped columns. Merge the pg_attribute manipulations
17548 : * into a single SQL command, so that we don't cause repeated
17549 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17550 : * relcache bloat while dropping N columns.
17551 : */
17552 1596 : resetPQExpBuffer(extra);
17553 1596 : firstitem = true;
17554 7836 : for (j = 0; j < tbinfo->numatts; j++)
17555 : {
17556 6240 : if (tbinfo->attisdropped[j])
17557 : {
17558 168 : if (firstitem)
17559 : {
17560 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17561 : "UPDATE pg_catalog.pg_attribute\n"
17562 : "SET attlen = v.dlen, "
17563 : "attalign = v.dalign, "
17564 : "attbyval = false\n"
17565 : "FROM (VALUES ");
17566 76 : firstitem = false;
17567 : }
17568 : else
17569 92 : appendPQExpBufferStr(q, ",\n ");
17570 168 : appendPQExpBufferChar(q, '(');
17571 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17572 168 : appendPQExpBuffer(q, ", %d, '%c')",
17573 168 : tbinfo->attlen[j],
17574 168 : tbinfo->attalign[j]);
17575 : /* The ALTER ... DROP COLUMN commands must come after */
17576 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17577 : foreign, qualrelname);
17578 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17579 168 : fmtId(tbinfo->attnames[j]));
17580 : }
17581 : }
17582 1596 : if (!firstitem)
17583 : {
17584 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17585 : "WHERE attrelid = ");
17586 76 : appendStringLiteralAH(q, qualrelname, fout);
17587 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17588 : " AND attname = v.dname;\n");
17589 : /* Now we can issue the actual DROP COLUMN commands */
17590 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17591 : }
17592 :
17593 : /*
17594 : * Fix up inherited columns. As above, do the pg_attribute
17595 : * manipulations in a single SQL command.
17596 : */
17597 1596 : firstitem = true;
17598 7836 : for (j = 0; j < tbinfo->numatts; j++)
17599 : {
17600 6240 : if (!tbinfo->attisdropped[j] &&
17601 6072 : !tbinfo->attislocal[j])
17602 : {
17603 1206 : if (firstitem)
17604 : {
17605 532 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17606 532 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17607 : "SET attislocal = false\n"
17608 : "WHERE attrelid = ");
17609 532 : appendStringLiteralAH(q, qualrelname, fout);
17610 532 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17611 : " AND attname IN (");
17612 532 : firstitem = false;
17613 : }
17614 : else
17615 674 : appendPQExpBufferStr(q, ", ");
17616 1206 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17617 : }
17618 : }
17619 1596 : if (!firstitem)
17620 532 : appendPQExpBufferStr(q, ");\n");
17621 :
17622 : /*
17623 : * Fix up not-null constraints that come from inheritance. As
17624 : * above, do the pg_constraint manipulations in a single SQL
17625 : * command. (Actually, two in special cases, if we're doing an
17626 : * upgrade from < 18).
17627 : */
17628 1596 : firstitem = true;
17629 1596 : firstitem_extra = true;
17630 1596 : resetPQExpBuffer(extra);
17631 7836 : for (j = 0; j < tbinfo->numatts; j++)
17632 : {
17633 : /*
17634 : * If a not-null constraint comes from inheritance, reset
17635 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17636 : * below. Special hack: in versions < 18, columns with no
17637 : * local definition need their constraint to be matched by
17638 : * column number in conkeys instead of by constraint name,
17639 : * because the latter is not available. (We distinguish the
17640 : * case because the constraint name is the empty string.)
17641 : */
17642 6240 : if (tbinfo->notnull_constrs[j] != NULL &&
17643 580 : !tbinfo->notnull_islocal[j])
17644 : {
17645 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17646 : {
17647 142 : if (firstitem)
17648 : {
17649 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17650 : "SET conislocal = false\n"
17651 : "WHERE contype = 'n' AND conrelid = ");
17652 122 : appendStringLiteralAH(q, qualrelname, fout);
17653 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17654 : "conname IN (");
17655 122 : firstitem = false;
17656 : }
17657 : else
17658 20 : appendPQExpBufferStr(q, ", ");
17659 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17660 : }
17661 : else
17662 : {
17663 26 : if (firstitem_extra)
17664 : {
17665 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17666 : "SET conislocal = false\n"
17667 : "WHERE contype = 'n' AND conrelid = ");
17668 26 : appendStringLiteralAH(extra, qualrelname, fout);
17669 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17670 : "conkey IN (");
17671 26 : firstitem_extra = false;
17672 : }
17673 : else
17674 0 : appendPQExpBufferStr(extra, ", ");
17675 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17676 : }
17677 : }
17678 : }
17679 1596 : if (!firstitem)
17680 122 : appendPQExpBufferStr(q, ");\n");
17681 1596 : if (!firstitem_extra)
17682 26 : appendPQExpBufferStr(extra, ");\n");
17683 :
17684 1596 : if (extra->len > 0)
17685 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17686 :
17687 : /*
17688 : * Add inherited CHECK constraints, if any.
17689 : *
17690 : * For partitions, they were already dumped, and conislocal
17691 : * doesn't need fixing.
17692 : *
17693 : * As above, issue only one direct manipulation of pg_constraint.
17694 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17695 : * commands into one as well, refrain for now due to concern about
17696 : * possible backend memory bloat if there are many such
17697 : * constraints.
17698 : */
17699 1596 : resetPQExpBuffer(extra);
17700 1596 : firstitem = true;
17701 1724 : for (k = 0; k < tbinfo->ncheck; k++)
17702 : {
17703 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17704 :
17705 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17706 124 : continue;
17707 :
17708 4 : if (firstitem)
17709 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17710 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17711 : foreign, qualrelname,
17712 4 : fmtId(constr->dobj.name),
17713 : constr->condef);
17714 : /* Update pg_constraint after all the ALTER TABLEs */
17715 4 : if (firstitem)
17716 : {
17717 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17718 : "SET conislocal = false\n"
17719 : "WHERE contype = 'c' AND conrelid = ");
17720 4 : appendStringLiteralAH(extra, qualrelname, fout);
17721 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17722 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17723 4 : firstitem = false;
17724 : }
17725 : else
17726 0 : appendPQExpBufferStr(extra, ", ");
17727 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17728 : }
17729 1596 : if (!firstitem)
17730 : {
17731 4 : appendPQExpBufferStr(extra, ");\n");
17732 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17733 : }
17734 :
17735 1596 : if (numParents > 0 && !tbinfo->ispartition)
17736 : {
17737 126 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17738 274 : for (k = 0; k < numParents; k++)
17739 : {
17740 148 : TableInfo *parentRel = parents[k];
17741 :
17742 148 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17743 : qualrelname,
17744 148 : fmtQualifiedDumpable(parentRel));
17745 : }
17746 : }
17747 :
17748 1596 : if (OidIsValid(tbinfo->reloftype))
17749 : {
17750 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17751 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17752 : qualrelname,
17753 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17754 : zeroIsError));
17755 : }
17756 : }
17757 :
17758 : /*
17759 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17760 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17761 : * TOAST tables semi-independently, here we see them only as children
17762 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17763 : * child toast table is handled below.)
17764 : */
17765 11802 : if (dopt->binary_upgrade &&
17766 1632 : (tbinfo->relkind == RELKIND_RELATION ||
17767 220 : tbinfo->relkind == RELKIND_MATVIEW))
17768 : {
17769 1448 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17770 1448 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17771 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17772 : "WHERE oid = ",
17773 1448 : tbinfo->frozenxid, tbinfo->minmxid);
17774 1448 : appendStringLiteralAH(q, qualrelname, fout);
17775 1448 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17776 :
17777 1448 : if (tbinfo->toast_oid)
17778 : {
17779 : /*
17780 : * The toast table will have the same OID at restore, so we
17781 : * can safely target it by OID.
17782 : */
17783 560 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17784 560 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17785 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17786 : "WHERE oid = '%u';\n",
17787 560 : tbinfo->toast_frozenxid,
17788 560 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17789 : }
17790 : }
17791 :
17792 : /*
17793 : * In binary_upgrade mode, restore matviews' populated status by
17794 : * poking pg_class directly. This is pretty ugly, but we can't use
17795 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17796 : * matview is not populated even though this matview is; in any case,
17797 : * we want to transfer the matview's heap storage, not run REFRESH.
17798 : */
17799 11802 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17800 36 : tbinfo->relispopulated)
17801 : {
17802 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17803 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17804 : "SET relispopulated = 't'\n"
17805 : "WHERE oid = ");
17806 32 : appendStringLiteralAH(q, qualrelname, fout);
17807 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17808 : }
17809 :
17810 : /*
17811 : * Dump additional per-column properties that we can't handle in the
17812 : * main CREATE TABLE command.
17813 : */
17814 52970 : for (j = 0; j < tbinfo->numatts; j++)
17815 : {
17816 : /* None of this applies to dropped columns */
17817 41168 : if (tbinfo->attisdropped[j])
17818 900 : continue;
17819 :
17820 : /*
17821 : * Dump per-column statistics information. We only issue an ALTER
17822 : * TABLE statement if the attstattarget entry for this column is
17823 : * not the default value.
17824 : */
17825 40268 : if (tbinfo->attstattarget[j] >= 0)
17826 70 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17827 : foreign, qualrelname,
17828 70 : fmtId(tbinfo->attnames[j]),
17829 70 : tbinfo->attstattarget[j]);
17830 :
17831 : /*
17832 : * Dump per-column storage information. The statement is only
17833 : * dumped if the storage has been changed from the type's default.
17834 : */
17835 40268 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17836 : {
17837 170 : switch (tbinfo->attstorage[j])
17838 : {
17839 20 : case TYPSTORAGE_PLAIN:
17840 20 : storage = "PLAIN";
17841 20 : break;
17842 80 : case TYPSTORAGE_EXTERNAL:
17843 80 : storage = "EXTERNAL";
17844 80 : break;
17845 0 : case TYPSTORAGE_EXTENDED:
17846 0 : storage = "EXTENDED";
17847 0 : break;
17848 70 : case TYPSTORAGE_MAIN:
17849 70 : storage = "MAIN";
17850 70 : break;
17851 0 : default:
17852 0 : storage = NULL;
17853 : }
17854 :
17855 : /*
17856 : * Only dump the statement if it's a storage type we recognize
17857 : */
17858 170 : if (storage != NULL)
17859 170 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17860 : foreign, qualrelname,
17861 170 : fmtId(tbinfo->attnames[j]),
17862 : storage);
17863 : }
17864 :
17865 : /*
17866 : * Dump per-column compression, if it's been set.
17867 : */
17868 40268 : if (!dopt->no_toast_compression)
17869 : {
17870 : const char *cmname;
17871 :
17872 40072 : switch (tbinfo->attcompression[j])
17873 : {
17874 148 : case 'p':
17875 148 : cmname = "pglz";
17876 148 : break;
17877 186 : case 'l':
17878 186 : cmname = "lz4";
17879 186 : break;
17880 39738 : default:
17881 39738 : cmname = NULL;
17882 39738 : break;
17883 : }
17884 :
17885 40072 : if (cmname != NULL)
17886 334 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17887 : foreign, qualrelname,
17888 334 : fmtId(tbinfo->attnames[j]),
17889 : cmname);
17890 : }
17891 :
17892 : /*
17893 : * Dump per-column attributes.
17894 : */
17895 40268 : if (tbinfo->attoptions[j][0] != '\0')
17896 70 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17897 : foreign, qualrelname,
17898 70 : fmtId(tbinfo->attnames[j]),
17899 70 : tbinfo->attoptions[j]);
17900 :
17901 : /*
17902 : * Dump per-column fdw options.
17903 : */
17904 40268 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17905 74 : tbinfo->attfdwoptions[j][0] != '\0')
17906 70 : appendPQExpBuffer(q,
17907 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17908 : " %s\n"
17909 : ");\n",
17910 : qualrelname,
17911 70 : fmtId(tbinfo->attnames[j]),
17912 70 : tbinfo->attfdwoptions[j]);
17913 : } /* end loop over columns */
17914 :
17915 11802 : free(partkeydef);
17916 11802 : free(ftoptions);
17917 11802 : free(srvname);
17918 : }
17919 :
17920 : /*
17921 : * dump properties we only have ALTER TABLE syntax for
17922 : */
17923 12868 : if ((tbinfo->relkind == RELKIND_RELATION ||
17924 3076 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17925 1920 : tbinfo->relkind == RELKIND_MATVIEW) &&
17926 11728 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17927 : {
17928 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17929 : {
17930 : /* nothing to do, will be set when the index is dumped */
17931 : }
17932 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17933 : {
17934 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17935 : qualrelname);
17936 : }
17937 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17938 : {
17939 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17940 : qualrelname);
17941 : }
17942 : }
17943 :
17944 12868 : if (tbinfo->forcerowsec)
17945 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17946 : qualrelname);
17947 :
17948 12868 : if (dopt->binary_upgrade)
17949 1736 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17950 : reltypename, qrelname,
17951 1736 : tbinfo->dobj.namespace->dobj.name);
17952 :
17953 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17954 : {
17955 12868 : char *tablespace = NULL;
17956 12868 : char *tableam = NULL;
17957 :
17958 : /*
17959 : * _selectTablespace() relies on tablespace-enabled objects in the
17960 : * default tablespace to have a tablespace of "" (empty string) versus
17961 : * non-tablespace-enabled objects to have a tablespace of NULL.
17962 : * getTables() sets tbinfo->reltablespace to "" for the default
17963 : * tablespace (not NULL).
17964 : */
17965 12868 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17966 11728 : tablespace = tbinfo->reltablespace;
17967 :
17968 12868 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17969 2296 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17970 11728 : tableam = tbinfo->amname;
17971 :
17972 12868 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17973 12868 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17974 : .namespace = tbinfo->dobj.namespace->dobj.name,
17975 : .tablespace = tablespace,
17976 : .tableam = tableam,
17977 : .relkind = tbinfo->relkind,
17978 : .owner = tbinfo->rolname,
17979 : .description = reltypename,
17980 : .section = tbinfo->postponed_def ?
17981 : SECTION_POST_DATA : SECTION_PRE_DATA,
17982 : .createStmt = q->data,
17983 : .dropStmt = delq->data));
17984 : }
17985 :
17986 : /* Dump Table Comments */
17987 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17988 160 : dumpTableComment(fout, tbinfo, reltypename);
17989 :
17990 : /* Dump Table Security Labels */
17991 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17992 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17993 :
17994 : /*
17995 : * Dump comments for not-null constraints that aren't to be dumped
17996 : * separately (those are processed by collectComments/dumpComment).
17997 : */
17998 12868 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
17999 12868 : fout->remoteVersion >= 180000)
18000 : {
18001 12868 : PQExpBuffer comment = NULL;
18002 12868 : PQExpBuffer tag = NULL;
18003 :
18004 60738 : for (j = 0; j < tbinfo->numatts; j++)
18005 : {
18006 47870 : if (tbinfo->notnull_constrs[j] != NULL &&
18007 4924 : tbinfo->notnull_comment[j] != NULL)
18008 : {
18009 90 : if (comment == NULL)
18010 : {
18011 90 : comment = createPQExpBuffer();
18012 90 : tag = createPQExpBuffer();
18013 : }
18014 : else
18015 : {
18016 0 : resetPQExpBuffer(comment);
18017 0 : resetPQExpBuffer(tag);
18018 : }
18019 :
18020 90 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18021 90 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18022 90 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18023 90 : appendPQExpBufferStr(comment, ";\n");
18024 :
18025 90 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18026 90 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18027 :
18028 90 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18029 90 : ARCHIVE_OPTS(.tag = tag->data,
18030 : .namespace = tbinfo->dobj.namespace->dobj.name,
18031 : .owner = tbinfo->rolname,
18032 : .description = "COMMENT",
18033 : .section = SECTION_NONE,
18034 : .createStmt = comment->data,
18035 : .deps = &(tbinfo->dobj.dumpId),
18036 : .nDeps = 1));
18037 : }
18038 : }
18039 :
18040 12868 : destroyPQExpBuffer(comment);
18041 12868 : destroyPQExpBuffer(tag);
18042 : }
18043 :
18044 : /* Dump comments on inlined table constraints */
18045 14094 : for (j = 0; j < tbinfo->ncheck; j++)
18046 : {
18047 1226 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18048 :
18049 1226 : if (constr->separate || !constr->conislocal)
18050 518 : continue;
18051 :
18052 708 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18053 80 : dumpTableConstraintComment(fout, constr);
18054 : }
18055 :
18056 12868 : destroyPQExpBuffer(q);
18057 12868 : destroyPQExpBuffer(delq);
18058 12868 : destroyPQExpBuffer(extra);
18059 12868 : free(qrelname);
18060 12868 : free(qualrelname);
18061 12868 : }
18062 :
18063 : /*
18064 : * dumpTableAttach
18065 : * write to fout the commands to attach a child partition
18066 : *
18067 : * Child partitions are always made by creating them separately
18068 : * and then using ATTACH PARTITION, rather than using
18069 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18070 : * any possible discrepancy in column layout, to allow assigning the
18071 : * correct tablespace if different, and so that it's possible to restore
18072 : * a partition without restoring its parent. (You'll get an error from
18073 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18074 : * using "pg_restore -L" if you prefer.) The last point motivates
18075 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18076 : * rather than emitting it within the child partition's ArchiveEntry.
18077 : */
18078 : static void
18079 2816 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18080 : {
18081 2816 : DumpOptions *dopt = fout->dopt;
18082 : PQExpBuffer q;
18083 : PGresult *res;
18084 : char *partbound;
18085 :
18086 : /* Do nothing if not dumping schema */
18087 2816 : if (!dopt->dumpSchema)
18088 108 : return;
18089 :
18090 2708 : q = createPQExpBuffer();
18091 :
18092 2708 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18093 : {
18094 : /* Set up query for partbound details */
18095 92 : appendPQExpBufferStr(q,
18096 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18097 :
18098 92 : appendPQExpBufferStr(q,
18099 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18100 : "FROM pg_class c "
18101 : "WHERE c.oid = $1");
18102 :
18103 92 : ExecuteSqlStatement(fout, q->data);
18104 :
18105 92 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18106 : }
18107 :
18108 2708 : printfPQExpBuffer(q,
18109 : "EXECUTE dumpTableAttach('%u')",
18110 2708 : attachinfo->partitionTbl->dobj.catId.oid);
18111 :
18112 2708 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18113 2708 : partbound = PQgetvalue(res, 0, 0);
18114 :
18115 : /* Perform ALTER TABLE on the parent */
18116 2708 : printfPQExpBuffer(q,
18117 : "ALTER TABLE ONLY %s ",
18118 2708 : fmtQualifiedDumpable(attachinfo->parentTbl));
18119 2708 : appendPQExpBuffer(q,
18120 : "ATTACH PARTITION %s %s;\n",
18121 2708 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18122 : partbound);
18123 :
18124 : /*
18125 : * There is no point in creating a drop query as the drop is done by table
18126 : * drop. (If you think to change this, see also _printTocEntry().)
18127 : * Although this object doesn't really have ownership as such, set the
18128 : * owner field anyway to ensure that the command is run by the correct
18129 : * role at restore time.
18130 : */
18131 2708 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18132 2708 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18133 : .namespace = attachinfo->dobj.namespace->dobj.name,
18134 : .owner = attachinfo->partitionTbl->rolname,
18135 : .description = "TABLE ATTACH",
18136 : .section = SECTION_PRE_DATA,
18137 : .createStmt = q->data));
18138 :
18139 2708 : PQclear(res);
18140 2708 : destroyPQExpBuffer(q);
18141 : }
18142 :
18143 : /*
18144 : * dumpAttrDef --- dump an attribute's default-value declaration
18145 : */
18146 : static void
18147 2136 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18148 : {
18149 2136 : DumpOptions *dopt = fout->dopt;
18150 2136 : TableInfo *tbinfo = adinfo->adtable;
18151 2136 : int adnum = adinfo->adnum;
18152 : PQExpBuffer q;
18153 : PQExpBuffer delq;
18154 : char *qualrelname;
18155 : char *tag;
18156 : char *foreign;
18157 :
18158 : /* Do nothing if not dumping schema */
18159 2136 : if (!dopt->dumpSchema)
18160 0 : return;
18161 :
18162 : /* Skip if not "separate"; it was dumped in the table's definition */
18163 2136 : if (!adinfo->separate)
18164 1786 : return;
18165 :
18166 350 : q = createPQExpBuffer();
18167 350 : delq = createPQExpBuffer();
18168 :
18169 350 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18170 :
18171 350 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18172 :
18173 350 : appendPQExpBuffer(q,
18174 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18175 350 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18176 350 : adinfo->adef_expr);
18177 :
18178 350 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18179 : foreign, qualrelname,
18180 350 : fmtId(tbinfo->attnames[adnum - 1]));
18181 :
18182 350 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18183 :
18184 350 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18185 350 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18186 350 : ARCHIVE_OPTS(.tag = tag,
18187 : .namespace = tbinfo->dobj.namespace->dobj.name,
18188 : .owner = tbinfo->rolname,
18189 : .description = "DEFAULT",
18190 : .section = SECTION_PRE_DATA,
18191 : .createStmt = q->data,
18192 : .dropStmt = delq->data));
18193 :
18194 350 : free(tag);
18195 350 : destroyPQExpBuffer(q);
18196 350 : destroyPQExpBuffer(delq);
18197 350 : free(qualrelname);
18198 : }
18199 :
18200 : /*
18201 : * getAttrName: extract the correct name for an attribute
18202 : *
18203 : * The array tblInfo->attnames[] only provides names of user attributes;
18204 : * if a system attribute number is supplied, we have to fake it.
18205 : * We also do a little bit of bounds checking for safety's sake.
18206 : */
18207 : static const char *
18208 4192 : getAttrName(int attrnum, const TableInfo *tblInfo)
18209 : {
18210 4192 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18211 4192 : return tblInfo->attnames[attrnum - 1];
18212 0 : switch (attrnum)
18213 : {
18214 0 : case SelfItemPointerAttributeNumber:
18215 0 : return "ctid";
18216 0 : case MinTransactionIdAttributeNumber:
18217 0 : return "xmin";
18218 0 : case MinCommandIdAttributeNumber:
18219 0 : return "cmin";
18220 0 : case MaxTransactionIdAttributeNumber:
18221 0 : return "xmax";
18222 0 : case MaxCommandIdAttributeNumber:
18223 0 : return "cmax";
18224 0 : case TableOidAttributeNumber:
18225 0 : return "tableoid";
18226 : }
18227 0 : pg_fatal("invalid column number %d for table \"%s\"",
18228 : attrnum, tblInfo->dobj.name);
18229 : return NULL; /* keep compiler quiet */
18230 : }
18231 :
18232 : /*
18233 : * dumpIndex
18234 : * write out to fout a user-defined index
18235 : */
18236 : static void
18237 5262 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18238 : {
18239 5262 : DumpOptions *dopt = fout->dopt;
18240 5262 : TableInfo *tbinfo = indxinfo->indextable;
18241 5262 : bool is_constraint = (indxinfo->indexconstraint != 0);
18242 : PQExpBuffer q;
18243 : PQExpBuffer delq;
18244 : char *qindxname;
18245 : char *qqindxname;
18246 :
18247 : /* Do nothing if not dumping schema */
18248 5262 : if (!dopt->dumpSchema)
18249 234 : return;
18250 :
18251 5028 : q = createPQExpBuffer();
18252 5028 : delq = createPQExpBuffer();
18253 :
18254 5028 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18255 5028 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18256 :
18257 : /*
18258 : * If there's an associated constraint, don't dump the index per se, but
18259 : * do dump any comment for it. (This is safe because dependency ordering
18260 : * will have ensured the constraint is emitted first.) Note that the
18261 : * emitted comment has to be shown as depending on the constraint, not the
18262 : * index, in such cases.
18263 : */
18264 5028 : if (!is_constraint)
18265 : {
18266 2112 : char *indstatcols = indxinfo->indstatcols;
18267 2112 : char *indstatvals = indxinfo->indstatvals;
18268 2112 : char **indstatcolsarray = NULL;
18269 2112 : char **indstatvalsarray = NULL;
18270 2112 : int nstatcols = 0;
18271 2112 : int nstatvals = 0;
18272 :
18273 2112 : if (dopt->binary_upgrade)
18274 312 : binary_upgrade_set_pg_class_oids(fout, q,
18275 312 : indxinfo->dobj.catId.oid);
18276 :
18277 : /* Plain secondary index */
18278 2112 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18279 :
18280 : /*
18281 : * Append ALTER TABLE commands as needed to set properties that we
18282 : * only have ALTER TABLE syntax for. Keep this in sync with the
18283 : * similar code in dumpConstraint!
18284 : */
18285 :
18286 : /* If the index is clustered, we need to record that. */
18287 2112 : if (indxinfo->indisclustered)
18288 : {
18289 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18290 0 : fmtQualifiedDumpable(tbinfo));
18291 : /* index name is not qualified in this syntax */
18292 0 : appendPQExpBuffer(q, " ON %s;\n",
18293 : qindxname);
18294 : }
18295 :
18296 : /*
18297 : * If the index has any statistics on some of its columns, generate
18298 : * the associated ALTER INDEX queries.
18299 : */
18300 2112 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18301 : {
18302 : int j;
18303 :
18304 70 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18305 0 : pg_fatal("could not parse index statistic columns");
18306 70 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18307 0 : pg_fatal("could not parse index statistic values");
18308 70 : if (nstatcols != nstatvals)
18309 0 : pg_fatal("mismatched number of columns and values for index statistics");
18310 :
18311 210 : for (j = 0; j < nstatcols; j++)
18312 : {
18313 140 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18314 :
18315 : /*
18316 : * Note that this is a column number, so no quotes should be
18317 : * used.
18318 : */
18319 140 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18320 140 : indstatcolsarray[j]);
18321 140 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18322 140 : indstatvalsarray[j]);
18323 : }
18324 : }
18325 :
18326 : /* Indexes can depend on extensions */
18327 2112 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18328 : "pg_catalog.pg_class",
18329 : "INDEX", qqindxname);
18330 :
18331 : /* If the index defines identity, we need to record that. */
18332 2112 : if (indxinfo->indisreplident)
18333 : {
18334 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18335 0 : fmtQualifiedDumpable(tbinfo));
18336 : /* index name is not qualified in this syntax */
18337 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18338 : qindxname);
18339 : }
18340 :
18341 : /*
18342 : * If this index is a member of a partitioned index, the backend will
18343 : * not allow us to drop it separately, so don't try. It will go away
18344 : * automatically when we drop either the index's table or the
18345 : * partitioned index. (If, in a selective restore with --clean, we
18346 : * drop neither of those, then this index will not be dropped either.
18347 : * But that's fine, and even if you think it's not, the backend won't
18348 : * let us do differently.)
18349 : */
18350 2112 : if (indxinfo->parentidx == 0)
18351 1724 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18352 :
18353 2112 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18354 2112 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18355 2112 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18356 : .namespace = tbinfo->dobj.namespace->dobj.name,
18357 : .tablespace = indxinfo->tablespace,
18358 : .owner = tbinfo->rolname,
18359 : .description = "INDEX",
18360 : .section = SECTION_POST_DATA,
18361 : .createStmt = q->data,
18362 : .dropStmt = delq->data));
18363 :
18364 2112 : free(indstatcolsarray);
18365 2112 : free(indstatvalsarray);
18366 : }
18367 :
18368 : /* Dump Index Comments */
18369 5028 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18370 30 : dumpComment(fout, "INDEX", qindxname,
18371 30 : tbinfo->dobj.namespace->dobj.name,
18372 : tbinfo->rolname,
18373 : indxinfo->dobj.catId, 0,
18374 : is_constraint ? indxinfo->indexconstraint :
18375 : indxinfo->dobj.dumpId);
18376 :
18377 5028 : destroyPQExpBuffer(q);
18378 5028 : destroyPQExpBuffer(delq);
18379 5028 : free(qindxname);
18380 5028 : free(qqindxname);
18381 : }
18382 :
18383 : /*
18384 : * dumpIndexAttach
18385 : * write out to fout a partitioned-index attachment clause
18386 : */
18387 : static void
18388 1176 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18389 : {
18390 : /* Do nothing if not dumping schema */
18391 1176 : if (!fout->dopt->dumpSchema)
18392 96 : return;
18393 :
18394 1080 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18395 : {
18396 1080 : PQExpBuffer q = createPQExpBuffer();
18397 :
18398 1080 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18399 1080 : fmtQualifiedDumpable(attachinfo->parentIdx));
18400 1080 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18401 1080 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18402 :
18403 : /*
18404 : * There is no need for a dropStmt since the drop is done implicitly
18405 : * when we drop either the index's table or the partitioned index.
18406 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18407 : * there's no way to do it anyway. (If you think to change this,
18408 : * consider also what to do with --if-exists.)
18409 : *
18410 : * Although this object doesn't really have ownership as such, set the
18411 : * owner field anyway to ensure that the command is run by the correct
18412 : * role at restore time.
18413 : */
18414 1080 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18415 1080 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18416 : .namespace = attachinfo->dobj.namespace->dobj.name,
18417 : .owner = attachinfo->parentIdx->indextable->rolname,
18418 : .description = "INDEX ATTACH",
18419 : .section = SECTION_POST_DATA,
18420 : .createStmt = q->data));
18421 :
18422 1080 : destroyPQExpBuffer(q);
18423 : }
18424 : }
18425 :
18426 : /*
18427 : * dumpStatisticsExt
18428 : * write out to fout an extended statistics object
18429 : */
18430 : static void
18431 284 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18432 : {
18433 284 : DumpOptions *dopt = fout->dopt;
18434 : PQExpBuffer q;
18435 : PQExpBuffer delq;
18436 : PQExpBuffer query;
18437 : char *qstatsextname;
18438 : PGresult *res;
18439 : char *stxdef;
18440 :
18441 : /* Do nothing if not dumping schema */
18442 284 : if (!dopt->dumpSchema)
18443 36 : return;
18444 :
18445 248 : q = createPQExpBuffer();
18446 248 : delq = createPQExpBuffer();
18447 248 : query = createPQExpBuffer();
18448 :
18449 248 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18450 :
18451 248 : appendPQExpBuffer(query, "SELECT "
18452 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18453 248 : statsextinfo->dobj.catId.oid);
18454 :
18455 248 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18456 :
18457 248 : stxdef = PQgetvalue(res, 0, 0);
18458 :
18459 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18460 248 : appendPQExpBuffer(q, "%s;\n", stxdef);
18461 :
18462 : /*
18463 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18464 : * for this statistics object is not the default value.
18465 : */
18466 248 : if (statsextinfo->stattarget >= 0)
18467 : {
18468 70 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18469 70 : fmtQualifiedDumpable(statsextinfo));
18470 70 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18471 70 : statsextinfo->stattarget);
18472 : }
18473 :
18474 248 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18475 248 : fmtQualifiedDumpable(statsextinfo));
18476 :
18477 248 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18478 248 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18479 248 : statsextinfo->dobj.dumpId,
18480 248 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18481 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18482 : .owner = statsextinfo->rolname,
18483 : .description = "STATISTICS",
18484 : .section = SECTION_POST_DATA,
18485 : .createStmt = q->data,
18486 : .dropStmt = delq->data));
18487 :
18488 : /* Dump Statistics Comments */
18489 248 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18490 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18491 0 : statsextinfo->dobj.namespace->dobj.name,
18492 0 : statsextinfo->rolname,
18493 : statsextinfo->dobj.catId, 0,
18494 0 : statsextinfo->dobj.dumpId);
18495 :
18496 248 : PQclear(res);
18497 248 : destroyPQExpBuffer(q);
18498 248 : destroyPQExpBuffer(delq);
18499 248 : destroyPQExpBuffer(query);
18500 248 : free(qstatsextname);
18501 : }
18502 :
18503 : /*
18504 : * dumpConstraint
18505 : * write out to fout a user-defined constraint
18506 : */
18507 : static void
18508 5128 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18509 : {
18510 5128 : DumpOptions *dopt = fout->dopt;
18511 5128 : TableInfo *tbinfo = coninfo->contable;
18512 : PQExpBuffer q;
18513 : PQExpBuffer delq;
18514 5128 : char *tag = NULL;
18515 : char *foreign;
18516 :
18517 : /* Do nothing if not dumping schema */
18518 5128 : if (!dopt->dumpSchema)
18519 196 : return;
18520 :
18521 4932 : q = createPQExpBuffer();
18522 4932 : delq = createPQExpBuffer();
18523 :
18524 9544 : foreign = tbinfo &&
18525 4932 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18526 :
18527 4932 : if (coninfo->contype == 'p' ||
18528 2500 : coninfo->contype == 'u' ||
18529 2036 : coninfo->contype == 'x')
18530 2916 : {
18531 : /* Index-related constraint */
18532 : IndxInfo *indxinfo;
18533 : int k;
18534 :
18535 2916 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18536 :
18537 2916 : if (indxinfo == NULL)
18538 0 : pg_fatal("missing index for constraint \"%s\"",
18539 : coninfo->dobj.name);
18540 :
18541 2916 : if (dopt->binary_upgrade)
18542 292 : binary_upgrade_set_pg_class_oids(fout, q,
18543 : indxinfo->dobj.catId.oid);
18544 :
18545 2916 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18546 2916 : fmtQualifiedDumpable(tbinfo));
18547 2916 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18548 2916 : fmtId(coninfo->dobj.name));
18549 :
18550 2916 : if (coninfo->condef)
18551 : {
18552 : /* pg_get_constraintdef should have provided everything */
18553 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18554 : }
18555 : else
18556 : {
18557 2896 : appendPQExpBufferStr(q,
18558 2896 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18559 :
18560 : /*
18561 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18562 : * indexes. Being able to create this was fixed, but we need to
18563 : * make the index distinct in order to be able to restore the
18564 : * dump.
18565 : */
18566 2896 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18567 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18568 2896 : appendPQExpBufferStr(q, " (");
18569 7008 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18570 : {
18571 4112 : int indkey = (int) indxinfo->indkeys[k];
18572 : const char *attname;
18573 :
18574 4112 : if (indkey == InvalidAttrNumber)
18575 0 : break;
18576 4112 : attname = getAttrName(indkey, tbinfo);
18577 :
18578 4112 : appendPQExpBuffer(q, "%s%s",
18579 : (k == 0) ? "" : ", ",
18580 : fmtId(attname));
18581 : }
18582 2896 : if (coninfo->conperiod)
18583 220 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18584 :
18585 2896 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18586 40 : appendPQExpBufferStr(q, ") INCLUDE (");
18587 :
18588 2976 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18589 : {
18590 80 : int indkey = (int) indxinfo->indkeys[k];
18591 : const char *attname;
18592 :
18593 80 : if (indkey == InvalidAttrNumber)
18594 0 : break;
18595 80 : attname = getAttrName(indkey, tbinfo);
18596 :
18597 160 : appendPQExpBuffer(q, "%s%s",
18598 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18599 : fmtId(attname));
18600 : }
18601 :
18602 2896 : appendPQExpBufferChar(q, ')');
18603 :
18604 2896 : if (nonemptyReloptions(indxinfo->indreloptions))
18605 : {
18606 0 : appendPQExpBufferStr(q, " WITH (");
18607 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18608 0 : appendPQExpBufferChar(q, ')');
18609 : }
18610 :
18611 2896 : if (coninfo->condeferrable)
18612 : {
18613 50 : appendPQExpBufferStr(q, " DEFERRABLE");
18614 50 : if (coninfo->condeferred)
18615 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18616 : }
18617 :
18618 2896 : appendPQExpBufferStr(q, ";\n");
18619 : }
18620 :
18621 : /*
18622 : * Append ALTER TABLE commands as needed to set properties that we
18623 : * only have ALTER TABLE syntax for. Keep this in sync with the
18624 : * similar code in dumpIndex!
18625 : */
18626 :
18627 : /* If the index is clustered, we need to record that. */
18628 2916 : if (indxinfo->indisclustered)
18629 : {
18630 70 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18631 70 : fmtQualifiedDumpable(tbinfo));
18632 : /* index name is not qualified in this syntax */
18633 70 : appendPQExpBuffer(q, " ON %s;\n",
18634 70 : fmtId(indxinfo->dobj.name));
18635 : }
18636 :
18637 : /* If the index defines identity, we need to record that. */
18638 2916 : if (indxinfo->indisreplident)
18639 : {
18640 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18641 0 : fmtQualifiedDumpable(tbinfo));
18642 : /* index name is not qualified in this syntax */
18643 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18644 0 : fmtId(indxinfo->dobj.name));
18645 : }
18646 :
18647 : /* Indexes can depend on extensions */
18648 2916 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18649 : "pg_catalog.pg_class", "INDEX",
18650 2916 : fmtQualifiedDumpable(indxinfo));
18651 :
18652 2916 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18653 2916 : fmtQualifiedDumpable(tbinfo));
18654 2916 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18655 2916 : fmtId(coninfo->dobj.name));
18656 :
18657 2916 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18658 :
18659 2916 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18660 2916 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18661 2916 : ARCHIVE_OPTS(.tag = tag,
18662 : .namespace = tbinfo->dobj.namespace->dobj.name,
18663 : .tablespace = indxinfo->tablespace,
18664 : .owner = tbinfo->rolname,
18665 : .description = "CONSTRAINT",
18666 : .section = SECTION_POST_DATA,
18667 : .createStmt = q->data,
18668 : .dropStmt = delq->data));
18669 : }
18670 2016 : else if (coninfo->contype == 'f')
18671 : {
18672 : char *only;
18673 :
18674 : /*
18675 : * Foreign keys on partitioned tables are always declared as
18676 : * inheriting to partitions; for all other cases, emit them as
18677 : * applying ONLY directly to the named table, because that's how they
18678 : * work for regular inherited tables.
18679 : */
18680 330 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18681 :
18682 : /*
18683 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18684 : * current table data is not processed
18685 : */
18686 330 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18687 330 : only, fmtQualifiedDumpable(tbinfo));
18688 330 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18689 330 : fmtId(coninfo->dobj.name),
18690 330 : coninfo->condef);
18691 :
18692 330 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18693 330 : only, fmtQualifiedDumpable(tbinfo));
18694 330 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18695 330 : fmtId(coninfo->dobj.name));
18696 :
18697 330 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18698 :
18699 330 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18700 330 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18701 330 : ARCHIVE_OPTS(.tag = tag,
18702 : .namespace = tbinfo->dobj.namespace->dobj.name,
18703 : .owner = tbinfo->rolname,
18704 : .description = "FK CONSTRAINT",
18705 : .section = SECTION_POST_DATA,
18706 : .createStmt = q->data,
18707 : .dropStmt = delq->data));
18708 : }
18709 1686 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18710 : {
18711 : /* CHECK or invalid not-null constraint on a table */
18712 :
18713 : /* Ignore if not to be dumped separately, or if it was inherited */
18714 1366 : if (coninfo->separate && coninfo->conislocal)
18715 : {
18716 : const char *keyword;
18717 :
18718 220 : if (coninfo->contype == 'c')
18719 90 : keyword = "CHECK CONSTRAINT";
18720 : else
18721 130 : keyword = "CONSTRAINT";
18722 :
18723 : /* not ONLY since we want it to propagate to children */
18724 220 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18725 220 : fmtQualifiedDumpable(tbinfo));
18726 220 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18727 220 : fmtId(coninfo->dobj.name),
18728 220 : coninfo->condef);
18729 :
18730 220 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18731 220 : fmtQualifiedDumpable(tbinfo));
18732 220 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18733 220 : fmtId(coninfo->dobj.name));
18734 :
18735 220 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18736 :
18737 220 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18738 220 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18739 220 : ARCHIVE_OPTS(.tag = tag,
18740 : .namespace = tbinfo->dobj.namespace->dobj.name,
18741 : .owner = tbinfo->rolname,
18742 : .description = keyword,
18743 : .section = SECTION_POST_DATA,
18744 : .createStmt = q->data,
18745 : .dropStmt = delq->data));
18746 : }
18747 : }
18748 320 : else if (tbinfo == NULL)
18749 : {
18750 : /* CHECK, NOT NULL constraint on a domain */
18751 320 : TypeInfo *tyinfo = coninfo->condomain;
18752 :
18753 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18754 :
18755 : /* Ignore if not to be dumped separately */
18756 320 : if (coninfo->separate)
18757 : {
18758 : const char *keyword;
18759 :
18760 10 : if (coninfo->contype == 'c')
18761 10 : keyword = "CHECK CONSTRAINT";
18762 : else
18763 0 : keyword = "CONSTRAINT";
18764 :
18765 10 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18766 10 : fmtQualifiedDumpable(tyinfo));
18767 10 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18768 10 : fmtId(coninfo->dobj.name),
18769 10 : coninfo->condef);
18770 :
18771 10 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18772 10 : fmtQualifiedDumpable(tyinfo));
18773 10 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18774 10 : fmtId(coninfo->dobj.name));
18775 :
18776 10 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18777 :
18778 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18779 10 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18780 10 : ARCHIVE_OPTS(.tag = tag,
18781 : .namespace = tyinfo->dobj.namespace->dobj.name,
18782 : .owner = tyinfo->rolname,
18783 : .description = keyword,
18784 : .section = SECTION_POST_DATA,
18785 : .createStmt = q->data,
18786 : .dropStmt = delq->data));
18787 :
18788 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18789 : {
18790 10 : PQExpBuffer conprefix = createPQExpBuffer();
18791 10 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18792 :
18793 10 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18794 10 : fmtId(coninfo->dobj.name));
18795 :
18796 10 : dumpComment(fout, conprefix->data, qtypname,
18797 10 : tyinfo->dobj.namespace->dobj.name,
18798 : tyinfo->rolname,
18799 : coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
18800 10 : destroyPQExpBuffer(conprefix);
18801 10 : free(qtypname);
18802 : }
18803 : }
18804 : }
18805 : else
18806 : {
18807 0 : pg_fatal("unrecognized constraint type: %c",
18808 : coninfo->contype);
18809 : }
18810 :
18811 : /* Dump Constraint Comments --- only works for table constraints */
18812 4932 : if (tbinfo && coninfo->separate &&
18813 3526 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18814 100 : dumpTableConstraintComment(fout, coninfo);
18815 :
18816 4932 : free(tag);
18817 4932 : destroyPQExpBuffer(q);
18818 4932 : destroyPQExpBuffer(delq);
18819 : }
18820 :
18821 : /*
18822 : * dumpTableConstraintComment --- dump a constraint's comment if any
18823 : *
18824 : * This is split out because we need the function in two different places
18825 : * depending on whether the constraint is dumped as part of CREATE TABLE
18826 : * or as a separate ALTER command.
18827 : */
18828 : static void
18829 180 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18830 : {
18831 180 : TableInfo *tbinfo = coninfo->contable;
18832 180 : PQExpBuffer conprefix = createPQExpBuffer();
18833 : char *qtabname;
18834 :
18835 180 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18836 :
18837 180 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18838 180 : fmtId(coninfo->dobj.name));
18839 :
18840 180 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18841 180 : dumpComment(fout, conprefix->data, qtabname,
18842 180 : tbinfo->dobj.namespace->dobj.name,
18843 : tbinfo->rolname,
18844 : coninfo->dobj.catId, 0,
18845 180 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18846 :
18847 180 : destroyPQExpBuffer(conprefix);
18848 180 : free(qtabname);
18849 180 : }
18850 :
18851 : static inline SeqType
18852 1294 : parse_sequence_type(const char *name)
18853 : {
18854 2886 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18855 : {
18856 2886 : if (strcmp(SeqTypeNames[i], name) == 0)
18857 1294 : return (SeqType) i;
18858 : }
18859 :
18860 0 : pg_fatal("unrecognized sequence type: %s", name);
18861 : return (SeqType) 0; /* keep compiler quiet */
18862 : }
18863 :
18864 : /*
18865 : * bsearch() comparator for SequenceItem
18866 : */
18867 : static int
18868 5968 : SequenceItemCmp(const void *p1, const void *p2)
18869 : {
18870 5968 : SequenceItem v1 = *((const SequenceItem *) p1);
18871 5968 : SequenceItem v2 = *((const SequenceItem *) p2);
18872 :
18873 5968 : return pg_cmp_u32(v1.oid, v2.oid);
18874 : }
18875 :
18876 : /*
18877 : * collectSequences
18878 : *
18879 : * Construct a table of sequence information. This table is sorted by OID for
18880 : * speed in lookup.
18881 : */
18882 : static void
18883 364 : collectSequences(Archive *fout)
18884 : {
18885 : PGresult *res;
18886 : const char *query;
18887 :
18888 : /*
18889 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18890 : * some extra effort, we might be able to use the sorted table for those
18891 : * versions, but for now it seems unlikely to be worth it.
18892 : *
18893 : * Since version 18, we can gather the sequence data in this query with
18894 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18895 : */
18896 364 : if (fout->remoteVersion < 100000)
18897 0 : return;
18898 364 : else if (fout->remoteVersion < 180000 ||
18899 364 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18900 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18901 : "seqstart, seqincrement, "
18902 : "seqmax, seqmin, "
18903 : "seqcache, seqcycle, "
18904 : "NULL, 'f' "
18905 : "FROM pg_catalog.pg_sequence "
18906 : "ORDER BY seqrelid";
18907 : else
18908 348 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18909 : "seqstart, seqincrement, "
18910 : "seqmax, seqmin, "
18911 : "seqcache, seqcycle, "
18912 : "last_value, is_called "
18913 : "FROM pg_catalog.pg_sequence, "
18914 : "pg_get_sequence_data(seqrelid) "
18915 : "ORDER BY seqrelid;";
18916 :
18917 364 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18918 :
18919 364 : nsequences = PQntuples(res);
18920 364 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18921 :
18922 1658 : for (int i = 0; i < nsequences; i++)
18923 : {
18924 1294 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18925 1294 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18926 1294 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18927 1294 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18928 1294 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18929 1294 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18930 1294 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18931 1294 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18932 1294 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18933 1294 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18934 : }
18935 :
18936 364 : PQclear(res);
18937 : }
18938 :
18939 : /*
18940 : * dumpSequence
18941 : * write the declaration (not data) of one user-defined sequence
18942 : */
18943 : static void
18944 768 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18945 : {
18946 768 : DumpOptions *dopt = fout->dopt;
18947 : SequenceItem *seq;
18948 : bool is_ascending;
18949 : int64 default_minv,
18950 : default_maxv;
18951 768 : PQExpBuffer query = createPQExpBuffer();
18952 768 : PQExpBuffer delqry = createPQExpBuffer();
18953 : char *qseqname;
18954 768 : TableInfo *owning_tab = NULL;
18955 :
18956 768 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18957 :
18958 : /*
18959 : * For versions >= 10, the sequence information is gathered in a sorted
18960 : * table before any calls to dumpSequence(). See collectSequences() for
18961 : * more information.
18962 : */
18963 768 : if (fout->remoteVersion >= 100000)
18964 : {
18965 768 : SequenceItem key = {0};
18966 :
18967 : Assert(sequences);
18968 :
18969 768 : key.oid = tbinfo->dobj.catId.oid;
18970 768 : seq = bsearch(&key, sequences, nsequences,
18971 : sizeof(SequenceItem), SequenceItemCmp);
18972 : }
18973 : else
18974 : {
18975 : PGresult *res;
18976 :
18977 : /*
18978 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18979 : *
18980 : * Note: it might seem that 'bigint' potentially needs to be
18981 : * schema-qualified, but actually that's a keyword.
18982 : */
18983 0 : appendPQExpBuffer(query,
18984 : "SELECT 'bigint' AS sequence_type, "
18985 : "start_value, increment_by, max_value, min_value, "
18986 : "cache_value, is_cycled FROM %s",
18987 0 : fmtQualifiedDumpable(tbinfo));
18988 :
18989 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18990 :
18991 0 : if (PQntuples(res) != 1)
18992 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18993 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18994 : PQntuples(res)),
18995 : tbinfo->dobj.name, PQntuples(res));
18996 :
18997 0 : seq = pg_malloc0(sizeof(SequenceItem));
18998 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18999 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19000 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19001 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19002 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19003 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19004 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19005 :
19006 0 : PQclear(res);
19007 : }
19008 :
19009 : /* Calculate default limits for a sequence of this type */
19010 768 : is_ascending = (seq->incby >= 0);
19011 768 : if (seq->seqtype == SEQTYPE_SMALLINT)
19012 : {
19013 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19014 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19015 : }
19016 718 : else if (seq->seqtype == SEQTYPE_INTEGER)
19017 : {
19018 586 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19019 586 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19020 : }
19021 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
19022 : {
19023 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19024 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19025 : }
19026 : else
19027 : {
19028 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19029 : default_minv = default_maxv = 0; /* keep compiler quiet */
19030 : }
19031 :
19032 : /*
19033 : * Identity sequences are not to be dropped separately.
19034 : */
19035 768 : if (!tbinfo->is_identity_sequence)
19036 : {
19037 478 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19038 478 : fmtQualifiedDumpable(tbinfo));
19039 : }
19040 :
19041 768 : resetPQExpBuffer(query);
19042 :
19043 768 : if (dopt->binary_upgrade)
19044 : {
19045 132 : binary_upgrade_set_pg_class_oids(fout, query,
19046 132 : tbinfo->dobj.catId.oid);
19047 :
19048 : /*
19049 : * In older PG versions a sequence will have a pg_type entry, but v14
19050 : * and up don't use that, so don't attempt to preserve the type OID.
19051 : */
19052 : }
19053 :
19054 768 : if (tbinfo->is_identity_sequence)
19055 : {
19056 290 : owning_tab = findTableByOid(tbinfo->owning_tab);
19057 :
19058 290 : appendPQExpBuffer(query,
19059 : "ALTER TABLE %s ",
19060 290 : fmtQualifiedDumpable(owning_tab));
19061 290 : appendPQExpBuffer(query,
19062 : "ALTER COLUMN %s ADD GENERATED ",
19063 290 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19064 290 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19065 210 : appendPQExpBufferStr(query, "ALWAYS");
19066 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19067 80 : appendPQExpBufferStr(query, "BY DEFAULT");
19068 290 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19069 290 : fmtQualifiedDumpable(tbinfo));
19070 :
19071 : /*
19072 : * Emit persistence option only if it's different from the owning
19073 : * table's. This avoids using this new syntax unnecessarily.
19074 : */
19075 290 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19076 20 : appendPQExpBuffer(query, " %s\n",
19077 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19078 : "UNLOGGED" : "LOGGED");
19079 : }
19080 : else
19081 : {
19082 478 : appendPQExpBuffer(query,
19083 : "CREATE %sSEQUENCE %s\n",
19084 478 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19085 : "UNLOGGED " : "",
19086 478 : fmtQualifiedDumpable(tbinfo));
19087 :
19088 478 : if (seq->seqtype != SEQTYPE_BIGINT)
19089 376 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19090 : }
19091 :
19092 768 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19093 :
19094 768 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19095 :
19096 768 : if (seq->minv != default_minv)
19097 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19098 : else
19099 738 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19100 :
19101 768 : if (seq->maxv != default_maxv)
19102 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19103 : else
19104 738 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19105 :
19106 768 : appendPQExpBuffer(query,
19107 : " CACHE " INT64_FORMAT "%s",
19108 768 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19109 :
19110 768 : if (tbinfo->is_identity_sequence)
19111 290 : appendPQExpBufferStr(query, "\n);\n");
19112 : else
19113 478 : appendPQExpBufferStr(query, ";\n");
19114 :
19115 : /* binary_upgrade: no need to clear TOAST table oid */
19116 :
19117 768 : if (dopt->binary_upgrade)
19118 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19119 : "SEQUENCE", qseqname,
19120 132 : tbinfo->dobj.namespace->dobj.name);
19121 :
19122 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19123 768 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19124 768 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19125 : .namespace = tbinfo->dobj.namespace->dobj.name,
19126 : .owner = tbinfo->rolname,
19127 : .description = "SEQUENCE",
19128 : .section = SECTION_PRE_DATA,
19129 : .createStmt = query->data,
19130 : .dropStmt = delqry->data));
19131 :
19132 : /*
19133 : * If the sequence is owned by a table column, emit the ALTER for it as a
19134 : * separate TOC entry immediately following the sequence's own entry. It's
19135 : * OK to do this rather than using full sorting logic, because the
19136 : * dependency that tells us it's owned will have forced the table to be
19137 : * created first. We can't just include the ALTER in the TOC entry
19138 : * because it will fail if we haven't reassigned the sequence owner to
19139 : * match the table's owner.
19140 : *
19141 : * We need not schema-qualify the table reference because both sequence
19142 : * and table must be in the same schema.
19143 : */
19144 768 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19145 : {
19146 286 : owning_tab = findTableByOid(tbinfo->owning_tab);
19147 :
19148 286 : if (owning_tab == NULL)
19149 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19150 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19151 :
19152 286 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19153 : {
19154 282 : resetPQExpBuffer(query);
19155 282 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19156 282 : fmtQualifiedDumpable(tbinfo));
19157 282 : appendPQExpBuffer(query, " OWNED BY %s",
19158 282 : fmtQualifiedDumpable(owning_tab));
19159 282 : appendPQExpBuffer(query, ".%s;\n",
19160 282 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19161 :
19162 282 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19163 282 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19164 282 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19165 : .namespace = tbinfo->dobj.namespace->dobj.name,
19166 : .owner = tbinfo->rolname,
19167 : .description = "SEQUENCE OWNED BY",
19168 : .section = SECTION_PRE_DATA,
19169 : .createStmt = query->data,
19170 : .deps = &(tbinfo->dobj.dumpId),
19171 : .nDeps = 1));
19172 : }
19173 : }
19174 :
19175 : /* Dump Sequence Comments and Security Labels */
19176 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19177 0 : dumpComment(fout, "SEQUENCE", qseqname,
19178 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19179 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19180 :
19181 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19182 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19183 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19184 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19185 :
19186 768 : if (fout->remoteVersion < 100000)
19187 0 : pg_free(seq);
19188 768 : destroyPQExpBuffer(query);
19189 768 : destroyPQExpBuffer(delqry);
19190 768 : free(qseqname);
19191 768 : }
19192 :
19193 : /*
19194 : * dumpSequenceData
19195 : * write the data of one user-defined sequence
19196 : */
19197 : static void
19198 804 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19199 : {
19200 804 : TableInfo *tbinfo = tdinfo->tdtable;
19201 : int64 last;
19202 : bool called;
19203 804 : PQExpBuffer query = createPQExpBuffer();
19204 :
19205 : /*
19206 : * For versions >= 18, the sequence information is gathered in the sorted
19207 : * array before any calls to dumpSequenceData(). See collectSequences()
19208 : * for more information.
19209 : *
19210 : * For older versions, we have to query the sequence relations
19211 : * individually.
19212 : */
19213 804 : if (fout->remoteVersion < 180000)
19214 : {
19215 : PGresult *res;
19216 :
19217 0 : appendPQExpBuffer(query,
19218 : "SELECT last_value, is_called FROM %s",
19219 0 : fmtQualifiedDumpable(tbinfo));
19220 :
19221 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19222 :
19223 0 : if (PQntuples(res) != 1)
19224 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19225 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19226 : PQntuples(res)),
19227 : tbinfo->dobj.name, PQntuples(res));
19228 :
19229 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19230 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19231 :
19232 0 : PQclear(res);
19233 : }
19234 : else
19235 : {
19236 804 : SequenceItem key = {0};
19237 : SequenceItem *entry;
19238 :
19239 : Assert(sequences);
19240 : Assert(tbinfo->dobj.catId.oid);
19241 :
19242 804 : key.oid = tbinfo->dobj.catId.oid;
19243 804 : entry = bsearch(&key, sequences, nsequences,
19244 : sizeof(SequenceItem), SequenceItemCmp);
19245 :
19246 804 : last = entry->last_value;
19247 804 : called = entry->is_called;
19248 : }
19249 :
19250 804 : resetPQExpBuffer(query);
19251 804 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19252 804 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19253 804 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19254 : last, (called ? "true" : "false"));
19255 :
19256 804 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19257 804 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19258 804 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19259 : .namespace = tbinfo->dobj.namespace->dobj.name,
19260 : .owner = tbinfo->rolname,
19261 : .description = "SEQUENCE SET",
19262 : .section = SECTION_DATA,
19263 : .createStmt = query->data,
19264 : .deps = &(tbinfo->dobj.dumpId),
19265 : .nDeps = 1));
19266 :
19267 804 : destroyPQExpBuffer(query);
19268 804 : }
19269 :
19270 : /*
19271 : * dumpTrigger
19272 : * write the declaration of one user-defined table trigger
19273 : */
19274 : static void
19275 1076 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19276 : {
19277 1076 : DumpOptions *dopt = fout->dopt;
19278 1076 : TableInfo *tbinfo = tginfo->tgtable;
19279 : PQExpBuffer query;
19280 : PQExpBuffer delqry;
19281 : PQExpBuffer trigprefix;
19282 : PQExpBuffer trigidentity;
19283 : char *qtabname;
19284 : char *tag;
19285 :
19286 : /* Do nothing if not dumping schema */
19287 1076 : if (!dopt->dumpSchema)
19288 62 : return;
19289 :
19290 1014 : query = createPQExpBuffer();
19291 1014 : delqry = createPQExpBuffer();
19292 1014 : trigprefix = createPQExpBuffer();
19293 1014 : trigidentity = createPQExpBuffer();
19294 :
19295 1014 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19296 :
19297 1014 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19298 1014 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19299 :
19300 1014 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19301 1014 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19302 :
19303 : /* Triggers can depend on extensions */
19304 1014 : append_depends_on_extension(fout, query, &tginfo->dobj,
19305 : "pg_catalog.pg_trigger", "TRIGGER",
19306 1014 : trigidentity->data);
19307 :
19308 1014 : if (tginfo->tgispartition)
19309 : {
19310 : Assert(tbinfo->ispartition);
19311 :
19312 : /*
19313 : * Partition triggers only appear here because their 'tgenabled' flag
19314 : * differs from its parent's. The trigger is created already, so
19315 : * remove the CREATE and replace it with an ALTER. (Clear out the
19316 : * DROP query too, so that pg_dump --create does not cause errors.)
19317 : */
19318 236 : resetPQExpBuffer(query);
19319 236 : resetPQExpBuffer(delqry);
19320 236 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19321 236 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19322 236 : fmtQualifiedDumpable(tbinfo));
19323 236 : switch (tginfo->tgenabled)
19324 : {
19325 82 : case 'f':
19326 : case 'D':
19327 82 : appendPQExpBufferStr(query, "DISABLE");
19328 82 : break;
19329 0 : case 't':
19330 : case 'O':
19331 0 : appendPQExpBufferStr(query, "ENABLE");
19332 0 : break;
19333 72 : case 'R':
19334 72 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19335 72 : break;
19336 82 : case 'A':
19337 82 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19338 82 : break;
19339 : }
19340 236 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19341 236 : fmtId(tginfo->dobj.name));
19342 : }
19343 778 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19344 : {
19345 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19346 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19347 0 : fmtQualifiedDumpable(tbinfo));
19348 0 : switch (tginfo->tgenabled)
19349 : {
19350 0 : case 'D':
19351 : case 'f':
19352 0 : appendPQExpBufferStr(query, "DISABLE");
19353 0 : break;
19354 0 : case 'A':
19355 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19356 0 : break;
19357 0 : case 'R':
19358 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19359 0 : break;
19360 0 : default:
19361 0 : appendPQExpBufferStr(query, "ENABLE");
19362 0 : break;
19363 : }
19364 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19365 0 : fmtId(tginfo->dobj.name));
19366 : }
19367 :
19368 1014 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19369 1014 : fmtId(tginfo->dobj.name));
19370 :
19371 1014 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19372 :
19373 1014 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19374 1014 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19375 1014 : ARCHIVE_OPTS(.tag = tag,
19376 : .namespace = tbinfo->dobj.namespace->dobj.name,
19377 : .owner = tbinfo->rolname,
19378 : .description = "TRIGGER",
19379 : .section = SECTION_POST_DATA,
19380 : .createStmt = query->data,
19381 : .dropStmt = delqry->data));
19382 :
19383 1014 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19384 0 : dumpComment(fout, trigprefix->data, qtabname,
19385 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19386 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19387 :
19388 1014 : free(tag);
19389 1014 : destroyPQExpBuffer(query);
19390 1014 : destroyPQExpBuffer(delqry);
19391 1014 : destroyPQExpBuffer(trigprefix);
19392 1014 : destroyPQExpBuffer(trigidentity);
19393 1014 : free(qtabname);
19394 : }
19395 :
19396 : /*
19397 : * dumpEventTrigger
19398 : * write the declaration of one user-defined event trigger
19399 : */
19400 : static void
19401 90 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19402 : {
19403 90 : DumpOptions *dopt = fout->dopt;
19404 : PQExpBuffer query;
19405 : PQExpBuffer delqry;
19406 : char *qevtname;
19407 :
19408 : /* Do nothing if not dumping schema */
19409 90 : if (!dopt->dumpSchema)
19410 12 : return;
19411 :
19412 78 : query = createPQExpBuffer();
19413 78 : delqry = createPQExpBuffer();
19414 :
19415 78 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19416 :
19417 78 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19418 78 : appendPQExpBufferStr(query, qevtname);
19419 78 : appendPQExpBufferStr(query, " ON ");
19420 78 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19421 :
19422 78 : if (strcmp("", evtinfo->evttags) != 0)
19423 : {
19424 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19425 10 : appendPQExpBufferStr(query, evtinfo->evttags);
19426 10 : appendPQExpBufferChar(query, ')');
19427 : }
19428 :
19429 78 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19430 78 : appendPQExpBufferStr(query, evtinfo->evtfname);
19431 78 : appendPQExpBufferStr(query, "();\n");
19432 :
19433 78 : if (evtinfo->evtenabled != 'O')
19434 : {
19435 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19436 : qevtname);
19437 0 : switch (evtinfo->evtenabled)
19438 : {
19439 0 : case 'D':
19440 0 : appendPQExpBufferStr(query, "DISABLE");
19441 0 : break;
19442 0 : case 'A':
19443 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19444 0 : break;
19445 0 : case 'R':
19446 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19447 0 : break;
19448 0 : default:
19449 0 : appendPQExpBufferStr(query, "ENABLE");
19450 0 : break;
19451 : }
19452 0 : appendPQExpBufferStr(query, ";\n");
19453 : }
19454 :
19455 78 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19456 : qevtname);
19457 :
19458 78 : if (dopt->binary_upgrade)
19459 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19460 : "EVENT TRIGGER", qevtname, NULL);
19461 :
19462 78 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19463 78 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19464 78 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19465 : .owner = evtinfo->evtowner,
19466 : .description = "EVENT TRIGGER",
19467 : .section = SECTION_POST_DATA,
19468 : .createStmt = query->data,
19469 : .dropStmt = delqry->data));
19470 :
19471 78 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19472 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19473 0 : NULL, evtinfo->evtowner,
19474 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19475 :
19476 78 : destroyPQExpBuffer(query);
19477 78 : destroyPQExpBuffer(delqry);
19478 78 : free(qevtname);
19479 : }
19480 :
19481 : /*
19482 : * dumpRule
19483 : * Dump a rule
19484 : */
19485 : static void
19486 2380 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19487 : {
19488 2380 : DumpOptions *dopt = fout->dopt;
19489 2380 : TableInfo *tbinfo = rinfo->ruletable;
19490 : bool is_view;
19491 : PQExpBuffer query;
19492 : PQExpBuffer cmd;
19493 : PQExpBuffer delcmd;
19494 : PQExpBuffer ruleprefix;
19495 : char *qtabname;
19496 : PGresult *res;
19497 : char *tag;
19498 :
19499 : /* Do nothing if not dumping schema */
19500 2380 : if (!dopt->dumpSchema)
19501 132 : return;
19502 :
19503 : /*
19504 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19505 : * we do not want to dump it as a separate object.
19506 : */
19507 2248 : if (!rinfo->separate)
19508 1826 : return;
19509 :
19510 : /*
19511 : * If it's an ON SELECT rule, we want to print it as a view definition,
19512 : * instead of a rule.
19513 : */
19514 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19515 :
19516 422 : query = createPQExpBuffer();
19517 422 : cmd = createPQExpBuffer();
19518 422 : delcmd = createPQExpBuffer();
19519 422 : ruleprefix = createPQExpBuffer();
19520 :
19521 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19522 :
19523 422 : if (is_view)
19524 : {
19525 : PQExpBuffer result;
19526 :
19527 : /*
19528 : * We need OR REPLACE here because we'll be replacing a dummy view.
19529 : * Otherwise this should look largely like the regular view dump code.
19530 : */
19531 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19532 20 : fmtQualifiedDumpable(tbinfo));
19533 20 : if (nonemptyReloptions(tbinfo->reloptions))
19534 : {
19535 0 : appendPQExpBufferStr(cmd, " WITH (");
19536 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19537 0 : appendPQExpBufferChar(cmd, ')');
19538 : }
19539 20 : result = createViewAsClause(fout, tbinfo);
19540 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19541 20 : destroyPQExpBuffer(result);
19542 20 : if (tbinfo->checkoption != NULL)
19543 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19544 : tbinfo->checkoption);
19545 20 : appendPQExpBufferStr(cmd, ";\n");
19546 : }
19547 : else
19548 : {
19549 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19550 402 : appendPQExpBuffer(query,
19551 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19552 402 : rinfo->dobj.catId.oid);
19553 :
19554 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19555 :
19556 402 : if (PQntuples(res) != 1)
19557 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19558 : rinfo->dobj.name, tbinfo->dobj.name);
19559 :
19560 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19561 :
19562 402 : PQclear(res);
19563 : }
19564 :
19565 : /*
19566 : * Add the command to alter the rules replication firing semantics if it
19567 : * differs from the default.
19568 : */
19569 422 : if (rinfo->ev_enabled != 'O')
19570 : {
19571 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19572 30 : switch (rinfo->ev_enabled)
19573 : {
19574 0 : case 'A':
19575 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19576 0 : fmtId(rinfo->dobj.name));
19577 0 : break;
19578 0 : case 'R':
19579 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19580 0 : fmtId(rinfo->dobj.name));
19581 0 : break;
19582 30 : case 'D':
19583 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19584 30 : fmtId(rinfo->dobj.name));
19585 30 : break;
19586 : }
19587 : }
19588 :
19589 422 : if (is_view)
19590 : {
19591 : /*
19592 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19593 : * REPLACE VIEW to replace the rule with something with minimal
19594 : * dependencies.
19595 : */
19596 : PQExpBuffer result;
19597 :
19598 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19599 20 : fmtQualifiedDumpable(tbinfo));
19600 20 : result = createDummyViewAsClause(fout, tbinfo);
19601 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19602 20 : destroyPQExpBuffer(result);
19603 : }
19604 : else
19605 : {
19606 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19607 402 : fmtId(rinfo->dobj.name));
19608 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
19609 402 : fmtQualifiedDumpable(tbinfo));
19610 : }
19611 :
19612 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19613 422 : fmtId(rinfo->dobj.name));
19614 :
19615 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19616 :
19617 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19618 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19619 422 : ARCHIVE_OPTS(.tag = tag,
19620 : .namespace = tbinfo->dobj.namespace->dobj.name,
19621 : .owner = tbinfo->rolname,
19622 : .description = "RULE",
19623 : .section = SECTION_POST_DATA,
19624 : .createStmt = cmd->data,
19625 : .dropStmt = delcmd->data));
19626 :
19627 : /* Dump rule comments */
19628 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19629 0 : dumpComment(fout, ruleprefix->data, qtabname,
19630 0 : tbinfo->dobj.namespace->dobj.name,
19631 : tbinfo->rolname,
19632 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19633 :
19634 422 : free(tag);
19635 422 : destroyPQExpBuffer(query);
19636 422 : destroyPQExpBuffer(cmd);
19637 422 : destroyPQExpBuffer(delcmd);
19638 422 : destroyPQExpBuffer(ruleprefix);
19639 422 : free(qtabname);
19640 : }
19641 :
19642 : /*
19643 : * getExtensionMembership --- obtain extension membership data
19644 : *
19645 : * We need to identify objects that are extension members as soon as they're
19646 : * loaded, so that we can correctly determine whether they need to be dumped.
19647 : * Generally speaking, extension member objects will get marked as *not* to
19648 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19649 : * command. However, in binary upgrade mode we still need to dump the members
19650 : * individually.
19651 : */
19652 : void
19653 366 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19654 : int numExtensions)
19655 : {
19656 : PQExpBuffer query;
19657 : PGresult *res;
19658 : int ntups,
19659 : i;
19660 : int i_classid,
19661 : i_objid,
19662 : i_refobjid;
19663 : ExtensionInfo *ext;
19664 :
19665 : /* Nothing to do if no extensions */
19666 366 : if (numExtensions == 0)
19667 0 : return;
19668 :
19669 366 : query = createPQExpBuffer();
19670 :
19671 : /* refclassid constraint is redundant but may speed the search */
19672 366 : appendPQExpBufferStr(query, "SELECT "
19673 : "classid, objid, refobjid "
19674 : "FROM pg_depend "
19675 : "WHERE refclassid = 'pg_extension'::regclass "
19676 : "AND deptype = 'e' "
19677 : "ORDER BY 3");
19678 :
19679 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19680 :
19681 366 : ntups = PQntuples(res);
19682 :
19683 366 : i_classid = PQfnumber(res, "classid");
19684 366 : i_objid = PQfnumber(res, "objid");
19685 366 : i_refobjid = PQfnumber(res, "refobjid");
19686 :
19687 : /*
19688 : * Since we ordered the SELECT by referenced ID, we can expect that
19689 : * multiple entries for the same extension will appear together; this
19690 : * saves on searches.
19691 : */
19692 366 : ext = NULL;
19693 :
19694 3040 : for (i = 0; i < ntups; i++)
19695 : {
19696 : CatalogId objId;
19697 : Oid extId;
19698 :
19699 2674 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19700 2674 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19701 2674 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19702 :
19703 2674 : if (ext == NULL ||
19704 2308 : ext->dobj.catId.oid != extId)
19705 426 : ext = findExtensionByOid(extId);
19706 :
19707 2674 : if (ext == NULL)
19708 : {
19709 : /* shouldn't happen */
19710 0 : pg_log_warning("could not find referenced extension %u", extId);
19711 0 : continue;
19712 : }
19713 :
19714 2674 : recordExtensionMembership(objId, ext);
19715 : }
19716 :
19717 366 : PQclear(res);
19718 :
19719 366 : destroyPQExpBuffer(query);
19720 : }
19721 :
19722 : /*
19723 : * processExtensionTables --- deal with extension configuration tables
19724 : *
19725 : * There are two parts to this process:
19726 : *
19727 : * 1. Identify and create dump records for extension configuration tables.
19728 : *
19729 : * Extensions can mark tables as "configuration", which means that the user
19730 : * is able and expected to modify those tables after the extension has been
19731 : * loaded. For these tables, we dump out only the data- the structure is
19732 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19733 : * foreign keys, which brings us to-
19734 : *
19735 : * 2. Record FK dependencies between configuration tables.
19736 : *
19737 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19738 : * the data is loaded, we have to work out what the best order for reloading
19739 : * the data is, to avoid FK violations when the tables are restored. This is
19740 : * not perfect- we can't handle circular dependencies and if any exist they
19741 : * will cause an invalid dump to be produced (though at least all of the data
19742 : * is included for a user to manually restore). This is currently documented
19743 : * but perhaps we can provide a better solution in the future.
19744 : */
19745 : void
19746 364 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19747 : int numExtensions)
19748 : {
19749 364 : DumpOptions *dopt = fout->dopt;
19750 : PQExpBuffer query;
19751 : PGresult *res;
19752 : int ntups,
19753 : i;
19754 : int i_conrelid,
19755 : i_confrelid;
19756 :
19757 : /* Nothing to do if no extensions */
19758 364 : if (numExtensions == 0)
19759 0 : return;
19760 :
19761 : /*
19762 : * Identify extension configuration tables and create TableDataInfo
19763 : * objects for them, ensuring their data will be dumped even though the
19764 : * tables themselves won't be.
19765 : *
19766 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19767 : * user data in a configuration table is treated like schema data. This
19768 : * seems appropriate since system data in a config table would get
19769 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19770 : * list of extensions to be included, none of its data is dumped.
19771 : */
19772 788 : for (i = 0; i < numExtensions; i++)
19773 : {
19774 424 : ExtensionInfo *curext = &(extinfo[i]);
19775 424 : char *extconfig = curext->extconfig;
19776 424 : char *extcondition = curext->extcondition;
19777 424 : char **extconfigarray = NULL;
19778 424 : char **extconditionarray = NULL;
19779 424 : int nconfigitems = 0;
19780 424 : int nconditionitems = 0;
19781 :
19782 : /*
19783 : * Check if this extension is listed as to include in the dump. If
19784 : * not, any table data associated with it is discarded.
19785 : */
19786 424 : if (extension_include_oids.head != NULL &&
19787 16 : !simple_oid_list_member(&extension_include_oids,
19788 : curext->dobj.catId.oid))
19789 12 : continue;
19790 :
19791 : /*
19792 : * Check if this extension is listed as to exclude in the dump. If
19793 : * yes, any table data associated with it is discarded.
19794 : */
19795 424 : if (extension_exclude_oids.head != NULL &&
19796 8 : simple_oid_list_member(&extension_exclude_oids,
19797 : curext->dobj.catId.oid))
19798 4 : continue;
19799 :
19800 412 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19801 : {
19802 : int j;
19803 :
19804 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19805 0 : pg_fatal("could not parse %s array", "extconfig");
19806 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19807 0 : pg_fatal("could not parse %s array", "extcondition");
19808 40 : if (nconfigitems != nconditionitems)
19809 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19810 :
19811 120 : for (j = 0; j < nconfigitems; j++)
19812 : {
19813 : TableInfo *configtbl;
19814 80 : Oid configtbloid = atooid(extconfigarray[j]);
19815 80 : bool dumpobj =
19816 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19817 :
19818 80 : configtbl = findTableByOid(configtbloid);
19819 80 : if (configtbl == NULL)
19820 0 : continue;
19821 :
19822 : /*
19823 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19824 : * unless the table or its schema is explicitly included
19825 : */
19826 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19827 : {
19828 : /* check table explicitly requested */
19829 4 : if (table_include_oids.head != NULL &&
19830 0 : simple_oid_list_member(&table_include_oids,
19831 : configtbloid))
19832 0 : dumpobj = true;
19833 :
19834 : /* check table's schema explicitly requested */
19835 4 : if (configtbl->dobj.namespace->dobj.dump &
19836 : DUMP_COMPONENT_DATA)
19837 4 : dumpobj = true;
19838 : }
19839 :
19840 : /* check table excluded by an exclusion switch */
19841 88 : if (table_exclude_oids.head != NULL &&
19842 8 : simple_oid_list_member(&table_exclude_oids,
19843 : configtbloid))
19844 2 : dumpobj = false;
19845 :
19846 : /* check schema excluded by an exclusion switch */
19847 80 : if (simple_oid_list_member(&schema_exclude_oids,
19848 80 : configtbl->dobj.namespace->dobj.catId.oid))
19849 0 : dumpobj = false;
19850 :
19851 80 : if (dumpobj)
19852 : {
19853 78 : makeTableDataInfo(dopt, configtbl);
19854 78 : if (configtbl->dataObj != NULL)
19855 : {
19856 78 : if (strlen(extconditionarray[j]) > 0)
19857 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19858 : }
19859 : }
19860 : }
19861 : }
19862 412 : if (extconfigarray)
19863 40 : free(extconfigarray);
19864 412 : if (extconditionarray)
19865 40 : free(extconditionarray);
19866 : }
19867 :
19868 : /*
19869 : * Now that all the TableDataInfo objects have been created for all the
19870 : * extensions, check their FK dependencies and register them to try and
19871 : * dump the data out in an order that they can be restored in.
19872 : *
19873 : * Note that this is not a problem for user tables as their FKs are
19874 : * recreated after the data has been loaded.
19875 : */
19876 :
19877 364 : query = createPQExpBuffer();
19878 :
19879 364 : printfPQExpBuffer(query,
19880 : "SELECT conrelid, confrelid "
19881 : "FROM pg_constraint "
19882 : "JOIN pg_depend ON (objid = confrelid) "
19883 : "WHERE contype = 'f' "
19884 : "AND refclassid = 'pg_extension'::regclass "
19885 : "AND classid = 'pg_class'::regclass;");
19886 :
19887 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19888 364 : ntups = PQntuples(res);
19889 :
19890 364 : i_conrelid = PQfnumber(res, "conrelid");
19891 364 : i_confrelid = PQfnumber(res, "confrelid");
19892 :
19893 : /* Now get the dependencies and register them */
19894 364 : for (i = 0; i < ntups; i++)
19895 : {
19896 : Oid conrelid,
19897 : confrelid;
19898 : TableInfo *reftable,
19899 : *contable;
19900 :
19901 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19902 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19903 0 : contable = findTableByOid(conrelid);
19904 0 : reftable = findTableByOid(confrelid);
19905 :
19906 0 : if (reftable == NULL ||
19907 0 : reftable->dataObj == NULL ||
19908 0 : contable == NULL ||
19909 0 : contable->dataObj == NULL)
19910 0 : continue;
19911 :
19912 : /*
19913 : * Make referencing TABLE_DATA object depend on the referenced table's
19914 : * TABLE_DATA object.
19915 : */
19916 0 : addObjectDependency(&contable->dataObj->dobj,
19917 0 : reftable->dataObj->dobj.dumpId);
19918 : }
19919 364 : PQclear(res);
19920 364 : destroyPQExpBuffer(query);
19921 : }
19922 :
19923 : /*
19924 : * getDependencies --- obtain available dependency data
19925 : */
19926 : static void
19927 364 : getDependencies(Archive *fout)
19928 : {
19929 : PQExpBuffer query;
19930 : PGresult *res;
19931 : int ntups,
19932 : i;
19933 : int i_classid,
19934 : i_objid,
19935 : i_refclassid,
19936 : i_refobjid,
19937 : i_deptype;
19938 : DumpableObject *dobj,
19939 : *refdobj;
19940 :
19941 364 : pg_log_info("reading dependency data");
19942 :
19943 364 : query = createPQExpBuffer();
19944 :
19945 : /*
19946 : * Messy query to collect the dependency data we need. Note that we
19947 : * ignore the sub-object column, so that dependencies of or on a column
19948 : * look the same as dependencies of or on a whole table.
19949 : *
19950 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19951 : * already processed by getExtensionMembership.
19952 : */
19953 364 : appendPQExpBufferStr(query, "SELECT "
19954 : "classid, objid, refclassid, refobjid, deptype "
19955 : "FROM pg_depend "
19956 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19957 :
19958 : /*
19959 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19960 : * have to translate their dependencies into dependencies of their parent
19961 : * opfamily. Ignore internal dependencies though, as those will point to
19962 : * their parent opclass, which we needn't consider here (and if we did,
19963 : * it'd just result in circular dependencies). Also, "loose" opfamily
19964 : * entries will have dependencies on their parent opfamily, which we
19965 : * should drop since they'd likewise become useless self-dependencies.
19966 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19967 : */
19968 364 : appendPQExpBufferStr(query, "UNION ALL\n"
19969 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19970 : "FROM pg_depend d, pg_amop o "
19971 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19972 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19973 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19974 :
19975 : /* Likewise for pg_amproc entries */
19976 364 : appendPQExpBufferStr(query, "UNION ALL\n"
19977 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19978 : "FROM pg_depend d, pg_amproc p "
19979 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19980 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19981 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19982 :
19983 : /* Sort the output for efficiency below */
19984 364 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19985 :
19986 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19987 :
19988 364 : ntups = PQntuples(res);
19989 :
19990 364 : i_classid = PQfnumber(res, "classid");
19991 364 : i_objid = PQfnumber(res, "objid");
19992 364 : i_refclassid = PQfnumber(res, "refclassid");
19993 364 : i_refobjid = PQfnumber(res, "refobjid");
19994 364 : i_deptype = PQfnumber(res, "deptype");
19995 :
19996 : /*
19997 : * Since we ordered the SELECT by referencing ID, we can expect that
19998 : * multiple entries for the same object will appear together; this saves
19999 : * on searches.
20000 : */
20001 364 : dobj = NULL;
20002 :
20003 791046 : for (i = 0; i < ntups; i++)
20004 : {
20005 : CatalogId objId;
20006 : CatalogId refobjId;
20007 : char deptype;
20008 :
20009 790682 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20010 790682 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20011 790682 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20012 790682 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20013 790682 : deptype = *(PQgetvalue(res, i, i_deptype));
20014 :
20015 790682 : if (dobj == NULL ||
20016 740212 : dobj->catId.tableoid != objId.tableoid ||
20017 735952 : dobj->catId.oid != objId.oid)
20018 348874 : dobj = findObjectByCatalogId(objId);
20019 :
20020 : /*
20021 : * Failure to find objects mentioned in pg_depend is not unexpected,
20022 : * since for example we don't collect info about TOAST tables.
20023 : */
20024 790682 : if (dobj == NULL)
20025 : {
20026 : #ifdef NOT_USED
20027 : pg_log_warning("no referencing object %u %u",
20028 : objId.tableoid, objId.oid);
20029 : #endif
20030 51774 : continue;
20031 : }
20032 :
20033 740576 : refdobj = findObjectByCatalogId(refobjId);
20034 :
20035 740576 : if (refdobj == NULL)
20036 : {
20037 : #ifdef NOT_USED
20038 : pg_log_warning("no referenced object %u %u",
20039 : refobjId.tableoid, refobjId.oid);
20040 : #endif
20041 1668 : continue;
20042 : }
20043 :
20044 : /*
20045 : * For 'x' dependencies, mark the object for later; we still add the
20046 : * normal dependency, for possible ordering purposes. Currently
20047 : * pg_dump_sort.c knows to put extensions ahead of all object types
20048 : * that could possibly depend on them, but this is safer.
20049 : */
20050 738908 : if (deptype == 'x')
20051 88 : dobj->depends_on_ext = true;
20052 :
20053 : /*
20054 : * Ordinarily, table rowtypes have implicit dependencies on their
20055 : * tables. However, for a composite type the implicit dependency goes
20056 : * the other way in pg_depend; which is the right thing for DROP but
20057 : * it doesn't produce the dependency ordering we need. So in that one
20058 : * case, we reverse the direction of the dependency.
20059 : */
20060 738908 : if (deptype == 'i' &&
20061 208208 : dobj->objType == DO_TABLE &&
20062 2524 : refdobj->objType == DO_TYPE)
20063 370 : addObjectDependency(refdobj, dobj->dumpId);
20064 : else
20065 : /* normal case */
20066 738538 : addObjectDependency(dobj, refdobj->dumpId);
20067 : }
20068 :
20069 364 : PQclear(res);
20070 :
20071 364 : destroyPQExpBuffer(query);
20072 364 : }
20073 :
20074 :
20075 : /*
20076 : * createBoundaryObjects - create dummy DumpableObjects to represent
20077 : * dump section boundaries.
20078 : */
20079 : static DumpableObject *
20080 364 : createBoundaryObjects(void)
20081 : {
20082 : DumpableObject *dobjs;
20083 :
20084 364 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20085 :
20086 364 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20087 364 : dobjs[0].catId = nilCatalogId;
20088 364 : AssignDumpId(dobjs + 0);
20089 364 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20090 :
20091 364 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20092 364 : dobjs[1].catId = nilCatalogId;
20093 364 : AssignDumpId(dobjs + 1);
20094 364 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20095 :
20096 364 : return dobjs;
20097 : }
20098 :
20099 : /*
20100 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20101 : * section boundaries.
20102 : */
20103 : static void
20104 364 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20105 : DumpableObject *boundaryObjs)
20106 : {
20107 364 : DumpableObject *preDataBound = boundaryObjs + 0;
20108 364 : DumpableObject *postDataBound = boundaryObjs + 1;
20109 : int i;
20110 :
20111 1355542 : for (i = 0; i < numObjs; i++)
20112 : {
20113 1355178 : DumpableObject *dobj = dobjs[i];
20114 :
20115 : /*
20116 : * The classification of object types here must match the SECTION_xxx
20117 : * values assigned during subsequent ArchiveEntry calls!
20118 : */
20119 1355178 : switch (dobj->objType)
20120 : {
20121 1264122 : case DO_NAMESPACE:
20122 : case DO_EXTENSION:
20123 : case DO_TYPE:
20124 : case DO_SHELL_TYPE:
20125 : case DO_FUNC:
20126 : case DO_AGG:
20127 : case DO_OPERATOR:
20128 : case DO_ACCESS_METHOD:
20129 : case DO_OPCLASS:
20130 : case DO_OPFAMILY:
20131 : case DO_COLLATION:
20132 : case DO_CONVERSION:
20133 : case DO_TABLE:
20134 : case DO_TABLE_ATTACH:
20135 : case DO_ATTRDEF:
20136 : case DO_PROCLANG:
20137 : case DO_CAST:
20138 : case DO_DUMMY_TYPE:
20139 : case DO_TSPARSER:
20140 : case DO_TSDICT:
20141 : case DO_TSTEMPLATE:
20142 : case DO_TSCONFIG:
20143 : case DO_FDW:
20144 : case DO_FOREIGN_SERVER:
20145 : case DO_TRANSFORM:
20146 : /* Pre-data objects: must come before the pre-data boundary */
20147 1264122 : addObjectDependency(preDataBound, dobj->dumpId);
20148 1264122 : break;
20149 9924 : case DO_TABLE_DATA:
20150 : case DO_SEQUENCE_SET:
20151 : case DO_LARGE_OBJECT:
20152 : case DO_LARGE_OBJECT_DATA:
20153 : /* Data objects: must come between the boundaries */
20154 9924 : addObjectDependency(dobj, preDataBound->dumpId);
20155 9924 : addObjectDependency(postDataBound, dobj->dumpId);
20156 9924 : break;
20157 11736 : case DO_INDEX:
20158 : case DO_INDEX_ATTACH:
20159 : case DO_STATSEXT:
20160 : case DO_REFRESH_MATVIEW:
20161 : case DO_TRIGGER:
20162 : case DO_EVENT_TRIGGER:
20163 : case DO_DEFAULT_ACL:
20164 : case DO_POLICY:
20165 : case DO_PUBLICATION:
20166 : case DO_PUBLICATION_REL:
20167 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20168 : case DO_SUBSCRIPTION:
20169 : case DO_SUBSCRIPTION_REL:
20170 : /* Post-data objects: must come after the post-data boundary */
20171 11736 : addObjectDependency(dobj, postDataBound->dumpId);
20172 11736 : break;
20173 56300 : case DO_RULE:
20174 : /* Rules are post-data, but only if dumped separately */
20175 56300 : if (((RuleInfo *) dobj)->separate)
20176 1274 : addObjectDependency(dobj, postDataBound->dumpId);
20177 56300 : break;
20178 5216 : case DO_CONSTRAINT:
20179 : case DO_FK_CONSTRAINT:
20180 : /* Constraints are post-data, but only if dumped separately */
20181 5216 : if (((ConstraintInfo *) dobj)->separate)
20182 3708 : addObjectDependency(dobj, postDataBound->dumpId);
20183 5216 : break;
20184 364 : case DO_PRE_DATA_BOUNDARY:
20185 : /* nothing to do */
20186 364 : break;
20187 364 : case DO_POST_DATA_BOUNDARY:
20188 : /* must come after the pre-data boundary */
20189 364 : addObjectDependency(dobj, preDataBound->dumpId);
20190 364 : break;
20191 7152 : case DO_REL_STATS:
20192 : /* stats section varies by parent object type, DATA or POST */
20193 7152 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20194 : {
20195 4586 : addObjectDependency(dobj, preDataBound->dumpId);
20196 4586 : addObjectDependency(postDataBound, dobj->dumpId);
20197 : }
20198 : else
20199 2566 : addObjectDependency(dobj, postDataBound->dumpId);
20200 7152 : break;
20201 : }
20202 : }
20203 364 : }
20204 :
20205 :
20206 : /*
20207 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20208 : *
20209 : * The raw dependency data obtained by getDependencies() is not terribly
20210 : * useful in an archive dump, because in many cases there are dependency
20211 : * chains linking through objects that don't appear explicitly in the dump.
20212 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20213 : * will depend on other objects --- but the rule will not appear as a separate
20214 : * object in the dump. We need to adjust the view's dependencies to include
20215 : * whatever the rule depends on that is included in the dump.
20216 : *
20217 : * Just to make things more complicated, there are also "special" dependencies
20218 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20219 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20220 : * its table. In these cases we must leave the dependencies strictly as-is
20221 : * even if they refer to not-to-be-dumped objects.
20222 : *
20223 : * To handle this, the convention is that "special" dependencies are created
20224 : * during ArchiveEntry calls, and an archive TOC item that has any such
20225 : * entries will not be touched here. Otherwise, we recursively search the
20226 : * DumpableObject data structures to build the correct dependencies for each
20227 : * archive TOC item.
20228 : */
20229 : static void
20230 106 : BuildArchiveDependencies(Archive *fout)
20231 : {
20232 106 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20233 : TocEntry *te;
20234 :
20235 : /* Scan all TOC entries in the archive */
20236 14660 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20237 : {
20238 : DumpableObject *dobj;
20239 : DumpId *dependencies;
20240 : int nDeps;
20241 : int allocDeps;
20242 :
20243 : /* No need to process entries that will not be dumped */
20244 14554 : if (te->reqs == 0)
20245 7202 : continue;
20246 : /* Ignore entries that already have "special" dependencies */
20247 14528 : if (te->nDeps > 0)
20248 6304 : continue;
20249 : /* Otherwise, look up the item's original DumpableObject, if any */
20250 8224 : dobj = findObjectByDumpId(te->dumpId);
20251 8224 : if (dobj == NULL)
20252 652 : continue;
20253 : /* No work if it has no dependencies */
20254 7572 : if (dobj->nDeps <= 0)
20255 220 : continue;
20256 : /* Set up work array */
20257 7352 : allocDeps = 64;
20258 7352 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20259 7352 : nDeps = 0;
20260 : /* Recursively find all dumpable dependencies */
20261 7352 : findDumpableDependencies(AH, dobj,
20262 : &dependencies, &nDeps, &allocDeps);
20263 : /* And save 'em ... */
20264 7352 : if (nDeps > 0)
20265 : {
20266 5650 : dependencies = (DumpId *) pg_realloc(dependencies,
20267 : nDeps * sizeof(DumpId));
20268 5650 : te->dependencies = dependencies;
20269 5650 : te->nDeps = nDeps;
20270 : }
20271 : else
20272 1702 : free(dependencies);
20273 : }
20274 106 : }
20275 :
20276 : /* Recursive search subroutine for BuildArchiveDependencies */
20277 : static void
20278 17614 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20279 : DumpId **dependencies, int *nDeps, int *allocDeps)
20280 : {
20281 : int i;
20282 :
20283 : /*
20284 : * Ignore section boundary objects: if we search through them, we'll
20285 : * report lots of bogus dependencies.
20286 : */
20287 17614 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20288 17582 : dobj->objType == DO_POST_DATA_BOUNDARY)
20289 3060 : return;
20290 :
20291 36702 : for (i = 0; i < dobj->nDeps; i++)
20292 : {
20293 22148 : DumpId depid = dobj->dependencies[i];
20294 :
20295 22148 : if (TocIDRequired(AH, depid) != 0)
20296 : {
20297 : /* Object will be dumped, so just reference it as a dependency */
20298 11886 : if (*nDeps >= *allocDeps)
20299 : {
20300 0 : *allocDeps *= 2;
20301 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20302 0 : *allocDeps * sizeof(DumpId));
20303 : }
20304 11886 : (*dependencies)[*nDeps] = depid;
20305 11886 : (*nDeps)++;
20306 : }
20307 : else
20308 : {
20309 : /*
20310 : * Object will not be dumped, so recursively consider its deps. We
20311 : * rely on the assumption that sortDumpableObjects already broke
20312 : * any dependency loops, else we might recurse infinitely.
20313 : */
20314 10262 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20315 :
20316 10262 : if (otherdobj)
20317 10262 : findDumpableDependencies(AH, otherdobj,
20318 : dependencies, nDeps, allocDeps);
20319 : }
20320 : }
20321 : }
20322 :
20323 :
20324 : /*
20325 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20326 : * given type OID.
20327 : *
20328 : * This does not guarantee to schema-qualify the output, so it should not
20329 : * be used to create the target object name for CREATE or ALTER commands.
20330 : *
20331 : * Note that the result is cached and must not be freed by the caller.
20332 : */
20333 : static const char *
20334 4672 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20335 : {
20336 : TypeInfo *typeInfo;
20337 : char *result;
20338 : PQExpBuffer query;
20339 : PGresult *res;
20340 :
20341 4672 : if (oid == 0)
20342 : {
20343 0 : if ((opts & zeroAsStar) != 0)
20344 0 : return "*";
20345 0 : else if ((opts & zeroAsNone) != 0)
20346 0 : return "NONE";
20347 : }
20348 :
20349 : /* see if we have the result cached in the type's TypeInfo record */
20350 4672 : typeInfo = findTypeByOid(oid);
20351 4672 : if (typeInfo && typeInfo->ftypname)
20352 3696 : return typeInfo->ftypname;
20353 :
20354 976 : query = createPQExpBuffer();
20355 976 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20356 : oid);
20357 :
20358 976 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20359 :
20360 : /* result of format_type is already quoted */
20361 976 : result = pg_strdup(PQgetvalue(res, 0, 0));
20362 :
20363 976 : PQclear(res);
20364 976 : destroyPQExpBuffer(query);
20365 :
20366 : /*
20367 : * Cache the result for re-use in later requests, if possible. If we
20368 : * don't have a TypeInfo for the type, the string will be leaked once the
20369 : * caller is done with it ... but that case really should not happen, so
20370 : * leaking if it does seems acceptable.
20371 : */
20372 976 : if (typeInfo)
20373 976 : typeInfo->ftypname = result;
20374 :
20375 976 : return result;
20376 : }
20377 :
20378 : /*
20379 : * Return a column list clause for the given relation.
20380 : *
20381 : * Special case: if there are no undropped columns in the relation, return
20382 : * "", not an invalid "()" column list.
20383 : */
20384 : static const char *
20385 17020 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20386 : {
20387 17020 : int numatts = ti->numatts;
20388 17020 : char **attnames = ti->attnames;
20389 17020 : bool *attisdropped = ti->attisdropped;
20390 17020 : char *attgenerated = ti->attgenerated;
20391 : bool needComma;
20392 : int i;
20393 :
20394 17020 : appendPQExpBufferChar(buffer, '(');
20395 17020 : needComma = false;
20396 81816 : for (i = 0; i < numatts; i++)
20397 : {
20398 64796 : if (attisdropped[i])
20399 1208 : continue;
20400 63588 : if (attgenerated[i])
20401 2304 : continue;
20402 61284 : if (needComma)
20403 44748 : appendPQExpBufferStr(buffer, ", ");
20404 61284 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20405 61284 : needComma = true;
20406 : }
20407 :
20408 17020 : if (!needComma)
20409 484 : return ""; /* no undropped columns */
20410 :
20411 16536 : appendPQExpBufferChar(buffer, ')');
20412 16536 : return buffer->data;
20413 : }
20414 :
20415 : /*
20416 : * Check if a reloptions array is nonempty.
20417 : */
20418 : static bool
20419 27856 : nonemptyReloptions(const char *reloptions)
20420 : {
20421 : /* Don't want to print it if it's just "{}" */
20422 27856 : return (reloptions != NULL && strlen(reloptions) > 2);
20423 : }
20424 :
20425 : /*
20426 : * Format a reloptions array and append it to the given buffer.
20427 : *
20428 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20429 : */
20430 : static void
20431 428 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20432 : const char *prefix, Archive *fout)
20433 : {
20434 : bool res;
20435 :
20436 428 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20437 428 : fout->std_strings);
20438 428 : if (!res)
20439 0 : pg_log_warning("could not parse %s array", "reloptions");
20440 428 : }
20441 :
20442 : /*
20443 : * read_dump_filters - retrieve object identifier patterns from file
20444 : *
20445 : * Parse the specified filter file for include and exclude patterns, and add
20446 : * them to the relevant lists. If the filename is "-" then filters will be
20447 : * read from STDIN rather than a file.
20448 : */
20449 : static void
20450 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20451 : {
20452 : FilterStateData fstate;
20453 : char *objname;
20454 : FilterCommandType comtype;
20455 : FilterObjectType objtype;
20456 :
20457 52 : filter_init(&fstate, filename, exit_nicely);
20458 :
20459 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20460 : {
20461 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20462 : {
20463 34 : switch (objtype)
20464 : {
20465 0 : case FILTER_OBJECT_TYPE_NONE:
20466 0 : break;
20467 0 : case FILTER_OBJECT_TYPE_DATABASE:
20468 : case FILTER_OBJECT_TYPE_FUNCTION:
20469 : case FILTER_OBJECT_TYPE_INDEX:
20470 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20471 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20472 : case FILTER_OBJECT_TYPE_TRIGGER:
20473 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20474 : "include",
20475 : filter_object_type_name(objtype));
20476 0 : exit_nicely(1);
20477 : break; /* unreachable */
20478 :
20479 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20480 2 : simple_string_list_append(&extension_include_patterns, objname);
20481 2 : break;
20482 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20483 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20484 2 : break;
20485 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20486 2 : simple_string_list_append(&schema_include_patterns, objname);
20487 2 : dopt->include_everything = false;
20488 2 : break;
20489 26 : case FILTER_OBJECT_TYPE_TABLE:
20490 26 : simple_string_list_append(&table_include_patterns, objname);
20491 26 : dopt->include_everything = false;
20492 26 : break;
20493 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20494 2 : simple_string_list_append(&table_include_patterns_and_children,
20495 : objname);
20496 2 : dopt->include_everything = false;
20497 2 : break;
20498 : }
20499 : }
20500 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20501 : {
20502 18 : switch (objtype)
20503 : {
20504 0 : case FILTER_OBJECT_TYPE_NONE:
20505 0 : break;
20506 2 : case FILTER_OBJECT_TYPE_DATABASE:
20507 : case FILTER_OBJECT_TYPE_FUNCTION:
20508 : case FILTER_OBJECT_TYPE_INDEX:
20509 : case FILTER_OBJECT_TYPE_TRIGGER:
20510 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20511 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20512 : "exclude",
20513 : filter_object_type_name(objtype));
20514 2 : exit_nicely(1);
20515 : break;
20516 :
20517 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20518 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20519 2 : break;
20520 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20521 2 : simple_string_list_append(&tabledata_exclude_patterns,
20522 : objname);
20523 2 : break;
20524 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20525 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20526 : objname);
20527 2 : break;
20528 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20529 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20530 4 : break;
20531 4 : case FILTER_OBJECT_TYPE_TABLE:
20532 4 : simple_string_list_append(&table_exclude_patterns, objname);
20533 4 : break;
20534 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20535 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20536 : objname);
20537 2 : break;
20538 : }
20539 : }
20540 : else
20541 : {
20542 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20543 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20544 : }
20545 :
20546 64 : if (objname)
20547 50 : free(objname);
20548 : }
20549 :
20550 44 : filter_free(&fstate);
20551 44 : }
|