Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_dump.c
4 : * pg_dump is a utility for dumping out a postgres database
5 : * into a script file.
6 : *
7 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_constraint_d.h"
51 : #include "catalog/pg_default_acl_d.h"
52 : #include "catalog/pg_largeobject_d.h"
53 : #include "catalog/pg_largeobject_metadata_d.h"
54 : #include "catalog/pg_proc_d.h"
55 : #include "catalog/pg_publication_d.h"
56 : #include "catalog/pg_shdepend_d.h"
57 : #include "catalog/pg_subscription_d.h"
58 : #include "catalog/pg_type_d.h"
59 : #include "common/connect.h"
60 : #include "common/int.h"
61 : #include "common/relpath.h"
62 : #include "common/shortest_dec.h"
63 : #include "compress_io.h"
64 : #include "dumputils.h"
65 : #include "fe_utils/option_utils.h"
66 : #include "fe_utils/string_utils.h"
67 : #include "filter.h"
68 : #include "getopt_long.h"
69 : #include "libpq/libpq-fs.h"
70 : #include "parallel.h"
71 : #include "pg_backup_db.h"
72 : #include "pg_backup_utils.h"
73 : #include "pg_dump.h"
74 : #include "statistics/statistics_format.h"
75 : #include "storage/block.h"
76 :
77 : typedef struct
78 : {
79 : Oid roleoid; /* role's OID */
80 : const char *rolename; /* role's name */
81 : } RoleNameItem;
82 :
83 : typedef struct
84 : {
85 : const char *descr; /* comment for an object */
86 : Oid classoid; /* object class (catalog OID) */
87 : Oid objoid; /* object OID */
88 : int objsubid; /* subobject (table column #) */
89 : } CommentItem;
90 :
91 : typedef struct
92 : {
93 : const char *provider; /* label provider of this security label */
94 : const char *label; /* security label for an object */
95 : Oid classoid; /* object class (catalog OID) */
96 : Oid objoid; /* object OID */
97 : int objsubid; /* subobject (table column #) */
98 : } SecLabelItem;
99 :
100 : typedef struct
101 : {
102 : Oid oid; /* object OID */
103 : char relkind; /* object kind */
104 : RelFileNumber relfilenumber; /* object filenode */
105 : Oid toast_oid; /* toast table OID */
106 : RelFileNumber toast_relfilenumber; /* toast table filenode */
107 : Oid toast_index_oid; /* toast table index OID */
108 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
109 : } BinaryUpgradeClassOidItem;
110 :
111 : /* sequence types */
112 : typedef enum SeqType
113 : {
114 : SEQTYPE_SMALLINT,
115 : SEQTYPE_INTEGER,
116 : SEQTYPE_BIGINT,
117 : } SeqType;
118 :
119 : static const char *const SeqTypeNames[] =
120 : {
121 : [SEQTYPE_SMALLINT] = "smallint",
122 : [SEQTYPE_INTEGER] = "integer",
123 : [SEQTYPE_BIGINT] = "bigint",
124 : };
125 :
126 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
127 : "array length mismatch");
128 :
129 : typedef struct
130 : {
131 : Oid oid; /* sequence OID */
132 : SeqType seqtype; /* data type of sequence */
133 : bool cycled; /* whether sequence cycles */
134 : int64 minv; /* minimum value */
135 : int64 maxv; /* maximum value */
136 : int64 startv; /* start value */
137 : int64 incby; /* increment value */
138 : int64 cache; /* cache size */
139 : int64 last_value; /* last value of sequence */
140 : bool is_called; /* whether nextval advances before returning */
141 : bool null_seqtuple; /* did pg_get_sequence_data return nulls? */
142 : } SequenceItem;
143 :
144 : typedef enum OidOptions
145 : {
146 : zeroIsError = 1,
147 : zeroAsStar = 2,
148 : zeroAsNone = 4,
149 : } OidOptions;
150 :
151 : /* global decls */
152 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
153 :
154 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
155 :
156 : /* The specified names/patterns should to match at least one entity */
157 : static int strict_names = 0;
158 :
159 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
160 :
161 : /*
162 : * Object inclusion/exclusion lists
163 : *
164 : * The string lists record the patterns given by command-line switches,
165 : * which we then convert to lists of OIDs of matching objects.
166 : */
167 : static SimpleStringList schema_include_patterns = {NULL, NULL};
168 : static SimpleOidList schema_include_oids = {NULL, NULL};
169 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
170 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
171 :
172 : static SimpleStringList table_include_patterns = {NULL, NULL};
173 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
174 : static SimpleOidList table_include_oids = {NULL, NULL};
175 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
176 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
177 : static SimpleOidList table_exclude_oids = {NULL, NULL};
178 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
179 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
180 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
181 :
182 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
183 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
184 :
185 : static SimpleStringList extension_include_patterns = {NULL, NULL};
186 : static SimpleOidList extension_include_oids = {NULL, NULL};
187 :
188 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
189 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
190 :
191 : static const CatalogId nilCatalogId = {0, 0};
192 :
193 : /* override for standard extra_float_digits setting */
194 : static bool have_extra_float_digits = false;
195 : static int extra_float_digits;
196 :
197 : /* sorted table of role names */
198 : static RoleNameItem *rolenames = NULL;
199 : static int nrolenames = 0;
200 :
201 : /* sorted table of comments */
202 : static CommentItem *comments = NULL;
203 : static int ncomments = 0;
204 :
205 : /* sorted table of security labels */
206 : static SecLabelItem *seclabels = NULL;
207 : static int nseclabels = 0;
208 :
209 : /* sorted table of pg_class information for binary upgrade */
210 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
211 : static int nbinaryUpgradeClassOids = 0;
212 :
213 : /* sorted table of sequences */
214 : static SequenceItem *sequences = NULL;
215 : static int nsequences = 0;
216 :
217 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
218 : #define MAX_ATTR_STATS_RELS 64
219 :
220 : /*
221 : * The default number of rows per INSERT when
222 : * --inserts is specified without --rows-per-insert
223 : */
224 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
225 :
226 : /*
227 : * Maximum number of large objects to group into a single ArchiveEntry.
228 : * At some point we might want to make this user-controllable, but for now
229 : * a hard-wired setting will suffice.
230 : */
231 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
232 :
233 : /*
234 : * Macro for producing quoted, schema-qualified name of a dumpable object.
235 : */
236 : #define fmtQualifiedDumpable(obj) \
237 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
238 : (obj)->dobj.name)
239 :
240 : static void help(const char *progname);
241 : static void setup_connection(Archive *AH,
242 : const char *dumpencoding, const char *dumpsnapshot,
243 : char *use_role);
244 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
245 : static void expand_schema_name_patterns(Archive *fout,
246 : SimpleStringList *patterns,
247 : SimpleOidList *oids,
248 : bool strict_names);
249 : static void expand_extension_name_patterns(Archive *fout,
250 : SimpleStringList *patterns,
251 : SimpleOidList *oids,
252 : bool strict_names);
253 : static void expand_foreign_server_name_patterns(Archive *fout,
254 : SimpleStringList *patterns,
255 : SimpleOidList *oids);
256 : static void expand_table_name_patterns(Archive *fout,
257 : SimpleStringList *patterns,
258 : SimpleOidList *oids,
259 : bool strict_names,
260 : bool with_child_tables);
261 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
262 : const char *pattern);
263 :
264 : static NamespaceInfo *findNamespace(Oid nsoid);
265 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
266 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
267 : static const char *getRoleName(const char *roleoid_str);
268 : static void collectRoleNames(Archive *fout);
269 : static void getAdditionalACLs(Archive *fout);
270 : static void dumpCommentExtended(Archive *fout, const char *type,
271 : const char *name, const char *namespace,
272 : const char *owner, CatalogId catalogId,
273 : int subid, DumpId dumpId,
274 : const char *initdb_comment);
275 : static inline void dumpComment(Archive *fout, const char *type,
276 : const char *name, const char *namespace,
277 : const char *owner, CatalogId catalogId,
278 : int subid, DumpId dumpId);
279 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
280 : static void collectComments(Archive *fout);
281 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
282 : const char *namespace, const char *owner,
283 : CatalogId catalogId, int subid, DumpId dumpId);
284 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
285 : static void collectSecLabels(Archive *fout);
286 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
287 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
288 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
289 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
290 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
291 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
292 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
293 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
294 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
295 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
296 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
297 : PGresult *res);
298 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
299 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
300 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
301 : static void dumpCast(Archive *fout, const CastInfo *cast);
302 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
303 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
304 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
305 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
306 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
307 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
308 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
309 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
310 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
311 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
312 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
313 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
314 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
315 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
316 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
317 : static void collectSequences(Archive *fout);
318 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
319 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
320 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
321 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
322 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
323 : static void dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo);
324 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
325 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
326 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
327 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
328 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
329 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
330 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
331 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
332 : static void dumpUserMappings(Archive *fout,
333 : const char *servername, const char *namespace,
334 : const char *owner, CatalogId catalogId, DumpId dumpId);
335 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
336 :
337 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
338 : const char *type, const char *name, const char *subname,
339 : const char *nspname, const char *tag, const char *owner,
340 : const DumpableAcl *dacl);
341 :
342 : static void getDependencies(Archive *fout);
343 : static void BuildArchiveDependencies(Archive *fout);
344 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
345 : DumpId **dependencies, int *nDeps, int *allocDeps);
346 :
347 : static DumpableObject *createBoundaryObjects(void);
348 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
349 : DumpableObject *boundaryObjs);
350 :
351 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
352 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
353 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
354 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
355 : static void buildMatViewRefreshDependencies(Archive *fout);
356 : static void getTableDataFKConstraints(void);
357 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
358 : TableInfo *tbinfo, int j,
359 : int i_notnull_name,
360 : int i_notnull_comment,
361 : int i_notnull_invalidoid,
362 : int i_notnull_noinherit,
363 : int i_notnull_islocal,
364 : PQExpBuffer *invalidnotnulloids);
365 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
366 : bool is_agg);
367 : static char *format_function_signature(Archive *fout,
368 : const FuncInfo *finfo, bool honor_quotes);
369 : static char *convertRegProcReference(const char *proc);
370 : static char *getFormattedOperatorName(const char *oproid);
371 : static char *convertTSFunction(Archive *fout, Oid funcOid);
372 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
373 : static void getLOs(Archive *fout);
374 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
375 : static int dumpLOs(Archive *fout, const void *arg);
376 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
377 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
378 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
379 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
380 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
381 : static void dumpDatabase(Archive *fout);
382 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
383 : const char *dbname, Oid dboid);
384 : static void dumpEncoding(Archive *AH);
385 : static void dumpStdStrings(Archive *AH);
386 : static void dumpSearchPath(Archive *AH);
387 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
388 : PQExpBuffer upgrade_buffer,
389 : Oid pg_type_oid,
390 : bool force_array_type,
391 : bool include_multirange_type);
392 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
393 : PQExpBuffer upgrade_buffer,
394 : const TableInfo *tbinfo);
395 : static void collectBinaryUpgradeClassOids(Archive *fout);
396 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
397 : PQExpBuffer upgrade_buffer,
398 : Oid pg_class_oid);
399 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
400 : const DumpableObject *dobj,
401 : const char *objtype,
402 : const char *objname,
403 : const char *objnamespace);
404 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
405 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
406 : static bool nonemptyReloptions(const char *reloptions);
407 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
408 : const char *prefix, Archive *fout);
409 : static char *get_synchronized_snapshot(Archive *fout);
410 : static void set_restrict_relation_kind(Archive *AH, const char *value);
411 : static void setupDumpWorker(Archive *AH);
412 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
413 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
414 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
415 :
416 :
417 : int
418 389 : main(int argc, char **argv)
419 : {
420 : int c;
421 389 : const char *filename = NULL;
422 389 : const char *format = "p";
423 : TableInfo *tblinfo;
424 : int numTables;
425 : DumpableObject **dobjs;
426 : int numObjs;
427 : DumpableObject *boundaryObjs;
428 : int i;
429 : int optindex;
430 : RestoreOptions *ropt;
431 : Archive *fout; /* the script file */
432 389 : bool g_verbose = false;
433 389 : const char *dumpencoding = NULL;
434 389 : const char *dumpsnapshot = NULL;
435 389 : char *use_role = NULL;
436 389 : int numWorkers = 1;
437 389 : int plainText = 0;
438 389 : ArchiveFormat archiveFormat = archUnknown;
439 : ArchiveMode archiveMode;
440 389 : pg_compress_specification compression_spec = {0};
441 389 : char *compression_detail = NULL;
442 389 : char *compression_algorithm_str = "none";
443 389 : char *error_detail = NULL;
444 389 : bool user_compression_defined = false;
445 389 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
446 389 : bool data_only = false;
447 389 : bool schema_only = false;
448 389 : bool statistics_only = false;
449 389 : bool with_statistics = false;
450 389 : bool no_data = false;
451 389 : bool no_schema = false;
452 389 : bool no_statistics = false;
453 :
454 : static DumpOptions dopt;
455 :
456 : static struct option long_options[] = {
457 : {"data-only", no_argument, NULL, 'a'},
458 : {"blobs", no_argument, NULL, 'b'},
459 : {"large-objects", no_argument, NULL, 'b'},
460 : {"no-blobs", no_argument, NULL, 'B'},
461 : {"no-large-objects", no_argument, NULL, 'B'},
462 : {"clean", no_argument, NULL, 'c'},
463 : {"create", no_argument, NULL, 'C'},
464 : {"dbname", required_argument, NULL, 'd'},
465 : {"extension", required_argument, NULL, 'e'},
466 : {"file", required_argument, NULL, 'f'},
467 : {"format", required_argument, NULL, 'F'},
468 : {"host", required_argument, NULL, 'h'},
469 : {"jobs", 1, NULL, 'j'},
470 : {"no-reconnect", no_argument, NULL, 'R'},
471 : {"no-owner", no_argument, NULL, 'O'},
472 : {"port", required_argument, NULL, 'p'},
473 : {"schema", required_argument, NULL, 'n'},
474 : {"exclude-schema", required_argument, NULL, 'N'},
475 : {"schema-only", no_argument, NULL, 's'},
476 : {"superuser", required_argument, NULL, 'S'},
477 : {"table", required_argument, NULL, 't'},
478 : {"exclude-table", required_argument, NULL, 'T'},
479 : {"no-password", no_argument, NULL, 'w'},
480 : {"password", no_argument, NULL, 'W'},
481 : {"username", required_argument, NULL, 'U'},
482 : {"verbose", no_argument, NULL, 'v'},
483 : {"no-privileges", no_argument, NULL, 'x'},
484 : {"no-acl", no_argument, NULL, 'x'},
485 : {"compress", required_argument, NULL, 'Z'},
486 : {"encoding", required_argument, NULL, 'E'},
487 : {"help", no_argument, NULL, '?'},
488 : {"version", no_argument, NULL, 'V'},
489 :
490 : /*
491 : * the following options don't have an equivalent short option letter
492 : */
493 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
494 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
495 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
496 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
497 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
498 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
499 : {"exclude-table-data", required_argument, NULL, 4},
500 : {"extra-float-digits", required_argument, NULL, 8},
501 : {"if-exists", no_argument, &dopt.if_exists, 1},
502 : {"inserts", no_argument, NULL, 9},
503 : {"lock-wait-timeout", required_argument, NULL, 2},
504 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
505 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
506 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
507 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
508 : {"role", required_argument, NULL, 3},
509 : {"section", required_argument, NULL, 5},
510 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
511 : {"snapshot", required_argument, NULL, 6},
512 : {"statistics", no_argument, NULL, 22},
513 : {"statistics-only", no_argument, NULL, 18},
514 : {"strict-names", no_argument, &strict_names, 1},
515 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
516 : {"no-comments", no_argument, &dopt.no_comments, 1},
517 : {"no-data", no_argument, NULL, 19},
518 : {"no-policies", no_argument, &dopt.no_policies, 1},
519 : {"no-publications", no_argument, &dopt.no_publications, 1},
520 : {"no-schema", no_argument, NULL, 20},
521 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
522 : {"no-statistics", no_argument, NULL, 21},
523 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
524 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
525 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
526 : {"no-sync", no_argument, NULL, 7},
527 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
528 : {"rows-per-insert", required_argument, NULL, 10},
529 : {"include-foreign-data", required_argument, NULL, 11},
530 : {"table-and-children", required_argument, NULL, 12},
531 : {"exclude-table-and-children", required_argument, NULL, 13},
532 : {"exclude-table-data-and-children", required_argument, NULL, 14},
533 : {"sync-method", required_argument, NULL, 15},
534 : {"filter", required_argument, NULL, 16},
535 : {"exclude-extension", required_argument, NULL, 17},
536 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
537 : {"restrict-key", required_argument, NULL, 25},
538 :
539 : {NULL, 0, NULL, 0}
540 : };
541 :
542 389 : pg_logging_init(argv[0]);
543 389 : pg_logging_set_level(PG_LOG_WARNING);
544 389 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
545 :
546 : /*
547 : * Initialize what we need for parallel execution, especially for thread
548 : * support on Windows.
549 : */
550 389 : init_parallel_dump_utils();
551 :
552 389 : progname = get_progname(argv[0]);
553 :
554 389 : if (argc > 1)
555 : {
556 389 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
557 : {
558 1 : help(progname);
559 1 : exit_nicely(0);
560 : }
561 388 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
562 : {
563 84 : puts("pg_dump (PostgreSQL) " PG_VERSION);
564 84 : exit_nicely(0);
565 : }
566 : }
567 :
568 304 : InitDumpOptions(&dopt);
569 :
570 1673 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
571 1673 : long_options, &optindex)) != -1)
572 : {
573 1377 : switch (c)
574 : {
575 9 : case 'a': /* Dump data only */
576 9 : data_only = true;
577 9 : break;
578 :
579 1 : case 'b': /* Dump LOs */
580 1 : dopt.outputLOs = true;
581 1 : break;
582 :
583 2 : case 'B': /* Don't dump LOs */
584 2 : dopt.dontOutputLOs = true;
585 2 : break;
586 :
587 6 : case 'c': /* clean (i.e., drop) schema prior to create */
588 6 : dopt.outputClean = 1;
589 6 : break;
590 :
591 82 : case 'C': /* Create DB */
592 82 : dopt.outputCreateDB = 1;
593 82 : break;
594 :
595 5 : case 'd': /* database name */
596 5 : dopt.cparams.dbname = pg_strdup(optarg);
597 5 : break;
598 :
599 4 : case 'e': /* include extension(s) */
600 4 : simple_string_list_append(&extension_include_patterns, optarg);
601 4 : dopt.include_everything = false;
602 4 : break;
603 :
604 2 : case 'E': /* Dump encoding */
605 2 : dumpencoding = pg_strdup(optarg);
606 2 : break;
607 :
608 336 : case 'f':
609 336 : filename = pg_strdup(optarg);
610 336 : break;
611 :
612 184 : case 'F':
613 184 : format = pg_strdup(optarg);
614 184 : break;
615 :
616 38 : case 'h': /* server host */
617 38 : dopt.cparams.pghost = pg_strdup(optarg);
618 38 : break;
619 :
620 11 : case 'j': /* number of dump jobs */
621 11 : if (!option_parse_int(optarg, "-j/--jobs", 1,
622 : PG_MAX_JOBS,
623 : &numWorkers))
624 1 : exit_nicely(1);
625 10 : break;
626 :
627 17 : case 'n': /* include schema(s) */
628 17 : simple_string_list_append(&schema_include_patterns, optarg);
629 17 : dopt.include_everything = false;
630 17 : break;
631 :
632 1 : case 'N': /* exclude schema(s) */
633 1 : simple_string_list_append(&schema_exclude_patterns, optarg);
634 1 : break;
635 :
636 2 : case 'O': /* Don't reconnect to match owner */
637 2 : dopt.outputNoOwner = 1;
638 2 : break;
639 :
640 77 : case 'p': /* server port */
641 77 : dopt.cparams.pgport = pg_strdup(optarg);
642 77 : break;
643 :
644 2 : case 'R':
645 : /* no-op, still accepted for backwards compatibility */
646 2 : break;
647 :
648 7 : case 's': /* dump schema only */
649 7 : schema_only = true;
650 7 : break;
651 :
652 1 : case 'S': /* Username for superuser in plain text output */
653 1 : dopt.outputSuperuser = pg_strdup(optarg);
654 1 : break;
655 :
656 8 : case 't': /* include table(s) */
657 8 : simple_string_list_append(&table_include_patterns, optarg);
658 8 : dopt.include_everything = false;
659 8 : break;
660 :
661 4 : case 'T': /* exclude table(s) */
662 4 : simple_string_list_append(&table_exclude_patterns, optarg);
663 4 : break;
664 :
665 40 : case 'U':
666 40 : dopt.cparams.username = pg_strdup(optarg);
667 40 : break;
668 :
669 6 : case 'v': /* verbose */
670 6 : g_verbose = true;
671 6 : pg_logging_increase_verbosity();
672 6 : break;
673 :
674 1 : case 'w':
675 1 : dopt.cparams.promptPassword = TRI_NO;
676 1 : break;
677 :
678 0 : case 'W':
679 0 : dopt.cparams.promptPassword = TRI_YES;
680 0 : break;
681 :
682 2 : case 'x': /* skip ACL dump */
683 2 : dopt.aclsSkip = true;
684 2 : break;
685 :
686 13 : case 'Z': /* Compression */
687 13 : parse_compress_options(optarg, &compression_algorithm_str,
688 : &compression_detail);
689 13 : user_compression_defined = true;
690 13 : break;
691 :
692 141 : case 0:
693 : /* This covers the long options. */
694 141 : break;
695 :
696 2 : case 2: /* lock-wait-timeout */
697 2 : dopt.lockWaitTimeout = pg_strdup(optarg);
698 2 : break;
699 :
700 3 : case 3: /* SET ROLE */
701 3 : use_role = pg_strdup(optarg);
702 3 : break;
703 :
704 1 : case 4: /* exclude table(s) data */
705 1 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
706 1 : break;
707 :
708 6 : case 5: /* section */
709 6 : set_dump_section(optarg, &dopt.dumpSections);
710 6 : break;
711 :
712 0 : case 6: /* snapshot */
713 0 : dumpsnapshot = pg_strdup(optarg);
714 0 : break;
715 :
716 151 : case 7: /* no-sync */
717 151 : dosync = false;
718 151 : break;
719 :
720 1 : case 8:
721 1 : have_extra_float_digits = true;
722 1 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
723 : &extra_float_digits))
724 1 : exit_nicely(1);
725 0 : break;
726 :
727 2 : case 9: /* inserts */
728 :
729 : /*
730 : * dump_inserts also stores --rows-per-insert, careful not to
731 : * overwrite that.
732 : */
733 2 : if (dopt.dump_inserts == 0)
734 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
735 2 : break;
736 :
737 2 : case 10: /* rows per insert */
738 2 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
739 : &dopt.dump_inserts))
740 1 : exit_nicely(1);
741 1 : break;
742 :
743 4 : case 11: /* include foreign data */
744 4 : simple_string_list_append(&foreign_servers_include_patterns,
745 : optarg);
746 4 : break;
747 :
748 1 : case 12: /* include table(s) and their children */
749 1 : simple_string_list_append(&table_include_patterns_and_children,
750 : optarg);
751 1 : dopt.include_everything = false;
752 1 : break;
753 :
754 1 : case 13: /* exclude table(s) and their children */
755 1 : simple_string_list_append(&table_exclude_patterns_and_children,
756 : optarg);
757 1 : break;
758 :
759 1 : case 14: /* exclude data of table(s) and children */
760 1 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
761 : optarg);
762 1 : break;
763 :
764 0 : case 15:
765 0 : if (!parse_sync_method(optarg, &sync_method))
766 0 : exit_nicely(1);
767 0 : break;
768 :
769 26 : case 16: /* read object filters from file */
770 26 : read_dump_filters(optarg, &dopt);
771 22 : break;
772 :
773 1 : case 17: /* exclude extension(s) */
774 1 : simple_string_list_append(&extension_exclude_patterns,
775 : optarg);
776 1 : break;
777 :
778 4 : case 18:
779 4 : statistics_only = true;
780 4 : break;
781 :
782 40 : case 19:
783 40 : no_data = true;
784 40 : break;
785 :
786 2 : case 20:
787 2 : no_schema = true;
788 2 : break;
789 :
790 8 : case 21:
791 8 : no_statistics = true;
792 8 : break;
793 :
794 92 : case 22:
795 92 : with_statistics = true;
796 92 : break;
797 :
798 26 : case 25:
799 26 : dopt.restrict_key = pg_strdup(optarg);
800 26 : break;
801 :
802 1 : default:
803 : /* getopt_long already emitted a complaint */
804 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
805 1 : exit_nicely(1);
806 : }
807 : }
808 :
809 : /*
810 : * Non-option argument specifies database name as long as it wasn't
811 : * already specified with -d / --dbname
812 : */
813 296 : if (optind < argc && dopt.cparams.dbname == NULL)
814 260 : dopt.cparams.dbname = argv[optind++];
815 :
816 : /* Complain if any arguments remain */
817 296 : if (optind < argc)
818 : {
819 1 : pg_log_error("too many command-line arguments (first is \"%s\")",
820 : argv[optind]);
821 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
822 1 : exit_nicely(1);
823 : }
824 :
825 : /* --column-inserts implies --inserts */
826 295 : if (dopt.column_inserts && dopt.dump_inserts == 0)
827 1 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
828 :
829 : /* *-only options are incompatible with each other */
830 295 : check_mut_excl_opts(data_only, "-a/--data-only",
831 : schema_only, "-s/--schema-only",
832 : statistics_only, "--statistics-only");
833 :
834 : /* --no-* and *-only for same thing are incompatible */
835 292 : check_mut_excl_opts(data_only, "-a/--data-only",
836 : no_data, "--no-data");
837 292 : check_mut_excl_opts(schema_only, "-s/--schema-only",
838 : no_schema, "--no-schema");
839 292 : check_mut_excl_opts(statistics_only, "--statistics-only",
840 : no_statistics, "--no-statistics");
841 :
842 : /* --statistics and --no-statistics are incompatible */
843 291 : check_mut_excl_opts(with_statistics, "--statistics",
844 : no_statistics, "--no-statistics");
845 :
846 : /* --statistics is incompatible with *-only (except --statistics-only) */
847 291 : check_mut_excl_opts(with_statistics, "--statistics",
848 : data_only, "-a/--data-only",
849 : schema_only, "-s/--schema-only");
850 :
851 : /* --include-foreign-data is incompatible with --schema-only */
852 290 : check_mut_excl_opts(foreign_servers_include_patterns.head, "--include-foreign-data",
853 : schema_only, "-s/--schema-only");
854 :
855 289 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
856 1 : pg_fatal("option %s is not supported with parallel backup",
857 : "--include-foreign-data");
858 :
859 : /* --clean is incompatible with --data-only */
860 288 : check_mut_excl_opts(dopt.outputClean, "-c/--clean",
861 : data_only, "-a/--data-only");
862 :
863 287 : if (dopt.if_exists && !dopt.outputClean)
864 1 : pg_fatal("option %s requires option %s",
865 : "--if-exists", "-c/--clean");
866 :
867 : /*
868 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
869 : * "--schema-only --no-schema", will have already caused an error in one
870 : * of the checks above.
871 : */
872 286 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
873 572 : data_only) && !no_data;
874 286 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
875 572 : schema_only) && !no_schema;
876 286 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
877 572 : (statistics_only || with_statistics)) && !no_statistics;
878 :
879 :
880 : /*
881 : * --inserts are already implied above if --column-inserts or
882 : * --rows-per-insert were specified.
883 : */
884 286 : if (dopt.do_nothing && dopt.dump_inserts == 0)
885 1 : pg_fatal("option %s requires option %s, %s, or %s",
886 : "--on-conflict-do-nothing",
887 : "--inserts", "--rows-per-insert", "--column-inserts");
888 :
889 : /* Identify archive format to emit */
890 285 : archiveFormat = parseArchiveFormat(format, &archiveMode);
891 :
892 : /* archiveFormat specific setup */
893 284 : if (archiveFormat == archNull)
894 : {
895 152 : plainText = 1;
896 :
897 : /*
898 : * If you don't provide a restrict key, one will be appointed for you.
899 : */
900 152 : if (!dopt.restrict_key)
901 126 : dopt.restrict_key = generate_restrict_key();
902 152 : if (!dopt.restrict_key)
903 0 : pg_fatal("could not generate restrict key");
904 152 : if (!valid_restrict_key(dopt.restrict_key))
905 0 : pg_fatal("invalid restrict key");
906 : }
907 132 : else if (dopt.restrict_key)
908 0 : pg_fatal("option %s can only be used with %s",
909 : "--restrict-key", "--format=plain");
910 :
911 : /*
912 : * Custom and directory formats are compressed by default with gzip when
913 : * available, not the others. If gzip is not available, no compression is
914 : * done by default.
915 : */
916 284 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
917 121 : !user_compression_defined)
918 : {
919 : #ifdef HAVE_LIBZ
920 115 : compression_algorithm_str = "gzip";
921 : #else
922 : compression_algorithm_str = "none";
923 : #endif
924 : }
925 :
926 : /*
927 : * Compression options
928 : */
929 284 : if (!parse_compress_algorithm(compression_algorithm_str,
930 : &compression_algorithm))
931 1 : pg_fatal("unrecognized compression algorithm: \"%s\"",
932 : compression_algorithm_str);
933 :
934 283 : parse_compress_specification(compression_algorithm, compression_detail,
935 : &compression_spec);
936 283 : error_detail = validate_compress_specification(&compression_spec);
937 283 : if (error_detail != NULL)
938 3 : pg_fatal("invalid compression specification: %s",
939 : error_detail);
940 :
941 280 : error_detail = supports_compression(compression_spec);
942 280 : if (error_detail != NULL)
943 0 : pg_fatal("%s", error_detail);
944 :
945 : /*
946 : * Disable support for zstd workers for now - these are based on
947 : * threading, and it's unclear how it interacts with parallel dumps on
948 : * platforms where that relies on threads too (e.g. Windows).
949 : */
950 280 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
951 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
952 : "workers");
953 :
954 : /*
955 : * If emitting an archive format, we always want to emit a DATABASE item,
956 : * in case --create is specified at pg_restore time.
957 : */
958 280 : if (!plainText)
959 132 : dopt.outputCreateDB = 1;
960 :
961 : /* Parallel backup only in the directory archive format so far */
962 280 : if (archiveFormat != archDirectory && numWorkers > 1)
963 1 : pg_fatal("parallel backup only supported by the directory format");
964 :
965 : /* Open the output file */
966 279 : fout = CreateArchive(filename, archiveFormat, compression_spec,
967 : dosync, archiveMode, setupDumpWorker, sync_method);
968 :
969 : /* Make dump options accessible right away */
970 278 : SetArchiveOptions(fout, &dopt, NULL);
971 :
972 : /* Register the cleanup hook */
973 278 : on_exit_close_archive(fout);
974 :
975 : /* Let the archiver know how noisy to be */
976 278 : fout->verbose = g_verbose;
977 :
978 :
979 : /*
980 : * We allow the server to be back to 9.2, and up to any minor release of
981 : * our own major version. (See also version check in pg_dumpall.c.)
982 : */
983 278 : fout->minRemoteVersion = 90200;
984 278 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
985 :
986 278 : fout->numWorkers = numWorkers;
987 :
988 : /*
989 : * Open the database using the Archiver, so it knows about it. Errors mean
990 : * death.
991 : */
992 278 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
993 276 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
994 :
995 : /*
996 : * On hot standbys, never try to dump unlogged table data, since it will
997 : * just throw an error.
998 : */
999 276 : if (fout->isStandby)
1000 4 : dopt.no_unlogged_table_data = true;
1001 :
1002 : /*
1003 : * Find the last built-in OID, if needed (prior to 8.1)
1004 : *
1005 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1006 : */
1007 276 : g_last_builtin_oid = FirstNormalObjectId - 1;
1008 :
1009 276 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1010 :
1011 : /* Expand schema selection patterns into OID lists */
1012 276 : if (schema_include_patterns.head != NULL)
1013 : {
1014 18 : expand_schema_name_patterns(fout, &schema_include_patterns,
1015 : &schema_include_oids,
1016 : strict_names);
1017 12 : if (schema_include_oids.head == NULL)
1018 1 : pg_fatal("no matching schemas were found");
1019 : }
1020 269 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1021 : &schema_exclude_oids,
1022 : false);
1023 : /* non-matching exclusion patterns aren't an error */
1024 :
1025 : /* Expand table selection patterns into OID lists */
1026 269 : expand_table_name_patterns(fout, &table_include_patterns,
1027 : &table_include_oids,
1028 : strict_names, false);
1029 264 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1030 : &table_include_oids,
1031 : strict_names, true);
1032 264 : if ((table_include_patterns.head != NULL ||
1033 253 : table_include_patterns_and_children.head != NULL) &&
1034 13 : table_include_oids.head == NULL)
1035 2 : pg_fatal("no matching tables were found");
1036 :
1037 262 : expand_table_name_patterns(fout, &table_exclude_patterns,
1038 : &table_exclude_oids,
1039 : false, false);
1040 262 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1041 : &table_exclude_oids,
1042 : false, true);
1043 :
1044 262 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1045 : &tabledata_exclude_oids,
1046 : false, false);
1047 262 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1048 : &tabledata_exclude_oids,
1049 : false, true);
1050 :
1051 262 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1052 : &foreign_servers_include_oids);
1053 :
1054 : /* non-matching exclusion patterns aren't an error */
1055 :
1056 : /* Expand extension selection patterns into OID lists */
1057 261 : if (extension_include_patterns.head != NULL)
1058 : {
1059 5 : expand_extension_name_patterns(fout, &extension_include_patterns,
1060 : &extension_include_oids,
1061 : strict_names);
1062 5 : if (extension_include_oids.head == NULL)
1063 1 : pg_fatal("no matching extensions were found");
1064 : }
1065 260 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1066 : &extension_exclude_oids,
1067 : false);
1068 : /* non-matching exclusion patterns aren't an error */
1069 :
1070 : /*
1071 : * Dumping LOs is the default for dumps where an inclusion switch is not
1072 : * used (an "include everything" dump). -B can be used to exclude LOs
1073 : * from those dumps. -b can be used to include LOs even when an inclusion
1074 : * switch is used.
1075 : *
1076 : * -s means "schema only" and LOs are data, not schema, so we never
1077 : * include LOs when -s is used.
1078 : */
1079 260 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1080 191 : dopt.outputLOs = true;
1081 :
1082 : /*
1083 : * Collect role names so we can map object owner OIDs to names.
1084 : */
1085 260 : collectRoleNames(fout);
1086 :
1087 : /*
1088 : * Now scan the database and create DumpableObject structs for all the
1089 : * objects we intend to dump.
1090 : */
1091 260 : tblinfo = getSchemaData(fout, &numTables);
1092 :
1093 259 : if (dopt.dumpData)
1094 : {
1095 215 : getTableData(&dopt, tblinfo, numTables, 0);
1096 215 : buildMatViewRefreshDependencies(fout);
1097 215 : if (!dopt.dumpSchema)
1098 7 : getTableDataFKConstraints();
1099 : }
1100 :
1101 259 : if (!dopt.dumpData && dopt.sequence_data)
1102 36 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1103 :
1104 : /*
1105 : * For binary upgrade mode, dump the pg_shdepend rows for large objects
1106 : * and maybe even pg_largeobject_metadata (see comment below for details).
1107 : * This is faster to restore than the equivalent set of large object
1108 : * commands.
1109 : */
1110 259 : if (dopt.binary_upgrade)
1111 : {
1112 : TableInfo *shdepend;
1113 :
1114 40 : shdepend = findTableByOid(SharedDependRelationId);
1115 40 : makeTableDataInfo(&dopt, shdepend);
1116 :
1117 : /*
1118 : * Only dump large object shdepend rows for this database.
1119 : */
1120 40 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1121 : "AND dbid = (SELECT oid FROM pg_database "
1122 : " WHERE datname = current_database())";
1123 :
1124 : /*
1125 : * For binary upgrades from v16 and newer versions, we can copy
1126 : * pg_largeobject_metadata's files from the old cluster, so we don't
1127 : * need to dump its contents. pg_upgrade can't copy/link the files
1128 : * from older versions because aclitem (needed by
1129 : * pg_largeobject_metadata.lomacl) changed its storage format in v16.
1130 : */
1131 40 : if (fout->remoteVersion < 160000)
1132 : {
1133 : TableInfo *lo_metadata;
1134 :
1135 0 : lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1136 0 : makeTableDataInfo(&dopt, lo_metadata);
1137 : }
1138 : }
1139 :
1140 : /*
1141 : * In binary-upgrade mode, we do not have to worry about the actual LO
1142 : * data or the associated metadata that resides in the pg_largeobject and
1143 : * pg_largeobject_metadata tables, respectively.
1144 : *
1145 : * However, we do need to collect LO information as there may be comments
1146 : * or other information on LOs that we do need to dump out.
1147 : */
1148 259 : if (dopt.outputLOs || dopt.binary_upgrade)
1149 231 : getLOs(fout);
1150 :
1151 : /*
1152 : * Collect dependency data to assist in ordering the objects.
1153 : */
1154 259 : getDependencies(fout);
1155 :
1156 : /*
1157 : * Collect ACLs, comments, and security labels, if wanted.
1158 : */
1159 259 : if (!dopt.aclsSkip)
1160 257 : getAdditionalACLs(fout);
1161 259 : if (!dopt.no_comments)
1162 259 : collectComments(fout);
1163 259 : if (!dopt.no_security_labels)
1164 259 : collectSecLabels(fout);
1165 :
1166 : /* For binary upgrade mode, collect required pg_class information. */
1167 259 : if (dopt.binary_upgrade)
1168 40 : collectBinaryUpgradeClassOids(fout);
1169 :
1170 : /* Collect sequence information. */
1171 259 : collectSequences(fout);
1172 :
1173 : /* Lastly, create dummy objects to represent the section boundaries */
1174 259 : boundaryObjs = createBoundaryObjects();
1175 :
1176 : /* Get pointers to all the known DumpableObjects */
1177 259 : getDumpableObjects(&dobjs, &numObjs);
1178 :
1179 : /*
1180 : * Add dummy dependencies to enforce the dump section ordering.
1181 : */
1182 259 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1183 :
1184 : /*
1185 : * Sort the objects into a safe dump order (no forward references).
1186 : *
1187 : * We rely on dependency information to help us determine a safe order, so
1188 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1189 : * ensure that logically identical schemas will dump identically.
1190 : */
1191 259 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1192 :
1193 259 : sortDumpableObjects(dobjs, numObjs,
1194 259 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1195 :
1196 : /*
1197 : * Create archive TOC entries for all the objects to be dumped, in a safe
1198 : * order.
1199 : */
1200 :
1201 : /*
1202 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1203 : */
1204 259 : dumpEncoding(fout);
1205 259 : dumpStdStrings(fout);
1206 259 : dumpSearchPath(fout);
1207 :
1208 : /* The database items are always next, unless we don't want them at all */
1209 259 : if (dopt.outputCreateDB)
1210 160 : dumpDatabase(fout);
1211 :
1212 : /* Now the rearrangeable objects. */
1213 980461 : for (i = 0; i < numObjs; i++)
1214 980202 : dumpDumpableObject(fout, dobjs[i]);
1215 :
1216 : /*
1217 : * Set up options info to ensure we dump what we want.
1218 : */
1219 259 : ropt = NewRestoreOptions();
1220 259 : ropt->filename = filename;
1221 :
1222 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1223 259 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1224 259 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1225 259 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1226 259 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1227 259 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1228 259 : ropt->dropSchema = dopt.outputClean;
1229 259 : ropt->dumpData = dopt.dumpData;
1230 259 : ropt->dumpSchema = dopt.dumpSchema;
1231 259 : ropt->dumpStatistics = dopt.dumpStatistics;
1232 259 : ropt->if_exists = dopt.if_exists;
1233 259 : ropt->column_inserts = dopt.column_inserts;
1234 259 : ropt->dumpSections = dopt.dumpSections;
1235 259 : ropt->aclsSkip = dopt.aclsSkip;
1236 259 : ropt->superuser = dopt.outputSuperuser;
1237 259 : ropt->createDB = dopt.outputCreateDB;
1238 259 : ropt->noOwner = dopt.outputNoOwner;
1239 259 : ropt->noTableAm = dopt.outputNoTableAm;
1240 259 : ropt->noTablespace = dopt.outputNoTablespaces;
1241 259 : ropt->disable_triggers = dopt.disable_triggers;
1242 259 : ropt->use_setsessauth = dopt.use_setsessauth;
1243 259 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1244 259 : ropt->dump_inserts = dopt.dump_inserts;
1245 259 : ropt->no_comments = dopt.no_comments;
1246 259 : ropt->no_policies = dopt.no_policies;
1247 259 : ropt->no_publications = dopt.no_publications;
1248 259 : ropt->no_security_labels = dopt.no_security_labels;
1249 259 : ropt->no_subscriptions = dopt.no_subscriptions;
1250 259 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1251 259 : ropt->include_everything = dopt.include_everything;
1252 259 : ropt->enable_row_security = dopt.enable_row_security;
1253 259 : ropt->sequence_data = dopt.sequence_data;
1254 259 : ropt->binary_upgrade = dopt.binary_upgrade;
1255 259 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1256 :
1257 259 : ropt->compression_spec = compression_spec;
1258 :
1259 259 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1260 :
1261 259 : SetArchiveOptions(fout, &dopt, ropt);
1262 :
1263 : /* Mark which entries should be output */
1264 259 : ProcessArchiveRestoreOptions(fout);
1265 :
1266 : /*
1267 : * The archive's TOC entries are now marked as to which ones will actually
1268 : * be output, so we can set up their dependency lists properly. This isn't
1269 : * necessary for plain-text output, though.
1270 : */
1271 259 : if (!plainText)
1272 131 : BuildArchiveDependencies(fout);
1273 :
1274 : /*
1275 : * And finally we can do the actual output.
1276 : *
1277 : * Note: for non-plain-text output formats, the output file is written
1278 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1279 : * right now.
1280 : */
1281 259 : if (plainText)
1282 128 : RestoreArchive(fout, false);
1283 :
1284 258 : CloseArchive(fout);
1285 :
1286 258 : exit_nicely(0);
1287 : }
1288 :
1289 :
1290 : static void
1291 1 : help(const char *progname)
1292 : {
1293 1 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1294 1 : printf(_("Usage:\n"));
1295 1 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1296 :
1297 1 : printf(_("\nGeneral options:\n"));
1298 1 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1299 1 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1300 : " plain text (default))\n"));
1301 1 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1302 1 : printf(_(" -v, --verbose verbose mode\n"));
1303 1 : printf(_(" -V, --version output version information, then exit\n"));
1304 1 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1305 : " compress as specified\n"));
1306 1 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1307 1 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1308 1 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1309 1 : printf(_(" -?, --help show this help, then exit\n"));
1310 :
1311 1 : printf(_("\nOptions controlling the output content:\n"));
1312 1 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1313 1 : printf(_(" -b, --large-objects include large objects in dump\n"));
1314 1 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1315 1 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1316 1 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1317 1 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1318 1 : printf(_(" -C, --create include commands to create database in dump\n"));
1319 1 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1320 1 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1321 1 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1322 1 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1323 1 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1324 : " plain-text format\n"));
1325 1 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1326 1 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1327 1 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1328 1 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1329 1 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1330 1 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1331 1 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1332 1 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1333 1 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1334 1 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1335 : " access to)\n"));
1336 1 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1337 1 : printf(_(" --exclude-table-and-children=PATTERN\n"
1338 : " do NOT dump the specified table(s), including\n"
1339 : " child and partition tables\n"));
1340 1 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1341 1 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1342 : " do NOT dump data for the specified table(s),\n"
1343 : " including child and partition tables\n"));
1344 1 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1345 1 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1346 : " based on expressions in FILENAME\n"));
1347 1 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1348 1 : printf(_(" --include-foreign-data=PATTERN\n"
1349 : " include data of foreign tables on foreign\n"
1350 : " servers matching PATTERN\n"));
1351 1 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1352 1 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1353 1 : printf(_(" --no-comments do not dump comment commands\n"));
1354 1 : printf(_(" --no-data do not dump data\n"));
1355 1 : printf(_(" --no-policies do not dump row security policies\n"));
1356 1 : printf(_(" --no-publications do not dump publications\n"));
1357 1 : printf(_(" --no-schema do not dump schema\n"));
1358 1 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1359 1 : printf(_(" --no-statistics do not dump statistics\n"));
1360 1 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1361 1 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1362 1 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1363 1 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1364 1 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1365 1 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1366 1 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1367 1 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1368 1 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1369 1 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1370 1 : printf(_(" --sequence-data include sequence data in dump\n"));
1371 1 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1372 1 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1373 1 : printf(_(" --statistics dump the statistics\n"));
1374 1 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1375 1 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1376 : " match at least one entity each\n"));
1377 1 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1378 : " child and partition tables\n"));
1379 1 : printf(_(" --use-set-session-authorization\n"
1380 : " use SET SESSION AUTHORIZATION commands instead of\n"
1381 : " ALTER OWNER commands to set ownership\n"));
1382 :
1383 1 : printf(_("\nConnection options:\n"));
1384 1 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1385 1 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1386 1 : printf(_(" -p, --port=PORT database server port number\n"));
1387 1 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1388 1 : printf(_(" -w, --no-password never prompt for password\n"));
1389 1 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1390 1 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1391 :
1392 1 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1393 : "variable value is used.\n\n"));
1394 1 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1395 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1396 1 : }
1397 :
1398 : static void
1399 292 : setup_connection(Archive *AH, const char *dumpencoding,
1400 : const char *dumpsnapshot, char *use_role)
1401 : {
1402 292 : DumpOptions *dopt = AH->dopt;
1403 292 : PGconn *conn = GetConnection(AH);
1404 :
1405 292 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1406 :
1407 : /*
1408 : * Set the client encoding if requested.
1409 : */
1410 292 : if (dumpencoding)
1411 : {
1412 18 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1413 0 : pg_fatal("invalid client encoding \"%s\" specified",
1414 : dumpencoding);
1415 : }
1416 :
1417 : /*
1418 : * Force standard_conforming_strings on, just in case we are dumping from
1419 : * an old server that has it disabled. Without this, literals in views,
1420 : * expressions, etc, would be incorrect for modern servers.
1421 : */
1422 292 : ExecuteSqlStatement(AH, "SET standard_conforming_strings = on");
1423 :
1424 : /*
1425 : * And reflect that to AH->std_strings. You might think that we should
1426 : * just delete that variable and the code that checks it, but that would
1427 : * be problematic for pg_restore, which at least for now should still cope
1428 : * with archives containing the other setting (cf. processStdStringsEntry
1429 : * in pg_backup_archiver.c).
1430 : */
1431 292 : AH->std_strings = true;
1432 :
1433 : /*
1434 : * Get the active encoding, so we know how to escape strings.
1435 : */
1436 292 : AH->encoding = PQclientEncoding(conn);
1437 292 : setFmtEncoding(AH->encoding);
1438 :
1439 : /*
1440 : * Set the role if requested. In a parallel dump worker, we'll be passed
1441 : * use_role == NULL, but AH->use_role is already set (if user specified it
1442 : * originally) and we should use that.
1443 : */
1444 292 : if (!use_role && AH->use_role)
1445 2 : use_role = AH->use_role;
1446 :
1447 : /* Set the role if requested */
1448 292 : if (use_role)
1449 : {
1450 5 : PQExpBuffer query = createPQExpBuffer();
1451 :
1452 5 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1453 5 : ExecuteSqlStatement(AH, query->data);
1454 5 : destroyPQExpBuffer(query);
1455 :
1456 : /* save it for possible later use by parallel workers */
1457 5 : if (!AH->use_role)
1458 3 : AH->use_role = pg_strdup(use_role);
1459 : }
1460 :
1461 : /* Set the datestyle to ISO to ensure the dump's portability */
1462 292 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1463 :
1464 : /* Likewise, avoid using sql_standard intervalstyle */
1465 292 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1466 :
1467 : /*
1468 : * Use an explicitly specified extra_float_digits if it has been provided.
1469 : * Otherwise, set extra_float_digits so that we can dump float data
1470 : * exactly (given correctly implemented float I/O code, anyway).
1471 : */
1472 292 : if (have_extra_float_digits)
1473 : {
1474 0 : PQExpBuffer q = createPQExpBuffer();
1475 :
1476 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1477 : extra_float_digits);
1478 0 : ExecuteSqlStatement(AH, q->data);
1479 0 : destroyPQExpBuffer(q);
1480 : }
1481 : else
1482 292 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1483 :
1484 : /*
1485 : * Disable synchronized scanning, to prevent unpredictable changes in row
1486 : * ordering across a dump and reload.
1487 : */
1488 292 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1489 :
1490 : /*
1491 : * Disable timeouts if supported.
1492 : */
1493 292 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1494 292 : if (AH->remoteVersion >= 90300)
1495 292 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1496 292 : if (AH->remoteVersion >= 90600)
1497 292 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1498 292 : if (AH->remoteVersion >= 170000)
1499 292 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1500 :
1501 : /*
1502 : * Quote all identifiers, if requested.
1503 : */
1504 292 : if (quote_all_identifiers)
1505 38 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1506 :
1507 : /*
1508 : * Adjust row-security mode, if supported.
1509 : */
1510 292 : if (AH->remoteVersion >= 90500)
1511 : {
1512 292 : if (dopt->enable_row_security)
1513 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1514 : else
1515 292 : ExecuteSqlStatement(AH, "SET row_security = off");
1516 : }
1517 :
1518 : /*
1519 : * For security reasons, we restrict the expansion of non-system views and
1520 : * access to foreign tables during the pg_dump process. This restriction
1521 : * is adjusted when dumping foreign table data.
1522 : */
1523 292 : set_restrict_relation_kind(AH, "view, foreign-table");
1524 :
1525 : /*
1526 : * Initialize prepared-query state to "nothing prepared". We do this here
1527 : * so that a parallel dump worker will have its own state.
1528 : */
1529 292 : AH->is_prepared = pg_malloc0_array(bool, NUM_PREP_QUERIES);
1530 :
1531 : /*
1532 : * Start transaction-snapshot mode transaction to dump consistent data.
1533 : */
1534 292 : ExecuteSqlStatement(AH, "BEGIN");
1535 :
1536 : /*
1537 : * To support the combination of serializable_deferrable with the jobs
1538 : * option we use REPEATABLE READ for the worker connections that are
1539 : * passed a snapshot. As long as the snapshot is acquired in a
1540 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1541 : * REPEATABLE READ transaction provides the appropriate integrity
1542 : * guarantees. This is a kluge, but safe for back-patching.
1543 : */
1544 292 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1545 0 : ExecuteSqlStatement(AH,
1546 : "SET TRANSACTION ISOLATION LEVEL "
1547 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1548 : else
1549 292 : ExecuteSqlStatement(AH,
1550 : "SET TRANSACTION ISOLATION LEVEL "
1551 : "REPEATABLE READ, READ ONLY");
1552 :
1553 : /*
1554 : * If user specified a snapshot to use, select that. In a parallel dump
1555 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1556 : * is already set (if the server can handle it) and we should use that.
1557 : */
1558 292 : if (dumpsnapshot)
1559 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1560 :
1561 292 : if (AH->sync_snapshot_id)
1562 : {
1563 16 : PQExpBuffer query = createPQExpBuffer();
1564 :
1565 16 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1566 16 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1567 16 : ExecuteSqlStatement(AH, query->data);
1568 16 : destroyPQExpBuffer(query);
1569 : }
1570 276 : else if (AH->numWorkers > 1)
1571 : {
1572 8 : if (AH->isStandby && AH->remoteVersion < 100000)
1573 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1574 8 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1575 : }
1576 292 : }
1577 :
1578 : /* Set up connection for a parallel worker process */
1579 : static void
1580 16 : setupDumpWorker(Archive *AH)
1581 : {
1582 : /*
1583 : * We want to re-select all the same values the leader connection is
1584 : * using. We'll have inherited directly-usable values in
1585 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1586 : * inherited encoding value back to a string to pass to setup_connection.
1587 : */
1588 16 : setup_connection(AH,
1589 : pg_encoding_to_char(AH->encoding),
1590 : NULL,
1591 : NULL);
1592 16 : }
1593 :
1594 : static char *
1595 8 : get_synchronized_snapshot(Archive *fout)
1596 : {
1597 8 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1598 : char *result;
1599 : PGresult *res;
1600 :
1601 8 : res = ExecuteSqlQueryForSingleRow(fout, query);
1602 8 : result = pg_strdup(PQgetvalue(res, 0, 0));
1603 8 : PQclear(res);
1604 :
1605 8 : return result;
1606 : }
1607 :
1608 : static ArchiveFormat
1609 285 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1610 : {
1611 : ArchiveFormat archiveFormat;
1612 :
1613 285 : *mode = archModeWrite;
1614 :
1615 285 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1616 : {
1617 : /* This is used by pg_dumpall, and is not documented */
1618 48 : archiveFormat = archNull;
1619 48 : *mode = archModeAppend;
1620 : }
1621 237 : else if (pg_strcasecmp(format, "c") == 0)
1622 0 : archiveFormat = archCustom;
1623 237 : else if (pg_strcasecmp(format, "custom") == 0)
1624 56 : archiveFormat = archCustom;
1625 181 : else if (pg_strcasecmp(format, "d") == 0)
1626 0 : archiveFormat = archDirectory;
1627 181 : else if (pg_strcasecmp(format, "directory") == 0)
1628 65 : archiveFormat = archDirectory;
1629 116 : else if (pg_strcasecmp(format, "p") == 0)
1630 101 : archiveFormat = archNull;
1631 15 : else if (pg_strcasecmp(format, "plain") == 0)
1632 3 : archiveFormat = archNull;
1633 12 : else if (pg_strcasecmp(format, "t") == 0)
1634 0 : archiveFormat = archTar;
1635 12 : else if (pg_strcasecmp(format, "tar") == 0)
1636 11 : archiveFormat = archTar;
1637 : else
1638 1 : pg_fatal("invalid output format \"%s\" specified", format);
1639 284 : return archiveFormat;
1640 : }
1641 :
1642 : /*
1643 : * Find the OIDs of all schemas matching the given list of patterns,
1644 : * and append them to the given OID list.
1645 : */
1646 : static void
1647 287 : expand_schema_name_patterns(Archive *fout,
1648 : SimpleStringList *patterns,
1649 : SimpleOidList *oids,
1650 : bool strict_names)
1651 : {
1652 : PQExpBuffer query;
1653 : PGresult *res;
1654 : SimpleStringListCell *cell;
1655 : int i;
1656 :
1657 287 : if (patterns->head == NULL)
1658 266 : return; /* nothing to do */
1659 :
1660 21 : query = createPQExpBuffer();
1661 :
1662 : /*
1663 : * The loop below runs multiple SELECTs might sometimes result in
1664 : * duplicate entries in the OID list, but we don't care.
1665 : */
1666 :
1667 36 : for (cell = patterns->head; cell; cell = cell->next)
1668 : {
1669 : PQExpBufferData dbbuf;
1670 : int dotcnt;
1671 :
1672 21 : appendPQExpBufferStr(query,
1673 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1674 21 : initPQExpBuffer(&dbbuf);
1675 21 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1676 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1677 : &dotcnt);
1678 21 : if (dotcnt > 1)
1679 2 : pg_fatal("improper qualified name (too many dotted names): %s",
1680 : cell->val);
1681 19 : else if (dotcnt == 1)
1682 3 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1683 16 : termPQExpBuffer(&dbbuf);
1684 :
1685 16 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1686 16 : if (strict_names && PQntuples(res) == 0)
1687 1 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1688 :
1689 29 : for (i = 0; i < PQntuples(res); i++)
1690 : {
1691 14 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1692 : }
1693 :
1694 15 : PQclear(res);
1695 15 : resetPQExpBuffer(query);
1696 : }
1697 :
1698 15 : destroyPQExpBuffer(query);
1699 : }
1700 :
1701 : /*
1702 : * Find the OIDs of all extensions matching the given list of patterns,
1703 : * and append them to the given OID list.
1704 : */
1705 : static void
1706 265 : expand_extension_name_patterns(Archive *fout,
1707 : SimpleStringList *patterns,
1708 : SimpleOidList *oids,
1709 : bool strict_names)
1710 : {
1711 : PQExpBuffer query;
1712 : PGresult *res;
1713 : SimpleStringListCell *cell;
1714 : int i;
1715 :
1716 265 : if (patterns->head == NULL)
1717 258 : return; /* nothing to do */
1718 :
1719 7 : query = createPQExpBuffer();
1720 :
1721 : /*
1722 : * The loop below runs multiple SELECTs might sometimes result in
1723 : * duplicate entries in the OID list, but we don't care.
1724 : */
1725 14 : for (cell = patterns->head; cell; cell = cell->next)
1726 : {
1727 : int dotcnt;
1728 :
1729 7 : appendPQExpBufferStr(query,
1730 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1731 7 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1732 : false, NULL, "e.extname", NULL, NULL, NULL,
1733 : &dotcnt);
1734 7 : if (dotcnt > 0)
1735 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1736 : cell->val);
1737 :
1738 7 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1739 7 : if (strict_names && PQntuples(res) == 0)
1740 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1741 :
1742 13 : for (i = 0; i < PQntuples(res); i++)
1743 : {
1744 6 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1745 : }
1746 :
1747 7 : PQclear(res);
1748 7 : resetPQExpBuffer(query);
1749 : }
1750 :
1751 7 : destroyPQExpBuffer(query);
1752 : }
1753 :
1754 : /*
1755 : * Find the OIDs of all foreign servers matching the given list of patterns,
1756 : * and append them to the given OID list.
1757 : */
1758 : static void
1759 262 : expand_foreign_server_name_patterns(Archive *fout,
1760 : SimpleStringList *patterns,
1761 : SimpleOidList *oids)
1762 : {
1763 : PQExpBuffer query;
1764 : PGresult *res;
1765 : SimpleStringListCell *cell;
1766 : int i;
1767 :
1768 262 : if (patterns->head == NULL)
1769 259 : return; /* nothing to do */
1770 :
1771 3 : query = createPQExpBuffer();
1772 :
1773 : /*
1774 : * The loop below runs multiple SELECTs might sometimes result in
1775 : * duplicate entries in the OID list, but we don't care.
1776 : */
1777 :
1778 5 : for (cell = patterns->head; cell; cell = cell->next)
1779 : {
1780 : int dotcnt;
1781 :
1782 3 : appendPQExpBufferStr(query,
1783 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1784 3 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1785 : false, NULL, "s.srvname", NULL, NULL, NULL,
1786 : &dotcnt);
1787 3 : if (dotcnt > 0)
1788 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1789 : cell->val);
1790 :
1791 3 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1792 3 : if (PQntuples(res) == 0)
1793 1 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1794 :
1795 4 : for (i = 0; i < PQntuples(res); i++)
1796 2 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1797 :
1798 2 : PQclear(res);
1799 2 : resetPQExpBuffer(query);
1800 : }
1801 :
1802 2 : destroyPQExpBuffer(query);
1803 : }
1804 :
1805 : /*
1806 : * Find the OIDs of all tables matching the given list of patterns,
1807 : * and append them to the given OID list. See also expand_dbname_patterns()
1808 : * in pg_dumpall.c
1809 : */
1810 : static void
1811 1581 : expand_table_name_patterns(Archive *fout,
1812 : SimpleStringList *patterns, SimpleOidList *oids,
1813 : bool strict_names, bool with_child_tables)
1814 : {
1815 : PQExpBuffer query;
1816 : PGresult *res;
1817 : SimpleStringListCell *cell;
1818 : int i;
1819 :
1820 1581 : if (patterns->head == NULL)
1821 1552 : return; /* nothing to do */
1822 :
1823 29 : query = createPQExpBuffer();
1824 :
1825 : /*
1826 : * this might sometimes result in duplicate entries in the OID list, but
1827 : * we don't care.
1828 : */
1829 :
1830 59 : for (cell = patterns->head; cell; cell = cell->next)
1831 : {
1832 : PQExpBufferData dbbuf;
1833 : int dotcnt;
1834 :
1835 : /*
1836 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1837 : * would be unnecessary given a pg_table_is_visible() variant taking a
1838 : * search_path argument.
1839 : *
1840 : * For with_child_tables, we start with the basic query's results and
1841 : * recursively search the inheritance tree to add child tables.
1842 : */
1843 35 : if (with_child_tables)
1844 : {
1845 6 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1846 : }
1847 :
1848 35 : appendPQExpBuffer(query,
1849 : "SELECT c.oid"
1850 : "\nFROM pg_catalog.pg_class c"
1851 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1852 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1853 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1854 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
1855 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1856 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1857 : RELKIND_PARTITIONED_TABLE, RELKIND_PROPGRAPH);
1858 35 : initPQExpBuffer(&dbbuf);
1859 35 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1860 : false, "n.nspname", "c.relname", NULL,
1861 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1862 : &dotcnt);
1863 35 : if (dotcnt > 2)
1864 1 : pg_fatal("improper relation name (too many dotted names): %s",
1865 : cell->val);
1866 34 : else if (dotcnt == 2)
1867 2 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1868 32 : termPQExpBuffer(&dbbuf);
1869 :
1870 32 : if (with_child_tables)
1871 : {
1872 6 : appendPQExpBufferStr(query, "UNION"
1873 : "\nSELECT i.inhrelid"
1874 : "\nFROM partition_tree p"
1875 : "\n JOIN pg_catalog.pg_inherits i"
1876 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1877 : "\n)"
1878 : "\nSELECT relid FROM partition_tree");
1879 : }
1880 :
1881 32 : ExecuteSqlStatement(fout, "RESET search_path");
1882 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1883 32 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1884 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1885 32 : if (strict_names && PQntuples(res) == 0)
1886 2 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1887 :
1888 74 : for (i = 0; i < PQntuples(res); i++)
1889 : {
1890 44 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1891 : }
1892 :
1893 30 : PQclear(res);
1894 30 : resetPQExpBuffer(query);
1895 : }
1896 :
1897 24 : destroyPQExpBuffer(query);
1898 : }
1899 :
1900 : /*
1901 : * Verifies that the connected database name matches the given database name,
1902 : * and if not, dies with an error about the given pattern.
1903 : *
1904 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1905 : */
1906 : static void
1907 5 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1908 : {
1909 : const char *db;
1910 :
1911 5 : db = PQdb(conn);
1912 5 : if (db == NULL)
1913 0 : pg_fatal("You are currently not connected to a database.");
1914 :
1915 5 : if (strcmp(db, dbname) != 0)
1916 5 : pg_fatal("cross-database references are not implemented: %s",
1917 : pattern);
1918 0 : }
1919 :
1920 : /*
1921 : * checkExtensionMembership
1922 : * Determine whether object is an extension member, and if so,
1923 : * record an appropriate dependency and set the object's dump flag.
1924 : *
1925 : * It's important to call this for each object that could be an extension
1926 : * member. Generally, we integrate this with determining the object's
1927 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1928 : *
1929 : * Returns true if object is an extension member, else false.
1930 : */
1931 : static bool
1932 840268 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1933 : {
1934 840268 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1935 :
1936 840268 : if (ext == NULL)
1937 839383 : return false;
1938 :
1939 885 : dobj->ext_member = true;
1940 :
1941 : /* Record dependency so that getDependencies needn't deal with that */
1942 885 : addObjectDependency(dobj, ext->dobj.dumpId);
1943 :
1944 : /*
1945 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1946 : * dumped. (Any initial ACLs will be removed later, using data from
1947 : * pg_init_privs, so that we'll dump only the delta from the extension's
1948 : * initial setup.)
1949 : *
1950 : * Prior to 9.6, we do not include any extension member components.
1951 : *
1952 : * In binary upgrades, we still dump all components of the members
1953 : * individually, since the idea is to exactly reproduce the database
1954 : * contents rather than replace the extension contents with something
1955 : * different.
1956 : *
1957 : * Note: it might be interesting someday to implement storage and delta
1958 : * dumping of extension members' RLS policies and/or security labels.
1959 : * However there is a pitfall for RLS policies: trying to dump them
1960 : * requires getting a lock on their tables, and the calling user might not
1961 : * have privileges for that. We need no lock to examine a table's ACLs,
1962 : * so the current feature doesn't have a problem of that sort.
1963 : */
1964 885 : if (fout->dopt->binary_upgrade)
1965 186 : dobj->dump = ext->dobj.dump;
1966 : else
1967 : {
1968 699 : if (fout->remoteVersion < 90600)
1969 0 : dobj->dump = DUMP_COMPONENT_NONE;
1970 : else
1971 699 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1972 : }
1973 :
1974 885 : return true;
1975 : }
1976 :
1977 : /*
1978 : * selectDumpableNamespace: policy-setting subroutine
1979 : * Mark a namespace as to be dumped or not
1980 : */
1981 : static void
1982 1767 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1983 : {
1984 : /*
1985 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1986 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1987 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1988 : */
1989 1767 : nsinfo->create = true;
1990 :
1991 : /*
1992 : * If specific tables are being dumped, do not dump any complete
1993 : * namespaces. If specific namespaces are being dumped, dump just those
1994 : * namespaces. Otherwise, dump all non-system namespaces.
1995 : */
1996 1767 : if (table_include_oids.head != NULL)
1997 50 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1998 1717 : else if (schema_include_oids.head != NULL)
1999 195 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2000 195 : simple_oid_list_member(&schema_include_oids,
2001 : nsinfo->dobj.catId.oid) ?
2002 195 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2003 1522 : else if (fout->remoteVersion >= 90600 &&
2004 1522 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2005 : {
2006 : /*
2007 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2008 : * they are interesting (and not the original ACLs which were set at
2009 : * initdb time, see pg_init_privs).
2010 : */
2011 238 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2012 : }
2013 1284 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2014 678 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2015 : {
2016 : /* Other system schemas don't get dumped */
2017 844 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2018 : }
2019 440 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2020 : {
2021 : /*
2022 : * The public schema is a strange beast that sits in a sort of
2023 : * no-mans-land between being a system object and a user object.
2024 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2025 : * a comment and an indication of ownership. If the owner is the
2026 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2027 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2028 : */
2029 234 : nsinfo->create = false;
2030 234 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2031 234 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2032 192 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2033 234 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2034 :
2035 : /*
2036 : * Also, make like it has a comment even if it doesn't; this is so
2037 : * that we'll emit a command to drop the comment, if appropriate.
2038 : * (Without this, we'd not call dumpCommentExtended for it.)
2039 : */
2040 234 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2041 : }
2042 : else
2043 206 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2044 :
2045 : /*
2046 : * In any case, a namespace can be excluded by an exclusion switch
2047 : */
2048 2456 : if (nsinfo->dobj.dump_contains &&
2049 689 : simple_oid_list_member(&schema_exclude_oids,
2050 : nsinfo->dobj.catId.oid))
2051 3 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2052 :
2053 : /*
2054 : * If the schema belongs to an extension, allow extension membership to
2055 : * override the dump decision for the schema itself. However, this does
2056 : * not change dump_contains, so this won't change what we do with objects
2057 : * within the schema. (If they belong to the extension, they'll get
2058 : * suppressed by it, otherwise not.)
2059 : */
2060 1767 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2061 1767 : }
2062 :
2063 : /*
2064 : * selectDumpableTable: policy-setting subroutine
2065 : * Mark a table as to be dumped or not
2066 : */
2067 : static void
2068 70009 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2069 : {
2070 70009 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2071 225 : return; /* extension membership overrides all else */
2072 :
2073 : /*
2074 : * If specific tables are being dumped, dump just those tables; else, dump
2075 : * according to the parent namespace's dump flag.
2076 : */
2077 69784 : if (table_include_oids.head != NULL)
2078 5580 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2079 : tbinfo->dobj.catId.oid) ?
2080 2790 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2081 : else
2082 66994 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2083 :
2084 : /*
2085 : * In any case, a table can be excluded by an exclusion switch
2086 : */
2087 113837 : if (tbinfo->dobj.dump &&
2088 44053 : simple_oid_list_member(&table_exclude_oids,
2089 : tbinfo->dobj.catId.oid))
2090 12 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2091 : }
2092 :
2093 : /*
2094 : * selectDumpableType: policy-setting subroutine
2095 : * Mark a type as to be dumped or not
2096 : *
2097 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2098 : * special type code to facilitate sorting into the desired order. (We don't
2099 : * want to consider those to be ordinary types because that would bring tables
2100 : * up into the datatype part of the dump order.) We still set the object's
2101 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2102 : * need it so that casts involving such types will be dumped correctly -- see
2103 : * dumpCast. This means the flag should be set the same as for the underlying
2104 : * object (the table or base type).
2105 : */
2106 : static void
2107 191483 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2108 : {
2109 : /* skip complex types, except for standalone composite types */
2110 191483 : if (OidIsValid(tyinfo->typrelid) &&
2111 69254 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2112 : {
2113 69072 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2114 :
2115 69072 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2116 69072 : if (tytable != NULL)
2117 69072 : tyinfo->dobj.dump = tytable->dobj.dump;
2118 : else
2119 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2120 69072 : return;
2121 : }
2122 :
2123 : /* skip auto-generated array and multirange types */
2124 122411 : if (tyinfo->isArray || tyinfo->isMultirange)
2125 : {
2126 93646 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2127 :
2128 : /*
2129 : * Fall through to set the dump flag; we assume that the subsequent
2130 : * rules will do the same thing as they would for the array's base
2131 : * type or multirange's range type. (We cannot reliably look up the
2132 : * base type here, since getTypes may not have processed it yet.)
2133 : */
2134 : }
2135 :
2136 122411 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2137 150 : return; /* extension membership overrides all else */
2138 :
2139 : /* Dump based on if the contents of the namespace are being dumped */
2140 122261 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2141 : }
2142 :
2143 : /*
2144 : * selectDumpableDefaultACL: policy-setting subroutine
2145 : * Mark a default ACL as to be dumped or not
2146 : *
2147 : * For per-schema default ACLs, dump if the schema is to be dumped.
2148 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2149 : * and aclsSkip are checked separately.
2150 : */
2151 : static void
2152 194 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2153 : {
2154 : /* Default ACLs can't be extension members */
2155 :
2156 194 : if (dinfo->dobj.namespace)
2157 : /* default ACLs are considered part of the namespace */
2158 90 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2159 : else
2160 104 : dinfo->dobj.dump = dopt->include_everything ?
2161 104 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2162 194 : }
2163 :
2164 : /*
2165 : * selectDumpableCast: policy-setting subroutine
2166 : * Mark a cast as to be dumped or not
2167 : *
2168 : * Casts do not belong to any particular namespace (since they haven't got
2169 : * names), nor do they have identifiable owners. To distinguish user-defined
2170 : * casts from built-in ones, we must resort to checking whether the cast's
2171 : * OID is in the range reserved for initdb.
2172 : */
2173 : static void
2174 63024 : selectDumpableCast(CastInfo *cast, Archive *fout)
2175 : {
2176 63024 : if (checkExtensionMembership(&cast->dobj, fout))
2177 0 : return; /* extension membership overrides all else */
2178 :
2179 : /*
2180 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2181 : * support ACLs currently.
2182 : */
2183 63024 : if (cast->dobj.catId.oid <= g_last_builtin_oid)
2184 62937 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2185 : else
2186 87 : cast->dobj.dump = fout->dopt->include_everything ?
2187 87 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2188 : }
2189 :
2190 : /*
2191 : * selectDumpableProcLang: policy-setting subroutine
2192 : * Mark a procedural language as to be dumped or not
2193 : *
2194 : * Procedural languages do not belong to any particular namespace. To
2195 : * identify built-in languages, we must resort to checking whether the
2196 : * language's OID is in the range reserved for initdb.
2197 : */
2198 : static void
2199 304 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2200 : {
2201 304 : if (checkExtensionMembership(&plang->dobj, fout))
2202 259 : return; /* extension membership overrides all else */
2203 :
2204 : /*
2205 : * Only include procedural languages when we are dumping everything.
2206 : *
2207 : * For from-initdb procedural languages, only include ACLs, as we do for
2208 : * the pg_catalog namespace. We need this because procedural languages do
2209 : * not live in any namespace.
2210 : */
2211 45 : if (!fout->dopt->include_everything)
2212 8 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2213 : else
2214 : {
2215 37 : if (plang->dobj.catId.oid <= g_last_builtin_oid)
2216 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2217 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2218 : else
2219 37 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2220 : }
2221 : }
2222 :
2223 : /*
2224 : * selectDumpableAccessMethod: policy-setting subroutine
2225 : * Mark an access method as to be dumped or not
2226 : *
2227 : * Access methods do not belong to any particular namespace. To identify
2228 : * built-in access methods, we must resort to checking whether the
2229 : * method's OID is in the range reserved for initdb.
2230 : */
2231 : static void
2232 1935 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2233 : {
2234 : /* see getAccessMethods() comment about v9.6. */
2235 1935 : if (fout->remoteVersion < 90600)
2236 : {
2237 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2238 0 : return;
2239 : }
2240 :
2241 1935 : if (checkExtensionMembership(&method->dobj, fout))
2242 25 : return; /* extension membership overrides all else */
2243 :
2244 : /*
2245 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2246 : * they do not support ACLs currently.
2247 : */
2248 1910 : if (method->dobj.catId.oid <= g_last_builtin_oid)
2249 1813 : method->dobj.dump = DUMP_COMPONENT_NONE;
2250 : else
2251 97 : method->dobj.dump = fout->dopt->include_everything ?
2252 97 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2253 : }
2254 :
2255 : /*
2256 : * selectDumpableExtension: policy-setting subroutine
2257 : * Mark an extension as to be dumped or not
2258 : *
2259 : * Built-in extensions should be skipped except for checking ACLs, since we
2260 : * assume those will already be installed in the target database. We identify
2261 : * such extensions by their having OIDs in the range reserved for initdb.
2262 : * We dump all user-added extensions by default. No extensions are dumped
2263 : * if include_everything is false (i.e., a --schema or --table switch was
2264 : * given), except if --extension specifies a list of extensions to dump.
2265 : */
2266 : static void
2267 291 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2268 : {
2269 : /*
2270 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2271 : * change permissions on their member objects, if they wish to, and have
2272 : * those changes preserved.
2273 : */
2274 291 : if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2275 260 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2276 : else
2277 : {
2278 : /* check if there is a list of extensions to dump */
2279 31 : if (extension_include_oids.head != NULL)
2280 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2281 4 : simple_oid_list_member(&extension_include_oids,
2282 : extinfo->dobj.catId.oid) ?
2283 4 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2284 : else
2285 27 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2286 27 : dopt->include_everything ?
2287 27 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2288 :
2289 : /* check that the extension is not explicitly excluded */
2290 58 : if (extinfo->dobj.dump &&
2291 27 : simple_oid_list_member(&extension_exclude_oids,
2292 : extinfo->dobj.catId.oid))
2293 2 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2294 : }
2295 291 : }
2296 :
2297 : /*
2298 : * selectDumpablePublicationObject: policy-setting subroutine
2299 : * Mark a publication object as to be dumped or not
2300 : *
2301 : * A publication can have schemas and tables which have schemas, but those are
2302 : * ignored in decision making, because publications are only dumped when we are
2303 : * dumping everything.
2304 : */
2305 : static void
2306 475 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2307 : {
2308 475 : if (checkExtensionMembership(dobj, fout))
2309 0 : return; /* extension membership overrides all else */
2310 :
2311 475 : dobj->dump = fout->dopt->include_everything ?
2312 475 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2313 : }
2314 :
2315 : /*
2316 : * selectDumpableStatisticsObject: policy-setting subroutine
2317 : * Mark an extended statistics object as to be dumped or not
2318 : *
2319 : * We dump an extended statistics object if the schema it's in and the table
2320 : * it's for are being dumped. (This'll need more thought if statistics
2321 : * objects ever support cross-table stats.)
2322 : */
2323 : static void
2324 208 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2325 : {
2326 208 : if (checkExtensionMembership(&sobj->dobj, fout))
2327 0 : return; /* extension membership overrides all else */
2328 :
2329 208 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2330 208 : if (sobj->stattable == NULL ||
2331 208 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2332 35 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2333 : }
2334 :
2335 : /*
2336 : * selectDumpableObject: policy-setting subroutine
2337 : * Mark a generic dumpable object as to be dumped or not
2338 : *
2339 : * Use this only for object types without a special-case routine above.
2340 : */
2341 : static void
2342 580135 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2343 : {
2344 580135 : if (checkExtensionMembership(dobj, fout))
2345 201 : return; /* extension membership overrides all else */
2346 :
2347 : /*
2348 : * Default policy is to dump if parent namespace is dumpable, or for
2349 : * non-namespace-associated items, dump if we're dumping "everything".
2350 : */
2351 579934 : if (dobj->namespace)
2352 579070 : dobj->dump = dobj->namespace->dobj.dump_contains;
2353 : else
2354 864 : dobj->dump = fout->dopt->include_everything ?
2355 864 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2356 : }
2357 :
2358 : /*
2359 : * Dump a table's contents for loading using the COPY command
2360 : * - this routine is called by the Archiver when it wants the table
2361 : * to be dumped.
2362 : */
2363 : static int
2364 4463 : dumpTableData_copy(Archive *fout, const void *dcontext)
2365 : {
2366 4463 : const TableDataInfo *tdinfo = dcontext;
2367 4463 : const TableInfo *tbinfo = tdinfo->tdtable;
2368 4463 : const char *classname = tbinfo->dobj.name;
2369 4463 : PQExpBuffer q = createPQExpBuffer();
2370 :
2371 : /*
2372 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2373 : * which uses it already.
2374 : */
2375 4463 : PQExpBuffer clistBuf = createPQExpBuffer();
2376 4463 : PGconn *conn = GetConnection(fout);
2377 : PGresult *res;
2378 : int ret;
2379 : char *copybuf;
2380 : const char *column_list;
2381 :
2382 4463 : pg_log_info("dumping contents of table \"%s.%s\"",
2383 : tbinfo->dobj.namespace->dobj.name, classname);
2384 :
2385 : /*
2386 : * Specify the column list explicitly so that we have no possibility of
2387 : * retrieving data in the wrong column order. (The default column
2388 : * ordering of COPY will not be what we want in certain corner cases
2389 : * involving ADD COLUMN and inheritance.)
2390 : */
2391 4463 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2392 :
2393 : /*
2394 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, when a
2395 : * filter condition was specified, and when in binary upgrade mode and
2396 : * dumping an old pg_largeobject_metadata defined WITH OIDS. For other
2397 : * cases a simple COPY suffices.
2398 : */
2399 4463 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2400 4422 : (fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
2401 0 : tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
2402 : {
2403 : /* Temporary allows to access to foreign tables to dump data */
2404 41 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2405 1 : set_restrict_relation_kind(fout, "view");
2406 :
2407 41 : appendPQExpBufferStr(q, "COPY (SELECT ");
2408 : /* klugery to get rid of parens in column list */
2409 41 : if (strlen(column_list) > 2)
2410 : {
2411 41 : appendPQExpBufferStr(q, column_list + 1);
2412 41 : q->data[q->len - 1] = ' ';
2413 : }
2414 : else
2415 0 : appendPQExpBufferStr(q, "* ");
2416 :
2417 82 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2418 41 : fmtQualifiedDumpable(tbinfo),
2419 41 : tdinfo->filtercond ? tdinfo->filtercond : "");
2420 : }
2421 : else
2422 : {
2423 4422 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2424 4422 : fmtQualifiedDumpable(tbinfo),
2425 : column_list);
2426 : }
2427 4463 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2428 4462 : PQclear(res);
2429 4462 : destroyPQExpBuffer(clistBuf);
2430 :
2431 : for (;;)
2432 : {
2433 1823465 : ret = PQgetCopyData(conn, ©buf, 0);
2434 :
2435 1823465 : if (ret < 0)
2436 4462 : break; /* done or error */
2437 :
2438 1819003 : if (copybuf)
2439 : {
2440 1819003 : WriteData(fout, copybuf, ret);
2441 1819003 : PQfreemem(copybuf);
2442 : }
2443 :
2444 : /* ----------
2445 : * THROTTLE:
2446 : *
2447 : * There was considerable discussion in late July, 2000 regarding
2448 : * slowing down pg_dump when backing up large tables. Users with both
2449 : * slow & fast (multi-processor) machines experienced performance
2450 : * degradation when doing a backup.
2451 : *
2452 : * Initial attempts based on sleeping for a number of ms for each ms
2453 : * of work were deemed too complex, then a simple 'sleep in each loop'
2454 : * implementation was suggested. The latter failed because the loop
2455 : * was too tight. Finally, the following was implemented:
2456 : *
2457 : * If throttle is non-zero, then
2458 : * See how long since the last sleep.
2459 : * Work out how long to sleep (based on ratio).
2460 : * If sleep is more than 100ms, then
2461 : * sleep
2462 : * reset timer
2463 : * EndIf
2464 : * EndIf
2465 : *
2466 : * where the throttle value was the number of ms to sleep per ms of
2467 : * work. The calculation was done in each loop.
2468 : *
2469 : * Most of the hard work is done in the backend, and this solution
2470 : * still did not work particularly well: on slow machines, the ratio
2471 : * was 50:1, and on medium paced machines, 1:1, and on fast
2472 : * multi-processor machines, it had little or no effect, for reasons
2473 : * that were unclear.
2474 : *
2475 : * Further discussion ensued, and the proposal was dropped.
2476 : *
2477 : * For those people who want this feature, it can be implemented using
2478 : * gettimeofday in each loop, calculating the time since last sleep,
2479 : * multiplying that by the sleep ratio, then if the result is more
2480 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2481 : * function to sleep for a subsecond period ie.
2482 : *
2483 : * select(0, NULL, NULL, NULL, &tvi);
2484 : *
2485 : * This will return after the interval specified in the structure tvi.
2486 : * Finally, call gettimeofday again to save the 'last sleep time'.
2487 : * ----------
2488 : */
2489 : }
2490 4462 : archprintf(fout, "\\.\n\n\n");
2491 :
2492 4462 : if (ret == -2)
2493 : {
2494 : /* copy data transfer failed */
2495 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2496 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2497 0 : pg_log_error_detail("Command was: %s", q->data);
2498 0 : exit_nicely(1);
2499 : }
2500 :
2501 : /* Check command status and return to normal libpq state */
2502 4462 : res = PQgetResult(conn);
2503 4462 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2504 : {
2505 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2506 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2507 0 : pg_log_error_detail("Command was: %s", q->data);
2508 0 : exit_nicely(1);
2509 : }
2510 4462 : PQclear(res);
2511 :
2512 : /* Do this to ensure we've pumped libpq back to idle state */
2513 4462 : if (PQgetResult(conn) != NULL)
2514 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2515 : classname);
2516 :
2517 4462 : destroyPQExpBuffer(q);
2518 :
2519 : /* Revert back the setting */
2520 4462 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2521 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2522 :
2523 4462 : return 1;
2524 : }
2525 :
2526 : /*
2527 : * Dump table data using INSERT commands.
2528 : *
2529 : * Caution: when we restore from an archive file direct to database, the
2530 : * INSERT commands emitted by this function have to be parsed by
2531 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2532 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2533 : */
2534 : static int
2535 87 : dumpTableData_insert(Archive *fout, const void *dcontext)
2536 : {
2537 87 : const TableDataInfo *tdinfo = dcontext;
2538 87 : const TableInfo *tbinfo = tdinfo->tdtable;
2539 87 : DumpOptions *dopt = fout->dopt;
2540 87 : PQExpBuffer q = createPQExpBuffer();
2541 87 : PQExpBuffer insertStmt = NULL;
2542 : char *attgenerated;
2543 : PGresult *res;
2544 : int nfields,
2545 : i;
2546 87 : int rows_per_statement = dopt->dump_inserts;
2547 87 : int rows_this_statement = 0;
2548 :
2549 : /* Temporary allows to access to foreign tables to dump data */
2550 87 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2551 0 : set_restrict_relation_kind(fout, "view");
2552 :
2553 : /*
2554 : * If we're going to emit INSERTs with column names, the most efficient
2555 : * way to deal with generated columns is to exclude them entirely. For
2556 : * INSERTs without column names, we have to emit DEFAULT rather than the
2557 : * actual column value --- but we can save a few cycles by fetching nulls
2558 : * rather than the uninteresting-to-us value.
2559 : */
2560 87 : attgenerated = pg_malloc_array(char, tbinfo->numatts);
2561 87 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2562 87 : nfields = 0;
2563 269 : for (i = 0; i < tbinfo->numatts; i++)
2564 : {
2565 182 : if (tbinfo->attisdropped[i])
2566 2 : continue;
2567 180 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2568 8 : continue;
2569 172 : if (nfields > 0)
2570 92 : appendPQExpBufferStr(q, ", ");
2571 172 : if (tbinfo->attgenerated[i])
2572 8 : appendPQExpBufferStr(q, "NULL");
2573 : else
2574 164 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2575 172 : attgenerated[nfields] = tbinfo->attgenerated[i];
2576 172 : nfields++;
2577 : }
2578 : /* Servers before 9.4 will complain about zero-column SELECT */
2579 87 : if (nfields == 0)
2580 7 : appendPQExpBufferStr(q, "NULL");
2581 87 : appendPQExpBuffer(q, " FROM ONLY %s",
2582 87 : fmtQualifiedDumpable(tbinfo));
2583 87 : if (tdinfo->filtercond)
2584 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2585 :
2586 87 : ExecuteSqlStatement(fout, q->data);
2587 :
2588 : while (1)
2589 : {
2590 139 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2591 : PGRES_TUPLES_OK);
2592 :
2593 : /* cross-check field count, allowing for dummy NULL if any */
2594 139 : if (nfields != PQnfields(res) &&
2595 10 : !(nfields == 0 && PQnfields(res) == 1))
2596 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2597 : tbinfo->dobj.name);
2598 :
2599 : /*
2600 : * First time through, we build as much of the INSERT statement as
2601 : * possible in "insertStmt", which we can then just print for each
2602 : * statement. If the table happens to have zero dumpable columns then
2603 : * this will be a complete statement, otherwise it will end in
2604 : * "VALUES" and be ready to have the row's column values printed.
2605 : */
2606 139 : if (insertStmt == NULL)
2607 : {
2608 : const TableInfo *targettab;
2609 :
2610 87 : insertStmt = createPQExpBuffer();
2611 :
2612 : /*
2613 : * When load-via-partition-root is set or forced, get the root
2614 : * table name for the partition table, so that we can reload data
2615 : * through the root table.
2616 : */
2617 87 : if (tbinfo->ispartition &&
2618 48 : (dopt->load_via_partition_root ||
2619 24 : forcePartitionRootLoad(tbinfo)))
2620 7 : targettab = getRootTableInfo(tbinfo);
2621 : else
2622 80 : targettab = tbinfo;
2623 :
2624 87 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2625 87 : fmtQualifiedDumpable(targettab));
2626 :
2627 : /* corner case for zero-column table */
2628 87 : if (nfields == 0)
2629 : {
2630 7 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2631 : }
2632 : else
2633 : {
2634 : /* append the list of column names if required */
2635 80 : if (dopt->column_inserts)
2636 : {
2637 36 : appendPQExpBufferChar(insertStmt, '(');
2638 109 : for (int field = 0; field < nfields; field++)
2639 : {
2640 73 : if (field > 0)
2641 37 : appendPQExpBufferStr(insertStmt, ", ");
2642 73 : appendPQExpBufferStr(insertStmt,
2643 73 : fmtId(PQfname(res, field)));
2644 : }
2645 36 : appendPQExpBufferStr(insertStmt, ") ");
2646 : }
2647 :
2648 80 : if (tbinfo->needs_override)
2649 2 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2650 :
2651 80 : appendPQExpBufferStr(insertStmt, "VALUES");
2652 : }
2653 : }
2654 :
2655 3608 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2656 : {
2657 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2658 3469 : if (rows_this_statement == 0)
2659 3463 : archputs(insertStmt->data, fout);
2660 :
2661 : /*
2662 : * If it is zero-column table then we've already written the
2663 : * complete statement, which will mean we've disobeyed
2664 : * --rows-per-insert when it's set greater than 1. We do support
2665 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2666 : * UNION ALL ... but that's non-standard so we should avoid it
2667 : * given that using INSERTs is mostly only ever needed for
2668 : * cross-database exports.
2669 : */
2670 3469 : if (nfields == 0)
2671 6 : continue;
2672 :
2673 : /* Emit a row heading */
2674 3463 : if (rows_per_statement == 1)
2675 3454 : archputs(" (", fout);
2676 9 : else if (rows_this_statement > 0)
2677 6 : archputs(",\n\t(", fout);
2678 : else
2679 3 : archputs("\n\t(", fout);
2680 :
2681 10445 : for (int field = 0; field < nfields; field++)
2682 : {
2683 6982 : if (field > 0)
2684 3519 : archputs(", ", fout);
2685 6982 : if (attgenerated[field])
2686 : {
2687 2 : archputs("DEFAULT", fout);
2688 2 : continue;
2689 : }
2690 6980 : if (PQgetisnull(res, tuple, field))
2691 : {
2692 83 : archputs("NULL", fout);
2693 83 : continue;
2694 : }
2695 :
2696 : /* XXX This code is partially duplicated in ruleutils.c */
2697 6897 : switch (PQftype(res, field))
2698 : {
2699 4869 : case INT2OID:
2700 : case INT4OID:
2701 : case INT8OID:
2702 : case OIDOID:
2703 : case FLOAT4OID:
2704 : case FLOAT8OID:
2705 : case NUMERICOID:
2706 : {
2707 : /*
2708 : * These types are printed without quotes unless
2709 : * they contain values that aren't accepted by the
2710 : * scanner unquoted (e.g., 'NaN'). Note that
2711 : * strtod() and friends might accept NaN, so we
2712 : * can't use that to test.
2713 : *
2714 : * In reality we only need to defend against
2715 : * infinity and NaN, so we need not get too crazy
2716 : * about pattern matching here.
2717 : */
2718 4869 : const char *s = PQgetvalue(res, tuple, field);
2719 :
2720 4869 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2721 4867 : archputs(s, fout);
2722 : else
2723 2 : archprintf(fout, "'%s'", s);
2724 : }
2725 4869 : break;
2726 :
2727 2 : case BITOID:
2728 : case VARBITOID:
2729 2 : archprintf(fout, "B'%s'",
2730 : PQgetvalue(res, tuple, field));
2731 2 : break;
2732 :
2733 4 : case BOOLOID:
2734 4 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2735 2 : archputs("true", fout);
2736 : else
2737 2 : archputs("false", fout);
2738 4 : break;
2739 :
2740 2022 : default:
2741 : /* All other types are printed as string literals. */
2742 2022 : resetPQExpBuffer(q);
2743 2022 : appendStringLiteralAH(q,
2744 : PQgetvalue(res, tuple, field),
2745 : fout);
2746 2022 : archputs(q->data, fout);
2747 2022 : break;
2748 : }
2749 : }
2750 :
2751 : /* Terminate the row ... */
2752 3463 : archputs(")", fout);
2753 :
2754 : /* ... and the statement, if the target no. of rows is reached */
2755 3463 : if (++rows_this_statement >= rows_per_statement)
2756 : {
2757 3456 : if (dopt->do_nothing)
2758 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2759 : else
2760 3456 : archputs(";\n", fout);
2761 : /* Reset the row counter */
2762 3456 : rows_this_statement = 0;
2763 : }
2764 : }
2765 :
2766 139 : if (PQntuples(res) <= 0)
2767 : {
2768 87 : PQclear(res);
2769 87 : break;
2770 : }
2771 52 : PQclear(res);
2772 : }
2773 :
2774 : /* Terminate any statements that didn't make the row count. */
2775 87 : if (rows_this_statement > 0)
2776 : {
2777 1 : if (dopt->do_nothing)
2778 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2779 : else
2780 1 : archputs(";\n", fout);
2781 : }
2782 :
2783 87 : archputs("\n\n", fout);
2784 :
2785 87 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2786 :
2787 87 : destroyPQExpBuffer(q);
2788 87 : if (insertStmt != NULL)
2789 87 : destroyPQExpBuffer(insertStmt);
2790 87 : free(attgenerated);
2791 :
2792 : /* Revert back the setting */
2793 87 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2794 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2795 :
2796 87 : return 1;
2797 : }
2798 :
2799 : /*
2800 : * getRootTableInfo:
2801 : * get the root TableInfo for the given partition table.
2802 : */
2803 : static TableInfo *
2804 79 : getRootTableInfo(const TableInfo *tbinfo)
2805 : {
2806 : TableInfo *parentTbinfo;
2807 :
2808 : Assert(tbinfo->ispartition);
2809 : Assert(tbinfo->numParents == 1);
2810 :
2811 79 : parentTbinfo = tbinfo->parents[0];
2812 79 : while (parentTbinfo->ispartition)
2813 : {
2814 : Assert(parentTbinfo->numParents == 1);
2815 0 : parentTbinfo = parentTbinfo->parents[0];
2816 : }
2817 :
2818 79 : return parentTbinfo;
2819 : }
2820 :
2821 : /*
2822 : * forcePartitionRootLoad
2823 : * Check if we must force load_via_partition_root for this partition.
2824 : *
2825 : * This is required if any level of ancestral partitioned table has an
2826 : * unsafe partitioning scheme.
2827 : */
2828 : static bool
2829 1076 : forcePartitionRootLoad(const TableInfo *tbinfo)
2830 : {
2831 : TableInfo *parentTbinfo;
2832 :
2833 : Assert(tbinfo->ispartition);
2834 : Assert(tbinfo->numParents == 1);
2835 :
2836 1076 : parentTbinfo = tbinfo->parents[0];
2837 1076 : if (parentTbinfo->unsafe_partitions)
2838 79 : return true;
2839 1217 : while (parentTbinfo->ispartition)
2840 : {
2841 : Assert(parentTbinfo->numParents == 1);
2842 220 : parentTbinfo = parentTbinfo->parents[0];
2843 220 : if (parentTbinfo->unsafe_partitions)
2844 0 : return true;
2845 : }
2846 :
2847 997 : return false;
2848 : }
2849 :
2850 : /*
2851 : * dumpTableData -
2852 : * dump the contents of a single table
2853 : *
2854 : * Actually, this just makes an ArchiveEntry for the table contents.
2855 : */
2856 : static void
2857 4636 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2858 : {
2859 4636 : DumpOptions *dopt = fout->dopt;
2860 4636 : const TableInfo *tbinfo = tdinfo->tdtable;
2861 4636 : PQExpBuffer copyBuf = createPQExpBuffer();
2862 4636 : PQExpBuffer clistBuf = createPQExpBuffer();
2863 : DataDumperPtr dumpFn;
2864 4636 : char *tdDefn = NULL;
2865 : char *copyStmt;
2866 : const char *copyFrom;
2867 :
2868 : /* We had better have loaded per-column details about this table */
2869 : Assert(tbinfo->interesting);
2870 :
2871 : /*
2872 : * When load-via-partition-root is set or forced, get the root table name
2873 : * for the partition table, so that we can reload data through the root
2874 : * table. Then construct a comment to be inserted into the TOC entry's
2875 : * defn field, so that such cases can be identified reliably.
2876 : */
2877 4636 : if (tbinfo->ispartition &&
2878 2104 : (dopt->load_via_partition_root ||
2879 1052 : forcePartitionRootLoad(tbinfo)))
2880 72 : {
2881 : const TableInfo *parentTbinfo;
2882 : char *sanitized;
2883 :
2884 72 : parentTbinfo = getRootTableInfo(tbinfo);
2885 72 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2886 72 : sanitized = sanitize_line(copyFrom, true);
2887 72 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2888 : sanitized);
2889 72 : free(sanitized);
2890 72 : tdDefn = pg_strdup(copyBuf->data);
2891 : }
2892 : else
2893 4564 : copyFrom = fmtQualifiedDumpable(tbinfo);
2894 :
2895 4636 : if (dopt->dump_inserts == 0)
2896 : {
2897 : /* Dump/restore using COPY */
2898 4549 : dumpFn = dumpTableData_copy;
2899 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2900 4549 : printfPQExpBuffer(copyBuf, "COPY %s ",
2901 : copyFrom);
2902 4549 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2903 : fmtCopyColumnList(tbinfo, clistBuf));
2904 4549 : copyStmt = copyBuf->data;
2905 : }
2906 : else
2907 : {
2908 : /* Restore using INSERT */
2909 87 : dumpFn = dumpTableData_insert;
2910 87 : copyStmt = NULL;
2911 : }
2912 :
2913 : /*
2914 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2915 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2916 : * See comments for BuildArchiveDependencies.
2917 : */
2918 4636 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2919 : {
2920 : TocEntry *te;
2921 :
2922 4636 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2923 4636 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2924 : .namespace = tbinfo->dobj.namespace->dobj.name,
2925 : .owner = tbinfo->rolname,
2926 : .description = "TABLE DATA",
2927 : .section = SECTION_DATA,
2928 : .createStmt = tdDefn,
2929 : .copyStmt = copyStmt,
2930 : .deps = &(tbinfo->dobj.dumpId),
2931 : .nDeps = 1,
2932 : .dumpFn = dumpFn,
2933 : .dumpArg = tdinfo));
2934 :
2935 : /*
2936 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2937 : * and want to order dump jobs by table size. We choose to measure
2938 : * dataLength in table pages (including TOAST pages) during dump, so
2939 : * no scaling is needed.
2940 : *
2941 : * However, relpages is declared as "integer" in pg_class, and hence
2942 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2943 : * Cast so that we get the right interpretation of table sizes
2944 : * exceeding INT_MAX pages.
2945 : */
2946 4636 : te->dataLength = (BlockNumber) tbinfo->relpages;
2947 4636 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2948 :
2949 : /*
2950 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2951 : * and instead we'd better worry about integer overflow. Clamp to
2952 : * INT_MAX if the correct result exceeds that.
2953 : */
2954 : if (sizeof(te->dataLength) == 4 &&
2955 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2956 : te->dataLength < 0))
2957 : te->dataLength = INT_MAX;
2958 : }
2959 :
2960 4636 : destroyPQExpBuffer(copyBuf);
2961 4636 : destroyPQExpBuffer(clistBuf);
2962 4636 : }
2963 :
2964 : /*
2965 : * refreshMatViewData -
2966 : * load or refresh the contents of a single materialized view
2967 : *
2968 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2969 : * statement.
2970 : */
2971 : static void
2972 345 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2973 : {
2974 345 : TableInfo *tbinfo = tdinfo->tdtable;
2975 : PQExpBuffer q;
2976 :
2977 : /* If the materialized view is not flagged as populated, skip this. */
2978 345 : if (!tbinfo->relispopulated)
2979 68 : return;
2980 :
2981 277 : q = createPQExpBuffer();
2982 :
2983 277 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2984 277 : fmtQualifiedDumpable(tbinfo));
2985 :
2986 277 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2987 277 : ArchiveEntry(fout,
2988 : tdinfo->dobj.catId, /* catalog ID */
2989 277 : tdinfo->dobj.dumpId, /* dump ID */
2990 277 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2991 : .namespace = tbinfo->dobj.namespace->dobj.name,
2992 : .owner = tbinfo->rolname,
2993 : .description = "MATERIALIZED VIEW DATA",
2994 : .section = SECTION_POST_DATA,
2995 : .createStmt = q->data,
2996 : .deps = tdinfo->dobj.dependencies,
2997 : .nDeps = tdinfo->dobj.nDeps));
2998 :
2999 277 : destroyPQExpBuffer(q);
3000 : }
3001 :
3002 : /*
3003 : * getTableData -
3004 : * set up dumpable objects representing the contents of tables
3005 : */
3006 : static void
3007 251 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3008 : {
3009 : int i;
3010 :
3011 68102 : for (i = 0; i < numTables; i++)
3012 : {
3013 67851 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3014 1002 : (!relkind || tblinfo[i].relkind == relkind))
3015 6463 : makeTableDataInfo(dopt, &(tblinfo[i]));
3016 : }
3017 251 : }
3018 :
3019 : /*
3020 : * Make a dumpable object for the data of this specific table
3021 : *
3022 : * Note: we make a TableDataInfo if and only if we are going to dump the
3023 : * table data; the "dump" field in such objects isn't very interesting.
3024 : */
3025 : static void
3026 6542 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3027 : {
3028 : TableDataInfo *tdinfo;
3029 :
3030 : /*
3031 : * Nothing to do if we already decided to dump the table. This will
3032 : * happen for "config" tables.
3033 : */
3034 6542 : if (tbinfo->dataObj != NULL)
3035 1 : return;
3036 :
3037 : /* Skip property graphs (no data to dump) */
3038 6541 : if (tbinfo->relkind == RELKIND_PROPGRAPH)
3039 86 : return;
3040 : /* Skip VIEWs (no data to dump) */
3041 6455 : if (tbinfo->relkind == RELKIND_VIEW)
3042 502 : return;
3043 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3044 5953 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3045 38 : (foreign_servers_include_oids.head == NULL ||
3046 4 : !simple_oid_list_member(&foreign_servers_include_oids,
3047 : tbinfo->foreign_server)))
3048 37 : return;
3049 : /* Skip partitioned tables (data in partitions) */
3050 5916 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3051 507 : return;
3052 :
3053 : /* Don't dump data in unlogged tables, if so requested */
3054 5409 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3055 41 : dopt->no_unlogged_table_data)
3056 18 : return;
3057 :
3058 : /* Check that the data is not explicitly excluded */
3059 5391 : if (simple_oid_list_member(&tabledata_exclude_oids,
3060 : tbinfo->dobj.catId.oid))
3061 8 : return;
3062 :
3063 : /* OK, let's dump it */
3064 5383 : tdinfo = pg_malloc_object(TableDataInfo);
3065 :
3066 5383 : if (tbinfo->relkind == RELKIND_MATVIEW)
3067 345 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3068 5038 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3069 402 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3070 : else
3071 4636 : tdinfo->dobj.objType = DO_TABLE_DATA;
3072 :
3073 : /*
3074 : * Note: use tableoid 0 so that this object won't be mistaken for
3075 : * something that pg_depend entries apply to.
3076 : */
3077 5383 : tdinfo->dobj.catId.tableoid = 0;
3078 5383 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3079 5383 : AssignDumpId(&tdinfo->dobj);
3080 5383 : tdinfo->dobj.name = tbinfo->dobj.name;
3081 5383 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3082 5383 : tdinfo->tdtable = tbinfo;
3083 5383 : tdinfo->filtercond = NULL; /* might get set later */
3084 5383 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3085 :
3086 : /* A TableDataInfo contains data, of course */
3087 5383 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3088 :
3089 5383 : tbinfo->dataObj = tdinfo;
3090 :
3091 : /*
3092 : * Materialized view statistics must be restored after the data, because
3093 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3094 : *
3095 : * The dependency is added here because the statistics objects are created
3096 : * first.
3097 : */
3098 5383 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3099 : {
3100 268 : tbinfo->stats->section = SECTION_POST_DATA;
3101 268 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3102 : }
3103 :
3104 : /* Make sure that we'll collect per-column info for this table. */
3105 5383 : tbinfo->interesting = true;
3106 : }
3107 :
3108 : /*
3109 : * The refresh for a materialized view must be dependent on the refresh for
3110 : * any materialized view that this one is dependent on.
3111 : *
3112 : * This must be called after all the objects are created, but before they are
3113 : * sorted.
3114 : */
3115 : static void
3116 215 : buildMatViewRefreshDependencies(Archive *fout)
3117 : {
3118 : PQExpBuffer query;
3119 : PGresult *res;
3120 : int ntups,
3121 : i;
3122 : int i_classid,
3123 : i_objid,
3124 : i_refobjid;
3125 :
3126 : /* No Mat Views before 9.3. */
3127 215 : if (fout->remoteVersion < 90300)
3128 0 : return;
3129 :
3130 215 : query = createPQExpBuffer();
3131 :
3132 215 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3133 : "( "
3134 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3135 : "FROM pg_depend d1 "
3136 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3137 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3138 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3139 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3140 : "AND d2.objid = r1.oid "
3141 : "AND d2.refobjid <> d1.objid "
3142 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3143 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3144 : CppAsString2(RELKIND_VIEW) ") "
3145 : "WHERE d1.classid = 'pg_class'::regclass "
3146 : "UNION "
3147 : "SELECT w.objid, d3.refobjid, c3.relkind "
3148 : "FROM w "
3149 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3150 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3151 : "AND d3.objid = r3.oid "
3152 : "AND d3.refobjid <> w.refobjid "
3153 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3154 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3155 : CppAsString2(RELKIND_VIEW) ") "
3156 : ") "
3157 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3158 : "FROM w "
3159 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3160 :
3161 215 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3162 :
3163 215 : ntups = PQntuples(res);
3164 :
3165 215 : i_classid = PQfnumber(res, "classid");
3166 215 : i_objid = PQfnumber(res, "objid");
3167 215 : i_refobjid = PQfnumber(res, "refobjid");
3168 :
3169 479 : for (i = 0; i < ntups; i++)
3170 : {
3171 : CatalogId objId;
3172 : CatalogId refobjId;
3173 : DumpableObject *dobj;
3174 : DumpableObject *refdobj;
3175 : TableInfo *tbinfo;
3176 : TableInfo *reftbinfo;
3177 :
3178 264 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3179 264 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3180 264 : refobjId.tableoid = objId.tableoid;
3181 264 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3182 :
3183 264 : dobj = findObjectByCatalogId(objId);
3184 264 : if (dobj == NULL)
3185 48 : continue;
3186 :
3187 : Assert(dobj->objType == DO_TABLE);
3188 264 : tbinfo = (TableInfo *) dobj;
3189 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3190 264 : dobj = (DumpableObject *) tbinfo->dataObj;
3191 264 : if (dobj == NULL)
3192 48 : continue;
3193 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3194 :
3195 216 : refdobj = findObjectByCatalogId(refobjId);
3196 216 : if (refdobj == NULL)
3197 0 : continue;
3198 :
3199 : Assert(refdobj->objType == DO_TABLE);
3200 216 : reftbinfo = (TableInfo *) refdobj;
3201 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3202 216 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3203 216 : if (refdobj == NULL)
3204 0 : continue;
3205 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3206 :
3207 216 : addObjectDependency(dobj, refdobj->dumpId);
3208 :
3209 216 : if (!reftbinfo->relispopulated)
3210 34 : tbinfo->relispopulated = false;
3211 : }
3212 :
3213 215 : PQclear(res);
3214 :
3215 215 : destroyPQExpBuffer(query);
3216 : }
3217 :
3218 : /*
3219 : * getTableDataFKConstraints -
3220 : * add dump-order dependencies reflecting foreign key constraints
3221 : *
3222 : * This code is executed only in a data-only dump --- in schema+data dumps
3223 : * we handle foreign key issues by not creating the FK constraints until
3224 : * after the data is loaded. In a data-only dump, however, we want to
3225 : * order the table data objects in such a way that a table's referenced
3226 : * tables are restored first. (In the presence of circular references or
3227 : * self-references this may be impossible; we'll detect and complain about
3228 : * that during the dependency sorting step.)
3229 : */
3230 : static void
3231 7 : getTableDataFKConstraints(void)
3232 : {
3233 : DumpableObject **dobjs;
3234 : int numObjs;
3235 : int i;
3236 :
3237 : /* Search through all the dumpable objects for FK constraints */
3238 7 : getDumpableObjects(&dobjs, &numObjs);
3239 26929 : for (i = 0; i < numObjs; i++)
3240 : {
3241 26922 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3242 : {
3243 8 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3244 : TableInfo *ftable;
3245 :
3246 : /* Not interesting unless both tables are to be dumped */
3247 8 : if (cinfo->contable == NULL ||
3248 8 : cinfo->contable->dataObj == NULL)
3249 4 : continue;
3250 4 : ftable = findTableByOid(cinfo->confrelid);
3251 4 : if (ftable == NULL ||
3252 4 : ftable->dataObj == NULL)
3253 0 : continue;
3254 :
3255 : /*
3256 : * Okay, make referencing table's TABLE_DATA object depend on the
3257 : * referenced table's TABLE_DATA object.
3258 : */
3259 4 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3260 4 : ftable->dataObj->dobj.dumpId);
3261 : }
3262 : }
3263 7 : free(dobjs);
3264 7 : }
3265 :
3266 :
3267 : /*
3268 : * dumpDatabase:
3269 : * dump the database definition
3270 : */
3271 : static void
3272 160 : dumpDatabase(Archive *fout)
3273 : {
3274 160 : DumpOptions *dopt = fout->dopt;
3275 160 : PQExpBuffer dbQry = createPQExpBuffer();
3276 160 : PQExpBuffer delQry = createPQExpBuffer();
3277 160 : PQExpBuffer creaQry = createPQExpBuffer();
3278 160 : PQExpBuffer labelq = createPQExpBuffer();
3279 160 : PGconn *conn = GetConnection(fout);
3280 : PGresult *res;
3281 : int i_tableoid,
3282 : i_oid,
3283 : i_datname,
3284 : i_datdba,
3285 : i_encoding,
3286 : i_datlocprovider,
3287 : i_collate,
3288 : i_ctype,
3289 : i_datlocale,
3290 : i_daticurules,
3291 : i_frozenxid,
3292 : i_minmxid,
3293 : i_datacl,
3294 : i_acldefault,
3295 : i_datistemplate,
3296 : i_datconnlimit,
3297 : i_datcollversion,
3298 : i_tablespace;
3299 : CatalogId dbCatId;
3300 : DumpId dbDumpId;
3301 : DumpableAcl dbdacl;
3302 : const char *datname,
3303 : *dba,
3304 : *encoding,
3305 : *datlocprovider,
3306 : *collate,
3307 : *ctype,
3308 : *locale,
3309 : *icurules,
3310 : *datistemplate,
3311 : *datconnlimit,
3312 : *tablespace;
3313 : uint32 frozenxid,
3314 : minmxid;
3315 : char *qdatname;
3316 :
3317 160 : pg_log_info("saving database definition");
3318 :
3319 : /*
3320 : * Fetch the database-level properties for this database.
3321 : */
3322 160 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3323 : "datdba, "
3324 : "pg_encoding_to_char(encoding) AS encoding, "
3325 : "datcollate, datctype, datfrozenxid, "
3326 : "datacl, acldefault('d', datdba) AS acldefault, "
3327 : "datistemplate, datconnlimit, ");
3328 160 : if (fout->remoteVersion >= 90300)
3329 160 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3330 : else
3331 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3332 160 : if (fout->remoteVersion >= 170000)
3333 160 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3334 0 : else if (fout->remoteVersion >= 150000)
3335 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3336 : else
3337 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3338 160 : if (fout->remoteVersion >= 160000)
3339 160 : appendPQExpBufferStr(dbQry, "daticurules, ");
3340 : else
3341 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3342 160 : appendPQExpBufferStr(dbQry,
3343 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3344 : "shobj_description(oid, 'pg_database') AS description "
3345 : "FROM pg_database "
3346 : "WHERE datname = current_database()");
3347 :
3348 160 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3349 :
3350 160 : i_tableoid = PQfnumber(res, "tableoid");
3351 160 : i_oid = PQfnumber(res, "oid");
3352 160 : i_datname = PQfnumber(res, "datname");
3353 160 : i_datdba = PQfnumber(res, "datdba");
3354 160 : i_encoding = PQfnumber(res, "encoding");
3355 160 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3356 160 : i_collate = PQfnumber(res, "datcollate");
3357 160 : i_ctype = PQfnumber(res, "datctype");
3358 160 : i_datlocale = PQfnumber(res, "datlocale");
3359 160 : i_daticurules = PQfnumber(res, "daticurules");
3360 160 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3361 160 : i_minmxid = PQfnumber(res, "datminmxid");
3362 160 : i_datacl = PQfnumber(res, "datacl");
3363 160 : i_acldefault = PQfnumber(res, "acldefault");
3364 160 : i_datistemplate = PQfnumber(res, "datistemplate");
3365 160 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3366 160 : i_datcollversion = PQfnumber(res, "datcollversion");
3367 160 : i_tablespace = PQfnumber(res, "tablespace");
3368 :
3369 160 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3370 160 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3371 160 : datname = PQgetvalue(res, 0, i_datname);
3372 160 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3373 160 : encoding = PQgetvalue(res, 0, i_encoding);
3374 160 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3375 160 : collate = PQgetvalue(res, 0, i_collate);
3376 160 : ctype = PQgetvalue(res, 0, i_ctype);
3377 160 : if (!PQgetisnull(res, 0, i_datlocale))
3378 14 : locale = PQgetvalue(res, 0, i_datlocale);
3379 : else
3380 146 : locale = NULL;
3381 160 : if (!PQgetisnull(res, 0, i_daticurules))
3382 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3383 : else
3384 160 : icurules = NULL;
3385 160 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3386 160 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3387 160 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3388 160 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3389 160 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3390 160 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3391 160 : tablespace = PQgetvalue(res, 0, i_tablespace);
3392 :
3393 160 : qdatname = pg_strdup(fmtId(datname));
3394 :
3395 : /*
3396 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3397 : * to preserve that), as well as the encoding, locale, and tablespace
3398 : * since those can't be altered later. Other DB properties are left to
3399 : * the DATABASE PROPERTIES entry, so that they can be applied after
3400 : * reconnecting to the target DB.
3401 : *
3402 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3403 : * shown it to be faster. When the server is in binary upgrade mode, it
3404 : * will also skip the checkpoints this strategy ordinarily performs.
3405 : */
3406 160 : if (dopt->binary_upgrade)
3407 : {
3408 39 : appendPQExpBuffer(creaQry,
3409 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3410 : "OID = %u STRATEGY = FILE_COPY",
3411 : qdatname, dbCatId.oid);
3412 : }
3413 : else
3414 : {
3415 121 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3416 : qdatname);
3417 : }
3418 160 : if (strlen(encoding) > 0)
3419 : {
3420 160 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3421 160 : appendStringLiteralAH(creaQry, encoding, fout);
3422 : }
3423 :
3424 160 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3425 160 : if (datlocprovider[0] == 'b')
3426 14 : appendPQExpBufferStr(creaQry, "builtin");
3427 146 : else if (datlocprovider[0] == 'c')
3428 146 : appendPQExpBufferStr(creaQry, "libc");
3429 0 : else if (datlocprovider[0] == 'i')
3430 0 : appendPQExpBufferStr(creaQry, "icu");
3431 : else
3432 0 : pg_fatal("unrecognized locale provider: %s",
3433 : datlocprovider);
3434 :
3435 160 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3436 : {
3437 160 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3438 160 : appendStringLiteralAH(creaQry, collate, fout);
3439 : }
3440 : else
3441 : {
3442 0 : if (strlen(collate) > 0)
3443 : {
3444 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3445 0 : appendStringLiteralAH(creaQry, collate, fout);
3446 : }
3447 0 : if (strlen(ctype) > 0)
3448 : {
3449 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3450 0 : appendStringLiteralAH(creaQry, ctype, fout);
3451 : }
3452 : }
3453 160 : if (locale)
3454 : {
3455 14 : if (datlocprovider[0] == 'b')
3456 14 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3457 : else
3458 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3459 :
3460 14 : appendStringLiteralAH(creaQry, locale, fout);
3461 : }
3462 :
3463 160 : if (icurules)
3464 : {
3465 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3466 0 : appendStringLiteralAH(creaQry, icurules, fout);
3467 : }
3468 :
3469 : /*
3470 : * For binary upgrade, carry over the collation version. For normal
3471 : * dump/restore, omit the version, so that it is computed upon restore.
3472 : */
3473 160 : if (dopt->binary_upgrade)
3474 : {
3475 39 : if (!PQgetisnull(res, 0, i_datcollversion))
3476 : {
3477 39 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3478 39 : appendStringLiteralAH(creaQry,
3479 : PQgetvalue(res, 0, i_datcollversion),
3480 : fout);
3481 : }
3482 : }
3483 :
3484 : /*
3485 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3486 : * thing; the decision whether to specify a tablespace should be left till
3487 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3488 : * label the DATABASE entry with the tablespace and let the normal
3489 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3490 : * attention to default_tablespace, so that won't work.
3491 : */
3492 160 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3493 5 : !dopt->outputNoTablespaces)
3494 5 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3495 : fmtId(tablespace));
3496 160 : appendPQExpBufferStr(creaQry, ";\n");
3497 :
3498 160 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3499 : qdatname);
3500 :
3501 160 : dbDumpId = createDumpId();
3502 :
3503 160 : ArchiveEntry(fout,
3504 : dbCatId, /* catalog ID */
3505 : dbDumpId, /* dump ID */
3506 160 : ARCHIVE_OPTS(.tag = datname,
3507 : .owner = dba,
3508 : .description = "DATABASE",
3509 : .section = SECTION_PRE_DATA,
3510 : .createStmt = creaQry->data,
3511 : .dropStmt = delQry->data));
3512 :
3513 : /* Compute correct tag for archive entry */
3514 160 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3515 :
3516 : /* Dump DB comment if any */
3517 : {
3518 : /*
3519 : * 8.2 and up keep comments on shared objects in a shared table, so we
3520 : * cannot use the dumpComment() code used for other database objects.
3521 : * Be careful that the ArchiveEntry parameters match that function.
3522 : */
3523 160 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3524 :
3525 160 : if (comment && *comment && !dopt->no_comments)
3526 : {
3527 62 : resetPQExpBuffer(dbQry);
3528 :
3529 : /*
3530 : * Generates warning when loaded into a differently-named
3531 : * database.
3532 : */
3533 62 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3534 62 : appendStringLiteralAH(dbQry, comment, fout);
3535 62 : appendPQExpBufferStr(dbQry, ";\n");
3536 :
3537 62 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3538 62 : ARCHIVE_OPTS(.tag = labelq->data,
3539 : .owner = dba,
3540 : .description = "COMMENT",
3541 : .section = SECTION_NONE,
3542 : .createStmt = dbQry->data,
3543 : .deps = &dbDumpId,
3544 : .nDeps = 1));
3545 : }
3546 : }
3547 :
3548 : /* Dump DB security label, if enabled */
3549 160 : if (!dopt->no_security_labels)
3550 : {
3551 : PGresult *shres;
3552 : PQExpBuffer seclabelQry;
3553 :
3554 160 : seclabelQry = createPQExpBuffer();
3555 :
3556 160 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3557 160 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3558 160 : resetPQExpBuffer(seclabelQry);
3559 160 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3560 160 : if (seclabelQry->len > 0)
3561 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3562 0 : ARCHIVE_OPTS(.tag = labelq->data,
3563 : .owner = dba,
3564 : .description = "SECURITY LABEL",
3565 : .section = SECTION_NONE,
3566 : .createStmt = seclabelQry->data,
3567 : .deps = &dbDumpId,
3568 : .nDeps = 1));
3569 160 : destroyPQExpBuffer(seclabelQry);
3570 160 : PQclear(shres);
3571 : }
3572 :
3573 : /*
3574 : * Dump ACL if any. Note that we do not support initial privileges
3575 : * (pg_init_privs) on databases.
3576 : */
3577 160 : dbdacl.privtype = 0;
3578 160 : dbdacl.initprivs = NULL;
3579 :
3580 160 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3581 : qdatname, NULL, NULL,
3582 : NULL, dba, &dbdacl);
3583 :
3584 : /*
3585 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3586 : * non-default database-level properties. (The reason this must be
3587 : * separate is that we cannot put any additional commands into the TOC
3588 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3589 : * in an implicit transaction block, and the backend won't allow CREATE
3590 : * DATABASE in that context.)
3591 : */
3592 160 : resetPQExpBuffer(creaQry);
3593 160 : resetPQExpBuffer(delQry);
3594 :
3595 160 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3596 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3597 : qdatname, datconnlimit);
3598 :
3599 160 : if (strcmp(datistemplate, "t") == 0)
3600 : {
3601 21 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3602 : qdatname);
3603 :
3604 : /*
3605 : * The backend won't accept DROP DATABASE on a template database. We
3606 : * can deal with that by removing the template marking before the DROP
3607 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3608 : * since no such command is currently supported, fake it with a direct
3609 : * UPDATE on pg_database.
3610 : */
3611 21 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3612 : "SET datistemplate = false WHERE datname = ");
3613 21 : appendStringLiteralAH(delQry, datname, fout);
3614 21 : appendPQExpBufferStr(delQry, ";\n");
3615 : }
3616 :
3617 : /*
3618 : * We do not restore pg_database.dathasloginevt because it is set
3619 : * automatically on login event trigger creation.
3620 : */
3621 :
3622 : /* Add database-specific SET options */
3623 160 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3624 :
3625 : /*
3626 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3627 : * entry, too, for lack of a better place.
3628 : */
3629 160 : if (dopt->binary_upgrade)
3630 : {
3631 39 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3632 39 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3633 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3634 : "WHERE datname = ",
3635 : frozenxid, minmxid);
3636 39 : appendStringLiteralAH(creaQry, datname, fout);
3637 39 : appendPQExpBufferStr(creaQry, ";\n");
3638 : }
3639 :
3640 160 : if (creaQry->len > 0)
3641 52 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3642 52 : ARCHIVE_OPTS(.tag = datname,
3643 : .owner = dba,
3644 : .description = "DATABASE PROPERTIES",
3645 : .section = SECTION_PRE_DATA,
3646 : .createStmt = creaQry->data,
3647 : .dropStmt = delQry->data,
3648 : .deps = &dbDumpId));
3649 :
3650 : /*
3651 : * pg_largeobject comes from the old system intact, so set its
3652 : * relfrozenxids, relminmxids and relfilenode.
3653 : *
3654 : * pg_largeobject_metadata also comes from the old system intact for
3655 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3656 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3657 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3658 : * changed its storage format in v16.
3659 : */
3660 160 : if (dopt->binary_upgrade)
3661 : {
3662 : PGresult *lo_res;
3663 39 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3664 39 : PQExpBuffer loOutQry = createPQExpBuffer();
3665 39 : PQExpBuffer lomOutQry = createPQExpBuffer();
3666 39 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3667 39 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3668 : int ii_relfrozenxid,
3669 : ii_relfilenode,
3670 : ii_oid,
3671 : ii_relminmxid;
3672 :
3673 39 : if (fout->remoteVersion >= 90300)
3674 39 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3675 : "FROM pg_catalog.pg_class\n"
3676 : "WHERE oid IN (%u, %u, %u, %u);\n",
3677 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3678 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3679 : else
3680 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3681 : "FROM pg_catalog.pg_class\n"
3682 : "WHERE oid IN (%u, %u);\n",
3683 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3684 :
3685 39 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3686 :
3687 39 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3688 39 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3689 39 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3690 39 : ii_oid = PQfnumber(lo_res, "oid");
3691 :
3692 39 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3693 39 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3694 39 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3695 39 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3696 195 : for (int i = 0; i < PQntuples(lo_res); ++i)
3697 : {
3698 : Oid oid;
3699 : RelFileNumber relfilenumber;
3700 : PQExpBuffer horizonQry;
3701 : PQExpBuffer outQry;
3702 :
3703 156 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3704 156 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3705 :
3706 156 : if (oid == LargeObjectRelationId ||
3707 : oid == LargeObjectLOidPNIndexId)
3708 : {
3709 78 : horizonQry = loHorizonQry;
3710 78 : outQry = loOutQry;
3711 : }
3712 : else
3713 : {
3714 78 : horizonQry = lomHorizonQry;
3715 78 : outQry = lomOutQry;
3716 : }
3717 :
3718 156 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3719 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3720 : "WHERE oid = %u;\n",
3721 156 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3722 156 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3723 156 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3724 :
3725 156 : if (oid == LargeObjectRelationId ||
3726 : oid == LargeObjectMetadataRelationId)
3727 78 : appendPQExpBuffer(outQry,
3728 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3729 : relfilenumber);
3730 78 : else if (oid == LargeObjectLOidPNIndexId ||
3731 : oid == LargeObjectMetadataOidIndexId)
3732 78 : appendPQExpBuffer(outQry,
3733 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3734 : relfilenumber);
3735 : }
3736 :
3737 39 : appendPQExpBufferStr(loOutQry,
3738 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3739 39 : appendPQExpBufferStr(lomOutQry,
3740 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3741 :
3742 39 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3743 39 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3744 :
3745 39 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3746 39 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3747 : .description = "pg_largeobject",
3748 : .section = SECTION_PRE_DATA,
3749 : .createStmt = loOutQry->data));
3750 :
3751 39 : if (fout->remoteVersion >= 160000)
3752 39 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3753 39 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3754 : .description = "pg_largeobject_metadata",
3755 : .section = SECTION_PRE_DATA,
3756 : .createStmt = lomOutQry->data));
3757 :
3758 39 : PQclear(lo_res);
3759 :
3760 39 : destroyPQExpBuffer(loFrozenQry);
3761 39 : destroyPQExpBuffer(loHorizonQry);
3762 39 : destroyPQExpBuffer(lomHorizonQry);
3763 39 : destroyPQExpBuffer(loOutQry);
3764 39 : destroyPQExpBuffer(lomOutQry);
3765 : }
3766 :
3767 160 : PQclear(res);
3768 :
3769 160 : free(qdatname);
3770 160 : destroyPQExpBuffer(dbQry);
3771 160 : destroyPQExpBuffer(delQry);
3772 160 : destroyPQExpBuffer(creaQry);
3773 160 : destroyPQExpBuffer(labelq);
3774 160 : }
3775 :
3776 : /*
3777 : * Collect any database-specific or role-and-database-specific SET options
3778 : * for this database, and append them to outbuf.
3779 : */
3780 : static void
3781 160 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3782 : const char *dbname, Oid dboid)
3783 : {
3784 160 : PGconn *conn = GetConnection(AH);
3785 160 : PQExpBuffer buf = createPQExpBuffer();
3786 : PGresult *res;
3787 :
3788 : /* First collect database-specific options */
3789 160 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3790 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3791 : dboid);
3792 :
3793 160 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3794 :
3795 190 : for (int i = 0; i < PQntuples(res); i++)
3796 30 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3797 : "DATABASE", dbname, NULL, NULL,
3798 : outbuf);
3799 :
3800 160 : PQclear(res);
3801 :
3802 : /* Now look for role-and-database-specific options */
3803 160 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3804 : "FROM pg_db_role_setting s, pg_roles r "
3805 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3806 : dboid);
3807 :
3808 160 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3809 :
3810 160 : for (int i = 0; i < PQntuples(res); i++)
3811 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3812 0 : "ROLE", PQgetvalue(res, i, 0),
3813 : "DATABASE", dbname,
3814 : outbuf);
3815 :
3816 160 : PQclear(res);
3817 :
3818 160 : destroyPQExpBuffer(buf);
3819 160 : }
3820 :
3821 : /*
3822 : * dumpEncoding: put the correct encoding into the archive
3823 : */
3824 : static void
3825 259 : dumpEncoding(Archive *AH)
3826 : {
3827 259 : const char *encname = pg_encoding_to_char(AH->encoding);
3828 259 : PQExpBuffer qry = createPQExpBuffer();
3829 :
3830 259 : pg_log_info("saving encoding = %s", encname);
3831 :
3832 259 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3833 259 : appendStringLiteralAH(qry, encname, AH);
3834 259 : appendPQExpBufferStr(qry, ";\n");
3835 :
3836 259 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3837 259 : ARCHIVE_OPTS(.tag = "ENCODING",
3838 : .description = "ENCODING",
3839 : .section = SECTION_PRE_DATA,
3840 : .createStmt = qry->data));
3841 :
3842 259 : destroyPQExpBuffer(qry);
3843 259 : }
3844 :
3845 :
3846 : /*
3847 : * dumpStdStrings: put the correct escape string behavior into the archive
3848 : */
3849 : static void
3850 259 : dumpStdStrings(Archive *AH)
3851 : {
3852 259 : const char *stdstrings = AH->std_strings ? "on" : "off";
3853 259 : PQExpBuffer qry = createPQExpBuffer();
3854 :
3855 259 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3856 : stdstrings);
3857 :
3858 259 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3859 : stdstrings);
3860 :
3861 259 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3862 259 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3863 : .description = "STDSTRINGS",
3864 : .section = SECTION_PRE_DATA,
3865 : .createStmt = qry->data));
3866 :
3867 259 : destroyPQExpBuffer(qry);
3868 259 : }
3869 :
3870 : /*
3871 : * dumpSearchPath: record the active search_path in the archive
3872 : */
3873 : static void
3874 259 : dumpSearchPath(Archive *AH)
3875 : {
3876 259 : PQExpBuffer qry = createPQExpBuffer();
3877 259 : PQExpBuffer path = createPQExpBuffer();
3878 : PGresult *res;
3879 259 : char **schemanames = NULL;
3880 259 : int nschemanames = 0;
3881 : int i;
3882 :
3883 : /*
3884 : * We use the result of current_schemas(), not the search_path GUC,
3885 : * because that might contain wildcards such as "$user", which won't
3886 : * necessarily have the same value during restore. Also, this way avoids
3887 : * listing schemas that may appear in search_path but not actually exist,
3888 : * which seems like a prudent exclusion.
3889 : */
3890 259 : res = ExecuteSqlQueryForSingleRow(AH,
3891 : "SELECT pg_catalog.current_schemas(false)");
3892 :
3893 259 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3894 0 : pg_fatal("could not parse result of current_schemas()");
3895 :
3896 : /*
3897 : * We use set_config(), not a simple "SET search_path" command, because
3898 : * the latter has less-clean behavior if the search path is empty. While
3899 : * that's likely to get fixed at some point, it seems like a good idea to
3900 : * be as backwards-compatible as possible in what we put into archives.
3901 : */
3902 259 : for (i = 0; i < nschemanames; i++)
3903 : {
3904 0 : if (i > 0)
3905 0 : appendPQExpBufferStr(path, ", ");
3906 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3907 : }
3908 :
3909 259 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3910 259 : appendStringLiteralAH(qry, path->data, AH);
3911 259 : appendPQExpBufferStr(qry, ", false);\n");
3912 :
3913 259 : pg_log_info("saving \"search_path = %s\"", path->data);
3914 :
3915 259 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3916 259 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3917 : .description = "SEARCHPATH",
3918 : .section = SECTION_PRE_DATA,
3919 : .createStmt = qry->data));
3920 :
3921 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3922 259 : AH->searchpath = pg_strdup(qry->data);
3923 :
3924 259 : free(schemanames);
3925 259 : PQclear(res);
3926 259 : destroyPQExpBuffer(qry);
3927 259 : destroyPQExpBuffer(path);
3928 259 : }
3929 :
3930 :
3931 : /*
3932 : * getLOs:
3933 : * Collect schema-level data about large objects
3934 : */
3935 : static void
3936 231 : getLOs(Archive *fout)
3937 : {
3938 231 : DumpOptions *dopt = fout->dopt;
3939 231 : PQExpBuffer loQry = createPQExpBuffer();
3940 : PGresult *res;
3941 : int ntups;
3942 : int i;
3943 : int n;
3944 : int i_oid;
3945 : int i_lomowner;
3946 : int i_lomacl;
3947 : int i_acldefault;
3948 :
3949 231 : pg_log_info("reading large objects");
3950 :
3951 : /*
3952 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3953 : * with the same owner/ACL appear together.
3954 : */
3955 231 : appendPQExpBufferStr(loQry,
3956 : "SELECT oid, lomowner, lomacl, "
3957 : "acldefault('L', lomowner) AS acldefault "
3958 : "FROM pg_largeobject_metadata ");
3959 :
3960 : /*
3961 : * For binary upgrades, we transfer pg_largeobject_metadata via COPY or by
3962 : * copying/linking its files from the old cluster. On such upgrades, we
3963 : * only need to consider large objects that have comments or security
3964 : * labels, since we still restore those objects via COMMENT/SECURITY LABEL
3965 : * commands.
3966 : */
3967 231 : if (dopt->binary_upgrade)
3968 40 : appendPQExpBufferStr(loQry,
3969 : "WHERE oid IN "
3970 : "(SELECT objoid FROM pg_description "
3971 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
3972 : "UNION SELECT objoid FROM pg_seclabel "
3973 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ") ");
3974 :
3975 231 : appendPQExpBufferStr(loQry,
3976 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3977 :
3978 231 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3979 :
3980 231 : i_oid = PQfnumber(res, "oid");
3981 231 : i_lomowner = PQfnumber(res, "lomowner");
3982 231 : i_lomacl = PQfnumber(res, "lomacl");
3983 231 : i_acldefault = PQfnumber(res, "acldefault");
3984 :
3985 231 : ntups = PQntuples(res);
3986 :
3987 : /*
3988 : * Group the blobs into suitably-sized groups that have the same owner and
3989 : * ACL setting, and build a metadata and a data DumpableObject for each
3990 : * group. (If we supported initprivs for blobs, we'd have to insist that
3991 : * groups also share initprivs settings, since the DumpableObject only has
3992 : * room for one.) i is the index of the first tuple in the current group,
3993 : * and n is the number of tuples we include in the group.
3994 : */
3995 315 : for (i = 0; i < ntups; i += n)
3996 : {
3997 84 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3998 84 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3999 84 : char *thisacl = PQgetvalue(res, i, i_lomacl);
4000 : LoInfo *loinfo;
4001 : DumpableObject *lodata;
4002 : char namebuf[64];
4003 :
4004 : /* Scan to find first tuple not to be included in group */
4005 84 : n = 1;
4006 98 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4007 : {
4008 47 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4009 47 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4010 : break;
4011 14 : n++;
4012 : }
4013 :
4014 : /* Build the metadata DumpableObject */
4015 84 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4016 :
4017 84 : loinfo->dobj.objType = DO_LARGE_OBJECT;
4018 84 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4019 84 : loinfo->dobj.catId.oid = thisoid;
4020 84 : AssignDumpId(&loinfo->dobj);
4021 :
4022 84 : if (n > 1)
4023 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4024 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4025 : else
4026 74 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4027 84 : loinfo->dobj.name = pg_strdup(namebuf);
4028 84 : loinfo->dacl.acl = pg_strdup(thisacl);
4029 84 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4030 84 : loinfo->dacl.privtype = 0;
4031 84 : loinfo->dacl.initprivs = NULL;
4032 84 : loinfo->rolname = getRoleName(thisowner);
4033 84 : loinfo->numlos = n;
4034 84 : loinfo->looids[0] = thisoid;
4035 : /* Collect OIDs of the remaining blobs in this group */
4036 98 : for (int k = 1; k < n; k++)
4037 : {
4038 : CatalogId extraID;
4039 :
4040 14 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4041 :
4042 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4043 14 : extraID.tableoid = LargeObjectRelationId;
4044 14 : extraID.oid = loinfo->looids[k];
4045 14 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4046 : }
4047 :
4048 : /* LOs have data */
4049 84 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4050 :
4051 : /* Mark whether LO group has a non-empty ACL */
4052 84 : if (!PQgetisnull(res, i, i_lomacl))
4053 34 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4054 :
4055 : /*
4056 : * In binary upgrade mode, pg_largeobject and pg_largeobject_metadata
4057 : * are transferred via COPY or by copying/linking the files from the
4058 : * old cluster. Thus, we do not need to dump LO data, definitions, or
4059 : * ACLs.
4060 : */
4061 84 : if (dopt->binary_upgrade)
4062 7 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4063 :
4064 : /*
4065 : * Create a "BLOBS" data item for the group, too. This is just a
4066 : * placeholder for sorting; it carries no data now.
4067 : */
4068 84 : lodata = pg_malloc_object(DumpableObject);
4069 84 : lodata->objType = DO_LARGE_OBJECT_DATA;
4070 84 : lodata->catId = nilCatalogId;
4071 84 : AssignDumpId(lodata);
4072 84 : lodata->name = pg_strdup(namebuf);
4073 84 : lodata->components |= DUMP_COMPONENT_DATA;
4074 : /* Set up explicit dependency from data to metadata */
4075 84 : lodata->dependencies = pg_malloc_object(DumpId);
4076 84 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4077 84 : lodata->nDeps = lodata->allocDeps = 1;
4078 : }
4079 :
4080 231 : PQclear(res);
4081 231 : destroyPQExpBuffer(loQry);
4082 231 : }
4083 :
4084 : /*
4085 : * dumpLO
4086 : *
4087 : * dump the definition (metadata) of the given large object group
4088 : */
4089 : static void
4090 84 : dumpLO(Archive *fout, const LoInfo *loinfo)
4091 : {
4092 84 : PQExpBuffer cquery = createPQExpBuffer();
4093 :
4094 : /*
4095 : * The "definition" is just a newline-separated list of OIDs. We need to
4096 : * put something into the dropStmt too, but it can just be a comment.
4097 : */
4098 182 : for (int i = 0; i < loinfo->numlos; i++)
4099 98 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4100 :
4101 84 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4102 77 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4103 77 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4104 : .owner = loinfo->rolname,
4105 : .description = "BLOB METADATA",
4106 : .section = SECTION_DATA,
4107 : .createStmt = cquery->data,
4108 : .dropStmt = "-- dummy"));
4109 :
4110 : /*
4111 : * Dump per-blob comments and seclabels if any. We assume these are rare
4112 : * enough that it's okay to generate retail TOC entries for them.
4113 : */
4114 84 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4115 : DUMP_COMPONENT_SECLABEL))
4116 : {
4117 102 : for (int i = 0; i < loinfo->numlos; i++)
4118 : {
4119 : CatalogId catId;
4120 : char namebuf[32];
4121 :
4122 : /* Build identifying info for this blob */
4123 58 : catId.tableoid = loinfo->dobj.catId.tableoid;
4124 58 : catId.oid = loinfo->looids[i];
4125 58 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4126 :
4127 58 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4128 58 : dumpComment(fout, "LARGE OBJECT", namebuf,
4129 58 : NULL, loinfo->rolname,
4130 58 : catId, 0, loinfo->dobj.dumpId);
4131 :
4132 58 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4133 10 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4134 10 : NULL, loinfo->rolname,
4135 10 : catId, 0, loinfo->dobj.dumpId);
4136 : }
4137 : }
4138 :
4139 : /*
4140 : * Dump the ACLs if any (remember that all blobs in the group will have
4141 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4142 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4143 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4144 : * string to emit a mutated version for each blob.
4145 : */
4146 84 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4147 : {
4148 : char namebuf[32];
4149 :
4150 : /* Build identifying info for the first blob */
4151 33 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4152 :
4153 33 : if (loinfo->numlos > 1)
4154 : {
4155 : char tagbuf[64];
4156 :
4157 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4158 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4159 :
4160 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4161 : "LARGE OBJECT", namebuf, NULL, NULL,
4162 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4163 : }
4164 : else
4165 : {
4166 33 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4167 : "LARGE OBJECT", namebuf, NULL, NULL,
4168 33 : NULL, loinfo->rolname, &loinfo->dacl);
4169 : }
4170 : }
4171 :
4172 84 : destroyPQExpBuffer(cquery);
4173 84 : }
4174 :
4175 : /*
4176 : * dumpLOs:
4177 : * dump the data contents of the large objects in the given group
4178 : */
4179 : static int
4180 73 : dumpLOs(Archive *fout, const void *arg)
4181 : {
4182 73 : const LoInfo *loinfo = (const LoInfo *) arg;
4183 73 : PGconn *conn = GetConnection(fout);
4184 : char buf[LOBBUFSIZE];
4185 :
4186 73 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4187 :
4188 154 : for (int i = 0; i < loinfo->numlos; i++)
4189 : {
4190 81 : Oid loOid = loinfo->looids[i];
4191 : int loFd;
4192 : int cnt;
4193 :
4194 : /* Open the LO */
4195 81 : loFd = lo_open(conn, loOid, INV_READ);
4196 81 : if (loFd == -1)
4197 0 : pg_fatal("could not open large object %u: %s",
4198 : loOid, PQerrorMessage(conn));
4199 :
4200 81 : StartLO(fout, loOid);
4201 :
4202 : /* Now read it in chunks, sending data to archive */
4203 : do
4204 : {
4205 127 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4206 127 : if (cnt < 0)
4207 0 : pg_fatal("error reading large object %u: %s",
4208 : loOid, PQerrorMessage(conn));
4209 :
4210 127 : WriteData(fout, buf, cnt);
4211 127 : } while (cnt > 0);
4212 :
4213 81 : lo_close(conn, loFd);
4214 :
4215 81 : EndLO(fout, loOid);
4216 : }
4217 :
4218 73 : return 1;
4219 : }
4220 :
4221 : /*
4222 : * getPolicies
4223 : * get information about all RLS policies on dumpable tables.
4224 : */
4225 : void
4226 259 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4227 : {
4228 259 : DumpOptions *dopt = fout->dopt;
4229 : PQExpBuffer query;
4230 : PQExpBuffer tbloids;
4231 : PGresult *res;
4232 : PolicyInfo *polinfo;
4233 : int i_oid;
4234 : int i_tableoid;
4235 : int i_polrelid;
4236 : int i_polname;
4237 : int i_polcmd;
4238 : int i_polpermissive;
4239 : int i_polroles;
4240 : int i_polqual;
4241 : int i_polwithcheck;
4242 : int i,
4243 : j,
4244 : ntups;
4245 :
4246 : /* No policies before 9.5 */
4247 259 : if (fout->remoteVersion < 90500)
4248 0 : return;
4249 :
4250 : /* Skip if --no-policies was specified */
4251 259 : if (dopt->no_policies)
4252 1 : return;
4253 :
4254 258 : query = createPQExpBuffer();
4255 258 : tbloids = createPQExpBuffer();
4256 :
4257 : /*
4258 : * Identify tables of interest, and check which ones have RLS enabled.
4259 : */
4260 258 : appendPQExpBufferChar(tbloids, '{');
4261 69868 : for (i = 0; i < numTables; i++)
4262 : {
4263 69610 : TableInfo *tbinfo = &tblinfo[i];
4264 :
4265 : /* Ignore row security on tables not to be dumped */
4266 69610 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4267 62087 : continue;
4268 :
4269 : /* It can't have RLS or policies if it's not a table */
4270 7523 : if (tbinfo->relkind != RELKIND_RELATION &&
4271 2093 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4272 1482 : continue;
4273 :
4274 : /* Add it to the list of table OIDs to be probed below */
4275 6041 : if (tbloids->len > 1) /* do we have more than the '{'? */
4276 5866 : appendPQExpBufferChar(tbloids, ',');
4277 6041 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4278 :
4279 : /* Is RLS enabled? (That's separate from whether it has policies) */
4280 6041 : if (tbinfo->rowsec)
4281 : {
4282 63 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4283 :
4284 : /*
4285 : * We represent RLS being enabled on a table by creating a
4286 : * PolicyInfo object with null polname.
4287 : *
4288 : * Note: use tableoid 0 so that this object won't be mistaken for
4289 : * something that pg_depend entries apply to.
4290 : */
4291 63 : polinfo = pg_malloc_object(PolicyInfo);
4292 63 : polinfo->dobj.objType = DO_POLICY;
4293 63 : polinfo->dobj.catId.tableoid = 0;
4294 63 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4295 63 : AssignDumpId(&polinfo->dobj);
4296 63 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4297 63 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4298 63 : polinfo->poltable = tbinfo;
4299 63 : polinfo->polname = NULL;
4300 63 : polinfo->polcmd = '\0';
4301 63 : polinfo->polpermissive = 0;
4302 63 : polinfo->polroles = NULL;
4303 63 : polinfo->polqual = NULL;
4304 63 : polinfo->polwithcheck = NULL;
4305 : }
4306 : }
4307 258 : appendPQExpBufferChar(tbloids, '}');
4308 :
4309 : /*
4310 : * Now, read all RLS policies belonging to the tables of interest, and
4311 : * create PolicyInfo objects for them. (Note that we must filter the
4312 : * results server-side not locally, because we dare not apply pg_get_expr
4313 : * to tables we don't have lock on.)
4314 : */
4315 258 : pg_log_info("reading row-level security policies");
4316 :
4317 258 : printfPQExpBuffer(query,
4318 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4319 258 : if (fout->remoteVersion >= 100000)
4320 258 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4321 : else
4322 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4323 258 : appendPQExpBuffer(query,
4324 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4325 : " 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, "
4326 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4327 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4328 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4329 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4330 : tbloids->data);
4331 :
4332 258 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4333 :
4334 258 : ntups = PQntuples(res);
4335 258 : if (ntups > 0)
4336 : {
4337 43 : i_oid = PQfnumber(res, "oid");
4338 43 : i_tableoid = PQfnumber(res, "tableoid");
4339 43 : i_polrelid = PQfnumber(res, "polrelid");
4340 43 : i_polname = PQfnumber(res, "polname");
4341 43 : i_polcmd = PQfnumber(res, "polcmd");
4342 43 : i_polpermissive = PQfnumber(res, "polpermissive");
4343 43 : i_polroles = PQfnumber(res, "polroles");
4344 43 : i_polqual = PQfnumber(res, "polqual");
4345 43 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4346 :
4347 43 : polinfo = pg_malloc_array(PolicyInfo, ntups);
4348 :
4349 316 : for (j = 0; j < ntups; j++)
4350 : {
4351 273 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4352 273 : TableInfo *tbinfo = findTableByOid(polrelid);
4353 :
4354 273 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4355 :
4356 273 : polinfo[j].dobj.objType = DO_POLICY;
4357 273 : polinfo[j].dobj.catId.tableoid =
4358 273 : atooid(PQgetvalue(res, j, i_tableoid));
4359 273 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4360 273 : AssignDumpId(&polinfo[j].dobj);
4361 273 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4362 273 : polinfo[j].poltable = tbinfo;
4363 273 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4364 273 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4365 :
4366 273 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4367 273 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4368 :
4369 273 : if (PQgetisnull(res, j, i_polroles))
4370 121 : polinfo[j].polroles = NULL;
4371 : else
4372 152 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4373 :
4374 273 : if (PQgetisnull(res, j, i_polqual))
4375 38 : polinfo[j].polqual = NULL;
4376 : else
4377 235 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4378 :
4379 273 : if (PQgetisnull(res, j, i_polwithcheck))
4380 144 : polinfo[j].polwithcheck = NULL;
4381 : else
4382 129 : polinfo[j].polwithcheck
4383 129 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4384 : }
4385 : }
4386 :
4387 258 : PQclear(res);
4388 :
4389 258 : destroyPQExpBuffer(query);
4390 258 : destroyPQExpBuffer(tbloids);
4391 : }
4392 :
4393 : /*
4394 : * dumpPolicy
4395 : * dump the definition of the given policy
4396 : */
4397 : static void
4398 336 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4399 : {
4400 336 : DumpOptions *dopt = fout->dopt;
4401 336 : TableInfo *tbinfo = polinfo->poltable;
4402 : PQExpBuffer query;
4403 : PQExpBuffer delqry;
4404 : PQExpBuffer polprefix;
4405 : char *qtabname;
4406 : const char *cmd;
4407 : char *tag;
4408 :
4409 : /* Do nothing if not dumping schema */
4410 336 : if (!dopt->dumpSchema)
4411 49 : return;
4412 :
4413 : /*
4414 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4415 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4416 : * ROW LEVEL SECURITY.
4417 : */
4418 287 : if (polinfo->polname == NULL)
4419 : {
4420 56 : query = createPQExpBuffer();
4421 :
4422 56 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4423 56 : fmtQualifiedDumpable(tbinfo));
4424 :
4425 : /*
4426 : * We must emit the ROW SECURITY object's dependency on its table
4427 : * explicitly, because it will not match anything in pg_depend (unlike
4428 : * the case for other PolicyInfo objects).
4429 : */
4430 56 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4431 56 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4432 56 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4433 : .namespace = polinfo->dobj.namespace->dobj.name,
4434 : .owner = tbinfo->rolname,
4435 : .description = "ROW SECURITY",
4436 : .section = SECTION_POST_DATA,
4437 : .createStmt = query->data,
4438 : .deps = &(tbinfo->dobj.dumpId),
4439 : .nDeps = 1));
4440 :
4441 56 : destroyPQExpBuffer(query);
4442 56 : return;
4443 : }
4444 :
4445 231 : if (polinfo->polcmd == '*')
4446 77 : cmd = "";
4447 154 : else if (polinfo->polcmd == 'r')
4448 41 : cmd = " FOR SELECT";
4449 113 : else if (polinfo->polcmd == 'a')
4450 31 : cmd = " FOR INSERT";
4451 82 : else if (polinfo->polcmd == 'w')
4452 41 : cmd = " FOR UPDATE";
4453 41 : else if (polinfo->polcmd == 'd')
4454 41 : cmd = " FOR DELETE";
4455 : else
4456 0 : pg_fatal("unexpected policy command type: %c",
4457 : polinfo->polcmd);
4458 :
4459 231 : query = createPQExpBuffer();
4460 231 : delqry = createPQExpBuffer();
4461 231 : polprefix = createPQExpBuffer();
4462 :
4463 231 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4464 :
4465 231 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4466 :
4467 231 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4468 231 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4469 :
4470 231 : if (polinfo->polroles != NULL)
4471 124 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4472 :
4473 231 : if (polinfo->polqual != NULL)
4474 200 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4475 :
4476 231 : if (polinfo->polwithcheck != NULL)
4477 108 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4478 :
4479 231 : appendPQExpBufferStr(query, ";\n");
4480 :
4481 231 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4482 231 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4483 :
4484 231 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4485 231 : fmtId(polinfo->polname));
4486 :
4487 231 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4488 :
4489 231 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4490 231 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4491 231 : ARCHIVE_OPTS(.tag = tag,
4492 : .namespace = polinfo->dobj.namespace->dobj.name,
4493 : .owner = tbinfo->rolname,
4494 : .description = "POLICY",
4495 : .section = SECTION_POST_DATA,
4496 : .createStmt = query->data,
4497 : .dropStmt = delqry->data));
4498 :
4499 231 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4500 31 : dumpComment(fout, polprefix->data, qtabname,
4501 31 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4502 31 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4503 :
4504 231 : free(tag);
4505 231 : destroyPQExpBuffer(query);
4506 231 : destroyPQExpBuffer(delqry);
4507 231 : destroyPQExpBuffer(polprefix);
4508 231 : free(qtabname);
4509 : }
4510 :
4511 : /*
4512 : * getPublications
4513 : * get information about publications
4514 : */
4515 : void
4516 259 : getPublications(Archive *fout)
4517 : {
4518 259 : DumpOptions *dopt = fout->dopt;
4519 : PQExpBuffer query;
4520 : PGresult *res;
4521 : PublicationInfo *pubinfo;
4522 : int i_tableoid;
4523 : int i_oid;
4524 : int i_pubname;
4525 : int i_pubowner;
4526 : int i_puballtables;
4527 : int i_puballsequences;
4528 : int i_pubinsert;
4529 : int i_pubupdate;
4530 : int i_pubdelete;
4531 : int i_pubtruncate;
4532 : int i_pubviaroot;
4533 : int i_pubgencols;
4534 : int i,
4535 : ntups;
4536 :
4537 259 : if (dopt->no_publications || fout->remoteVersion < 100000)
4538 0 : return;
4539 :
4540 259 : query = createPQExpBuffer();
4541 :
4542 : /* Get the publications. */
4543 259 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4544 : "p.pubowner, p.puballtables, p.pubinsert, "
4545 : "p.pubupdate, p.pubdelete, ");
4546 :
4547 259 : if (fout->remoteVersion >= 110000)
4548 259 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4549 : else
4550 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4551 :
4552 259 : if (fout->remoteVersion >= 130000)
4553 259 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4554 : else
4555 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4556 :
4557 259 : if (fout->remoteVersion >= 180000)
4558 259 : appendPQExpBufferStr(query, "p.pubgencols, ");
4559 : else
4560 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4561 :
4562 259 : if (fout->remoteVersion >= 190000)
4563 259 : appendPQExpBufferStr(query, "p.puballsequences ");
4564 : else
4565 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4566 :
4567 259 : appendPQExpBufferStr(query, "FROM pg_publication p");
4568 :
4569 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4570 :
4571 259 : ntups = PQntuples(res);
4572 :
4573 259 : if (ntups == 0)
4574 206 : goto cleanup;
4575 :
4576 53 : i_tableoid = PQfnumber(res, "tableoid");
4577 53 : i_oid = PQfnumber(res, "oid");
4578 53 : i_pubname = PQfnumber(res, "pubname");
4579 53 : i_pubowner = PQfnumber(res, "pubowner");
4580 53 : i_puballtables = PQfnumber(res, "puballtables");
4581 53 : i_puballsequences = PQfnumber(res, "puballsequences");
4582 53 : i_pubinsert = PQfnumber(res, "pubinsert");
4583 53 : i_pubupdate = PQfnumber(res, "pubupdate");
4584 53 : i_pubdelete = PQfnumber(res, "pubdelete");
4585 53 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4586 53 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4587 53 : i_pubgencols = PQfnumber(res, "pubgencols");
4588 :
4589 53 : pubinfo = pg_malloc_array(PublicationInfo, ntups);
4590 :
4591 539 : for (i = 0; i < ntups; i++)
4592 : {
4593 486 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4594 486 : pubinfo[i].dobj.catId.tableoid =
4595 486 : atooid(PQgetvalue(res, i, i_tableoid));
4596 486 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4597 486 : AssignDumpId(&pubinfo[i].dobj);
4598 486 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4599 486 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4600 486 : pubinfo[i].puballtables =
4601 486 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4602 486 : pubinfo[i].puballsequences =
4603 486 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4604 486 : pubinfo[i].pubinsert =
4605 486 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4606 486 : pubinfo[i].pubupdate =
4607 486 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4608 486 : pubinfo[i].pubdelete =
4609 486 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4610 486 : pubinfo[i].pubtruncate =
4611 486 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4612 486 : pubinfo[i].pubviaroot =
4613 486 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4614 486 : pubinfo[i].pubgencols_type =
4615 486 : *(PQgetvalue(res, i, i_pubgencols));
4616 486 : pubinfo[i].except_tables = (SimplePtrList)
4617 : {
4618 : NULL, NULL
4619 : };
4620 :
4621 : /* Decide whether we want to dump it */
4622 486 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4623 :
4624 : /*
4625 : * Get the list of tables for publications specified in the EXCEPT
4626 : * TABLE clause.
4627 : *
4628 : * Although individual EXCEPT TABLE entries could be stored in
4629 : * PublicationRelInfo, dumpPublicationTable cannot be used to emit
4630 : * them, because there is no ALTER PUBLICATION ... ADD command to add
4631 : * individual table entries to the EXCEPT TABLE list.
4632 : *
4633 : * Therefore, the approach is to dump the complete EXCEPT TABLE list
4634 : * in a single CREATE PUBLICATION statement. PublicationInfo is used
4635 : * to collect this information, which is then emitted by
4636 : * dumpPublication().
4637 : */
4638 486 : if (fout->remoteVersion >= 190000)
4639 : {
4640 : int ntbls;
4641 : PGresult *res_tbls;
4642 :
4643 486 : resetPQExpBuffer(query);
4644 486 : appendPQExpBuffer(query,
4645 : "SELECT prrelid\n"
4646 : "FROM pg_catalog.pg_publication_rel\n"
4647 : "WHERE prpubid = %u AND prexcept",
4648 486 : pubinfo[i].dobj.catId.oid);
4649 :
4650 486 : res_tbls = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4651 :
4652 486 : ntbls = PQntuples(res_tbls);
4653 :
4654 711 : for (int j = 0; j < ntbls; j++)
4655 : {
4656 : Oid prrelid;
4657 : TableInfo *tbinfo;
4658 :
4659 225 : prrelid = atooid(PQgetvalue(res_tbls, j, 0));
4660 :
4661 225 : tbinfo = findTableByOid(prrelid);
4662 :
4663 225 : if (tbinfo != NULL)
4664 225 : simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
4665 : }
4666 :
4667 486 : PQclear(res_tbls);
4668 : }
4669 : }
4670 :
4671 53 : cleanup:
4672 259 : PQclear(res);
4673 :
4674 259 : destroyPQExpBuffer(query);
4675 : }
4676 :
4677 : /*
4678 : * dumpPublication
4679 : * dump the definition of the given publication
4680 : */
4681 : static void
4682 396 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4683 : {
4684 396 : DumpOptions *dopt = fout->dopt;
4685 : PQExpBuffer delq;
4686 : PQExpBuffer query;
4687 : char *qpubname;
4688 396 : bool first = true;
4689 :
4690 : /* Do nothing if not dumping schema */
4691 396 : if (!dopt->dumpSchema)
4692 60 : return;
4693 :
4694 336 : delq = createPQExpBuffer();
4695 336 : query = createPQExpBuffer();
4696 :
4697 336 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4698 :
4699 336 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4700 : qpubname);
4701 :
4702 336 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4703 : qpubname);
4704 :
4705 336 : if (pubinfo->puballtables)
4706 : {
4707 156 : int n_except = 0;
4708 :
4709 156 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4710 :
4711 : /* Include EXCEPT TABLE clause if there are except_tables. */
4712 311 : for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
4713 : {
4714 155 : TableInfo *tbinfo = (TableInfo *) cell->ptr;
4715 :
4716 155 : if (++n_except == 1)
4717 93 : appendPQExpBufferStr(query, " EXCEPT TABLE (");
4718 : else
4719 62 : appendPQExpBufferStr(query, ", ");
4720 155 : appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
4721 : }
4722 156 : if (n_except > 0)
4723 93 : appendPQExpBufferStr(query, ")");
4724 :
4725 156 : if (pubinfo->puballsequences)
4726 31 : appendPQExpBufferStr(query, ", ALL SEQUENCES");
4727 : }
4728 180 : else if (pubinfo->puballsequences)
4729 31 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4730 :
4731 336 : appendPQExpBufferStr(query, " WITH (publish = '");
4732 336 : if (pubinfo->pubinsert)
4733 : {
4734 274 : appendPQExpBufferStr(query, "insert");
4735 274 : first = false;
4736 : }
4737 :
4738 336 : if (pubinfo->pubupdate)
4739 : {
4740 274 : if (!first)
4741 274 : appendPQExpBufferStr(query, ", ");
4742 :
4743 274 : appendPQExpBufferStr(query, "update");
4744 274 : first = false;
4745 : }
4746 :
4747 336 : if (pubinfo->pubdelete)
4748 : {
4749 274 : if (!first)
4750 274 : appendPQExpBufferStr(query, ", ");
4751 :
4752 274 : appendPQExpBufferStr(query, "delete");
4753 274 : first = false;
4754 : }
4755 :
4756 336 : if (pubinfo->pubtruncate)
4757 : {
4758 274 : if (!first)
4759 274 : appendPQExpBufferStr(query, ", ");
4760 :
4761 274 : appendPQExpBufferStr(query, "truncate");
4762 274 : first = false;
4763 : }
4764 :
4765 336 : appendPQExpBufferChar(query, '\'');
4766 :
4767 336 : if (pubinfo->pubviaroot)
4768 5 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4769 :
4770 336 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4771 31 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4772 :
4773 336 : appendPQExpBufferStr(query, ");\n");
4774 :
4775 336 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4776 336 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4777 336 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4778 : .owner = pubinfo->rolname,
4779 : .description = "PUBLICATION",
4780 : .section = SECTION_POST_DATA,
4781 : .createStmt = query->data,
4782 : .dropStmt = delq->data));
4783 :
4784 336 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4785 31 : dumpComment(fout, "PUBLICATION", qpubname,
4786 31 : NULL, pubinfo->rolname,
4787 31 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4788 :
4789 336 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4790 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4791 0 : NULL, pubinfo->rolname,
4792 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4793 :
4794 336 : destroyPQExpBuffer(delq);
4795 336 : destroyPQExpBuffer(query);
4796 336 : free(qpubname);
4797 : }
4798 :
4799 : /*
4800 : * getPublicationNamespaces
4801 : * get information about publication membership for dumpable schemas.
4802 : */
4803 : void
4804 259 : getPublicationNamespaces(Archive *fout)
4805 : {
4806 : PQExpBuffer query;
4807 : PGresult *res;
4808 : PublicationSchemaInfo *pubsinfo;
4809 259 : DumpOptions *dopt = fout->dopt;
4810 : int i_tableoid;
4811 : int i_oid;
4812 : int i_pnpubid;
4813 : int i_pnnspid;
4814 : int i,
4815 : j,
4816 : ntups;
4817 :
4818 259 : if (dopt->no_publications || fout->remoteVersion < 150000)
4819 0 : return;
4820 :
4821 259 : query = createPQExpBuffer();
4822 :
4823 : /* Collect all publication membership info. */
4824 259 : appendPQExpBufferStr(query,
4825 : "SELECT tableoid, oid, pnpubid, pnnspid "
4826 : "FROM pg_catalog.pg_publication_namespace");
4827 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4828 :
4829 259 : ntups = PQntuples(res);
4830 :
4831 259 : i_tableoid = PQfnumber(res, "tableoid");
4832 259 : i_oid = PQfnumber(res, "oid");
4833 259 : i_pnpubid = PQfnumber(res, "pnpubid");
4834 259 : i_pnnspid = PQfnumber(res, "pnnspid");
4835 :
4836 : /* this allocation may be more than we need */
4837 259 : pubsinfo = pg_malloc_array(PublicationSchemaInfo, ntups);
4838 259 : j = 0;
4839 :
4840 384 : for (i = 0; i < ntups; i++)
4841 : {
4842 125 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4843 125 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4844 : PublicationInfo *pubinfo;
4845 : NamespaceInfo *nspinfo;
4846 :
4847 : /*
4848 : * Ignore any entries for which we aren't interested in either the
4849 : * publication or the rel.
4850 : */
4851 125 : pubinfo = findPublicationByOid(pnpubid);
4852 125 : if (pubinfo == NULL)
4853 0 : continue;
4854 125 : nspinfo = findNamespaceByOid(pnnspid);
4855 125 : if (nspinfo == NULL)
4856 0 : continue;
4857 :
4858 : /* OK, make a DumpableObject for this relationship */
4859 125 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4860 125 : pubsinfo[j].dobj.catId.tableoid =
4861 125 : atooid(PQgetvalue(res, i, i_tableoid));
4862 125 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4863 125 : AssignDumpId(&pubsinfo[j].dobj);
4864 125 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4865 125 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4866 125 : pubsinfo[j].publication = pubinfo;
4867 125 : pubsinfo[j].pubschema = nspinfo;
4868 :
4869 : /* Decide whether we want to dump it */
4870 125 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4871 :
4872 125 : j++;
4873 : }
4874 :
4875 259 : PQclear(res);
4876 259 : destroyPQExpBuffer(query);
4877 : }
4878 :
4879 : /*
4880 : * getPublicationTables
4881 : * get information about publication membership for dumpable tables.
4882 : */
4883 : void
4884 259 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4885 : {
4886 : PQExpBuffer query;
4887 : PGresult *res;
4888 : PublicationRelInfo *pubrinfo;
4889 259 : DumpOptions *dopt = fout->dopt;
4890 : int i_tableoid;
4891 : int i_oid;
4892 : int i_prpubid;
4893 : int i_prrelid;
4894 : int i_prrelqual;
4895 : int i_prattrs;
4896 : int i,
4897 : j,
4898 : ntups;
4899 :
4900 259 : if (dopt->no_publications || fout->remoteVersion < 100000)
4901 0 : return;
4902 :
4903 259 : query = createPQExpBuffer();
4904 :
4905 : /* Collect all publication membership info. */
4906 259 : if (fout->remoteVersion >= 150000)
4907 : {
4908 259 : appendPQExpBufferStr(query,
4909 : "SELECT tableoid, oid, prpubid, prrelid, "
4910 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4911 : "(CASE\n"
4912 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4913 : " (SELECT array_agg(attname)\n"
4914 : " FROM\n"
4915 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4916 : " pg_catalog.pg_attribute\n"
4917 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4918 : " ELSE NULL END) prattrs "
4919 : "FROM pg_catalog.pg_publication_rel pr");
4920 259 : if (fout->remoteVersion >= 190000)
4921 259 : appendPQExpBufferStr(query, " WHERE NOT pr.prexcept");
4922 : }
4923 : else
4924 0 : appendPQExpBufferStr(query,
4925 : "SELECT tableoid, oid, prpubid, prrelid, "
4926 : "NULL AS prrelqual, NULL AS prattrs "
4927 : "FROM pg_catalog.pg_publication_rel");
4928 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4929 :
4930 259 : ntups = PQntuples(res);
4931 :
4932 259 : i_tableoid = PQfnumber(res, "tableoid");
4933 259 : i_oid = PQfnumber(res, "oid");
4934 259 : i_prpubid = PQfnumber(res, "prpubid");
4935 259 : i_prrelid = PQfnumber(res, "prrelid");
4936 259 : i_prrelqual = PQfnumber(res, "prrelqual");
4937 259 : i_prattrs = PQfnumber(res, "prattrs");
4938 :
4939 : /* this allocation may be more than we need */
4940 259 : pubrinfo = pg_malloc_array(PublicationRelInfo, ntups);
4941 259 : j = 0;
4942 :
4943 609 : for (i = 0; i < ntups; i++)
4944 : {
4945 350 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4946 350 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4947 : PublicationInfo *pubinfo;
4948 : TableInfo *tbinfo;
4949 :
4950 : /*
4951 : * Ignore any entries for which we aren't interested in either the
4952 : * publication or the rel.
4953 : */
4954 350 : pubinfo = findPublicationByOid(prpubid);
4955 350 : if (pubinfo == NULL)
4956 0 : continue;
4957 350 : tbinfo = findTableByOid(prrelid);
4958 350 : if (tbinfo == NULL)
4959 0 : continue;
4960 :
4961 : /* OK, make a DumpableObject for this relationship */
4962 350 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4963 350 : pubrinfo[j].dobj.catId.tableoid =
4964 350 : atooid(PQgetvalue(res, i, i_tableoid));
4965 350 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4966 350 : AssignDumpId(&pubrinfo[j].dobj);
4967 350 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4968 350 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4969 350 : pubrinfo[j].publication = pubinfo;
4970 350 : pubrinfo[j].pubtable = tbinfo;
4971 350 : if (PQgetisnull(res, i, i_prrelqual))
4972 194 : pubrinfo[j].pubrelqual = NULL;
4973 : else
4974 156 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4975 :
4976 350 : if (!PQgetisnull(res, i, i_prattrs))
4977 : {
4978 : char **attnames;
4979 : int nattnames;
4980 : PQExpBuffer attribs;
4981 :
4982 111 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4983 : &attnames, &nattnames))
4984 0 : pg_fatal("could not parse %s array", "prattrs");
4985 111 : attribs = createPQExpBuffer();
4986 319 : for (int k = 0; k < nattnames; k++)
4987 : {
4988 208 : if (k > 0)
4989 97 : appendPQExpBufferStr(attribs, ", ");
4990 :
4991 208 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4992 : }
4993 111 : pubrinfo[j].pubrattrs = attribs->data;
4994 111 : free(attribs); /* but not attribs->data */
4995 111 : free(attnames);
4996 : }
4997 : else
4998 239 : pubrinfo[j].pubrattrs = NULL;
4999 :
5000 : /* Decide whether we want to dump it */
5001 350 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
5002 :
5003 350 : j++;
5004 : }
5005 :
5006 259 : PQclear(res);
5007 259 : destroyPQExpBuffer(query);
5008 : }
5009 :
5010 : /*
5011 : * dumpPublicationNamespace
5012 : * dump the definition of the given publication schema mapping.
5013 : */
5014 : static void
5015 99 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
5016 : {
5017 99 : DumpOptions *dopt = fout->dopt;
5018 99 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
5019 99 : PublicationInfo *pubinfo = pubsinfo->publication;
5020 : PQExpBuffer query;
5021 : char *tag;
5022 :
5023 : /* Do nothing if not dumping schema */
5024 99 : if (!dopt->dumpSchema)
5025 12 : return;
5026 :
5027 87 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
5028 :
5029 87 : query = createPQExpBuffer();
5030 :
5031 87 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
5032 87 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
5033 :
5034 : /*
5035 : * There is no point in creating drop query as the drop is done by schema
5036 : * drop.
5037 : */
5038 87 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5039 87 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
5040 87 : ARCHIVE_OPTS(.tag = tag,
5041 : .namespace = schemainfo->dobj.name,
5042 : .owner = pubinfo->rolname,
5043 : .description = "PUBLICATION TABLES IN SCHEMA",
5044 : .section = SECTION_POST_DATA,
5045 : .createStmt = query->data));
5046 :
5047 : /* These objects can't currently have comments or seclabels */
5048 :
5049 87 : free(tag);
5050 87 : destroyPQExpBuffer(query);
5051 : }
5052 :
5053 : /*
5054 : * dumpPublicationTable
5055 : * dump the definition of the given publication table mapping
5056 : */
5057 : static void
5058 284 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
5059 : {
5060 284 : DumpOptions *dopt = fout->dopt;
5061 284 : PublicationInfo *pubinfo = pubrinfo->publication;
5062 284 : TableInfo *tbinfo = pubrinfo->pubtable;
5063 : PQExpBuffer query;
5064 : char *tag;
5065 :
5066 : /* Do nothing if not dumping schema */
5067 284 : if (!dopt->dumpSchema)
5068 42 : return;
5069 :
5070 242 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5071 :
5072 242 : query = createPQExpBuffer();
5073 :
5074 242 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5075 242 : fmtId(pubinfo->dobj.name));
5076 242 : appendPQExpBuffer(query, " %s",
5077 242 : fmtQualifiedDumpable(tbinfo));
5078 :
5079 242 : if (pubrinfo->pubrattrs)
5080 77 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5081 :
5082 242 : if (pubrinfo->pubrelqual)
5083 : {
5084 : /*
5085 : * It's necessary to add parentheses around the expression because
5086 : * pg_get_expr won't supply the parentheses for things like WHERE
5087 : * TRUE.
5088 : */
5089 108 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5090 : }
5091 242 : appendPQExpBufferStr(query, ";\n");
5092 :
5093 : /*
5094 : * There is no point in creating a drop query as the drop is done by table
5095 : * drop. (If you think to change this, see also _printTocEntry().)
5096 : * Although this object doesn't really have ownership as such, set the
5097 : * owner field anyway to ensure that the command is run by the correct
5098 : * role at restore time.
5099 : */
5100 242 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5101 242 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5102 242 : ARCHIVE_OPTS(.tag = tag,
5103 : .namespace = tbinfo->dobj.namespace->dobj.name,
5104 : .owner = pubinfo->rolname,
5105 : .description = "PUBLICATION TABLE",
5106 : .section = SECTION_POST_DATA,
5107 : .createStmt = query->data));
5108 :
5109 : /* These objects can't currently have comments or seclabels */
5110 :
5111 242 : free(tag);
5112 242 : destroyPQExpBuffer(query);
5113 : }
5114 :
5115 : /*
5116 : * Is the currently connected user a superuser?
5117 : */
5118 : static bool
5119 258 : is_superuser(Archive *fout)
5120 : {
5121 258 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5122 : const char *val;
5123 :
5124 258 : val = PQparameterStatus(AH->connection, "is_superuser");
5125 :
5126 258 : if (val && strcmp(val, "on") == 0)
5127 255 : return true;
5128 :
5129 3 : return false;
5130 : }
5131 :
5132 : /*
5133 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5134 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5135 : * the setting query is effective only where available.
5136 : */
5137 : static void
5138 293 : set_restrict_relation_kind(Archive *AH, const char *value)
5139 : {
5140 293 : PQExpBuffer query = createPQExpBuffer();
5141 : PGresult *res;
5142 :
5143 293 : appendPQExpBuffer(query,
5144 : "SELECT set_config(name, '%s', false) "
5145 : "FROM pg_settings "
5146 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5147 : value);
5148 293 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5149 :
5150 293 : PQclear(res);
5151 293 : destroyPQExpBuffer(query);
5152 293 : }
5153 :
5154 : /*
5155 : * getSubscriptions
5156 : * get information about subscriptions
5157 : */
5158 : void
5159 259 : getSubscriptions(Archive *fout)
5160 : {
5161 259 : DumpOptions *dopt = fout->dopt;
5162 : PQExpBuffer query;
5163 : PGresult *res;
5164 : SubscriptionInfo *subinfo;
5165 : int i_tableoid;
5166 : int i_oid;
5167 : int i_subname;
5168 : int i_subowner;
5169 : int i_subbinary;
5170 : int i_substream;
5171 : int i_subtwophasestate;
5172 : int i_subdisableonerr;
5173 : int i_subpasswordrequired;
5174 : int i_subrunasowner;
5175 : int i_subservername;
5176 : int i_subconninfo;
5177 : int i_subslotname;
5178 : int i_subsynccommit;
5179 : int i_subwalrcvtimeout;
5180 : int i_subpublications;
5181 : int i_suborigin;
5182 : int i_suboriginremotelsn;
5183 : int i_subenabled;
5184 : int i_subfailover;
5185 : int i_subretaindeadtuples;
5186 : int i_submaxretention;
5187 : int i,
5188 : ntups;
5189 :
5190 259 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5191 1 : return;
5192 :
5193 258 : if (!is_superuser(fout))
5194 : {
5195 : int n;
5196 :
5197 3 : res = ExecuteSqlQuery(fout,
5198 : "SELECT count(*) FROM pg_subscription "
5199 : "WHERE subdbid = (SELECT oid FROM pg_database"
5200 : " WHERE datname = current_database())",
5201 : PGRES_TUPLES_OK);
5202 3 : n = atoi(PQgetvalue(res, 0, 0));
5203 3 : if (n > 0)
5204 2 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5205 3 : PQclear(res);
5206 3 : return;
5207 : }
5208 :
5209 255 : query = createPQExpBuffer();
5210 :
5211 : /* Get the subscriptions in current database. */
5212 255 : appendPQExpBufferStr(query,
5213 : "SELECT s.tableoid, s.oid, s.subname,\n"
5214 : " s.subowner,\n"
5215 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5216 : " s.subpublications,\n");
5217 :
5218 255 : if (fout->remoteVersion >= 140000)
5219 255 : appendPQExpBufferStr(query, " s.subbinary,\n");
5220 : else
5221 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5222 :
5223 255 : if (fout->remoteVersion >= 140000)
5224 255 : appendPQExpBufferStr(query, " s.substream,\n");
5225 : else
5226 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5227 :
5228 255 : if (fout->remoteVersion >= 150000)
5229 255 : appendPQExpBufferStr(query,
5230 : " s.subtwophasestate,\n"
5231 : " s.subdisableonerr,\n");
5232 : else
5233 0 : appendPQExpBuffer(query,
5234 : " '%c' AS subtwophasestate,\n"
5235 : " false AS subdisableonerr,\n",
5236 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5237 :
5238 255 : if (fout->remoteVersion >= 160000)
5239 255 : appendPQExpBufferStr(query,
5240 : " s.subpasswordrequired,\n"
5241 : " s.subrunasowner,\n"
5242 : " s.suborigin,\n");
5243 : else
5244 0 : appendPQExpBuffer(query,
5245 : " 't' AS subpasswordrequired,\n"
5246 : " 't' AS subrunasowner,\n"
5247 : " '%s' AS suborigin,\n",
5248 : LOGICALREP_ORIGIN_ANY);
5249 :
5250 255 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5251 40 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5252 : " s.subenabled,\n");
5253 : else
5254 215 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5255 : " false AS subenabled,\n");
5256 :
5257 255 : if (fout->remoteVersion >= 170000)
5258 255 : appendPQExpBufferStr(query,
5259 : " s.subfailover,\n");
5260 : else
5261 0 : appendPQExpBufferStr(query,
5262 : " false AS subfailover,\n");
5263 :
5264 255 : if (fout->remoteVersion >= 190000)
5265 255 : appendPQExpBufferStr(query,
5266 : " s.subretaindeadtuples,\n");
5267 : else
5268 0 : appendPQExpBufferStr(query,
5269 : " false AS subretaindeadtuples,\n");
5270 :
5271 255 : if (fout->remoteVersion >= 190000)
5272 255 : appendPQExpBufferStr(query,
5273 : " s.submaxretention,\n");
5274 : else
5275 0 : appendPQExpBuffer(query,
5276 : " 0 AS submaxretention,\n");
5277 :
5278 255 : if (fout->remoteVersion >= 190000)
5279 255 : appendPQExpBufferStr(query,
5280 : " s.subwalrcvtimeout,\n");
5281 : else
5282 0 : appendPQExpBufferStr(query,
5283 : " '-1' AS subwalrcvtimeout,\n");
5284 :
5285 255 : if (fout->remoteVersion >= 190000)
5286 255 : appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5287 : else
5288 0 : appendPQExpBufferStr(query, " NULL AS subservername\n");
5289 :
5290 255 : appendPQExpBufferStr(query,
5291 : "FROM pg_subscription s\n");
5292 :
5293 255 : if (fout->remoteVersion >= 190000)
5294 255 : appendPQExpBufferStr(query,
5295 : "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5296 : " ON fs.oid = s.subserver \n");
5297 :
5298 255 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5299 40 : appendPQExpBufferStr(query,
5300 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5301 : " ON o.external_id = 'pg_' || s.oid::text \n");
5302 :
5303 255 : appendPQExpBufferStr(query,
5304 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5305 : " WHERE datname = current_database())");
5306 :
5307 255 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5308 :
5309 255 : ntups = PQntuples(res);
5310 :
5311 : /*
5312 : * Get subscription fields. We don't include subskiplsn in the dump as
5313 : * after restoring the dump this value may no longer be relevant.
5314 : */
5315 255 : i_tableoid = PQfnumber(res, "tableoid");
5316 255 : i_oid = PQfnumber(res, "oid");
5317 255 : i_subname = PQfnumber(res, "subname");
5318 255 : i_subowner = PQfnumber(res, "subowner");
5319 255 : i_subenabled = PQfnumber(res, "subenabled");
5320 255 : i_subbinary = PQfnumber(res, "subbinary");
5321 255 : i_substream = PQfnumber(res, "substream");
5322 255 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5323 255 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5324 255 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5325 255 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5326 255 : i_subfailover = PQfnumber(res, "subfailover");
5327 255 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5328 255 : i_submaxretention = PQfnumber(res, "submaxretention");
5329 255 : i_subservername = PQfnumber(res, "subservername");
5330 255 : i_subconninfo = PQfnumber(res, "subconninfo");
5331 255 : i_subslotname = PQfnumber(res, "subslotname");
5332 255 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5333 255 : i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5334 255 : i_subpublications = PQfnumber(res, "subpublications");
5335 255 : i_suborigin = PQfnumber(res, "suborigin");
5336 255 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5337 :
5338 255 : subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5339 :
5340 383 : for (i = 0; i < ntups; i++)
5341 : {
5342 128 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5343 128 : subinfo[i].dobj.catId.tableoid =
5344 128 : atooid(PQgetvalue(res, i, i_tableoid));
5345 128 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5346 128 : AssignDumpId(&subinfo[i].dobj);
5347 128 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5348 128 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5349 :
5350 128 : subinfo[i].subenabled =
5351 128 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5352 128 : if (PQgetisnull(res, i, i_subservername))
5353 128 : subinfo[i].subservername = NULL;
5354 : else
5355 0 : subinfo[i].subservername = pg_strdup(PQgetvalue(res, i, i_subservername));
5356 128 : subinfo[i].subbinary =
5357 128 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5358 128 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5359 128 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5360 128 : subinfo[i].subdisableonerr =
5361 128 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5362 128 : subinfo[i].subpasswordrequired =
5363 128 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5364 128 : subinfo[i].subrunasowner =
5365 128 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5366 128 : subinfo[i].subfailover =
5367 128 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5368 128 : subinfo[i].subretaindeadtuples =
5369 128 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5370 128 : subinfo[i].submaxretention =
5371 128 : atoi(PQgetvalue(res, i, i_submaxretention));
5372 128 : if (PQgetisnull(res, i, i_subconninfo))
5373 0 : subinfo[i].subconninfo = NULL;
5374 : else
5375 128 : subinfo[i].subconninfo =
5376 128 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5377 128 : if (PQgetisnull(res, i, i_subslotname))
5378 0 : subinfo[i].subslotname = NULL;
5379 : else
5380 128 : subinfo[i].subslotname =
5381 128 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5382 256 : subinfo[i].subsynccommit =
5383 128 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5384 256 : subinfo[i].subwalrcvtimeout =
5385 128 : pg_strdup(PQgetvalue(res, i, i_subwalrcvtimeout));
5386 256 : subinfo[i].subpublications =
5387 128 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5388 128 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5389 128 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5390 127 : subinfo[i].suboriginremotelsn = NULL;
5391 : else
5392 1 : subinfo[i].suboriginremotelsn =
5393 1 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5394 :
5395 : /* Decide whether we want to dump it */
5396 128 : selectDumpableObject(&(subinfo[i].dobj), fout);
5397 : }
5398 255 : PQclear(res);
5399 :
5400 255 : destroyPQExpBuffer(query);
5401 : }
5402 :
5403 : /*
5404 : * getSubscriptionRelations
5405 : * Get information about subscription membership for dumpable relations. This
5406 : * will be used only in binary-upgrade mode for PG17 or later versions.
5407 : */
5408 : void
5409 259 : getSubscriptionRelations(Archive *fout)
5410 : {
5411 259 : DumpOptions *dopt = fout->dopt;
5412 259 : SubscriptionInfo *subinfo = NULL;
5413 : SubRelInfo *subrinfo;
5414 : PGresult *res;
5415 : int i_srsubid;
5416 : int i_srrelid;
5417 : int i_srsubstate;
5418 : int i_srsublsn;
5419 : int ntups;
5420 259 : Oid last_srsubid = InvalidOid;
5421 :
5422 259 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5423 40 : fout->remoteVersion < 170000)
5424 219 : return;
5425 :
5426 40 : res = ExecuteSqlQuery(fout,
5427 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5428 : "FROM pg_catalog.pg_subscription_rel "
5429 : "ORDER BY srsubid",
5430 : PGRES_TUPLES_OK);
5431 40 : ntups = PQntuples(res);
5432 40 : if (ntups == 0)
5433 39 : goto cleanup;
5434 :
5435 : /* Get pg_subscription_rel attributes */
5436 1 : i_srsubid = PQfnumber(res, "srsubid");
5437 1 : i_srrelid = PQfnumber(res, "srrelid");
5438 1 : i_srsubstate = PQfnumber(res, "srsubstate");
5439 1 : i_srsublsn = PQfnumber(res, "srsublsn");
5440 :
5441 1 : subrinfo = pg_malloc_array(SubRelInfo, ntups);
5442 4 : for (int i = 0; i < ntups; i++)
5443 : {
5444 3 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5445 3 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5446 : TableInfo *tblinfo;
5447 :
5448 : /*
5449 : * If we switched to a new subscription, check if the subscription
5450 : * exists.
5451 : */
5452 3 : if (cur_srsubid != last_srsubid)
5453 : {
5454 2 : subinfo = findSubscriptionByOid(cur_srsubid);
5455 2 : if (subinfo == NULL)
5456 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5457 :
5458 2 : last_srsubid = cur_srsubid;
5459 : }
5460 :
5461 3 : tblinfo = findTableByOid(relid);
5462 3 : if (tblinfo == NULL)
5463 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5464 : relid);
5465 :
5466 : /* OK, make a DumpableObject for this relationship */
5467 3 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5468 3 : subrinfo[i].dobj.catId.tableoid = relid;
5469 3 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5470 3 : AssignDumpId(&subrinfo[i].dobj);
5471 3 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5472 3 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5473 3 : subrinfo[i].subinfo = subinfo;
5474 3 : subrinfo[i].tblinfo = tblinfo;
5475 3 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5476 3 : if (PQgetisnull(res, i, i_srsublsn))
5477 1 : subrinfo[i].srsublsn = NULL;
5478 : else
5479 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5480 :
5481 : /* Decide whether we want to dump it */
5482 3 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5483 : }
5484 :
5485 1 : cleanup:
5486 40 : PQclear(res);
5487 : }
5488 :
5489 : /*
5490 : * dumpSubscriptionTable
5491 : * Dump the definition of the given subscription table mapping. This will be
5492 : * used only in binary-upgrade mode for PG17 or later versions.
5493 : */
5494 : static void
5495 3 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5496 : {
5497 3 : DumpOptions *dopt = fout->dopt;
5498 3 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5499 : PQExpBuffer query;
5500 : char *tag;
5501 :
5502 : /* Do nothing if not dumping schema */
5503 3 : if (!dopt->dumpSchema)
5504 0 : return;
5505 :
5506 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5507 :
5508 3 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5509 :
5510 3 : query = createPQExpBuffer();
5511 :
5512 3 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5513 : {
5514 : /*
5515 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5516 : * to pg_subscription_rel table. This will be used only in
5517 : * binary-upgrade mode.
5518 : */
5519 3 : appendPQExpBufferStr(query,
5520 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5521 3 : appendPQExpBufferStr(query,
5522 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5523 3 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5524 3 : appendPQExpBuffer(query,
5525 : ", %u, '%c'",
5526 3 : subrinfo->tblinfo->dobj.catId.oid,
5527 3 : subrinfo->srsubstate);
5528 :
5529 3 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5530 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5531 : else
5532 1 : appendPQExpBufferStr(query, ", NULL");
5533 :
5534 3 : appendPQExpBufferStr(query, ");\n");
5535 : }
5536 :
5537 : /*
5538 : * There is no point in creating a drop query as the drop is done by table
5539 : * drop. (If you think to change this, see also _printTocEntry().)
5540 : * Although this object doesn't really have ownership as such, set the
5541 : * owner field anyway to ensure that the command is run by the correct
5542 : * role at restore time.
5543 : */
5544 3 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5545 3 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5546 3 : ARCHIVE_OPTS(.tag = tag,
5547 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5548 : .owner = subinfo->rolname,
5549 : .description = "SUBSCRIPTION TABLE",
5550 : .section = SECTION_POST_DATA,
5551 : .createStmt = query->data));
5552 :
5553 : /* These objects can't currently have comments or seclabels */
5554 :
5555 3 : free(tag);
5556 3 : destroyPQExpBuffer(query);
5557 : }
5558 :
5559 : /*
5560 : * dumpSubscription
5561 : * dump the definition of the given subscription
5562 : */
5563 : static void
5564 110 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5565 : {
5566 110 : DumpOptions *dopt = fout->dopt;
5567 : PQExpBuffer delq;
5568 : PQExpBuffer query;
5569 : PQExpBuffer publications;
5570 : char *qsubname;
5571 110 : char **pubnames = NULL;
5572 110 : int npubnames = 0;
5573 : int i;
5574 :
5575 : /* Do nothing if not dumping schema */
5576 110 : if (!dopt->dumpSchema)
5577 18 : return;
5578 :
5579 92 : delq = createPQExpBuffer();
5580 92 : query = createPQExpBuffer();
5581 :
5582 92 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5583 :
5584 92 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5585 : qsubname);
5586 :
5587 92 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5588 : qsubname);
5589 92 : if (subinfo->subservername)
5590 : {
5591 0 : appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5592 : }
5593 : else
5594 : {
5595 92 : appendPQExpBuffer(query, "CONNECTION ");
5596 92 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5597 : }
5598 :
5599 : /* Build list of quoted publications and append them to query. */
5600 92 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5601 0 : pg_fatal("could not parse %s array", "subpublications");
5602 :
5603 92 : publications = createPQExpBuffer();
5604 184 : for (i = 0; i < npubnames; i++)
5605 : {
5606 92 : if (i > 0)
5607 0 : appendPQExpBufferStr(publications, ", ");
5608 :
5609 92 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5610 : }
5611 :
5612 92 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5613 92 : if (subinfo->subslotname)
5614 92 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5615 : else
5616 0 : appendPQExpBufferStr(query, "NONE");
5617 :
5618 92 : if (subinfo->subbinary)
5619 0 : appendPQExpBufferStr(query, ", binary = true");
5620 :
5621 92 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5622 30 : appendPQExpBufferStr(query, ", streaming = on");
5623 62 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5624 32 : appendPQExpBufferStr(query, ", streaming = parallel");
5625 : else
5626 30 : appendPQExpBufferStr(query, ", streaming = off");
5627 :
5628 92 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5629 0 : appendPQExpBufferStr(query, ", two_phase = on");
5630 :
5631 92 : if (subinfo->subdisableonerr)
5632 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5633 :
5634 92 : if (!subinfo->subpasswordrequired)
5635 0 : appendPQExpBufferStr(query, ", password_required = false");
5636 :
5637 92 : if (subinfo->subrunasowner)
5638 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5639 :
5640 92 : if (subinfo->subfailover)
5641 1 : appendPQExpBufferStr(query, ", failover = true");
5642 :
5643 92 : if (subinfo->subretaindeadtuples)
5644 1 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5645 :
5646 92 : if (subinfo->submaxretention)
5647 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5648 :
5649 92 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5650 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5651 :
5652 92 : if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5653 0 : appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5654 :
5655 92 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5656 30 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5657 :
5658 92 : appendPQExpBufferStr(query, ");\n");
5659 :
5660 : /*
5661 : * In binary-upgrade mode, we allow the replication to continue after the
5662 : * upgrade.
5663 : */
5664 92 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5665 : {
5666 5 : if (subinfo->suboriginremotelsn)
5667 : {
5668 : /*
5669 : * Preserve the remote_lsn for the subscriber's replication
5670 : * origin. This value is required to start the replication from
5671 : * the position before the upgrade. This value will be stale if
5672 : * the publisher gets upgraded before the subscriber node.
5673 : * However, this shouldn't be a problem as the upgrade of the
5674 : * publisher ensures that all the transactions were replicated
5675 : * before upgrading it.
5676 : */
5677 1 : appendPQExpBufferStr(query,
5678 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5679 1 : appendPQExpBufferStr(query,
5680 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5681 1 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5682 1 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5683 : }
5684 :
5685 5 : if (subinfo->subenabled)
5686 : {
5687 : /*
5688 : * Enable the subscription to allow the replication to continue
5689 : * after the upgrade.
5690 : */
5691 1 : appendPQExpBufferStr(query,
5692 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5693 1 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5694 : }
5695 : }
5696 :
5697 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5698 92 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5699 92 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5700 : .owner = subinfo->rolname,
5701 : .description = "SUBSCRIPTION",
5702 : .section = SECTION_POST_DATA,
5703 : .createStmt = query->data,
5704 : .dropStmt = delq->data));
5705 :
5706 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5707 30 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5708 30 : NULL, subinfo->rolname,
5709 30 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5710 :
5711 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5712 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5713 0 : NULL, subinfo->rolname,
5714 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5715 :
5716 92 : destroyPQExpBuffer(publications);
5717 92 : free(pubnames);
5718 :
5719 92 : destroyPQExpBuffer(delq);
5720 92 : destroyPQExpBuffer(query);
5721 92 : free(qsubname);
5722 : }
5723 :
5724 : /*
5725 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5726 : * the object needs.
5727 : */
5728 : static void
5729 5264 : append_depends_on_extension(Archive *fout,
5730 : PQExpBuffer create,
5731 : const DumpableObject *dobj,
5732 : const char *catalog,
5733 : const char *keyword,
5734 : const char *objname)
5735 : {
5736 5264 : if (dobj->depends_on_ext)
5737 : {
5738 : char *nm;
5739 : PGresult *res;
5740 : PQExpBuffer query;
5741 : int ntups;
5742 : int i_extname;
5743 : int i;
5744 :
5745 : /* dodge fmtId() non-reentrancy */
5746 42 : nm = pg_strdup(objname);
5747 :
5748 42 : query = createPQExpBuffer();
5749 42 : appendPQExpBuffer(query,
5750 : "SELECT e.extname "
5751 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5752 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5753 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5754 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5755 : catalog,
5756 42 : dobj->catId.oid);
5757 42 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5758 42 : ntups = PQntuples(res);
5759 42 : i_extname = PQfnumber(res, "extname");
5760 84 : for (i = 0; i < ntups; i++)
5761 : {
5762 42 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5763 : keyword, nm,
5764 42 : fmtId(PQgetvalue(res, i, i_extname)));
5765 : }
5766 :
5767 42 : PQclear(res);
5768 42 : destroyPQExpBuffer(query);
5769 42 : pg_free(nm);
5770 : }
5771 5264 : }
5772 :
5773 : static Oid
5774 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5775 : {
5776 : /*
5777 : * If the old version didn't assign an array type, but the new version
5778 : * does, we must select an unused type OID to assign. This currently only
5779 : * happens for domains, when upgrading pre-v11 to v11 and up.
5780 : *
5781 : * Note: local state here is kind of ugly, but we must have some, since we
5782 : * mustn't choose the same unused OID more than once.
5783 : */
5784 : static Oid next_possible_free_oid = FirstNormalObjectId;
5785 : PGresult *res;
5786 : bool is_dup;
5787 :
5788 : do
5789 : {
5790 0 : ++next_possible_free_oid;
5791 0 : printfPQExpBuffer(upgrade_query,
5792 : "SELECT EXISTS(SELECT 1 "
5793 : "FROM pg_catalog.pg_type "
5794 : "WHERE oid = '%u'::pg_catalog.oid);",
5795 : next_possible_free_oid);
5796 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5797 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5798 0 : PQclear(res);
5799 0 : } while (is_dup);
5800 :
5801 0 : return next_possible_free_oid;
5802 : }
5803 :
5804 : static void
5805 1016 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5806 : PQExpBuffer upgrade_buffer,
5807 : Oid pg_type_oid,
5808 : bool force_array_type,
5809 : bool include_multirange_type)
5810 : {
5811 1016 : PQExpBuffer upgrade_query = createPQExpBuffer();
5812 : PGresult *res;
5813 : Oid pg_type_array_oid;
5814 : Oid pg_type_multirange_oid;
5815 : Oid pg_type_multirange_array_oid;
5816 : TypeInfo *tinfo;
5817 :
5818 1016 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5819 1016 : appendPQExpBuffer(upgrade_buffer,
5820 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5821 : pg_type_oid);
5822 :
5823 1016 : tinfo = findTypeByOid(pg_type_oid);
5824 1016 : if (tinfo)
5825 1016 : pg_type_array_oid = tinfo->typarray;
5826 : else
5827 0 : pg_type_array_oid = InvalidOid;
5828 :
5829 1016 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5830 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5831 :
5832 1016 : if (OidIsValid(pg_type_array_oid))
5833 : {
5834 1014 : appendPQExpBufferStr(upgrade_buffer,
5835 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5836 1014 : appendPQExpBuffer(upgrade_buffer,
5837 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5838 : pg_type_array_oid);
5839 : }
5840 :
5841 : /*
5842 : * Pre-set the multirange type oid and its own array type oid.
5843 : */
5844 1016 : if (include_multirange_type)
5845 : {
5846 8 : if (fout->remoteVersion >= 140000)
5847 : {
5848 8 : printfPQExpBuffer(upgrade_query,
5849 : "SELECT t.oid, t.typarray "
5850 : "FROM pg_catalog.pg_type t "
5851 : "JOIN pg_catalog.pg_range r "
5852 : "ON t.oid = r.rngmultitypid "
5853 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5854 : pg_type_oid);
5855 :
5856 8 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5857 :
5858 8 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5859 8 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5860 :
5861 8 : PQclear(res);
5862 : }
5863 : else
5864 : {
5865 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5866 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5867 : }
5868 :
5869 8 : appendPQExpBufferStr(upgrade_buffer,
5870 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5871 8 : appendPQExpBuffer(upgrade_buffer,
5872 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5873 : pg_type_multirange_oid);
5874 8 : appendPQExpBufferStr(upgrade_buffer,
5875 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5876 8 : appendPQExpBuffer(upgrade_buffer,
5877 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5878 : pg_type_multirange_array_oid);
5879 : }
5880 :
5881 1016 : destroyPQExpBuffer(upgrade_query);
5882 1016 : }
5883 :
5884 : static void
5885 941 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5886 : PQExpBuffer upgrade_buffer,
5887 : const TableInfo *tbinfo)
5888 : {
5889 941 : Oid pg_type_oid = tbinfo->reltype;
5890 :
5891 941 : if (OidIsValid(pg_type_oid))
5892 941 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5893 : pg_type_oid, false, false);
5894 941 : }
5895 :
5896 : /*
5897 : * bsearch() comparator for BinaryUpgradeClassOidItem
5898 : */
5899 : static int
5900 13565 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5901 : {
5902 13565 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5903 13565 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5904 :
5905 13565 : return pg_cmp_u32(v1.oid, v2.oid);
5906 : }
5907 :
5908 : /*
5909 : * collectBinaryUpgradeClassOids
5910 : *
5911 : * Construct a table of pg_class information required for
5912 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5913 : * lookup.
5914 : */
5915 : static void
5916 40 : collectBinaryUpgradeClassOids(Archive *fout)
5917 : {
5918 : PGresult *res;
5919 : const char *query;
5920 :
5921 40 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5922 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5923 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5924 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5925 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5926 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5927 : "ORDER BY c.oid;";
5928 :
5929 40 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5930 :
5931 40 : nbinaryUpgradeClassOids = PQntuples(res);
5932 40 : binaryUpgradeClassOids =
5933 40 : pg_malloc_array(BinaryUpgradeClassOidItem, nbinaryUpgradeClassOids);
5934 :
5935 19946 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5936 : {
5937 19906 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5938 19906 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5939 19906 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5940 19906 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5941 19906 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5942 19906 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5943 19906 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5944 : }
5945 :
5946 40 : PQclear(res);
5947 40 : }
5948 :
5949 : static void
5950 1358 : binary_upgrade_set_pg_class_oids(Archive *fout,
5951 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5952 : {
5953 1358 : BinaryUpgradeClassOidItem key = {0};
5954 : BinaryUpgradeClassOidItem *entry;
5955 :
5956 : Assert(binaryUpgradeClassOids);
5957 :
5958 : /*
5959 : * Preserve the OID and relfilenumber of the table, table's index, table's
5960 : * toast table and toast table's index if any.
5961 : *
5962 : * One complexity is that the current table definition might not require
5963 : * the creation of a TOAST table, but the old database might have a TOAST
5964 : * table that was created earlier, before some wide columns were dropped.
5965 : * By setting the TOAST oid we force creation of the TOAST heap and index
5966 : * by the new backend, so we can copy the files during binary upgrade
5967 : * without worrying about this case.
5968 : */
5969 1358 : key.oid = pg_class_oid;
5970 1358 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5971 : sizeof(BinaryUpgradeClassOidItem),
5972 : BinaryUpgradeClassOidItemCmp);
5973 :
5974 1358 : appendPQExpBufferStr(upgrade_buffer,
5975 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5976 :
5977 1358 : if (entry->relkind != RELKIND_INDEX &&
5978 1055 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5979 : {
5980 1025 : appendPQExpBuffer(upgrade_buffer,
5981 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5982 : pg_class_oid);
5983 :
5984 : /*
5985 : * Not every relation has storage. Also, in a pre-v12 database,
5986 : * partitioned tables have a relfilenumber, which should not be
5987 : * preserved when upgrading.
5988 : */
5989 1025 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5990 841 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5991 841 : appendPQExpBuffer(upgrade_buffer,
5992 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5993 : entry->relfilenumber);
5994 :
5995 : /*
5996 : * In a pre-v12 database, partitioned tables might be marked as having
5997 : * toast tables, but we should ignore them if so.
5998 : */
5999 1025 : if (OidIsValid(entry->toast_oid) &&
6000 294 : entry->relkind != RELKIND_PARTITIONED_TABLE)
6001 : {
6002 294 : appendPQExpBuffer(upgrade_buffer,
6003 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
6004 : entry->toast_oid);
6005 294 : appendPQExpBuffer(upgrade_buffer,
6006 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
6007 : entry->toast_relfilenumber);
6008 :
6009 : /* every toast table has an index */
6010 294 : appendPQExpBuffer(upgrade_buffer,
6011 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6012 : entry->toast_index_oid);
6013 294 : appendPQExpBuffer(upgrade_buffer,
6014 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6015 : entry->toast_index_relfilenumber);
6016 : }
6017 : }
6018 : else
6019 : {
6020 : /* Preserve the OID and relfilenumber of the index */
6021 333 : appendPQExpBuffer(upgrade_buffer,
6022 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6023 : pg_class_oid);
6024 333 : appendPQExpBuffer(upgrade_buffer,
6025 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6026 : entry->relfilenumber);
6027 : }
6028 :
6029 1358 : appendPQExpBufferChar(upgrade_buffer, '\n');
6030 1358 : }
6031 :
6032 : /*
6033 : * If the DumpableObject is a member of an extension, add a suitable
6034 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
6035 : *
6036 : * For somewhat historical reasons, objname should already be quoted,
6037 : * but not objnamespace (if any).
6038 : */
6039 : static void
6040 1596 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
6041 : const DumpableObject *dobj,
6042 : const char *objtype,
6043 : const char *objname,
6044 : const char *objnamespace)
6045 : {
6046 1596 : DumpableObject *extobj = NULL;
6047 : int i;
6048 :
6049 1596 : if (!dobj->ext_member)
6050 1574 : return;
6051 :
6052 : /*
6053 : * Find the parent extension. We could avoid this search if we wanted to
6054 : * add a link field to DumpableObject, but the space costs of that would
6055 : * be considerable. We assume that member objects could only have a
6056 : * direct dependency on their own extension, not any others.
6057 : */
6058 22 : for (i = 0; i < dobj->nDeps; i++)
6059 : {
6060 22 : extobj = findObjectByDumpId(dobj->dependencies[i]);
6061 22 : if (extobj && extobj->objType == DO_EXTENSION)
6062 22 : break;
6063 0 : extobj = NULL;
6064 : }
6065 22 : if (extobj == NULL)
6066 0 : pg_fatal("could not find parent extension for %s %s",
6067 : objtype, objname);
6068 :
6069 22 : appendPQExpBufferStr(upgrade_buffer,
6070 : "\n-- For binary upgrade, handle extension membership the hard way\n");
6071 22 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6072 22 : fmtId(extobj->name),
6073 : objtype);
6074 22 : if (objnamespace && *objnamespace)
6075 19 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
6076 22 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6077 : }
6078 :
6079 : /*
6080 : * getNamespaces:
6081 : * get information about all namespaces in the system catalogs
6082 : */
6083 : void
6084 260 : getNamespaces(Archive *fout)
6085 : {
6086 : PGresult *res;
6087 : int ntups;
6088 : int i;
6089 : PQExpBuffer query;
6090 : NamespaceInfo *nsinfo;
6091 : int i_tableoid;
6092 : int i_oid;
6093 : int i_nspname;
6094 : int i_nspowner;
6095 : int i_nspacl;
6096 : int i_acldefault;
6097 :
6098 260 : query = createPQExpBuffer();
6099 :
6100 : /*
6101 : * we fetch all namespaces including system ones, so that every object we
6102 : * read in can be linked to a containing namespace.
6103 : */
6104 260 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6105 : "n.nspowner, "
6106 : "n.nspacl, "
6107 : "acldefault('n', n.nspowner) AS acldefault "
6108 : "FROM pg_namespace n");
6109 :
6110 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6111 :
6112 260 : ntups = PQntuples(res);
6113 :
6114 260 : nsinfo = pg_malloc_array(NamespaceInfo, ntups);
6115 :
6116 260 : i_tableoid = PQfnumber(res, "tableoid");
6117 260 : i_oid = PQfnumber(res, "oid");
6118 260 : i_nspname = PQfnumber(res, "nspname");
6119 260 : i_nspowner = PQfnumber(res, "nspowner");
6120 260 : i_nspacl = PQfnumber(res, "nspacl");
6121 260 : i_acldefault = PQfnumber(res, "acldefault");
6122 :
6123 2027 : for (i = 0; i < ntups; i++)
6124 : {
6125 : const char *nspowner;
6126 :
6127 1767 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6128 1767 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6129 1767 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6130 1767 : AssignDumpId(&nsinfo[i].dobj);
6131 1767 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6132 1767 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6133 1767 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6134 1767 : nsinfo[i].dacl.privtype = 0;
6135 1767 : nsinfo[i].dacl.initprivs = NULL;
6136 1767 : nspowner = PQgetvalue(res, i, i_nspowner);
6137 1767 : nsinfo[i].nspowner = atooid(nspowner);
6138 1767 : nsinfo[i].rolname = getRoleName(nspowner);
6139 :
6140 : /* Decide whether to dump this namespace */
6141 1767 : selectDumpableNamespace(&nsinfo[i], fout);
6142 :
6143 : /* Mark whether namespace has an ACL */
6144 1767 : if (!PQgetisnull(res, i, i_nspacl))
6145 879 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6146 :
6147 : /*
6148 : * We ignore any pg_init_privs.initprivs entry for the public schema
6149 : * and assume a predetermined default, for several reasons. First,
6150 : * dropping and recreating the schema removes its pg_init_privs entry,
6151 : * but an empty destination database starts with this ACL nonetheless.
6152 : * Second, we support dump/reload of public schema ownership changes.
6153 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6154 : * initprivs continues to reflect the initial owner. Hence,
6155 : * synthesize the value that nspacl will have after the restore's
6156 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6157 : * match the source's ACL, even if the latter was an initdb-default
6158 : * ACL, which changed in v15. An upgrade pulls in changes to most
6159 : * system object ACLs that the DBA had not customized. We've made the
6160 : * public schema depart from that, because changing its ACL so easily
6161 : * breaks applications.
6162 : */
6163 1767 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6164 : {
6165 256 : PQExpBuffer aclarray = createPQExpBuffer();
6166 256 : PQExpBuffer aclitem = createPQExpBuffer();
6167 :
6168 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6169 256 : appendPQExpBufferChar(aclarray, '{');
6170 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6171 256 : appendPQExpBufferStr(aclitem, "=UC/");
6172 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6173 256 : appendPGArray(aclarray, aclitem->data);
6174 256 : resetPQExpBuffer(aclitem);
6175 256 : appendPQExpBufferStr(aclitem, "=U/");
6176 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6177 256 : appendPGArray(aclarray, aclitem->data);
6178 256 : appendPQExpBufferChar(aclarray, '}');
6179 :
6180 256 : nsinfo[i].dacl.privtype = 'i';
6181 256 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6182 256 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6183 :
6184 256 : destroyPQExpBuffer(aclarray);
6185 256 : destroyPQExpBuffer(aclitem);
6186 : }
6187 : }
6188 :
6189 260 : PQclear(res);
6190 260 : destroyPQExpBuffer(query);
6191 260 : }
6192 :
6193 : /*
6194 : * findNamespace:
6195 : * given a namespace OID, look up the info read by getNamespaces
6196 : */
6197 : static NamespaceInfo *
6198 841241 : findNamespace(Oid nsoid)
6199 : {
6200 : NamespaceInfo *nsinfo;
6201 :
6202 841241 : nsinfo = findNamespaceByOid(nsoid);
6203 841241 : if (nsinfo == NULL)
6204 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6205 841241 : return nsinfo;
6206 : }
6207 :
6208 : /*
6209 : * getExtensions:
6210 : * read all extensions in the system catalogs and return them in the
6211 : * ExtensionInfo* structure
6212 : *
6213 : * numExtensions is set to the number of extensions read in
6214 : */
6215 : ExtensionInfo *
6216 260 : getExtensions(Archive *fout, int *numExtensions)
6217 : {
6218 260 : DumpOptions *dopt = fout->dopt;
6219 : PGresult *res;
6220 : int ntups;
6221 : int i;
6222 : PQExpBuffer query;
6223 260 : ExtensionInfo *extinfo = NULL;
6224 : int i_tableoid;
6225 : int i_oid;
6226 : int i_extname;
6227 : int i_nspname;
6228 : int i_extrelocatable;
6229 : int i_extversion;
6230 : int i_extconfig;
6231 : int i_extcondition;
6232 :
6233 260 : query = createPQExpBuffer();
6234 :
6235 260 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6236 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6237 : "FROM pg_extension x "
6238 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6239 :
6240 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6241 :
6242 260 : ntups = PQntuples(res);
6243 260 : if (ntups == 0)
6244 0 : goto cleanup;
6245 :
6246 260 : extinfo = pg_malloc_array(ExtensionInfo, ntups);
6247 :
6248 260 : i_tableoid = PQfnumber(res, "tableoid");
6249 260 : i_oid = PQfnumber(res, "oid");
6250 260 : i_extname = PQfnumber(res, "extname");
6251 260 : i_nspname = PQfnumber(res, "nspname");
6252 260 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6253 260 : i_extversion = PQfnumber(res, "extversion");
6254 260 : i_extconfig = PQfnumber(res, "extconfig");
6255 260 : i_extcondition = PQfnumber(res, "extcondition");
6256 :
6257 551 : for (i = 0; i < ntups; i++)
6258 : {
6259 291 : extinfo[i].dobj.objType = DO_EXTENSION;
6260 291 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6261 291 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6262 291 : AssignDumpId(&extinfo[i].dobj);
6263 291 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6264 291 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6265 291 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6266 291 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6267 291 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6268 291 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6269 :
6270 : /* Decide whether we want to dump it */
6271 291 : selectDumpableExtension(&(extinfo[i]), dopt);
6272 : }
6273 :
6274 260 : cleanup:
6275 260 : PQclear(res);
6276 260 : destroyPQExpBuffer(query);
6277 :
6278 260 : *numExtensions = ntups;
6279 :
6280 260 : return extinfo;
6281 : }
6282 :
6283 : /*
6284 : * getTypes:
6285 : * get information about all types in the system catalogs
6286 : *
6287 : * NB: this must run after getFuncs() because we assume we can do
6288 : * findFuncByOid().
6289 : */
6290 : void
6291 259 : getTypes(Archive *fout)
6292 : {
6293 : PGresult *res;
6294 : int ntups;
6295 : int i;
6296 259 : PQExpBuffer query = createPQExpBuffer();
6297 : TypeInfo *tyinfo;
6298 : ShellTypeInfo *stinfo;
6299 : int i_tableoid;
6300 : int i_oid;
6301 : int i_typname;
6302 : int i_typnamespace;
6303 : int i_typacl;
6304 : int i_acldefault;
6305 : int i_typowner;
6306 : int i_typelem;
6307 : int i_typrelid;
6308 : int i_typrelkind;
6309 : int i_typtype;
6310 : int i_typisdefined;
6311 : int i_isarray;
6312 : int i_typarray;
6313 :
6314 : /*
6315 : * we include even the built-in types because those may be used as array
6316 : * elements by user-defined types
6317 : *
6318 : * we filter out the built-in types when we dump out the types
6319 : *
6320 : * same approach for undefined (shell) types and array types
6321 : *
6322 : * Note: as of 8.3 we can reliably detect whether a type is an
6323 : * auto-generated array type by checking the element type's typarray.
6324 : * (Before that the test is capable of generating false positives.) We
6325 : * still check for name beginning with '_', though, so as to avoid the
6326 : * cost of the subselect probe for all standard types. This would have to
6327 : * be revisited if the backend ever allows renaming of array types.
6328 : */
6329 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6330 : "typnamespace, typacl, "
6331 : "acldefault('T', typowner) AS acldefault, "
6332 : "typowner, "
6333 : "typelem, typrelid, typarray, "
6334 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6335 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6336 : "typtype, typisdefined, "
6337 : "typname[0] = '_' AND typelem != 0 AND "
6338 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6339 : "FROM pg_type");
6340 :
6341 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6342 :
6343 259 : ntups = PQntuples(res);
6344 :
6345 259 : tyinfo = pg_malloc_array(TypeInfo, ntups);
6346 :
6347 259 : i_tableoid = PQfnumber(res, "tableoid");
6348 259 : i_oid = PQfnumber(res, "oid");
6349 259 : i_typname = PQfnumber(res, "typname");
6350 259 : i_typnamespace = PQfnumber(res, "typnamespace");
6351 259 : i_typacl = PQfnumber(res, "typacl");
6352 259 : i_acldefault = PQfnumber(res, "acldefault");
6353 259 : i_typowner = PQfnumber(res, "typowner");
6354 259 : i_typelem = PQfnumber(res, "typelem");
6355 259 : i_typrelid = PQfnumber(res, "typrelid");
6356 259 : i_typrelkind = PQfnumber(res, "typrelkind");
6357 259 : i_typtype = PQfnumber(res, "typtype");
6358 259 : i_typisdefined = PQfnumber(res, "typisdefined");
6359 259 : i_isarray = PQfnumber(res, "isarray");
6360 259 : i_typarray = PQfnumber(res, "typarray");
6361 :
6362 191742 : for (i = 0; i < ntups; i++)
6363 : {
6364 191483 : tyinfo[i].dobj.objType = DO_TYPE;
6365 191483 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6366 191483 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6367 191483 : AssignDumpId(&tyinfo[i].dobj);
6368 191483 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6369 382966 : tyinfo[i].dobj.namespace =
6370 191483 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6371 191483 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6372 191483 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6373 191483 : tyinfo[i].dacl.privtype = 0;
6374 191483 : tyinfo[i].dacl.initprivs = NULL;
6375 191483 : tyinfo[i].ftypname = NULL; /* may get filled later */
6376 191483 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6377 191483 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6378 191483 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6379 191483 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6380 191483 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6381 191483 : tyinfo[i].shellType = NULL;
6382 :
6383 191483 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6384 191431 : tyinfo[i].isDefined = true;
6385 : else
6386 52 : tyinfo[i].isDefined = false;
6387 :
6388 191483 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6389 91960 : tyinfo[i].isArray = true;
6390 : else
6391 99523 : tyinfo[i].isArray = false;
6392 :
6393 191483 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6394 :
6395 191483 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6396 1686 : tyinfo[i].isMultirange = true;
6397 : else
6398 189797 : tyinfo[i].isMultirange = false;
6399 :
6400 : /* Decide whether we want to dump it */
6401 191483 : selectDumpableType(&tyinfo[i], fout);
6402 :
6403 : /* Mark whether type has an ACL */
6404 191483 : if (!PQgetisnull(res, i, i_typacl))
6405 205 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6406 :
6407 : /*
6408 : * If it's a domain, fetch info about its constraints, if any
6409 : */
6410 191483 : tyinfo[i].nDomChecks = 0;
6411 191483 : tyinfo[i].domChecks = NULL;
6412 191483 : tyinfo[i].notnull = NULL;
6413 191483 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6414 16275 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6415 158 : getDomainConstraints(fout, &(tyinfo[i]));
6416 :
6417 : /*
6418 : * If it's a base type, make a DumpableObject representing a shell
6419 : * definition of the type. We will need to dump that ahead of the I/O
6420 : * functions for the type. Similarly, range types need a shell
6421 : * definition in case they have a canonicalize function.
6422 : *
6423 : * Note: the shell type doesn't have a catId. You might think it
6424 : * should copy the base type's catId, but then it might capture the
6425 : * pg_depend entries for the type, which we don't want.
6426 : */
6427 191483 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6428 16275 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6429 7921 : tyinfo[i].typtype == TYPTYPE_RANGE))
6430 : {
6431 8478 : stinfo = pg_malloc_object(ShellTypeInfo);
6432 8478 : stinfo->dobj.objType = DO_SHELL_TYPE;
6433 8478 : stinfo->dobj.catId = nilCatalogId;
6434 8478 : AssignDumpId(&stinfo->dobj);
6435 8478 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6436 8478 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6437 8478 : stinfo->baseType = &(tyinfo[i]);
6438 8478 : tyinfo[i].shellType = stinfo;
6439 :
6440 : /*
6441 : * Initially mark the shell type as not to be dumped. We'll only
6442 : * dump it if the I/O or canonicalize functions need to be dumped;
6443 : * this is taken care of while sorting dependencies.
6444 : */
6445 8478 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6446 : }
6447 : }
6448 :
6449 259 : PQclear(res);
6450 :
6451 259 : destroyPQExpBuffer(query);
6452 259 : }
6453 :
6454 : /*
6455 : * getOperators:
6456 : * get information about all operators in the system catalogs
6457 : */
6458 : void
6459 259 : getOperators(Archive *fout)
6460 : {
6461 : PGresult *res;
6462 : int ntups;
6463 : int i;
6464 259 : PQExpBuffer query = createPQExpBuffer();
6465 : OprInfo *oprinfo;
6466 : int i_tableoid;
6467 : int i_oid;
6468 : int i_oprname;
6469 : int i_oprnamespace;
6470 : int i_oprowner;
6471 : int i_oprkind;
6472 : int i_oprleft;
6473 : int i_oprright;
6474 : int i_oprcode;
6475 :
6476 : /*
6477 : * find all operators, including builtin operators; we filter out
6478 : * system-defined operators at dump-out time.
6479 : */
6480 :
6481 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6482 : "oprnamespace, "
6483 : "oprowner, "
6484 : "oprkind, "
6485 : "oprleft, "
6486 : "oprright, "
6487 : "oprcode::oid AS oprcode "
6488 : "FROM pg_operator");
6489 :
6490 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6491 :
6492 259 : ntups = PQntuples(res);
6493 :
6494 259 : oprinfo = pg_malloc_array(OprInfo, ntups);
6495 :
6496 259 : i_tableoid = PQfnumber(res, "tableoid");
6497 259 : i_oid = PQfnumber(res, "oid");
6498 259 : i_oprname = PQfnumber(res, "oprname");
6499 259 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6500 259 : i_oprowner = PQfnumber(res, "oprowner");
6501 259 : i_oprkind = PQfnumber(res, "oprkind");
6502 259 : i_oprleft = PQfnumber(res, "oprleft");
6503 259 : i_oprright = PQfnumber(res, "oprright");
6504 259 : i_oprcode = PQfnumber(res, "oprcode");
6505 :
6506 208896 : for (i = 0; i < ntups; i++)
6507 : {
6508 208637 : oprinfo[i].dobj.objType = DO_OPERATOR;
6509 208637 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6510 208637 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6511 208637 : AssignDumpId(&oprinfo[i].dobj);
6512 208637 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6513 417274 : oprinfo[i].dobj.namespace =
6514 208637 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6515 208637 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6516 208637 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6517 208637 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6518 208637 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6519 208637 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6520 :
6521 : /* Decide whether we want to dump it */
6522 208637 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6523 : }
6524 :
6525 259 : PQclear(res);
6526 :
6527 259 : destroyPQExpBuffer(query);
6528 259 : }
6529 :
6530 : /*
6531 : * getCollations:
6532 : * get information about all collations in the system catalogs
6533 : */
6534 : void
6535 259 : getCollations(Archive *fout)
6536 : {
6537 : PGresult *res;
6538 : int ntups;
6539 : int i;
6540 : PQExpBuffer query;
6541 : CollInfo *collinfo;
6542 : int i_tableoid;
6543 : int i_oid;
6544 : int i_collname;
6545 : int i_collnamespace;
6546 : int i_collowner;
6547 : int i_collencoding;
6548 :
6549 259 : query = createPQExpBuffer();
6550 :
6551 : /*
6552 : * find all collations, including builtin collations; we filter out
6553 : * system-defined collations at dump-out time.
6554 : */
6555 :
6556 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6557 : "collnamespace, "
6558 : "collowner, "
6559 : "collencoding "
6560 : "FROM pg_collation");
6561 :
6562 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6563 :
6564 259 : ntups = PQntuples(res);
6565 :
6566 259 : collinfo = pg_malloc_array(CollInfo, ntups);
6567 :
6568 259 : i_tableoid = PQfnumber(res, "tableoid");
6569 259 : i_oid = PQfnumber(res, "oid");
6570 259 : i_collname = PQfnumber(res, "collname");
6571 259 : i_collnamespace = PQfnumber(res, "collnamespace");
6572 259 : i_collowner = PQfnumber(res, "collowner");
6573 259 : i_collencoding = PQfnumber(res, "collencoding");
6574 :
6575 228290 : for (i = 0; i < ntups; i++)
6576 : {
6577 228031 : collinfo[i].dobj.objType = DO_COLLATION;
6578 228031 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6579 228031 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6580 228031 : AssignDumpId(&collinfo[i].dobj);
6581 228031 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6582 456062 : collinfo[i].dobj.namespace =
6583 228031 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6584 228031 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6585 228031 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6586 :
6587 : /* Decide whether we want to dump it */
6588 228031 : selectDumpableObject(&(collinfo[i].dobj), fout);
6589 : }
6590 :
6591 259 : PQclear(res);
6592 :
6593 259 : destroyPQExpBuffer(query);
6594 259 : }
6595 :
6596 : /*
6597 : * getConversions:
6598 : * get information about all conversions in the system catalogs
6599 : */
6600 : void
6601 259 : getConversions(Archive *fout)
6602 : {
6603 : PGresult *res;
6604 : int ntups;
6605 : int i;
6606 : PQExpBuffer query;
6607 : ConvInfo *convinfo;
6608 : int i_tableoid;
6609 : int i_oid;
6610 : int i_conname;
6611 : int i_connamespace;
6612 : int i_conowner;
6613 :
6614 259 : query = createPQExpBuffer();
6615 :
6616 : /*
6617 : * find all conversions, including builtin conversions; we filter out
6618 : * system-defined conversions at dump-out time.
6619 : */
6620 :
6621 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6622 : "connamespace, "
6623 : "conowner "
6624 : "FROM pg_conversion");
6625 :
6626 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6627 :
6628 259 : ntups = PQntuples(res);
6629 :
6630 259 : convinfo = pg_malloc_array(ConvInfo, ntups);
6631 :
6632 259 : i_tableoid = PQfnumber(res, "tableoid");
6633 259 : i_oid = PQfnumber(res, "oid");
6634 259 : i_conname = PQfnumber(res, "conname");
6635 259 : i_connamespace = PQfnumber(res, "connamespace");
6636 259 : i_conowner = PQfnumber(res, "conowner");
6637 :
6638 33456 : for (i = 0; i < ntups; i++)
6639 : {
6640 33197 : convinfo[i].dobj.objType = DO_CONVERSION;
6641 33197 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6642 33197 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6643 33197 : AssignDumpId(&convinfo[i].dobj);
6644 33197 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6645 66394 : convinfo[i].dobj.namespace =
6646 33197 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6647 33197 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6648 :
6649 : /* Decide whether we want to dump it */
6650 33197 : selectDumpableObject(&(convinfo[i].dobj), fout);
6651 : }
6652 :
6653 259 : PQclear(res);
6654 :
6655 259 : destroyPQExpBuffer(query);
6656 259 : }
6657 :
6658 : /*
6659 : * getAccessMethods:
6660 : * get information about all user-defined access methods
6661 : */
6662 : void
6663 259 : getAccessMethods(Archive *fout)
6664 : {
6665 : PGresult *res;
6666 : int ntups;
6667 : int i;
6668 : PQExpBuffer query;
6669 : AccessMethodInfo *aminfo;
6670 : int i_tableoid;
6671 : int i_oid;
6672 : int i_amname;
6673 : int i_amhandler;
6674 : int i_amtype;
6675 :
6676 259 : query = createPQExpBuffer();
6677 :
6678 : /*
6679 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6680 : * ACCESS METHOD, so earlier versions usually have only built-in access
6681 : * methods. v9.6 also changed the access method API, replacing dozens of
6682 : * pg_am columns with amhandler. Even if a user created an access method
6683 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6684 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6685 : * pg_am just to facilitate findAccessMethodByOid() providing the
6686 : * OID-to-name mapping.
6687 : */
6688 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6689 259 : if (fout->remoteVersion >= 90600)
6690 259 : appendPQExpBufferStr(query,
6691 : "amtype, "
6692 : "amhandler::pg_catalog.regproc AS amhandler ");
6693 : else
6694 0 : appendPQExpBufferStr(query,
6695 : "'i'::pg_catalog.\"char\" AS amtype, "
6696 : "'-'::pg_catalog.regproc AS amhandler ");
6697 259 : appendPQExpBufferStr(query, "FROM pg_am");
6698 :
6699 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6700 :
6701 259 : ntups = PQntuples(res);
6702 :
6703 259 : aminfo = pg_malloc_array(AccessMethodInfo, ntups);
6704 :
6705 259 : i_tableoid = PQfnumber(res, "tableoid");
6706 259 : i_oid = PQfnumber(res, "oid");
6707 259 : i_amname = PQfnumber(res, "amname");
6708 259 : i_amhandler = PQfnumber(res, "amhandler");
6709 259 : i_amtype = PQfnumber(res, "amtype");
6710 :
6711 2194 : for (i = 0; i < ntups; i++)
6712 : {
6713 1935 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6714 1935 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6715 1935 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6716 1935 : AssignDumpId(&aminfo[i].dobj);
6717 1935 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6718 1935 : aminfo[i].dobj.namespace = NULL;
6719 1935 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6720 1935 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6721 :
6722 : /* Decide whether we want to dump it */
6723 1935 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6724 : }
6725 :
6726 259 : PQclear(res);
6727 :
6728 259 : destroyPQExpBuffer(query);
6729 259 : }
6730 :
6731 :
6732 : /*
6733 : * getOpclasses:
6734 : * get information about all opclasses in the system catalogs
6735 : */
6736 : void
6737 259 : getOpclasses(Archive *fout)
6738 : {
6739 : PGresult *res;
6740 : int ntups;
6741 : int i;
6742 259 : PQExpBuffer query = createPQExpBuffer();
6743 : OpclassInfo *opcinfo;
6744 : int i_tableoid;
6745 : int i_oid;
6746 : int i_opcmethod;
6747 : int i_opcname;
6748 : int i_opcnamespace;
6749 : int i_opcowner;
6750 :
6751 : /*
6752 : * find all opclasses, including builtin opclasses; we filter out
6753 : * system-defined opclasses at dump-out time.
6754 : */
6755 :
6756 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6757 : "opcnamespace, "
6758 : "opcowner "
6759 : "FROM pg_opclass");
6760 :
6761 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6762 :
6763 259 : ntups = PQntuples(res);
6764 :
6765 259 : opcinfo = pg_malloc_array(OpclassInfo, ntups);
6766 :
6767 259 : i_tableoid = PQfnumber(res, "tableoid");
6768 259 : i_oid = PQfnumber(res, "oid");
6769 259 : i_opcmethod = PQfnumber(res, "opcmethod");
6770 259 : i_opcname = PQfnumber(res, "opcname");
6771 259 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6772 259 : i_opcowner = PQfnumber(res, "opcowner");
6773 :
6774 46776 : for (i = 0; i < ntups; i++)
6775 : {
6776 46517 : opcinfo[i].dobj.objType = DO_OPCLASS;
6777 46517 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6778 46517 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6779 46517 : AssignDumpId(&opcinfo[i].dobj);
6780 46517 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6781 93034 : opcinfo[i].dobj.namespace =
6782 46517 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6783 46517 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6784 46517 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6785 :
6786 : /* Decide whether we want to dump it */
6787 46517 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6788 : }
6789 :
6790 259 : PQclear(res);
6791 :
6792 259 : destroyPQExpBuffer(query);
6793 259 : }
6794 :
6795 : /*
6796 : * getOpfamilies:
6797 : * get information about all opfamilies in the system catalogs
6798 : */
6799 : void
6800 259 : getOpfamilies(Archive *fout)
6801 : {
6802 : PGresult *res;
6803 : int ntups;
6804 : int i;
6805 : PQExpBuffer query;
6806 : OpfamilyInfo *opfinfo;
6807 : int i_tableoid;
6808 : int i_oid;
6809 : int i_opfmethod;
6810 : int i_opfname;
6811 : int i_opfnamespace;
6812 : int i_opfowner;
6813 :
6814 259 : query = createPQExpBuffer();
6815 :
6816 : /*
6817 : * find all opfamilies, including builtin opfamilies; we filter out
6818 : * system-defined opfamilies at dump-out time.
6819 : */
6820 :
6821 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6822 : "opfnamespace, "
6823 : "opfowner "
6824 : "FROM pg_opfamily");
6825 :
6826 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6827 :
6828 259 : ntups = PQntuples(res);
6829 :
6830 259 : opfinfo = pg_malloc_array(OpfamilyInfo, ntups);
6831 :
6832 259 : i_tableoid = PQfnumber(res, "tableoid");
6833 259 : i_oid = PQfnumber(res, "oid");
6834 259 : i_opfname = PQfnumber(res, "opfname");
6835 259 : i_opfmethod = PQfnumber(res, "opfmethod");
6836 259 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6837 259 : i_opfowner = PQfnumber(res, "opfowner");
6838 :
6839 38730 : for (i = 0; i < ntups; i++)
6840 : {
6841 38471 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6842 38471 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6843 38471 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6844 38471 : AssignDumpId(&opfinfo[i].dobj);
6845 38471 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6846 76942 : opfinfo[i].dobj.namespace =
6847 38471 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6848 38471 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6849 38471 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6850 :
6851 : /* Decide whether we want to dump it */
6852 38471 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6853 : }
6854 :
6855 259 : PQclear(res);
6856 :
6857 259 : destroyPQExpBuffer(query);
6858 259 : }
6859 :
6860 : /*
6861 : * getAggregates:
6862 : * get information about all user-defined aggregates in the system catalogs
6863 : */
6864 : void
6865 259 : getAggregates(Archive *fout)
6866 : {
6867 259 : DumpOptions *dopt = fout->dopt;
6868 : PGresult *res;
6869 : int ntups;
6870 : int i;
6871 259 : PQExpBuffer query = createPQExpBuffer();
6872 : AggInfo *agginfo;
6873 : int i_tableoid;
6874 : int i_oid;
6875 : int i_aggname;
6876 : int i_aggnamespace;
6877 : int i_pronargs;
6878 : int i_proargtypes;
6879 : int i_proowner;
6880 : int i_aggacl;
6881 : int i_acldefault;
6882 :
6883 : /*
6884 : * Find all interesting aggregates. See comment in getFuncs() for the
6885 : * rationale behind the filtering logic.
6886 : */
6887 259 : if (fout->remoteVersion >= 90600)
6888 : {
6889 : const char *agg_check;
6890 :
6891 518 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6892 259 : : "p.proisagg");
6893 :
6894 259 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6895 : "p.proname AS aggname, "
6896 : "p.pronamespace AS aggnamespace, "
6897 : "p.pronargs, p.proargtypes, "
6898 : "p.proowner, "
6899 : "p.proacl AS aggacl, "
6900 : "acldefault('f', p.proowner) AS acldefault "
6901 : "FROM pg_proc p "
6902 : "LEFT JOIN pg_init_privs pip ON "
6903 : "(p.oid = pip.objoid "
6904 : "AND pip.classoid = 'pg_proc'::regclass "
6905 : "AND pip.objsubid = 0) "
6906 : "WHERE %s AND ("
6907 : "p.pronamespace != "
6908 : "(SELECT oid FROM pg_namespace "
6909 : "WHERE nspname = 'pg_catalog') OR "
6910 : "p.proacl IS DISTINCT FROM pip.initprivs",
6911 : agg_check);
6912 259 : if (dopt->binary_upgrade)
6913 40 : appendPQExpBufferStr(query,
6914 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6915 : "classid = 'pg_proc'::regclass AND "
6916 : "objid = p.oid AND "
6917 : "refclassid = 'pg_extension'::regclass AND "
6918 : "deptype = 'e')");
6919 259 : appendPQExpBufferChar(query, ')');
6920 : }
6921 : else
6922 : {
6923 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6924 : "pronamespace AS aggnamespace, "
6925 : "pronargs, proargtypes, "
6926 : "proowner, "
6927 : "proacl AS aggacl, "
6928 : "acldefault('f', proowner) AS acldefault "
6929 : "FROM pg_proc p "
6930 : "WHERE proisagg AND ("
6931 : "pronamespace != "
6932 : "(SELECT oid FROM pg_namespace "
6933 : "WHERE nspname = 'pg_catalog')");
6934 0 : if (dopt->binary_upgrade)
6935 0 : appendPQExpBufferStr(query,
6936 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6937 : "classid = 'pg_proc'::regclass AND "
6938 : "objid = p.oid AND "
6939 : "refclassid = 'pg_extension'::regclass AND "
6940 : "deptype = 'e')");
6941 0 : appendPQExpBufferChar(query, ')');
6942 : }
6943 :
6944 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6945 :
6946 259 : ntups = PQntuples(res);
6947 :
6948 259 : agginfo = pg_malloc_array(AggInfo, ntups);
6949 :
6950 259 : i_tableoid = PQfnumber(res, "tableoid");
6951 259 : i_oid = PQfnumber(res, "oid");
6952 259 : i_aggname = PQfnumber(res, "aggname");
6953 259 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6954 259 : i_pronargs = PQfnumber(res, "pronargs");
6955 259 : i_proargtypes = PQfnumber(res, "proargtypes");
6956 259 : i_proowner = PQfnumber(res, "proowner");
6957 259 : i_aggacl = PQfnumber(res, "aggacl");
6958 259 : i_acldefault = PQfnumber(res, "acldefault");
6959 :
6960 658 : for (i = 0; i < ntups; i++)
6961 : {
6962 399 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6963 399 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6964 399 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6965 399 : AssignDumpId(&agginfo[i].aggfn.dobj);
6966 399 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6967 798 : agginfo[i].aggfn.dobj.namespace =
6968 399 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6969 399 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6970 399 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6971 399 : agginfo[i].aggfn.dacl.privtype = 0;
6972 399 : agginfo[i].aggfn.dacl.initprivs = NULL;
6973 399 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6974 399 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6975 399 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6976 399 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6977 399 : if (agginfo[i].aggfn.nargs == 0)
6978 56 : agginfo[i].aggfn.argtypes = NULL;
6979 : else
6980 : {
6981 343 : agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6982 343 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6983 343 : agginfo[i].aggfn.argtypes,
6984 343 : agginfo[i].aggfn.nargs);
6985 : }
6986 399 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6987 :
6988 : /* Decide whether we want to dump it */
6989 399 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6990 :
6991 : /* Mark whether aggregate has an ACL */
6992 399 : if (!PQgetisnull(res, i, i_aggacl))
6993 25 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6994 : }
6995 :
6996 259 : PQclear(res);
6997 :
6998 259 : destroyPQExpBuffer(query);
6999 259 : }
7000 :
7001 : /*
7002 : * getFuncs:
7003 : * get information about all user-defined functions in the system catalogs
7004 : */
7005 : void
7006 259 : getFuncs(Archive *fout)
7007 : {
7008 259 : DumpOptions *dopt = fout->dopt;
7009 : PGresult *res;
7010 : int ntups;
7011 : int i;
7012 259 : PQExpBuffer query = createPQExpBuffer();
7013 : FuncInfo *finfo;
7014 : int i_tableoid;
7015 : int i_oid;
7016 : int i_proname;
7017 : int i_pronamespace;
7018 : int i_proowner;
7019 : int i_prolang;
7020 : int i_pronargs;
7021 : int i_proargtypes;
7022 : int i_prorettype;
7023 : int i_proacl;
7024 : int i_acldefault;
7025 :
7026 : /*
7027 : * Find all interesting functions. This is a bit complicated:
7028 : *
7029 : * 1. Always exclude aggregates; those are handled elsewhere.
7030 : *
7031 : * 2. Always exclude functions that are internally dependent on something
7032 : * else, since presumably those will be created as a result of creating
7033 : * the something else. This currently acts only to suppress constructor
7034 : * functions for range types. Note this is OK only because the
7035 : * constructors don't have any dependencies the range type doesn't have;
7036 : * otherwise we might not get creation ordering correct.
7037 : *
7038 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
7039 : * they're members of extensions and we are in binary-upgrade mode then
7040 : * include them, since we want to dump extension members individually in
7041 : * that mode. Also, if they are used by casts or transforms then we need
7042 : * to gather the information about them, though they won't be dumped if
7043 : * they are built-in. Also, in 9.6 and up, include functions in
7044 : * pg_catalog if they have an ACL different from what's shown in
7045 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
7046 : */
7047 259 : if (fout->remoteVersion >= 90600)
7048 : {
7049 : const char *not_agg_check;
7050 :
7051 518 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
7052 259 : : "NOT p.proisagg");
7053 :
7054 259 : appendPQExpBuffer(query,
7055 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
7056 : "p.pronargs, p.proargtypes, p.prorettype, "
7057 : "p.proacl, "
7058 : "acldefault('f', p.proowner) AS acldefault, "
7059 : "p.pronamespace, "
7060 : "p.proowner "
7061 : "FROM pg_proc p "
7062 : "LEFT JOIN pg_init_privs pip ON "
7063 : "(p.oid = pip.objoid "
7064 : "AND pip.classoid = 'pg_proc'::regclass "
7065 : "AND pip.objsubid = 0) "
7066 : "WHERE %s"
7067 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7068 : "WHERE classid = 'pg_proc'::regclass AND "
7069 : "objid = p.oid AND deptype = 'i')"
7070 : "\n AND ("
7071 : "\n pronamespace != "
7072 : "(SELECT oid FROM pg_namespace "
7073 : "WHERE nspname = 'pg_catalog')"
7074 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7075 : "\n WHERE pg_cast.oid > %u "
7076 : "\n AND p.oid = pg_cast.castfunc)"
7077 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7078 : "\n WHERE pg_transform.oid > %u AND "
7079 : "\n (p.oid = pg_transform.trffromsql"
7080 : "\n OR p.oid = pg_transform.trftosql))",
7081 : not_agg_check,
7082 : g_last_builtin_oid,
7083 : g_last_builtin_oid);
7084 259 : if (dopt->binary_upgrade)
7085 40 : appendPQExpBufferStr(query,
7086 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7087 : "classid = 'pg_proc'::regclass AND "
7088 : "objid = p.oid AND "
7089 : "refclassid = 'pg_extension'::regclass AND "
7090 : "deptype = 'e')");
7091 259 : appendPQExpBufferStr(query,
7092 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7093 259 : appendPQExpBufferChar(query, ')');
7094 : }
7095 : else
7096 : {
7097 0 : appendPQExpBuffer(query,
7098 : "SELECT tableoid, oid, proname, prolang, "
7099 : "pronargs, proargtypes, prorettype, proacl, "
7100 : "acldefault('f', proowner) AS acldefault, "
7101 : "pronamespace, "
7102 : "proowner "
7103 : "FROM pg_proc p "
7104 : "WHERE NOT proisagg"
7105 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7106 : "WHERE classid = 'pg_proc'::regclass AND "
7107 : "objid = p.oid AND deptype = 'i')"
7108 : "\n AND ("
7109 : "\n pronamespace != "
7110 : "(SELECT oid FROM pg_namespace "
7111 : "WHERE nspname = 'pg_catalog')"
7112 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7113 : "\n WHERE pg_cast.oid > '%u'::oid"
7114 : "\n AND p.oid = pg_cast.castfunc)",
7115 : g_last_builtin_oid);
7116 :
7117 0 : if (fout->remoteVersion >= 90500)
7118 0 : appendPQExpBuffer(query,
7119 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7120 : "\n WHERE pg_transform.oid > '%u'::oid"
7121 : "\n AND (p.oid = pg_transform.trffromsql"
7122 : "\n OR p.oid = pg_transform.trftosql))",
7123 : g_last_builtin_oid);
7124 :
7125 0 : if (dopt->binary_upgrade)
7126 0 : appendPQExpBufferStr(query,
7127 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7128 : "classid = 'pg_proc'::regclass AND "
7129 : "objid = p.oid AND "
7130 : "refclassid = 'pg_extension'::regclass AND "
7131 : "deptype = 'e')");
7132 0 : appendPQExpBufferChar(query, ')');
7133 : }
7134 :
7135 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7136 :
7137 259 : ntups = PQntuples(res);
7138 :
7139 259 : finfo = pg_malloc0_array(FuncInfo, ntups);
7140 :
7141 259 : i_tableoid = PQfnumber(res, "tableoid");
7142 259 : i_oid = PQfnumber(res, "oid");
7143 259 : i_proname = PQfnumber(res, "proname");
7144 259 : i_pronamespace = PQfnumber(res, "pronamespace");
7145 259 : i_proowner = PQfnumber(res, "proowner");
7146 259 : i_prolang = PQfnumber(res, "prolang");
7147 259 : i_pronargs = PQfnumber(res, "pronargs");
7148 259 : i_proargtypes = PQfnumber(res, "proargtypes");
7149 259 : i_prorettype = PQfnumber(res, "prorettype");
7150 259 : i_proacl = PQfnumber(res, "proacl");
7151 259 : i_acldefault = PQfnumber(res, "acldefault");
7152 :
7153 5874 : for (i = 0; i < ntups; i++)
7154 : {
7155 5615 : finfo[i].dobj.objType = DO_FUNC;
7156 5615 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7157 5615 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7158 5615 : AssignDumpId(&finfo[i].dobj);
7159 5615 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7160 11230 : finfo[i].dobj.namespace =
7161 5615 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7162 5615 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7163 5615 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7164 5615 : finfo[i].dacl.privtype = 0;
7165 5615 : finfo[i].dacl.initprivs = NULL;
7166 5615 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7167 5615 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7168 5615 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7169 5615 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7170 5615 : if (finfo[i].nargs == 0)
7171 1084 : finfo[i].argtypes = NULL;
7172 : else
7173 : {
7174 4531 : finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7175 4531 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7176 4531 : finfo[i].argtypes, finfo[i].nargs);
7177 : }
7178 5615 : finfo[i].postponed_def = false; /* might get set during sort */
7179 :
7180 : /* Decide whether we want to dump it */
7181 5615 : selectDumpableObject(&(finfo[i].dobj), fout);
7182 :
7183 : /* Mark whether function has an ACL */
7184 5615 : if (!PQgetisnull(res, i, i_proacl))
7185 149 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7186 : }
7187 :
7188 259 : PQclear(res);
7189 :
7190 259 : destroyPQExpBuffer(query);
7191 259 : }
7192 :
7193 : /*
7194 : * getRelationStatistics
7195 : * register the statistics object as a dependent of the relation.
7196 : *
7197 : * reltuples is passed as a string to avoid complexities in converting from/to
7198 : * floating point.
7199 : */
7200 : static RelStatsInfo *
7201 10403 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7202 : char *reltuples, int32 relallvisible,
7203 : int32 relallfrozen, char relkind,
7204 : char **indAttNames, int nindAttNames)
7205 : {
7206 10403 : if (!fout->dopt->dumpStatistics)
7207 6650 : return NULL;
7208 :
7209 3753 : if ((relkind == RELKIND_RELATION) ||
7210 1557 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7211 938 : (relkind == RELKIND_INDEX) ||
7212 620 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7213 317 : (relkind == RELKIND_MATVIEW ||
7214 : relkind == RELKIND_FOREIGN_TABLE))
7215 : {
7216 3468 : RelStatsInfo *info = pg_malloc0_object(RelStatsInfo);
7217 3468 : DumpableObject *dobj = &info->dobj;
7218 :
7219 3468 : dobj->objType = DO_REL_STATS;
7220 3468 : dobj->catId.tableoid = 0;
7221 3468 : dobj->catId.oid = 0;
7222 3468 : AssignDumpId(dobj);
7223 3468 : dobj->dependencies = pg_malloc_object(DumpId);
7224 3468 : dobj->dependencies[0] = rel->dumpId;
7225 3468 : dobj->nDeps = 1;
7226 3468 : dobj->allocDeps = 1;
7227 3468 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7228 3468 : dobj->name = pg_strdup(rel->name);
7229 3468 : dobj->namespace = rel->namespace;
7230 3468 : info->relid = rel->catId.oid;
7231 3468 : info->relpages = relpages;
7232 3468 : info->reltuples = pstrdup(reltuples);
7233 3468 : info->relallvisible = relallvisible;
7234 3468 : info->relallfrozen = relallfrozen;
7235 3468 : info->relkind = relkind;
7236 3468 : info->indAttNames = indAttNames;
7237 3468 : info->nindAttNames = nindAttNames;
7238 :
7239 : /*
7240 : * Ordinarily, stats go in SECTION_DATA for tables and
7241 : * SECTION_POST_DATA for indexes.
7242 : *
7243 : * However, the section may be updated later for materialized view
7244 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7245 : * the stats, so the stats must be restored after the data. Also, the
7246 : * materialized view definition may be postponed to SECTION_POST_DATA
7247 : * (see repairMatViewBoundaryMultiLoop()).
7248 : */
7249 3468 : switch (info->relkind)
7250 : {
7251 2531 : case RELKIND_RELATION:
7252 : case RELKIND_PARTITIONED_TABLE:
7253 : case RELKIND_MATVIEW:
7254 : case RELKIND_FOREIGN_TABLE:
7255 2531 : info->section = SECTION_DATA;
7256 2531 : break;
7257 937 : case RELKIND_INDEX:
7258 : case RELKIND_PARTITIONED_INDEX:
7259 937 : info->section = SECTION_POST_DATA;
7260 937 : break;
7261 0 : default:
7262 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7263 : info->relkind);
7264 : }
7265 :
7266 3468 : return info;
7267 : }
7268 285 : return NULL;
7269 : }
7270 :
7271 : /*
7272 : * getTables
7273 : * read all the tables (no indexes) in the system catalogs,
7274 : * and return them as an array of TableInfo structures
7275 : *
7276 : * *numTables is set to the number of tables read in
7277 : */
7278 : TableInfo *
7279 260 : getTables(Archive *fout, int *numTables)
7280 : {
7281 260 : DumpOptions *dopt = fout->dopt;
7282 : PGresult *res;
7283 : int ntups;
7284 : int i;
7285 260 : PQExpBuffer query = createPQExpBuffer();
7286 : TableInfo *tblinfo;
7287 : int i_reltableoid;
7288 : int i_reloid;
7289 : int i_relname;
7290 : int i_relnamespace;
7291 : int i_relkind;
7292 : int i_reltype;
7293 : int i_relowner;
7294 : int i_relchecks;
7295 : int i_relhasindex;
7296 : int i_relhasrules;
7297 : int i_relpages;
7298 : int i_reltuples;
7299 : int i_relallvisible;
7300 : int i_relallfrozen;
7301 : int i_toastpages;
7302 : int i_owning_tab;
7303 : int i_owning_col;
7304 : int i_reltablespace;
7305 : int i_relhasoids;
7306 : int i_relhastriggers;
7307 : int i_relpersistence;
7308 : int i_relispopulated;
7309 : int i_relreplident;
7310 : int i_relrowsec;
7311 : int i_relforcerowsec;
7312 : int i_relfrozenxid;
7313 : int i_toastfrozenxid;
7314 : int i_toastoid;
7315 : int i_relminmxid;
7316 : int i_toastminmxid;
7317 : int i_reloptions;
7318 : int i_checkoption;
7319 : int i_toastreloptions;
7320 : int i_reloftype;
7321 : int i_foreignserver;
7322 : int i_amname;
7323 : int i_is_identity_sequence;
7324 : int i_relacl;
7325 : int i_acldefault;
7326 : int i_ispartition;
7327 :
7328 : /*
7329 : * Find all the tables and table-like objects.
7330 : *
7331 : * We must fetch all tables in this phase because otherwise we cannot
7332 : * correctly identify inherited columns, owned sequences, etc.
7333 : *
7334 : * We include system catalogs, so that we can work if a user table is
7335 : * defined to inherit from a system catalog (pretty weird, but...)
7336 : *
7337 : * Note: in this phase we should collect only a minimal amount of
7338 : * information about each table, basically just enough to decide if it is
7339 : * interesting. In particular, since we do not yet have lock on any user
7340 : * table, we MUST NOT invoke any server-side data collection functions
7341 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7342 : * wrong answers if any concurrent DDL is happening.
7343 : */
7344 :
7345 260 : appendPQExpBufferStr(query,
7346 : "SELECT c.tableoid, c.oid, c.relname, "
7347 : "c.relnamespace, c.relkind, c.reltype, "
7348 : "c.relowner, "
7349 : "c.relchecks, "
7350 : "c.relhasindex, c.relhasrules, c.relpages, "
7351 : "c.reltuples, c.relallvisible, ");
7352 :
7353 260 : if (fout->remoteVersion >= 180000)
7354 260 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7355 : else
7356 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7357 :
7358 260 : appendPQExpBufferStr(query,
7359 : "c.relhastriggers, c.relpersistence, "
7360 : "c.reloftype, "
7361 : "c.relacl, "
7362 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7363 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7364 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7365 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7366 : "ELSE 0 END AS foreignserver, "
7367 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7368 : "tc.oid AS toid, "
7369 : "tc.relpages AS toastpages, "
7370 : "tc.reloptions AS toast_reloptions, "
7371 : "d.refobjid AS owning_tab, "
7372 : "d.refobjsubid AS owning_col, "
7373 : "tsp.spcname AS reltablespace, ");
7374 :
7375 260 : if (fout->remoteVersion >= 120000)
7376 260 : appendPQExpBufferStr(query,
7377 : "false AS relhasoids, ");
7378 : else
7379 0 : appendPQExpBufferStr(query,
7380 : "c.relhasoids, ");
7381 :
7382 260 : if (fout->remoteVersion >= 90300)
7383 260 : appendPQExpBufferStr(query,
7384 : "c.relispopulated, ");
7385 : else
7386 0 : appendPQExpBufferStr(query,
7387 : "'t' as relispopulated, ");
7388 :
7389 260 : if (fout->remoteVersion >= 90400)
7390 260 : appendPQExpBufferStr(query,
7391 : "c.relreplident, ");
7392 : else
7393 0 : appendPQExpBufferStr(query,
7394 : "'d' AS relreplident, ");
7395 :
7396 260 : if (fout->remoteVersion >= 90500)
7397 260 : appendPQExpBufferStr(query,
7398 : "c.relrowsecurity, c.relforcerowsecurity, ");
7399 : else
7400 0 : appendPQExpBufferStr(query,
7401 : "false AS relrowsecurity, "
7402 : "false AS relforcerowsecurity, ");
7403 :
7404 260 : if (fout->remoteVersion >= 90300)
7405 260 : appendPQExpBufferStr(query,
7406 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7407 : else
7408 0 : appendPQExpBufferStr(query,
7409 : "0 AS relminmxid, 0 AS tminmxid, ");
7410 :
7411 260 : if (fout->remoteVersion >= 90300)
7412 260 : appendPQExpBufferStr(query,
7413 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7414 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7415 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7416 : else
7417 0 : appendPQExpBufferStr(query,
7418 : "c.reloptions, NULL AS checkoption, ");
7419 :
7420 260 : if (fout->remoteVersion >= 90600)
7421 260 : appendPQExpBufferStr(query,
7422 : "am.amname, ");
7423 : else
7424 0 : appendPQExpBufferStr(query,
7425 : "NULL AS amname, ");
7426 :
7427 260 : if (fout->remoteVersion >= 90600)
7428 260 : appendPQExpBufferStr(query,
7429 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7430 : else
7431 0 : appendPQExpBufferStr(query,
7432 : "false AS is_identity_sequence, ");
7433 :
7434 260 : if (fout->remoteVersion >= 100000)
7435 260 : appendPQExpBufferStr(query,
7436 : "c.relispartition AS ispartition ");
7437 : else
7438 0 : appendPQExpBufferStr(query,
7439 : "false AS ispartition ");
7440 :
7441 : /*
7442 : * Left join to pg_depend to pick up dependency info linking sequences to
7443 : * their owning column, if any (note this dependency is AUTO except for
7444 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7445 : * collect the spcname.
7446 : */
7447 260 : appendPQExpBufferStr(query,
7448 : "\nFROM pg_class c\n"
7449 : "LEFT JOIN pg_depend d ON "
7450 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7451 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7452 : "d.objsubid = 0 AND "
7453 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7454 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7455 :
7456 : /*
7457 : * In 9.6 and up, left join to pg_am to pick up the amname.
7458 : */
7459 260 : if (fout->remoteVersion >= 90600)
7460 260 : appendPQExpBufferStr(query,
7461 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7462 :
7463 : /*
7464 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7465 : * that versions 10 and 11 have them, but later versions do not, so
7466 : * emitting them causes the upgrade to fail.
7467 : */
7468 260 : appendPQExpBufferStr(query,
7469 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7470 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7471 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7472 :
7473 : /*
7474 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7475 : * relkinds are possible in older servers, but it's not worth the trouble
7476 : * to emit a version-dependent list.
7477 : *
7478 : * Composite-type table entries won't be dumped as such, but we have to
7479 : * make a DumpableObject for them so that we can track dependencies of the
7480 : * composite type (pg_depend entries for columns of the composite type
7481 : * link to the pg_class entry not the pg_type entry).
7482 : */
7483 260 : appendPQExpBufferStr(query,
7484 : "WHERE c.relkind IN ("
7485 : CppAsString2(RELKIND_RELATION) ", "
7486 : CppAsString2(RELKIND_SEQUENCE) ", "
7487 : CppAsString2(RELKIND_VIEW) ", "
7488 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7489 : CppAsString2(RELKIND_MATVIEW) ", "
7490 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7491 : CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
7492 : CppAsString2(RELKIND_PROPGRAPH) ")\n"
7493 : "ORDER BY c.oid");
7494 :
7495 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7496 :
7497 260 : ntups = PQntuples(res);
7498 :
7499 260 : *numTables = ntups;
7500 :
7501 : /*
7502 : * Extract data from result and lock dumpable tables. We do the locking
7503 : * before anything else, to minimize the window wherein a table could
7504 : * disappear under us.
7505 : *
7506 : * Note that we have to save info about all tables here, even when dumping
7507 : * only one, because we don't yet know which tables might be inheritance
7508 : * ancestors of the target table.
7509 : */
7510 260 : tblinfo = pg_malloc0_array(TableInfo, ntups);
7511 :
7512 260 : i_reltableoid = PQfnumber(res, "tableoid");
7513 260 : i_reloid = PQfnumber(res, "oid");
7514 260 : i_relname = PQfnumber(res, "relname");
7515 260 : i_relnamespace = PQfnumber(res, "relnamespace");
7516 260 : i_relkind = PQfnumber(res, "relkind");
7517 260 : i_reltype = PQfnumber(res, "reltype");
7518 260 : i_relowner = PQfnumber(res, "relowner");
7519 260 : i_relchecks = PQfnumber(res, "relchecks");
7520 260 : i_relhasindex = PQfnumber(res, "relhasindex");
7521 260 : i_relhasrules = PQfnumber(res, "relhasrules");
7522 260 : i_relpages = PQfnumber(res, "relpages");
7523 260 : i_reltuples = PQfnumber(res, "reltuples");
7524 260 : i_relallvisible = PQfnumber(res, "relallvisible");
7525 260 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7526 260 : i_toastpages = PQfnumber(res, "toastpages");
7527 260 : i_owning_tab = PQfnumber(res, "owning_tab");
7528 260 : i_owning_col = PQfnumber(res, "owning_col");
7529 260 : i_reltablespace = PQfnumber(res, "reltablespace");
7530 260 : i_relhasoids = PQfnumber(res, "relhasoids");
7531 260 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7532 260 : i_relpersistence = PQfnumber(res, "relpersistence");
7533 260 : i_relispopulated = PQfnumber(res, "relispopulated");
7534 260 : i_relreplident = PQfnumber(res, "relreplident");
7535 260 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7536 260 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7537 260 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7538 260 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7539 260 : i_toastoid = PQfnumber(res, "toid");
7540 260 : i_relminmxid = PQfnumber(res, "relminmxid");
7541 260 : i_toastminmxid = PQfnumber(res, "tminmxid");
7542 260 : i_reloptions = PQfnumber(res, "reloptions");
7543 260 : i_checkoption = PQfnumber(res, "checkoption");
7544 260 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7545 260 : i_reloftype = PQfnumber(res, "reloftype");
7546 260 : i_foreignserver = PQfnumber(res, "foreignserver");
7547 260 : i_amname = PQfnumber(res, "amname");
7548 260 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7549 260 : i_relacl = PQfnumber(res, "relacl");
7550 260 : i_acldefault = PQfnumber(res, "acldefault");
7551 260 : i_ispartition = PQfnumber(res, "ispartition");
7552 :
7553 260 : if (dopt->lockWaitTimeout)
7554 : {
7555 : /*
7556 : * Arrange to fail instead of waiting forever for a table lock.
7557 : *
7558 : * NB: this coding assumes that the only queries issued within the
7559 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7560 : * applied to other things too.
7561 : */
7562 2 : resetPQExpBuffer(query);
7563 2 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7564 2 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7565 2 : ExecuteSqlStatement(fout, query->data);
7566 : }
7567 :
7568 260 : resetPQExpBuffer(query);
7569 :
7570 70452 : for (i = 0; i < ntups; i++)
7571 : {
7572 70192 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7573 70192 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7574 :
7575 70192 : tblinfo[i].dobj.objType = DO_TABLE;
7576 70192 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7577 70192 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7578 70192 : AssignDumpId(&tblinfo[i].dobj);
7579 70192 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7580 140384 : tblinfo[i].dobj.namespace =
7581 70192 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7582 70192 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7583 70192 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7584 70192 : tblinfo[i].dacl.privtype = 0;
7585 70192 : tblinfo[i].dacl.initprivs = NULL;
7586 70192 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7587 70192 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7588 70192 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7589 70192 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7590 70192 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7591 70192 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7592 70192 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7593 70192 : if (PQgetisnull(res, i, i_toastpages))
7594 56998 : tblinfo[i].toastpages = 0;
7595 : else
7596 13194 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7597 70192 : if (PQgetisnull(res, i, i_owning_tab))
7598 : {
7599 69777 : tblinfo[i].owning_tab = InvalidOid;
7600 69777 : tblinfo[i].owning_col = 0;
7601 : }
7602 : else
7603 : {
7604 415 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7605 415 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7606 : }
7607 70192 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7608 70192 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7609 70192 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7610 70192 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7611 70192 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7612 70192 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7613 70192 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7614 70192 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7615 70192 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7616 70192 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7617 70192 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7618 70192 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7619 70192 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7620 70192 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7621 70192 : if (PQgetisnull(res, i, i_checkoption))
7622 70146 : tblinfo[i].checkoption = NULL;
7623 : else
7624 46 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7625 70192 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7626 70192 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7627 70192 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7628 70192 : if (PQgetisnull(res, i, i_amname))
7629 43570 : tblinfo[i].amname = NULL;
7630 : else
7631 26622 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7632 70192 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7633 70192 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7634 :
7635 : /* other fields were zeroed above */
7636 :
7637 : /*
7638 : * Decide whether we want to dump this table.
7639 : */
7640 70192 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7641 183 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7642 : else
7643 70009 : selectDumpableTable(&tblinfo[i], fout);
7644 :
7645 : /*
7646 : * Now, consider the table "interesting" if we need to dump its
7647 : * definition, data or its statistics. Later on, we'll skip a lot of
7648 : * data collection for uninteresting tables.
7649 : *
7650 : * Note: the "interesting" flag will also be set by flagInhTables for
7651 : * parents of interesting tables, so that we collect necessary
7652 : * inheritance info even when the parents are not themselves being
7653 : * dumped. This is the main reason why we need an "interesting" flag
7654 : * that's separate from the components-to-dump bitmask.
7655 : */
7656 70192 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7657 : (DUMP_COMPONENT_DEFINITION |
7658 : DUMP_COMPONENT_DATA |
7659 70192 : DUMP_COMPONENT_STATISTICS)) != 0;
7660 :
7661 70192 : tblinfo[i].dummy_view = false; /* might get set during sort */
7662 70192 : tblinfo[i].postponed_def = false; /* might get set during sort */
7663 :
7664 : /* Tables have data */
7665 70192 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7666 :
7667 : /* Mark whether table has an ACL */
7668 70192 : if (!PQgetisnull(res, i, i_relacl))
7669 59174 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7670 70192 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7671 :
7672 : /* Add statistics */
7673 70192 : if (tblinfo[i].interesting)
7674 : {
7675 : RelStatsInfo *stats;
7676 :
7677 15272 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7678 7636 : tblinfo[i].relpages,
7679 : PQgetvalue(res, i, i_reltuples),
7680 : relallvisible, relallfrozen,
7681 7636 : tblinfo[i].relkind, NULL, 0);
7682 7636 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7683 398 : tblinfo[i].stats = stats;
7684 : }
7685 :
7686 : /*
7687 : * Read-lock target tables to make sure they aren't DROPPED or altered
7688 : * in schema before we get around to dumping them.
7689 : *
7690 : * Note that we don't explicitly lock parents of the target tables; we
7691 : * assume our lock on the child is enough to prevent schema
7692 : * alterations to parent tables.
7693 : *
7694 : * NOTE: it'd be kinda nice to lock other relations too, not only
7695 : * plain or partitioned tables, but the backend doesn't presently
7696 : * allow that.
7697 : *
7698 : * We only need to lock the table for certain components; see
7699 : * pg_dump.h
7700 : */
7701 70192 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7702 7636 : (tblinfo[i].relkind == RELKIND_RELATION ||
7703 2128 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7704 : {
7705 : /*
7706 : * Tables are locked in batches. When dumping from a remote
7707 : * server this can save a significant amount of time by reducing
7708 : * the number of round trips.
7709 : */
7710 6127 : if (query->len == 0)
7711 177 : appendPQExpBuffer(query, "LOCK TABLE %s",
7712 177 : fmtQualifiedDumpable(&tblinfo[i]));
7713 : else
7714 : {
7715 5950 : appendPQExpBuffer(query, ", %s",
7716 5950 : fmtQualifiedDumpable(&tblinfo[i]));
7717 :
7718 : /* Arbitrarily end a batch when query length reaches 100K. */
7719 5950 : if (query->len >= 100000)
7720 : {
7721 : /* Lock another batch of tables. */
7722 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7723 0 : ExecuteSqlStatement(fout, query->data);
7724 0 : resetPQExpBuffer(query);
7725 : }
7726 : }
7727 : }
7728 : }
7729 :
7730 260 : if (query->len != 0)
7731 : {
7732 : /* Lock the tables in the last batch. */
7733 177 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7734 177 : ExecuteSqlStatement(fout, query->data);
7735 : }
7736 :
7737 259 : if (dopt->lockWaitTimeout)
7738 : {
7739 2 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7740 : }
7741 :
7742 259 : PQclear(res);
7743 :
7744 259 : destroyPQExpBuffer(query);
7745 :
7746 259 : return tblinfo;
7747 : }
7748 :
7749 : /*
7750 : * getOwnedSeqs
7751 : * identify owned sequences and mark them as dumpable if owning table is
7752 : *
7753 : * We used to do this in getTables(), but it's better to do it after the
7754 : * index used by findTableByOid() has been set up.
7755 : */
7756 : void
7757 259 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7758 : {
7759 : int i;
7760 :
7761 : /*
7762 : * Force sequences that are "owned" by table columns to be dumped whenever
7763 : * their owning table is being dumped.
7764 : */
7765 70160 : for (i = 0; i < numTables; i++)
7766 : {
7767 69901 : TableInfo *seqinfo = &tblinfo[i];
7768 : TableInfo *owning_tab;
7769 :
7770 69901 : if (!OidIsValid(seqinfo->owning_tab))
7771 69489 : continue; /* not an owned sequence */
7772 :
7773 412 : owning_tab = findTableByOid(seqinfo->owning_tab);
7774 412 : if (owning_tab == NULL)
7775 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7776 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7777 :
7778 : /*
7779 : * For an identity sequence, dump exactly the same components for the
7780 : * sequence as for the owning table. This is important because we
7781 : * treat the identity sequence as an integral part of the table. For
7782 : * example, there is not any DDL command that allows creation of such
7783 : * a sequence independently of the table.
7784 : *
7785 : * For other owned sequences such as serial sequences, we need to dump
7786 : * the components that are being dumped for the table and any
7787 : * components that the sequence is explicitly marked with.
7788 : *
7789 : * We can't simply use the set of components which are being dumped
7790 : * for the table as the table might be in an extension (and only the
7791 : * non-extension components, eg: ACLs if changed, security labels, and
7792 : * policies, are being dumped) while the sequence is not (and
7793 : * therefore the definition and other components should also be
7794 : * dumped).
7795 : *
7796 : * If the sequence is part of the extension then it should be properly
7797 : * marked by checkExtensionMembership() and this will be a no-op as
7798 : * the table will be equivalently marked.
7799 : */
7800 412 : if (seqinfo->is_identity_sequence)
7801 199 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7802 : else
7803 213 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7804 :
7805 : /* Make sure that necessary data is available if we're dumping it */
7806 412 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7807 : {
7808 316 : seqinfo->interesting = true;
7809 316 : owning_tab->interesting = true;
7810 : }
7811 : }
7812 259 : }
7813 :
7814 : /*
7815 : * getInherits
7816 : * read all the inheritance information
7817 : * from the system catalogs return them in the InhInfo* structure
7818 : *
7819 : * numInherits is set to the number of pairs read in
7820 : */
7821 : InhInfo *
7822 259 : getInherits(Archive *fout, int *numInherits)
7823 : {
7824 : PGresult *res;
7825 : int ntups;
7826 : int i;
7827 259 : PQExpBuffer query = createPQExpBuffer();
7828 : InhInfo *inhinfo;
7829 :
7830 : int i_inhrelid;
7831 : int i_inhparent;
7832 :
7833 : /* find all the inheritance information */
7834 259 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7835 :
7836 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7837 :
7838 259 : ntups = PQntuples(res);
7839 :
7840 259 : *numInherits = ntups;
7841 :
7842 259 : inhinfo = pg_malloc_array(InhInfo, ntups);
7843 :
7844 259 : i_inhrelid = PQfnumber(res, "inhrelid");
7845 259 : i_inhparent = PQfnumber(res, "inhparent");
7846 :
7847 3798 : for (i = 0; i < ntups; i++)
7848 : {
7849 3539 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7850 3539 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7851 : }
7852 :
7853 259 : PQclear(res);
7854 :
7855 259 : destroyPQExpBuffer(query);
7856 :
7857 259 : return inhinfo;
7858 : }
7859 :
7860 : /*
7861 : * getPartitioningInfo
7862 : * get information about partitioning
7863 : *
7864 : * For the most part, we only collect partitioning info about tables we
7865 : * intend to dump. However, this function has to consider all partitioned
7866 : * tables in the database, because we need to know about parents of partitions
7867 : * we are going to dump even if the parents themselves won't be dumped.
7868 : *
7869 : * Specifically, what we need to know is whether each partitioned table
7870 : * has an "unsafe" partitioning scheme that requires us to force
7871 : * load-via-partition-root mode for its children. Currently the only case
7872 : * for which we force that is hash partitioning on enum columns, since the
7873 : * hash codes depend on enum value OIDs which won't be replicated across
7874 : * dump-and-reload. There are other cases in which load-via-partition-root
7875 : * might be necessary, but we expect users to cope with them.
7876 : */
7877 : void
7878 259 : getPartitioningInfo(Archive *fout)
7879 : {
7880 : PQExpBuffer query;
7881 : PGresult *res;
7882 : int ntups;
7883 :
7884 : /* hash partitioning didn't exist before v11 */
7885 259 : if (fout->remoteVersion < 110000)
7886 0 : return;
7887 : /* needn't bother if not dumping data */
7888 259 : if (!fout->dopt->dumpData)
7889 44 : return;
7890 :
7891 215 : query = createPQExpBuffer();
7892 :
7893 : /*
7894 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7895 : * appears among the partition opclasses. We needn't check partstrat.
7896 : *
7897 : * Note that this query may well retrieve info about tables we aren't
7898 : * going to dump and hence have no lock on. That's okay since we need not
7899 : * invoke any unsafe server-side functions.
7900 : */
7901 215 : appendPQExpBufferStr(query,
7902 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7903 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7904 : "ON c.opcmethod = a.oid\n"
7905 : "WHERE opcname = 'enum_ops' "
7906 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7907 : "AND amname = 'hash') = ANY(partclass)");
7908 :
7909 215 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7910 :
7911 215 : ntups = PQntuples(res);
7912 :
7913 258 : for (int i = 0; i < ntups; i++)
7914 : {
7915 43 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7916 : TableInfo *tbinfo;
7917 :
7918 43 : tbinfo = findTableByOid(tabrelid);
7919 43 : if (tbinfo == NULL)
7920 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7921 : tabrelid);
7922 43 : tbinfo->unsafe_partitions = true;
7923 : }
7924 :
7925 215 : PQclear(res);
7926 :
7927 215 : destroyPQExpBuffer(query);
7928 : }
7929 :
7930 : /*
7931 : * getIndexes
7932 : * get information about every index on a dumpable table
7933 : *
7934 : * Note: index data is not returned directly to the caller, but it
7935 : * does get entered into the DumpableObject tables.
7936 : */
7937 : void
7938 259 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7939 : {
7940 259 : PQExpBuffer query = createPQExpBuffer();
7941 259 : PQExpBuffer tbloids = createPQExpBuffer();
7942 : PGresult *res;
7943 : int ntups;
7944 : int curtblindx;
7945 : IndxInfo *indxinfo;
7946 : int i_tableoid,
7947 : i_oid,
7948 : i_indrelid,
7949 : i_indexname,
7950 : i_relpages,
7951 : i_reltuples,
7952 : i_relallvisible,
7953 : i_relallfrozen,
7954 : i_parentidx,
7955 : i_indexdef,
7956 : i_indnkeyatts,
7957 : i_indnatts,
7958 : i_indkey,
7959 : i_indisclustered,
7960 : i_indisreplident,
7961 : i_indnullsnotdistinct,
7962 : i_contype,
7963 : i_conname,
7964 : i_condeferrable,
7965 : i_condeferred,
7966 : i_conperiod,
7967 : i_contableoid,
7968 : i_conoid,
7969 : i_condef,
7970 : i_indattnames,
7971 : i_tablespace,
7972 : i_indreloptions,
7973 : i_indstatcols,
7974 : i_indstatvals;
7975 :
7976 : /*
7977 : * We want to perform just one query against pg_index. However, we
7978 : * mustn't try to select every row of the catalog and then sort it out on
7979 : * the client side, because some of the server-side functions we need
7980 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7981 : * build an array of the OIDs of tables we care about (and now have lock
7982 : * on!), and use a WHERE clause to constrain which rows are selected.
7983 : */
7984 259 : appendPQExpBufferChar(tbloids, '{');
7985 70160 : for (int i = 0; i < numTables; i++)
7986 : {
7987 69901 : TableInfo *tbinfo = &tblinfo[i];
7988 :
7989 69901 : if (!tbinfo->hasindex)
7990 49463 : continue;
7991 :
7992 : /*
7993 : * We can ignore indexes of uninteresting tables.
7994 : */
7995 20438 : if (!tbinfo->interesting)
7996 18295 : continue;
7997 :
7998 : /* OK, we need info for this table */
7999 2143 : if (tbloids->len > 1) /* do we have more than the '{'? */
8000 2064 : appendPQExpBufferChar(tbloids, ',');
8001 2143 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8002 : }
8003 259 : appendPQExpBufferChar(tbloids, '}');
8004 :
8005 259 : appendPQExpBufferStr(query,
8006 : "SELECT t.tableoid, t.oid, i.indrelid, "
8007 : "t.relname AS indexname, "
8008 : "t.relpages, t.reltuples, t.relallvisible, ");
8009 :
8010 259 : if (fout->remoteVersion >= 180000)
8011 259 : appendPQExpBufferStr(query, "t.relallfrozen, ");
8012 : else
8013 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
8014 :
8015 259 : appendPQExpBufferStr(query,
8016 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
8017 : "i.indkey, i.indisclustered, "
8018 : "c.contype, c.conname, "
8019 : "c.condeferrable, c.condeferred, "
8020 : "c.tableoid AS contableoid, "
8021 : "c.oid AS conoid, "
8022 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
8023 : "CASE WHEN i.indexprs IS NOT NULL THEN "
8024 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
8025 : " FROM pg_catalog.pg_attribute "
8026 : " WHERE attrelid = i.indexrelid) "
8027 : "ELSE NULL END AS indattnames, "
8028 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
8029 : "t.reloptions AS indreloptions, ");
8030 :
8031 :
8032 259 : if (fout->remoteVersion >= 90400)
8033 259 : appendPQExpBufferStr(query,
8034 : "i.indisreplident, ");
8035 : else
8036 0 : appendPQExpBufferStr(query,
8037 : "false AS indisreplident, ");
8038 :
8039 259 : if (fout->remoteVersion >= 110000)
8040 259 : appendPQExpBufferStr(query,
8041 : "inh.inhparent AS parentidx, "
8042 : "i.indnkeyatts AS indnkeyatts, "
8043 : "i.indnatts AS indnatts, "
8044 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
8045 : " FROM pg_catalog.pg_attribute "
8046 : " WHERE attrelid = i.indexrelid AND "
8047 : " attstattarget >= 0) AS indstatcols, "
8048 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
8049 : " FROM pg_catalog.pg_attribute "
8050 : " WHERE attrelid = i.indexrelid AND "
8051 : " attstattarget >= 0) AS indstatvals, ");
8052 : else
8053 0 : appendPQExpBufferStr(query,
8054 : "0 AS parentidx, "
8055 : "i.indnatts AS indnkeyatts, "
8056 : "i.indnatts AS indnatts, "
8057 : "'' AS indstatcols, "
8058 : "'' AS indstatvals, ");
8059 :
8060 259 : if (fout->remoteVersion >= 150000)
8061 259 : appendPQExpBufferStr(query,
8062 : "i.indnullsnotdistinct, ");
8063 : else
8064 0 : appendPQExpBufferStr(query,
8065 : "false AS indnullsnotdistinct, ");
8066 :
8067 259 : if (fout->remoteVersion >= 180000)
8068 259 : appendPQExpBufferStr(query,
8069 : "c.conperiod ");
8070 : else
8071 0 : appendPQExpBufferStr(query,
8072 : "NULL AS conperiod ");
8073 :
8074 : /*
8075 : * The point of the messy-looking outer join is to find a constraint that
8076 : * is related by an internal dependency link to the index. If we find one,
8077 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
8078 : * index won't have more than one internal dependency.
8079 : *
8080 : * Note: the check on conrelid is redundant, but useful because that
8081 : * column is indexed while conindid is not.
8082 : */
8083 259 : if (fout->remoteVersion >= 110000)
8084 : {
8085 259 : appendPQExpBuffer(query,
8086 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8087 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8088 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8089 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
8090 : "LEFT JOIN pg_catalog.pg_constraint c "
8091 : "ON (i.indrelid = c.conrelid AND "
8092 : "i.indexrelid = c.conindid AND "
8093 : "c.contype IN ('p','u','x')) "
8094 : "LEFT JOIN pg_catalog.pg_inherits inh "
8095 : "ON (inh.inhrelid = indexrelid) "
8096 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
8097 : "AND i.indisready "
8098 : "ORDER BY i.indrelid, indexname",
8099 : tbloids->data);
8100 : }
8101 : else
8102 : {
8103 : /*
8104 : * the test on indisready is necessary in 9.2, and harmless in
8105 : * earlier/later versions
8106 : */
8107 0 : appendPQExpBuffer(query,
8108 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8109 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8110 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8111 : "LEFT JOIN pg_catalog.pg_constraint c "
8112 : "ON (i.indrelid = c.conrelid AND "
8113 : "i.indexrelid = c.conindid AND "
8114 : "c.contype IN ('p','u','x')) "
8115 : "WHERE i.indisvalid AND i.indisready "
8116 : "ORDER BY i.indrelid, indexname",
8117 : tbloids->data);
8118 : }
8119 :
8120 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8121 :
8122 259 : ntups = PQntuples(res);
8123 :
8124 259 : i_tableoid = PQfnumber(res, "tableoid");
8125 259 : i_oid = PQfnumber(res, "oid");
8126 259 : i_indrelid = PQfnumber(res, "indrelid");
8127 259 : i_indexname = PQfnumber(res, "indexname");
8128 259 : i_relpages = PQfnumber(res, "relpages");
8129 259 : i_reltuples = PQfnumber(res, "reltuples");
8130 259 : i_relallvisible = PQfnumber(res, "relallvisible");
8131 259 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8132 259 : i_parentidx = PQfnumber(res, "parentidx");
8133 259 : i_indexdef = PQfnumber(res, "indexdef");
8134 259 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8135 259 : i_indnatts = PQfnumber(res, "indnatts");
8136 259 : i_indkey = PQfnumber(res, "indkey");
8137 259 : i_indisclustered = PQfnumber(res, "indisclustered");
8138 259 : i_indisreplident = PQfnumber(res, "indisreplident");
8139 259 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8140 259 : i_contype = PQfnumber(res, "contype");
8141 259 : i_conname = PQfnumber(res, "conname");
8142 259 : i_condeferrable = PQfnumber(res, "condeferrable");
8143 259 : i_condeferred = PQfnumber(res, "condeferred");
8144 259 : i_conperiod = PQfnumber(res, "conperiod");
8145 259 : i_contableoid = PQfnumber(res, "contableoid");
8146 259 : i_conoid = PQfnumber(res, "conoid");
8147 259 : i_condef = PQfnumber(res, "condef");
8148 259 : i_indattnames = PQfnumber(res, "indattnames");
8149 259 : i_tablespace = PQfnumber(res, "tablespace");
8150 259 : i_indreloptions = PQfnumber(res, "indreloptions");
8151 259 : i_indstatcols = PQfnumber(res, "indstatcols");
8152 259 : i_indstatvals = PQfnumber(res, "indstatvals");
8153 :
8154 259 : indxinfo = pg_malloc_array(IndxInfo, ntups);
8155 :
8156 : /*
8157 : * Outer loop iterates once per table, not once per row. Incrementing of
8158 : * j is handled by the inner loop.
8159 : */
8160 259 : curtblindx = -1;
8161 2386 : for (int j = 0; j < ntups;)
8162 : {
8163 2127 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8164 2127 : TableInfo *tbinfo = NULL;
8165 2127 : char **indAttNames = NULL;
8166 2127 : int nindAttNames = 0;
8167 : int numinds;
8168 :
8169 : /* Count rows for this table */
8170 2767 : for (numinds = 1; numinds < ntups - j; numinds++)
8171 2688 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8172 2048 : break;
8173 :
8174 : /*
8175 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8176 : * order.
8177 : */
8178 25006 : while (++curtblindx < numTables)
8179 : {
8180 25006 : tbinfo = &tblinfo[curtblindx];
8181 25006 : if (tbinfo->dobj.catId.oid == indrelid)
8182 2127 : break;
8183 : }
8184 2127 : if (curtblindx >= numTables)
8185 0 : pg_fatal("unrecognized table OID %u", indrelid);
8186 : /* cross-check that we only got requested tables */
8187 2127 : if (!tbinfo->hasindex ||
8188 2127 : !tbinfo->interesting)
8189 0 : pg_fatal("unexpected index data for table \"%s\"",
8190 : tbinfo->dobj.name);
8191 :
8192 : /* Save data for this table */
8193 2127 : tbinfo->indexes = indxinfo + j;
8194 2127 : tbinfo->numIndexes = numinds;
8195 :
8196 4894 : for (int c = 0; c < numinds; c++, j++)
8197 : {
8198 : char contype;
8199 : char indexkind;
8200 : RelStatsInfo *relstats;
8201 2767 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8202 2767 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8203 2767 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8204 :
8205 2767 : indxinfo[j].dobj.objType = DO_INDEX;
8206 2767 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8207 2767 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8208 2767 : AssignDumpId(&indxinfo[j].dobj);
8209 2767 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8210 2767 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8211 2767 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8212 2767 : indxinfo[j].indextable = tbinfo;
8213 2767 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8214 2767 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8215 2767 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8216 2767 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8217 2767 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8218 2767 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8219 2767 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8220 2767 : indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8221 2767 : parseOidArray(PQgetvalue(res, j, i_indkey),
8222 2767 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8223 2767 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8224 2767 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8225 2767 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8226 2767 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8227 2767 : indxinfo[j].partattaches = (SimplePtrList)
8228 : {
8229 : NULL, NULL
8230 : };
8231 :
8232 2767 : if (indxinfo[j].parentidx == 0)
8233 2173 : indexkind = RELKIND_INDEX;
8234 : else
8235 594 : indexkind = RELKIND_PARTITIONED_INDEX;
8236 :
8237 2767 : if (!PQgetisnull(res, j, i_indattnames))
8238 : {
8239 146 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8240 : &indAttNames, &nindAttNames))
8241 0 : pg_fatal("could not parse %s array", "indattnames");
8242 : }
8243 :
8244 2767 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8245 : PQgetvalue(res, j, i_reltuples),
8246 : relallvisible, relallfrozen, indexkind,
8247 : indAttNames, nindAttNames);
8248 :
8249 2767 : contype = *(PQgetvalue(res, j, i_contype));
8250 2767 : if (contype == 'p' || contype == 'u' || contype == 'x')
8251 1672 : {
8252 : /*
8253 : * If we found a constraint matching the index, create an
8254 : * entry for it.
8255 : */
8256 : ConstraintInfo *constrinfo;
8257 :
8258 1672 : constrinfo = pg_malloc_object(ConstraintInfo);
8259 1672 : constrinfo->dobj.objType = DO_CONSTRAINT;
8260 1672 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8261 1672 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8262 1672 : AssignDumpId(&constrinfo->dobj);
8263 1672 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8264 1672 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8265 1672 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8266 1672 : constrinfo->contable = tbinfo;
8267 1672 : constrinfo->condomain = NULL;
8268 1672 : constrinfo->contype = contype;
8269 1672 : if (contype == 'x')
8270 10 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8271 : else
8272 1662 : constrinfo->condef = NULL;
8273 1672 : constrinfo->confrelid = InvalidOid;
8274 1672 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8275 1672 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8276 1672 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8277 1672 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8278 1672 : constrinfo->conislocal = true;
8279 1672 : constrinfo->separate = true;
8280 :
8281 1672 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8282 1672 : if (relstats != NULL)
8283 555 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8284 : }
8285 : else
8286 : {
8287 : /* Plain secondary index */
8288 1095 : indxinfo[j].indexconstraint = 0;
8289 : }
8290 : }
8291 : }
8292 :
8293 259 : PQclear(res);
8294 :
8295 259 : destroyPQExpBuffer(query);
8296 259 : destroyPQExpBuffer(tbloids);
8297 259 : }
8298 :
8299 : /*
8300 : * getExtendedStatistics
8301 : * get information about extended-statistics objects.
8302 : *
8303 : * Note: extended statistics data is not returned directly to the caller, but
8304 : * it does get entered into the DumpableObject tables.
8305 : */
8306 : void
8307 259 : getExtendedStatistics(Archive *fout)
8308 : {
8309 : PQExpBuffer query;
8310 : PGresult *res;
8311 : StatsExtInfo *statsextinfo;
8312 : int ntups;
8313 : int i_tableoid;
8314 : int i_oid;
8315 : int i_stxname;
8316 : int i_stxnamespace;
8317 : int i_stxowner;
8318 : int i_stxrelid;
8319 : int i_stattarget;
8320 : int i;
8321 :
8322 : /* Extended statistics were new in v10 */
8323 259 : if (fout->remoteVersion < 100000)
8324 0 : return;
8325 :
8326 259 : query = createPQExpBuffer();
8327 :
8328 259 : if (fout->remoteVersion < 130000)
8329 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8330 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8331 : "FROM pg_catalog.pg_statistic_ext");
8332 : else
8333 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8334 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8335 : "FROM pg_catalog.pg_statistic_ext");
8336 :
8337 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8338 :
8339 259 : ntups = PQntuples(res);
8340 :
8341 259 : i_tableoid = PQfnumber(res, "tableoid");
8342 259 : i_oid = PQfnumber(res, "oid");
8343 259 : i_stxname = PQfnumber(res, "stxname");
8344 259 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8345 259 : i_stxowner = PQfnumber(res, "stxowner");
8346 259 : i_stxrelid = PQfnumber(res, "stxrelid");
8347 259 : i_stattarget = PQfnumber(res, "stxstattarget");
8348 :
8349 259 : statsextinfo = pg_malloc_array(StatsExtInfo, ntups);
8350 :
8351 467 : for (i = 0; i < ntups; i++)
8352 : {
8353 208 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8354 208 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8355 208 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8356 208 : AssignDumpId(&statsextinfo[i].dobj);
8357 208 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8358 416 : statsextinfo[i].dobj.namespace =
8359 208 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8360 208 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8361 416 : statsextinfo[i].stattable =
8362 208 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8363 208 : if (PQgetisnull(res, i, i_stattarget))
8364 163 : statsextinfo[i].stattarget = -1;
8365 : else
8366 45 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8367 :
8368 : /* Decide whether we want to dump it */
8369 208 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8370 :
8371 208 : if (fout->dopt->dumpStatistics)
8372 152 : statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8373 : }
8374 :
8375 259 : PQclear(res);
8376 259 : destroyPQExpBuffer(query);
8377 : }
8378 :
8379 : /*
8380 : * getConstraints
8381 : *
8382 : * Get info about constraints on dumpable tables.
8383 : *
8384 : * Currently handles foreign keys only.
8385 : * Unique and primary key constraints are handled with indexes,
8386 : * while check constraints are processed in getTableAttrs().
8387 : */
8388 : void
8389 259 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8390 : {
8391 259 : PQExpBuffer query = createPQExpBuffer();
8392 259 : PQExpBuffer tbloids = createPQExpBuffer();
8393 : PGresult *res;
8394 : int ntups;
8395 : int curtblindx;
8396 259 : TableInfo *tbinfo = NULL;
8397 : ConstraintInfo *constrinfo;
8398 : int i_contableoid,
8399 : i_conoid,
8400 : i_conrelid,
8401 : i_conname,
8402 : i_confrelid,
8403 : i_conindid,
8404 : i_condef;
8405 :
8406 : /*
8407 : * We want to perform just one query against pg_constraint. However, we
8408 : * mustn't try to select every row of the catalog and then sort it out on
8409 : * the client side, because some of the server-side functions we need
8410 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8411 : * build an array of the OIDs of tables we care about (and now have lock
8412 : * on!), and use a WHERE clause to constrain which rows are selected.
8413 : */
8414 259 : appendPQExpBufferChar(tbloids, '{');
8415 70160 : for (int i = 0; i < numTables; i++)
8416 : {
8417 69901 : TableInfo *tinfo = &tblinfo[i];
8418 :
8419 69901 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8420 62320 : continue;
8421 :
8422 : /* OK, we need info for this table */
8423 7581 : if (tbloids->len > 1) /* do we have more than the '{'? */
8424 7403 : appendPQExpBufferChar(tbloids, ',');
8425 7581 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8426 : }
8427 259 : appendPQExpBufferChar(tbloids, '}');
8428 :
8429 259 : appendPQExpBufferStr(query,
8430 : "SELECT c.tableoid, c.oid, "
8431 : "conrelid, conname, confrelid, ");
8432 259 : if (fout->remoteVersion >= 110000)
8433 259 : appendPQExpBufferStr(query, "conindid, ");
8434 : else
8435 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8436 259 : appendPQExpBuffer(query,
8437 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8438 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8439 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8440 : "WHERE contype = 'f' ",
8441 : tbloids->data);
8442 259 : if (fout->remoteVersion >= 110000)
8443 259 : appendPQExpBufferStr(query,
8444 : "AND conparentid = 0 ");
8445 259 : appendPQExpBufferStr(query,
8446 : "ORDER BY conrelid, conname");
8447 :
8448 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8449 :
8450 259 : ntups = PQntuples(res);
8451 :
8452 259 : i_contableoid = PQfnumber(res, "tableoid");
8453 259 : i_conoid = PQfnumber(res, "oid");
8454 259 : i_conrelid = PQfnumber(res, "conrelid");
8455 259 : i_conname = PQfnumber(res, "conname");
8456 259 : i_confrelid = PQfnumber(res, "confrelid");
8457 259 : i_conindid = PQfnumber(res, "conindid");
8458 259 : i_condef = PQfnumber(res, "condef");
8459 :
8460 259 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8461 :
8462 259 : curtblindx = -1;
8463 490 : for (int j = 0; j < ntups; j++)
8464 : {
8465 231 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8466 : TableInfo *reftable;
8467 :
8468 : /*
8469 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8470 : * order.
8471 : */
8472 231 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8473 : {
8474 14602 : while (++curtblindx < numTables)
8475 : {
8476 14602 : tbinfo = &tblinfo[curtblindx];
8477 14602 : if (tbinfo->dobj.catId.oid == conrelid)
8478 191 : break;
8479 : }
8480 191 : if (curtblindx >= numTables)
8481 0 : pg_fatal("unrecognized table OID %u", conrelid);
8482 : }
8483 :
8484 231 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8485 231 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8486 231 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8487 231 : AssignDumpId(&constrinfo[j].dobj);
8488 231 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8489 231 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8490 231 : constrinfo[j].contable = tbinfo;
8491 231 : constrinfo[j].condomain = NULL;
8492 231 : constrinfo[j].contype = 'f';
8493 231 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8494 231 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8495 231 : constrinfo[j].conindex = 0;
8496 231 : constrinfo[j].condeferrable = false;
8497 231 : constrinfo[j].condeferred = false;
8498 231 : constrinfo[j].conislocal = true;
8499 231 : constrinfo[j].separate = true;
8500 :
8501 : /*
8502 : * Restoring an FK that points to a partitioned table requires that
8503 : * all partition indexes have been attached beforehand. Ensure that
8504 : * happens by making the constraint depend on each index partition
8505 : * attach object.
8506 : */
8507 231 : reftable = findTableByOid(constrinfo[j].confrelid);
8508 231 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8509 : {
8510 30 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8511 :
8512 30 : if (indexOid != InvalidOid)
8513 : {
8514 30 : for (int k = 0; k < reftable->numIndexes; k++)
8515 : {
8516 : IndxInfo *refidx;
8517 :
8518 : /* not our index? */
8519 30 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8520 0 : continue;
8521 :
8522 30 : refidx = &reftable->indexes[k];
8523 30 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8524 30 : break;
8525 : }
8526 : }
8527 : }
8528 : }
8529 :
8530 259 : PQclear(res);
8531 :
8532 259 : destroyPQExpBuffer(query);
8533 259 : destroyPQExpBuffer(tbloids);
8534 259 : }
8535 :
8536 : /*
8537 : * addConstrChildIdxDeps
8538 : *
8539 : * Recursive subroutine for getConstraints
8540 : *
8541 : * Given an object representing a foreign key constraint and an index on the
8542 : * partitioned table it references, mark the constraint object as dependent
8543 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8544 : * drilling down to their partitions if any. This ensures that the FK is not
8545 : * restored until the index is fully marked valid.
8546 : */
8547 : static void
8548 55 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8549 : {
8550 : SimplePtrListCell *cell;
8551 :
8552 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8553 :
8554 185 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8555 : {
8556 130 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8557 :
8558 130 : addObjectDependency(dobj, attach->dobj.dumpId);
8559 :
8560 130 : if (attach->partitionIdx->partattaches.head != NULL)
8561 25 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8562 : }
8563 55 : }
8564 :
8565 : /*
8566 : * getDomainConstraints
8567 : *
8568 : * Get info about constraints on a domain.
8569 : */
8570 : static void
8571 158 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8572 : {
8573 : ConstraintInfo *constrinfo;
8574 158 : PQExpBuffer query = createPQExpBuffer();
8575 : PGresult *res;
8576 : int i_tableoid,
8577 : i_oid,
8578 : i_conname,
8579 : i_consrc,
8580 : i_convalidated,
8581 : i_contype;
8582 : int ntups;
8583 :
8584 158 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8585 : {
8586 : /*
8587 : * Set up query for constraint-specific details. For servers 17 and
8588 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8589 : * just the latter.
8590 : */
8591 43 : appendPQExpBuffer(query,
8592 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8593 : "SELECT tableoid, oid, conname, "
8594 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8595 : "convalidated, contype "
8596 : "FROM pg_catalog.pg_constraint "
8597 : "WHERE contypid = $1 AND contype IN (%s) "
8598 : "ORDER BY conname",
8599 43 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8600 :
8601 43 : ExecuteSqlStatement(fout, query->data);
8602 :
8603 43 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8604 : }
8605 :
8606 158 : printfPQExpBuffer(query,
8607 : "EXECUTE getDomainConstraints('%u')",
8608 : tyinfo->dobj.catId.oid);
8609 :
8610 158 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8611 :
8612 158 : ntups = PQntuples(res);
8613 :
8614 158 : i_tableoid = PQfnumber(res, "tableoid");
8615 158 : i_oid = PQfnumber(res, "oid");
8616 158 : i_conname = PQfnumber(res, "conname");
8617 158 : i_consrc = PQfnumber(res, "consrc");
8618 158 : i_convalidated = PQfnumber(res, "convalidated");
8619 158 : i_contype = PQfnumber(res, "contype");
8620 :
8621 158 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8622 158 : tyinfo->domChecks = constrinfo;
8623 :
8624 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8625 324 : for (int i = 0, j = 0; i < ntups; i++)
8626 : {
8627 166 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8628 166 : char contype = (PQgetvalue(res, i, i_contype))[0];
8629 : ConstraintInfo *constraint;
8630 :
8631 166 : if (contype == CONSTRAINT_CHECK)
8632 : {
8633 113 : constraint = &constrinfo[j++];
8634 113 : tyinfo->nDomChecks++;
8635 : }
8636 : else
8637 : {
8638 : Assert(contype == CONSTRAINT_NOTNULL);
8639 : Assert(tyinfo->notnull == NULL);
8640 : /* use last item in array for the not-null constraint */
8641 53 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8642 53 : constraint = tyinfo->notnull;
8643 : }
8644 :
8645 166 : constraint->dobj.objType = DO_CONSTRAINT;
8646 166 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8647 166 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8648 166 : AssignDumpId(&(constraint->dobj));
8649 166 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8650 166 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8651 166 : constraint->contable = NULL;
8652 166 : constraint->condomain = tyinfo;
8653 166 : constraint->contype = contype;
8654 166 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8655 166 : constraint->confrelid = InvalidOid;
8656 166 : constraint->conindex = 0;
8657 166 : constraint->condeferrable = false;
8658 166 : constraint->condeferred = false;
8659 166 : constraint->conislocal = true;
8660 :
8661 166 : constraint->separate = !validated;
8662 :
8663 : /*
8664 : * Make the domain depend on the constraint, ensuring it won't be
8665 : * output till any constraint dependencies are OK. If the constraint
8666 : * has not been validated, it's going to be dumped after the domain
8667 : * anyway, so this doesn't matter.
8668 : */
8669 166 : if (validated)
8670 161 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8671 : }
8672 :
8673 158 : PQclear(res);
8674 :
8675 158 : destroyPQExpBuffer(query);
8676 158 : }
8677 :
8678 : /*
8679 : * getRules
8680 : * get basic information about every rule in the system
8681 : */
8682 : void
8683 259 : getRules(Archive *fout)
8684 : {
8685 : PGresult *res;
8686 : int ntups;
8687 : int i;
8688 259 : PQExpBuffer query = createPQExpBuffer();
8689 : RuleInfo *ruleinfo;
8690 : int i_tableoid;
8691 : int i_oid;
8692 : int i_rulename;
8693 : int i_ruletable;
8694 : int i_ev_type;
8695 : int i_is_instead;
8696 : int i_ev_enabled;
8697 :
8698 259 : appendPQExpBufferStr(query, "SELECT "
8699 : "tableoid, oid, rulename, "
8700 : "ev_class AS ruletable, ev_type, is_instead, "
8701 : "ev_enabled "
8702 : "FROM pg_rewrite "
8703 : "ORDER BY oid");
8704 :
8705 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8706 :
8707 259 : ntups = PQntuples(res);
8708 :
8709 259 : ruleinfo = pg_malloc_array(RuleInfo, ntups);
8710 :
8711 259 : i_tableoid = PQfnumber(res, "tableoid");
8712 259 : i_oid = PQfnumber(res, "oid");
8713 259 : i_rulename = PQfnumber(res, "rulename");
8714 259 : i_ruletable = PQfnumber(res, "ruletable");
8715 259 : i_ev_type = PQfnumber(res, "ev_type");
8716 259 : i_is_instead = PQfnumber(res, "is_instead");
8717 259 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8718 :
8719 43122 : for (i = 0; i < ntups; i++)
8720 : {
8721 : Oid ruletableoid;
8722 :
8723 42863 : ruleinfo[i].dobj.objType = DO_RULE;
8724 42863 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8725 42863 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8726 42863 : AssignDumpId(&ruleinfo[i].dobj);
8727 42863 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8728 42863 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8729 42863 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8730 42863 : if (ruleinfo[i].ruletable == NULL)
8731 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8732 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8733 42863 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8734 42863 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8735 42863 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8736 42863 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8737 42863 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8738 42863 : if (ruleinfo[i].ruletable)
8739 : {
8740 : /*
8741 : * If the table is a view or materialized view, force its ON
8742 : * SELECT rule to be sorted before the view itself --- this
8743 : * ensures that any dependencies for the rule affect the table's
8744 : * positioning. Other rules are forced to appear after their
8745 : * table.
8746 : */
8747 42863 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8748 699 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8749 42632 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8750 : {
8751 42072 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8752 42072 : ruleinfo[i].dobj.dumpId);
8753 : /* We'll merge the rule into CREATE VIEW, if possible */
8754 42072 : ruleinfo[i].separate = false;
8755 : }
8756 : else
8757 : {
8758 791 : addObjectDependency(&ruleinfo[i].dobj,
8759 791 : ruleinfo[i].ruletable->dobj.dumpId);
8760 791 : ruleinfo[i].separate = true;
8761 : }
8762 : }
8763 : else
8764 0 : ruleinfo[i].separate = true;
8765 : }
8766 :
8767 259 : PQclear(res);
8768 :
8769 259 : destroyPQExpBuffer(query);
8770 259 : }
8771 :
8772 : /*
8773 : * getTriggers
8774 : * get information about every trigger on a dumpable table
8775 : *
8776 : * Note: trigger data is not returned directly to the caller, but it
8777 : * does get entered into the DumpableObject tables.
8778 : */
8779 : void
8780 259 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8781 : {
8782 259 : PQExpBuffer query = createPQExpBuffer();
8783 259 : PQExpBuffer tbloids = createPQExpBuffer();
8784 : PGresult *res;
8785 : int ntups;
8786 : int curtblindx;
8787 : TriggerInfo *tginfo;
8788 : int i_tableoid,
8789 : i_oid,
8790 : i_tgrelid,
8791 : i_tgname,
8792 : i_tgenabled,
8793 : i_tgispartition,
8794 : i_tgdef;
8795 :
8796 : /*
8797 : * We want to perform just one query against pg_trigger. However, we
8798 : * mustn't try to select every row of the catalog and then sort it out on
8799 : * the client side, because some of the server-side functions we need
8800 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8801 : * build an array of the OIDs of tables we care about (and now have lock
8802 : * on!), and use a WHERE clause to constrain which rows are selected.
8803 : */
8804 259 : appendPQExpBufferChar(tbloids, '{');
8805 70160 : for (int i = 0; i < numTables; i++)
8806 : {
8807 69901 : TableInfo *tbinfo = &tblinfo[i];
8808 :
8809 69901 : if (!tbinfo->hastriggers ||
8810 1233 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8811 68964 : continue;
8812 :
8813 : /* OK, we need info for this table */
8814 937 : if (tbloids->len > 1) /* do we have more than the '{'? */
8815 886 : appendPQExpBufferChar(tbloids, ',');
8816 937 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8817 : }
8818 259 : appendPQExpBufferChar(tbloids, '}');
8819 :
8820 259 : if (fout->remoteVersion >= 150000)
8821 : {
8822 : /*
8823 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8824 : * result in non-forward-compatible dumps of WHEN clauses due to
8825 : * under-parenthesization.
8826 : *
8827 : * NB: We need to see partition triggers in case the tgenabled flag
8828 : * has been changed from the parent.
8829 : */
8830 259 : appendPQExpBuffer(query,
8831 : "SELECT t.tgrelid, t.tgname, "
8832 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8833 : "t.tgenabled, t.tableoid, t.oid, "
8834 : "t.tgparentid <> 0 AS tgispartition\n"
8835 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8836 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8837 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8838 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8839 : "OR t.tgenabled != u.tgenabled) "
8840 : "ORDER BY t.tgrelid, t.tgname",
8841 : tbloids->data);
8842 : }
8843 0 : else if (fout->remoteVersion >= 130000)
8844 : {
8845 : /*
8846 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8847 : * result in non-forward-compatible dumps of WHEN clauses due to
8848 : * under-parenthesization.
8849 : *
8850 : * NB: We need to see tgisinternal triggers in partitions, in case the
8851 : * tgenabled flag has been changed from the parent.
8852 : */
8853 0 : appendPQExpBuffer(query,
8854 : "SELECT t.tgrelid, t.tgname, "
8855 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8856 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8857 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8858 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8859 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8860 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8861 : "ORDER BY t.tgrelid, t.tgname",
8862 : tbloids->data);
8863 : }
8864 0 : else if (fout->remoteVersion >= 110000)
8865 : {
8866 : /*
8867 : * NB: We need to see tgisinternal triggers in partitions, in case the
8868 : * tgenabled flag has been changed from the parent. No tgparentid in
8869 : * version 11-12, so we have to match them via pg_depend.
8870 : *
8871 : * See above about pretty=true in pg_get_triggerdef.
8872 : */
8873 0 : appendPQExpBuffer(query,
8874 : "SELECT t.tgrelid, t.tgname, "
8875 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8876 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8877 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8878 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8879 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8880 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8881 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8882 : " d.objid = t.oid "
8883 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8884 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8885 : "ORDER BY t.tgrelid, t.tgname",
8886 : tbloids->data);
8887 : }
8888 : else
8889 : {
8890 : /* See above about pretty=true in pg_get_triggerdef */
8891 0 : appendPQExpBuffer(query,
8892 : "SELECT t.tgrelid, t.tgname, "
8893 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8894 : "t.tgenabled, false as tgispartition, "
8895 : "t.tableoid, t.oid "
8896 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8897 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8898 : "WHERE NOT tgisinternal "
8899 : "ORDER BY t.tgrelid, t.tgname",
8900 : tbloids->data);
8901 : }
8902 :
8903 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8904 :
8905 259 : ntups = PQntuples(res);
8906 :
8907 259 : i_tableoid = PQfnumber(res, "tableoid");
8908 259 : i_oid = PQfnumber(res, "oid");
8909 259 : i_tgrelid = PQfnumber(res, "tgrelid");
8910 259 : i_tgname = PQfnumber(res, "tgname");
8911 259 : i_tgenabled = PQfnumber(res, "tgenabled");
8912 259 : i_tgispartition = PQfnumber(res, "tgispartition");
8913 259 : i_tgdef = PQfnumber(res, "tgdef");
8914 :
8915 259 : tginfo = pg_malloc_array(TriggerInfo, ntups);
8916 :
8917 : /*
8918 : * Outer loop iterates once per table, not once per row. Incrementing of
8919 : * j is handled by the inner loop.
8920 : */
8921 259 : curtblindx = -1;
8922 565 : for (int j = 0; j < ntups;)
8923 : {
8924 306 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8925 306 : TableInfo *tbinfo = NULL;
8926 : int numtrigs;
8927 :
8928 : /* Count rows for this table */
8929 523 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8930 472 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8931 255 : break;
8932 :
8933 : /*
8934 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8935 : * order.
8936 : */
8937 17245 : while (++curtblindx < numTables)
8938 : {
8939 17245 : tbinfo = &tblinfo[curtblindx];
8940 17245 : if (tbinfo->dobj.catId.oid == tgrelid)
8941 306 : break;
8942 : }
8943 306 : if (curtblindx >= numTables)
8944 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8945 :
8946 : /* Save data for this table */
8947 306 : tbinfo->triggers = tginfo + j;
8948 306 : tbinfo->numTriggers = numtrigs;
8949 :
8950 829 : for (int c = 0; c < numtrigs; c++, j++)
8951 : {
8952 523 : tginfo[j].dobj.objType = DO_TRIGGER;
8953 523 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8954 523 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8955 523 : AssignDumpId(&tginfo[j].dobj);
8956 523 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8957 523 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8958 523 : tginfo[j].tgtable = tbinfo;
8959 523 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8960 523 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8961 523 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8962 : }
8963 : }
8964 :
8965 259 : PQclear(res);
8966 :
8967 259 : destroyPQExpBuffer(query);
8968 259 : destroyPQExpBuffer(tbloids);
8969 259 : }
8970 :
8971 : /*
8972 : * getEventTriggers
8973 : * get information about event triggers
8974 : */
8975 : void
8976 259 : getEventTriggers(Archive *fout)
8977 : {
8978 : int i;
8979 : PQExpBuffer query;
8980 : PGresult *res;
8981 : EventTriggerInfo *evtinfo;
8982 : int i_tableoid,
8983 : i_oid,
8984 : i_evtname,
8985 : i_evtevent,
8986 : i_evtowner,
8987 : i_evttags,
8988 : i_evtfname,
8989 : i_evtenabled;
8990 : int ntups;
8991 :
8992 : /* Before 9.3, there are no event triggers */
8993 259 : if (fout->remoteVersion < 90300)
8994 0 : return;
8995 :
8996 259 : query = createPQExpBuffer();
8997 :
8998 259 : appendPQExpBufferStr(query,
8999 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
9000 : "evtevent, evtowner, "
9001 : "array_to_string(array("
9002 : "select quote_literal(x) "
9003 : " from unnest(evttags) as t(x)), ', ') as evttags, "
9004 : "e.evtfoid::regproc as evtfname "
9005 : "FROM pg_event_trigger e "
9006 : "ORDER BY e.oid");
9007 :
9008 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9009 :
9010 259 : ntups = PQntuples(res);
9011 :
9012 259 : evtinfo = pg_malloc_array(EventTriggerInfo, ntups);
9013 :
9014 259 : i_tableoid = PQfnumber(res, "tableoid");
9015 259 : i_oid = PQfnumber(res, "oid");
9016 259 : i_evtname = PQfnumber(res, "evtname");
9017 259 : i_evtevent = PQfnumber(res, "evtevent");
9018 259 : i_evtowner = PQfnumber(res, "evtowner");
9019 259 : i_evttags = PQfnumber(res, "evttags");
9020 259 : i_evtfname = PQfnumber(res, "evtfname");
9021 259 : i_evtenabled = PQfnumber(res, "evtenabled");
9022 :
9023 311 : for (i = 0; i < ntups; i++)
9024 : {
9025 52 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
9026 52 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9027 52 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9028 52 : AssignDumpId(&evtinfo[i].dobj);
9029 52 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
9030 52 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
9031 52 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
9032 52 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
9033 52 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
9034 52 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
9035 52 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
9036 :
9037 : /* Decide whether we want to dump it */
9038 52 : selectDumpableObject(&(evtinfo[i].dobj), fout);
9039 : }
9040 :
9041 259 : PQclear(res);
9042 :
9043 259 : destroyPQExpBuffer(query);
9044 : }
9045 :
9046 : /*
9047 : * getProcLangs
9048 : * get basic information about every procedural language in the system
9049 : *
9050 : * NB: this must run after getFuncs() because we assume we can do
9051 : * findFuncByOid().
9052 : */
9053 : void
9054 259 : getProcLangs(Archive *fout)
9055 : {
9056 : PGresult *res;
9057 : int ntups;
9058 : int i;
9059 259 : PQExpBuffer query = createPQExpBuffer();
9060 : ProcLangInfo *planginfo;
9061 : int i_tableoid;
9062 : int i_oid;
9063 : int i_lanname;
9064 : int i_lanpltrusted;
9065 : int i_lanplcallfoid;
9066 : int i_laninline;
9067 : int i_lanvalidator;
9068 : int i_lanacl;
9069 : int i_acldefault;
9070 : int i_lanowner;
9071 :
9072 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9073 : "lanname, lanpltrusted, lanplcallfoid, "
9074 : "laninline, lanvalidator, "
9075 : "lanacl, "
9076 : "acldefault('l', lanowner) AS acldefault, "
9077 : "lanowner "
9078 : "FROM pg_language "
9079 : "WHERE lanispl "
9080 : "ORDER BY oid");
9081 :
9082 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9083 :
9084 259 : ntups = PQntuples(res);
9085 :
9086 259 : planginfo = pg_malloc_array(ProcLangInfo, ntups);
9087 :
9088 259 : i_tableoid = PQfnumber(res, "tableoid");
9089 259 : i_oid = PQfnumber(res, "oid");
9090 259 : i_lanname = PQfnumber(res, "lanname");
9091 259 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
9092 259 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
9093 259 : i_laninline = PQfnumber(res, "laninline");
9094 259 : i_lanvalidator = PQfnumber(res, "lanvalidator");
9095 259 : i_lanacl = PQfnumber(res, "lanacl");
9096 259 : i_acldefault = PQfnumber(res, "acldefault");
9097 259 : i_lanowner = PQfnumber(res, "lanowner");
9098 :
9099 563 : for (i = 0; i < ntups; i++)
9100 : {
9101 304 : planginfo[i].dobj.objType = DO_PROCLANG;
9102 304 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9103 304 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9104 304 : AssignDumpId(&planginfo[i].dobj);
9105 :
9106 304 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9107 304 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9108 304 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9109 304 : planginfo[i].dacl.privtype = 0;
9110 304 : planginfo[i].dacl.initprivs = NULL;
9111 304 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9112 304 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9113 304 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9114 304 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9115 304 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9116 :
9117 : /* Decide whether we want to dump it */
9118 304 : selectDumpableProcLang(&(planginfo[i]), fout);
9119 :
9120 : /* Mark whether language has an ACL */
9121 304 : if (!PQgetisnull(res, i, i_lanacl))
9122 45 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9123 : }
9124 :
9125 259 : PQclear(res);
9126 :
9127 259 : destroyPQExpBuffer(query);
9128 259 : }
9129 :
9130 : /*
9131 : * getCasts
9132 : * get basic information about most casts in the system
9133 : *
9134 : * Skip casts from a range to its multirange, since we'll create those
9135 : * automatically.
9136 : */
9137 : void
9138 259 : getCasts(Archive *fout)
9139 : {
9140 : PGresult *res;
9141 : int ntups;
9142 : int i;
9143 259 : PQExpBuffer query = createPQExpBuffer();
9144 : CastInfo *castinfo;
9145 : int i_tableoid;
9146 : int i_oid;
9147 : int i_castsource;
9148 : int i_casttarget;
9149 : int i_castfunc;
9150 : int i_castcontext;
9151 : int i_castmethod;
9152 :
9153 259 : if (fout->remoteVersion >= 140000)
9154 : {
9155 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9156 : "castsource, casttarget, castfunc, castcontext, "
9157 : "castmethod "
9158 : "FROM pg_cast c "
9159 : "WHERE NOT EXISTS ( "
9160 : "SELECT 1 FROM pg_range r "
9161 : "WHERE c.castsource = r.rngtypid "
9162 : "AND c.casttarget = r.rngmultitypid "
9163 : ") "
9164 : "ORDER BY 3,4");
9165 : }
9166 : else
9167 : {
9168 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9169 : "castsource, casttarget, castfunc, castcontext, "
9170 : "castmethod "
9171 : "FROM pg_cast ORDER BY 3,4");
9172 : }
9173 :
9174 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9175 :
9176 259 : ntups = PQntuples(res);
9177 :
9178 259 : castinfo = pg_malloc_array(CastInfo, ntups);
9179 :
9180 259 : i_tableoid = PQfnumber(res, "tableoid");
9181 259 : i_oid = PQfnumber(res, "oid");
9182 259 : i_castsource = PQfnumber(res, "castsource");
9183 259 : i_casttarget = PQfnumber(res, "casttarget");
9184 259 : i_castfunc = PQfnumber(res, "castfunc");
9185 259 : i_castcontext = PQfnumber(res, "castcontext");
9186 259 : i_castmethod = PQfnumber(res, "castmethod");
9187 :
9188 63283 : for (i = 0; i < ntups; i++)
9189 : {
9190 : PQExpBufferData namebuf;
9191 : TypeInfo *sTypeInfo;
9192 : TypeInfo *tTypeInfo;
9193 :
9194 63024 : castinfo[i].dobj.objType = DO_CAST;
9195 63024 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9196 63024 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9197 63024 : AssignDumpId(&castinfo[i].dobj);
9198 63024 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9199 63024 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9200 63024 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9201 63024 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9202 63024 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9203 :
9204 : /*
9205 : * Try to name cast as concatenation of typnames. This is only used
9206 : * for purposes of sorting. If we fail to find either type, the name
9207 : * will be an empty string.
9208 : */
9209 63024 : initPQExpBuffer(&namebuf);
9210 63024 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9211 63024 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9212 63024 : if (sTypeInfo && tTypeInfo)
9213 63024 : appendPQExpBuffer(&namebuf, "%s %s",
9214 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9215 63024 : castinfo[i].dobj.name = namebuf.data;
9216 :
9217 : /* Decide whether we want to dump it */
9218 63024 : selectDumpableCast(&(castinfo[i]), fout);
9219 : }
9220 :
9221 259 : PQclear(res);
9222 :
9223 259 : destroyPQExpBuffer(query);
9224 259 : }
9225 :
9226 : static char *
9227 88 : get_language_name(Archive *fout, Oid langid)
9228 : {
9229 : PQExpBuffer query;
9230 : PGresult *res;
9231 : char *lanname;
9232 :
9233 88 : query = createPQExpBuffer();
9234 88 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9235 88 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9236 88 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9237 88 : destroyPQExpBuffer(query);
9238 88 : PQclear(res);
9239 :
9240 88 : return lanname;
9241 : }
9242 :
9243 : /*
9244 : * getTransforms
9245 : * get basic information about every transform in the system
9246 : */
9247 : void
9248 259 : getTransforms(Archive *fout)
9249 : {
9250 : PGresult *res;
9251 : int ntups;
9252 : int i;
9253 : PQExpBuffer query;
9254 : TransformInfo *transforminfo;
9255 : int i_tableoid;
9256 : int i_oid;
9257 : int i_trftype;
9258 : int i_trflang;
9259 : int i_trffromsql;
9260 : int i_trftosql;
9261 :
9262 : /* Transforms didn't exist pre-9.5 */
9263 259 : if (fout->remoteVersion < 90500)
9264 0 : return;
9265 :
9266 259 : query = createPQExpBuffer();
9267 :
9268 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9269 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9270 : "FROM pg_transform "
9271 : "ORDER BY 3,4");
9272 :
9273 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9274 :
9275 259 : ntups = PQntuples(res);
9276 :
9277 259 : transforminfo = pg_malloc_array(TransformInfo, ntups);
9278 :
9279 259 : i_tableoid = PQfnumber(res, "tableoid");
9280 259 : i_oid = PQfnumber(res, "oid");
9281 259 : i_trftype = PQfnumber(res, "trftype");
9282 259 : i_trflang = PQfnumber(res, "trflang");
9283 259 : i_trffromsql = PQfnumber(res, "trffromsql");
9284 259 : i_trftosql = PQfnumber(res, "trftosql");
9285 :
9286 311 : for (i = 0; i < ntups; i++)
9287 : {
9288 : PQExpBufferData namebuf;
9289 : TypeInfo *typeInfo;
9290 : char *lanname;
9291 :
9292 52 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9293 52 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9294 52 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9295 52 : AssignDumpId(&transforminfo[i].dobj);
9296 52 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9297 52 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9298 52 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9299 52 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9300 :
9301 : /*
9302 : * Try to name transform as concatenation of type and language name.
9303 : * This is only used for purposes of sorting. If we fail to find
9304 : * either, the name will be an empty string.
9305 : */
9306 52 : initPQExpBuffer(&namebuf);
9307 52 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9308 52 : lanname = get_language_name(fout, transforminfo[i].trflang);
9309 52 : if (typeInfo && lanname)
9310 52 : appendPQExpBuffer(&namebuf, "%s %s",
9311 : typeInfo->dobj.name, lanname);
9312 52 : transforminfo[i].dobj.name = namebuf.data;
9313 52 : free(lanname);
9314 :
9315 : /* Decide whether we want to dump it */
9316 52 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9317 : }
9318 :
9319 259 : PQclear(res);
9320 :
9321 259 : destroyPQExpBuffer(query);
9322 : }
9323 :
9324 : /*
9325 : * getTableAttrs -
9326 : * for each interesting table, read info about its attributes
9327 : * (names, types, default values, CHECK constraints, etc)
9328 : *
9329 : * modifies tblinfo
9330 : */
9331 : void
9332 259 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9333 : {
9334 259 : DumpOptions *dopt = fout->dopt;
9335 259 : PQExpBuffer q = createPQExpBuffer();
9336 259 : PQExpBuffer tbloids = createPQExpBuffer();
9337 259 : PQExpBuffer checkoids = createPQExpBuffer();
9338 259 : PQExpBuffer invalidnotnulloids = NULL;
9339 : PGresult *res;
9340 : int ntups;
9341 : int curtblindx;
9342 : int i_attrelid;
9343 : int i_attnum;
9344 : int i_attname;
9345 : int i_atttypname;
9346 : int i_attstattarget;
9347 : int i_attstorage;
9348 : int i_typstorage;
9349 : int i_attidentity;
9350 : int i_attgenerated;
9351 : int i_attisdropped;
9352 : int i_attlen;
9353 : int i_attalign;
9354 : int i_attislocal;
9355 : int i_notnull_name;
9356 : int i_notnull_comment;
9357 : int i_notnull_noinherit;
9358 : int i_notnull_islocal;
9359 : int i_notnull_invalidoid;
9360 : int i_attoptions;
9361 : int i_attcollation;
9362 : int i_attcompression;
9363 : int i_attfdwoptions;
9364 : int i_attmissingval;
9365 : int i_atthasdef;
9366 :
9367 : /*
9368 : * We want to perform just one query against pg_attribute, and then just
9369 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9370 : * (for CHECK constraints and for NOT NULL constraints). However, we
9371 : * mustn't try to select every row of those catalogs and then sort it out
9372 : * on the client side, because some of the server-side functions we need
9373 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9374 : * build an array of the OIDs of tables we care about (and now have lock
9375 : * on!), and use a WHERE clause to constrain which rows are selected.
9376 : */
9377 259 : appendPQExpBufferChar(tbloids, '{');
9378 259 : appendPQExpBufferChar(checkoids, '{');
9379 70160 : for (int i = 0; i < numTables; i++)
9380 : {
9381 69901 : TableInfo *tbinfo = &tblinfo[i];
9382 :
9383 : /* Don't bother to collect info for sequences */
9384 69901 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9385 647 : continue;
9386 :
9387 : /*
9388 : * Don't bother with uninteresting tables, either. For binary
9389 : * upgrades, this is bypassed for pg_largeobject_metadata and
9390 : * pg_shdepend so that the columns names are collected for the
9391 : * corresponding COPY commands. Restoring the data for those catalogs
9392 : * is faster than restoring the equivalent set of large object
9393 : * commands.
9394 : */
9395 69254 : if (!tbinfo->interesting &&
9396 62035 : !(fout->dopt->binary_upgrade &&
9397 9298 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9398 9258 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9399 61955 : continue;
9400 :
9401 : /* OK, we need info for this table */
9402 7299 : if (tbloids->len > 1) /* do we have more than the '{'? */
9403 7100 : appendPQExpBufferChar(tbloids, ',');
9404 7299 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9405 :
9406 7299 : if (tbinfo->ncheck > 0)
9407 : {
9408 : /* Also make a list of the ones with check constraints */
9409 528 : if (checkoids->len > 1) /* do we have more than the '{'? */
9410 459 : appendPQExpBufferChar(checkoids, ',');
9411 528 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9412 : }
9413 : }
9414 259 : appendPQExpBufferChar(tbloids, '}');
9415 259 : appendPQExpBufferChar(checkoids, '}');
9416 :
9417 : /*
9418 : * Find all the user attributes and their types.
9419 : *
9420 : * Since we only want to dump COLLATE clauses for attributes whose
9421 : * collation is different from their type's default, we use a CASE here to
9422 : * suppress uninteresting attcollations cheaply.
9423 : */
9424 259 : appendPQExpBufferStr(q,
9425 : "SELECT\n"
9426 : "a.attrelid,\n"
9427 : "a.attnum,\n"
9428 : "a.attname,\n"
9429 : "a.attstattarget,\n"
9430 : "a.attstorage,\n"
9431 : "t.typstorage,\n"
9432 : "a.atthasdef,\n"
9433 : "a.attisdropped,\n"
9434 : "a.attlen,\n"
9435 : "a.attalign,\n"
9436 : "a.attislocal,\n"
9437 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9438 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9439 : "CASE WHEN a.attcollation <> t.typcollation "
9440 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9441 : "pg_catalog.array_to_string(ARRAY("
9442 : "SELECT pg_catalog.quote_ident(option_name) || "
9443 : "' ' || pg_catalog.quote_literal(option_value) "
9444 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9445 : "ORDER BY option_name"
9446 : "), E',\n ') AS attfdwoptions,\n");
9447 :
9448 : /*
9449 : * Find out any NOT NULL markings for each column. In 18 and up we read
9450 : * pg_constraint to obtain the constraint name, and for valid constraints
9451 : * also pg_description to obtain its comment. notnull_noinherit is set
9452 : * according to the NO INHERIT property. For versions prior to 18, we
9453 : * store an empty string as the name when a constraint is marked as
9454 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9455 : * without a name); also, such cases are never NO INHERIT.
9456 : *
9457 : * For invalid constraints, we need to store their OIDs for processing
9458 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9459 : * is invalid, and NULL otherwise. Their comments are handled not here
9460 : * but by collectComments, because they're their own dumpable object.
9461 : *
9462 : * We track in notnull_islocal whether the constraint was defined directly
9463 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9464 : * might modify this later.
9465 : */
9466 259 : if (fout->remoteVersion >= 180000)
9467 259 : appendPQExpBufferStr(q,
9468 : "co.conname AS notnull_name,\n"
9469 : "CASE WHEN co.convalidated THEN pt.description"
9470 : " ELSE NULL END AS notnull_comment,\n"
9471 : "CASE WHEN NOT co.convalidated THEN co.oid "
9472 : "ELSE NULL END AS notnull_invalidoid,\n"
9473 : "co.connoinherit AS notnull_noinherit,\n"
9474 : "co.conislocal AS notnull_islocal,\n");
9475 : else
9476 0 : appendPQExpBufferStr(q,
9477 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9478 : "NULL AS notnull_comment,\n"
9479 : "NULL AS notnull_invalidoid,\n"
9480 : "false AS notnull_noinherit,\n"
9481 : "CASE WHEN a.attislocal THEN true\n"
9482 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9483 : " ELSE false\n"
9484 : "END AS notnull_islocal,\n");
9485 :
9486 259 : if (fout->remoteVersion >= 140000)
9487 259 : appendPQExpBufferStr(q,
9488 : "a.attcompression AS attcompression,\n");
9489 : else
9490 0 : appendPQExpBufferStr(q,
9491 : "'' AS attcompression,\n");
9492 :
9493 259 : if (fout->remoteVersion >= 100000)
9494 259 : appendPQExpBufferStr(q,
9495 : "a.attidentity,\n");
9496 : else
9497 0 : appendPQExpBufferStr(q,
9498 : "'' AS attidentity,\n");
9499 :
9500 259 : if (fout->remoteVersion >= 110000)
9501 259 : appendPQExpBufferStr(q,
9502 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9503 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9504 : else
9505 0 : appendPQExpBufferStr(q,
9506 : "NULL AS attmissingval,\n");
9507 :
9508 259 : if (fout->remoteVersion >= 120000)
9509 259 : appendPQExpBufferStr(q,
9510 : "a.attgenerated\n");
9511 : else
9512 0 : appendPQExpBufferStr(q,
9513 : "'' AS attgenerated\n");
9514 :
9515 : /* need left join to pg_type to not fail on dropped columns ... */
9516 259 : appendPQExpBuffer(q,
9517 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9518 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9519 : "LEFT JOIN pg_catalog.pg_type t "
9520 : "ON (a.atttypid = t.oid)\n",
9521 : tbloids->data);
9522 :
9523 : /*
9524 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9525 : * entries and pg_description to get their comments.
9526 : */
9527 259 : if (fout->remoteVersion >= 180000)
9528 259 : appendPQExpBufferStr(q,
9529 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9530 : "(a.attrelid = co.conrelid\n"
9531 : " AND co.contype = 'n' AND "
9532 : "co.conkey = array[a.attnum])\n"
9533 : " LEFT JOIN pg_catalog.pg_description pt ON "
9534 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9535 :
9536 259 : appendPQExpBufferStr(q,
9537 : "WHERE a.attnum > 0::pg_catalog.int2\n");
9538 :
9539 : /*
9540 : * For binary upgrades from <v12, be sure to pick up
9541 : * pg_largeobject_metadata's oid column.
9542 : */
9543 259 : if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9544 0 : appendPQExpBufferStr(q,
9545 : "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9546 : CppAsString2(LargeObjectMetadataRelationId) ")\n");
9547 :
9548 259 : appendPQExpBufferStr(q,
9549 : "ORDER BY a.attrelid, a.attnum");
9550 :
9551 259 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9552 :
9553 259 : ntups = PQntuples(res);
9554 :
9555 259 : i_attrelid = PQfnumber(res, "attrelid");
9556 259 : i_attnum = PQfnumber(res, "attnum");
9557 259 : i_attname = PQfnumber(res, "attname");
9558 259 : i_atttypname = PQfnumber(res, "atttypname");
9559 259 : i_attstattarget = PQfnumber(res, "attstattarget");
9560 259 : i_attstorage = PQfnumber(res, "attstorage");
9561 259 : i_typstorage = PQfnumber(res, "typstorage");
9562 259 : i_attidentity = PQfnumber(res, "attidentity");
9563 259 : i_attgenerated = PQfnumber(res, "attgenerated");
9564 259 : i_attisdropped = PQfnumber(res, "attisdropped");
9565 259 : i_attlen = PQfnumber(res, "attlen");
9566 259 : i_attalign = PQfnumber(res, "attalign");
9567 259 : i_attislocal = PQfnumber(res, "attislocal");
9568 259 : i_notnull_name = PQfnumber(res, "notnull_name");
9569 259 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9570 259 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9571 259 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9572 259 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9573 259 : i_attoptions = PQfnumber(res, "attoptions");
9574 259 : i_attcollation = PQfnumber(res, "attcollation");
9575 259 : i_attcompression = PQfnumber(res, "attcompression");
9576 259 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9577 259 : i_attmissingval = PQfnumber(res, "attmissingval");
9578 259 : i_atthasdef = PQfnumber(res, "atthasdef");
9579 :
9580 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9581 259 : resetPQExpBuffer(tbloids);
9582 259 : appendPQExpBufferChar(tbloids, '{');
9583 :
9584 : /*
9585 : * Outer loop iterates once per table, not once per row. Incrementing of
9586 : * r is handled by the inner loop.
9587 : */
9588 259 : curtblindx = -1;
9589 7317 : for (int r = 0; r < ntups;)
9590 : {
9591 7058 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9592 7058 : TableInfo *tbinfo = NULL;
9593 : int numatts;
9594 : bool hasdefaults;
9595 :
9596 : /* Count rows for this table */
9597 26200 : for (numatts = 1; numatts < ntups - r; numatts++)
9598 26004 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9599 6862 : break;
9600 :
9601 : /*
9602 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9603 : * order.
9604 : */
9605 49214 : while (++curtblindx < numTables)
9606 : {
9607 49214 : tbinfo = &tblinfo[curtblindx];
9608 49214 : if (tbinfo->dobj.catId.oid == attrelid)
9609 7058 : break;
9610 : }
9611 7058 : if (curtblindx >= numTables)
9612 0 : pg_fatal("unrecognized table OID %u", attrelid);
9613 : /* cross-check that we only got requested tables */
9614 7058 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9615 7058 : (!tbinfo->interesting &&
9616 80 : !(fout->dopt->binary_upgrade &&
9617 80 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9618 40 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9619 0 : pg_fatal("unexpected column data for table \"%s\"",
9620 : tbinfo->dobj.name);
9621 :
9622 : /* Save data for this table */
9623 7058 : tbinfo->numatts = numatts;
9624 7058 : tbinfo->attnames = pg_malloc_array(char *, numatts);
9625 7058 : tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9626 7058 : tbinfo->attstattarget = pg_malloc_array(int, numatts);
9627 7058 : tbinfo->attstorage = pg_malloc_array(char, numatts);
9628 7058 : tbinfo->typstorage = pg_malloc_array(char, numatts);
9629 7058 : tbinfo->attidentity = pg_malloc_array(char, numatts);
9630 7058 : tbinfo->attgenerated = pg_malloc_array(char, numatts);
9631 7058 : tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9632 7058 : tbinfo->attlen = pg_malloc_array(int, numatts);
9633 7058 : tbinfo->attalign = pg_malloc_array(char, numatts);
9634 7058 : tbinfo->attislocal = pg_malloc_array(bool, numatts);
9635 7058 : tbinfo->attoptions = pg_malloc_array(char *, numatts);
9636 7058 : tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9637 7058 : tbinfo->attcompression = pg_malloc_array(char, numatts);
9638 7058 : tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9639 7058 : tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9640 7058 : tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9641 7058 : tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9642 7058 : tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9643 7058 : tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9644 7058 : tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9645 7058 : tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9646 7058 : hasdefaults = false;
9647 :
9648 33258 : for (int j = 0; j < numatts; j++, r++)
9649 : {
9650 26200 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9651 0 : !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9652 0 : tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9653 0 : pg_fatal("invalid column numbering in table \"%s\"",
9654 : tbinfo->dobj.name);
9655 26200 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9656 26200 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9657 26200 : if (PQgetisnull(res, r, i_attstattarget))
9658 26160 : tbinfo->attstattarget[j] = -1;
9659 : else
9660 40 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9661 26200 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9662 26200 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9663 26200 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9664 26200 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9665 26200 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9666 26200 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9667 26200 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9668 26200 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9669 26200 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9670 :
9671 : /* Handle not-null constraint name and flags */
9672 26200 : determineNotNullFlags(fout, res, r,
9673 : tbinfo, j,
9674 : i_notnull_name,
9675 : i_notnull_comment,
9676 : i_notnull_invalidoid,
9677 : i_notnull_noinherit,
9678 : i_notnull_islocal,
9679 : &invalidnotnulloids);
9680 :
9681 26200 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9682 26200 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9683 26200 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9684 26200 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9685 26200 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9686 26200 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9687 26200 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9688 26200 : tbinfo->attrdefs[j] = NULL; /* fix below */
9689 26200 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9690 1283 : hasdefaults = true;
9691 : }
9692 :
9693 7058 : if (hasdefaults)
9694 : {
9695 : /* Collect OIDs of interesting tables that have defaults */
9696 962 : if (tbloids->len > 1) /* do we have more than the '{'? */
9697 894 : appendPQExpBufferChar(tbloids, ',');
9698 962 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9699 : }
9700 : }
9701 :
9702 : /* If invalidnotnulloids has any data, finalize it */
9703 259 : if (invalidnotnulloids != NULL)
9704 43 : appendPQExpBufferChar(invalidnotnulloids, '}');
9705 :
9706 259 : PQclear(res);
9707 :
9708 : /*
9709 : * Now get info about column defaults. This is skipped for a data-only
9710 : * dump, as it is only needed for table schemas.
9711 : */
9712 259 : if (dopt->dumpSchema && tbloids->len > 1)
9713 : {
9714 : AttrDefInfo *attrdefs;
9715 : int numDefaults;
9716 60 : TableInfo *tbinfo = NULL;
9717 :
9718 60 : pg_log_info("finding table default expressions");
9719 :
9720 60 : appendPQExpBufferChar(tbloids, '}');
9721 :
9722 60 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9723 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9724 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9725 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9726 : "ORDER BY a.adrelid, a.adnum",
9727 : tbloids->data);
9728 :
9729 60 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9730 :
9731 60 : numDefaults = PQntuples(res);
9732 60 : attrdefs = pg_malloc_array(AttrDefInfo, numDefaults);
9733 :
9734 60 : curtblindx = -1;
9735 1245 : for (int j = 0; j < numDefaults; j++)
9736 : {
9737 1185 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9738 1185 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9739 1185 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9740 1185 : int adnum = atoi(PQgetvalue(res, j, 3));
9741 1185 : char *adsrc = PQgetvalue(res, j, 4);
9742 :
9743 : /*
9744 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9745 : * OID order.
9746 : */
9747 1185 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9748 : {
9749 20109 : while (++curtblindx < numTables)
9750 : {
9751 20109 : tbinfo = &tblinfo[curtblindx];
9752 20109 : if (tbinfo->dobj.catId.oid == adrelid)
9753 894 : break;
9754 : }
9755 894 : if (curtblindx >= numTables)
9756 0 : pg_fatal("unrecognized table OID %u", adrelid);
9757 : }
9758 :
9759 1185 : if (adnum <= 0 || adnum > tbinfo->numatts)
9760 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9761 : adnum, tbinfo->dobj.name);
9762 :
9763 : /*
9764 : * dropped columns shouldn't have defaults, but just in case,
9765 : * ignore 'em
9766 : */
9767 1185 : if (tbinfo->attisdropped[adnum - 1])
9768 0 : continue;
9769 :
9770 1185 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9771 1185 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9772 1185 : attrdefs[j].dobj.catId.oid = adoid;
9773 1185 : AssignDumpId(&attrdefs[j].dobj);
9774 1185 : attrdefs[j].adtable = tbinfo;
9775 1185 : attrdefs[j].adnum = adnum;
9776 1185 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9777 :
9778 1185 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9779 1185 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9780 :
9781 1185 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9782 :
9783 : /*
9784 : * Figure out whether the default/generation expression should be
9785 : * dumped as part of the main CREATE TABLE (or similar) command or
9786 : * as a separate ALTER TABLE (or similar) command. The preference
9787 : * is to put it into the CREATE command, but in some cases that's
9788 : * not possible.
9789 : */
9790 1185 : if (tbinfo->attgenerated[adnum - 1])
9791 : {
9792 : /*
9793 : * Column generation expressions cannot be dumped separately,
9794 : * because there is no syntax for it. By setting separate to
9795 : * false here we prevent the "default" from being processed as
9796 : * its own dumpable object. Later, flagInhAttrs() will mark
9797 : * it as not to be dumped at all, if possible (that is, if it
9798 : * can be inherited from a parent).
9799 : */
9800 656 : attrdefs[j].separate = false;
9801 : }
9802 529 : else if (tbinfo->relkind == RELKIND_VIEW)
9803 : {
9804 : /*
9805 : * Defaults on a VIEW must always be dumped as separate ALTER
9806 : * TABLE commands.
9807 : */
9808 32 : attrdefs[j].separate = true;
9809 : }
9810 497 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9811 : {
9812 : /* column will be suppressed, print default separately */
9813 4 : attrdefs[j].separate = true;
9814 : }
9815 : else
9816 : {
9817 493 : attrdefs[j].separate = false;
9818 : }
9819 :
9820 1185 : if (!attrdefs[j].separate)
9821 : {
9822 : /*
9823 : * Mark the default as needing to appear before the table, so
9824 : * that any dependencies it has must be emitted before the
9825 : * CREATE TABLE. If this is not possible, we'll change to
9826 : * "separate" mode while sorting dependencies.
9827 : */
9828 1149 : addObjectDependency(&tbinfo->dobj,
9829 1149 : attrdefs[j].dobj.dumpId);
9830 : }
9831 :
9832 1185 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9833 : }
9834 :
9835 60 : PQclear(res);
9836 : }
9837 :
9838 : /*
9839 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9840 : * data-only dump, as it is only needed for table schemas.
9841 : */
9842 259 : if (dopt->dumpSchema && invalidnotnulloids)
9843 : {
9844 : ConstraintInfo *constrs;
9845 : int numConstrs;
9846 : int i_tableoid;
9847 : int i_oid;
9848 : int i_conrelid;
9849 : int i_conname;
9850 : int i_consrc;
9851 : int i_conislocal;
9852 :
9853 37 : pg_log_info("finding invalid not-null constraints");
9854 :
9855 37 : resetPQExpBuffer(q);
9856 37 : appendPQExpBuffer(q,
9857 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9858 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9859 : "conislocal, convalidated "
9860 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9861 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9862 : "ORDER BY c.conrelid, c.conname",
9863 37 : invalidnotnulloids->data);
9864 :
9865 37 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9866 :
9867 37 : numConstrs = PQntuples(res);
9868 37 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9869 :
9870 37 : i_tableoid = PQfnumber(res, "tableoid");
9871 37 : i_oid = PQfnumber(res, "oid");
9872 37 : i_conrelid = PQfnumber(res, "conrelid");
9873 37 : i_conname = PQfnumber(res, "conname");
9874 37 : i_consrc = PQfnumber(res, "consrc");
9875 37 : i_conislocal = PQfnumber(res, "conislocal");
9876 :
9877 : /* As above, this loop iterates once per table, not once per row */
9878 37 : curtblindx = -1;
9879 104 : for (int j = 0; j < numConstrs;)
9880 : {
9881 67 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9882 67 : TableInfo *tbinfo = NULL;
9883 : int numcons;
9884 :
9885 : /* Count rows for this table */
9886 67 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9887 30 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9888 30 : break;
9889 :
9890 : /*
9891 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9892 : * OID order.
9893 : */
9894 13417 : while (++curtblindx < numTables)
9895 : {
9896 13417 : tbinfo = &tblinfo[curtblindx];
9897 13417 : if (tbinfo->dobj.catId.oid == conrelid)
9898 67 : break;
9899 : }
9900 67 : if (curtblindx >= numTables)
9901 0 : pg_fatal("unrecognized table OID %u", conrelid);
9902 :
9903 134 : for (int c = 0; c < numcons; c++, j++)
9904 : {
9905 67 : constrs[j].dobj.objType = DO_CONSTRAINT;
9906 67 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9907 67 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9908 67 : AssignDumpId(&constrs[j].dobj);
9909 67 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9910 67 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9911 67 : constrs[j].contable = tbinfo;
9912 67 : constrs[j].condomain = NULL;
9913 67 : constrs[j].contype = 'n';
9914 67 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9915 67 : constrs[j].confrelid = InvalidOid;
9916 67 : constrs[j].conindex = 0;
9917 67 : constrs[j].condeferrable = false;
9918 67 : constrs[j].condeferred = false;
9919 67 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9920 :
9921 : /*
9922 : * All invalid not-null constraints must be dumped separately,
9923 : * because CREATE TABLE would not create them as invalid, and
9924 : * also because they must be created after potentially
9925 : * violating data has been loaded.
9926 : */
9927 67 : constrs[j].separate = true;
9928 :
9929 67 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9930 : }
9931 : }
9932 37 : PQclear(res);
9933 : }
9934 :
9935 : /*
9936 : * Get info about table CHECK constraints. This is skipped for a
9937 : * data-only dump, as it is only needed for table schemas.
9938 : */
9939 259 : if (dopt->dumpSchema && checkoids->len > 2)
9940 : {
9941 : ConstraintInfo *constrs;
9942 : int numConstrs;
9943 : int i_tableoid;
9944 : int i_oid;
9945 : int i_conrelid;
9946 : int i_conname;
9947 : int i_consrc;
9948 : int i_conislocal;
9949 : int i_convalidated;
9950 :
9951 61 : pg_log_info("finding table check constraints");
9952 :
9953 61 : resetPQExpBuffer(q);
9954 61 : appendPQExpBuffer(q,
9955 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9956 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9957 : "conislocal, convalidated "
9958 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9959 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9960 : "WHERE contype = 'c' "
9961 : "ORDER BY c.conrelid, c.conname",
9962 : checkoids->data);
9963 :
9964 61 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9965 :
9966 61 : numConstrs = PQntuples(res);
9967 61 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9968 :
9969 61 : i_tableoid = PQfnumber(res, "tableoid");
9970 61 : i_oid = PQfnumber(res, "oid");
9971 61 : i_conrelid = PQfnumber(res, "conrelid");
9972 61 : i_conname = PQfnumber(res, "conname");
9973 61 : i_consrc = PQfnumber(res, "consrc");
9974 61 : i_conislocal = PQfnumber(res, "conislocal");
9975 61 : i_convalidated = PQfnumber(res, "convalidated");
9976 :
9977 : /* As above, this loop iterates once per table, not once per row */
9978 61 : curtblindx = -1;
9979 538 : for (int j = 0; j < numConstrs;)
9980 : {
9981 477 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9982 477 : TableInfo *tbinfo = NULL;
9983 : int numcons;
9984 :
9985 : /* Count rows for this table */
9986 612 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9987 551 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9988 416 : break;
9989 :
9990 : /*
9991 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9992 : * OID order.
9993 : */
9994 19408 : while (++curtblindx < numTables)
9995 : {
9996 19408 : tbinfo = &tblinfo[curtblindx];
9997 19408 : if (tbinfo->dobj.catId.oid == conrelid)
9998 477 : break;
9999 : }
10000 477 : if (curtblindx >= numTables)
10001 0 : pg_fatal("unrecognized table OID %u", conrelid);
10002 :
10003 477 : if (numcons != tbinfo->ncheck)
10004 : {
10005 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
10006 : "expected %d check constraints on table \"%s\" but found %d",
10007 : tbinfo->ncheck),
10008 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
10009 0 : pg_log_error_hint("The system catalogs might be corrupted.");
10010 0 : exit_nicely(1);
10011 : }
10012 :
10013 477 : tbinfo->checkexprs = constrs + j;
10014 :
10015 1089 : for (int c = 0; c < numcons; c++, j++)
10016 : {
10017 612 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
10018 :
10019 612 : constrs[j].dobj.objType = DO_CONSTRAINT;
10020 612 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
10021 612 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
10022 612 : AssignDumpId(&constrs[j].dobj);
10023 612 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
10024 612 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
10025 612 : constrs[j].contable = tbinfo;
10026 612 : constrs[j].condomain = NULL;
10027 612 : constrs[j].contype = 'c';
10028 612 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
10029 612 : constrs[j].confrelid = InvalidOid;
10030 612 : constrs[j].conindex = 0;
10031 612 : constrs[j].condeferrable = false;
10032 612 : constrs[j].condeferred = false;
10033 612 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
10034 :
10035 : /*
10036 : * An unvalidated constraint needs to be dumped separately, so
10037 : * that potentially-violating existing data is loaded before
10038 : * the constraint.
10039 : */
10040 612 : constrs[j].separate = !validated;
10041 :
10042 612 : constrs[j].dobj.dump = tbinfo->dobj.dump;
10043 :
10044 : /*
10045 : * Mark the constraint as needing to appear before the table
10046 : * --- this is so that any other dependencies of the
10047 : * constraint will be emitted before we try to create the
10048 : * table. If the constraint is to be dumped separately, it
10049 : * will be dumped after data is loaded anyway, so don't do it.
10050 : * (There's an automatic dependency in the opposite direction
10051 : * anyway, so don't need to add one manually here.)
10052 : */
10053 612 : if (!constrs[j].separate)
10054 547 : addObjectDependency(&tbinfo->dobj,
10055 547 : constrs[j].dobj.dumpId);
10056 :
10057 : /*
10058 : * We will detect later whether the constraint must be split
10059 : * out from the table definition.
10060 : */
10061 : }
10062 : }
10063 :
10064 61 : PQclear(res);
10065 : }
10066 :
10067 259 : destroyPQExpBuffer(q);
10068 259 : destroyPQExpBuffer(tbloids);
10069 259 : destroyPQExpBuffer(checkoids);
10070 259 : }
10071 :
10072 : /*
10073 : * Based on the getTableAttrs query's row corresponding to one column, set
10074 : * the name and flags to handle a not-null constraint for that column in
10075 : * the tbinfo struct.
10076 : *
10077 : * Result row 'r' is for tbinfo's attribute 'j'.
10078 : *
10079 : * There are four possibilities:
10080 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
10081 : * (the constraint name) remains NULL.
10082 : * 2) The column has a constraint with no name (this is the case when
10083 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
10084 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
10085 : * 3) The column has an invalid not-null constraint. This must be treated
10086 : * as a separate object (because it must be created after the table data
10087 : * is loaded). So we add its OID to invalidnotnulloids for processing
10088 : * elsewhere and do nothing further with it here. We distinguish this
10089 : * case because the "notnull_invalidoid" column has been set to a non-NULL
10090 : * value, which is the constraint OID. Valid constraints have a null OID.
10091 : * 4) The column has a constraint with a known name; in that case
10092 : * notnull_constrs carries that name and dumpTableSchema will print
10093 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
10094 : * (table_column_not_null) and there's no comment on the constraint,
10095 : * there's no need to print that name in the dump, so notnull_constrs
10096 : * is set to the empty string and it behaves as case 2.
10097 : *
10098 : * In a child table that inherits from a parent already containing NOT NULL
10099 : * constraints and the columns in the child don't have their own NOT NULL
10100 : * declarations, we suppress printing constraints in the child: the
10101 : * constraints are acquired at the point where the child is attached to the
10102 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
10103 : * set not here but in flagInhAttrs. That flag is also used when the
10104 : * constraint was validated in a child but all its parent have it as NOT
10105 : * VALID.
10106 : *
10107 : * Any of these constraints might have the NO INHERIT bit. If so we set
10108 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10109 : *
10110 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
10111 : * to do the right thing in all but the trivial case. However, the downside
10112 : * of getting it wrong is simply that the name is printed rather than
10113 : * suppressed, so it's not a big deal.
10114 : *
10115 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10116 : * constraints are found, it is initialized and filled with the array of
10117 : * OIDs of such constraints, for later processing.
10118 : */
10119 : static void
10120 26200 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
10121 : TableInfo *tbinfo, int j,
10122 : int i_notnull_name,
10123 : int i_notnull_comment,
10124 : int i_notnull_invalidoid,
10125 : int i_notnull_noinherit,
10126 : int i_notnull_islocal,
10127 : PQExpBuffer *invalidnotnulloids)
10128 : {
10129 26200 : DumpOptions *dopt = fout->dopt;
10130 :
10131 : /*
10132 : * If this not-null constraint is not valid, list its OID in
10133 : * invalidnotnulloids and do nothing further. It'll be processed
10134 : * elsewhere later.
10135 : *
10136 : * Because invalid not-null constraints are rare, we don't want to malloc
10137 : * invalidnotnulloids until we're sure we're going it need it, which
10138 : * happens here.
10139 : */
10140 26200 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10141 : {
10142 73 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10143 :
10144 73 : if (*invalidnotnulloids == NULL)
10145 : {
10146 43 : *invalidnotnulloids = createPQExpBuffer();
10147 43 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10148 43 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10149 : }
10150 : else
10151 30 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10152 :
10153 : /*
10154 : * Track when a parent constraint is invalid for the cases where a
10155 : * child constraint has been validated independenly.
10156 : */
10157 73 : tbinfo->notnull_invalid[j] = true;
10158 :
10159 : /* nothing else to do */
10160 73 : tbinfo->notnull_constrs[j] = NULL;
10161 73 : return;
10162 : }
10163 :
10164 : /*
10165 : * notnull_noinh is straight from the query result. notnull_islocal also,
10166 : * though flagInhAttrs may change that one later.
10167 : */
10168 26127 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10169 26127 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10170 26127 : tbinfo->notnull_invalid[j] = false;
10171 :
10172 : /*
10173 : * Determine a constraint name to use. If the column is not marked not-
10174 : * null, we set NULL which cues ... to do nothing. An empty string says
10175 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10176 : * use.
10177 : */
10178 26127 : if (fout->remoteVersion < 180000)
10179 : {
10180 : /*
10181 : * < 18 doesn't have not-null names, so an unnamed constraint is
10182 : * sufficient.
10183 : */
10184 0 : if (PQgetisnull(res, r, i_notnull_name))
10185 0 : tbinfo->notnull_constrs[j] = NULL;
10186 : else
10187 0 : tbinfo->notnull_constrs[j] = "";
10188 : }
10189 : else
10190 : {
10191 26127 : if (PQgetisnull(res, r, i_notnull_name))
10192 23274 : tbinfo->notnull_constrs[j] = NULL;
10193 : else
10194 : {
10195 : /*
10196 : * In binary upgrade of inheritance child tables, must have a
10197 : * constraint name that we can UPDATE later; same if there's a
10198 : * comment on the constraint.
10199 : */
10200 2853 : if ((dopt->binary_upgrade &&
10201 364 : !tbinfo->ispartition &&
10202 3112 : !tbinfo->notnull_islocal[j]) ||
10203 2832 : !PQgetisnull(res, r, i_notnull_comment))
10204 : {
10205 67 : tbinfo->notnull_constrs[j] =
10206 67 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10207 : }
10208 : else
10209 : {
10210 : char *default_name;
10211 :
10212 : /* XXX should match ChooseConstraintName better */
10213 2786 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10214 2786 : tbinfo->attnames[j]);
10215 2786 : if (strcmp(default_name,
10216 2786 : PQgetvalue(res, r, i_notnull_name)) == 0)
10217 1873 : tbinfo->notnull_constrs[j] = "";
10218 : else
10219 : {
10220 913 : tbinfo->notnull_constrs[j] =
10221 913 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10222 : }
10223 2786 : free(default_name);
10224 : }
10225 : }
10226 : }
10227 : }
10228 :
10229 : /*
10230 : * Test whether a column should be printed as part of table's CREATE TABLE.
10231 : * Column number is zero-based.
10232 : *
10233 : * Normally this is always true, but it's false for dropped columns, as well
10234 : * as those that were inherited without any local definition. (If we print
10235 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10236 : * For partitions, it's always true, because we want the partitions to be
10237 : * created independently and ATTACH PARTITION used afterwards.
10238 : *
10239 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10240 : * attisdropped state later, so as to keep control of the physical column
10241 : * order.
10242 : *
10243 : * This function exists because there are scattered nonobvious places that
10244 : * must be kept in sync with this decision.
10245 : */
10246 : bool
10247 42698 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10248 : {
10249 42698 : if (dopt->binary_upgrade)
10250 6560 : return true;
10251 36138 : if (tbinfo->attisdropped[colno])
10252 734 : return false;
10253 35404 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10254 : }
10255 :
10256 :
10257 : /*
10258 : * getTSParsers:
10259 : * get information about all text search parsers in the system catalogs
10260 : */
10261 : void
10262 259 : getTSParsers(Archive *fout)
10263 : {
10264 : PGresult *res;
10265 : int ntups;
10266 : int i;
10267 : PQExpBuffer query;
10268 : TSParserInfo *prsinfo;
10269 : int i_tableoid;
10270 : int i_oid;
10271 : int i_prsname;
10272 : int i_prsnamespace;
10273 : int i_prsstart;
10274 : int i_prstoken;
10275 : int i_prsend;
10276 : int i_prsheadline;
10277 : int i_prslextype;
10278 :
10279 259 : query = createPQExpBuffer();
10280 :
10281 : /*
10282 : * find all text search objects, including builtin ones; we filter out
10283 : * system-defined objects at dump-out time.
10284 : */
10285 :
10286 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10287 : "prsstart::oid, prstoken::oid, "
10288 : "prsend::oid, prsheadline::oid, prslextype::oid "
10289 : "FROM pg_ts_parser");
10290 :
10291 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10292 :
10293 259 : ntups = PQntuples(res);
10294 :
10295 259 : prsinfo = pg_malloc_array(TSParserInfo, ntups);
10296 :
10297 259 : i_tableoid = PQfnumber(res, "tableoid");
10298 259 : i_oid = PQfnumber(res, "oid");
10299 259 : i_prsname = PQfnumber(res, "prsname");
10300 259 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10301 259 : i_prsstart = PQfnumber(res, "prsstart");
10302 259 : i_prstoken = PQfnumber(res, "prstoken");
10303 259 : i_prsend = PQfnumber(res, "prsend");
10304 259 : i_prsheadline = PQfnumber(res, "prsheadline");
10305 259 : i_prslextype = PQfnumber(res, "prslextype");
10306 :
10307 563 : for (i = 0; i < ntups; i++)
10308 : {
10309 304 : prsinfo[i].dobj.objType = DO_TSPARSER;
10310 304 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10311 304 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10312 304 : AssignDumpId(&prsinfo[i].dobj);
10313 304 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10314 608 : prsinfo[i].dobj.namespace =
10315 304 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10316 304 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10317 304 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10318 304 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10319 304 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10320 304 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10321 :
10322 : /* Decide whether we want to dump it */
10323 304 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10324 : }
10325 :
10326 259 : PQclear(res);
10327 :
10328 259 : destroyPQExpBuffer(query);
10329 259 : }
10330 :
10331 : /*
10332 : * getTSDictionaries:
10333 : * get information about all text search dictionaries in the system catalogs
10334 : */
10335 : void
10336 259 : getTSDictionaries(Archive *fout)
10337 : {
10338 : PGresult *res;
10339 : int ntups;
10340 : int i;
10341 : PQExpBuffer query;
10342 : TSDictInfo *dictinfo;
10343 : int i_tableoid;
10344 : int i_oid;
10345 : int i_dictname;
10346 : int i_dictnamespace;
10347 : int i_dictowner;
10348 : int i_dicttemplate;
10349 : int i_dictinitoption;
10350 :
10351 259 : query = createPQExpBuffer();
10352 :
10353 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10354 : "dictnamespace, dictowner, "
10355 : "dicttemplate, dictinitoption "
10356 : "FROM pg_ts_dict");
10357 :
10358 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10359 :
10360 259 : ntups = PQntuples(res);
10361 :
10362 259 : dictinfo = pg_malloc_array(TSDictInfo, ntups);
10363 :
10364 259 : i_tableoid = PQfnumber(res, "tableoid");
10365 259 : i_oid = PQfnumber(res, "oid");
10366 259 : i_dictname = PQfnumber(res, "dictname");
10367 259 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10368 259 : i_dictowner = PQfnumber(res, "dictowner");
10369 259 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10370 259 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10371 :
10372 8655 : for (i = 0; i < ntups; i++)
10373 : {
10374 8396 : dictinfo[i].dobj.objType = DO_TSDICT;
10375 8396 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10376 8396 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10377 8396 : AssignDumpId(&dictinfo[i].dobj);
10378 8396 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10379 16792 : dictinfo[i].dobj.namespace =
10380 8396 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10381 8396 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10382 8396 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10383 8396 : if (PQgetisnull(res, i, i_dictinitoption))
10384 304 : dictinfo[i].dictinitoption = NULL;
10385 : else
10386 8092 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10387 :
10388 : /* Decide whether we want to dump it */
10389 8396 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10390 : }
10391 :
10392 259 : PQclear(res);
10393 :
10394 259 : destroyPQExpBuffer(query);
10395 259 : }
10396 :
10397 : /*
10398 : * getTSTemplates:
10399 : * get information about all text search templates in the system catalogs
10400 : */
10401 : void
10402 259 : getTSTemplates(Archive *fout)
10403 : {
10404 : PGresult *res;
10405 : int ntups;
10406 : int i;
10407 : PQExpBuffer query;
10408 : TSTemplateInfo *tmplinfo;
10409 : int i_tableoid;
10410 : int i_oid;
10411 : int i_tmplname;
10412 : int i_tmplnamespace;
10413 : int i_tmplinit;
10414 : int i_tmpllexize;
10415 :
10416 259 : query = createPQExpBuffer();
10417 :
10418 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10419 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10420 : "FROM pg_ts_template");
10421 :
10422 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10423 :
10424 259 : ntups = PQntuples(res);
10425 :
10426 259 : tmplinfo = pg_malloc_array(TSTemplateInfo, ntups);
10427 :
10428 259 : i_tableoid = PQfnumber(res, "tableoid");
10429 259 : i_oid = PQfnumber(res, "oid");
10430 259 : i_tmplname = PQfnumber(res, "tmplname");
10431 259 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10432 259 : i_tmplinit = PQfnumber(res, "tmplinit");
10433 259 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10434 :
10435 1599 : for (i = 0; i < ntups; i++)
10436 : {
10437 1340 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10438 1340 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10439 1340 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10440 1340 : AssignDumpId(&tmplinfo[i].dobj);
10441 1340 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10442 2680 : tmplinfo[i].dobj.namespace =
10443 1340 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10444 1340 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10445 1340 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10446 :
10447 : /* Decide whether we want to dump it */
10448 1340 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10449 : }
10450 :
10451 259 : PQclear(res);
10452 :
10453 259 : destroyPQExpBuffer(query);
10454 259 : }
10455 :
10456 : /*
10457 : * getTSConfigurations:
10458 : * get information about all text search configurations
10459 : */
10460 : void
10461 259 : getTSConfigurations(Archive *fout)
10462 : {
10463 : PGresult *res;
10464 : int ntups;
10465 : int i;
10466 : PQExpBuffer query;
10467 : TSConfigInfo *cfginfo;
10468 : int i_tableoid;
10469 : int i_oid;
10470 : int i_cfgname;
10471 : int i_cfgnamespace;
10472 : int i_cfgowner;
10473 : int i_cfgparser;
10474 :
10475 259 : query = createPQExpBuffer();
10476 :
10477 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10478 : "cfgnamespace, cfgowner, cfgparser "
10479 : "FROM pg_ts_config");
10480 :
10481 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10482 :
10483 259 : ntups = PQntuples(res);
10484 :
10485 259 : cfginfo = pg_malloc_array(TSConfigInfo, ntups);
10486 :
10487 259 : i_tableoid = PQfnumber(res, "tableoid");
10488 259 : i_oid = PQfnumber(res, "oid");
10489 259 : i_cfgname = PQfnumber(res, "cfgname");
10490 259 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10491 259 : i_cfgowner = PQfnumber(res, "cfgowner");
10492 259 : i_cfgparser = PQfnumber(res, "cfgparser");
10493 :
10494 8620 : for (i = 0; i < ntups; i++)
10495 : {
10496 8361 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10497 8361 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10498 8361 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10499 8361 : AssignDumpId(&cfginfo[i].dobj);
10500 8361 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10501 16722 : cfginfo[i].dobj.namespace =
10502 8361 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10503 8361 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10504 8361 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10505 :
10506 : /* Decide whether we want to dump it */
10507 8361 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10508 : }
10509 :
10510 259 : PQclear(res);
10511 :
10512 259 : destroyPQExpBuffer(query);
10513 259 : }
10514 :
10515 : /*
10516 : * getForeignDataWrappers:
10517 : * get information about all foreign-data wrappers in the system catalogs
10518 : */
10519 : void
10520 259 : getForeignDataWrappers(Archive *fout)
10521 : {
10522 : PGresult *res;
10523 : int ntups;
10524 : int i;
10525 : PQExpBuffer query;
10526 : FdwInfo *fdwinfo;
10527 : int i_tableoid;
10528 : int i_oid;
10529 : int i_fdwname;
10530 : int i_fdwowner;
10531 : int i_fdwhandler;
10532 : int i_fdwvalidator;
10533 : int i_fdwconnection;
10534 : int i_fdwacl;
10535 : int i_acldefault;
10536 : int i_fdwoptions;
10537 :
10538 259 : query = createPQExpBuffer();
10539 :
10540 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10541 : "fdwowner, "
10542 : "fdwhandler::pg_catalog.regproc, "
10543 : "fdwvalidator::pg_catalog.regproc, ");
10544 :
10545 259 : if (fout->remoteVersion >= 190000)
10546 259 : appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10547 : else
10548 0 : appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10549 :
10550 259 : appendPQExpBufferStr(query,
10551 : "fdwacl, "
10552 : "acldefault('F', fdwowner) AS acldefault, "
10553 : "array_to_string(ARRAY("
10554 : "SELECT quote_ident(option_name) || ' ' || "
10555 : "quote_literal(option_value) "
10556 : "FROM pg_options_to_table(fdwoptions) "
10557 : "ORDER BY option_name"
10558 : "), E',\n ') AS fdwoptions "
10559 : "FROM pg_foreign_data_wrapper");
10560 :
10561 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10562 :
10563 259 : ntups = PQntuples(res);
10564 :
10565 259 : fdwinfo = pg_malloc_array(FdwInfo, ntups);
10566 :
10567 259 : i_tableoid = PQfnumber(res, "tableoid");
10568 259 : i_oid = PQfnumber(res, "oid");
10569 259 : i_fdwname = PQfnumber(res, "fdwname");
10570 259 : i_fdwowner = PQfnumber(res, "fdwowner");
10571 259 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10572 259 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10573 259 : i_fdwconnection = PQfnumber(res, "fdwconnection");
10574 259 : i_fdwacl = PQfnumber(res, "fdwacl");
10575 259 : i_acldefault = PQfnumber(res, "acldefault");
10576 259 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10577 :
10578 330 : for (i = 0; i < ntups; i++)
10579 : {
10580 71 : fdwinfo[i].dobj.objType = DO_FDW;
10581 71 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10582 71 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10583 71 : AssignDumpId(&fdwinfo[i].dobj);
10584 71 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10585 71 : fdwinfo[i].dobj.namespace = NULL;
10586 71 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10587 71 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10588 71 : fdwinfo[i].dacl.privtype = 0;
10589 71 : fdwinfo[i].dacl.initprivs = NULL;
10590 71 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10591 71 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10592 71 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10593 71 : fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10594 71 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10595 :
10596 : /* Decide whether we want to dump it */
10597 71 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10598 :
10599 : /* Mark whether FDW has an ACL */
10600 71 : if (!PQgetisnull(res, i, i_fdwacl))
10601 45 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10602 : }
10603 :
10604 259 : PQclear(res);
10605 :
10606 259 : destroyPQExpBuffer(query);
10607 259 : }
10608 :
10609 : /*
10610 : * getForeignServers:
10611 : * get information about all foreign servers in the system catalogs
10612 : */
10613 : void
10614 259 : getForeignServers(Archive *fout)
10615 : {
10616 : PGresult *res;
10617 : int ntups;
10618 : int i;
10619 : PQExpBuffer query;
10620 : ForeignServerInfo *srvinfo;
10621 : int i_tableoid;
10622 : int i_oid;
10623 : int i_srvname;
10624 : int i_srvowner;
10625 : int i_srvfdw;
10626 : int i_srvtype;
10627 : int i_srvversion;
10628 : int i_srvacl;
10629 : int i_acldefault;
10630 : int i_srvoptions;
10631 :
10632 259 : query = createPQExpBuffer();
10633 :
10634 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10635 : "srvowner, "
10636 : "srvfdw, srvtype, srvversion, srvacl, "
10637 : "acldefault('S', srvowner) AS acldefault, "
10638 : "array_to_string(ARRAY("
10639 : "SELECT quote_ident(option_name) || ' ' || "
10640 : "quote_literal(option_value) "
10641 : "FROM pg_options_to_table(srvoptions) "
10642 : "ORDER BY option_name"
10643 : "), E',\n ') AS srvoptions "
10644 : "FROM pg_foreign_server");
10645 :
10646 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10647 :
10648 259 : ntups = PQntuples(res);
10649 :
10650 259 : srvinfo = pg_malloc_array(ForeignServerInfo, ntups);
10651 :
10652 259 : i_tableoid = PQfnumber(res, "tableoid");
10653 259 : i_oid = PQfnumber(res, "oid");
10654 259 : i_srvname = PQfnumber(res, "srvname");
10655 259 : i_srvowner = PQfnumber(res, "srvowner");
10656 259 : i_srvfdw = PQfnumber(res, "srvfdw");
10657 259 : i_srvtype = PQfnumber(res, "srvtype");
10658 259 : i_srvversion = PQfnumber(res, "srvversion");
10659 259 : i_srvacl = PQfnumber(res, "srvacl");
10660 259 : i_acldefault = PQfnumber(res, "acldefault");
10661 259 : i_srvoptions = PQfnumber(res, "srvoptions");
10662 :
10663 334 : for (i = 0; i < ntups; i++)
10664 : {
10665 75 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10666 75 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10667 75 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10668 75 : AssignDumpId(&srvinfo[i].dobj);
10669 75 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10670 75 : srvinfo[i].dobj.namespace = NULL;
10671 75 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10672 75 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10673 75 : srvinfo[i].dacl.privtype = 0;
10674 75 : srvinfo[i].dacl.initprivs = NULL;
10675 75 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10676 75 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10677 75 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10678 75 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10679 75 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10680 :
10681 : /* Decide whether we want to dump it */
10682 75 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10683 :
10684 : /* Servers have user mappings */
10685 75 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10686 :
10687 : /* Mark whether server has an ACL */
10688 75 : if (!PQgetisnull(res, i, i_srvacl))
10689 45 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10690 : }
10691 :
10692 259 : PQclear(res);
10693 :
10694 259 : destroyPQExpBuffer(query);
10695 259 : }
10696 :
10697 : /*
10698 : * getDefaultACLs:
10699 : * get information about all default ACL information in the system catalogs
10700 : */
10701 : void
10702 259 : getDefaultACLs(Archive *fout)
10703 : {
10704 259 : DumpOptions *dopt = fout->dopt;
10705 : DefaultACLInfo *daclinfo;
10706 : PQExpBuffer query;
10707 : PGresult *res;
10708 : int i_oid;
10709 : int i_tableoid;
10710 : int i_defaclrole;
10711 : int i_defaclnamespace;
10712 : int i_defaclobjtype;
10713 : int i_defaclacl;
10714 : int i_acldefault;
10715 : int i,
10716 : ntups;
10717 :
10718 259 : query = createPQExpBuffer();
10719 :
10720 : /*
10721 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10722 : * ACL for their object type. We should dump them as deltas from the
10723 : * default ACL, since that will be used as a starting point for
10724 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10725 : * non-global entries can only add privileges not revoke them. We must
10726 : * dump those as-is (i.e., as deltas from an empty ACL).
10727 : *
10728 : * We can use defaclobjtype as the object type for acldefault(), except
10729 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10730 : * 's'.
10731 : */
10732 259 : appendPQExpBufferStr(query,
10733 : "SELECT oid, tableoid, "
10734 : "defaclrole, "
10735 : "defaclnamespace, "
10736 : "defaclobjtype, "
10737 : "defaclacl, "
10738 : "CASE WHEN defaclnamespace = 0 THEN "
10739 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10740 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10741 : "defaclrole) ELSE '{}' END AS acldefault "
10742 : "FROM pg_default_acl");
10743 :
10744 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10745 :
10746 259 : ntups = PQntuples(res);
10747 :
10748 259 : daclinfo = pg_malloc_array(DefaultACLInfo, ntups);
10749 :
10750 259 : i_oid = PQfnumber(res, "oid");
10751 259 : i_tableoid = PQfnumber(res, "tableoid");
10752 259 : i_defaclrole = PQfnumber(res, "defaclrole");
10753 259 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10754 259 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10755 259 : i_defaclacl = PQfnumber(res, "defaclacl");
10756 259 : i_acldefault = PQfnumber(res, "acldefault");
10757 :
10758 453 : for (i = 0; i < ntups; i++)
10759 : {
10760 194 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10761 :
10762 194 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10763 194 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10764 194 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10765 194 : AssignDumpId(&daclinfo[i].dobj);
10766 : /* cheesy ... is it worth coming up with a better object name? */
10767 194 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10768 :
10769 194 : if (nspid != InvalidOid)
10770 90 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10771 : else
10772 104 : daclinfo[i].dobj.namespace = NULL;
10773 :
10774 194 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10775 194 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10776 194 : daclinfo[i].dacl.privtype = 0;
10777 194 : daclinfo[i].dacl.initprivs = NULL;
10778 194 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10779 194 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10780 :
10781 : /* Default ACLs are ACLs, of course */
10782 194 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10783 :
10784 : /* Decide whether we want to dump it */
10785 194 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10786 : }
10787 :
10788 259 : PQclear(res);
10789 :
10790 259 : destroyPQExpBuffer(query);
10791 259 : }
10792 :
10793 : /*
10794 : * getRoleName -- look up the name of a role, given its OID
10795 : *
10796 : * In current usage, we don't expect failures, so error out for a bad OID.
10797 : */
10798 : static const char *
10799 842828 : getRoleName(const char *roleoid_str)
10800 : {
10801 842828 : Oid roleoid = atooid(roleoid_str);
10802 :
10803 : /*
10804 : * Do binary search to find the appropriate item.
10805 : */
10806 842828 : if (nrolenames > 0)
10807 : {
10808 842828 : RoleNameItem *low = &rolenames[0];
10809 842828 : RoleNameItem *high = &rolenames[nrolenames - 1];
10810 :
10811 3371374 : while (low <= high)
10812 : {
10813 3371374 : RoleNameItem *middle = low + (high - low) / 2;
10814 :
10815 3371374 : if (roleoid < middle->roleoid)
10816 2527262 : high = middle - 1;
10817 844112 : else if (roleoid > middle->roleoid)
10818 1284 : low = middle + 1;
10819 : else
10820 842828 : return middle->rolename; /* found a match */
10821 : }
10822 : }
10823 :
10824 0 : pg_fatal("role with OID %u does not exist", roleoid);
10825 : return NULL; /* keep compiler quiet */
10826 : }
10827 :
10828 : /*
10829 : * collectRoleNames --
10830 : *
10831 : * Construct a table of all known roles.
10832 : * The table is sorted by OID for speed in lookup.
10833 : */
10834 : static void
10835 260 : collectRoleNames(Archive *fout)
10836 : {
10837 : PGresult *res;
10838 : const char *query;
10839 : int i;
10840 :
10841 260 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10842 :
10843 260 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10844 :
10845 260 : nrolenames = PQntuples(res);
10846 :
10847 260 : rolenames = pg_malloc_array(RoleNameItem, nrolenames);
10848 :
10849 5772 : for (i = 0; i < nrolenames; i++)
10850 : {
10851 5512 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10852 5512 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10853 : }
10854 :
10855 260 : PQclear(res);
10856 260 : }
10857 :
10858 : /*
10859 : * getAdditionalACLs
10860 : *
10861 : * We have now created all the DumpableObjects, and collected the ACL data
10862 : * that appears in the directly-associated catalog entries. However, there's
10863 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10864 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10865 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10866 : * Also, in versions having the pg_init_privs catalog, read that and load the
10867 : * information into the relevant DumpableObjects.
10868 : */
10869 : static void
10870 257 : getAdditionalACLs(Archive *fout)
10871 : {
10872 257 : PQExpBuffer query = createPQExpBuffer();
10873 : PGresult *res;
10874 : int ntups,
10875 : i;
10876 :
10877 : /* Check for per-column ACLs */
10878 257 : appendPQExpBufferStr(query,
10879 : "SELECT DISTINCT attrelid FROM pg_attribute "
10880 : "WHERE attacl IS NOT NULL");
10881 :
10882 257 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10883 :
10884 257 : ntups = PQntuples(res);
10885 684 : for (i = 0; i < ntups; i++)
10886 : {
10887 427 : Oid relid = atooid(PQgetvalue(res, i, 0));
10888 : TableInfo *tblinfo;
10889 :
10890 427 : tblinfo = findTableByOid(relid);
10891 : /* OK to ignore tables we haven't got a DumpableObject for */
10892 427 : if (tblinfo)
10893 : {
10894 427 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10895 427 : tblinfo->hascolumnACLs = true;
10896 : }
10897 : }
10898 257 : PQclear(res);
10899 :
10900 : /* Fetch initial-privileges data */
10901 257 : if (fout->remoteVersion >= 90600)
10902 : {
10903 257 : printfPQExpBuffer(query,
10904 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10905 : "FROM pg_init_privs");
10906 :
10907 257 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10908 :
10909 257 : ntups = PQntuples(res);
10910 63016 : for (i = 0; i < ntups; i++)
10911 : {
10912 62759 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10913 62759 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10914 62759 : int objsubid = atoi(PQgetvalue(res, i, 2));
10915 62759 : char privtype = *(PQgetvalue(res, i, 3));
10916 62759 : char *initprivs = PQgetvalue(res, i, 4);
10917 : CatalogId objId;
10918 : DumpableObject *dobj;
10919 :
10920 62759 : objId.tableoid = classoid;
10921 62759 : objId.oid = objoid;
10922 62759 : dobj = findObjectByCatalogId(objId);
10923 : /* OK to ignore entries we haven't got a DumpableObject for */
10924 62759 : if (dobj)
10925 : {
10926 : /* Cope with sub-object initprivs */
10927 45584 : if (objsubid != 0)
10928 : {
10929 5421 : if (dobj->objType == DO_TABLE)
10930 : {
10931 : /* For a column initprivs, set the table's ACL flags */
10932 5421 : dobj->components |= DUMP_COMPONENT_ACL;
10933 5421 : ((TableInfo *) dobj)->hascolumnACLs = true;
10934 : }
10935 : else
10936 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10937 : classoid, objoid, objsubid);
10938 5674 : continue;
10939 : }
10940 :
10941 : /*
10942 : * We ignore any pg_init_privs.initprivs entry for the public
10943 : * schema, as explained in getNamespaces().
10944 : */
10945 40163 : if (dobj->objType == DO_NAMESPACE &&
10946 510 : strcmp(dobj->name, "public") == 0)
10947 253 : continue;
10948 :
10949 : /* Else it had better be of a type we think has ACLs */
10950 39910 : if (dobj->objType == DO_NAMESPACE ||
10951 39653 : dobj->objType == DO_TYPE ||
10952 39629 : dobj->objType == DO_FUNC ||
10953 39537 : dobj->objType == DO_AGG ||
10954 39513 : dobj->objType == DO_TABLE ||
10955 0 : dobj->objType == DO_PROCLANG ||
10956 0 : dobj->objType == DO_FDW ||
10957 0 : dobj->objType == DO_FOREIGN_SERVER)
10958 39910 : {
10959 39910 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10960 :
10961 39910 : daobj->dacl.privtype = privtype;
10962 39910 : daobj->dacl.initprivs = pstrdup(initprivs);
10963 : }
10964 : else
10965 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10966 : classoid, objoid, objsubid);
10967 : }
10968 : }
10969 257 : PQclear(res);
10970 : }
10971 :
10972 257 : destroyPQExpBuffer(query);
10973 257 : }
10974 :
10975 : /*
10976 : * dumpCommentExtended --
10977 : *
10978 : * This routine is used to dump any comments associated with the
10979 : * object handed to this routine. The routine takes the object type
10980 : * and object name (ready to print, except for schema decoration), plus
10981 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10982 : * plus catalog ID and subid which are the lookup key for pg_description,
10983 : * plus the dump ID for the object (for setting a dependency).
10984 : * If a matching pg_description entry is found, it is dumped.
10985 : *
10986 : * Note: in some cases, such as comments for triggers and rules, the "type"
10987 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10988 : * but it doesn't seem worth complicating the API for all callers to make
10989 : * it cleaner.
10990 : *
10991 : * Note: although this routine takes a dumpId for dependency purposes,
10992 : * that purpose is just to mark the dependency in the emitted dump file
10993 : * for possible future use by pg_restore. We do NOT use it for determining
10994 : * ordering of the comment in the dump file, because this routine is called
10995 : * after dependency sorting occurs. This routine should be called just after
10996 : * calling ArchiveEntry() for the specified object.
10997 : */
10998 : static void
10999 6764 : dumpCommentExtended(Archive *fout, const char *type,
11000 : const char *name, const char *namespace,
11001 : const char *owner, CatalogId catalogId,
11002 : int subid, DumpId dumpId,
11003 : const char *initdb_comment)
11004 : {
11005 6764 : DumpOptions *dopt = fout->dopt;
11006 : CommentItem *comments;
11007 : int ncomments;
11008 :
11009 : /* do nothing, if --no-comments is supplied */
11010 6764 : if (dopt->no_comments)
11011 0 : return;
11012 :
11013 : /* Comments are schema not data ... except LO comments are data */
11014 6764 : if (strcmp(type, "LARGE OBJECT") != 0)
11015 : {
11016 6706 : if (!dopt->dumpSchema)
11017 0 : return;
11018 : }
11019 : else
11020 : {
11021 : /* We do dump LO comments in binary-upgrade mode */
11022 58 : if (!dopt->dumpData && !dopt->binary_upgrade)
11023 0 : return;
11024 : }
11025 :
11026 : /* Search for comments associated with catalogId, using table */
11027 6764 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
11028 : &comments);
11029 :
11030 : /* Is there one matching the subid? */
11031 6764 : while (ncomments > 0)
11032 : {
11033 6719 : if (comments->objsubid == subid)
11034 6719 : break;
11035 0 : comments++;
11036 0 : ncomments--;
11037 : }
11038 :
11039 6764 : if (initdb_comment != NULL)
11040 : {
11041 : static CommentItem empty_comment = {.descr = ""};
11042 :
11043 : /*
11044 : * initdb creates this object with a comment. Skip dumping the
11045 : * initdb-provided comment, which would complicate matters for
11046 : * non-superuser use of pg_dump. When the DBA has removed initdb's
11047 : * comment, replicate that.
11048 : */
11049 186 : if (ncomments == 0)
11050 : {
11051 4 : comments = &empty_comment;
11052 4 : ncomments = 1;
11053 : }
11054 182 : else if (strcmp(comments->descr, initdb_comment) == 0)
11055 182 : ncomments = 0;
11056 : }
11057 :
11058 : /* If a comment exists, build COMMENT ON statement */
11059 6764 : if (ncomments > 0)
11060 : {
11061 6541 : PQExpBuffer query = createPQExpBuffer();
11062 6541 : PQExpBuffer tag = createPQExpBuffer();
11063 :
11064 6541 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
11065 6541 : if (namespace && *namespace)
11066 6360 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
11067 6541 : appendPQExpBuffer(query, "%s IS ", name);
11068 6541 : appendStringLiteralAH(query, comments->descr, fout);
11069 6541 : appendPQExpBufferStr(query, ";\n");
11070 :
11071 6541 : appendPQExpBuffer(tag, "%s %s", type, name);
11072 :
11073 : /*
11074 : * We mark comments as SECTION_NONE because they really belong in the
11075 : * same section as their parent, whether that is pre-data or
11076 : * post-data.
11077 : */
11078 6541 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11079 6541 : ARCHIVE_OPTS(.tag = tag->data,
11080 : .namespace = namespace,
11081 : .owner = owner,
11082 : .description = "COMMENT",
11083 : .section = SECTION_NONE,
11084 : .createStmt = query->data,
11085 : .deps = &dumpId,
11086 : .nDeps = 1));
11087 :
11088 6541 : destroyPQExpBuffer(query);
11089 6541 : destroyPQExpBuffer(tag);
11090 : }
11091 : }
11092 :
11093 : /*
11094 : * dumpComment --
11095 : *
11096 : * Typical simplification of the above function.
11097 : */
11098 : static inline void
11099 6535 : dumpComment(Archive *fout, const char *type,
11100 : const char *name, const char *namespace,
11101 : const char *owner, CatalogId catalogId,
11102 : int subid, DumpId dumpId)
11103 : {
11104 6535 : dumpCommentExtended(fout, type, name, namespace, owner,
11105 : catalogId, subid, dumpId, NULL);
11106 6535 : }
11107 :
11108 : /*
11109 : * appendNamedArgument --
11110 : *
11111 : * Convenience routine for constructing parameters of the form:
11112 : * 'paraname', 'value'::type
11113 : */
11114 : static void
11115 5646 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
11116 : const char *argtype, const char *argval)
11117 : {
11118 5646 : appendPQExpBufferStr(out, ",\n\t");
11119 :
11120 5646 : appendStringLiteralAH(out, argname, fout);
11121 5646 : appendPQExpBufferStr(out, ", ");
11122 :
11123 5646 : appendStringLiteralAH(out, argval, fout);
11124 5646 : appendPQExpBuffer(out, "::%s", argtype);
11125 5646 : }
11126 :
11127 : /*
11128 : * fetchAttributeStats --
11129 : *
11130 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11131 : */
11132 : static PGresult *
11133 1039 : fetchAttributeStats(Archive *fout)
11134 : {
11135 1039 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11136 1039 : PQExpBuffer relids = createPQExpBuffer();
11137 1039 : PQExpBuffer nspnames = createPQExpBuffer();
11138 1039 : PQExpBuffer relnames = createPQExpBuffer();
11139 1039 : int count = 0;
11140 1039 : PGresult *res = NULL;
11141 : static TocEntry *te;
11142 : static bool restarted;
11143 1039 : int max_rels = MAX_ATTR_STATS_RELS;
11144 :
11145 : /*
11146 : * Our query for retrieving statistics for multiple relations uses WITH
11147 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11148 : * in v9.4. For older versions, we resort to gathering statistics for a
11149 : * single relation at a time.
11150 : */
11151 1039 : if (fout->remoteVersion < 90400)
11152 0 : max_rels = 1;
11153 :
11154 : /* If we're just starting, set our TOC pointer. */
11155 1039 : if (!te)
11156 62 : te = AH->toc->next;
11157 :
11158 : /*
11159 : * We can't easily avoid a second TOC scan for the tar format because it
11160 : * writes restore.sql separately, which means we must execute the queries
11161 : * twice. This feels risky, but there is no known reason it should
11162 : * generate different output than the first pass. Even if it does, the
11163 : * worst-case scenario is that restore.sql might have different statistics
11164 : * data than the archive.
11165 : */
11166 1039 : if (!restarted && te == AH->toc && AH->format == archTar)
11167 : {
11168 1 : te = AH->toc->next;
11169 1 : restarted = true;
11170 : }
11171 :
11172 1039 : appendPQExpBufferChar(relids, '{');
11173 1039 : appendPQExpBufferChar(nspnames, '{');
11174 1039 : appendPQExpBufferChar(relnames, '{');
11175 :
11176 : /*
11177 : * Scan the TOC for the next set of relevant stats entries. We assume
11178 : * that statistics are dumped in the order they are listed in the TOC.
11179 : * This is perhaps not the sturdiest assumption, so we verify it matches
11180 : * reality in dumpRelationStats_dumper().
11181 : */
11182 16322 : for (; te != AH->toc && count < max_rels; te = te->next)
11183 : {
11184 15283 : if ((te->reqs & REQ_STATS) == 0 ||
11185 3431 : strcmp(te->desc, "STATISTICS DATA") != 0)
11186 11887 : continue;
11187 :
11188 3396 : if (fout->remoteVersion >= 190000)
11189 : {
11190 3396 : RelStatsInfo *rsinfo = (RelStatsInfo *) te->defnDumperArg;
11191 : char relid[32];
11192 :
11193 3396 : sprintf(relid, "%u", rsinfo->relid);
11194 3396 : appendPGArray(relids, relid);
11195 : }
11196 : else
11197 : {
11198 0 : appendPGArray(nspnames, te->namespace);
11199 0 : appendPGArray(relnames, te->tag);
11200 : }
11201 :
11202 3396 : count++;
11203 : }
11204 :
11205 1039 : appendPQExpBufferChar(relids, '}');
11206 1039 : appendPQExpBufferChar(nspnames, '}');
11207 1039 : appendPQExpBufferChar(relnames, '}');
11208 :
11209 : /* Execute the query for the next batch of relations. */
11210 1039 : if (count > 0)
11211 : {
11212 107 : PQExpBuffer query = createPQExpBuffer();
11213 :
11214 107 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11215 :
11216 107 : if (fout->remoteVersion >= 190000)
11217 : {
11218 107 : appendStringLiteralAH(query, relids->data, fout);
11219 107 : appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11220 : }
11221 : else
11222 : {
11223 0 : appendStringLiteralAH(query, nspnames->data, fout);
11224 0 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11225 0 : appendStringLiteralAH(query, relnames->data, fout);
11226 0 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11227 : }
11228 :
11229 107 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11230 107 : destroyPQExpBuffer(query);
11231 : }
11232 :
11233 1039 : destroyPQExpBuffer(relids);
11234 1039 : destroyPQExpBuffer(nspnames);
11235 1039 : destroyPQExpBuffer(relnames);
11236 1039 : return res;
11237 : }
11238 :
11239 : /*
11240 : * dumpRelationStats_dumper --
11241 : *
11242 : * Generate command to import stats into the relation on the new database.
11243 : * This routine is called by the Archiver when it wants the statistics to be
11244 : * dumped.
11245 : */
11246 : static char *
11247 3396 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11248 : {
11249 3396 : const RelStatsInfo *rsinfo = userArg;
11250 : static PGresult *res;
11251 : static int rownum;
11252 : PQExpBuffer query;
11253 : PQExpBufferData out_data;
11254 3396 : PQExpBuffer out = &out_data;
11255 : int i_schemaname;
11256 : int i_tablename;
11257 : int i_attname;
11258 : int i_inherited;
11259 : int i_null_frac;
11260 : int i_avg_width;
11261 : int i_n_distinct;
11262 : int i_most_common_vals;
11263 : int i_most_common_freqs;
11264 : int i_histogram_bounds;
11265 : int i_correlation;
11266 : int i_most_common_elems;
11267 : int i_most_common_elem_freqs;
11268 : int i_elem_count_histogram;
11269 : int i_range_length_histogram;
11270 : int i_range_empty_frac;
11271 : int i_range_bounds_histogram;
11272 : static TocEntry *expected_te;
11273 :
11274 : /*
11275 : * fetchAttributeStats() assumes that the statistics are dumped in the
11276 : * order they are listed in the TOC. We verify that here for safety.
11277 : */
11278 3396 : if (!expected_te)
11279 62 : expected_te = ((ArchiveHandle *) fout)->toc;
11280 :
11281 3396 : expected_te = expected_te->next;
11282 13350 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11283 3397 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11284 9954 : expected_te = expected_te->next;
11285 :
11286 3396 : if (te != expected_te)
11287 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11288 : te->dumpId, te->desc, te->tag,
11289 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11290 :
11291 3396 : query = createPQExpBuffer();
11292 3396 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11293 : {
11294 62 : if (fout->remoteVersion >= 190000)
11295 62 : appendPQExpBufferStr(query,
11296 : "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11297 : else
11298 0 : appendPQExpBufferStr(query,
11299 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11300 :
11301 62 : appendPQExpBufferStr(query,
11302 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11303 : "s.null_frac, s.avg_width, s.n_distinct, "
11304 : "s.most_common_vals, s.most_common_freqs, "
11305 : "s.histogram_bounds, s.correlation, "
11306 : "s.most_common_elems, s.most_common_elem_freqs, "
11307 : "s.elem_count_histogram, ");
11308 :
11309 62 : if (fout->remoteVersion >= 170000)
11310 62 : appendPQExpBufferStr(query,
11311 : "s.range_length_histogram, "
11312 : "s.range_empty_frac, "
11313 : "s.range_bounds_histogram ");
11314 : else
11315 0 : appendPQExpBufferStr(query,
11316 : "NULL AS range_length_histogram,"
11317 : "NULL AS range_empty_frac,"
11318 : "NULL AS range_bounds_histogram ");
11319 :
11320 : /*
11321 : * The results must be in the order of the relations supplied in the
11322 : * parameters to ensure we remain in sync as we walk through the TOC.
11323 : *
11324 : * For v9.4 through v18, the redundant filter clause on s.tablename =
11325 : * ANY(...) seems sufficient to convince the planner to use
11326 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11327 : * In newer versions, pg_stats returns the table OIDs, eliminating the
11328 : * need for that hack.
11329 : *
11330 : * Our query for retrieving statistics for multiple relations uses
11331 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11332 : * introduced in v9.4. For older versions, we resort to gathering
11333 : * statistics for a single relation at a time.
11334 : */
11335 62 : if (fout->remoteVersion >= 190000)
11336 62 : appendPQExpBufferStr(query,
11337 : "FROM pg_catalog.pg_stats s "
11338 : "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11339 : "ON s.tableid = u.tableid "
11340 : "ORDER BY u.ord, s.attname, s.inherited");
11341 0 : else if (fout->remoteVersion >= 90400)
11342 0 : appendPQExpBufferStr(query,
11343 : "FROM pg_catalog.pg_stats s "
11344 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11345 : "ON s.schemaname = u.schemaname "
11346 : "AND s.tablename = u.tablename "
11347 : "WHERE s.tablename = ANY($2) "
11348 : "ORDER BY u.ord, s.attname, s.inherited");
11349 : else
11350 0 : appendPQExpBufferStr(query,
11351 : "FROM pg_catalog.pg_stats s "
11352 : "WHERE s.schemaname = $1[1] "
11353 : "AND s.tablename = $2[1] "
11354 : "ORDER BY s.attname, s.inherited");
11355 :
11356 62 : ExecuteSqlStatement(fout, query->data);
11357 :
11358 62 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11359 62 : resetPQExpBuffer(query);
11360 : }
11361 :
11362 3396 : initPQExpBuffer(out);
11363 :
11364 : /* restore relation stats */
11365 3396 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11366 3396 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11367 : fout->remoteVersion);
11368 3396 : appendPQExpBufferStr(out, "\t'schemaname', ");
11369 3396 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11370 3396 : appendPQExpBufferStr(out, ",\n");
11371 3396 : appendPQExpBufferStr(out, "\t'relname', ");
11372 3396 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11373 3396 : appendPQExpBufferStr(out, ",\n");
11374 3396 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11375 :
11376 : /*
11377 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11378 : * the relation is empty, or it could mean that it hadn't yet been
11379 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11380 : * This ambiguity allegedly can cause the planner to choose inefficient
11381 : * plans after restoring to v18 or newer. To deal with this, let's just
11382 : * set reltuples to -1 in that case.
11383 : */
11384 3396 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11385 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11386 : else
11387 3396 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11388 :
11389 3396 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11390 3396 : rsinfo->relallvisible);
11391 :
11392 3396 : if (fout->remoteVersion >= 180000)
11393 3396 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11394 :
11395 3396 : appendPQExpBufferStr(out, "\n);\n");
11396 :
11397 : /* Fetch the next batch of attribute statistics if needed. */
11398 3396 : if (rownum >= PQntuples(res))
11399 : {
11400 1039 : PQclear(res);
11401 1039 : res = fetchAttributeStats(fout);
11402 1039 : rownum = 0;
11403 : }
11404 :
11405 3396 : i_schemaname = PQfnumber(res, "schemaname");
11406 3396 : i_tablename = PQfnumber(res, "tablename");
11407 3396 : i_attname = PQfnumber(res, "attname");
11408 3396 : i_inherited = PQfnumber(res, "inherited");
11409 3396 : i_null_frac = PQfnumber(res, "null_frac");
11410 3396 : i_avg_width = PQfnumber(res, "avg_width");
11411 3396 : i_n_distinct = PQfnumber(res, "n_distinct");
11412 3396 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11413 3396 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11414 3396 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11415 3396 : i_correlation = PQfnumber(res, "correlation");
11416 3396 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11417 3396 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11418 3396 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11419 3396 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11420 3396 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11421 3396 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11422 :
11423 : /* restore attribute stats */
11424 4213 : for (; rownum < PQntuples(res); rownum++)
11425 : {
11426 : const char *attname;
11427 :
11428 : /* Stop if the next stat row in our cache isn't for this relation. */
11429 3174 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11430 817 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11431 : break;
11432 :
11433 817 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11434 817 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11435 : fout->remoteVersion);
11436 817 : appendPQExpBufferStr(out, "\t'schemaname', ");
11437 817 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11438 817 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11439 817 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11440 :
11441 817 : if (PQgetisnull(res, rownum, i_attname))
11442 0 : pg_fatal("unexpected null attname");
11443 817 : attname = PQgetvalue(res, rownum, i_attname);
11444 :
11445 : /*
11446 : * Indexes look up attname in indAttNames to derive attnum, all others
11447 : * use attname directly. We must specify attnum for indexes, since
11448 : * their attnames are not necessarily stable across dump/reload.
11449 : */
11450 817 : if (rsinfo->nindAttNames == 0)
11451 : {
11452 782 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11453 782 : appendStringLiteralAH(out, attname, fout);
11454 : }
11455 : else
11456 : {
11457 35 : bool found = false;
11458 :
11459 66 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11460 : {
11461 66 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11462 : {
11463 35 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11464 : i + 1);
11465 35 : found = true;
11466 35 : break;
11467 : }
11468 : }
11469 :
11470 35 : if (!found)
11471 0 : pg_fatal("could not find index attname \"%s\"", attname);
11472 : }
11473 :
11474 817 : if (!PQgetisnull(res, rownum, i_inherited))
11475 817 : appendNamedArgument(out, fout, "inherited", "boolean",
11476 817 : PQgetvalue(res, rownum, i_inherited));
11477 817 : if (!PQgetisnull(res, rownum, i_null_frac))
11478 817 : appendNamedArgument(out, fout, "null_frac", "real",
11479 817 : PQgetvalue(res, rownum, i_null_frac));
11480 817 : if (!PQgetisnull(res, rownum, i_avg_width))
11481 817 : appendNamedArgument(out, fout, "avg_width", "integer",
11482 817 : PQgetvalue(res, rownum, i_avg_width));
11483 817 : if (!PQgetisnull(res, rownum, i_n_distinct))
11484 817 : appendNamedArgument(out, fout, "n_distinct", "real",
11485 817 : PQgetvalue(res, rownum, i_n_distinct));
11486 817 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11487 405 : appendNamedArgument(out, fout, "most_common_vals", "text",
11488 405 : PQgetvalue(res, rownum, i_most_common_vals));
11489 817 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11490 405 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11491 405 : PQgetvalue(res, rownum, i_most_common_freqs));
11492 817 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11493 513 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11494 513 : PQgetvalue(res, rownum, i_histogram_bounds));
11495 817 : if (!PQgetisnull(res, rownum, i_correlation))
11496 784 : appendNamedArgument(out, fout, "correlation", "real",
11497 784 : PQgetvalue(res, rownum, i_correlation));
11498 817 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11499 8 : appendNamedArgument(out, fout, "most_common_elems", "text",
11500 8 : PQgetvalue(res, rownum, i_most_common_elems));
11501 817 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11502 8 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11503 8 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11504 817 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11505 7 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11506 7 : PQgetvalue(res, rownum, i_elem_count_histogram));
11507 817 : if (fout->remoteVersion >= 170000)
11508 : {
11509 817 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11510 3 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11511 3 : PQgetvalue(res, rownum, i_range_length_histogram));
11512 817 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11513 3 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11514 3 : PQgetvalue(res, rownum, i_range_empty_frac));
11515 817 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11516 3 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11517 3 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11518 : }
11519 817 : appendPQExpBufferStr(out, "\n);\n");
11520 : }
11521 :
11522 3396 : destroyPQExpBuffer(query);
11523 3396 : return out->data;
11524 : }
11525 :
11526 : /*
11527 : * dumpRelationStats --
11528 : *
11529 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11530 : * care of gathering the statistics and generating the restore commands when
11531 : * they are needed.
11532 : */
11533 : static void
11534 3468 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11535 : {
11536 3468 : const DumpableObject *dobj = &rsinfo->dobj;
11537 :
11538 : /* nothing to do if we are not dumping statistics */
11539 3468 : if (!fout->dopt->dumpStatistics)
11540 0 : return;
11541 :
11542 3468 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11543 3468 : ARCHIVE_OPTS(.tag = dobj->name,
11544 : .namespace = dobj->namespace->dobj.name,
11545 : .description = "STATISTICS DATA",
11546 : .section = rsinfo->section,
11547 : .defnFn = dumpRelationStats_dumper,
11548 : .defnArg = rsinfo,
11549 : .deps = dobj->dependencies,
11550 : .nDeps = dobj->nDeps));
11551 : }
11552 :
11553 : /*
11554 : * dumpTableComment --
11555 : *
11556 : * As above, but dump comments for both the specified table (or view)
11557 : * and its columns.
11558 : */
11559 : static void
11560 74 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11561 : const char *reltypename)
11562 : {
11563 74 : DumpOptions *dopt = fout->dopt;
11564 : CommentItem *comments;
11565 : int ncomments;
11566 : PQExpBuffer query;
11567 : PQExpBuffer tag;
11568 :
11569 : /* do nothing, if --no-comments is supplied */
11570 74 : if (dopt->no_comments)
11571 0 : return;
11572 :
11573 : /* Comments are SCHEMA not data */
11574 74 : if (!dopt->dumpSchema)
11575 0 : return;
11576 :
11577 : /* Search for comments associated with relation, using table */
11578 74 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11579 74 : tbinfo->dobj.catId.oid,
11580 : &comments);
11581 :
11582 : /* If comments exist, build COMMENT ON statements */
11583 74 : if (ncomments <= 0)
11584 0 : return;
11585 :
11586 74 : query = createPQExpBuffer();
11587 74 : tag = createPQExpBuffer();
11588 :
11589 212 : while (ncomments > 0)
11590 : {
11591 138 : const char *descr = comments->descr;
11592 138 : int objsubid = comments->objsubid;
11593 :
11594 138 : if (objsubid == 0)
11595 : {
11596 32 : resetPQExpBuffer(tag);
11597 32 : appendPQExpBuffer(tag, "%s %s", reltypename,
11598 32 : fmtId(tbinfo->dobj.name));
11599 :
11600 32 : resetPQExpBuffer(query);
11601 32 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11602 32 : fmtQualifiedDumpable(tbinfo));
11603 32 : appendStringLiteralAH(query, descr, fout);
11604 32 : appendPQExpBufferStr(query, ";\n");
11605 :
11606 32 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11607 32 : ARCHIVE_OPTS(.tag = tag->data,
11608 : .namespace = tbinfo->dobj.namespace->dobj.name,
11609 : .owner = tbinfo->rolname,
11610 : .description = "COMMENT",
11611 : .section = SECTION_NONE,
11612 : .createStmt = query->data,
11613 : .deps = &(tbinfo->dobj.dumpId),
11614 : .nDeps = 1));
11615 : }
11616 106 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11617 : {
11618 106 : resetPQExpBuffer(tag);
11619 106 : appendPQExpBuffer(tag, "COLUMN %s.",
11620 106 : fmtId(tbinfo->dobj.name));
11621 106 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11622 :
11623 106 : resetPQExpBuffer(query);
11624 106 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11625 106 : fmtQualifiedDumpable(tbinfo));
11626 106 : appendPQExpBuffer(query, "%s IS ",
11627 106 : fmtId(tbinfo->attnames[objsubid - 1]));
11628 106 : appendStringLiteralAH(query, descr, fout);
11629 106 : appendPQExpBufferStr(query, ";\n");
11630 :
11631 106 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11632 106 : ARCHIVE_OPTS(.tag = tag->data,
11633 : .namespace = tbinfo->dobj.namespace->dobj.name,
11634 : .owner = tbinfo->rolname,
11635 : .description = "COMMENT",
11636 : .section = SECTION_NONE,
11637 : .createStmt = query->data,
11638 : .deps = &(tbinfo->dobj.dumpId),
11639 : .nDeps = 1));
11640 : }
11641 :
11642 138 : comments++;
11643 138 : ncomments--;
11644 : }
11645 :
11646 74 : destroyPQExpBuffer(query);
11647 74 : destroyPQExpBuffer(tag);
11648 : }
11649 :
11650 : /*
11651 : * findComments --
11652 : *
11653 : * Find the comment(s), if any, associated with the given object. All the
11654 : * objsubid values associated with the given classoid/objoid are found with
11655 : * one search.
11656 : */
11657 : static int
11658 6870 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11659 : {
11660 6870 : CommentItem *middle = NULL;
11661 : CommentItem *low;
11662 : CommentItem *high;
11663 : int nmatch;
11664 :
11665 : /*
11666 : * Do binary search to find some item matching the object.
11667 : */
11668 6870 : low = &comments[0];
11669 6870 : high = &comments[ncomments - 1];
11670 67409 : while (low <= high)
11671 : {
11672 67364 : middle = low + (high - low) / 2;
11673 :
11674 67364 : if (classoid < middle->classoid)
11675 6853 : high = middle - 1;
11676 60511 : else if (classoid > middle->classoid)
11677 7216 : low = middle + 1;
11678 53295 : else if (objoid < middle->objoid)
11679 23236 : high = middle - 1;
11680 30059 : else if (objoid > middle->objoid)
11681 23234 : low = middle + 1;
11682 : else
11683 6825 : break; /* found a match */
11684 : }
11685 :
11686 6870 : if (low > high) /* no matches */
11687 : {
11688 45 : *items = NULL;
11689 45 : return 0;
11690 : }
11691 :
11692 : /*
11693 : * Now determine how many items match the object. The search loop
11694 : * invariant still holds: only items between low and high inclusive could
11695 : * match.
11696 : */
11697 6825 : nmatch = 1;
11698 6825 : while (middle > low)
11699 : {
11700 3477 : if (classoid != middle[-1].classoid ||
11701 3373 : objoid != middle[-1].objoid)
11702 : break;
11703 0 : middle--;
11704 0 : nmatch++;
11705 : }
11706 :
11707 6825 : *items = middle;
11708 :
11709 6825 : middle += nmatch;
11710 6889 : while (middle <= high)
11711 : {
11712 3673 : if (classoid != middle->classoid ||
11713 3159 : objoid != middle->objoid)
11714 : break;
11715 64 : middle++;
11716 64 : nmatch++;
11717 : }
11718 :
11719 6825 : return nmatch;
11720 : }
11721 :
11722 : /*
11723 : * collectComments --
11724 : *
11725 : * Construct a table of all comments available for database objects;
11726 : * also set the has-comment component flag for each relevant object.
11727 : *
11728 : * We used to do per-object queries for the comments, but it's much faster
11729 : * to pull them all over at once, and on most databases the memory cost
11730 : * isn't high.
11731 : *
11732 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11733 : */
11734 : static void
11735 259 : collectComments(Archive *fout)
11736 : {
11737 : PGresult *res;
11738 : PQExpBuffer query;
11739 : int i_description;
11740 : int i_classoid;
11741 : int i_objoid;
11742 : int i_objsubid;
11743 : int ntups;
11744 : int i;
11745 : DumpableObject *dobj;
11746 :
11747 259 : query = createPQExpBuffer();
11748 :
11749 259 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11750 : "FROM pg_catalog.pg_description "
11751 : "ORDER BY classoid, objoid, objsubid");
11752 :
11753 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11754 :
11755 : /* Construct lookup table containing OIDs in numeric form */
11756 :
11757 259 : i_description = PQfnumber(res, "description");
11758 259 : i_classoid = PQfnumber(res, "classoid");
11759 259 : i_objoid = PQfnumber(res, "objoid");
11760 259 : i_objsubid = PQfnumber(res, "objsubid");
11761 :
11762 259 : ntups = PQntuples(res);
11763 :
11764 259 : comments = pg_malloc_array(CommentItem, ntups);
11765 259 : ncomments = 0;
11766 259 : dobj = NULL;
11767 :
11768 1407078 : for (i = 0; i < ntups; i++)
11769 : {
11770 : CatalogId objId;
11771 : int subid;
11772 :
11773 1406819 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11774 1406819 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11775 1406819 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11776 :
11777 : /* We needn't remember comments that don't match any dumpable object */
11778 1406819 : if (dobj == NULL ||
11779 514923 : dobj->catId.tableoid != objId.tableoid ||
11780 511837 : dobj->catId.oid != objId.oid)
11781 1406729 : dobj = findObjectByCatalogId(objId);
11782 1406819 : if (dobj == NULL)
11783 891643 : continue;
11784 :
11785 : /*
11786 : * Comments on columns of composite types are linked to the type's
11787 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11788 : * in the type's own DumpableObject.
11789 : */
11790 515176 : if (subid != 0 && dobj->objType == DO_TABLE &&
11791 194 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11792 45 : {
11793 : TypeInfo *cTypeInfo;
11794 :
11795 45 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11796 45 : if (cTypeInfo)
11797 45 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11798 : }
11799 : else
11800 515131 : dobj->components |= DUMP_COMPONENT_COMMENT;
11801 :
11802 515176 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11803 515176 : comments[ncomments].classoid = objId.tableoid;
11804 515176 : comments[ncomments].objoid = objId.oid;
11805 515176 : comments[ncomments].objsubid = subid;
11806 515176 : ncomments++;
11807 : }
11808 :
11809 259 : PQclear(res);
11810 259 : destroyPQExpBuffer(query);
11811 259 : }
11812 :
11813 : /*
11814 : * dumpDumpableObject
11815 : *
11816 : * This routine and its subsidiaries are responsible for creating
11817 : * ArchiveEntries (TOC objects) for each object to be dumped.
11818 : */
11819 : static void
11820 980202 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11821 : {
11822 : /*
11823 : * Clear any dump-request bits for components that don't exist for this
11824 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11825 : * request for every kind of object.)
11826 : */
11827 980202 : dobj->dump &= dobj->components;
11828 :
11829 : /* Now, short-circuit if there's nothing to be done here. */
11830 980202 : if (dobj->dump == 0)
11831 887962 : return;
11832 :
11833 92240 : switch (dobj->objType)
11834 : {
11835 660 : case DO_NAMESPACE:
11836 660 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11837 660 : break;
11838 25 : case DO_EXTENSION:
11839 25 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11840 25 : break;
11841 923 : case DO_TYPE:
11842 923 : dumpType(fout, (const TypeInfo *) dobj);
11843 923 : break;
11844 73 : case DO_SHELL_TYPE:
11845 73 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11846 73 : break;
11847 1858 : case DO_FUNC:
11848 1858 : dumpFunc(fout, (const FuncInfo *) dobj);
11849 1858 : break;
11850 292 : case DO_AGG:
11851 292 : dumpAgg(fout, (const AggInfo *) dobj);
11852 292 : break;
11853 2522 : case DO_OPERATOR:
11854 2522 : dumpOpr(fout, (const OprInfo *) dobj);
11855 2522 : break;
11856 80 : case DO_ACCESS_METHOD:
11857 80 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11858 80 : break;
11859 666 : case DO_OPCLASS:
11860 666 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11861 666 : break;
11862 555 : case DO_OPFAMILY:
11863 555 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11864 555 : break;
11865 2729 : case DO_COLLATION:
11866 2729 : dumpCollation(fout, (const CollInfo *) dobj);
11867 2729 : break;
11868 422 : case DO_CONVERSION:
11869 422 : dumpConversion(fout, (const ConvInfo *) dobj);
11870 422 : break;
11871 44004 : case DO_TABLE:
11872 44004 : dumpTable(fout, (const TableInfo *) dobj);
11873 44004 : break;
11874 1421 : case DO_TABLE_ATTACH:
11875 1421 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11876 1421 : break;
11877 1047 : case DO_ATTRDEF:
11878 1047 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11879 1047 : break;
11880 2758 : case DO_INDEX:
11881 2758 : dumpIndex(fout, (const IndxInfo *) dobj);
11882 2758 : break;
11883 594 : case DO_INDEX_ATTACH:
11884 594 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11885 594 : break;
11886 171 : case DO_STATSEXT:
11887 171 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11888 171 : dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11889 171 : break;
11890 345 : case DO_REFRESH_MATVIEW:
11891 345 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11892 345 : break;
11893 1150 : case DO_RULE:
11894 1150 : dumpRule(fout, (const RuleInfo *) dobj);
11895 1150 : break;
11896 523 : case DO_TRIGGER:
11897 523 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11898 523 : break;
11899 42 : case DO_EVENT_TRIGGER:
11900 42 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11901 42 : break;
11902 2473 : case DO_CONSTRAINT:
11903 2473 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11904 2473 : break;
11905 231 : case DO_FK_CONSTRAINT:
11906 231 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11907 231 : break;
11908 82 : case DO_PROCLANG:
11909 82 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11910 82 : break;
11911 67 : case DO_CAST:
11912 67 : dumpCast(fout, (const CastInfo *) dobj);
11913 67 : break;
11914 42 : case DO_TRANSFORM:
11915 42 : dumpTransform(fout, (const TransformInfo *) dobj);
11916 42 : break;
11917 402 : case DO_SEQUENCE_SET:
11918 402 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11919 402 : break;
11920 4636 : case DO_TABLE_DATA:
11921 4636 : dumpTableData(fout, (const TableDataInfo *) dobj);
11922 4636 : break;
11923 15370 : case DO_DUMMY_TYPE:
11924 : /* table rowtypes and array types are never dumped separately */
11925 15370 : break;
11926 41 : case DO_TSPARSER:
11927 41 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11928 41 : break;
11929 179 : case DO_TSDICT:
11930 179 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11931 179 : break;
11932 53 : case DO_TSTEMPLATE:
11933 53 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11934 53 : break;
11935 154 : case DO_TSCONFIG:
11936 154 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11937 154 : break;
11938 52 : case DO_FDW:
11939 52 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11940 52 : break;
11941 56 : case DO_FOREIGN_SERVER:
11942 56 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11943 56 : break;
11944 160 : case DO_DEFAULT_ACL:
11945 160 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11946 160 : break;
11947 84 : case DO_LARGE_OBJECT:
11948 84 : dumpLO(fout, (const LoInfo *) dobj);
11949 84 : break;
11950 84 : case DO_LARGE_OBJECT_DATA:
11951 84 : if (dobj->dump & DUMP_COMPONENT_DATA)
11952 : {
11953 : LoInfo *loinfo;
11954 : TocEntry *te;
11955 :
11956 84 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11957 84 : if (loinfo == NULL)
11958 0 : pg_fatal("missing metadata for large objects \"%s\"",
11959 : dobj->name);
11960 :
11961 84 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11962 84 : ARCHIVE_OPTS(.tag = dobj->name,
11963 : .owner = loinfo->rolname,
11964 : .description = "BLOBS",
11965 : .section = SECTION_DATA,
11966 : .deps = dobj->dependencies,
11967 : .nDeps = dobj->nDeps,
11968 : .dumpFn = dumpLOs,
11969 : .dumpArg = loinfo));
11970 :
11971 : /*
11972 : * Set the TocEntry's dataLength in case we are doing a
11973 : * parallel dump and want to order dump jobs by table size.
11974 : * (We need some size estimate for every TocEntry with a
11975 : * DataDumper function.) We don't currently have any cheap
11976 : * way to estimate the size of LOs, but fortunately it doesn't
11977 : * matter too much as long as we get large batches of LOs
11978 : * processed reasonably early. Assume 8K per blob.
11979 : */
11980 84 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11981 : }
11982 84 : break;
11983 336 : case DO_POLICY:
11984 336 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11985 336 : break;
11986 396 : case DO_PUBLICATION:
11987 396 : dumpPublication(fout, (const PublicationInfo *) dobj);
11988 396 : break;
11989 284 : case DO_PUBLICATION_REL:
11990 284 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11991 284 : break;
11992 99 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11993 99 : dumpPublicationNamespace(fout,
11994 : (const PublicationSchemaInfo *) dobj);
11995 99 : break;
11996 110 : case DO_SUBSCRIPTION:
11997 110 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11998 110 : break;
11999 3 : case DO_SUBSCRIPTION_REL:
12000 3 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
12001 3 : break;
12002 3468 : case DO_REL_STATS:
12003 3468 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
12004 3468 : break;
12005 518 : case DO_PRE_DATA_BOUNDARY:
12006 : case DO_POST_DATA_BOUNDARY:
12007 : /* never dumped, nothing to do */
12008 518 : break;
12009 : }
12010 : }
12011 :
12012 : /*
12013 : * dumpNamespace
12014 : * writes out to fout the queries to recreate a user-defined namespace
12015 : */
12016 : static void
12017 660 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
12018 : {
12019 660 : DumpOptions *dopt = fout->dopt;
12020 : PQExpBuffer q;
12021 : PQExpBuffer delq;
12022 : char *qnspname;
12023 :
12024 : /* Do nothing if not dumping schema */
12025 660 : if (!dopt->dumpSchema)
12026 28 : return;
12027 :
12028 632 : q = createPQExpBuffer();
12029 632 : delq = createPQExpBuffer();
12030 :
12031 632 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
12032 :
12033 632 : if (nspinfo->create)
12034 : {
12035 408 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
12036 408 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
12037 : }
12038 : else
12039 : {
12040 : /* see selectDumpableNamespace() */
12041 224 : appendPQExpBufferStr(delq,
12042 : "-- *not* dropping schema, since initdb creates it\n");
12043 224 : appendPQExpBufferStr(q,
12044 : "-- *not* creating schema, since initdb creates it\n");
12045 : }
12046 :
12047 632 : if (dopt->binary_upgrade)
12048 102 : binary_upgrade_extension_member(q, &nspinfo->dobj,
12049 : "SCHEMA", qnspname, NULL);
12050 :
12051 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12052 214 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
12053 214 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
12054 : .owner = nspinfo->rolname,
12055 : .description = "SCHEMA",
12056 : .section = SECTION_PRE_DATA,
12057 : .createStmt = q->data,
12058 : .dropStmt = delq->data));
12059 :
12060 : /* Dump Schema Comments and Security Labels */
12061 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12062 : {
12063 229 : const char *initdb_comment = NULL;
12064 :
12065 229 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
12066 186 : initdb_comment = "standard public schema";
12067 229 : dumpCommentExtended(fout, "SCHEMA", qnspname,
12068 229 : NULL, nspinfo->rolname,
12069 229 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
12070 : initdb_comment);
12071 : }
12072 :
12073 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12074 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
12075 0 : NULL, nspinfo->rolname,
12076 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
12077 :
12078 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
12079 530 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
12080 : qnspname, NULL, NULL,
12081 530 : NULL, nspinfo->rolname, &nspinfo->dacl);
12082 :
12083 632 : free(qnspname);
12084 :
12085 632 : destroyPQExpBuffer(q);
12086 632 : destroyPQExpBuffer(delq);
12087 : }
12088 :
12089 : /*
12090 : * dumpExtension
12091 : * writes out to fout the queries to recreate an extension
12092 : */
12093 : static void
12094 25 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
12095 : {
12096 25 : DumpOptions *dopt = fout->dopt;
12097 : PQExpBuffer q;
12098 : PQExpBuffer delq;
12099 : char *qextname;
12100 :
12101 : /* Do nothing if not dumping schema */
12102 25 : if (!dopt->dumpSchema)
12103 1 : return;
12104 :
12105 24 : q = createPQExpBuffer();
12106 24 : delq = createPQExpBuffer();
12107 :
12108 24 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
12109 :
12110 24 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
12111 :
12112 24 : if (!dopt->binary_upgrade)
12113 : {
12114 : /*
12115 : * In a regular dump, we simply create the extension, intentionally
12116 : * not specifying a version, so that the destination installation's
12117 : * default version is used.
12118 : *
12119 : * Use of IF NOT EXISTS here is unlike our behavior for other object
12120 : * types; but there are various scenarios in which it's convenient to
12121 : * manually create the desired extension before restoring, so we
12122 : * prefer to allow it to exist already.
12123 : */
12124 17 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
12125 17 : qextname, fmtId(extinfo->namespace));
12126 : }
12127 : else
12128 : {
12129 : /*
12130 : * In binary-upgrade mode, it's critical to reproduce the state of the
12131 : * database exactly, so our procedure is to create an empty extension,
12132 : * restore all the contained objects normally, and add them to the
12133 : * extension one by one. This function performs just the first of
12134 : * those steps. binary_upgrade_extension_member() takes care of
12135 : * adding member objects as they're created.
12136 : */
12137 : int i;
12138 : int n;
12139 :
12140 7 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
12141 :
12142 : /*
12143 : * We unconditionally create the extension, so we must drop it if it
12144 : * exists. This could happen if the user deleted 'plpgsql' and then
12145 : * readded it, causing its oid to be greater than g_last_builtin_oid.
12146 : */
12147 7 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
12148 :
12149 7 : appendPQExpBufferStr(q,
12150 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
12151 7 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
12152 7 : appendPQExpBufferStr(q, ", ");
12153 7 : appendStringLiteralAH(q, extinfo->namespace, fout);
12154 7 : appendPQExpBufferStr(q, ", ");
12155 7 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
12156 7 : appendStringLiteralAH(q, extinfo->extversion, fout);
12157 7 : appendPQExpBufferStr(q, ", ");
12158 :
12159 : /*
12160 : * Note that we're pushing extconfig (an OID array) back into
12161 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
12162 : * preserved in binary upgrade.
12163 : */
12164 7 : if (strlen(extinfo->extconfig) > 2)
12165 1 : appendStringLiteralAH(q, extinfo->extconfig, fout);
12166 : else
12167 6 : appendPQExpBufferStr(q, "NULL");
12168 7 : appendPQExpBufferStr(q, ", ");
12169 7 : if (strlen(extinfo->extcondition) > 2)
12170 1 : appendStringLiteralAH(q, extinfo->extcondition, fout);
12171 : else
12172 6 : appendPQExpBufferStr(q, "NULL");
12173 7 : appendPQExpBufferStr(q, ", ");
12174 7 : appendPQExpBufferStr(q, "ARRAY[");
12175 7 : n = 0;
12176 14 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12177 : {
12178 : DumpableObject *extobj;
12179 :
12180 7 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12181 7 : if (extobj && extobj->objType == DO_EXTENSION)
12182 : {
12183 0 : if (n++ > 0)
12184 0 : appendPQExpBufferChar(q, ',');
12185 0 : appendStringLiteralAH(q, extobj->name, fout);
12186 : }
12187 : }
12188 7 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12189 7 : appendPQExpBufferStr(q, ");\n");
12190 : }
12191 :
12192 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12193 24 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12194 24 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12195 : .description = "EXTENSION",
12196 : .section = SECTION_PRE_DATA,
12197 : .createStmt = q->data,
12198 : .dropStmt = delq->data));
12199 :
12200 : /* Dump Extension Comments */
12201 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12202 24 : dumpComment(fout, "EXTENSION", qextname,
12203 : NULL, "",
12204 24 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12205 :
12206 24 : free(qextname);
12207 :
12208 24 : destroyPQExpBuffer(q);
12209 24 : destroyPQExpBuffer(delq);
12210 : }
12211 :
12212 : /*
12213 : * dumpType
12214 : * writes out to fout the queries to recreate a user-defined type
12215 : */
12216 : static void
12217 923 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12218 : {
12219 923 : DumpOptions *dopt = fout->dopt;
12220 :
12221 : /* Do nothing if not dumping schema */
12222 923 : if (!dopt->dumpSchema)
12223 49 : return;
12224 :
12225 : /* Dump out in proper style */
12226 874 : if (tyinfo->typtype == TYPTYPE_BASE)
12227 283 : dumpBaseType(fout, tyinfo);
12228 591 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12229 152 : dumpDomain(fout, tyinfo);
12230 439 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12231 130 : dumpCompositeType(fout, tyinfo);
12232 309 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12233 85 : dumpEnumType(fout, tyinfo);
12234 224 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12235 112 : dumpRangeType(fout, tyinfo);
12236 112 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12237 37 : dumpUndefinedType(fout, tyinfo);
12238 : else
12239 75 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12240 : tyinfo->dobj.name);
12241 : }
12242 :
12243 : /*
12244 : * dumpEnumType
12245 : * writes out to fout the queries to recreate a user-defined enum type
12246 : */
12247 : static void
12248 85 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12249 : {
12250 85 : DumpOptions *dopt = fout->dopt;
12251 85 : PQExpBuffer q = createPQExpBuffer();
12252 85 : PQExpBuffer delq = createPQExpBuffer();
12253 85 : PQExpBuffer query = createPQExpBuffer();
12254 : PGresult *res;
12255 : int num,
12256 : i;
12257 : Oid enum_oid;
12258 : char *qtypname;
12259 : char *qualtypname;
12260 : char *label;
12261 : int i_enumlabel;
12262 : int i_oid;
12263 :
12264 85 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12265 : {
12266 : /* Set up query for enum-specific details */
12267 40 : appendPQExpBufferStr(query,
12268 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12269 : "SELECT oid, enumlabel "
12270 : "FROM pg_catalog.pg_enum "
12271 : "WHERE enumtypid = $1 "
12272 : "ORDER BY enumsortorder");
12273 :
12274 40 : ExecuteSqlStatement(fout, query->data);
12275 :
12276 40 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12277 : }
12278 :
12279 85 : printfPQExpBuffer(query,
12280 : "EXECUTE dumpEnumType('%u')",
12281 85 : tyinfo->dobj.catId.oid);
12282 :
12283 85 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12284 :
12285 85 : num = PQntuples(res);
12286 :
12287 85 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12288 85 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12289 :
12290 : /*
12291 : * CASCADE shouldn't be required here as for normal types since the I/O
12292 : * functions are generic and do not get dropped.
12293 : */
12294 85 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12295 :
12296 85 : if (dopt->binary_upgrade)
12297 6 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12298 6 : tyinfo->dobj.catId.oid,
12299 : false, false);
12300 :
12301 85 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12302 : qualtypname);
12303 :
12304 85 : if (!dopt->binary_upgrade)
12305 : {
12306 79 : i_enumlabel = PQfnumber(res, "enumlabel");
12307 :
12308 : /* Labels with server-assigned oids */
12309 482 : for (i = 0; i < num; i++)
12310 : {
12311 403 : label = PQgetvalue(res, i, i_enumlabel);
12312 403 : if (i > 0)
12313 324 : appendPQExpBufferChar(q, ',');
12314 403 : appendPQExpBufferStr(q, "\n ");
12315 403 : appendStringLiteralAH(q, label, fout);
12316 : }
12317 : }
12318 :
12319 85 : appendPQExpBufferStr(q, "\n);\n");
12320 :
12321 85 : if (dopt->binary_upgrade)
12322 : {
12323 6 : i_oid = PQfnumber(res, "oid");
12324 6 : i_enumlabel = PQfnumber(res, "enumlabel");
12325 :
12326 : /* Labels with dump-assigned (preserved) oids */
12327 62 : for (i = 0; i < num; i++)
12328 : {
12329 56 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12330 56 : label = PQgetvalue(res, i, i_enumlabel);
12331 :
12332 56 : if (i == 0)
12333 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12334 56 : appendPQExpBuffer(q,
12335 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12336 : enum_oid);
12337 56 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12338 56 : appendStringLiteralAH(q, label, fout);
12339 56 : appendPQExpBufferStr(q, ";\n\n");
12340 : }
12341 : }
12342 :
12343 85 : if (dopt->binary_upgrade)
12344 6 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12345 : "TYPE", qtypname,
12346 6 : tyinfo->dobj.namespace->dobj.name);
12347 :
12348 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12349 85 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12350 85 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12351 : .namespace = tyinfo->dobj.namespace->dobj.name,
12352 : .owner = tyinfo->rolname,
12353 : .description = "TYPE",
12354 : .section = SECTION_PRE_DATA,
12355 : .createStmt = q->data,
12356 : .dropStmt = delq->data));
12357 :
12358 : /* Dump Type Comments and Security Labels */
12359 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12360 32 : dumpComment(fout, "TYPE", qtypname,
12361 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12362 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12363 :
12364 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12365 0 : dumpSecLabel(fout, "TYPE", qtypname,
12366 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12367 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12368 :
12369 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12370 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12371 : qtypname, NULL,
12372 32 : tyinfo->dobj.namespace->dobj.name,
12373 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12374 :
12375 85 : PQclear(res);
12376 85 : destroyPQExpBuffer(q);
12377 85 : destroyPQExpBuffer(delq);
12378 85 : destroyPQExpBuffer(query);
12379 85 : free(qtypname);
12380 85 : free(qualtypname);
12381 85 : }
12382 :
12383 : /*
12384 : * dumpRangeType
12385 : * writes out to fout the queries to recreate a user-defined range type
12386 : */
12387 : static void
12388 112 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12389 : {
12390 112 : DumpOptions *dopt = fout->dopt;
12391 112 : PQExpBuffer q = createPQExpBuffer();
12392 112 : PQExpBuffer delq = createPQExpBuffer();
12393 112 : PQExpBuffer query = createPQExpBuffer();
12394 : PGresult *res;
12395 : Oid collationOid;
12396 : char *qtypname;
12397 : char *qualtypname;
12398 : char *procname;
12399 :
12400 112 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12401 : {
12402 : /* Set up query for range-specific details */
12403 40 : appendPQExpBufferStr(query,
12404 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12405 :
12406 40 : appendPQExpBufferStr(query,
12407 : "SELECT ");
12408 :
12409 40 : if (fout->remoteVersion >= 140000)
12410 40 : appendPQExpBufferStr(query,
12411 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12412 : else
12413 0 : appendPQExpBufferStr(query,
12414 : "NULL AS rngmultitype, ");
12415 :
12416 40 : appendPQExpBufferStr(query,
12417 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12418 : "opc.opcname AS opcname, "
12419 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12420 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12421 : "opc.opcdefault, "
12422 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12423 : " ELSE rngcollation END AS collation, "
12424 : "rngcanonical, rngsubdiff "
12425 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12426 : " pg_catalog.pg_opclass opc "
12427 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12428 : "rngtypid = $1");
12429 :
12430 40 : ExecuteSqlStatement(fout, query->data);
12431 :
12432 40 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12433 : }
12434 :
12435 112 : printfPQExpBuffer(query,
12436 : "EXECUTE dumpRangeType('%u')",
12437 112 : tyinfo->dobj.catId.oid);
12438 :
12439 112 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12440 :
12441 112 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12442 112 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12443 :
12444 : /*
12445 : * CASCADE shouldn't be required here as for normal types since the I/O
12446 : * functions are generic and do not get dropped.
12447 : */
12448 112 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12449 :
12450 112 : if (dopt->binary_upgrade)
12451 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12452 8 : tyinfo->dobj.catId.oid,
12453 : false, true);
12454 :
12455 112 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12456 : qualtypname);
12457 :
12458 112 : appendPQExpBuffer(q, "\n subtype = %s",
12459 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12460 :
12461 112 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12462 112 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12463 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12464 :
12465 : /* print subtype_opclass only if not default for subtype */
12466 112 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12467 : {
12468 32 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12469 32 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12470 :
12471 32 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12472 : fmtId(nspname));
12473 32 : appendPQExpBufferStr(q, fmtId(opcname));
12474 : }
12475 :
12476 112 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12477 112 : if (OidIsValid(collationOid))
12478 : {
12479 37 : CollInfo *coll = findCollationByOid(collationOid);
12480 :
12481 37 : if (coll)
12482 37 : appendPQExpBuffer(q, ",\n collation = %s",
12483 37 : fmtQualifiedDumpable(coll));
12484 : }
12485 :
12486 112 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12487 112 : if (strcmp(procname, "-") != 0)
12488 9 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12489 :
12490 112 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12491 112 : if (strcmp(procname, "-") != 0)
12492 23 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12493 :
12494 112 : appendPQExpBufferStr(q, "\n);\n");
12495 :
12496 112 : if (dopt->binary_upgrade)
12497 8 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12498 : "TYPE", qtypname,
12499 8 : tyinfo->dobj.namespace->dobj.name);
12500 :
12501 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12502 112 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12503 112 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12504 : .namespace = tyinfo->dobj.namespace->dobj.name,
12505 : .owner = tyinfo->rolname,
12506 : .description = "TYPE",
12507 : .section = SECTION_PRE_DATA,
12508 : .createStmt = q->data,
12509 : .dropStmt = delq->data));
12510 :
12511 : /* Dump Type Comments and Security Labels */
12512 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12513 50 : dumpComment(fout, "TYPE", qtypname,
12514 50 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12515 50 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12516 :
12517 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12518 0 : dumpSecLabel(fout, "TYPE", qtypname,
12519 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12520 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12521 :
12522 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12523 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12524 : qtypname, NULL,
12525 32 : tyinfo->dobj.namespace->dobj.name,
12526 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12527 :
12528 112 : PQclear(res);
12529 112 : destroyPQExpBuffer(q);
12530 112 : destroyPQExpBuffer(delq);
12531 112 : destroyPQExpBuffer(query);
12532 112 : free(qtypname);
12533 112 : free(qualtypname);
12534 112 : }
12535 :
12536 : /*
12537 : * dumpUndefinedType
12538 : * writes out to fout the queries to recreate a !typisdefined type
12539 : *
12540 : * This is a shell type, but we use different terminology to distinguish
12541 : * this case from where we have to emit a shell type definition to break
12542 : * circular dependencies. An undefined type shouldn't ever have anything
12543 : * depending on it.
12544 : */
12545 : static void
12546 37 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12547 : {
12548 37 : DumpOptions *dopt = fout->dopt;
12549 37 : PQExpBuffer q = createPQExpBuffer();
12550 37 : PQExpBuffer delq = createPQExpBuffer();
12551 : char *qtypname;
12552 : char *qualtypname;
12553 :
12554 37 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12555 37 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12556 :
12557 37 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12558 :
12559 37 : if (dopt->binary_upgrade)
12560 2 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12561 2 : tyinfo->dobj.catId.oid,
12562 : false, false);
12563 :
12564 37 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12565 : qualtypname);
12566 :
12567 37 : if (dopt->binary_upgrade)
12568 2 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12569 : "TYPE", qtypname,
12570 2 : tyinfo->dobj.namespace->dobj.name);
12571 :
12572 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12573 37 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12574 37 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12575 : .namespace = tyinfo->dobj.namespace->dobj.name,
12576 : .owner = tyinfo->rolname,
12577 : .description = "TYPE",
12578 : .section = SECTION_PRE_DATA,
12579 : .createStmt = q->data,
12580 : .dropStmt = delq->data));
12581 :
12582 : /* Dump Type Comments and Security Labels */
12583 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12584 32 : dumpComment(fout, "TYPE", qtypname,
12585 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12586 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12587 :
12588 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12589 0 : dumpSecLabel(fout, "TYPE", qtypname,
12590 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12591 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12592 :
12593 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12594 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12595 : qtypname, NULL,
12596 0 : tyinfo->dobj.namespace->dobj.name,
12597 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12598 :
12599 37 : destroyPQExpBuffer(q);
12600 37 : destroyPQExpBuffer(delq);
12601 37 : free(qtypname);
12602 37 : free(qualtypname);
12603 37 : }
12604 :
12605 : /*
12606 : * dumpBaseType
12607 : * writes out to fout the queries to recreate a user-defined base type
12608 : */
12609 : static void
12610 283 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12611 : {
12612 283 : DumpOptions *dopt = fout->dopt;
12613 283 : PQExpBuffer q = createPQExpBuffer();
12614 283 : PQExpBuffer delq = createPQExpBuffer();
12615 283 : PQExpBuffer query = createPQExpBuffer();
12616 : PGresult *res;
12617 : char *qtypname;
12618 : char *qualtypname;
12619 : char *typlen;
12620 : char *typinput;
12621 : char *typoutput;
12622 : char *typreceive;
12623 : char *typsend;
12624 : char *typmodin;
12625 : char *typmodout;
12626 : char *typanalyze;
12627 : char *typsubscript;
12628 : Oid typreceiveoid;
12629 : Oid typsendoid;
12630 : Oid typmodinoid;
12631 : Oid typmodoutoid;
12632 : Oid typanalyzeoid;
12633 : Oid typsubscriptoid;
12634 : char *typcategory;
12635 : char *typispreferred;
12636 : char *typdelim;
12637 : char *typbyval;
12638 : char *typalign;
12639 : char *typstorage;
12640 : char *typcollatable;
12641 : char *typdefault;
12642 283 : bool typdefault_is_literal = false;
12643 :
12644 283 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12645 : {
12646 : /* Set up query for type-specific details */
12647 40 : appendPQExpBufferStr(query,
12648 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12649 : "SELECT typlen, "
12650 : "typinput, typoutput, typreceive, typsend, "
12651 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12652 : "typsend::pg_catalog.oid AS typsendoid, "
12653 : "typanalyze, "
12654 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12655 : "typdelim, typbyval, typalign, typstorage, "
12656 : "typmodin, typmodout, "
12657 : "typmodin::pg_catalog.oid AS typmodinoid, "
12658 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12659 : "typcategory, typispreferred, "
12660 : "(typcollation <> 0) AS typcollatable, "
12661 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12662 :
12663 40 : if (fout->remoteVersion >= 140000)
12664 40 : appendPQExpBufferStr(query,
12665 : "typsubscript, "
12666 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12667 : else
12668 0 : appendPQExpBufferStr(query,
12669 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12670 :
12671 40 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12672 : "WHERE oid = $1");
12673 :
12674 40 : ExecuteSqlStatement(fout, query->data);
12675 :
12676 40 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12677 : }
12678 :
12679 283 : printfPQExpBuffer(query,
12680 : "EXECUTE dumpBaseType('%u')",
12681 283 : tyinfo->dobj.catId.oid);
12682 :
12683 283 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12684 :
12685 283 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12686 283 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12687 283 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12688 283 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12689 283 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12690 283 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12691 283 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12692 283 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12693 283 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12694 283 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12695 283 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12696 283 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12697 283 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12698 283 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12699 283 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12700 283 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12701 283 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12702 283 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12703 283 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12704 283 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12705 283 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12706 283 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12707 283 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12708 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12709 283 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12710 : {
12711 42 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12712 42 : typdefault_is_literal = true; /* it needs quotes */
12713 : }
12714 : else
12715 241 : typdefault = NULL;
12716 :
12717 283 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12718 283 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12719 :
12720 : /*
12721 : * The reason we include CASCADE is that the circular dependency between
12722 : * the type and its I/O functions makes it impossible to drop the type any
12723 : * other way.
12724 : */
12725 283 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12726 :
12727 : /*
12728 : * We might already have a shell type, but setting pg_type_oid is
12729 : * harmless, and in any case we'd better set the array type OID.
12730 : */
12731 283 : if (dopt->binary_upgrade)
12732 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12733 8 : tyinfo->dobj.catId.oid,
12734 : false, false);
12735 :
12736 283 : appendPQExpBuffer(q,
12737 : "CREATE TYPE %s (\n"
12738 : " INTERNALLENGTH = %s",
12739 : qualtypname,
12740 283 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12741 :
12742 : /* regproc result is sufficiently quoted already */
12743 283 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12744 283 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12745 283 : if (OidIsValid(typreceiveoid))
12746 210 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12747 283 : if (OidIsValid(typsendoid))
12748 210 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12749 283 : if (OidIsValid(typmodinoid))
12750 35 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12751 283 : if (OidIsValid(typmodoutoid))
12752 35 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12753 283 : if (OidIsValid(typanalyzeoid))
12754 3 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12755 :
12756 283 : if (strcmp(typcollatable, "t") == 0)
12757 30 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12758 :
12759 283 : if (typdefault != NULL)
12760 : {
12761 42 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12762 42 : if (typdefault_is_literal)
12763 42 : appendStringLiteralAH(q, typdefault, fout);
12764 : else
12765 0 : appendPQExpBufferStr(q, typdefault);
12766 : }
12767 :
12768 283 : if (OidIsValid(typsubscriptoid))
12769 29 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12770 :
12771 283 : if (OidIsValid(tyinfo->typelem))
12772 26 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12773 26 : getFormattedTypeName(fout, tyinfo->typelem,
12774 : zeroIsError));
12775 :
12776 283 : if (strcmp(typcategory, "U") != 0)
12777 : {
12778 161 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12779 161 : appendStringLiteralAH(q, typcategory, fout);
12780 : }
12781 :
12782 283 : if (strcmp(typispreferred, "t") == 0)
12783 29 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12784 :
12785 283 : if (typdelim && strcmp(typdelim, ",") != 0)
12786 : {
12787 3 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12788 3 : appendStringLiteralAH(q, typdelim, fout);
12789 : }
12790 :
12791 283 : if (*typalign == TYPALIGN_CHAR)
12792 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12793 271 : else if (*typalign == TYPALIGN_SHORT)
12794 6 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12795 265 : else if (*typalign == TYPALIGN_INT)
12796 187 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12797 78 : else if (*typalign == TYPALIGN_DOUBLE)
12798 78 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12799 :
12800 283 : if (*typstorage == TYPSTORAGE_PLAIN)
12801 208 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12802 75 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12803 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12804 75 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12805 66 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12806 9 : else if (*typstorage == TYPSTORAGE_MAIN)
12807 9 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12808 :
12809 283 : if (strcmp(typbyval, "t") == 0)
12810 137 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12811 :
12812 283 : appendPQExpBufferStr(q, "\n);\n");
12813 :
12814 283 : if (dopt->binary_upgrade)
12815 8 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12816 : "TYPE", qtypname,
12817 8 : tyinfo->dobj.namespace->dobj.name);
12818 :
12819 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12820 283 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12821 283 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12822 : .namespace = tyinfo->dobj.namespace->dobj.name,
12823 : .owner = tyinfo->rolname,
12824 : .description = "TYPE",
12825 : .section = SECTION_PRE_DATA,
12826 : .createStmt = q->data,
12827 : .dropStmt = delq->data));
12828 :
12829 : /* Dump Type Comments and Security Labels */
12830 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12831 248 : dumpComment(fout, "TYPE", qtypname,
12832 248 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12833 248 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12834 :
12835 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12836 0 : dumpSecLabel(fout, "TYPE", qtypname,
12837 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12838 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12839 :
12840 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12841 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12842 : qtypname, NULL,
12843 32 : tyinfo->dobj.namespace->dobj.name,
12844 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12845 :
12846 283 : PQclear(res);
12847 283 : destroyPQExpBuffer(q);
12848 283 : destroyPQExpBuffer(delq);
12849 283 : destroyPQExpBuffer(query);
12850 283 : free(qtypname);
12851 283 : free(qualtypname);
12852 283 : }
12853 :
12854 : /*
12855 : * dumpDomain
12856 : * writes out to fout the queries to recreate a user-defined domain
12857 : */
12858 : static void
12859 152 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12860 : {
12861 152 : DumpOptions *dopt = fout->dopt;
12862 152 : PQExpBuffer q = createPQExpBuffer();
12863 152 : PQExpBuffer delq = createPQExpBuffer();
12864 152 : PQExpBuffer query = createPQExpBuffer();
12865 : PGresult *res;
12866 : int i;
12867 : char *qtypname;
12868 : char *qualtypname;
12869 : char *typnotnull;
12870 : char *typdefn;
12871 : char *typdefault;
12872 : Oid typcollation;
12873 152 : bool typdefault_is_literal = false;
12874 :
12875 152 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12876 : {
12877 : /* Set up query for domain-specific details */
12878 37 : appendPQExpBufferStr(query,
12879 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12880 :
12881 37 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12882 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12883 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12884 : "t.typdefault, "
12885 : "CASE WHEN t.typcollation <> u.typcollation "
12886 : "THEN t.typcollation ELSE 0 END AS typcollation "
12887 : "FROM pg_catalog.pg_type t "
12888 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12889 : "WHERE t.oid = $1");
12890 :
12891 37 : ExecuteSqlStatement(fout, query->data);
12892 :
12893 37 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12894 : }
12895 :
12896 152 : printfPQExpBuffer(query,
12897 : "EXECUTE dumpDomain('%u')",
12898 152 : tyinfo->dobj.catId.oid);
12899 :
12900 152 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12901 :
12902 152 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12903 152 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12904 152 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12905 37 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12906 115 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12907 : {
12908 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12909 0 : typdefault_is_literal = true; /* it needs quotes */
12910 : }
12911 : else
12912 115 : typdefault = NULL;
12913 152 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12914 :
12915 152 : if (dopt->binary_upgrade)
12916 25 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12917 25 : tyinfo->dobj.catId.oid,
12918 : true, /* force array type */
12919 : false); /* force multirange type */
12920 :
12921 152 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12922 152 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12923 :
12924 152 : appendPQExpBuffer(q,
12925 : "CREATE DOMAIN %s AS %s",
12926 : qualtypname,
12927 : typdefn);
12928 :
12929 : /* Print collation only if different from base type's collation */
12930 152 : if (OidIsValid(typcollation))
12931 : {
12932 : CollInfo *coll;
12933 :
12934 32 : coll = findCollationByOid(typcollation);
12935 32 : if (coll)
12936 32 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12937 : }
12938 :
12939 : /*
12940 : * Print a not-null constraint if there's one. In servers older than 17
12941 : * these don't have names, so just print it unadorned; in newer ones they
12942 : * do, but most of the time it's going to be the standard generated one,
12943 : * so omit the name in that case also.
12944 : */
12945 152 : if (typnotnull[0] == 't')
12946 : {
12947 47 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12948 0 : appendPQExpBufferStr(q, " NOT NULL");
12949 : else
12950 : {
12951 47 : ConstraintInfo *notnull = tyinfo->notnull;
12952 :
12953 47 : if (!notnull->separate)
12954 : {
12955 : char *default_name;
12956 :
12957 : /* XXX should match ChooseConstraintName better */
12958 47 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12959 :
12960 47 : if (strcmp(default_name, notnull->dobj.name) == 0)
12961 15 : appendPQExpBufferStr(q, " NOT NULL");
12962 : else
12963 32 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12964 32 : fmtId(notnull->dobj.name), notnull->condef);
12965 47 : free(default_name);
12966 : }
12967 : }
12968 : }
12969 :
12970 152 : if (typdefault != NULL)
12971 : {
12972 37 : appendPQExpBufferStr(q, " DEFAULT ");
12973 37 : if (typdefault_is_literal)
12974 0 : appendStringLiteralAH(q, typdefault, fout);
12975 : else
12976 37 : appendPQExpBufferStr(q, typdefault);
12977 : }
12978 :
12979 152 : PQclear(res);
12980 :
12981 : /*
12982 : * Add any CHECK constraints for the domain
12983 : */
12984 259 : for (i = 0; i < tyinfo->nDomChecks; i++)
12985 : {
12986 107 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12987 :
12988 107 : if (!domcheck->separate && domcheck->contype == 'c')
12989 102 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12990 102 : fmtId(domcheck->dobj.name), domcheck->condef);
12991 : }
12992 :
12993 152 : appendPQExpBufferStr(q, ";\n");
12994 :
12995 152 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12996 :
12997 152 : if (dopt->binary_upgrade)
12998 25 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12999 : "DOMAIN", qtypname,
13000 25 : tyinfo->dobj.namespace->dobj.name);
13001 :
13002 152 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13003 152 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13004 152 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13005 : .namespace = tyinfo->dobj.namespace->dobj.name,
13006 : .owner = tyinfo->rolname,
13007 : .description = "DOMAIN",
13008 : .section = SECTION_PRE_DATA,
13009 : .createStmt = q->data,
13010 : .dropStmt = delq->data));
13011 :
13012 : /* Dump Domain Comments and Security Labels */
13013 152 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13014 0 : dumpComment(fout, "DOMAIN", qtypname,
13015 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13016 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13017 :
13018 152 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13019 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
13020 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13021 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13022 :
13023 152 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13024 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13025 : qtypname, NULL,
13026 32 : tyinfo->dobj.namespace->dobj.name,
13027 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
13028 :
13029 : /* Dump any per-constraint comments */
13030 259 : for (i = 0; i < tyinfo->nDomChecks; i++)
13031 : {
13032 107 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
13033 : PQExpBuffer conprefix;
13034 :
13035 : /* but only if the constraint itself was dumped here */
13036 107 : if (domcheck->separate)
13037 5 : continue;
13038 :
13039 102 : conprefix = createPQExpBuffer();
13040 102 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13041 102 : fmtId(domcheck->dobj.name));
13042 :
13043 102 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
13044 32 : dumpComment(fout, conprefix->data, qtypname,
13045 32 : tyinfo->dobj.namespace->dobj.name,
13046 32 : tyinfo->rolname,
13047 32 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
13048 :
13049 102 : destroyPQExpBuffer(conprefix);
13050 : }
13051 :
13052 : /*
13053 : * And a comment on the not-null constraint, if there's one -- but only if
13054 : * the constraint itself was dumped here
13055 : */
13056 152 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
13057 : {
13058 47 : PQExpBuffer conprefix = createPQExpBuffer();
13059 :
13060 47 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13061 47 : fmtId(tyinfo->notnull->dobj.name));
13062 :
13063 47 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
13064 32 : dumpComment(fout, conprefix->data, qtypname,
13065 32 : tyinfo->dobj.namespace->dobj.name,
13066 32 : tyinfo->rolname,
13067 32 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
13068 47 : destroyPQExpBuffer(conprefix);
13069 : }
13070 :
13071 152 : destroyPQExpBuffer(q);
13072 152 : destroyPQExpBuffer(delq);
13073 152 : destroyPQExpBuffer(query);
13074 152 : free(qtypname);
13075 152 : free(qualtypname);
13076 152 : }
13077 :
13078 : /*
13079 : * dumpCompositeType
13080 : * writes out to fout the queries to recreate a user-defined stand-alone
13081 : * composite type
13082 : */
13083 : static void
13084 130 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
13085 : {
13086 130 : DumpOptions *dopt = fout->dopt;
13087 130 : PQExpBuffer q = createPQExpBuffer();
13088 130 : PQExpBuffer dropped = createPQExpBuffer();
13089 130 : PQExpBuffer delq = createPQExpBuffer();
13090 130 : PQExpBuffer query = createPQExpBuffer();
13091 : PGresult *res;
13092 : char *qtypname;
13093 : char *qualtypname;
13094 : int ntups;
13095 : int i_attname;
13096 : int i_atttypdefn;
13097 : int i_attlen;
13098 : int i_attalign;
13099 : int i_attisdropped;
13100 : int i_attcollation;
13101 : int i;
13102 : int actual_atts;
13103 :
13104 130 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
13105 : {
13106 : /*
13107 : * Set up query for type-specific details.
13108 : *
13109 : * Since we only want to dump COLLATE clauses for attributes whose
13110 : * collation is different from their type's default, we use a CASE
13111 : * here to suppress uninteresting attcollations cheaply. atttypid
13112 : * will be 0 for dropped columns; collation does not matter for those.
13113 : */
13114 55 : appendPQExpBufferStr(query,
13115 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
13116 : "SELECT a.attname, a.attnum, "
13117 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
13118 : "a.attlen, a.attalign, a.attisdropped, "
13119 : "CASE WHEN a.attcollation <> at.typcollation "
13120 : "THEN a.attcollation ELSE 0 END AS attcollation "
13121 : "FROM pg_catalog.pg_type ct "
13122 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
13123 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
13124 : "WHERE ct.oid = $1 "
13125 : "ORDER BY a.attnum");
13126 :
13127 55 : ExecuteSqlStatement(fout, query->data);
13128 :
13129 55 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
13130 : }
13131 :
13132 130 : printfPQExpBuffer(query,
13133 : "EXECUTE dumpCompositeType('%u')",
13134 130 : tyinfo->dobj.catId.oid);
13135 :
13136 130 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13137 :
13138 130 : ntups = PQntuples(res);
13139 :
13140 130 : i_attname = PQfnumber(res, "attname");
13141 130 : i_atttypdefn = PQfnumber(res, "atttypdefn");
13142 130 : i_attlen = PQfnumber(res, "attlen");
13143 130 : i_attalign = PQfnumber(res, "attalign");
13144 130 : i_attisdropped = PQfnumber(res, "attisdropped");
13145 130 : i_attcollation = PQfnumber(res, "attcollation");
13146 :
13147 130 : if (dopt->binary_upgrade)
13148 : {
13149 18 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13150 18 : tyinfo->dobj.catId.oid,
13151 : false, false);
13152 18 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
13153 : }
13154 :
13155 130 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
13156 130 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
13157 :
13158 130 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13159 : qualtypname);
13160 :
13161 130 : actual_atts = 0;
13162 412 : for (i = 0; i < ntups; i++)
13163 : {
13164 : char *attname;
13165 : char *atttypdefn;
13166 : char *attlen;
13167 : char *attalign;
13168 : bool attisdropped;
13169 : Oid attcollation;
13170 :
13171 282 : attname = PQgetvalue(res, i, i_attname);
13172 282 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13173 282 : attlen = PQgetvalue(res, i, i_attlen);
13174 282 : attalign = PQgetvalue(res, i, i_attalign);
13175 282 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13176 282 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13177 :
13178 282 : if (attisdropped && !dopt->binary_upgrade)
13179 8 : continue;
13180 :
13181 : /* Format properly if not first attr */
13182 274 : if (actual_atts++ > 0)
13183 144 : appendPQExpBufferChar(q, ',');
13184 274 : appendPQExpBufferStr(q, "\n\t");
13185 :
13186 274 : if (!attisdropped)
13187 : {
13188 272 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13189 :
13190 : /* Add collation if not default for the column type */
13191 272 : if (OidIsValid(attcollation))
13192 : {
13193 : CollInfo *coll;
13194 :
13195 0 : coll = findCollationByOid(attcollation);
13196 0 : if (coll)
13197 0 : appendPQExpBuffer(q, " COLLATE %s",
13198 0 : fmtQualifiedDumpable(coll));
13199 : }
13200 : }
13201 : else
13202 : {
13203 : /*
13204 : * This is a dropped attribute and we're in binary_upgrade mode.
13205 : * Insert a placeholder for it in the CREATE TYPE command, and set
13206 : * length and alignment with direct UPDATE to the catalogs
13207 : * afterwards. See similar code in dumpTableSchema().
13208 : */
13209 2 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13210 :
13211 : /* stash separately for insertion after the CREATE TYPE */
13212 2 : appendPQExpBufferStr(dropped,
13213 : "\n-- For binary upgrade, recreate dropped column.\n");
13214 2 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13215 : "SET attlen = %s, "
13216 : "attalign = '%s', attbyval = false\n"
13217 : "WHERE attname = ", attlen, attalign);
13218 2 : appendStringLiteralAH(dropped, attname, fout);
13219 2 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13220 2 : appendStringLiteralAH(dropped, qualtypname, fout);
13221 2 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13222 :
13223 2 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13224 : qualtypname);
13225 2 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13226 : fmtId(attname));
13227 : }
13228 : }
13229 130 : appendPQExpBufferStr(q, "\n);\n");
13230 130 : appendPQExpBufferStr(q, dropped->data);
13231 :
13232 130 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13233 :
13234 130 : if (dopt->binary_upgrade)
13235 18 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13236 : "TYPE", qtypname,
13237 18 : tyinfo->dobj.namespace->dobj.name);
13238 :
13239 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13240 113 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13241 113 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13242 : .namespace = tyinfo->dobj.namespace->dobj.name,
13243 : .owner = tyinfo->rolname,
13244 : .description = "TYPE",
13245 : .section = SECTION_PRE_DATA,
13246 : .createStmt = q->data,
13247 : .dropStmt = delq->data));
13248 :
13249 :
13250 : /* Dump Type Comments and Security Labels */
13251 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13252 32 : dumpComment(fout, "TYPE", qtypname,
13253 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13254 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13255 :
13256 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13257 0 : dumpSecLabel(fout, "TYPE", qtypname,
13258 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13259 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13260 :
13261 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13262 18 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13263 : qtypname, NULL,
13264 18 : tyinfo->dobj.namespace->dobj.name,
13265 18 : NULL, tyinfo->rolname, &tyinfo->dacl);
13266 :
13267 : /* Dump any per-column comments */
13268 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13269 32 : dumpCompositeTypeColComments(fout, tyinfo, res);
13270 :
13271 130 : PQclear(res);
13272 130 : destroyPQExpBuffer(q);
13273 130 : destroyPQExpBuffer(dropped);
13274 130 : destroyPQExpBuffer(delq);
13275 130 : destroyPQExpBuffer(query);
13276 130 : free(qtypname);
13277 130 : free(qualtypname);
13278 130 : }
13279 :
13280 : /*
13281 : * dumpCompositeTypeColComments
13282 : * writes out to fout the queries to recreate comments on the columns of
13283 : * a user-defined stand-alone composite type.
13284 : *
13285 : * The caller has already made a query to collect the names and attnums
13286 : * of the type's columns, so we just pass that result into here rather
13287 : * than reading them again.
13288 : */
13289 : static void
13290 32 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13291 : PGresult *res)
13292 : {
13293 : CommentItem *comments;
13294 : int ncomments;
13295 : PQExpBuffer query;
13296 : PQExpBuffer target;
13297 : int i;
13298 : int ntups;
13299 : int i_attname;
13300 : int i_attnum;
13301 : int i_attisdropped;
13302 :
13303 : /* do nothing, if --no-comments is supplied */
13304 32 : if (fout->dopt->no_comments)
13305 0 : return;
13306 :
13307 : /* Search for comments associated with type's pg_class OID */
13308 32 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13309 : &comments);
13310 :
13311 : /* If no comments exist, we're done */
13312 32 : if (ncomments <= 0)
13313 0 : return;
13314 :
13315 : /* Build COMMENT ON statements */
13316 32 : query = createPQExpBuffer();
13317 32 : target = createPQExpBuffer();
13318 :
13319 32 : ntups = PQntuples(res);
13320 32 : i_attnum = PQfnumber(res, "attnum");
13321 32 : i_attname = PQfnumber(res, "attname");
13322 32 : i_attisdropped = PQfnumber(res, "attisdropped");
13323 64 : while (ncomments > 0)
13324 : {
13325 : const char *attname;
13326 :
13327 32 : attname = NULL;
13328 32 : for (i = 0; i < ntups; i++)
13329 : {
13330 32 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13331 32 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13332 : {
13333 32 : attname = PQgetvalue(res, i, i_attname);
13334 32 : break;
13335 : }
13336 : }
13337 32 : if (attname) /* just in case we don't find it */
13338 : {
13339 32 : const char *descr = comments->descr;
13340 :
13341 32 : resetPQExpBuffer(target);
13342 32 : appendPQExpBuffer(target, "COLUMN %s.",
13343 32 : fmtId(tyinfo->dobj.name));
13344 32 : appendPQExpBufferStr(target, fmtId(attname));
13345 :
13346 32 : resetPQExpBuffer(query);
13347 32 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13348 32 : fmtQualifiedDumpable(tyinfo));
13349 32 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13350 32 : appendStringLiteralAH(query, descr, fout);
13351 32 : appendPQExpBufferStr(query, ";\n");
13352 :
13353 32 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13354 32 : ARCHIVE_OPTS(.tag = target->data,
13355 : .namespace = tyinfo->dobj.namespace->dobj.name,
13356 : .owner = tyinfo->rolname,
13357 : .description = "COMMENT",
13358 : .section = SECTION_NONE,
13359 : .createStmt = query->data,
13360 : .deps = &(tyinfo->dobj.dumpId),
13361 : .nDeps = 1));
13362 : }
13363 :
13364 32 : comments++;
13365 32 : ncomments--;
13366 : }
13367 :
13368 32 : destroyPQExpBuffer(query);
13369 32 : destroyPQExpBuffer(target);
13370 : }
13371 :
13372 : /*
13373 : * dumpShellType
13374 : * writes out to fout the queries to create a shell type
13375 : *
13376 : * We dump a shell definition in advance of the I/O functions for the type.
13377 : */
13378 : static void
13379 73 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13380 : {
13381 73 : DumpOptions *dopt = fout->dopt;
13382 : PQExpBuffer q;
13383 :
13384 : /* Do nothing if not dumping schema */
13385 73 : if (!dopt->dumpSchema)
13386 6 : return;
13387 :
13388 67 : q = createPQExpBuffer();
13389 :
13390 : /*
13391 : * Note the lack of a DROP command for the shell type; any required DROP
13392 : * is driven off the base type entry, instead. This interacts with
13393 : * _printTocEntry()'s use of the presence of a DROP command to decide
13394 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13395 : * the shell type's owner immediately on creation; that should happen only
13396 : * after it's filled in, otherwise the backend complains.
13397 : */
13398 :
13399 67 : if (dopt->binary_upgrade)
13400 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13401 8 : stinfo->baseType->dobj.catId.oid,
13402 : false, false);
13403 :
13404 67 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13405 67 : fmtQualifiedDumpable(stinfo));
13406 :
13407 67 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13408 67 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13409 67 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13410 : .namespace = stinfo->dobj.namespace->dobj.name,
13411 : .owner = stinfo->baseType->rolname,
13412 : .description = "SHELL TYPE",
13413 : .section = SECTION_PRE_DATA,
13414 : .createStmt = q->data));
13415 :
13416 67 : destroyPQExpBuffer(q);
13417 : }
13418 :
13419 : /*
13420 : * dumpProcLang
13421 : * writes out to fout the queries to recreate a user-defined
13422 : * procedural language
13423 : */
13424 : static void
13425 82 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13426 : {
13427 82 : DumpOptions *dopt = fout->dopt;
13428 : PQExpBuffer defqry;
13429 : PQExpBuffer delqry;
13430 : bool useParams;
13431 : char *qlanname;
13432 : FuncInfo *funcInfo;
13433 82 : FuncInfo *inlineInfo = NULL;
13434 82 : FuncInfo *validatorInfo = NULL;
13435 :
13436 : /* Do nothing if not dumping schema */
13437 82 : if (!dopt->dumpSchema)
13438 13 : return;
13439 :
13440 : /*
13441 : * Try to find the support function(s). It is not an error if we don't
13442 : * find them --- if the functions are in the pg_catalog schema, as is
13443 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13444 : * we will emit a parameterless CREATE LANGUAGE command, which will
13445 : * require PL template knowledge in the backend to reload.)
13446 : */
13447 :
13448 69 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13449 69 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13450 2 : funcInfo = NULL; /* treat not-dumped same as not-found */
13451 :
13452 69 : if (OidIsValid(plang->laninline))
13453 : {
13454 38 : inlineInfo = findFuncByOid(plang->laninline);
13455 38 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13456 1 : inlineInfo = NULL;
13457 : }
13458 :
13459 69 : if (OidIsValid(plang->lanvalidator))
13460 : {
13461 38 : validatorInfo = findFuncByOid(plang->lanvalidator);
13462 38 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13463 1 : validatorInfo = NULL;
13464 : }
13465 :
13466 : /*
13467 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13468 : * parameters. Otherwise, we'll write a parameterless command, which will
13469 : * be interpreted as CREATE EXTENSION.
13470 : */
13471 30 : useParams = (funcInfo != NULL &&
13472 129 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13473 30 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13474 :
13475 69 : defqry = createPQExpBuffer();
13476 69 : delqry = createPQExpBuffer();
13477 :
13478 69 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13479 :
13480 69 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13481 : qlanname);
13482 :
13483 69 : if (useParams)
13484 : {
13485 30 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13486 30 : plang->lanpltrusted ? "TRUSTED " : "",
13487 : qlanname);
13488 30 : appendPQExpBuffer(defqry, " HANDLER %s",
13489 30 : fmtQualifiedDumpable(funcInfo));
13490 30 : if (OidIsValid(plang->laninline))
13491 0 : appendPQExpBuffer(defqry, " INLINE %s",
13492 0 : fmtQualifiedDumpable(inlineInfo));
13493 30 : if (OidIsValid(plang->lanvalidator))
13494 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13495 0 : fmtQualifiedDumpable(validatorInfo));
13496 : }
13497 : else
13498 : {
13499 : /*
13500 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13501 : * command will not fail if the language is preinstalled in the target
13502 : * database.
13503 : *
13504 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13505 : * EXISTS; perhaps we should emit that instead? But it might just add
13506 : * confusion.
13507 : */
13508 39 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13509 : qlanname);
13510 : }
13511 69 : appendPQExpBufferStr(defqry, ";\n");
13512 :
13513 69 : if (dopt->binary_upgrade)
13514 2 : binary_upgrade_extension_member(defqry, &plang->dobj,
13515 : "LANGUAGE", qlanname, NULL);
13516 :
13517 69 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13518 31 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13519 31 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13520 : .owner = plang->lanowner,
13521 : .description = "PROCEDURAL LANGUAGE",
13522 : .section = SECTION_PRE_DATA,
13523 : .createStmt = defqry->data,
13524 : .dropStmt = delqry->data,
13525 : ));
13526 :
13527 : /* Dump Proc Lang Comments and Security Labels */
13528 69 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13529 0 : dumpComment(fout, "LANGUAGE", qlanname,
13530 0 : NULL, plang->lanowner,
13531 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13532 :
13533 69 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13534 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13535 0 : NULL, plang->lanowner,
13536 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13537 :
13538 69 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13539 38 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13540 : qlanname, NULL, NULL,
13541 38 : NULL, plang->lanowner, &plang->dacl);
13542 :
13543 69 : free(qlanname);
13544 :
13545 69 : destroyPQExpBuffer(defqry);
13546 69 : destroyPQExpBuffer(delqry);
13547 : }
13548 :
13549 : /*
13550 : * format_function_arguments: generate function name and argument list
13551 : *
13552 : * This is used when we can rely on pg_get_function_arguments to format
13553 : * the argument list. Note, however, that pg_get_function_arguments
13554 : * does not special-case zero-argument aggregates.
13555 : */
13556 : static char *
13557 4162 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13558 : {
13559 : PQExpBufferData fn;
13560 :
13561 4162 : initPQExpBuffer(&fn);
13562 4162 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13563 4162 : if (is_agg && finfo->nargs == 0)
13564 80 : appendPQExpBufferStr(&fn, "(*)");
13565 : else
13566 4082 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13567 4162 : return fn.data;
13568 : }
13569 :
13570 : /*
13571 : * format_function_signature: generate function name and argument list
13572 : *
13573 : * Only a minimal list of input argument types is generated; this is
13574 : * sufficient to reference the function, but not to define it.
13575 : *
13576 : * If honor_quotes is false then the function name is never quoted.
13577 : * This is appropriate for use in TOC tags, but not in SQL commands.
13578 : */
13579 : static char *
13580 2189 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13581 : {
13582 : PQExpBufferData fn;
13583 : int j;
13584 :
13585 2189 : initPQExpBuffer(&fn);
13586 2189 : if (honor_quotes)
13587 393 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13588 : else
13589 1796 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13590 4020 : for (j = 0; j < finfo->nargs; j++)
13591 : {
13592 1831 : if (j > 0)
13593 432 : appendPQExpBufferStr(&fn, ", ");
13594 :
13595 1831 : appendPQExpBufferStr(&fn,
13596 1831 : getFormattedTypeName(fout, finfo->argtypes[j],
13597 : zeroIsError));
13598 : }
13599 2189 : appendPQExpBufferChar(&fn, ')');
13600 2189 : return fn.data;
13601 : }
13602 :
13603 :
13604 : /*
13605 : * dumpFunc:
13606 : * dump out one function
13607 : */
13608 : static void
13609 1858 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13610 : {
13611 1858 : DumpOptions *dopt = fout->dopt;
13612 : PQExpBuffer query;
13613 : PQExpBuffer q;
13614 : PQExpBuffer delqry;
13615 : PQExpBuffer asPart;
13616 : PGresult *res;
13617 : char *funcsig; /* identity signature */
13618 1858 : char *funcfullsig = NULL; /* full signature */
13619 : char *funcsig_tag;
13620 : char *qual_funcsig;
13621 : char *proretset;
13622 : char *prosrc;
13623 : char *probin;
13624 : char *prosqlbody;
13625 : char *funcargs;
13626 : char *funciargs;
13627 : char *funcresult;
13628 : char *protrftypes;
13629 : char *prokind;
13630 : char *provolatile;
13631 : char *proisstrict;
13632 : char *prosecdef;
13633 : char *proleakproof;
13634 : char *proconfig;
13635 : char *procost;
13636 : char *prorows;
13637 : char *prosupport;
13638 : char *proparallel;
13639 : char *lanname;
13640 1858 : char **configitems = NULL;
13641 1858 : int nconfigitems = 0;
13642 : const char *keyword;
13643 :
13644 : /* Do nothing if not dumping schema */
13645 1858 : if (!dopt->dumpSchema)
13646 62 : return;
13647 :
13648 1796 : query = createPQExpBuffer();
13649 1796 : q = createPQExpBuffer();
13650 1796 : delqry = createPQExpBuffer();
13651 1796 : asPart = createPQExpBuffer();
13652 :
13653 1796 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13654 : {
13655 : /* Set up query for function-specific details */
13656 76 : appendPQExpBufferStr(query,
13657 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13658 :
13659 76 : appendPQExpBufferStr(query,
13660 : "SELECT\n"
13661 : "proretset,\n"
13662 : "prosrc,\n"
13663 : "probin,\n"
13664 : "provolatile,\n"
13665 : "proisstrict,\n"
13666 : "prosecdef,\n"
13667 : "lanname,\n"
13668 : "proconfig,\n"
13669 : "procost,\n"
13670 : "prorows,\n"
13671 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13672 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13673 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13674 : "proleakproof,\n");
13675 :
13676 76 : if (fout->remoteVersion >= 90500)
13677 76 : appendPQExpBufferStr(query,
13678 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13679 : else
13680 0 : appendPQExpBufferStr(query,
13681 : "NULL AS protrftypes,\n");
13682 :
13683 76 : if (fout->remoteVersion >= 90600)
13684 76 : appendPQExpBufferStr(query,
13685 : "proparallel,\n");
13686 : else
13687 0 : appendPQExpBufferStr(query,
13688 : "'u' AS proparallel,\n");
13689 :
13690 76 : if (fout->remoteVersion >= 110000)
13691 76 : appendPQExpBufferStr(query,
13692 : "prokind,\n");
13693 : else
13694 0 : appendPQExpBufferStr(query,
13695 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13696 :
13697 76 : if (fout->remoteVersion >= 120000)
13698 76 : appendPQExpBufferStr(query,
13699 : "prosupport,\n");
13700 : else
13701 0 : appendPQExpBufferStr(query,
13702 : "'-' AS prosupport,\n");
13703 :
13704 76 : if (fout->remoteVersion >= 140000)
13705 76 : appendPQExpBufferStr(query,
13706 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13707 : else
13708 0 : appendPQExpBufferStr(query,
13709 : "NULL AS prosqlbody\n");
13710 :
13711 76 : appendPQExpBufferStr(query,
13712 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13713 : "WHERE p.oid = $1 "
13714 : "AND l.oid = p.prolang");
13715 :
13716 76 : ExecuteSqlStatement(fout, query->data);
13717 :
13718 76 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13719 : }
13720 :
13721 1796 : printfPQExpBuffer(query,
13722 : "EXECUTE dumpFunc('%u')",
13723 1796 : finfo->dobj.catId.oid);
13724 :
13725 1796 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13726 :
13727 1796 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13728 1796 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13729 : {
13730 1748 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13731 1748 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13732 1748 : prosqlbody = NULL;
13733 : }
13734 : else
13735 : {
13736 48 : prosrc = NULL;
13737 48 : probin = NULL;
13738 48 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13739 : }
13740 1796 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13741 1796 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13742 1796 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13743 1796 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13744 1796 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13745 1796 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13746 1796 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13747 1796 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13748 1796 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13749 1796 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13750 1796 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13751 1796 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13752 1796 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13753 1796 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13754 1796 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13755 :
13756 : /*
13757 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13758 : * is used.
13759 : */
13760 1796 : if (prosqlbody)
13761 : {
13762 48 : appendPQExpBufferStr(asPart, prosqlbody);
13763 : }
13764 1748 : else if (probin[0] != '\0')
13765 : {
13766 153 : appendPQExpBufferStr(asPart, "AS ");
13767 153 : appendStringLiteralAH(asPart, probin, fout);
13768 153 : if (prosrc[0] != '\0')
13769 : {
13770 153 : appendPQExpBufferStr(asPart, ", ");
13771 :
13772 : /*
13773 : * where we have bin, use dollar quoting if allowed and src
13774 : * contains quote or backslash; else use regular quoting.
13775 : */
13776 153 : if (dopt->disable_dollar_quoting ||
13777 153 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13778 153 : appendStringLiteralAH(asPart, prosrc, fout);
13779 : else
13780 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13781 : }
13782 : }
13783 : else
13784 : {
13785 1595 : appendPQExpBufferStr(asPart, "AS ");
13786 : /* with no bin, dollar quote src unconditionally if allowed */
13787 1595 : if (dopt->disable_dollar_quoting)
13788 0 : appendStringLiteralAH(asPart, prosrc, fout);
13789 : else
13790 1595 : appendStringLiteralDQ(asPart, prosrc, NULL);
13791 : }
13792 :
13793 1796 : if (*proconfig)
13794 : {
13795 15 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13796 0 : pg_fatal("could not parse %s array", "proconfig");
13797 : }
13798 : else
13799 : {
13800 1781 : configitems = NULL;
13801 1781 : nconfigitems = 0;
13802 : }
13803 :
13804 1796 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13805 1796 : funcsig = format_function_arguments(finfo, funciargs, false);
13806 :
13807 1796 : funcsig_tag = format_function_signature(fout, finfo, false);
13808 :
13809 1796 : qual_funcsig = psprintf("%s.%s",
13810 1796 : fmtId(finfo->dobj.namespace->dobj.name),
13811 : funcsig);
13812 :
13813 1796 : if (prokind[0] == PROKIND_PROCEDURE)
13814 92 : keyword = "PROCEDURE";
13815 : else
13816 1704 : keyword = "FUNCTION"; /* works for window functions too */
13817 :
13818 1796 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13819 : keyword, qual_funcsig);
13820 :
13821 3592 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13822 : keyword,
13823 1796 : fmtId(finfo->dobj.namespace->dobj.name),
13824 : funcfullsig ? funcfullsig :
13825 : funcsig);
13826 :
13827 1796 : if (prokind[0] == PROKIND_PROCEDURE)
13828 : /* no result type to output */ ;
13829 1704 : else if (funcresult)
13830 1704 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13831 : else
13832 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13833 0 : (proretset[0] == 't') ? "SETOF " : "",
13834 0 : getFormattedTypeName(fout, finfo->prorettype,
13835 : zeroIsError));
13836 :
13837 1796 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13838 :
13839 1796 : if (*protrftypes)
13840 : {
13841 0 : Oid *typeids = pg_malloc_array(Oid, FUNC_MAX_ARGS);
13842 : int i;
13843 :
13844 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13845 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13846 0 : for (i = 0; typeids[i]; i++)
13847 : {
13848 0 : if (i != 0)
13849 0 : appendPQExpBufferStr(q, ", ");
13850 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13851 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13852 : }
13853 :
13854 0 : free(typeids);
13855 : }
13856 :
13857 1796 : if (prokind[0] == PROKIND_WINDOW)
13858 5 : appendPQExpBufferStr(q, " WINDOW");
13859 :
13860 1796 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13861 : {
13862 351 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13863 330 : appendPQExpBufferStr(q, " IMMUTABLE");
13864 21 : else if (provolatile[0] == PROVOLATILE_STABLE)
13865 21 : appendPQExpBufferStr(q, " STABLE");
13866 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13867 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13868 : finfo->dobj.name);
13869 : }
13870 :
13871 1796 : if (proisstrict[0] == 't')
13872 358 : appendPQExpBufferStr(q, " STRICT");
13873 :
13874 1796 : if (prosecdef[0] == 't')
13875 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13876 :
13877 1796 : if (proleakproof[0] == 't')
13878 10 : appendPQExpBufferStr(q, " LEAKPROOF");
13879 :
13880 : /*
13881 : * COST and ROWS are emitted only if present and not default, so as not to
13882 : * break backwards-compatibility of the dump without need. Keep this code
13883 : * in sync with the defaults in functioncmds.c.
13884 : */
13885 1796 : if (strcmp(procost, "0") != 0)
13886 : {
13887 1796 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13888 : {
13889 : /* default cost is 1 */
13890 382 : if (strcmp(procost, "1") != 0)
13891 0 : appendPQExpBuffer(q, " COST %s", procost);
13892 : }
13893 : else
13894 : {
13895 : /* default cost is 100 */
13896 1414 : if (strcmp(procost, "100") != 0)
13897 11 : appendPQExpBuffer(q, " COST %s", procost);
13898 : }
13899 : }
13900 1796 : if (proretset[0] == 't' &&
13901 192 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13902 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13903 :
13904 1796 : if (strcmp(prosupport, "-") != 0)
13905 : {
13906 : /* We rely on regprocout to provide quoting and qualification */
13907 42 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13908 : }
13909 :
13910 1796 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13911 : {
13912 116 : if (proparallel[0] == PROPARALLEL_SAFE)
13913 111 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13914 5 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13915 5 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13916 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13917 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13918 : finfo->dobj.name);
13919 : }
13920 :
13921 1836 : for (int i = 0; i < nconfigitems; i++)
13922 : {
13923 : /* we feel free to scribble on configitems[] here */
13924 40 : char *configitem = configitems[i];
13925 : char *pos;
13926 :
13927 40 : pos = strchr(configitem, '=');
13928 40 : if (pos == NULL)
13929 0 : continue;
13930 40 : *pos++ = '\0';
13931 40 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13932 :
13933 : /*
13934 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13935 : * by flatten_set_variable_args() before they were put into the
13936 : * proconfig array. However, because the quoting rules used there
13937 : * aren't exactly like SQL's, we have to break the list value apart
13938 : * and then quote the elements as string literals. (The elements may
13939 : * be double-quoted as-is, but we can't just feed them to the SQL
13940 : * parser; it would do the wrong thing with elements that are
13941 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13942 : * case for empty lists.
13943 : *
13944 : * Variables that are not so marked should just be emitted as simple
13945 : * string literals. If the variable is not known to
13946 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13947 : * to use GUC_LIST_QUOTE for extension variables.
13948 : */
13949 40 : if (variable_is_guc_list_quote(configitem))
13950 : {
13951 : char **namelist;
13952 : char **nameptr;
13953 :
13954 : /* Parse string into list of identifiers */
13955 : /* this shouldn't fail really */
13956 15 : if (SplitGUCList(pos, ',', &namelist))
13957 : {
13958 : /* Special case: represent an empty list as NULL */
13959 15 : if (*namelist == NULL)
13960 5 : appendPQExpBufferStr(q, "NULL");
13961 40 : for (nameptr = namelist; *nameptr; nameptr++)
13962 : {
13963 25 : if (nameptr != namelist)
13964 15 : appendPQExpBufferStr(q, ", ");
13965 25 : appendStringLiteralAH(q, *nameptr, fout);
13966 : }
13967 : }
13968 15 : pg_free(namelist);
13969 : }
13970 : else
13971 25 : appendStringLiteralAH(q, pos, fout);
13972 : }
13973 :
13974 1796 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13975 :
13976 1796 : append_depends_on_extension(fout, q, &finfo->dobj,
13977 : "pg_catalog.pg_proc", keyword,
13978 : qual_funcsig);
13979 :
13980 1796 : if (dopt->binary_upgrade)
13981 300 : binary_upgrade_extension_member(q, &finfo->dobj,
13982 : keyword, funcsig,
13983 300 : finfo->dobj.namespace->dobj.name);
13984 :
13985 1796 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13986 1700 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13987 1700 : ARCHIVE_OPTS(.tag = funcsig_tag,
13988 : .namespace = finfo->dobj.namespace->dobj.name,
13989 : .owner = finfo->rolname,
13990 : .description = keyword,
13991 : .section = finfo->postponed_def ?
13992 : SECTION_POST_DATA : SECTION_PRE_DATA,
13993 : .createStmt = q->data,
13994 : .dropStmt = delqry->data));
13995 :
13996 : /* Dump Function Comments and Security Labels */
13997 1796 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13998 9 : dumpComment(fout, keyword, funcsig,
13999 9 : finfo->dobj.namespace->dobj.name, finfo->rolname,
14000 9 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
14001 :
14002 1796 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
14003 0 : dumpSecLabel(fout, keyword, funcsig,
14004 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
14005 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
14006 :
14007 1796 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
14008 109 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
14009 : funcsig, NULL,
14010 109 : finfo->dobj.namespace->dobj.name,
14011 109 : NULL, finfo->rolname, &finfo->dacl);
14012 :
14013 1796 : PQclear(res);
14014 :
14015 1796 : destroyPQExpBuffer(query);
14016 1796 : destroyPQExpBuffer(q);
14017 1796 : destroyPQExpBuffer(delqry);
14018 1796 : destroyPQExpBuffer(asPart);
14019 1796 : free(funcsig);
14020 1796 : free(funcfullsig);
14021 1796 : free(funcsig_tag);
14022 1796 : free(qual_funcsig);
14023 1796 : free(configitems);
14024 : }
14025 :
14026 :
14027 : /*
14028 : * Dump a user-defined cast
14029 : */
14030 : static void
14031 67 : dumpCast(Archive *fout, const CastInfo *cast)
14032 : {
14033 67 : DumpOptions *dopt = fout->dopt;
14034 : PQExpBuffer defqry;
14035 : PQExpBuffer delqry;
14036 : PQExpBuffer labelq;
14037 : PQExpBuffer castargs;
14038 67 : FuncInfo *funcInfo = NULL;
14039 : const char *sourceType;
14040 : const char *targetType;
14041 :
14042 : /* Do nothing if not dumping schema */
14043 67 : if (!dopt->dumpSchema)
14044 6 : return;
14045 :
14046 : /* Cannot dump if we don't have the cast function's info */
14047 61 : if (OidIsValid(cast->castfunc))
14048 : {
14049 36 : funcInfo = findFuncByOid(cast->castfunc);
14050 36 : if (funcInfo == NULL)
14051 0 : pg_fatal("could not find function definition for function with OID %u",
14052 : cast->castfunc);
14053 : }
14054 :
14055 61 : defqry = createPQExpBuffer();
14056 61 : delqry = createPQExpBuffer();
14057 61 : labelq = createPQExpBuffer();
14058 61 : castargs = createPQExpBuffer();
14059 :
14060 61 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
14061 61 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
14062 61 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
14063 : sourceType, targetType);
14064 :
14065 61 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
14066 : sourceType, targetType);
14067 :
14068 61 : switch (cast->castmethod)
14069 : {
14070 25 : case COERCION_METHOD_BINARY:
14071 25 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
14072 25 : break;
14073 0 : case COERCION_METHOD_INOUT:
14074 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
14075 0 : break;
14076 36 : case COERCION_METHOD_FUNCTION:
14077 36 : if (funcInfo)
14078 : {
14079 36 : char *fsig = format_function_signature(fout, funcInfo, true);
14080 :
14081 : /*
14082 : * Always qualify the function name (format_function_signature
14083 : * won't qualify it).
14084 : */
14085 36 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
14086 36 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
14087 36 : free(fsig);
14088 : }
14089 : else
14090 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
14091 36 : break;
14092 0 : default:
14093 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
14094 : }
14095 :
14096 61 : if (cast->castcontext == 'a')
14097 31 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
14098 30 : else if (cast->castcontext == 'i')
14099 10 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
14100 61 : appendPQExpBufferStr(defqry, ";\n");
14101 :
14102 61 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
14103 : sourceType, targetType);
14104 :
14105 61 : appendPQExpBuffer(castargs, "(%s AS %s)",
14106 : sourceType, targetType);
14107 :
14108 61 : if (dopt->binary_upgrade)
14109 7 : binary_upgrade_extension_member(defqry, &cast->dobj,
14110 7 : "CAST", castargs->data, NULL);
14111 :
14112 61 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
14113 61 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
14114 61 : ARCHIVE_OPTS(.tag = labelq->data,
14115 : .description = "CAST",
14116 : .section = SECTION_PRE_DATA,
14117 : .createStmt = defqry->data,
14118 : .dropStmt = delqry->data));
14119 :
14120 : /* Dump Cast Comments */
14121 61 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
14122 0 : dumpComment(fout, "CAST", castargs->data,
14123 : NULL, "",
14124 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
14125 :
14126 61 : destroyPQExpBuffer(defqry);
14127 61 : destroyPQExpBuffer(delqry);
14128 61 : destroyPQExpBuffer(labelq);
14129 61 : destroyPQExpBuffer(castargs);
14130 : }
14131 :
14132 : /*
14133 : * Dump a transform
14134 : */
14135 : static void
14136 42 : dumpTransform(Archive *fout, const TransformInfo *transform)
14137 : {
14138 42 : DumpOptions *dopt = fout->dopt;
14139 : PQExpBuffer defqry;
14140 : PQExpBuffer delqry;
14141 : PQExpBuffer labelq;
14142 : PQExpBuffer transformargs;
14143 42 : FuncInfo *fromsqlFuncInfo = NULL;
14144 42 : FuncInfo *tosqlFuncInfo = NULL;
14145 : char *lanname;
14146 : const char *transformType;
14147 :
14148 : /* Do nothing if not dumping schema */
14149 42 : if (!dopt->dumpSchema)
14150 6 : return;
14151 :
14152 : /* Cannot dump if we don't have the transform functions' info */
14153 36 : if (OidIsValid(transform->trffromsql))
14154 : {
14155 36 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
14156 36 : if (fromsqlFuncInfo == NULL)
14157 0 : pg_fatal("could not find function definition for function with OID %u",
14158 : transform->trffromsql);
14159 : }
14160 36 : if (OidIsValid(transform->trftosql))
14161 : {
14162 36 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
14163 36 : if (tosqlFuncInfo == NULL)
14164 0 : pg_fatal("could not find function definition for function with OID %u",
14165 : transform->trftosql);
14166 : }
14167 :
14168 36 : defqry = createPQExpBuffer();
14169 36 : delqry = createPQExpBuffer();
14170 36 : labelq = createPQExpBuffer();
14171 36 : transformargs = createPQExpBuffer();
14172 :
14173 36 : lanname = get_language_name(fout, transform->trflang);
14174 36 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14175 :
14176 36 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14177 : transformType, lanname);
14178 :
14179 36 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14180 : transformType, lanname);
14181 :
14182 36 : if (!transform->trffromsql && !transform->trftosql)
14183 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14184 :
14185 36 : if (transform->trffromsql)
14186 : {
14187 36 : if (fromsqlFuncInfo)
14188 : {
14189 36 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14190 :
14191 : /*
14192 : * Always qualify the function name (format_function_signature
14193 : * won't qualify it).
14194 : */
14195 36 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14196 36 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14197 36 : free(fsig);
14198 : }
14199 : else
14200 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14201 : }
14202 :
14203 36 : if (transform->trftosql)
14204 : {
14205 36 : if (transform->trffromsql)
14206 36 : appendPQExpBufferStr(defqry, ", ");
14207 :
14208 36 : if (tosqlFuncInfo)
14209 : {
14210 36 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14211 :
14212 : /*
14213 : * Always qualify the function name (format_function_signature
14214 : * won't qualify it).
14215 : */
14216 36 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14217 36 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14218 36 : free(fsig);
14219 : }
14220 : else
14221 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14222 : }
14223 :
14224 36 : appendPQExpBufferStr(defqry, ");\n");
14225 :
14226 36 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14227 : transformType, lanname);
14228 :
14229 36 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14230 : transformType, lanname);
14231 :
14232 36 : if (dopt->binary_upgrade)
14233 2 : binary_upgrade_extension_member(defqry, &transform->dobj,
14234 2 : "TRANSFORM", transformargs->data, NULL);
14235 :
14236 36 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14237 36 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14238 36 : ARCHIVE_OPTS(.tag = labelq->data,
14239 : .description = "TRANSFORM",
14240 : .section = SECTION_PRE_DATA,
14241 : .createStmt = defqry->data,
14242 : .dropStmt = delqry->data,
14243 : .deps = transform->dobj.dependencies,
14244 : .nDeps = transform->dobj.nDeps));
14245 :
14246 : /* Dump Transform Comments */
14247 36 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14248 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14249 : NULL, "",
14250 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14251 :
14252 36 : free(lanname);
14253 36 : destroyPQExpBuffer(defqry);
14254 36 : destroyPQExpBuffer(delqry);
14255 36 : destroyPQExpBuffer(labelq);
14256 36 : destroyPQExpBuffer(transformargs);
14257 : }
14258 :
14259 :
14260 : /*
14261 : * dumpOpr
14262 : * write out a single operator definition
14263 : */
14264 : static void
14265 2522 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14266 : {
14267 2522 : DumpOptions *dopt = fout->dopt;
14268 : PQExpBuffer query;
14269 : PQExpBuffer q;
14270 : PQExpBuffer delq;
14271 : PQExpBuffer oprid;
14272 : PQExpBuffer details;
14273 : PGresult *res;
14274 : int i_oprkind;
14275 : int i_oprcode;
14276 : int i_oprleft;
14277 : int i_oprright;
14278 : int i_oprcom;
14279 : int i_oprnegate;
14280 : int i_oprrest;
14281 : int i_oprjoin;
14282 : int i_oprcanmerge;
14283 : int i_oprcanhash;
14284 : char *oprkind;
14285 : char *oprcode;
14286 : char *oprleft;
14287 : char *oprright;
14288 : char *oprcom;
14289 : char *oprnegate;
14290 : char *oprrest;
14291 : char *oprjoin;
14292 : char *oprcanmerge;
14293 : char *oprcanhash;
14294 : char *oprregproc;
14295 : char *oprref;
14296 :
14297 : /* Do nothing if not dumping schema */
14298 2522 : if (!dopt->dumpSchema)
14299 6 : return;
14300 :
14301 : /*
14302 : * some operators are invalid because they were the result of user
14303 : * defining operators before commutators exist
14304 : */
14305 2516 : if (!OidIsValid(oprinfo->oprcode))
14306 14 : return;
14307 :
14308 2502 : query = createPQExpBuffer();
14309 2502 : q = createPQExpBuffer();
14310 2502 : delq = createPQExpBuffer();
14311 2502 : oprid = createPQExpBuffer();
14312 2502 : details = createPQExpBuffer();
14313 :
14314 2502 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14315 : {
14316 : /* Set up query for operator-specific details */
14317 40 : appendPQExpBufferStr(query,
14318 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14319 : "SELECT oprkind, "
14320 : "oprcode::pg_catalog.regprocedure, "
14321 : "oprleft::pg_catalog.regtype, "
14322 : "oprright::pg_catalog.regtype, "
14323 : "oprcom, "
14324 : "oprnegate, "
14325 : "oprrest::pg_catalog.regprocedure, "
14326 : "oprjoin::pg_catalog.regprocedure, "
14327 : "oprcanmerge, oprcanhash "
14328 : "FROM pg_catalog.pg_operator "
14329 : "WHERE oid = $1");
14330 :
14331 40 : ExecuteSqlStatement(fout, query->data);
14332 :
14333 40 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14334 : }
14335 :
14336 2502 : printfPQExpBuffer(query,
14337 : "EXECUTE dumpOpr('%u')",
14338 2502 : oprinfo->dobj.catId.oid);
14339 :
14340 2502 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14341 :
14342 2502 : i_oprkind = PQfnumber(res, "oprkind");
14343 2502 : i_oprcode = PQfnumber(res, "oprcode");
14344 2502 : i_oprleft = PQfnumber(res, "oprleft");
14345 2502 : i_oprright = PQfnumber(res, "oprright");
14346 2502 : i_oprcom = PQfnumber(res, "oprcom");
14347 2502 : i_oprnegate = PQfnumber(res, "oprnegate");
14348 2502 : i_oprrest = PQfnumber(res, "oprrest");
14349 2502 : i_oprjoin = PQfnumber(res, "oprjoin");
14350 2502 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14351 2502 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14352 :
14353 2502 : oprkind = PQgetvalue(res, 0, i_oprkind);
14354 2502 : oprcode = PQgetvalue(res, 0, i_oprcode);
14355 2502 : oprleft = PQgetvalue(res, 0, i_oprleft);
14356 2502 : oprright = PQgetvalue(res, 0, i_oprright);
14357 2502 : oprcom = PQgetvalue(res, 0, i_oprcom);
14358 2502 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14359 2502 : oprrest = PQgetvalue(res, 0, i_oprrest);
14360 2502 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14361 2502 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14362 2502 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14363 :
14364 : /* In PG14 upwards postfix operator support does not exist anymore. */
14365 2502 : if (strcmp(oprkind, "r") == 0)
14366 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14367 : oprcode);
14368 :
14369 2502 : oprregproc = convertRegProcReference(oprcode);
14370 2502 : if (oprregproc)
14371 : {
14372 2502 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14373 2502 : free(oprregproc);
14374 : }
14375 :
14376 2502 : appendPQExpBuffer(oprid, "%s (",
14377 2502 : oprinfo->dobj.name);
14378 :
14379 : /*
14380 : * right unary means there's a left arg and left unary means there's a
14381 : * right arg. (Although the "r" case is dead code for PG14 and later,
14382 : * continue to support it in case we're dumping from an old server.)
14383 : */
14384 2502 : if (strcmp(oprkind, "r") == 0 ||
14385 2502 : strcmp(oprkind, "b") == 0)
14386 : {
14387 2359 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14388 2359 : appendPQExpBufferStr(oprid, oprleft);
14389 : }
14390 : else
14391 143 : appendPQExpBufferStr(oprid, "NONE");
14392 :
14393 2502 : if (strcmp(oprkind, "l") == 0 ||
14394 2359 : strcmp(oprkind, "b") == 0)
14395 : {
14396 2502 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14397 2502 : appendPQExpBuffer(oprid, ", %s)", oprright);
14398 : }
14399 : else
14400 0 : appendPQExpBufferStr(oprid, ", NONE)");
14401 :
14402 2502 : oprref = getFormattedOperatorName(oprcom);
14403 2502 : if (oprref)
14404 : {
14405 1679 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14406 1679 : free(oprref);
14407 : }
14408 :
14409 2502 : oprref = getFormattedOperatorName(oprnegate);
14410 2502 : if (oprref)
14411 : {
14412 1181 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14413 1181 : free(oprref);
14414 : }
14415 :
14416 2502 : if (strcmp(oprcanmerge, "t") == 0)
14417 188 : appendPQExpBufferStr(details, ",\n MERGES");
14418 :
14419 2502 : if (strcmp(oprcanhash, "t") == 0)
14420 141 : appendPQExpBufferStr(details, ",\n HASHES");
14421 :
14422 2502 : oprregproc = convertRegProcReference(oprrest);
14423 2502 : if (oprregproc)
14424 : {
14425 1532 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14426 1532 : free(oprregproc);
14427 : }
14428 :
14429 2502 : oprregproc = convertRegProcReference(oprjoin);
14430 2502 : if (oprregproc)
14431 : {
14432 1532 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14433 1532 : free(oprregproc);
14434 : }
14435 :
14436 2502 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14437 2502 : fmtId(oprinfo->dobj.namespace->dobj.name),
14438 : oprid->data);
14439 :
14440 2502 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14441 2502 : fmtId(oprinfo->dobj.namespace->dobj.name),
14442 2502 : oprinfo->dobj.name, details->data);
14443 :
14444 2502 : if (dopt->binary_upgrade)
14445 12 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14446 12 : "OPERATOR", oprid->data,
14447 12 : oprinfo->dobj.namespace->dobj.name);
14448 :
14449 2502 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14450 2502 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14451 2502 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14452 : .namespace = oprinfo->dobj.namespace->dobj.name,
14453 : .owner = oprinfo->rolname,
14454 : .description = "OPERATOR",
14455 : .section = SECTION_PRE_DATA,
14456 : .createStmt = q->data,
14457 : .dropStmt = delq->data));
14458 :
14459 : /* Dump Operator Comments */
14460 2502 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14461 2415 : dumpComment(fout, "OPERATOR", oprid->data,
14462 2415 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14463 2415 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14464 :
14465 2502 : PQclear(res);
14466 :
14467 2502 : destroyPQExpBuffer(query);
14468 2502 : destroyPQExpBuffer(q);
14469 2502 : destroyPQExpBuffer(delq);
14470 2502 : destroyPQExpBuffer(oprid);
14471 2502 : destroyPQExpBuffer(details);
14472 : }
14473 :
14474 : /*
14475 : * Convert a function reference obtained from pg_operator
14476 : *
14477 : * Returns allocated string of what to print, or NULL if function references
14478 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14479 : *
14480 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14481 : * part.
14482 : */
14483 : static char *
14484 7506 : convertRegProcReference(const char *proc)
14485 : {
14486 : char *name;
14487 : char *paren;
14488 : bool inquote;
14489 :
14490 : /* In all cases "-" means a null reference */
14491 7506 : if (strcmp(proc, "-") == 0)
14492 1940 : return NULL;
14493 :
14494 5566 : name = pg_strdup(proc);
14495 : /* find non-double-quoted left paren */
14496 5566 : inquote = false;
14497 66996 : for (paren = name; *paren; paren++)
14498 : {
14499 66996 : if (*paren == '(' && !inquote)
14500 : {
14501 5566 : *paren = '\0';
14502 5566 : break;
14503 : }
14504 61430 : if (*paren == '"')
14505 50 : inquote = !inquote;
14506 : }
14507 5566 : return name;
14508 : }
14509 :
14510 : /*
14511 : * getFormattedOperatorName - retrieve the operator name for the
14512 : * given operator OID (presented in string form).
14513 : *
14514 : * Returns an allocated string, or NULL if the given OID is invalid.
14515 : * Caller is responsible for free'ing result string.
14516 : *
14517 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14518 : * useful in commands where the operator's argument types can be inferred from
14519 : * context. We always schema-qualify the name, though. The predecessor to
14520 : * this code tried to skip the schema qualification if possible, but that led
14521 : * to wrong results in corner cases, such as if an operator and its negator
14522 : * are in different schemas.
14523 : */
14524 : static char *
14525 5289 : getFormattedOperatorName(const char *oproid)
14526 : {
14527 : OprInfo *oprInfo;
14528 :
14529 : /* In all cases "0" means a null reference */
14530 5289 : if (strcmp(oproid, "0") == 0)
14531 2429 : return NULL;
14532 :
14533 2860 : oprInfo = findOprByOid(atooid(oproid));
14534 2860 : if (oprInfo == NULL)
14535 : {
14536 0 : pg_log_warning("could not find operator with OID %s",
14537 : oproid);
14538 0 : return NULL;
14539 : }
14540 :
14541 2860 : return psprintf("OPERATOR(%s.%s)",
14542 2860 : fmtId(oprInfo->dobj.namespace->dobj.name),
14543 : oprInfo->dobj.name);
14544 : }
14545 :
14546 : /*
14547 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14548 : *
14549 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14550 : * argument lists of these functions are predetermined. Note that the
14551 : * caller should ensure we are in the proper schema, because the results
14552 : * are search path dependent!
14553 : */
14554 : static char *
14555 205 : convertTSFunction(Archive *fout, Oid funcOid)
14556 : {
14557 : char *result;
14558 : char query[128];
14559 : PGresult *res;
14560 :
14561 205 : snprintf(query, sizeof(query),
14562 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14563 205 : res = ExecuteSqlQueryForSingleRow(fout, query);
14564 :
14565 205 : result = pg_strdup(PQgetvalue(res, 0, 0));
14566 :
14567 205 : PQclear(res);
14568 :
14569 205 : return result;
14570 : }
14571 :
14572 : /*
14573 : * dumpAccessMethod
14574 : * write out a single access method definition
14575 : */
14576 : static void
14577 80 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14578 : {
14579 80 : DumpOptions *dopt = fout->dopt;
14580 : PQExpBuffer q;
14581 : PQExpBuffer delq;
14582 : char *qamname;
14583 :
14584 : /* Do nothing if not dumping schema */
14585 80 : if (!dopt->dumpSchema)
14586 12 : return;
14587 :
14588 68 : q = createPQExpBuffer();
14589 68 : delq = createPQExpBuffer();
14590 :
14591 68 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14592 :
14593 68 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14594 :
14595 68 : switch (aminfo->amtype)
14596 : {
14597 32 : case AMTYPE_INDEX:
14598 32 : appendPQExpBufferStr(q, "TYPE INDEX ");
14599 32 : break;
14600 36 : case AMTYPE_TABLE:
14601 36 : appendPQExpBufferStr(q, "TYPE TABLE ");
14602 36 : break;
14603 0 : default:
14604 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14605 : aminfo->amtype, qamname);
14606 0 : destroyPQExpBuffer(q);
14607 0 : destroyPQExpBuffer(delq);
14608 0 : free(qamname);
14609 0 : return;
14610 : }
14611 :
14612 68 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14613 :
14614 68 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14615 : qamname);
14616 :
14617 68 : if (dopt->binary_upgrade)
14618 4 : binary_upgrade_extension_member(q, &aminfo->dobj,
14619 : "ACCESS METHOD", qamname, NULL);
14620 :
14621 68 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14622 68 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14623 68 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14624 : .description = "ACCESS METHOD",
14625 : .section = SECTION_PRE_DATA,
14626 : .createStmt = q->data,
14627 : .dropStmt = delq->data));
14628 :
14629 : /* Dump Access Method Comments */
14630 68 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14631 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14632 : NULL, "",
14633 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14634 :
14635 68 : destroyPQExpBuffer(q);
14636 68 : destroyPQExpBuffer(delq);
14637 68 : free(qamname);
14638 : }
14639 :
14640 : /*
14641 : * dumpOpclass
14642 : * write out a single operator class definition
14643 : */
14644 : static void
14645 666 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14646 : {
14647 666 : DumpOptions *dopt = fout->dopt;
14648 : PQExpBuffer query;
14649 : PQExpBuffer q;
14650 : PQExpBuffer delq;
14651 : PQExpBuffer nameusing;
14652 : PGresult *res;
14653 : int ntups;
14654 : int i_opcintype;
14655 : int i_opckeytype;
14656 : int i_opcdefault;
14657 : int i_opcfamily;
14658 : int i_opcfamilyname;
14659 : int i_opcfamilynsp;
14660 : int i_amname;
14661 : int i_amopstrategy;
14662 : int i_amopopr;
14663 : int i_sortfamily;
14664 : int i_sortfamilynsp;
14665 : int i_amprocnum;
14666 : int i_amproc;
14667 : int i_amproclefttype;
14668 : int i_amprocrighttype;
14669 : char *opcintype;
14670 : char *opckeytype;
14671 : char *opcdefault;
14672 : char *opcfamily;
14673 : char *opcfamilyname;
14674 : char *opcfamilynsp;
14675 : char *amname;
14676 : char *amopstrategy;
14677 : char *amopopr;
14678 : char *sortfamily;
14679 : char *sortfamilynsp;
14680 : char *amprocnum;
14681 : char *amproc;
14682 : char *amproclefttype;
14683 : char *amprocrighttype;
14684 : bool needComma;
14685 : int i;
14686 :
14687 : /* Do nothing if not dumping schema */
14688 666 : if (!dopt->dumpSchema)
14689 18 : return;
14690 :
14691 648 : query = createPQExpBuffer();
14692 648 : q = createPQExpBuffer();
14693 648 : delq = createPQExpBuffer();
14694 648 : nameusing = createPQExpBuffer();
14695 :
14696 : /* Get additional fields from the pg_opclass row */
14697 648 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14698 : "opckeytype::pg_catalog.regtype, "
14699 : "opcdefault, opcfamily, "
14700 : "opfname AS opcfamilyname, "
14701 : "nspname AS opcfamilynsp, "
14702 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14703 : "FROM pg_catalog.pg_opclass c "
14704 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14705 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14706 : "WHERE c.oid = '%u'::pg_catalog.oid",
14707 648 : opcinfo->dobj.catId.oid);
14708 :
14709 648 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14710 :
14711 648 : i_opcintype = PQfnumber(res, "opcintype");
14712 648 : i_opckeytype = PQfnumber(res, "opckeytype");
14713 648 : i_opcdefault = PQfnumber(res, "opcdefault");
14714 648 : i_opcfamily = PQfnumber(res, "opcfamily");
14715 648 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14716 648 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14717 648 : i_amname = PQfnumber(res, "amname");
14718 :
14719 : /* opcintype may still be needed after we PQclear res */
14720 648 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14721 648 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14722 648 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14723 : /* opcfamily will still be needed after we PQclear res */
14724 648 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14725 648 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14726 648 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14727 : /* amname will still be needed after we PQclear res */
14728 648 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14729 :
14730 648 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14731 648 : fmtQualifiedDumpable(opcinfo));
14732 648 : appendPQExpBuffer(delq, " USING %s;\n",
14733 : fmtId(amname));
14734 :
14735 : /* Build the fixed portion of the CREATE command */
14736 648 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14737 648 : fmtQualifiedDumpable(opcinfo));
14738 648 : if (strcmp(opcdefault, "t") == 0)
14739 366 : appendPQExpBufferStr(q, "DEFAULT ");
14740 648 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14741 : opcintype,
14742 : fmtId(amname));
14743 648 : if (strlen(opcfamilyname) > 0)
14744 : {
14745 648 : appendPQExpBufferStr(q, " FAMILY ");
14746 648 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14747 648 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14748 : }
14749 648 : appendPQExpBufferStr(q, " AS\n ");
14750 :
14751 648 : needComma = false;
14752 :
14753 648 : if (strcmp(opckeytype, "-") != 0)
14754 : {
14755 252 : appendPQExpBuffer(q, "STORAGE %s",
14756 : opckeytype);
14757 252 : needComma = true;
14758 : }
14759 :
14760 648 : PQclear(res);
14761 :
14762 : /*
14763 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14764 : *
14765 : * Print only those opfamily members that are tied to the opclass by
14766 : * pg_depend entries.
14767 : */
14768 648 : resetPQExpBuffer(query);
14769 648 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14770 : "amopopr::pg_catalog.regoperator, "
14771 : "opfname AS sortfamily, "
14772 : "nspname AS sortfamilynsp "
14773 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14774 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14775 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14776 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14777 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14778 : "AND refobjid = '%u'::pg_catalog.oid "
14779 : "AND amopfamily = '%s'::pg_catalog.oid "
14780 : "ORDER BY amopstrategy",
14781 648 : opcinfo->dobj.catId.oid,
14782 : opcfamily);
14783 :
14784 648 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14785 :
14786 648 : ntups = PQntuples(res);
14787 :
14788 648 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14789 648 : i_amopopr = PQfnumber(res, "amopopr");
14790 648 : i_sortfamily = PQfnumber(res, "sortfamily");
14791 648 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14792 :
14793 850 : for (i = 0; i < ntups; i++)
14794 : {
14795 202 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14796 202 : amopopr = PQgetvalue(res, i, i_amopopr);
14797 202 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14798 202 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14799 :
14800 202 : if (needComma)
14801 128 : appendPQExpBufferStr(q, " ,\n ");
14802 :
14803 202 : appendPQExpBuffer(q, "OPERATOR %s %s",
14804 : amopstrategy, amopopr);
14805 :
14806 202 : if (strlen(sortfamily) > 0)
14807 : {
14808 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14809 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14810 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14811 : }
14812 :
14813 202 : needComma = true;
14814 : }
14815 :
14816 648 : PQclear(res);
14817 :
14818 : /*
14819 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14820 : *
14821 : * Print only those opfamily members that are tied to the opclass by
14822 : * pg_depend entries.
14823 : *
14824 : * We print the amproclefttype/amprocrighttype even though in most cases
14825 : * the backend could deduce the right values, because of the corner case
14826 : * of a btree sort support function for a cross-type comparison.
14827 : */
14828 648 : resetPQExpBuffer(query);
14829 :
14830 648 : appendPQExpBuffer(query, "SELECT amprocnum, "
14831 : "amproc::pg_catalog.regprocedure, "
14832 : "amproclefttype::pg_catalog.regtype, "
14833 : "amprocrighttype::pg_catalog.regtype "
14834 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14835 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14836 : "AND refobjid = '%u'::pg_catalog.oid "
14837 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14838 : "AND objid = ap.oid "
14839 : "ORDER BY amprocnum",
14840 648 : opcinfo->dobj.catId.oid);
14841 :
14842 648 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14843 :
14844 648 : ntups = PQntuples(res);
14845 :
14846 648 : i_amprocnum = PQfnumber(res, "amprocnum");
14847 648 : i_amproc = PQfnumber(res, "amproc");
14848 648 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14849 648 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14850 :
14851 680 : for (i = 0; i < ntups; i++)
14852 : {
14853 32 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14854 32 : amproc = PQgetvalue(res, i, i_amproc);
14855 32 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14856 32 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14857 :
14858 32 : if (needComma)
14859 32 : appendPQExpBufferStr(q, " ,\n ");
14860 :
14861 32 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14862 :
14863 32 : if (*amproclefttype && *amprocrighttype)
14864 32 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14865 :
14866 32 : appendPQExpBuffer(q, " %s", amproc);
14867 :
14868 32 : needComma = true;
14869 : }
14870 :
14871 648 : PQclear(res);
14872 :
14873 : /*
14874 : * If needComma is still false it means we haven't added anything after
14875 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14876 : * clause with the same datatype. This isn't sanctioned by the
14877 : * documentation, but actually DefineOpClass will treat it as a no-op.
14878 : */
14879 648 : if (!needComma)
14880 322 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14881 :
14882 648 : appendPQExpBufferStr(q, ";\n");
14883 :
14884 648 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14885 648 : appendPQExpBuffer(nameusing, " USING %s",
14886 : fmtId(amname));
14887 :
14888 648 : if (dopt->binary_upgrade)
14889 6 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14890 6 : "OPERATOR CLASS", nameusing->data,
14891 6 : opcinfo->dobj.namespace->dobj.name);
14892 :
14893 648 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14894 648 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14895 648 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14896 : .namespace = opcinfo->dobj.namespace->dobj.name,
14897 : .owner = opcinfo->rolname,
14898 : .description = "OPERATOR CLASS",
14899 : .section = SECTION_PRE_DATA,
14900 : .createStmt = q->data,
14901 : .dropStmt = delq->data));
14902 :
14903 : /* Dump Operator Class Comments */
14904 648 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14905 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14906 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14907 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14908 :
14909 648 : free(opcintype);
14910 648 : free(opcfamily);
14911 648 : free(amname);
14912 648 : destroyPQExpBuffer(query);
14913 648 : destroyPQExpBuffer(q);
14914 648 : destroyPQExpBuffer(delq);
14915 648 : destroyPQExpBuffer(nameusing);
14916 : }
14917 :
14918 : /*
14919 : * dumpOpfamily
14920 : * write out a single operator family definition
14921 : *
14922 : * Note: this also dumps any "loose" operator members that aren't bound to a
14923 : * specific opclass within the opfamily.
14924 : */
14925 : static void
14926 555 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14927 : {
14928 555 : DumpOptions *dopt = fout->dopt;
14929 : PQExpBuffer query;
14930 : PQExpBuffer q;
14931 : PQExpBuffer delq;
14932 : PQExpBuffer nameusing;
14933 : PGresult *res;
14934 : PGresult *res_ops;
14935 : PGresult *res_procs;
14936 : int ntups;
14937 : int i_amname;
14938 : int i_amopstrategy;
14939 : int i_amopopr;
14940 : int i_sortfamily;
14941 : int i_sortfamilynsp;
14942 : int i_amprocnum;
14943 : int i_amproc;
14944 : int i_amproclefttype;
14945 : int i_amprocrighttype;
14946 : char *amname;
14947 : char *amopstrategy;
14948 : char *amopopr;
14949 : char *sortfamily;
14950 : char *sortfamilynsp;
14951 : char *amprocnum;
14952 : char *amproc;
14953 : char *amproclefttype;
14954 : char *amprocrighttype;
14955 : bool needComma;
14956 : int i;
14957 :
14958 : /* Do nothing if not dumping schema */
14959 555 : if (!dopt->dumpSchema)
14960 12 : return;
14961 :
14962 543 : query = createPQExpBuffer();
14963 543 : q = createPQExpBuffer();
14964 543 : delq = createPQExpBuffer();
14965 543 : nameusing = createPQExpBuffer();
14966 :
14967 : /*
14968 : * Fetch only those opfamily members that are tied directly to the
14969 : * opfamily by pg_depend entries.
14970 : */
14971 543 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14972 : "amopopr::pg_catalog.regoperator, "
14973 : "opfname AS sortfamily, "
14974 : "nspname AS sortfamilynsp "
14975 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14976 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14977 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14978 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14979 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14980 : "AND refobjid = '%u'::pg_catalog.oid "
14981 : "AND amopfamily = '%u'::pg_catalog.oid "
14982 : "ORDER BY amopstrategy",
14983 543 : opfinfo->dobj.catId.oid,
14984 543 : opfinfo->dobj.catId.oid);
14985 :
14986 543 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14987 :
14988 543 : resetPQExpBuffer(query);
14989 :
14990 543 : appendPQExpBuffer(query, "SELECT amprocnum, "
14991 : "amproc::pg_catalog.regprocedure, "
14992 : "amproclefttype::pg_catalog.regtype, "
14993 : "amprocrighttype::pg_catalog.regtype "
14994 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14995 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14996 : "AND refobjid = '%u'::pg_catalog.oid "
14997 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14998 : "AND objid = ap.oid "
14999 : "ORDER BY amprocnum",
15000 543 : opfinfo->dobj.catId.oid);
15001 :
15002 543 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15003 :
15004 : /* Get additional fields from the pg_opfamily row */
15005 543 : resetPQExpBuffer(query);
15006 :
15007 543 : appendPQExpBuffer(query, "SELECT "
15008 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
15009 : "FROM pg_catalog.pg_opfamily "
15010 : "WHERE oid = '%u'::pg_catalog.oid",
15011 543 : opfinfo->dobj.catId.oid);
15012 :
15013 543 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15014 :
15015 543 : i_amname = PQfnumber(res, "amname");
15016 :
15017 : /* amname will still be needed after we PQclear res */
15018 543 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
15019 :
15020 543 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
15021 543 : fmtQualifiedDumpable(opfinfo));
15022 543 : appendPQExpBuffer(delq, " USING %s;\n",
15023 : fmtId(amname));
15024 :
15025 : /* Build the fixed portion of the CREATE command */
15026 543 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
15027 543 : fmtQualifiedDumpable(opfinfo));
15028 543 : appendPQExpBuffer(q, " USING %s;\n",
15029 : fmtId(amname));
15030 :
15031 543 : PQclear(res);
15032 :
15033 : /* Do we need an ALTER to add loose members? */
15034 543 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
15035 : {
15036 47 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
15037 47 : fmtQualifiedDumpable(opfinfo));
15038 47 : appendPQExpBuffer(q, " USING %s ADD\n ",
15039 : fmtId(amname));
15040 :
15041 47 : needComma = false;
15042 :
15043 : /*
15044 : * Now fetch and print the OPERATOR entries (pg_amop rows).
15045 : */
15046 47 : ntups = PQntuples(res_ops);
15047 :
15048 47 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
15049 47 : i_amopopr = PQfnumber(res_ops, "amopopr");
15050 47 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
15051 47 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
15052 :
15053 207 : for (i = 0; i < ntups; i++)
15054 : {
15055 160 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
15056 160 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
15057 160 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
15058 160 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
15059 :
15060 160 : if (needComma)
15061 128 : appendPQExpBufferStr(q, " ,\n ");
15062 :
15063 160 : appendPQExpBuffer(q, "OPERATOR %s %s",
15064 : amopstrategy, amopopr);
15065 :
15066 160 : if (strlen(sortfamily) > 0)
15067 : {
15068 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
15069 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
15070 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
15071 : }
15072 :
15073 160 : needComma = true;
15074 : }
15075 :
15076 : /*
15077 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
15078 : */
15079 47 : ntups = PQntuples(res_procs);
15080 :
15081 47 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
15082 47 : i_amproc = PQfnumber(res_procs, "amproc");
15083 47 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
15084 47 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
15085 :
15086 222 : for (i = 0; i < ntups; i++)
15087 : {
15088 175 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
15089 175 : amproc = PQgetvalue(res_procs, i, i_amproc);
15090 175 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
15091 175 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
15092 :
15093 175 : if (needComma)
15094 160 : appendPQExpBufferStr(q, " ,\n ");
15095 :
15096 175 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
15097 : amprocnum, amproclefttype, amprocrighttype,
15098 : amproc);
15099 :
15100 175 : needComma = true;
15101 : }
15102 :
15103 47 : appendPQExpBufferStr(q, ";\n");
15104 : }
15105 :
15106 543 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
15107 543 : appendPQExpBuffer(nameusing, " USING %s",
15108 : fmtId(amname));
15109 :
15110 543 : if (dopt->binary_upgrade)
15111 9 : binary_upgrade_extension_member(q, &opfinfo->dobj,
15112 9 : "OPERATOR FAMILY", nameusing->data,
15113 9 : opfinfo->dobj.namespace->dobj.name);
15114 :
15115 543 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15116 543 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
15117 543 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
15118 : .namespace = opfinfo->dobj.namespace->dobj.name,
15119 : .owner = opfinfo->rolname,
15120 : .description = "OPERATOR FAMILY",
15121 : .section = SECTION_PRE_DATA,
15122 : .createStmt = q->data,
15123 : .dropStmt = delq->data));
15124 :
15125 : /* Dump Operator Family Comments */
15126 543 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15127 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
15128 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
15129 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
15130 :
15131 543 : free(amname);
15132 543 : PQclear(res_ops);
15133 543 : PQclear(res_procs);
15134 543 : destroyPQExpBuffer(query);
15135 543 : destroyPQExpBuffer(q);
15136 543 : destroyPQExpBuffer(delq);
15137 543 : destroyPQExpBuffer(nameusing);
15138 : }
15139 :
15140 : /*
15141 : * dumpCollation
15142 : * write out a single collation definition
15143 : */
15144 : static void
15145 2729 : dumpCollation(Archive *fout, const CollInfo *collinfo)
15146 : {
15147 2729 : DumpOptions *dopt = fout->dopt;
15148 : PQExpBuffer query;
15149 : PQExpBuffer q;
15150 : PQExpBuffer delq;
15151 : char *qcollname;
15152 : PGresult *res;
15153 : int i_collprovider;
15154 : int i_collisdeterministic;
15155 : int i_collcollate;
15156 : int i_collctype;
15157 : int i_colllocale;
15158 : int i_collicurules;
15159 : const char *collprovider;
15160 : const char *collcollate;
15161 : const char *collctype;
15162 : const char *colllocale;
15163 : const char *collicurules;
15164 :
15165 : /* Do nothing if not dumping schema */
15166 2729 : if (!dopt->dumpSchema)
15167 12 : return;
15168 :
15169 2717 : query = createPQExpBuffer();
15170 2717 : q = createPQExpBuffer();
15171 2717 : delq = createPQExpBuffer();
15172 :
15173 2717 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15174 :
15175 : /* Get collation-specific details */
15176 2717 : appendPQExpBufferStr(query, "SELECT ");
15177 :
15178 2717 : if (fout->remoteVersion >= 100000)
15179 2717 : appendPQExpBufferStr(query,
15180 : "collprovider, "
15181 : "collversion, ");
15182 : else
15183 0 : appendPQExpBufferStr(query,
15184 : "'c' AS collprovider, "
15185 : "NULL AS collversion, ");
15186 :
15187 2717 : if (fout->remoteVersion >= 120000)
15188 2717 : appendPQExpBufferStr(query,
15189 : "collisdeterministic, ");
15190 : else
15191 0 : appendPQExpBufferStr(query,
15192 : "true AS collisdeterministic, ");
15193 :
15194 2717 : if (fout->remoteVersion >= 170000)
15195 2717 : appendPQExpBufferStr(query,
15196 : "colllocale, ");
15197 0 : else if (fout->remoteVersion >= 150000)
15198 0 : appendPQExpBufferStr(query,
15199 : "colliculocale AS colllocale, ");
15200 : else
15201 0 : appendPQExpBufferStr(query,
15202 : "NULL AS colllocale, ");
15203 :
15204 2717 : if (fout->remoteVersion >= 160000)
15205 2717 : appendPQExpBufferStr(query,
15206 : "collicurules, ");
15207 : else
15208 0 : appendPQExpBufferStr(query,
15209 : "NULL AS collicurules, ");
15210 :
15211 2717 : appendPQExpBuffer(query,
15212 : "collcollate, "
15213 : "collctype "
15214 : "FROM pg_catalog.pg_collation c "
15215 : "WHERE c.oid = '%u'::pg_catalog.oid",
15216 2717 : collinfo->dobj.catId.oid);
15217 :
15218 2717 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15219 :
15220 2717 : i_collprovider = PQfnumber(res, "collprovider");
15221 2717 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15222 2717 : i_collcollate = PQfnumber(res, "collcollate");
15223 2717 : i_collctype = PQfnumber(res, "collctype");
15224 2717 : i_colllocale = PQfnumber(res, "colllocale");
15225 2717 : i_collicurules = PQfnumber(res, "collicurules");
15226 :
15227 2717 : collprovider = PQgetvalue(res, 0, i_collprovider);
15228 :
15229 2717 : if (!PQgetisnull(res, 0, i_collcollate))
15230 46 : collcollate = PQgetvalue(res, 0, i_collcollate);
15231 : else
15232 2671 : collcollate = NULL;
15233 :
15234 2717 : if (!PQgetisnull(res, 0, i_collctype))
15235 46 : collctype = PQgetvalue(res, 0, i_collctype);
15236 : else
15237 2671 : collctype = NULL;
15238 :
15239 : /*
15240 : * Before version 15, collcollate and collctype were of type NAME and
15241 : * non-nullable. Treat empty strings as NULL for consistency.
15242 : */
15243 2717 : if (fout->remoteVersion < 150000)
15244 : {
15245 0 : if (collcollate[0] == '\0')
15246 0 : collcollate = NULL;
15247 0 : if (collctype[0] == '\0')
15248 0 : collctype = NULL;
15249 : }
15250 :
15251 2717 : if (!PQgetisnull(res, 0, i_colllocale))
15252 2668 : colllocale = PQgetvalue(res, 0, i_colllocale);
15253 : else
15254 49 : colllocale = NULL;
15255 :
15256 2717 : if (!PQgetisnull(res, 0, i_collicurules))
15257 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15258 : else
15259 2717 : collicurules = NULL;
15260 :
15261 2717 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15262 2717 : fmtQualifiedDumpable(collinfo));
15263 :
15264 2717 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15265 2717 : fmtQualifiedDumpable(collinfo));
15266 :
15267 2717 : appendPQExpBufferStr(q, "provider = ");
15268 2717 : if (collprovider[0] == 'b')
15269 19 : appendPQExpBufferStr(q, "builtin");
15270 2698 : else if (collprovider[0] == 'c')
15271 46 : appendPQExpBufferStr(q, "libc");
15272 2652 : else if (collprovider[0] == 'i')
15273 2649 : appendPQExpBufferStr(q, "icu");
15274 3 : else if (collprovider[0] == 'd')
15275 : /* to allow dumping pg_catalog; not accepted on input */
15276 3 : appendPQExpBufferStr(q, "default");
15277 : else
15278 0 : pg_fatal("unrecognized collation provider: %s",
15279 : collprovider);
15280 :
15281 2717 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15282 0 : appendPQExpBufferStr(q, ", deterministic = false");
15283 :
15284 2717 : if (collprovider[0] == 'd')
15285 : {
15286 3 : if (collcollate || collctype || colllocale || collicurules)
15287 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15288 :
15289 : /* no locale -- the default collation cannot be reloaded anyway */
15290 : }
15291 2714 : else if (collprovider[0] == 'b')
15292 : {
15293 19 : if (collcollate || collctype || !colllocale || collicurules)
15294 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15295 :
15296 19 : appendPQExpBufferStr(q, ", locale = ");
15297 19 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15298 : fout);
15299 : }
15300 2695 : else if (collprovider[0] == 'i')
15301 : {
15302 2649 : if (fout->remoteVersion >= 150000)
15303 : {
15304 2649 : if (collcollate || collctype || !colllocale)
15305 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15306 :
15307 2649 : appendPQExpBufferStr(q, ", locale = ");
15308 2649 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15309 : fout);
15310 : }
15311 : else
15312 : {
15313 0 : if (!collcollate || !collctype || colllocale ||
15314 0 : strcmp(collcollate, collctype) != 0)
15315 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15316 :
15317 0 : appendPQExpBufferStr(q, ", locale = ");
15318 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15319 : }
15320 :
15321 2649 : if (collicurules)
15322 : {
15323 0 : appendPQExpBufferStr(q, ", rules = ");
15324 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15325 : }
15326 : }
15327 46 : else if (collprovider[0] == 'c')
15328 : {
15329 46 : if (colllocale || collicurules || !collcollate || !collctype)
15330 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15331 :
15332 46 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15333 : {
15334 46 : appendPQExpBufferStr(q, ", locale = ");
15335 46 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15336 : }
15337 : else
15338 : {
15339 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15340 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15341 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15342 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15343 : }
15344 : }
15345 : else
15346 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15347 :
15348 : /*
15349 : * For binary upgrade, carry over the collation version. For normal
15350 : * dump/restore, omit the version, so that it is computed upon restore.
15351 : */
15352 2717 : if (dopt->binary_upgrade)
15353 : {
15354 : int i_collversion;
15355 :
15356 5 : i_collversion = PQfnumber(res, "collversion");
15357 5 : if (!PQgetisnull(res, 0, i_collversion))
15358 : {
15359 4 : appendPQExpBufferStr(q, ", version = ");
15360 4 : appendStringLiteralAH(q,
15361 : PQgetvalue(res, 0, i_collversion),
15362 : fout);
15363 : }
15364 : }
15365 :
15366 2717 : appendPQExpBufferStr(q, ");\n");
15367 :
15368 2717 : if (dopt->binary_upgrade)
15369 5 : binary_upgrade_extension_member(q, &collinfo->dobj,
15370 : "COLLATION", qcollname,
15371 5 : collinfo->dobj.namespace->dobj.name);
15372 :
15373 2717 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15374 2717 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15375 2717 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15376 : .namespace = collinfo->dobj.namespace->dobj.name,
15377 : .owner = collinfo->rolname,
15378 : .description = "COLLATION",
15379 : .section = SECTION_PRE_DATA,
15380 : .createStmt = q->data,
15381 : .dropStmt = delq->data));
15382 :
15383 : /* Dump Collation Comments */
15384 2717 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15385 2611 : dumpComment(fout, "COLLATION", qcollname,
15386 2611 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15387 2611 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15388 :
15389 2717 : PQclear(res);
15390 :
15391 2717 : destroyPQExpBuffer(query);
15392 2717 : destroyPQExpBuffer(q);
15393 2717 : destroyPQExpBuffer(delq);
15394 2717 : free(qcollname);
15395 : }
15396 :
15397 : /*
15398 : * dumpConversion
15399 : * write out a single conversion definition
15400 : */
15401 : static void
15402 422 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15403 : {
15404 422 : DumpOptions *dopt = fout->dopt;
15405 : PQExpBuffer query;
15406 : PQExpBuffer q;
15407 : PQExpBuffer delq;
15408 : char *qconvname;
15409 : PGresult *res;
15410 : int i_conforencoding;
15411 : int i_contoencoding;
15412 : int i_conproc;
15413 : int i_condefault;
15414 : const char *conforencoding;
15415 : const char *contoencoding;
15416 : const char *conproc;
15417 : bool condefault;
15418 :
15419 : /* Do nothing if not dumping schema */
15420 422 : if (!dopt->dumpSchema)
15421 6 : return;
15422 :
15423 416 : query = createPQExpBuffer();
15424 416 : q = createPQExpBuffer();
15425 416 : delq = createPQExpBuffer();
15426 :
15427 416 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15428 :
15429 : /* Get conversion-specific details */
15430 416 : appendPQExpBuffer(query, "SELECT "
15431 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15432 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15433 : "conproc, condefault "
15434 : "FROM pg_catalog.pg_conversion c "
15435 : "WHERE c.oid = '%u'::pg_catalog.oid",
15436 416 : convinfo->dobj.catId.oid);
15437 :
15438 416 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15439 :
15440 416 : i_conforencoding = PQfnumber(res, "conforencoding");
15441 416 : i_contoencoding = PQfnumber(res, "contoencoding");
15442 416 : i_conproc = PQfnumber(res, "conproc");
15443 416 : i_condefault = PQfnumber(res, "condefault");
15444 :
15445 416 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15446 416 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15447 416 : conproc = PQgetvalue(res, 0, i_conproc);
15448 416 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15449 :
15450 416 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15451 416 : fmtQualifiedDumpable(convinfo));
15452 :
15453 416 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15454 : (condefault) ? "DEFAULT " : "",
15455 416 : fmtQualifiedDumpable(convinfo));
15456 416 : appendStringLiteralAH(q, conforencoding, fout);
15457 416 : appendPQExpBufferStr(q, " TO ");
15458 416 : appendStringLiteralAH(q, contoencoding, fout);
15459 : /* regproc output is already sufficiently quoted */
15460 416 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15461 :
15462 416 : if (dopt->binary_upgrade)
15463 1 : binary_upgrade_extension_member(q, &convinfo->dobj,
15464 : "CONVERSION", qconvname,
15465 1 : convinfo->dobj.namespace->dobj.name);
15466 :
15467 416 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15468 416 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15469 416 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15470 : .namespace = convinfo->dobj.namespace->dobj.name,
15471 : .owner = convinfo->rolname,
15472 : .description = "CONVERSION",
15473 : .section = SECTION_PRE_DATA,
15474 : .createStmt = q->data,
15475 : .dropStmt = delq->data));
15476 :
15477 : /* Dump Conversion Comments */
15478 416 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15479 416 : dumpComment(fout, "CONVERSION", qconvname,
15480 416 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15481 416 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15482 :
15483 416 : PQclear(res);
15484 :
15485 416 : destroyPQExpBuffer(query);
15486 416 : destroyPQExpBuffer(q);
15487 416 : destroyPQExpBuffer(delq);
15488 416 : free(qconvname);
15489 : }
15490 :
15491 : /*
15492 : * format_aggregate_signature: generate aggregate name and argument list
15493 : *
15494 : * The argument type names are qualified if needed. The aggregate name
15495 : * is never qualified.
15496 : */
15497 : static char *
15498 285 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15499 : {
15500 : PQExpBufferData buf;
15501 : int j;
15502 :
15503 285 : initPQExpBuffer(&buf);
15504 285 : if (honor_quotes)
15505 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15506 : else
15507 285 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15508 :
15509 285 : if (agginfo->aggfn.nargs == 0)
15510 40 : appendPQExpBufferStr(&buf, "(*)");
15511 : else
15512 : {
15513 245 : appendPQExpBufferChar(&buf, '(');
15514 535 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15515 290 : appendPQExpBuffer(&buf, "%s%s",
15516 : (j > 0) ? ", " : "",
15517 : getFormattedTypeName(fout,
15518 290 : agginfo->aggfn.argtypes[j],
15519 : zeroIsError));
15520 245 : appendPQExpBufferChar(&buf, ')');
15521 : }
15522 285 : return buf.data;
15523 : }
15524 :
15525 : /*
15526 : * dumpAgg
15527 : * write out a single aggregate definition
15528 : */
15529 : static void
15530 292 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15531 : {
15532 292 : DumpOptions *dopt = fout->dopt;
15533 : PQExpBuffer query;
15534 : PQExpBuffer q;
15535 : PQExpBuffer delq;
15536 : PQExpBuffer details;
15537 : char *aggsig; /* identity signature */
15538 292 : char *aggfullsig = NULL; /* full signature */
15539 : char *aggsig_tag;
15540 : PGresult *res;
15541 : int i_agginitval;
15542 : int i_aggminitval;
15543 : const char *aggtransfn;
15544 : const char *aggfinalfn;
15545 : const char *aggcombinefn;
15546 : const char *aggserialfn;
15547 : const char *aggdeserialfn;
15548 : const char *aggmtransfn;
15549 : const char *aggminvtransfn;
15550 : const char *aggmfinalfn;
15551 : bool aggfinalextra;
15552 : bool aggmfinalextra;
15553 : char aggfinalmodify;
15554 : char aggmfinalmodify;
15555 : const char *aggsortop;
15556 : char *aggsortconvop;
15557 : char aggkind;
15558 : const char *aggtranstype;
15559 : const char *aggtransspace;
15560 : const char *aggmtranstype;
15561 : const char *aggmtransspace;
15562 : const char *agginitval;
15563 : const char *aggminitval;
15564 : const char *proparallel;
15565 : char defaultfinalmodify;
15566 :
15567 : /* Do nothing if not dumping schema */
15568 292 : if (!dopt->dumpSchema)
15569 7 : return;
15570 :
15571 285 : query = createPQExpBuffer();
15572 285 : q = createPQExpBuffer();
15573 285 : delq = createPQExpBuffer();
15574 285 : details = createPQExpBuffer();
15575 :
15576 285 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15577 : {
15578 : /* Set up query for aggregate-specific details */
15579 55 : appendPQExpBufferStr(query,
15580 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15581 :
15582 55 : appendPQExpBufferStr(query,
15583 : "SELECT "
15584 : "aggtransfn,\n"
15585 : "aggfinalfn,\n"
15586 : "aggtranstype::pg_catalog.regtype,\n"
15587 : "agginitval,\n"
15588 : "aggsortop,\n"
15589 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15590 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15591 :
15592 55 : if (fout->remoteVersion >= 90400)
15593 55 : appendPQExpBufferStr(query,
15594 : "aggkind,\n"
15595 : "aggmtransfn,\n"
15596 : "aggminvtransfn,\n"
15597 : "aggmfinalfn,\n"
15598 : "aggmtranstype::pg_catalog.regtype,\n"
15599 : "aggfinalextra,\n"
15600 : "aggmfinalextra,\n"
15601 : "aggtransspace,\n"
15602 : "aggmtransspace,\n"
15603 : "aggminitval,\n");
15604 : else
15605 0 : appendPQExpBufferStr(query,
15606 : "'n' AS aggkind,\n"
15607 : "'-' AS aggmtransfn,\n"
15608 : "'-' AS aggminvtransfn,\n"
15609 : "'-' AS aggmfinalfn,\n"
15610 : "0 AS aggmtranstype,\n"
15611 : "false AS aggfinalextra,\n"
15612 : "false AS aggmfinalextra,\n"
15613 : "0 AS aggtransspace,\n"
15614 : "0 AS aggmtransspace,\n"
15615 : "NULL AS aggminitval,\n");
15616 :
15617 55 : if (fout->remoteVersion >= 90600)
15618 55 : appendPQExpBufferStr(query,
15619 : "aggcombinefn,\n"
15620 : "aggserialfn,\n"
15621 : "aggdeserialfn,\n"
15622 : "proparallel,\n");
15623 : else
15624 0 : appendPQExpBufferStr(query,
15625 : "'-' AS aggcombinefn,\n"
15626 : "'-' AS aggserialfn,\n"
15627 : "'-' AS aggdeserialfn,\n"
15628 : "'u' AS proparallel,\n");
15629 :
15630 55 : if (fout->remoteVersion >= 110000)
15631 55 : appendPQExpBufferStr(query,
15632 : "aggfinalmodify,\n"
15633 : "aggmfinalmodify\n");
15634 : else
15635 0 : appendPQExpBufferStr(query,
15636 : "'0' AS aggfinalmodify,\n"
15637 : "'0' AS aggmfinalmodify\n");
15638 :
15639 55 : appendPQExpBufferStr(query,
15640 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15641 : "WHERE a.aggfnoid = p.oid "
15642 : "AND p.oid = $1");
15643 :
15644 55 : ExecuteSqlStatement(fout, query->data);
15645 :
15646 55 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15647 : }
15648 :
15649 285 : printfPQExpBuffer(query,
15650 : "EXECUTE dumpAgg('%u')",
15651 285 : agginfo->aggfn.dobj.catId.oid);
15652 :
15653 285 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15654 :
15655 285 : i_agginitval = PQfnumber(res, "agginitval");
15656 285 : i_aggminitval = PQfnumber(res, "aggminitval");
15657 :
15658 285 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15659 285 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15660 285 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15661 285 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15662 285 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15663 285 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15664 285 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15665 285 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15666 285 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15667 285 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15668 285 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15669 285 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15670 285 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15671 285 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15672 285 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15673 285 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15674 285 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15675 285 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15676 285 : agginitval = PQgetvalue(res, 0, i_agginitval);
15677 285 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15678 285 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15679 :
15680 : {
15681 : char *funcargs;
15682 : char *funciargs;
15683 :
15684 285 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15685 285 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15686 285 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15687 285 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15688 : }
15689 :
15690 285 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15691 :
15692 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15693 285 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15694 : /* replace omitted flags for old versions */
15695 285 : if (aggfinalmodify == '0')
15696 0 : aggfinalmodify = defaultfinalmodify;
15697 285 : if (aggmfinalmodify == '0')
15698 0 : aggmfinalmodify = defaultfinalmodify;
15699 :
15700 : /* regproc and regtype output is already sufficiently quoted */
15701 285 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15702 : aggtransfn, aggtranstype);
15703 :
15704 285 : if (strcmp(aggtransspace, "0") != 0)
15705 : {
15706 5 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15707 : aggtransspace);
15708 : }
15709 :
15710 285 : if (!PQgetisnull(res, 0, i_agginitval))
15711 : {
15712 207 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15713 207 : appendStringLiteralAH(details, agginitval, fout);
15714 : }
15715 :
15716 285 : if (strcmp(aggfinalfn, "-") != 0)
15717 : {
15718 132 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15719 : aggfinalfn);
15720 132 : if (aggfinalextra)
15721 10 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15722 132 : if (aggfinalmodify != defaultfinalmodify)
15723 : {
15724 32 : switch (aggfinalmodify)
15725 : {
15726 0 : case AGGMODIFY_READ_ONLY:
15727 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15728 0 : break;
15729 32 : case AGGMODIFY_SHAREABLE:
15730 32 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15731 32 : break;
15732 0 : case AGGMODIFY_READ_WRITE:
15733 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15734 0 : break;
15735 0 : default:
15736 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15737 : agginfo->aggfn.dobj.name);
15738 : break;
15739 : }
15740 : }
15741 : }
15742 :
15743 285 : if (strcmp(aggcombinefn, "-") != 0)
15744 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15745 :
15746 285 : if (strcmp(aggserialfn, "-") != 0)
15747 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15748 :
15749 285 : if (strcmp(aggdeserialfn, "-") != 0)
15750 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15751 :
15752 285 : if (strcmp(aggmtransfn, "-") != 0)
15753 : {
15754 30 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15755 : aggmtransfn,
15756 : aggminvtransfn,
15757 : aggmtranstype);
15758 : }
15759 :
15760 285 : if (strcmp(aggmtransspace, "0") != 0)
15761 : {
15762 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15763 : aggmtransspace);
15764 : }
15765 :
15766 285 : if (!PQgetisnull(res, 0, i_aggminitval))
15767 : {
15768 10 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15769 10 : appendStringLiteralAH(details, aggminitval, fout);
15770 : }
15771 :
15772 285 : if (strcmp(aggmfinalfn, "-") != 0)
15773 : {
15774 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15775 : aggmfinalfn);
15776 0 : if (aggmfinalextra)
15777 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15778 0 : if (aggmfinalmodify != defaultfinalmodify)
15779 : {
15780 0 : switch (aggmfinalmodify)
15781 : {
15782 0 : case AGGMODIFY_READ_ONLY:
15783 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15784 0 : break;
15785 0 : case AGGMODIFY_SHAREABLE:
15786 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15787 0 : break;
15788 0 : case AGGMODIFY_READ_WRITE:
15789 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15790 0 : break;
15791 0 : default:
15792 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15793 : agginfo->aggfn.dobj.name);
15794 : break;
15795 : }
15796 : }
15797 : }
15798 :
15799 285 : aggsortconvop = getFormattedOperatorName(aggsortop);
15800 285 : if (aggsortconvop)
15801 : {
15802 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15803 : aggsortconvop);
15804 0 : free(aggsortconvop);
15805 : }
15806 :
15807 285 : if (aggkind == AGGKIND_HYPOTHETICAL)
15808 5 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15809 :
15810 285 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15811 : {
15812 5 : if (proparallel[0] == PROPARALLEL_SAFE)
15813 5 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15814 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15815 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15816 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15817 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15818 : agginfo->aggfn.dobj.name);
15819 : }
15820 :
15821 285 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15822 285 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15823 : aggsig);
15824 :
15825 570 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15826 285 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15827 : aggfullsig ? aggfullsig : aggsig, details->data);
15828 :
15829 285 : if (dopt->binary_upgrade)
15830 49 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15831 : "AGGREGATE", aggsig,
15832 49 : agginfo->aggfn.dobj.namespace->dobj.name);
15833 :
15834 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15835 268 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15836 268 : agginfo->aggfn.dobj.dumpId,
15837 268 : ARCHIVE_OPTS(.tag = aggsig_tag,
15838 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15839 : .owner = agginfo->aggfn.rolname,
15840 : .description = "AGGREGATE",
15841 : .section = SECTION_PRE_DATA,
15842 : .createStmt = q->data,
15843 : .dropStmt = delq->data));
15844 :
15845 : /* Dump Aggregate Comments */
15846 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15847 10 : dumpComment(fout, "AGGREGATE", aggsig,
15848 10 : agginfo->aggfn.dobj.namespace->dobj.name,
15849 10 : agginfo->aggfn.rolname,
15850 10 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15851 :
15852 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15853 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15854 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15855 0 : agginfo->aggfn.rolname,
15856 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15857 :
15858 : /*
15859 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15860 : * command look like a function's GRANT; in particular this affects the
15861 : * syntax for zero-argument aggregates and ordered-set aggregates.
15862 : */
15863 285 : free(aggsig);
15864 :
15865 285 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15866 :
15867 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15868 18 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15869 : "FUNCTION", aggsig, NULL,
15870 18 : agginfo->aggfn.dobj.namespace->dobj.name,
15871 18 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15872 :
15873 285 : free(aggsig);
15874 285 : free(aggfullsig);
15875 285 : free(aggsig_tag);
15876 :
15877 285 : PQclear(res);
15878 :
15879 285 : destroyPQExpBuffer(query);
15880 285 : destroyPQExpBuffer(q);
15881 285 : destroyPQExpBuffer(delq);
15882 285 : destroyPQExpBuffer(details);
15883 : }
15884 :
15885 : /*
15886 : * dumpTSParser
15887 : * write out a single text search parser
15888 : */
15889 : static void
15890 41 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15891 : {
15892 41 : DumpOptions *dopt = fout->dopt;
15893 : PQExpBuffer q;
15894 : PQExpBuffer delq;
15895 : char *qprsname;
15896 :
15897 : /* Do nothing if not dumping schema */
15898 41 : if (!dopt->dumpSchema)
15899 6 : return;
15900 :
15901 35 : q = createPQExpBuffer();
15902 35 : delq = createPQExpBuffer();
15903 :
15904 35 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15905 :
15906 35 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15907 35 : fmtQualifiedDumpable(prsinfo));
15908 :
15909 35 : appendPQExpBuffer(q, " START = %s,\n",
15910 35 : convertTSFunction(fout, prsinfo->prsstart));
15911 35 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15912 35 : convertTSFunction(fout, prsinfo->prstoken));
15913 35 : appendPQExpBuffer(q, " END = %s,\n",
15914 35 : convertTSFunction(fout, prsinfo->prsend));
15915 35 : if (prsinfo->prsheadline != InvalidOid)
15916 3 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15917 3 : convertTSFunction(fout, prsinfo->prsheadline));
15918 35 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15919 35 : convertTSFunction(fout, prsinfo->prslextype));
15920 :
15921 35 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15922 35 : fmtQualifiedDumpable(prsinfo));
15923 :
15924 35 : if (dopt->binary_upgrade)
15925 1 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15926 : "TEXT SEARCH PARSER", qprsname,
15927 1 : prsinfo->dobj.namespace->dobj.name);
15928 :
15929 35 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15930 35 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15931 35 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15932 : .namespace = prsinfo->dobj.namespace->dobj.name,
15933 : .description = "TEXT SEARCH PARSER",
15934 : .section = SECTION_PRE_DATA,
15935 : .createStmt = q->data,
15936 : .dropStmt = delq->data));
15937 :
15938 : /* Dump Parser Comments */
15939 35 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15940 35 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15941 35 : prsinfo->dobj.namespace->dobj.name, "",
15942 35 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15943 :
15944 35 : destroyPQExpBuffer(q);
15945 35 : destroyPQExpBuffer(delq);
15946 35 : free(qprsname);
15947 : }
15948 :
15949 : /*
15950 : * dumpTSDictionary
15951 : * write out a single text search dictionary
15952 : */
15953 : static void
15954 179 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15955 : {
15956 179 : DumpOptions *dopt = fout->dopt;
15957 : PQExpBuffer q;
15958 : PQExpBuffer delq;
15959 : PQExpBuffer query;
15960 : char *qdictname;
15961 : PGresult *res;
15962 : char *nspname;
15963 : char *tmplname;
15964 :
15965 : /* Do nothing if not dumping schema */
15966 179 : if (!dopt->dumpSchema)
15967 6 : return;
15968 :
15969 173 : q = createPQExpBuffer();
15970 173 : delq = createPQExpBuffer();
15971 173 : query = createPQExpBuffer();
15972 :
15973 173 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15974 :
15975 : /* Fetch name and namespace of the dictionary's template */
15976 173 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15977 : "FROM pg_ts_template p, pg_namespace n "
15978 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15979 173 : dictinfo->dicttemplate);
15980 173 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15981 173 : nspname = PQgetvalue(res, 0, 0);
15982 173 : tmplname = PQgetvalue(res, 0, 1);
15983 :
15984 173 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15985 173 : fmtQualifiedDumpable(dictinfo));
15986 :
15987 173 : appendPQExpBufferStr(q, " TEMPLATE = ");
15988 173 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15989 173 : appendPQExpBufferStr(q, fmtId(tmplname));
15990 :
15991 173 : PQclear(res);
15992 :
15993 : /* the dictinitoption can be dumped straight into the command */
15994 173 : if (dictinfo->dictinitoption)
15995 138 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15996 :
15997 173 : appendPQExpBufferStr(q, " );\n");
15998 :
15999 173 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
16000 173 : fmtQualifiedDumpable(dictinfo));
16001 :
16002 173 : if (dopt->binary_upgrade)
16003 10 : binary_upgrade_extension_member(q, &dictinfo->dobj,
16004 : "TEXT SEARCH DICTIONARY", qdictname,
16005 10 : dictinfo->dobj.namespace->dobj.name);
16006 :
16007 173 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16008 173 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
16009 173 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
16010 : .namespace = dictinfo->dobj.namespace->dobj.name,
16011 : .owner = dictinfo->rolname,
16012 : .description = "TEXT SEARCH DICTIONARY",
16013 : .section = SECTION_PRE_DATA,
16014 : .createStmt = q->data,
16015 : .dropStmt = delq->data));
16016 :
16017 : /* Dump Dictionary Comments */
16018 173 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16019 128 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
16020 128 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
16021 128 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
16022 :
16023 173 : destroyPQExpBuffer(q);
16024 173 : destroyPQExpBuffer(delq);
16025 173 : destroyPQExpBuffer(query);
16026 173 : free(qdictname);
16027 : }
16028 :
16029 : /*
16030 : * dumpTSTemplate
16031 : * write out a single text search template
16032 : */
16033 : static void
16034 53 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
16035 : {
16036 53 : DumpOptions *dopt = fout->dopt;
16037 : PQExpBuffer q;
16038 : PQExpBuffer delq;
16039 : char *qtmplname;
16040 :
16041 : /* Do nothing if not dumping schema */
16042 53 : if (!dopt->dumpSchema)
16043 6 : return;
16044 :
16045 47 : q = createPQExpBuffer();
16046 47 : delq = createPQExpBuffer();
16047 :
16048 47 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
16049 :
16050 47 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
16051 47 : fmtQualifiedDumpable(tmplinfo));
16052 :
16053 47 : if (tmplinfo->tmplinit != InvalidOid)
16054 15 : appendPQExpBuffer(q, " INIT = %s,\n",
16055 15 : convertTSFunction(fout, tmplinfo->tmplinit));
16056 47 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
16057 47 : convertTSFunction(fout, tmplinfo->tmpllexize));
16058 :
16059 47 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
16060 47 : fmtQualifiedDumpable(tmplinfo));
16061 :
16062 47 : if (dopt->binary_upgrade)
16063 1 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
16064 : "TEXT SEARCH TEMPLATE", qtmplname,
16065 1 : tmplinfo->dobj.namespace->dobj.name);
16066 :
16067 47 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16068 47 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
16069 47 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
16070 : .namespace = tmplinfo->dobj.namespace->dobj.name,
16071 : .description = "TEXT SEARCH TEMPLATE",
16072 : .section = SECTION_PRE_DATA,
16073 : .createStmt = q->data,
16074 : .dropStmt = delq->data));
16075 :
16076 : /* Dump Template Comments */
16077 47 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16078 47 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
16079 47 : tmplinfo->dobj.namespace->dobj.name, "",
16080 47 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
16081 :
16082 47 : destroyPQExpBuffer(q);
16083 47 : destroyPQExpBuffer(delq);
16084 47 : free(qtmplname);
16085 : }
16086 :
16087 : /*
16088 : * dumpTSConfig
16089 : * write out a single text search configuration
16090 : */
16091 : static void
16092 154 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
16093 : {
16094 154 : DumpOptions *dopt = fout->dopt;
16095 : PQExpBuffer q;
16096 : PQExpBuffer delq;
16097 : PQExpBuffer query;
16098 : char *qcfgname;
16099 : PGresult *res;
16100 : char *nspname;
16101 : char *prsname;
16102 : int ntups,
16103 : i;
16104 : int i_tokenname;
16105 : int i_dictname;
16106 :
16107 : /* Do nothing if not dumping schema */
16108 154 : if (!dopt->dumpSchema)
16109 6 : return;
16110 :
16111 148 : q = createPQExpBuffer();
16112 148 : delq = createPQExpBuffer();
16113 148 : query = createPQExpBuffer();
16114 :
16115 148 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
16116 :
16117 : /* Fetch name and namespace of the config's parser */
16118 148 : appendPQExpBuffer(query, "SELECT nspname, prsname "
16119 : "FROM pg_ts_parser p, pg_namespace n "
16120 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
16121 148 : cfginfo->cfgparser);
16122 148 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16123 148 : nspname = PQgetvalue(res, 0, 0);
16124 148 : prsname = PQgetvalue(res, 0, 1);
16125 :
16126 148 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
16127 148 : fmtQualifiedDumpable(cfginfo));
16128 :
16129 148 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
16130 148 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
16131 :
16132 148 : PQclear(res);
16133 :
16134 148 : resetPQExpBuffer(query);
16135 148 : appendPQExpBuffer(query,
16136 : "SELECT\n"
16137 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
16138 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
16139 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
16140 : "FROM pg_catalog.pg_ts_config_map AS m\n"
16141 : "WHERE m.mapcfg = '%u'\n"
16142 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
16143 148 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
16144 :
16145 148 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16146 148 : ntups = PQntuples(res);
16147 :
16148 148 : i_tokenname = PQfnumber(res, "tokenname");
16149 148 : i_dictname = PQfnumber(res, "dictname");
16150 :
16151 3095 : for (i = 0; i < ntups; i++)
16152 : {
16153 2947 : char *tokenname = PQgetvalue(res, i, i_tokenname);
16154 2947 : char *dictname = PQgetvalue(res, i, i_dictname);
16155 :
16156 2947 : if (i == 0 ||
16157 2799 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
16158 : {
16159 : /* starting a new token type, so start a new command */
16160 2812 : if (i > 0)
16161 2664 : appendPQExpBufferStr(q, ";\n");
16162 2812 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16163 2812 : fmtQualifiedDumpable(cfginfo));
16164 : /* tokenname needs quoting, dictname does NOT */
16165 2812 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16166 : fmtId(tokenname), dictname);
16167 : }
16168 : else
16169 135 : appendPQExpBuffer(q, ", %s", dictname);
16170 : }
16171 :
16172 148 : if (ntups > 0)
16173 148 : appendPQExpBufferStr(q, ";\n");
16174 :
16175 148 : PQclear(res);
16176 :
16177 148 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16178 148 : fmtQualifiedDumpable(cfginfo));
16179 :
16180 148 : if (dopt->binary_upgrade)
16181 5 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16182 : "TEXT SEARCH CONFIGURATION", qcfgname,
16183 5 : cfginfo->dobj.namespace->dobj.name);
16184 :
16185 148 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16186 148 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16187 148 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16188 : .namespace = cfginfo->dobj.namespace->dobj.name,
16189 : .owner = cfginfo->rolname,
16190 : .description = "TEXT SEARCH CONFIGURATION",
16191 : .section = SECTION_PRE_DATA,
16192 : .createStmt = q->data,
16193 : .dropStmt = delq->data));
16194 :
16195 : /* Dump Configuration Comments */
16196 148 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16197 128 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16198 128 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16199 128 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16200 :
16201 148 : destroyPQExpBuffer(q);
16202 148 : destroyPQExpBuffer(delq);
16203 148 : destroyPQExpBuffer(query);
16204 148 : free(qcfgname);
16205 : }
16206 :
16207 : /*
16208 : * dumpForeignDataWrapper
16209 : * write out a single foreign-data wrapper definition
16210 : */
16211 : static void
16212 52 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16213 : {
16214 52 : DumpOptions *dopt = fout->dopt;
16215 : PQExpBuffer q;
16216 : PQExpBuffer delq;
16217 : char *qfdwname;
16218 :
16219 : /* Do nothing if not dumping schema */
16220 52 : if (!dopt->dumpSchema)
16221 7 : return;
16222 :
16223 45 : q = createPQExpBuffer();
16224 45 : delq = createPQExpBuffer();
16225 :
16226 45 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16227 :
16228 45 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16229 : qfdwname);
16230 :
16231 45 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16232 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16233 :
16234 45 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16235 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16236 :
16237 45 : if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16238 0 : appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16239 :
16240 45 : if (strlen(fdwinfo->fdwoptions) > 0)
16241 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16242 :
16243 45 : appendPQExpBufferStr(q, ";\n");
16244 :
16245 45 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16246 : qfdwname);
16247 :
16248 45 : if (dopt->binary_upgrade)
16249 2 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16250 : "FOREIGN DATA WRAPPER", qfdwname,
16251 : NULL);
16252 :
16253 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16254 45 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16255 45 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16256 : .owner = fdwinfo->rolname,
16257 : .description = "FOREIGN DATA WRAPPER",
16258 : .section = SECTION_PRE_DATA,
16259 : .createStmt = q->data,
16260 : .dropStmt = delq->data));
16261 :
16262 : /* Dump Foreign Data Wrapper Comments */
16263 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16264 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16265 0 : NULL, fdwinfo->rolname,
16266 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16267 :
16268 : /* Handle the ACL */
16269 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16270 31 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16271 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16272 31 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16273 :
16274 45 : free(qfdwname);
16275 :
16276 45 : destroyPQExpBuffer(q);
16277 45 : destroyPQExpBuffer(delq);
16278 : }
16279 :
16280 : /*
16281 : * dumpForeignServer
16282 : * write out a foreign server definition
16283 : */
16284 : static void
16285 56 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16286 : {
16287 56 : DumpOptions *dopt = fout->dopt;
16288 : PQExpBuffer q;
16289 : PQExpBuffer delq;
16290 : PQExpBuffer query;
16291 : PGresult *res;
16292 : char *qsrvname;
16293 : char *fdwname;
16294 :
16295 : /* Do nothing if not dumping schema */
16296 56 : if (!dopt->dumpSchema)
16297 9 : return;
16298 :
16299 47 : q = createPQExpBuffer();
16300 47 : delq = createPQExpBuffer();
16301 47 : query = createPQExpBuffer();
16302 :
16303 47 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16304 :
16305 : /* look up the foreign-data wrapper */
16306 47 : appendPQExpBuffer(query, "SELECT fdwname "
16307 : "FROM pg_foreign_data_wrapper w "
16308 : "WHERE w.oid = '%u'",
16309 47 : srvinfo->srvfdw);
16310 47 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16311 47 : fdwname = PQgetvalue(res, 0, 0);
16312 :
16313 47 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16314 47 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16315 : {
16316 0 : appendPQExpBufferStr(q, " TYPE ");
16317 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16318 : }
16319 47 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16320 : {
16321 0 : appendPQExpBufferStr(q, " VERSION ");
16322 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16323 : }
16324 :
16325 47 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16326 47 : appendPQExpBufferStr(q, fmtId(fdwname));
16327 :
16328 47 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16329 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16330 :
16331 47 : appendPQExpBufferStr(q, ";\n");
16332 :
16333 47 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16334 : qsrvname);
16335 :
16336 47 : if (dopt->binary_upgrade)
16337 2 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16338 : "SERVER", qsrvname, NULL);
16339 :
16340 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16341 47 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16342 47 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16343 : .owner = srvinfo->rolname,
16344 : .description = "SERVER",
16345 : .section = SECTION_PRE_DATA,
16346 : .createStmt = q->data,
16347 : .dropStmt = delq->data));
16348 :
16349 : /* Dump Foreign Server Comments */
16350 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16351 0 : dumpComment(fout, "SERVER", qsrvname,
16352 0 : NULL, srvinfo->rolname,
16353 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16354 :
16355 : /* Handle the ACL */
16356 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16357 31 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16358 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16359 31 : NULL, srvinfo->rolname, &srvinfo->dacl);
16360 :
16361 : /* Dump user mappings */
16362 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16363 47 : dumpUserMappings(fout,
16364 47 : srvinfo->dobj.name, NULL,
16365 47 : srvinfo->rolname,
16366 47 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16367 :
16368 47 : PQclear(res);
16369 :
16370 47 : free(qsrvname);
16371 :
16372 47 : destroyPQExpBuffer(q);
16373 47 : destroyPQExpBuffer(delq);
16374 47 : destroyPQExpBuffer(query);
16375 : }
16376 :
16377 : /*
16378 : * dumpUserMappings
16379 : *
16380 : * This routine is used to dump any user mappings associated with the
16381 : * server handed to this routine. Should be called after ArchiveEntry()
16382 : * for the server.
16383 : */
16384 : static void
16385 47 : dumpUserMappings(Archive *fout,
16386 : const char *servername, const char *namespace,
16387 : const char *owner,
16388 : CatalogId catalogId, DumpId dumpId)
16389 : {
16390 : PQExpBuffer q;
16391 : PQExpBuffer delq;
16392 : PQExpBuffer query;
16393 : PQExpBuffer tag;
16394 : PGresult *res;
16395 : int ntups;
16396 : int i_usename;
16397 : int i_umoptions;
16398 : int i;
16399 :
16400 47 : q = createPQExpBuffer();
16401 47 : tag = createPQExpBuffer();
16402 47 : delq = createPQExpBuffer();
16403 47 : query = createPQExpBuffer();
16404 :
16405 : /*
16406 : * We read from the publicly accessible view pg_user_mappings, so as not
16407 : * to fail if run by a non-superuser. Note that the view will show
16408 : * umoptions as null if the user hasn't got privileges for the associated
16409 : * server; this means that pg_dump will dump such a mapping, but with no
16410 : * OPTIONS clause. A possible alternative is to skip such mappings
16411 : * altogether, but it's not clear that that's an improvement.
16412 : */
16413 47 : appendPQExpBuffer(query,
16414 : "SELECT usename, "
16415 : "array_to_string(ARRAY("
16416 : "SELECT quote_ident(option_name) || ' ' || "
16417 : "quote_literal(option_value) "
16418 : "FROM pg_options_to_table(umoptions) "
16419 : "ORDER BY option_name"
16420 : "), E',\n ') AS umoptions "
16421 : "FROM pg_user_mappings "
16422 : "WHERE srvid = '%u' "
16423 : "ORDER BY usename",
16424 : catalogId.oid);
16425 :
16426 47 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16427 :
16428 47 : ntups = PQntuples(res);
16429 47 : i_usename = PQfnumber(res, "usename");
16430 47 : i_umoptions = PQfnumber(res, "umoptions");
16431 :
16432 78 : for (i = 0; i < ntups; i++)
16433 : {
16434 : char *usename;
16435 : char *umoptions;
16436 :
16437 31 : usename = PQgetvalue(res, i, i_usename);
16438 31 : umoptions = PQgetvalue(res, i, i_umoptions);
16439 :
16440 31 : resetPQExpBuffer(q);
16441 31 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16442 31 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16443 :
16444 31 : if (umoptions && strlen(umoptions) > 0)
16445 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16446 :
16447 31 : appendPQExpBufferStr(q, ";\n");
16448 :
16449 31 : resetPQExpBuffer(delq);
16450 31 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16451 31 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16452 :
16453 31 : resetPQExpBuffer(tag);
16454 31 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16455 : usename, servername);
16456 :
16457 31 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16458 31 : ARCHIVE_OPTS(.tag = tag->data,
16459 : .namespace = namespace,
16460 : .owner = owner,
16461 : .description = "USER MAPPING",
16462 : .section = SECTION_PRE_DATA,
16463 : .createStmt = q->data,
16464 : .dropStmt = delq->data));
16465 : }
16466 :
16467 47 : PQclear(res);
16468 :
16469 47 : destroyPQExpBuffer(query);
16470 47 : destroyPQExpBuffer(delq);
16471 47 : destroyPQExpBuffer(tag);
16472 47 : destroyPQExpBuffer(q);
16473 47 : }
16474 :
16475 : /*
16476 : * Write out default privileges information
16477 : */
16478 : static void
16479 160 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16480 : {
16481 160 : DumpOptions *dopt = fout->dopt;
16482 : PQExpBuffer q;
16483 : PQExpBuffer tag;
16484 : const char *type;
16485 :
16486 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16487 160 : if (!dopt->dumpSchema || dopt->aclsSkip)
16488 28 : return;
16489 :
16490 132 : q = createPQExpBuffer();
16491 132 : tag = createPQExpBuffer();
16492 :
16493 132 : switch (daclinfo->defaclobjtype)
16494 : {
16495 61 : case DEFACLOBJ_RELATION:
16496 61 : type = "TABLES";
16497 61 : break;
16498 0 : case DEFACLOBJ_SEQUENCE:
16499 0 : type = "SEQUENCES";
16500 0 : break;
16501 61 : case DEFACLOBJ_FUNCTION:
16502 61 : type = "FUNCTIONS";
16503 61 : break;
16504 10 : case DEFACLOBJ_TYPE:
16505 10 : type = "TYPES";
16506 10 : break;
16507 0 : case DEFACLOBJ_NAMESPACE:
16508 0 : type = "SCHEMAS";
16509 0 : break;
16510 0 : case DEFACLOBJ_LARGEOBJECT:
16511 0 : type = "LARGE OBJECTS";
16512 0 : break;
16513 0 : default:
16514 : /* shouldn't get here */
16515 0 : pg_fatal("unrecognized object type in default privileges: %d",
16516 : (int) daclinfo->defaclobjtype);
16517 : type = ""; /* keep compiler quiet */
16518 : }
16519 :
16520 132 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16521 :
16522 : /* build the actual command(s) for this tuple */
16523 132 : if (!buildDefaultACLCommands(type,
16524 132 : daclinfo->dobj.namespace != NULL ?
16525 62 : daclinfo->dobj.namespace->dobj.name : NULL,
16526 132 : daclinfo->dacl.acl,
16527 132 : daclinfo->dacl.acldefault,
16528 132 : daclinfo->defaclrole,
16529 : fout->remoteVersion,
16530 : q))
16531 0 : pg_fatal("could not parse default ACL list (%s)",
16532 : daclinfo->dacl.acl);
16533 :
16534 132 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16535 132 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16536 132 : ARCHIVE_OPTS(.tag = tag->data,
16537 : .namespace = daclinfo->dobj.namespace ?
16538 : daclinfo->dobj.namespace->dobj.name : NULL,
16539 : .owner = daclinfo->defaclrole,
16540 : .description = "DEFAULT ACL",
16541 : .section = SECTION_POST_DATA,
16542 : .createStmt = q->data));
16543 :
16544 132 : destroyPQExpBuffer(tag);
16545 132 : destroyPQExpBuffer(q);
16546 : }
16547 :
16548 : /*----------
16549 : * Write out grant/revoke information
16550 : *
16551 : * 'objDumpId' is the dump ID of the underlying object.
16552 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16553 : * or InvalidDumpId if there is no need for a second dependency.
16554 : * 'type' must be one of
16555 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16556 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16557 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16558 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16559 : * (Currently we assume that subname is only provided for table columns.)
16560 : * 'nspname' is the namespace the object is in (NULL if none).
16561 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16562 : * to use the default for the object type.
16563 : * 'owner' is the owner, NULL if there is no owner (for languages).
16564 : * 'dacl' is the DumpableAcl struct for the object.
16565 : *
16566 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16567 : * no ACL entry was created.
16568 : *----------
16569 : */
16570 : static DumpId
16571 42985 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16572 : const char *type, const char *name, const char *subname,
16573 : const char *nspname, const char *tag, const char *owner,
16574 : const DumpableAcl *dacl)
16575 : {
16576 42985 : DumpId aclDumpId = InvalidDumpId;
16577 42985 : DumpOptions *dopt = fout->dopt;
16578 42985 : const char *acls = dacl->acl;
16579 42985 : const char *acldefault = dacl->acldefault;
16580 42985 : char privtype = dacl->privtype;
16581 42985 : const char *initprivs = dacl->initprivs;
16582 : const char *baseacls;
16583 : PQExpBuffer sql;
16584 :
16585 : /* Do nothing if ACL dump is not enabled */
16586 42985 : if (dopt->aclsSkip)
16587 341 : return InvalidDumpId;
16588 :
16589 : /* --data-only skips ACLs *except* large object ACLs */
16590 42644 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16591 0 : return InvalidDumpId;
16592 :
16593 42644 : sql = createPQExpBuffer();
16594 :
16595 : /*
16596 : * In binary upgrade mode, we don't run an extension's script but instead
16597 : * dump out the objects independently and then recreate them. To preserve
16598 : * any initial privileges which were set on extension objects, we need to
16599 : * compute the set of GRANT and REVOKE commands necessary to get from the
16600 : * default privileges of an object to its initial privileges as recorded
16601 : * in pg_init_privs.
16602 : *
16603 : * At restore time, we apply these commands after having called
16604 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16605 : * copy the results into pg_init_privs. This is how we preserve the
16606 : * contents of that catalog across binary upgrades.
16607 : */
16608 42644 : if (dopt->binary_upgrade && privtype == 'e' &&
16609 13 : initprivs && *initprivs != '\0')
16610 : {
16611 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16612 13 : if (!buildACLCommands(name, subname, nspname, type,
16613 : initprivs, acldefault, owner,
16614 : "", fout->remoteVersion, sql))
16615 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16616 : initprivs, acldefault, name, type);
16617 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16618 : }
16619 :
16620 : /*
16621 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16622 : * actual current ACL, starting from the initprivs if given, else from the
16623 : * object-type-specific default. Also, while buildACLCommands will assume
16624 : * that a NULL/empty acls string means it needn't do anything, what that
16625 : * actually represents is the object-type-specific default; so we need to
16626 : * substitute the acldefault string to get the right results in that case.
16627 : */
16628 42644 : if (initprivs && *initprivs != '\0')
16629 : {
16630 40726 : baseacls = initprivs;
16631 40726 : if (acls == NULL || *acls == '\0')
16632 17 : acls = acldefault;
16633 : }
16634 : else
16635 1918 : baseacls = acldefault;
16636 :
16637 42644 : if (!buildACLCommands(name, subname, nspname, type,
16638 : acls, baseacls, owner,
16639 : "", fout->remoteVersion, sql))
16640 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16641 : acls, baseacls, name, type);
16642 :
16643 42644 : if (sql->len > 0)
16644 : {
16645 1921 : PQExpBuffer tagbuf = createPQExpBuffer();
16646 : DumpId aclDeps[2];
16647 1921 : int nDeps = 0;
16648 :
16649 1921 : if (tag)
16650 0 : appendPQExpBufferStr(tagbuf, tag);
16651 1921 : else if (subname)
16652 1047 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16653 : else
16654 874 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16655 :
16656 1921 : aclDeps[nDeps++] = objDumpId;
16657 1921 : if (altDumpId != InvalidDumpId)
16658 963 : aclDeps[nDeps++] = altDumpId;
16659 :
16660 1921 : aclDumpId = createDumpId();
16661 :
16662 1921 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16663 1921 : ARCHIVE_OPTS(.tag = tagbuf->data,
16664 : .namespace = nspname,
16665 : .owner = owner,
16666 : .description = "ACL",
16667 : .section = SECTION_NONE,
16668 : .createStmt = sql->data,
16669 : .deps = aclDeps,
16670 : .nDeps = nDeps));
16671 :
16672 1921 : destroyPQExpBuffer(tagbuf);
16673 : }
16674 :
16675 42644 : destroyPQExpBuffer(sql);
16676 :
16677 42644 : return aclDumpId;
16678 : }
16679 :
16680 : /*
16681 : * dumpSecLabel
16682 : *
16683 : * This routine is used to dump any security labels associated with the
16684 : * object handed to this routine. The routine takes the object type
16685 : * and object name (ready to print, except for schema decoration), plus
16686 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16687 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16688 : * plus the dump ID for the object (for setting a dependency).
16689 : * If a matching pg_seclabel entry is found, it is dumped.
16690 : *
16691 : * Note: although this routine takes a dumpId for dependency purposes,
16692 : * that purpose is just to mark the dependency in the emitted dump file
16693 : * for possible future use by pg_restore. We do NOT use it for determining
16694 : * ordering of the label in the dump file, because this routine is called
16695 : * after dependency sorting occurs. This routine should be called just after
16696 : * calling ArchiveEntry() for the specified object.
16697 : */
16698 : static void
16699 10 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16700 : const char *namespace, const char *owner,
16701 : CatalogId catalogId, int subid, DumpId dumpId)
16702 : {
16703 10 : DumpOptions *dopt = fout->dopt;
16704 : SecLabelItem *labels;
16705 : int nlabels;
16706 : int i;
16707 : PQExpBuffer query;
16708 :
16709 : /* do nothing, if --no-security-labels is supplied */
16710 10 : if (dopt->no_security_labels)
16711 0 : return;
16712 :
16713 : /*
16714 : * Security labels are schema not data ... except large object labels are
16715 : * data
16716 : */
16717 10 : if (strcmp(type, "LARGE OBJECT") != 0)
16718 : {
16719 0 : if (!dopt->dumpSchema)
16720 0 : return;
16721 : }
16722 : else
16723 : {
16724 : /* We do dump large object security labels in binary-upgrade mode */
16725 10 : if (!dopt->dumpData && !dopt->binary_upgrade)
16726 0 : return;
16727 : }
16728 :
16729 : /* Search for security labels associated with catalogId, using table */
16730 10 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16731 :
16732 10 : query = createPQExpBuffer();
16733 :
16734 15 : for (i = 0; i < nlabels; i++)
16735 : {
16736 : /*
16737 : * Ignore label entries for which the subid doesn't match.
16738 : */
16739 5 : if (labels[i].objsubid != subid)
16740 0 : continue;
16741 :
16742 5 : appendPQExpBuffer(query,
16743 : "SECURITY LABEL FOR %s ON %s ",
16744 5 : fmtId(labels[i].provider), type);
16745 5 : if (namespace && *namespace)
16746 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16747 5 : appendPQExpBuffer(query, "%s IS ", name);
16748 5 : appendStringLiteralAH(query, labels[i].label, fout);
16749 5 : appendPQExpBufferStr(query, ";\n");
16750 : }
16751 :
16752 10 : if (query->len > 0)
16753 : {
16754 5 : PQExpBuffer tag = createPQExpBuffer();
16755 :
16756 5 : appendPQExpBuffer(tag, "%s %s", type, name);
16757 5 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16758 5 : ARCHIVE_OPTS(.tag = tag->data,
16759 : .namespace = namespace,
16760 : .owner = owner,
16761 : .description = "SECURITY LABEL",
16762 : .section = SECTION_NONE,
16763 : .createStmt = query->data,
16764 : .deps = &dumpId,
16765 : .nDeps = 1));
16766 5 : destroyPQExpBuffer(tag);
16767 : }
16768 :
16769 10 : destroyPQExpBuffer(query);
16770 : }
16771 :
16772 : /*
16773 : * dumpTableSecLabel
16774 : *
16775 : * As above, but dump security label for both the specified table (or view)
16776 : * and its columns.
16777 : */
16778 : static void
16779 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16780 : {
16781 0 : DumpOptions *dopt = fout->dopt;
16782 : SecLabelItem *labels;
16783 : int nlabels;
16784 : int i;
16785 : PQExpBuffer query;
16786 : PQExpBuffer target;
16787 :
16788 : /* do nothing, if --no-security-labels is supplied */
16789 0 : if (dopt->no_security_labels)
16790 0 : return;
16791 :
16792 : /* SecLabel are SCHEMA not data */
16793 0 : if (!dopt->dumpSchema)
16794 0 : return;
16795 :
16796 : /* Search for comments associated with relation, using table */
16797 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16798 0 : tbinfo->dobj.catId.oid,
16799 : &labels);
16800 :
16801 : /* If security labels exist, build SECURITY LABEL statements */
16802 0 : if (nlabels <= 0)
16803 0 : return;
16804 :
16805 0 : query = createPQExpBuffer();
16806 0 : target = createPQExpBuffer();
16807 :
16808 0 : for (i = 0; i < nlabels; i++)
16809 : {
16810 : const char *colname;
16811 0 : const char *provider = labels[i].provider;
16812 0 : const char *label = labels[i].label;
16813 0 : int objsubid = labels[i].objsubid;
16814 :
16815 0 : resetPQExpBuffer(target);
16816 0 : if (objsubid == 0)
16817 : {
16818 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16819 0 : fmtQualifiedDumpable(tbinfo));
16820 : }
16821 : else
16822 : {
16823 0 : colname = getAttrName(objsubid, tbinfo);
16824 : /* first fmtXXX result must be consumed before calling again */
16825 0 : appendPQExpBuffer(target, "COLUMN %s",
16826 0 : fmtQualifiedDumpable(tbinfo));
16827 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16828 : }
16829 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16830 : fmtId(provider), target->data);
16831 0 : appendStringLiteralAH(query, label, fout);
16832 0 : appendPQExpBufferStr(query, ";\n");
16833 : }
16834 0 : if (query->len > 0)
16835 : {
16836 0 : resetPQExpBuffer(target);
16837 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16838 0 : fmtId(tbinfo->dobj.name));
16839 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16840 0 : ARCHIVE_OPTS(.tag = target->data,
16841 : .namespace = tbinfo->dobj.namespace->dobj.name,
16842 : .owner = tbinfo->rolname,
16843 : .description = "SECURITY LABEL",
16844 : .section = SECTION_NONE,
16845 : .createStmt = query->data,
16846 : .deps = &(tbinfo->dobj.dumpId),
16847 : .nDeps = 1));
16848 : }
16849 0 : destroyPQExpBuffer(query);
16850 0 : destroyPQExpBuffer(target);
16851 : }
16852 :
16853 : /*
16854 : * findSecLabels
16855 : *
16856 : * Find the security label(s), if any, associated with the given object.
16857 : * All the objsubid values associated with the given classoid/objoid are
16858 : * found with one search.
16859 : */
16860 : static int
16861 10 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16862 : {
16863 10 : SecLabelItem *middle = NULL;
16864 : SecLabelItem *low;
16865 : SecLabelItem *high;
16866 : int nmatch;
16867 :
16868 10 : if (nseclabels <= 0) /* no labels, so no match is possible */
16869 : {
16870 0 : *items = NULL;
16871 0 : return 0;
16872 : }
16873 :
16874 : /*
16875 : * Do binary search to find some item matching the object.
16876 : */
16877 10 : low = &seclabels[0];
16878 10 : high = &seclabels[nseclabels - 1];
16879 15 : while (low <= high)
16880 : {
16881 10 : middle = low + (high - low) / 2;
16882 :
16883 10 : if (classoid < middle->classoid)
16884 0 : high = middle - 1;
16885 10 : else if (classoid > middle->classoid)
16886 0 : low = middle + 1;
16887 10 : else if (objoid < middle->objoid)
16888 5 : high = middle - 1;
16889 5 : else if (objoid > middle->objoid)
16890 0 : low = middle + 1;
16891 : else
16892 5 : break; /* found a match */
16893 : }
16894 :
16895 10 : if (low > high) /* no matches */
16896 : {
16897 5 : *items = NULL;
16898 5 : return 0;
16899 : }
16900 :
16901 : /*
16902 : * Now determine how many items match the object. The search loop
16903 : * invariant still holds: only items between low and high inclusive could
16904 : * match.
16905 : */
16906 5 : nmatch = 1;
16907 5 : while (middle > low)
16908 : {
16909 0 : if (classoid != middle[-1].classoid ||
16910 0 : objoid != middle[-1].objoid)
16911 : break;
16912 0 : middle--;
16913 0 : nmatch++;
16914 : }
16915 :
16916 5 : *items = middle;
16917 :
16918 5 : middle += nmatch;
16919 5 : while (middle <= high)
16920 : {
16921 0 : if (classoid != middle->classoid ||
16922 0 : objoid != middle->objoid)
16923 : break;
16924 0 : middle++;
16925 0 : nmatch++;
16926 : }
16927 :
16928 5 : return nmatch;
16929 : }
16930 :
16931 : /*
16932 : * collectSecLabels
16933 : *
16934 : * Construct a table of all security labels available for database objects;
16935 : * also set the has-seclabel component flag for each relevant object.
16936 : *
16937 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16938 : */
16939 : static void
16940 259 : collectSecLabels(Archive *fout)
16941 : {
16942 : PGresult *res;
16943 : PQExpBuffer query;
16944 : int i_label;
16945 : int i_provider;
16946 : int i_classoid;
16947 : int i_objoid;
16948 : int i_objsubid;
16949 : int ntups;
16950 : int i;
16951 : DumpableObject *dobj;
16952 :
16953 259 : query = createPQExpBuffer();
16954 :
16955 259 : appendPQExpBufferStr(query,
16956 : "SELECT label, provider, classoid, objoid, objsubid "
16957 : "FROM pg_catalog.pg_seclabels "
16958 : "ORDER BY classoid, objoid, objsubid");
16959 :
16960 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16961 :
16962 : /* Construct lookup table containing OIDs in numeric form */
16963 259 : i_label = PQfnumber(res, "label");
16964 259 : i_provider = PQfnumber(res, "provider");
16965 259 : i_classoid = PQfnumber(res, "classoid");
16966 259 : i_objoid = PQfnumber(res, "objoid");
16967 259 : i_objsubid = PQfnumber(res, "objsubid");
16968 :
16969 259 : ntups = PQntuples(res);
16970 :
16971 259 : seclabels = pg_malloc_array(SecLabelItem, ntups);
16972 259 : nseclabels = 0;
16973 259 : dobj = NULL;
16974 :
16975 264 : for (i = 0; i < ntups; i++)
16976 : {
16977 : CatalogId objId;
16978 : int subid;
16979 :
16980 5 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16981 5 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16982 5 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16983 :
16984 : /* We needn't remember labels that don't match any dumpable object */
16985 5 : if (dobj == NULL ||
16986 0 : dobj->catId.tableoid != objId.tableoid ||
16987 0 : dobj->catId.oid != objId.oid)
16988 5 : dobj = findObjectByCatalogId(objId);
16989 5 : if (dobj == NULL)
16990 0 : continue;
16991 :
16992 : /*
16993 : * Labels on columns of composite types are linked to the type's
16994 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16995 : * in the type's own DumpableObject.
16996 : */
16997 5 : if (subid != 0 && dobj->objType == DO_TABLE &&
16998 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16999 0 : {
17000 : TypeInfo *cTypeInfo;
17001 :
17002 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
17003 0 : if (cTypeInfo)
17004 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
17005 : }
17006 : else
17007 5 : dobj->components |= DUMP_COMPONENT_SECLABEL;
17008 :
17009 5 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
17010 5 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
17011 5 : seclabels[nseclabels].classoid = objId.tableoid;
17012 5 : seclabels[nseclabels].objoid = objId.oid;
17013 5 : seclabels[nseclabels].objsubid = subid;
17014 5 : nseclabels++;
17015 : }
17016 :
17017 259 : PQclear(res);
17018 259 : destroyPQExpBuffer(query);
17019 259 : }
17020 :
17021 : /*
17022 : * dumpTable
17023 : * write out to fout the declarations (not data) of a user-defined table
17024 : */
17025 : static void
17026 44004 : dumpTable(Archive *fout, const TableInfo *tbinfo)
17027 : {
17028 44004 : DumpOptions *dopt = fout->dopt;
17029 44004 : DumpId tableAclDumpId = InvalidDumpId;
17030 : char *namecopy;
17031 :
17032 : /* Do nothing if not dumping schema */
17033 44004 : if (!dopt->dumpSchema)
17034 1588 : return;
17035 :
17036 42416 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17037 : {
17038 7226 : if (tbinfo->relkind == RELKIND_SEQUENCE)
17039 384 : dumpSequence(fout, tbinfo);
17040 : else
17041 6842 : dumpTableSchema(fout, tbinfo);
17042 : }
17043 :
17044 : /* Handle the ACL here */
17045 42416 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
17046 42416 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
17047 : {
17048 : const char *objtype;
17049 :
17050 36012 : switch (tbinfo->relkind)
17051 : {
17052 99 : case RELKIND_SEQUENCE:
17053 99 : objtype = "SEQUENCE";
17054 99 : break;
17055 37 : case RELKIND_PROPGRAPH:
17056 37 : objtype = "PROPERTY GRAPH";
17057 37 : break;
17058 35876 : default:
17059 35876 : objtype = "TABLE";
17060 35876 : break;
17061 : }
17062 :
17063 : tableAclDumpId =
17064 36012 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
17065 : objtype, namecopy, NULL,
17066 36012 : tbinfo->dobj.namespace->dobj.name,
17067 36012 : NULL, tbinfo->rolname, &tbinfo->dacl);
17068 : }
17069 :
17070 : /*
17071 : * Handle column ACLs, if any. Note: we pull these with a separate query
17072 : * rather than trying to fetch them during getTableAttrs, so that we won't
17073 : * miss ACLs on system columns. Doing it this way also allows us to dump
17074 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
17075 : */
17076 42416 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
17077 : {
17078 354 : PQExpBuffer query = createPQExpBuffer();
17079 : PGresult *res;
17080 : int i;
17081 :
17082 354 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
17083 : {
17084 : /* Set up query for column ACLs */
17085 233 : appendPQExpBufferStr(query,
17086 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
17087 :
17088 233 : if (fout->remoteVersion >= 90600)
17089 : {
17090 : /*
17091 : * In principle we should call acldefault('c', relowner) to
17092 : * get the default ACL for a column. However, we don't
17093 : * currently store the numeric OID of the relowner in
17094 : * TableInfo. We could convert the owner name using regrole,
17095 : * but that creates a risk of failure due to concurrent role
17096 : * renames. Given that the default ACL for columns is empty
17097 : * and is likely to stay that way, it's not worth extra cycles
17098 : * and risk to avoid hard-wiring that knowledge here.
17099 : */
17100 233 : appendPQExpBufferStr(query,
17101 : "SELECT at.attname, "
17102 : "at.attacl, "
17103 : "'{}' AS acldefault, "
17104 : "pip.privtype, pip.initprivs "
17105 : "FROM pg_catalog.pg_attribute at "
17106 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
17107 : "(at.attrelid = pip.objoid "
17108 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
17109 : "AND at.attnum = pip.objsubid) "
17110 : "WHERE at.attrelid = $1 AND "
17111 : "NOT at.attisdropped "
17112 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
17113 : "ORDER BY at.attnum");
17114 : }
17115 : else
17116 : {
17117 0 : appendPQExpBufferStr(query,
17118 : "SELECT attname, attacl, '{}' AS acldefault, "
17119 : "NULL AS privtype, NULL AS initprivs "
17120 : "FROM pg_catalog.pg_attribute "
17121 : "WHERE attrelid = $1 AND NOT attisdropped "
17122 : "AND attacl IS NOT NULL "
17123 : "ORDER BY attnum");
17124 : }
17125 :
17126 233 : ExecuteSqlStatement(fout, query->data);
17127 :
17128 233 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
17129 : }
17130 :
17131 354 : printfPQExpBuffer(query,
17132 : "EXECUTE getColumnACLs('%u')",
17133 354 : tbinfo->dobj.catId.oid);
17134 :
17135 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17136 :
17137 6231 : for (i = 0; i < PQntuples(res); i++)
17138 : {
17139 5877 : char *attname = PQgetvalue(res, i, 0);
17140 5877 : char *attacl = PQgetvalue(res, i, 1);
17141 5877 : char *acldefault = PQgetvalue(res, i, 2);
17142 5877 : char privtype = *(PQgetvalue(res, i, 3));
17143 5877 : char *initprivs = PQgetvalue(res, i, 4);
17144 : DumpableAcl coldacl;
17145 : char *attnamecopy;
17146 :
17147 5877 : coldacl.acl = attacl;
17148 5877 : coldacl.acldefault = acldefault;
17149 5877 : coldacl.privtype = privtype;
17150 5877 : coldacl.initprivs = initprivs;
17151 5877 : attnamecopy = pg_strdup(fmtId(attname));
17152 :
17153 : /*
17154 : * Column's GRANT type is always TABLE. Each column ACL depends
17155 : * on the table-level ACL, since we can restore column ACLs in
17156 : * parallel but the table-level ACL has to be done first.
17157 : */
17158 5877 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
17159 : "TABLE", namecopy, attnamecopy,
17160 5877 : tbinfo->dobj.namespace->dobj.name,
17161 5877 : NULL, tbinfo->rolname, &coldacl);
17162 5877 : free(attnamecopy);
17163 : }
17164 354 : PQclear(res);
17165 354 : destroyPQExpBuffer(query);
17166 : }
17167 :
17168 42416 : free(namecopy);
17169 : }
17170 :
17171 : /*
17172 : * Create the AS clause for a view or materialized view. The semicolon is
17173 : * stripped because a materialized view must add a WITH NO DATA clause.
17174 : *
17175 : * This returns a new buffer which must be freed by the caller.
17176 : */
17177 : static PQExpBuffer
17178 889 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
17179 : {
17180 889 : PQExpBuffer query = createPQExpBuffer();
17181 889 : PQExpBuffer result = createPQExpBuffer();
17182 : PGresult *res;
17183 : int len;
17184 :
17185 : /* Fetch the view definition */
17186 889 : appendPQExpBuffer(query,
17187 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17188 889 : tbinfo->dobj.catId.oid);
17189 :
17190 889 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17191 :
17192 889 : if (PQntuples(res) != 1)
17193 : {
17194 0 : if (PQntuples(res) < 1)
17195 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17196 : tbinfo->dobj.name);
17197 : else
17198 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17199 : tbinfo->dobj.name);
17200 : }
17201 :
17202 889 : len = PQgetlength(res, 0, 0);
17203 :
17204 889 : if (len == 0)
17205 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17206 : tbinfo->dobj.name);
17207 :
17208 : /* Strip off the trailing semicolon so that other things may follow. */
17209 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17210 889 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17211 :
17212 889 : PQclear(res);
17213 889 : destroyPQExpBuffer(query);
17214 :
17215 889 : return result;
17216 : }
17217 :
17218 : /*
17219 : * Create a dummy AS clause for a view. This is used when the real view
17220 : * definition has to be postponed because of circular dependencies.
17221 : * We must duplicate the view's external properties -- column names and types
17222 : * (including collation) -- so that it works for subsequent references.
17223 : *
17224 : * This returns a new buffer which must be freed by the caller.
17225 : */
17226 : static PQExpBuffer
17227 20 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17228 : {
17229 20 : PQExpBuffer result = createPQExpBuffer();
17230 : int j;
17231 :
17232 20 : appendPQExpBufferStr(result, "SELECT");
17233 :
17234 40 : for (j = 0; j < tbinfo->numatts; j++)
17235 : {
17236 20 : if (j > 0)
17237 10 : appendPQExpBufferChar(result, ',');
17238 20 : appendPQExpBufferStr(result, "\n ");
17239 :
17240 20 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17241 :
17242 : /*
17243 : * Must add collation if not default for the type, because CREATE OR
17244 : * REPLACE VIEW won't change it
17245 : */
17246 20 : if (OidIsValid(tbinfo->attcollation[j]))
17247 : {
17248 : CollInfo *coll;
17249 :
17250 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17251 0 : if (coll)
17252 0 : appendPQExpBuffer(result, " COLLATE %s",
17253 0 : fmtQualifiedDumpable(coll));
17254 : }
17255 :
17256 20 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17257 : }
17258 :
17259 20 : return result;
17260 : }
17261 :
17262 : /*
17263 : * dumpTableSchema
17264 : * write the declaration (not data) of one user-defined table or view
17265 : */
17266 : static void
17267 6842 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17268 : {
17269 6842 : DumpOptions *dopt = fout->dopt;
17270 6842 : PQExpBuffer q = createPQExpBuffer();
17271 6842 : PQExpBuffer delq = createPQExpBuffer();
17272 6842 : PQExpBuffer extra = createPQExpBuffer();
17273 : char *qrelname;
17274 : char *qualrelname;
17275 : int numParents;
17276 : TableInfo **parents;
17277 : int actual_atts; /* number of attrs in this CREATE statement */
17278 : const char *reltypename;
17279 : char *storage;
17280 : int j,
17281 : k;
17282 :
17283 : /* We had better have loaded per-column details about this table */
17284 : Assert(tbinfo->interesting);
17285 :
17286 6842 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17287 6842 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17288 :
17289 6842 : if (tbinfo->hasoids)
17290 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17291 : qrelname);
17292 :
17293 6842 : if (dopt->binary_upgrade)
17294 941 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17295 :
17296 : /* Is it a table or a view? */
17297 6842 : if (tbinfo->relkind == RELKIND_VIEW)
17298 : {
17299 : PQExpBuffer result;
17300 :
17301 : /*
17302 : * Note: keep this code in sync with the is_view case in dumpRule()
17303 : */
17304 :
17305 554 : reltypename = "VIEW";
17306 :
17307 554 : if (dopt->binary_upgrade)
17308 55 : binary_upgrade_set_pg_class_oids(fout, q,
17309 55 : tbinfo->dobj.catId.oid);
17310 :
17311 554 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17312 :
17313 554 : if (tbinfo->dummy_view)
17314 10 : result = createDummyViewAsClause(fout, tbinfo);
17315 : else
17316 : {
17317 544 : if (nonemptyReloptions(tbinfo->reloptions))
17318 : {
17319 61 : appendPQExpBufferStr(q, " WITH (");
17320 61 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17321 61 : appendPQExpBufferChar(q, ')');
17322 : }
17323 544 : result = createViewAsClause(fout, tbinfo);
17324 : }
17325 554 : appendPQExpBuffer(q, " AS\n%s", result->data);
17326 554 : destroyPQExpBuffer(result);
17327 :
17328 554 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17329 32 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17330 554 : appendPQExpBufferStr(q, ";\n");
17331 : }
17332 6288 : else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17333 : {
17334 97 : PQExpBuffer query = createPQExpBuffer();
17335 : PGresult *res;
17336 : int len;
17337 :
17338 97 : reltypename = "PROPERTY GRAPH";
17339 :
17340 97 : if (dopt->binary_upgrade)
17341 14 : binary_upgrade_set_pg_class_oids(fout, q,
17342 14 : tbinfo->dobj.catId.oid);
17343 :
17344 97 : appendPQExpBuffer(query,
17345 : "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17346 97 : tbinfo->dobj.catId.oid);
17347 :
17348 97 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17349 :
17350 97 : if (PQntuples(res) != 1)
17351 : {
17352 0 : if (PQntuples(res) < 1)
17353 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17354 : tbinfo->dobj.name);
17355 : else
17356 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17357 : tbinfo->dobj.name);
17358 : }
17359 :
17360 97 : len = PQgetlength(res, 0, 0);
17361 :
17362 97 : if (len == 0)
17363 0 : pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17364 : tbinfo->dobj.name);
17365 :
17366 97 : appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17367 :
17368 97 : PQclear(res);
17369 97 : destroyPQExpBuffer(query);
17370 :
17371 97 : appendPQExpBufferStr(q, ";\n");
17372 : }
17373 : else
17374 : {
17375 6191 : char *partkeydef = NULL;
17376 6191 : char *ftoptions = NULL;
17377 6191 : char *srvname = NULL;
17378 6191 : const char *foreign = "";
17379 :
17380 : /*
17381 : * Set reltypename, and collect any relkind-specific data that we
17382 : * didn't fetch during getTables().
17383 : */
17384 6191 : switch (tbinfo->relkind)
17385 : {
17386 591 : case RELKIND_PARTITIONED_TABLE:
17387 : {
17388 591 : PQExpBuffer query = createPQExpBuffer();
17389 : PGresult *res;
17390 :
17391 591 : reltypename = "TABLE";
17392 :
17393 : /* retrieve partition key definition */
17394 591 : appendPQExpBuffer(query,
17395 : "SELECT pg_get_partkeydef('%u')",
17396 591 : tbinfo->dobj.catId.oid);
17397 591 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17398 591 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17399 591 : PQclear(res);
17400 591 : destroyPQExpBuffer(query);
17401 591 : break;
17402 : }
17403 34 : case RELKIND_FOREIGN_TABLE:
17404 : {
17405 34 : PQExpBuffer query = createPQExpBuffer();
17406 : PGresult *res;
17407 : int i_srvname;
17408 : int i_ftoptions;
17409 :
17410 34 : reltypename = "FOREIGN TABLE";
17411 :
17412 : /* retrieve name of foreign server and generic options */
17413 34 : appendPQExpBuffer(query,
17414 : "SELECT fs.srvname, "
17415 : "pg_catalog.array_to_string(ARRAY("
17416 : "SELECT pg_catalog.quote_ident(option_name) || "
17417 : "' ' || pg_catalog.quote_literal(option_value) "
17418 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17419 : "ORDER BY option_name"
17420 : "), E',\n ') AS ftoptions "
17421 : "FROM pg_catalog.pg_foreign_table ft "
17422 : "JOIN pg_catalog.pg_foreign_server fs "
17423 : "ON (fs.oid = ft.ftserver) "
17424 : "WHERE ft.ftrelid = '%u'",
17425 34 : tbinfo->dobj.catId.oid);
17426 34 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17427 34 : i_srvname = PQfnumber(res, "srvname");
17428 34 : i_ftoptions = PQfnumber(res, "ftoptions");
17429 34 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17430 34 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17431 34 : PQclear(res);
17432 34 : destroyPQExpBuffer(query);
17433 :
17434 34 : foreign = "FOREIGN ";
17435 34 : break;
17436 : }
17437 335 : case RELKIND_MATVIEW:
17438 335 : reltypename = "MATERIALIZED VIEW";
17439 335 : break;
17440 5231 : default:
17441 5231 : reltypename = "TABLE";
17442 5231 : break;
17443 : }
17444 :
17445 6191 : numParents = tbinfo->numParents;
17446 6191 : parents = tbinfo->parents;
17447 :
17448 6191 : if (dopt->binary_upgrade)
17449 872 : binary_upgrade_set_pg_class_oids(fout, q,
17450 872 : tbinfo->dobj.catId.oid);
17451 :
17452 : /*
17453 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17454 : * ignore it when dumping if it was set in this case.
17455 : */
17456 6191 : appendPQExpBuffer(q, "CREATE %s%s %s",
17457 6191 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17458 20 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17459 : "UNLOGGED " : "",
17460 : reltypename,
17461 : qualrelname);
17462 :
17463 : /*
17464 : * Attach to type, if reloftype; except in case of a binary upgrade,
17465 : * we dump the table normally and attach it to the type afterward.
17466 : */
17467 6191 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17468 24 : appendPQExpBuffer(q, " OF %s",
17469 24 : getFormattedTypeName(fout, tbinfo->reloftype,
17470 : zeroIsError));
17471 :
17472 6191 : if (tbinfo->relkind != RELKIND_MATVIEW)
17473 : {
17474 : /* Dump the attributes */
17475 5856 : actual_atts = 0;
17476 26991 : for (j = 0; j < tbinfo->numatts; j++)
17477 : {
17478 : /*
17479 : * Normally, dump if it's locally defined in this table, and
17480 : * not dropped. But for binary upgrade, we'll dump all the
17481 : * columns, and then fix up the dropped and nonlocal cases
17482 : * below.
17483 : */
17484 21135 : if (shouldPrintColumn(dopt, tbinfo, j))
17485 : {
17486 : bool print_default;
17487 : bool print_notnull;
17488 :
17489 : /*
17490 : * Default value --- suppress if to be printed separately
17491 : * or not at all.
17492 : */
17493 41134 : print_default = (tbinfo->attrdefs[j] != NULL &&
17494 21049 : tbinfo->attrdefs[j]->dobj.dump &&
17495 1011 : !tbinfo->attrdefs[j]->separate);
17496 :
17497 : /*
17498 : * Not Null constraint --- print it if it is locally
17499 : * defined, or if binary upgrade. (In the latter case, we
17500 : * reset conislocal below.)
17501 : */
17502 22452 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17503 2414 : (tbinfo->notnull_islocal[j] ||
17504 637 : dopt->binary_upgrade ||
17505 547 : tbinfo->ispartition));
17506 :
17507 : /*
17508 : * Skip column if fully defined by reloftype, except in
17509 : * binary upgrade
17510 : */
17511 20038 : if (OidIsValid(tbinfo->reloftype) &&
17512 50 : !print_default && !print_notnull &&
17513 30 : !dopt->binary_upgrade)
17514 24 : continue;
17515 :
17516 : /* Format properly if not first attr */
17517 20014 : if (actual_atts == 0)
17518 5483 : appendPQExpBufferStr(q, " (");
17519 : else
17520 14531 : appendPQExpBufferChar(q, ',');
17521 20014 : appendPQExpBufferStr(q, "\n ");
17522 20014 : actual_atts++;
17523 :
17524 : /* Attribute name */
17525 20014 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17526 :
17527 20014 : if (tbinfo->attisdropped[j])
17528 : {
17529 : /*
17530 : * ALTER TABLE DROP COLUMN clears
17531 : * pg_attribute.atttypid, so we will not have gotten a
17532 : * valid type name; insert INTEGER as a stopgap. We'll
17533 : * clean things up later.
17534 : */
17535 85 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17536 : /* and skip to the next column */
17537 85 : continue;
17538 : }
17539 :
17540 : /*
17541 : * Attribute type; print it except when creating a typed
17542 : * table ('OF type_name'), but in binary-upgrade mode,
17543 : * print it in that case too.
17544 : */
17545 19929 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17546 : {
17547 19913 : appendPQExpBuffer(q, " %s",
17548 19913 : tbinfo->atttypnames[j]);
17549 : }
17550 :
17551 19929 : if (print_default)
17552 : {
17553 881 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17554 277 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17555 277 : tbinfo->attrdefs[j]->adef_expr);
17556 604 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17557 223 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17558 223 : tbinfo->attrdefs[j]->adef_expr);
17559 : else
17560 381 : appendPQExpBuffer(q, " DEFAULT %s",
17561 381 : tbinfo->attrdefs[j]->adef_expr);
17562 : }
17563 :
17564 19929 : if (print_notnull)
17565 : {
17566 2383 : if (tbinfo->notnull_constrs[j][0] == '\0')
17567 1718 : appendPQExpBufferStr(q, " NOT NULL");
17568 : else
17569 665 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17570 665 : fmtId(tbinfo->notnull_constrs[j]));
17571 :
17572 2383 : if (tbinfo->notnull_noinh[j])
17573 33 : appendPQExpBufferStr(q, " NO INHERIT");
17574 : }
17575 :
17576 : /* Add collation if not default for the type */
17577 19929 : if (OidIsValid(tbinfo->attcollation[j]))
17578 : {
17579 : CollInfo *coll;
17580 :
17581 213 : coll = findCollationByOid(tbinfo->attcollation[j]);
17582 213 : if (coll)
17583 213 : appendPQExpBuffer(q, " COLLATE %s",
17584 213 : fmtQualifiedDumpable(coll));
17585 : }
17586 : }
17587 :
17588 : /*
17589 : * On the other hand, if we choose not to print a column
17590 : * (likely because it is created by inheritance), but the
17591 : * column has a locally-defined not-null constraint, we need
17592 : * to dump the constraint as a standalone object.
17593 : *
17594 : * This syntax isn't SQL-conforming, but if you wanted
17595 : * standard output you wouldn't be creating non-standard
17596 : * objects to begin with.
17597 : */
17598 21026 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17599 1097 : !tbinfo->attisdropped[j] &&
17600 730 : tbinfo->notnull_constrs[j] != NULL &&
17601 208 : tbinfo->notnull_islocal[j])
17602 : {
17603 : /* Format properly if not first attr */
17604 90 : if (actual_atts == 0)
17605 86 : appendPQExpBufferStr(q, " (");
17606 : else
17607 4 : appendPQExpBufferChar(q, ',');
17608 90 : appendPQExpBufferStr(q, "\n ");
17609 90 : actual_atts++;
17610 :
17611 90 : if (tbinfo->notnull_constrs[j][0] == '\0')
17612 8 : appendPQExpBuffer(q, "NOT NULL %s",
17613 8 : fmtId(tbinfo->attnames[j]));
17614 : else
17615 164 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17616 82 : tbinfo->notnull_constrs[j],
17617 82 : fmtId(tbinfo->attnames[j]));
17618 :
17619 90 : if (tbinfo->notnull_noinh[j])
17620 31 : appendPQExpBufferStr(q, " NO INHERIT");
17621 : }
17622 : }
17623 :
17624 : /*
17625 : * Add non-inherited CHECK constraints, if any.
17626 : *
17627 : * For partitions, we need to include check constraints even if
17628 : * they're not defined locally, because the ALTER TABLE ATTACH
17629 : * PARTITION that we'll emit later expects the constraint to be
17630 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17631 : */
17632 6429 : for (j = 0; j < tbinfo->ncheck; j++)
17633 : {
17634 573 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17635 :
17636 573 : if (constr->separate ||
17637 503 : (!constr->conislocal && !tbinfo->ispartition))
17638 107 : continue;
17639 :
17640 466 : if (actual_atts == 0)
17641 16 : appendPQExpBufferStr(q, " (\n ");
17642 : else
17643 450 : appendPQExpBufferStr(q, ",\n ");
17644 :
17645 466 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17646 466 : fmtId(constr->dobj.name));
17647 466 : appendPQExpBufferStr(q, constr->condef);
17648 :
17649 466 : actual_atts++;
17650 : }
17651 :
17652 5856 : if (actual_atts)
17653 5585 : appendPQExpBufferStr(q, "\n)");
17654 271 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17655 : {
17656 : /*
17657 : * No attributes? we must have a parenthesized attribute list,
17658 : * even though empty, when not using the OF TYPE syntax.
17659 : */
17660 259 : appendPQExpBufferStr(q, " (\n)");
17661 : }
17662 :
17663 : /*
17664 : * Emit the INHERITS clause (not for partitions), except in
17665 : * binary-upgrade mode.
17666 : */
17667 5856 : if (numParents > 0 && !tbinfo->ispartition &&
17668 541 : !dopt->binary_upgrade)
17669 : {
17670 472 : appendPQExpBufferStr(q, "\nINHERITS (");
17671 1015 : for (k = 0; k < numParents; k++)
17672 : {
17673 543 : TableInfo *parentRel = parents[k];
17674 :
17675 543 : if (k > 0)
17676 71 : appendPQExpBufferStr(q, ", ");
17677 543 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17678 : }
17679 472 : appendPQExpBufferChar(q, ')');
17680 : }
17681 :
17682 5856 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17683 591 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17684 :
17685 5856 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17686 34 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17687 : }
17688 :
17689 12229 : if (nonemptyReloptions(tbinfo->reloptions) ||
17690 6038 : nonemptyReloptions(tbinfo->toast_reloptions))
17691 : {
17692 153 : bool addcomma = false;
17693 :
17694 153 : appendPQExpBufferStr(q, "\nWITH (");
17695 153 : if (nonemptyReloptions(tbinfo->reloptions))
17696 : {
17697 153 : addcomma = true;
17698 153 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17699 : }
17700 153 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17701 : {
17702 5 : if (addcomma)
17703 5 : appendPQExpBufferStr(q, ", ");
17704 5 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17705 : fout);
17706 : }
17707 153 : appendPQExpBufferChar(q, ')');
17708 : }
17709 :
17710 : /* Dump generic options if any */
17711 6191 : if (ftoptions && ftoptions[0])
17712 32 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17713 :
17714 : /*
17715 : * For materialized views, create the AS clause just like a view. At
17716 : * this point, we always mark the view as not populated.
17717 : */
17718 6191 : if (tbinfo->relkind == RELKIND_MATVIEW)
17719 : {
17720 : PQExpBuffer result;
17721 :
17722 335 : result = createViewAsClause(fout, tbinfo);
17723 335 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17724 : result->data);
17725 335 : destroyPQExpBuffer(result);
17726 : }
17727 : else
17728 5856 : appendPQExpBufferStr(q, ";\n");
17729 :
17730 : /* Materialized views can depend on extensions */
17731 6191 : if (tbinfo->relkind == RELKIND_MATVIEW)
17732 335 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17733 : "pg_catalog.pg_class",
17734 : "MATERIALIZED VIEW",
17735 : qualrelname);
17736 :
17737 : /*
17738 : * in binary upgrade mode, update the catalog with any missing values
17739 : * that might be present.
17740 : */
17741 6191 : if (dopt->binary_upgrade)
17742 : {
17743 4183 : for (j = 0; j < tbinfo->numatts; j++)
17744 : {
17745 3311 : if (tbinfo->attmissingval[j][0] != '\0')
17746 : {
17747 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17748 4 : appendPQExpBufferStr(q,
17749 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17750 4 : appendStringLiteralAH(q, qualrelname, fout);
17751 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17752 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17753 4 : appendPQExpBufferChar(q, ',');
17754 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17755 4 : appendPQExpBufferStr(q, ");\n\n");
17756 : }
17757 : }
17758 : }
17759 :
17760 : /*
17761 : * To create binary-compatible heap files, we have to ensure the same
17762 : * physical column order, including dropped columns, as in the
17763 : * original. Therefore, we create dropped columns above and drop them
17764 : * here, also updating their attlen/attalign values so that the
17765 : * dropped column can be skipped properly. (We do not bother with
17766 : * restoring the original attbyval setting.) Also, inheritance
17767 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17768 : * using an INHERITS clause --- the latter would possibly mess up the
17769 : * column order. That also means we have to take care about setting
17770 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17771 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17772 : *
17773 : * We process foreign and partitioned tables here, even though they
17774 : * lack heap storage, because they can participate in inheritance
17775 : * relationships and we want this stuff to be consistent across the
17776 : * inheritance tree. We can exclude indexes, toast tables, sequences
17777 : * and matviews, even though they have storage, because we don't
17778 : * support altering or dropping columns in them, nor can they be part
17779 : * of inheritance trees.
17780 : */
17781 6191 : if (dopt->binary_upgrade &&
17782 872 : (tbinfo->relkind == RELKIND_RELATION ||
17783 114 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17784 113 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17785 : {
17786 : bool firstitem;
17787 : bool firstitem_extra;
17788 :
17789 : /*
17790 : * Drop any dropped columns. Merge the pg_attribute manipulations
17791 : * into a single SQL command, so that we don't cause repeated
17792 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17793 : * relcache bloat while dropping N columns.
17794 : */
17795 855 : resetPQExpBuffer(extra);
17796 855 : firstitem = true;
17797 4145 : for (j = 0; j < tbinfo->numatts; j++)
17798 : {
17799 3290 : if (tbinfo->attisdropped[j])
17800 : {
17801 85 : if (firstitem)
17802 : {
17803 39 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17804 : "UPDATE pg_catalog.pg_attribute\n"
17805 : "SET attlen = v.dlen, "
17806 : "attalign = v.dalign, "
17807 : "attbyval = false\n"
17808 : "FROM (VALUES ");
17809 39 : firstitem = false;
17810 : }
17811 : else
17812 46 : appendPQExpBufferStr(q, ",\n ");
17813 85 : appendPQExpBufferChar(q, '(');
17814 85 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17815 85 : appendPQExpBuffer(q, ", %d, '%c')",
17816 85 : tbinfo->attlen[j],
17817 85 : tbinfo->attalign[j]);
17818 : /* The ALTER ... DROP COLUMN commands must come after */
17819 85 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17820 : foreign, qualrelname);
17821 85 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17822 85 : fmtId(tbinfo->attnames[j]));
17823 : }
17824 : }
17825 855 : if (!firstitem)
17826 : {
17827 39 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17828 : "WHERE attrelid = ");
17829 39 : appendStringLiteralAH(q, qualrelname, fout);
17830 39 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17831 : " AND attname = v.dname;\n");
17832 : /* Now we can issue the actual DROP COLUMN commands */
17833 39 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17834 : }
17835 :
17836 : /*
17837 : * Fix up inherited columns. As above, do the pg_attribute
17838 : * manipulations in a single SQL command.
17839 : */
17840 855 : firstitem = true;
17841 4145 : for (j = 0; j < tbinfo->numatts; j++)
17842 : {
17843 3290 : if (!tbinfo->attisdropped[j] &&
17844 3205 : !tbinfo->attislocal[j])
17845 : {
17846 640 : if (firstitem)
17847 : {
17848 280 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17849 280 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17850 : "SET attislocal = false\n"
17851 : "WHERE attrelid = ");
17852 280 : appendStringLiteralAH(q, qualrelname, fout);
17853 280 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17854 : " AND attname IN (");
17855 280 : firstitem = false;
17856 : }
17857 : else
17858 360 : appendPQExpBufferStr(q, ", ");
17859 640 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17860 : }
17861 : }
17862 855 : if (!firstitem)
17863 280 : appendPQExpBufferStr(q, ");\n");
17864 :
17865 : /*
17866 : * Fix up not-null constraints that come from inheritance. As
17867 : * above, do the pg_constraint manipulations in a single SQL
17868 : * command. (Actually, two in special cases, if we're doing an
17869 : * upgrade from < 18).
17870 : */
17871 855 : firstitem = true;
17872 855 : firstitem_extra = true;
17873 855 : resetPQExpBuffer(extra);
17874 4145 : for (j = 0; j < tbinfo->numatts; j++)
17875 : {
17876 : /*
17877 : * If a not-null constraint comes from inheritance, reset
17878 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17879 : * below. Special hack: in versions < 18, columns with no
17880 : * local definition need their constraint to be matched by
17881 : * column number in conkeys instead of by constraint name,
17882 : * because the latter is not available. (We distinguish the
17883 : * case because the constraint name is the empty string.)
17884 : */
17885 3290 : if (tbinfo->notnull_constrs[j] != NULL &&
17886 324 : !tbinfo->notnull_islocal[j])
17887 : {
17888 90 : if (tbinfo->notnull_constrs[j][0] != '\0')
17889 : {
17890 77 : if (firstitem)
17891 : {
17892 67 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17893 : "SET conislocal = false\n"
17894 : "WHERE contype = 'n' AND conrelid = ");
17895 67 : appendStringLiteralAH(q, qualrelname, fout);
17896 67 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17897 : "conname IN (");
17898 67 : firstitem = false;
17899 : }
17900 : else
17901 10 : appendPQExpBufferStr(q, ", ");
17902 77 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17903 : }
17904 : else
17905 : {
17906 13 : if (firstitem_extra)
17907 : {
17908 13 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17909 : "SET conislocal = false\n"
17910 : "WHERE contype = 'n' AND conrelid = ");
17911 13 : appendStringLiteralAH(extra, qualrelname, fout);
17912 13 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17913 : "conkey IN (");
17914 13 : firstitem_extra = false;
17915 : }
17916 : else
17917 0 : appendPQExpBufferStr(extra, ", ");
17918 13 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17919 : }
17920 : }
17921 : }
17922 855 : if (!firstitem)
17923 67 : appendPQExpBufferStr(q, ");\n");
17924 855 : if (!firstitem_extra)
17925 13 : appendPQExpBufferStr(extra, ");\n");
17926 :
17927 855 : if (extra->len > 0)
17928 13 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17929 :
17930 : /*
17931 : * Add inherited CHECK constraints, if any.
17932 : *
17933 : * For partitions, they were already dumped, and conislocal
17934 : * doesn't need fixing.
17935 : *
17936 : * As above, issue only one direct manipulation of pg_constraint.
17937 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17938 : * commands into one as well, refrain for now due to concern about
17939 : * possible backend memory bloat if there are many such
17940 : * constraints.
17941 : */
17942 855 : resetPQExpBuffer(extra);
17943 855 : firstitem = true;
17944 917 : for (k = 0; k < tbinfo->ncheck; k++)
17945 : {
17946 62 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17947 :
17948 62 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17949 60 : continue;
17950 :
17951 2 : if (firstitem)
17952 2 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17953 2 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17954 : foreign, qualrelname,
17955 2 : fmtId(constr->dobj.name),
17956 : constr->condef);
17957 : /* Update pg_constraint after all the ALTER TABLEs */
17958 2 : if (firstitem)
17959 : {
17960 2 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17961 : "SET conislocal = false\n"
17962 : "WHERE contype = 'c' AND conrelid = ");
17963 2 : appendStringLiteralAH(extra, qualrelname, fout);
17964 2 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17965 2 : appendPQExpBufferStr(extra, " AND conname IN (");
17966 2 : firstitem = false;
17967 : }
17968 : else
17969 0 : appendPQExpBufferStr(extra, ", ");
17970 2 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17971 : }
17972 855 : if (!firstitem)
17973 : {
17974 2 : appendPQExpBufferStr(extra, ");\n");
17975 2 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17976 : }
17977 :
17978 855 : if (numParents > 0 && !tbinfo->ispartition)
17979 : {
17980 69 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17981 149 : for (k = 0; k < numParents; k++)
17982 : {
17983 80 : TableInfo *parentRel = parents[k];
17984 :
17985 80 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17986 : qualrelname,
17987 80 : fmtQualifiedDumpable(parentRel));
17988 : }
17989 : }
17990 :
17991 855 : if (OidIsValid(tbinfo->reloftype))
17992 : {
17993 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17994 6 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17995 : qualrelname,
17996 6 : getFormattedTypeName(fout, tbinfo->reloftype,
17997 : zeroIsError));
17998 : }
17999 : }
18000 :
18001 : /*
18002 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
18003 : * relminmxid of all vacuumable relations. (While vacuum.c processes
18004 : * TOAST tables semi-independently, here we see them only as children
18005 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
18006 : * child toast table is handled below.)
18007 : */
18008 6191 : if (dopt->binary_upgrade &&
18009 872 : (tbinfo->relkind == RELKIND_RELATION ||
18010 114 : tbinfo->relkind == RELKIND_MATVIEW))
18011 : {
18012 775 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
18013 775 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18014 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18015 : "WHERE oid = ",
18016 775 : tbinfo->frozenxid, tbinfo->minmxid);
18017 775 : appendStringLiteralAH(q, qualrelname, fout);
18018 775 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18019 :
18020 775 : if (tbinfo->toast_oid)
18021 : {
18022 : /*
18023 : * The toast table will have the same OID at restore, so we
18024 : * can safely target it by OID.
18025 : */
18026 294 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
18027 294 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18028 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18029 : "WHERE oid = '%u';\n",
18030 294 : tbinfo->toast_frozenxid,
18031 294 : tbinfo->toast_minmxid, tbinfo->toast_oid);
18032 : }
18033 : }
18034 :
18035 : /*
18036 : * In binary_upgrade mode, restore matviews' populated status by
18037 : * poking pg_class directly. This is pretty ugly, but we can't use
18038 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
18039 : * matview is not populated even though this matview is; in any case,
18040 : * we want to transfer the matview's heap storage, not run REFRESH.
18041 : */
18042 6191 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
18043 17 : tbinfo->relispopulated)
18044 : {
18045 15 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
18046 15 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
18047 : "SET relispopulated = 't'\n"
18048 : "WHERE oid = ");
18049 15 : appendStringLiteralAH(q, qualrelname, fout);
18050 15 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18051 : }
18052 :
18053 : /*
18054 : * Dump additional per-column properties that we can't handle in the
18055 : * main CREATE TABLE command.
18056 : */
18057 27735 : for (j = 0; j < tbinfo->numatts; j++)
18058 : {
18059 : /* None of this applies to dropped columns */
18060 21544 : if (tbinfo->attisdropped[j])
18061 452 : continue;
18062 :
18063 : /*
18064 : * Dump per-column statistics information. We only issue an ALTER
18065 : * TABLE statement if the attstattarget entry for this column is
18066 : * not the default value.
18067 : */
18068 21092 : if (tbinfo->attstattarget[j] >= 0)
18069 32 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
18070 : foreign, qualrelname,
18071 32 : fmtId(tbinfo->attnames[j]),
18072 32 : tbinfo->attstattarget[j]);
18073 :
18074 : /*
18075 : * Dump per-column storage information. The statement is only
18076 : * dumped if the storage has been changed from the type's default.
18077 : */
18078 21092 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
18079 : {
18080 79 : switch (tbinfo->attstorage[j])
18081 : {
18082 10 : case TYPSTORAGE_PLAIN:
18083 10 : storage = "PLAIN";
18084 10 : break;
18085 37 : case TYPSTORAGE_EXTERNAL:
18086 37 : storage = "EXTERNAL";
18087 37 : break;
18088 0 : case TYPSTORAGE_EXTENDED:
18089 0 : storage = "EXTENDED";
18090 0 : break;
18091 32 : case TYPSTORAGE_MAIN:
18092 32 : storage = "MAIN";
18093 32 : break;
18094 0 : default:
18095 0 : storage = NULL;
18096 : }
18097 :
18098 : /*
18099 : * Only dump the statement if it's a storage type we recognize
18100 : */
18101 79 : if (storage != NULL)
18102 79 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
18103 : foreign, qualrelname,
18104 79 : fmtId(tbinfo->attnames[j]),
18105 : storage);
18106 : }
18107 :
18108 : /*
18109 : * Dump per-column compression, if it's been set.
18110 : */
18111 21092 : if (!dopt->no_toast_compression)
18112 : {
18113 : const char *cmname;
18114 :
18115 20992 : switch (tbinfo->attcompression[j])
18116 : {
18117 71 : case 'p':
18118 71 : cmname = "pglz";
18119 71 : break;
18120 39 : case 'l':
18121 39 : cmname = "lz4";
18122 39 : break;
18123 20882 : default:
18124 20882 : cmname = NULL;
18125 20882 : break;
18126 : }
18127 :
18128 20992 : if (cmname != NULL)
18129 110 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
18130 : foreign, qualrelname,
18131 110 : fmtId(tbinfo->attnames[j]),
18132 : cmname);
18133 : }
18134 :
18135 : /*
18136 : * Dump per-column attributes.
18137 : */
18138 21092 : if (tbinfo->attoptions[j][0] != '\0')
18139 32 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
18140 : foreign, qualrelname,
18141 32 : fmtId(tbinfo->attnames[j]),
18142 32 : tbinfo->attoptions[j]);
18143 :
18144 : /*
18145 : * Dump per-column fdw options.
18146 : */
18147 21092 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
18148 34 : tbinfo->attfdwoptions[j][0] != '\0')
18149 32 : appendPQExpBuffer(q,
18150 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
18151 : " %s\n"
18152 : ");\n",
18153 : qualrelname,
18154 32 : fmtId(tbinfo->attnames[j]),
18155 32 : tbinfo->attfdwoptions[j]);
18156 : } /* end loop over columns */
18157 :
18158 6191 : free(partkeydef);
18159 6191 : free(ftoptions);
18160 6191 : free(srvname);
18161 : }
18162 :
18163 : /*
18164 : * dump properties we only have ALTER TABLE syntax for
18165 : */
18166 6842 : if ((tbinfo->relkind == RELKIND_RELATION ||
18167 1611 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
18168 1020 : tbinfo->relkind == RELKIND_MATVIEW) &&
18169 6157 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
18170 : {
18171 207 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
18172 : {
18173 : /* nothing to do, will be set when the index is dumped */
18174 : }
18175 207 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
18176 : {
18177 207 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
18178 : qualrelname);
18179 : }
18180 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
18181 : {
18182 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
18183 : qualrelname);
18184 : }
18185 : }
18186 :
18187 6842 : if (tbinfo->forcerowsec)
18188 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
18189 : qualrelname);
18190 :
18191 6842 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
18192 :
18193 6842 : if (dopt->binary_upgrade)
18194 941 : binary_upgrade_extension_member(q, &tbinfo->dobj,
18195 : reltypename, qrelname,
18196 941 : tbinfo->dobj.namespace->dobj.name);
18197 :
18198 6842 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18199 : {
18200 6842 : char *tablespace = NULL;
18201 6842 : char *tableam = NULL;
18202 :
18203 : /*
18204 : * _selectTablespace() relies on tablespace-enabled objects in the
18205 : * default tablespace to have a tablespace of "" (empty string) versus
18206 : * non-tablespace-enabled objects to have a tablespace of NULL.
18207 : * getTables() sets tbinfo->reltablespace to "" for the default
18208 : * tablespace (not NULL).
18209 : */
18210 6842 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
18211 6157 : tablespace = tbinfo->reltablespace;
18212 :
18213 6842 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
18214 1276 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
18215 6157 : tableam = tbinfo->amname;
18216 :
18217 6842 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18218 6842 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18219 : .namespace = tbinfo->dobj.namespace->dobj.name,
18220 : .tablespace = tablespace,
18221 : .tableam = tableam,
18222 : .relkind = tbinfo->relkind,
18223 : .owner = tbinfo->rolname,
18224 : .description = reltypename,
18225 : .section = tbinfo->postponed_def ?
18226 : SECTION_POST_DATA : SECTION_PRE_DATA,
18227 : .createStmt = q->data,
18228 : .dropStmt = delq->data));
18229 : }
18230 :
18231 : /* Dump Table Comments */
18232 6842 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18233 74 : dumpTableComment(fout, tbinfo, reltypename);
18234 :
18235 : /* Dump Table Security Labels */
18236 6842 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18237 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18238 :
18239 : /*
18240 : * Dump comments for not-null constraints that aren't to be dumped
18241 : * separately (those are processed by collectComments/dumpComment).
18242 : */
18243 6842 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18244 6842 : fout->remoteVersion >= 180000)
18245 : {
18246 6842 : PQExpBuffer comment = NULL;
18247 6842 : PQExpBuffer tag = NULL;
18248 :
18249 31947 : for (j = 0; j < tbinfo->numatts; j++)
18250 : {
18251 25105 : if (tbinfo->notnull_constrs[j] != NULL &&
18252 2622 : tbinfo->notnull_comment[j] != NULL)
18253 : {
18254 42 : if (comment == NULL)
18255 : {
18256 42 : comment = createPQExpBuffer();
18257 42 : tag = createPQExpBuffer();
18258 : }
18259 : else
18260 : {
18261 0 : resetPQExpBuffer(comment);
18262 0 : resetPQExpBuffer(tag);
18263 : }
18264 :
18265 42 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18266 42 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18267 42 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18268 42 : appendPQExpBufferStr(comment, ";\n");
18269 :
18270 42 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18271 42 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18272 :
18273 42 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18274 42 : ARCHIVE_OPTS(.tag = tag->data,
18275 : .namespace = tbinfo->dobj.namespace->dobj.name,
18276 : .owner = tbinfo->rolname,
18277 : .description = "COMMENT",
18278 : .section = SECTION_NONE,
18279 : .createStmt = comment->data,
18280 : .deps = &(tbinfo->dobj.dumpId),
18281 : .nDeps = 1));
18282 : }
18283 : }
18284 :
18285 6842 : destroyPQExpBuffer(comment);
18286 6842 : destroyPQExpBuffer(tag);
18287 : }
18288 :
18289 : /* Dump comments on inlined table constraints */
18290 7415 : for (j = 0; j < tbinfo->ncheck; j++)
18291 : {
18292 573 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18293 :
18294 573 : if (constr->separate || !constr->conislocal)
18295 244 : continue;
18296 :
18297 329 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18298 37 : dumpTableConstraintComment(fout, constr);
18299 : }
18300 :
18301 6842 : destroyPQExpBuffer(q);
18302 6842 : destroyPQExpBuffer(delq);
18303 6842 : destroyPQExpBuffer(extra);
18304 6842 : free(qrelname);
18305 6842 : free(qualrelname);
18306 6842 : }
18307 :
18308 : /*
18309 : * dumpTableAttach
18310 : * write to fout the commands to attach a child partition
18311 : *
18312 : * Child partitions are always made by creating them separately
18313 : * and then using ATTACH PARTITION, rather than using
18314 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18315 : * any possible discrepancy in column layout, to allow assigning the
18316 : * correct tablespace if different, and so that it's possible to restore
18317 : * a partition without restoring its parent. (You'll get an error from
18318 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18319 : * using "pg_restore -L" if you prefer.) The last point motivates
18320 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18321 : * rather than emitting it within the child partition's ArchiveEntry.
18322 : */
18323 : static void
18324 1421 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18325 : {
18326 1421 : DumpOptions *dopt = fout->dopt;
18327 : PQExpBuffer q;
18328 : PGresult *res;
18329 : char *partbound;
18330 :
18331 : /* Do nothing if not dumping schema */
18332 1421 : if (!dopt->dumpSchema)
18333 54 : return;
18334 :
18335 1367 : q = createPQExpBuffer();
18336 :
18337 1367 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18338 : {
18339 : /* Set up query for partbound details */
18340 43 : appendPQExpBufferStr(q,
18341 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18342 :
18343 43 : appendPQExpBufferStr(q,
18344 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18345 : "FROM pg_class c "
18346 : "WHERE c.oid = $1");
18347 :
18348 43 : ExecuteSqlStatement(fout, q->data);
18349 :
18350 43 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18351 : }
18352 :
18353 1367 : printfPQExpBuffer(q,
18354 : "EXECUTE dumpTableAttach('%u')",
18355 1367 : attachinfo->partitionTbl->dobj.catId.oid);
18356 :
18357 1367 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18358 1367 : partbound = PQgetvalue(res, 0, 0);
18359 :
18360 : /* Perform ALTER TABLE on the parent */
18361 1367 : printfPQExpBuffer(q,
18362 : "ALTER TABLE ONLY %s ",
18363 1367 : fmtQualifiedDumpable(attachinfo->parentTbl));
18364 1367 : appendPQExpBuffer(q,
18365 : "ATTACH PARTITION %s %s;\n",
18366 1367 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18367 : partbound);
18368 :
18369 : /*
18370 : * There is no point in creating a drop query as the drop is done by table
18371 : * drop. (If you think to change this, see also _printTocEntry().)
18372 : * Although this object doesn't really have ownership as such, set the
18373 : * owner field anyway to ensure that the command is run by the correct
18374 : * role at restore time.
18375 : */
18376 1367 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18377 1367 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18378 : .namespace = attachinfo->dobj.namespace->dobj.name,
18379 : .owner = attachinfo->partitionTbl->rolname,
18380 : .description = "TABLE ATTACH",
18381 : .section = SECTION_PRE_DATA,
18382 : .createStmt = q->data));
18383 :
18384 1367 : PQclear(res);
18385 1367 : destroyPQExpBuffer(q);
18386 : }
18387 :
18388 : /*
18389 : * dumpAttrDef --- dump an attribute's default-value declaration
18390 : */
18391 : static void
18392 1047 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18393 : {
18394 1047 : DumpOptions *dopt = fout->dopt;
18395 1047 : TableInfo *tbinfo = adinfo->adtable;
18396 1047 : int adnum = adinfo->adnum;
18397 : PQExpBuffer q;
18398 : PQExpBuffer delq;
18399 : char *qualrelname;
18400 : char *tag;
18401 : char *foreign;
18402 :
18403 : /* Do nothing if not dumping schema */
18404 1047 : if (!dopt->dumpSchema)
18405 0 : return;
18406 :
18407 : /* Skip if not "separate"; it was dumped in the table's definition */
18408 1047 : if (!adinfo->separate)
18409 881 : return;
18410 :
18411 166 : q = createPQExpBuffer();
18412 166 : delq = createPQExpBuffer();
18413 :
18414 166 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18415 :
18416 166 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18417 :
18418 166 : appendPQExpBuffer(q,
18419 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18420 166 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18421 166 : adinfo->adef_expr);
18422 :
18423 166 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18424 : foreign, qualrelname,
18425 166 : fmtId(tbinfo->attnames[adnum - 1]));
18426 :
18427 166 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18428 :
18429 166 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18430 166 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18431 166 : ARCHIVE_OPTS(.tag = tag,
18432 : .namespace = tbinfo->dobj.namespace->dobj.name,
18433 : .owner = tbinfo->rolname,
18434 : .description = "DEFAULT",
18435 : .section = SECTION_PRE_DATA,
18436 : .createStmt = q->data,
18437 : .dropStmt = delq->data));
18438 :
18439 166 : free(tag);
18440 166 : destroyPQExpBuffer(q);
18441 166 : destroyPQExpBuffer(delq);
18442 166 : free(qualrelname);
18443 : }
18444 :
18445 : /*
18446 : * getAttrName: extract the correct name for an attribute
18447 : *
18448 : * The array tblInfo->attnames[] only provides names of user attributes;
18449 : * if a system attribute number is supplied, we have to fake it.
18450 : * We also do a little bit of bounds checking for safety's sake.
18451 : */
18452 : static const char *
18453 2235 : getAttrName(int attrnum, const TableInfo *tblInfo)
18454 : {
18455 2235 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18456 2235 : return tblInfo->attnames[attrnum - 1];
18457 0 : switch (attrnum)
18458 : {
18459 0 : case SelfItemPointerAttributeNumber:
18460 0 : return "ctid";
18461 0 : case MinTransactionIdAttributeNumber:
18462 0 : return "xmin";
18463 0 : case MinCommandIdAttributeNumber:
18464 0 : return "cmin";
18465 0 : case MaxTransactionIdAttributeNumber:
18466 0 : return "xmax";
18467 0 : case MaxCommandIdAttributeNumber:
18468 0 : return "cmax";
18469 0 : case TableOidAttributeNumber:
18470 0 : return "tableoid";
18471 : }
18472 0 : pg_fatal("invalid column number %d for table \"%s\"",
18473 : attrnum, tblInfo->dobj.name);
18474 : return NULL; /* keep compiler quiet */
18475 : }
18476 :
18477 : /*
18478 : * dumpIndex
18479 : * write out to fout a user-defined index
18480 : */
18481 : static void
18482 2758 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18483 : {
18484 2758 : DumpOptions *dopt = fout->dopt;
18485 2758 : TableInfo *tbinfo = indxinfo->indextable;
18486 2758 : bool is_constraint = (indxinfo->indexconstraint != 0);
18487 : PQExpBuffer q;
18488 : PQExpBuffer delq;
18489 : char *qindxname;
18490 : char *qqindxname;
18491 :
18492 : /* Do nothing if not dumping schema */
18493 2758 : if (!dopt->dumpSchema)
18494 117 : return;
18495 :
18496 2641 : q = createPQExpBuffer();
18497 2641 : delq = createPQExpBuffer();
18498 :
18499 2641 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18500 2641 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18501 :
18502 : /*
18503 : * If there's an associated constraint, don't dump the index per se, but
18504 : * do dump any comment for it. (This is safe because dependency ordering
18505 : * will have ensured the constraint is emitted first.) Note that the
18506 : * emitted comment has to be shown as depending on the constraint, not the
18507 : * index, in such cases.
18508 : */
18509 2641 : if (!is_constraint)
18510 : {
18511 1048 : char *indstatcols = indxinfo->indstatcols;
18512 1048 : char *indstatvals = indxinfo->indstatvals;
18513 1048 : char **indstatcolsarray = NULL;
18514 1048 : char **indstatvalsarray = NULL;
18515 1048 : int nstatcols = 0;
18516 1048 : int nstatvals = 0;
18517 :
18518 1048 : if (dopt->binary_upgrade)
18519 158 : binary_upgrade_set_pg_class_oids(fout, q,
18520 158 : indxinfo->dobj.catId.oid);
18521 :
18522 : /* Plain secondary index */
18523 1048 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18524 :
18525 : /*
18526 : * Append ALTER TABLE commands as needed to set properties that we
18527 : * only have ALTER TABLE syntax for. Keep this in sync with the
18528 : * similar code in dumpConstraint!
18529 : */
18530 :
18531 : /* If the index is clustered, we need to record that. */
18532 1048 : if (indxinfo->indisclustered)
18533 : {
18534 5 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18535 5 : fmtQualifiedDumpable(tbinfo));
18536 : /* index name is not qualified in this syntax */
18537 5 : appendPQExpBuffer(q, " ON %s;\n",
18538 : qindxname);
18539 : }
18540 :
18541 : /*
18542 : * If the index has any statistics on some of its columns, generate
18543 : * the associated ALTER INDEX queries.
18544 : */
18545 1048 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18546 : {
18547 : int j;
18548 :
18549 32 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18550 0 : pg_fatal("could not parse index statistic columns");
18551 32 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18552 0 : pg_fatal("could not parse index statistic values");
18553 32 : if (nstatcols != nstatvals)
18554 0 : pg_fatal("mismatched number of columns and values for index statistics");
18555 :
18556 96 : for (j = 0; j < nstatcols; j++)
18557 : {
18558 64 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18559 :
18560 : /*
18561 : * Note that this is a column number, so no quotes should be
18562 : * used.
18563 : */
18564 64 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18565 64 : indstatcolsarray[j]);
18566 64 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18567 64 : indstatvalsarray[j]);
18568 : }
18569 : }
18570 :
18571 : /* Indexes can depend on extensions */
18572 1048 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18573 : "pg_catalog.pg_class",
18574 : "INDEX", qqindxname);
18575 :
18576 : /* If the index defines identity, we need to record that. */
18577 1048 : if (indxinfo->indisreplident)
18578 : {
18579 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18580 0 : fmtQualifiedDumpable(tbinfo));
18581 : /* index name is not qualified in this syntax */
18582 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18583 : qindxname);
18584 : }
18585 :
18586 : /*
18587 : * If this index is a member of a partitioned index, the backend will
18588 : * not allow us to drop it separately, so don't try. It will go away
18589 : * automatically when we drop either the index's table or the
18590 : * partitioned index. (If, in a selective restore with --clean, we
18591 : * drop neither of those, then this index will not be dropped either.
18592 : * But that's fine, and even if you think it's not, the backend won't
18593 : * let us do differently.)
18594 : */
18595 1048 : if (indxinfo->parentidx == 0)
18596 866 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18597 :
18598 1048 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18599 1048 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18600 1048 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18601 : .namespace = tbinfo->dobj.namespace->dobj.name,
18602 : .tablespace = indxinfo->tablespace,
18603 : .owner = tbinfo->rolname,
18604 : .description = "INDEX",
18605 : .section = SECTION_POST_DATA,
18606 : .createStmt = q->data,
18607 : .dropStmt = delq->data));
18608 :
18609 1048 : free(indstatcolsarray);
18610 1048 : free(indstatvalsarray);
18611 : }
18612 :
18613 : /* Dump Index Comments */
18614 2641 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18615 15 : dumpComment(fout, "INDEX", qindxname,
18616 15 : tbinfo->dobj.namespace->dobj.name,
18617 : tbinfo->rolname,
18618 : indxinfo->dobj.catId, 0,
18619 : is_constraint ? indxinfo->indexconstraint :
18620 : indxinfo->dobj.dumpId);
18621 :
18622 2641 : destroyPQExpBuffer(q);
18623 2641 : destroyPQExpBuffer(delq);
18624 2641 : free(qindxname);
18625 2641 : free(qqindxname);
18626 : }
18627 :
18628 : /*
18629 : * dumpIndexAttach
18630 : * write out to fout a partitioned-index attachment clause
18631 : */
18632 : static void
18633 594 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18634 : {
18635 : /* Do nothing if not dumping schema */
18636 594 : if (!fout->dopt->dumpSchema)
18637 48 : return;
18638 :
18639 546 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18640 : {
18641 546 : PQExpBuffer q = createPQExpBuffer();
18642 :
18643 546 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18644 546 : fmtQualifiedDumpable(attachinfo->parentIdx));
18645 546 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18646 546 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18647 :
18648 : /*
18649 : * There is no need for a dropStmt since the drop is done implicitly
18650 : * when we drop either the index's table or the partitioned index.
18651 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18652 : * there's no way to do it anyway. (If you think to change this,
18653 : * consider also what to do with --if-exists.)
18654 : *
18655 : * Although this object doesn't really have ownership as such, set the
18656 : * owner field anyway to ensure that the command is run by the correct
18657 : * role at restore time.
18658 : */
18659 546 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18660 546 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18661 : .namespace = attachinfo->dobj.namespace->dobj.name,
18662 : .owner = attachinfo->parentIdx->indextable->rolname,
18663 : .description = "INDEX ATTACH",
18664 : .section = SECTION_POST_DATA,
18665 : .createStmt = q->data));
18666 :
18667 546 : destroyPQExpBuffer(q);
18668 : }
18669 : }
18670 :
18671 : /*
18672 : * dumpStatisticsExt
18673 : * write out to fout an extended statistics object
18674 : */
18675 : static void
18676 171 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18677 : {
18678 171 : DumpOptions *dopt = fout->dopt;
18679 : PQExpBuffer q;
18680 : PQExpBuffer delq;
18681 : PQExpBuffer query;
18682 : char *qstatsextname;
18683 : PGresult *res;
18684 : char *stxdef;
18685 :
18686 : /* Do nothing if not dumping schema */
18687 171 : if (!dopt->dumpSchema)
18688 24 : return;
18689 :
18690 147 : q = createPQExpBuffer();
18691 147 : delq = createPQExpBuffer();
18692 147 : query = createPQExpBuffer();
18693 :
18694 147 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18695 :
18696 147 : appendPQExpBuffer(query, "SELECT "
18697 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18698 147 : statsextinfo->dobj.catId.oid);
18699 :
18700 147 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18701 :
18702 147 : stxdef = PQgetvalue(res, 0, 0);
18703 :
18704 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18705 147 : appendPQExpBuffer(q, "%s;\n", stxdef);
18706 :
18707 : /*
18708 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18709 : * for this statistics object is not the default value.
18710 : */
18711 147 : if (statsextinfo->stattarget >= 0)
18712 : {
18713 32 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18714 32 : fmtQualifiedDumpable(statsextinfo));
18715 32 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18716 32 : statsextinfo->stattarget);
18717 : }
18718 :
18719 147 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18720 147 : fmtQualifiedDumpable(statsextinfo));
18721 :
18722 147 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18723 147 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18724 147 : statsextinfo->dobj.dumpId,
18725 147 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18726 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18727 : .owner = statsextinfo->rolname,
18728 : .description = "STATISTICS",
18729 : .section = SECTION_POST_DATA,
18730 : .createStmt = q->data,
18731 : .dropStmt = delq->data));
18732 :
18733 : /* Dump Statistics Comments */
18734 147 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18735 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18736 0 : statsextinfo->dobj.namespace->dobj.name,
18737 0 : statsextinfo->rolname,
18738 : statsextinfo->dobj.catId, 0,
18739 0 : statsextinfo->dobj.dumpId);
18740 :
18741 147 : PQclear(res);
18742 147 : destroyPQExpBuffer(q);
18743 147 : destroyPQExpBuffer(delq);
18744 147 : destroyPQExpBuffer(query);
18745 147 : free(qstatsextname);
18746 : }
18747 :
18748 : /*
18749 : * dumpStatisticsExtStats
18750 : * write out to fout the stats for an extended statistics object
18751 : */
18752 : static void
18753 171 : dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
18754 : {
18755 171 : DumpOptions *dopt = fout->dopt;
18756 : PQExpBuffer query;
18757 : PGresult *res;
18758 : int nstats;
18759 :
18760 : /* Do nothing if not dumping statistics */
18761 171 : if (!dopt->dumpStatistics)
18762 40 : return;
18763 :
18764 131 : if (!fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS])
18765 : {
18766 33 : PQExpBuffer pq = createPQExpBuffer();
18767 :
18768 : /*---------
18769 : * Set up query for details about extended statistics objects.
18770 : *
18771 : * The query depends on the backend version:
18772 : * - In v19 and newer versions, query directly the pg_stats_ext*
18773 : * catalogs.
18774 : * - In v18 and older versions, ndistinct and dependencies have a
18775 : * different format that needs translation.
18776 : * - In v14 and older versions, inherited does not exist.
18777 : * - In v11 and older versions, there is no pg_stats_ext, hence
18778 : * the logic joins pg_statistic_ext and pg_namespace.
18779 : *---------
18780 : */
18781 :
18782 33 : appendPQExpBufferStr(pq,
18783 : "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18784 : "SELECT ");
18785 :
18786 : /*
18787 : * Versions 15 and newer have inherited stats.
18788 : *
18789 : * Create this column in all versions because we need to order by it
18790 : * later.
18791 : */
18792 33 : if (fout->remoteVersion >= 150000)
18793 33 : appendPQExpBufferStr(pq, "e.inherited, ");
18794 : else
18795 0 : appendPQExpBufferStr(pq, "false AS inherited, ");
18796 :
18797 : /*--------
18798 : * The ndistinct and dependencies formats changed in v19, so
18799 : * everything before that needs to be translated.
18800 : *
18801 : * The ndistinct translation converts this kind of data:
18802 : * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18803 : *
18804 : * to this:
18805 : * [ {"attributes": [3,4], "ndistinct": 11},
18806 : * {"attributes": [3,6], "ndistinct": 11},
18807 : * {"attributes": [4,6], "ndistinct": 11},
18808 : * {"attributes": [3,4,6], "ndistinct": 11} ]
18809 : *
18810 : * The dependencies translation converts this kind of data:
18811 : * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18812 : * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18813 : * "3, 6 => 4": 1.000000}
18814 : *
18815 : * to this:
18816 : * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18817 : * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18818 : * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18819 : * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18820 : * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18821 : *--------
18822 : */
18823 33 : if (fout->remoteVersion >= 190000)
18824 33 : appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18825 : else
18826 0 : appendPQExpBufferStr(pq,
18827 : "( "
18828 : "SELECT json_agg( "
18829 : " json_build_object( "
18830 : " '" PG_NDISTINCT_KEY_ATTRIBUTES "', "
18831 : " string_to_array(kv.key, ', ')::integer[], "
18832 : " '" PG_NDISTINCT_KEY_NDISTINCT "', "
18833 : " kv.value::bigint )) "
18834 : "FROM json_each_text(e.n_distinct::text::json) AS kv"
18835 : ") AS n_distinct, "
18836 : "( "
18837 : "SELECT json_agg( "
18838 : " json_build_object( "
18839 : " '" PG_DEPENDENCIES_KEY_ATTRIBUTES "', "
18840 : " string_to_array( "
18841 : " split_part(kv.key, ' => ', 1), "
18842 : " ', ')::integer[], "
18843 : " '" PG_DEPENDENCIES_KEY_DEPENDENCY "', "
18844 : " split_part(kv.key, ' => ', 2)::integer, "
18845 : " '" PG_DEPENDENCIES_KEY_DEGREE "', "
18846 : " kv.value::double precision )) "
18847 : "FROM json_each_text(e.dependencies::text::json) AS kv "
18848 : ") AS dependencies, ");
18849 :
18850 : /* MCV was introduced v13 */
18851 33 : if (fout->remoteVersion >= 130000)
18852 33 : appendPQExpBufferStr(pq,
18853 : "e.most_common_vals, e.most_common_freqs, "
18854 : "e.most_common_base_freqs, ");
18855 : else
18856 0 : appendPQExpBufferStr(pq,
18857 : "NULL AS most_common_vals, NULL AS most_common_freqs, "
18858 : "NULL AS most_common_base_freqs, ");
18859 :
18860 : /* Expressions were introduced in v14 */
18861 33 : if (fout->remoteVersion >= 140000)
18862 : {
18863 : /*
18864 : * There is no ordering column in pg_stats_ext_exprs. However, we
18865 : * can rely on the unnesting of pg_statistic.ext_data.stxdexpr to
18866 : * maintain the desired order of expression elements.
18867 : */
18868 33 : appendPQExpBufferStr(pq,
18869 : "( "
18870 : "SELECT jsonb_pretty(jsonb_agg("
18871 : "nullif(j.obj, '{}'::jsonb))) "
18872 : "FROM pg_stats_ext_exprs AS ee "
18873 : "CROSS JOIN LATERAL jsonb_strip_nulls("
18874 : " jsonb_build_object( "
18875 : " 'null_frac', ee.null_frac::text, "
18876 : " 'avg_width', ee.avg_width::text, "
18877 : " 'n_distinct', ee.n_distinct::text, "
18878 : " 'most_common_vals', ee.most_common_vals::text, "
18879 : " 'most_common_freqs', ee.most_common_freqs::text, "
18880 : " 'histogram_bounds', ee.histogram_bounds::text, "
18881 : " 'correlation', ee.correlation::text, "
18882 : " 'most_common_elems', ee.most_common_elems::text, "
18883 : " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18884 : " 'elem_count_histogram', ee.elem_count_histogram::text");
18885 :
18886 : /* These three have been added to pg_stats_ext_exprs in v19. */
18887 33 : if (fout->remoteVersion >= 190000)
18888 33 : appendPQExpBufferStr(pq,
18889 : ", "
18890 : " 'range_length_histogram', ee.range_length_histogram::text, "
18891 : " 'range_empty_frac', ee.range_empty_frac::text, "
18892 : " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18893 :
18894 33 : appendPQExpBufferStr(pq,
18895 : " )) AS j(obj)"
18896 : "WHERE ee.statistics_schemaname = $1 "
18897 : "AND ee.statistics_name = $2 ");
18898 : /* Inherited expressions introduced in v15 */
18899 33 : if (fout->remoteVersion >= 150000)
18900 33 : appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18901 :
18902 33 : appendPQExpBufferStr(pq, ") AS exprs ");
18903 : }
18904 : else
18905 0 : appendPQExpBufferStr(pq, "NULL AS exprs ");
18906 :
18907 : /* pg_stats_ext introduced in v12 */
18908 33 : if (fout->remoteVersion >= 120000)
18909 33 : appendPQExpBufferStr(pq,
18910 : "FROM pg_catalog.pg_stats_ext AS e "
18911 : "WHERE e.statistics_schemaname = $1 "
18912 : "AND e.statistics_name = $2 ");
18913 : else
18914 0 : appendPQExpBufferStr(pq,
18915 : "FROM ( "
18916 : "SELECT s.stxndistinct AS n_distinct, "
18917 : " s.stxdependencies AS dependencies "
18918 : "FROM pg_catalog.pg_statistic_ext AS s "
18919 : "JOIN pg_catalog.pg_namespace AS n "
18920 : "ON n.oid = s.stxnamespace "
18921 : "WHERE n.nspname = $1 "
18922 : "AND s.stxname = $2 "
18923 : ") AS e ");
18924 :
18925 : /* we always have an inherited column, but it may be a constant */
18926 33 : appendPQExpBufferStr(pq, "ORDER BY inherited");
18927 :
18928 33 : ExecuteSqlStatement(fout, pq->data);
18929 :
18930 33 : fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS] = true;
18931 :
18932 33 : destroyPQExpBuffer(pq);
18933 : }
18934 :
18935 131 : query = createPQExpBuffer();
18936 :
18937 131 : appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18938 131 : appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18939 131 : appendPQExpBufferStr(query, "::pg_catalog.name, ");
18940 131 : appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18941 131 : appendPQExpBufferStr(query, "::pg_catalog.name)");
18942 :
18943 131 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18944 :
18945 131 : destroyPQExpBuffer(query);
18946 :
18947 131 : nstats = PQntuples(res);
18948 :
18949 131 : if (nstats > 0)
18950 : {
18951 36 : PQExpBuffer out = createPQExpBuffer();
18952 :
18953 36 : int i_inherited = PQfnumber(res, "inherited");
18954 36 : int i_ndistinct = PQfnumber(res, "n_distinct");
18955 36 : int i_dependencies = PQfnumber(res, "dependencies");
18956 36 : int i_mcv = PQfnumber(res, "most_common_vals");
18957 36 : int i_mcf = PQfnumber(res, "most_common_freqs");
18958 36 : int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18959 36 : int i_exprs = PQfnumber(res, "exprs");
18960 :
18961 72 : for (int i = 0; i < nstats; i++)
18962 : {
18963 36 : TableInfo *tbinfo = statsextinfo->stattable;
18964 :
18965 36 : if (PQgetisnull(res, i, i_inherited))
18966 0 : pg_fatal("inherited cannot be NULL");
18967 :
18968 36 : appendPQExpBufferStr(out,
18969 : "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18970 36 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18971 : fout->remoteVersion);
18972 :
18973 : /* Relation information */
18974 36 : appendPQExpBufferStr(out, "\t'schemaname', ");
18975 36 : appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18976 36 : appendPQExpBufferStr(out, ",\n\t'relname', ");
18977 36 : appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18978 :
18979 : /* Extended statistics information */
18980 36 : appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18981 36 : appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18982 36 : appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18983 36 : appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18984 36 : appendNamedArgument(out, fout, "inherited", "boolean",
18985 36 : PQgetvalue(res, i, i_inherited));
18986 :
18987 36 : if (!PQgetisnull(res, i, i_ndistinct))
18988 32 : appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18989 32 : PQgetvalue(res, i, i_ndistinct));
18990 :
18991 36 : if (!PQgetisnull(res, i, i_dependencies))
18992 33 : appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18993 33 : PQgetvalue(res, i, i_dependencies));
18994 :
18995 36 : if (!PQgetisnull(res, i, i_mcv))
18996 35 : appendNamedArgument(out, fout, "most_common_vals", "text[]",
18997 35 : PQgetvalue(res, i, i_mcv));
18998 :
18999 36 : if (!PQgetisnull(res, i, i_mcf))
19000 35 : appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
19001 35 : PQgetvalue(res, i, i_mcf));
19002 :
19003 36 : if (!PQgetisnull(res, i, i_mcbf))
19004 35 : appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
19005 35 : PQgetvalue(res, i, i_mcbf));
19006 :
19007 36 : if (!PQgetisnull(res, i, i_exprs))
19008 33 : appendNamedArgument(out, fout, "exprs", "jsonb",
19009 33 : PQgetvalue(res, i, i_exprs));
19010 :
19011 36 : appendPQExpBufferStr(out, "\n);\n");
19012 : }
19013 :
19014 36 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19015 36 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
19016 : .namespace = statsextinfo->dobj.namespace->dobj.name,
19017 : .owner = statsextinfo->rolname,
19018 : .description = "EXTENDED STATISTICS DATA",
19019 : .section = SECTION_POST_DATA,
19020 : .createStmt = out->data,
19021 : .deps = &statsextinfo->dobj.dumpId,
19022 : .nDeps = 1));
19023 36 : destroyPQExpBuffer(out);
19024 : }
19025 131 : PQclear(res);
19026 : }
19027 :
19028 : /*
19029 : * dumpConstraint
19030 : * write out to fout a user-defined constraint
19031 : */
19032 : static void
19033 2704 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
19034 : {
19035 2704 : DumpOptions *dopt = fout->dopt;
19036 2704 : TableInfo *tbinfo = coninfo->contable;
19037 : PQExpBuffer q;
19038 : PQExpBuffer delq;
19039 2704 : char *tag = NULL;
19040 : char *foreign;
19041 :
19042 : /* Do nothing if not dumping schema */
19043 2704 : if (!dopt->dumpSchema)
19044 98 : return;
19045 :
19046 2606 : q = createPQExpBuffer();
19047 2606 : delq = createPQExpBuffer();
19048 :
19049 5058 : foreign = tbinfo &&
19050 2606 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
19051 :
19052 2606 : if (coninfo->contype == 'p' ||
19053 1267 : coninfo->contype == 'u' ||
19054 1023 : coninfo->contype == 'x')
19055 1593 : {
19056 : /* Index-related constraint */
19057 : IndxInfo *indxinfo;
19058 : int k;
19059 :
19060 1593 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
19061 :
19062 1593 : if (indxinfo == NULL)
19063 0 : pg_fatal("missing index for constraint \"%s\"",
19064 : coninfo->dobj.name);
19065 :
19066 1593 : if (dopt->binary_upgrade)
19067 175 : binary_upgrade_set_pg_class_oids(fout, q,
19068 : indxinfo->dobj.catId.oid);
19069 :
19070 1593 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
19071 1593 : fmtQualifiedDumpable(tbinfo));
19072 1593 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
19073 1593 : fmtId(coninfo->dobj.name));
19074 :
19075 1593 : if (coninfo->condef)
19076 : {
19077 : /* pg_get_constraintdef should have provided everything */
19078 10 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
19079 : }
19080 : else
19081 : {
19082 1583 : appendPQExpBufferStr(q,
19083 1583 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
19084 :
19085 : /*
19086 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
19087 : * indexes. Being able to create this was fixed, but we need to
19088 : * make the index distinct in order to be able to restore the
19089 : * dump.
19090 : */
19091 1583 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
19092 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
19093 1583 : appendPQExpBufferStr(q, " (");
19094 3778 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
19095 : {
19096 2195 : int indkey = (int) indxinfo->indkeys[k];
19097 : const char *attname;
19098 :
19099 2195 : if (indkey == InvalidAttrNumber)
19100 0 : break;
19101 2195 : attname = getAttrName(indkey, tbinfo);
19102 :
19103 2195 : appendPQExpBuffer(q, "%s%s",
19104 : (k == 0) ? "" : ", ",
19105 : fmtId(attname));
19106 : }
19107 1583 : if (coninfo->conperiod)
19108 104 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
19109 :
19110 1583 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
19111 20 : appendPQExpBufferStr(q, ") INCLUDE (");
19112 :
19113 1623 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
19114 : {
19115 40 : int indkey = (int) indxinfo->indkeys[k];
19116 : const char *attname;
19117 :
19118 40 : if (indkey == InvalidAttrNumber)
19119 0 : break;
19120 40 : attname = getAttrName(indkey, tbinfo);
19121 :
19122 80 : appendPQExpBuffer(q, "%s%s",
19123 40 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
19124 : fmtId(attname));
19125 : }
19126 :
19127 1583 : appendPQExpBufferChar(q, ')');
19128 :
19129 1583 : if (nonemptyReloptions(indxinfo->indreloptions))
19130 : {
19131 0 : appendPQExpBufferStr(q, " WITH (");
19132 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
19133 0 : appendPQExpBufferChar(q, ')');
19134 : }
19135 :
19136 1583 : if (coninfo->condeferrable)
19137 : {
19138 25 : appendPQExpBufferStr(q, " DEFERRABLE");
19139 25 : if (coninfo->condeferred)
19140 15 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
19141 : }
19142 :
19143 1583 : appendPQExpBufferStr(q, ";\n");
19144 : }
19145 :
19146 : /*
19147 : * Append ALTER TABLE commands as needed to set properties that we
19148 : * only have ALTER TABLE syntax for. Keep this in sync with the
19149 : * similar code in dumpIndex!
19150 : */
19151 :
19152 : /* If the index is clustered, we need to record that. */
19153 1593 : if (indxinfo->indisclustered)
19154 : {
19155 32 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
19156 32 : fmtQualifiedDumpable(tbinfo));
19157 : /* index name is not qualified in this syntax */
19158 32 : appendPQExpBuffer(q, " ON %s;\n",
19159 32 : fmtId(indxinfo->dobj.name));
19160 : }
19161 :
19162 : /* If the index defines identity, we need to record that. */
19163 1593 : if (indxinfo->indisreplident)
19164 : {
19165 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
19166 0 : fmtQualifiedDumpable(tbinfo));
19167 : /* index name is not qualified in this syntax */
19168 0 : appendPQExpBuffer(q, " INDEX %s;\n",
19169 0 : fmtId(indxinfo->dobj.name));
19170 : }
19171 :
19172 : /* Indexes can depend on extensions */
19173 1593 : append_depends_on_extension(fout, q, &indxinfo->dobj,
19174 : "pg_catalog.pg_class", "INDEX",
19175 1593 : fmtQualifiedDumpable(indxinfo));
19176 :
19177 1593 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
19178 1593 : fmtQualifiedDumpable(tbinfo));
19179 1593 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19180 1593 : fmtId(coninfo->dobj.name));
19181 :
19182 1593 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19183 :
19184 1593 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19185 1593 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19186 1593 : ARCHIVE_OPTS(.tag = tag,
19187 : .namespace = tbinfo->dobj.namespace->dobj.name,
19188 : .tablespace = indxinfo->tablespace,
19189 : .owner = tbinfo->rolname,
19190 : .description = "CONSTRAINT",
19191 : .section = SECTION_POST_DATA,
19192 : .createStmt = q->data,
19193 : .dropStmt = delq->data));
19194 : }
19195 1013 : else if (coninfo->contype == 'f')
19196 : {
19197 : char *only;
19198 :
19199 : /*
19200 : * Foreign keys on partitioned tables are always declared as
19201 : * inheriting to partitions; for all other cases, emit them as
19202 : * applying ONLY directly to the named table, because that's how they
19203 : * work for regular inherited tables.
19204 : */
19205 219 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
19206 :
19207 : /*
19208 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
19209 : * current table data is not processed
19210 : */
19211 219 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
19212 219 : only, fmtQualifiedDumpable(tbinfo));
19213 219 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19214 219 : fmtId(coninfo->dobj.name),
19215 219 : coninfo->condef);
19216 :
19217 219 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
19218 219 : only, fmtQualifiedDumpable(tbinfo));
19219 219 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19220 219 : fmtId(coninfo->dobj.name));
19221 :
19222 219 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19223 :
19224 219 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19225 219 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19226 219 : ARCHIVE_OPTS(.tag = tag,
19227 : .namespace = tbinfo->dobj.namespace->dobj.name,
19228 : .owner = tbinfo->rolname,
19229 : .description = "FK CONSTRAINT",
19230 : .section = SECTION_POST_DATA,
19231 : .createStmt = q->data,
19232 : .dropStmt = delq->data));
19233 : }
19234 794 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
19235 : {
19236 : /* CHECK or invalid not-null constraint on a table */
19237 :
19238 : /* Ignore if not to be dumped separately, or if it was inherited */
19239 640 : if (coninfo->separate && coninfo->conislocal)
19240 : {
19241 : const char *keyword;
19242 :
19243 107 : if (coninfo->contype == 'c')
19244 45 : keyword = "CHECK CONSTRAINT";
19245 : else
19246 62 : keyword = "CONSTRAINT";
19247 :
19248 : /* not ONLY since we want it to propagate to children */
19249 107 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19250 107 : fmtQualifiedDumpable(tbinfo));
19251 107 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19252 107 : fmtId(coninfo->dobj.name),
19253 107 : coninfo->condef);
19254 :
19255 107 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19256 107 : fmtQualifiedDumpable(tbinfo));
19257 107 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19258 107 : fmtId(coninfo->dobj.name));
19259 :
19260 107 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19261 :
19262 107 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19263 107 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19264 107 : ARCHIVE_OPTS(.tag = tag,
19265 : .namespace = tbinfo->dobj.namespace->dobj.name,
19266 : .owner = tbinfo->rolname,
19267 : .description = keyword,
19268 : .section = SECTION_POST_DATA,
19269 : .createStmt = q->data,
19270 : .dropStmt = delq->data));
19271 : }
19272 : }
19273 154 : else if (tbinfo == NULL)
19274 : {
19275 : /* CHECK, NOT NULL constraint on a domain */
19276 154 : TypeInfo *tyinfo = coninfo->condomain;
19277 :
19278 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19279 :
19280 : /* Ignore if not to be dumped separately */
19281 154 : if (coninfo->separate)
19282 : {
19283 : const char *keyword;
19284 :
19285 5 : if (coninfo->contype == 'c')
19286 5 : keyword = "CHECK CONSTRAINT";
19287 : else
19288 0 : keyword = "CONSTRAINT";
19289 :
19290 5 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19291 5 : fmtQualifiedDumpable(tyinfo));
19292 5 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19293 5 : fmtId(coninfo->dobj.name),
19294 5 : coninfo->condef);
19295 :
19296 5 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19297 5 : fmtQualifiedDumpable(tyinfo));
19298 5 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19299 5 : fmtId(coninfo->dobj.name));
19300 :
19301 5 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19302 :
19303 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19304 5 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19305 5 : ARCHIVE_OPTS(.tag = tag,
19306 : .namespace = tyinfo->dobj.namespace->dobj.name,
19307 : .owner = tyinfo->rolname,
19308 : .description = keyword,
19309 : .section = SECTION_POST_DATA,
19310 : .createStmt = q->data,
19311 : .dropStmt = delq->data));
19312 :
19313 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19314 : {
19315 5 : PQExpBuffer conprefix = createPQExpBuffer();
19316 5 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19317 :
19318 5 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19319 5 : fmtId(coninfo->dobj.name));
19320 :
19321 5 : dumpComment(fout, conprefix->data, qtypname,
19322 5 : tyinfo->dobj.namespace->dobj.name,
19323 : tyinfo->rolname,
19324 5 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19325 5 : destroyPQExpBuffer(conprefix);
19326 5 : free(qtypname);
19327 : }
19328 : }
19329 : }
19330 : else
19331 : {
19332 0 : pg_fatal("unrecognized constraint type: %c",
19333 : coninfo->contype);
19334 : }
19335 :
19336 : /* Dump Constraint Comments --- only works for table constraints */
19337 2606 : if (tbinfo && coninfo->separate &&
19338 1949 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19339 47 : dumpTableConstraintComment(fout, coninfo);
19340 :
19341 2606 : free(tag);
19342 2606 : destroyPQExpBuffer(q);
19343 2606 : destroyPQExpBuffer(delq);
19344 : }
19345 :
19346 : /*
19347 : * dumpTableConstraintComment --- dump a constraint's comment if any
19348 : *
19349 : * This is split out because we need the function in two different places
19350 : * depending on whether the constraint is dumped as part of CREATE TABLE
19351 : * or as a separate ALTER command.
19352 : */
19353 : static void
19354 84 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
19355 : {
19356 84 : TableInfo *tbinfo = coninfo->contable;
19357 84 : PQExpBuffer conprefix = createPQExpBuffer();
19358 : char *qtabname;
19359 :
19360 84 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19361 :
19362 84 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19363 84 : fmtId(coninfo->dobj.name));
19364 :
19365 84 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19366 84 : dumpComment(fout, conprefix->data, qtabname,
19367 84 : tbinfo->dobj.namespace->dobj.name,
19368 : tbinfo->rolname,
19369 : coninfo->dobj.catId, 0,
19370 84 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19371 :
19372 84 : destroyPQExpBuffer(conprefix);
19373 84 : free(qtabname);
19374 84 : }
19375 :
19376 : static inline SeqType
19377 647 : parse_sequence_type(const char *name)
19378 : {
19379 1452 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
19380 : {
19381 1452 : if (strcmp(SeqTypeNames[i], name) == 0)
19382 647 : return (SeqType) i;
19383 : }
19384 :
19385 0 : pg_fatal("unrecognized sequence type: %s", name);
19386 : return (SeqType) 0; /* keep compiler quiet */
19387 : }
19388 :
19389 : /*
19390 : * bsearch() comparator for SequenceItem
19391 : */
19392 : static int
19393 2974 : SequenceItemCmp(const void *p1, const void *p2)
19394 : {
19395 2974 : SequenceItem v1 = *((const SequenceItem *) p1);
19396 2974 : SequenceItem v2 = *((const SequenceItem *) p2);
19397 :
19398 2974 : return pg_cmp_u32(v1.oid, v2.oid);
19399 : }
19400 :
19401 : /*
19402 : * collectSequences
19403 : *
19404 : * Construct a table of sequence information. This table is sorted by OID for
19405 : * speed in lookup.
19406 : */
19407 : static void
19408 259 : collectSequences(Archive *fout)
19409 : {
19410 : PGresult *res;
19411 : const char *query;
19412 :
19413 : /*
19414 : * Before Postgres 10, sequence metadata is in the sequence itself. With
19415 : * some extra effort, we might be able to use the sorted table for those
19416 : * versions, but for now it seems unlikely to be worth it.
19417 : *
19418 : * Since version 18, we can gather the sequence data in this query with
19419 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19420 : */
19421 259 : if (fout->remoteVersion < 100000)
19422 0 : return;
19423 259 : else if (fout->remoteVersion < 180000 ||
19424 259 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
19425 8 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19426 : "seqstart, seqincrement, "
19427 : "seqmax, seqmin, "
19428 : "seqcache, seqcycle, "
19429 : "NULL, 'f' "
19430 : "FROM pg_catalog.pg_sequence "
19431 : "ORDER BY seqrelid";
19432 : else
19433 251 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19434 : "seqstart, seqincrement, "
19435 : "seqmax, seqmin, "
19436 : "seqcache, seqcycle, "
19437 : "last_value, is_called "
19438 : "FROM pg_catalog.pg_sequence, "
19439 : "pg_get_sequence_data(seqrelid) "
19440 : "ORDER BY seqrelid;";
19441 :
19442 259 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19443 :
19444 259 : nsequences = PQntuples(res);
19445 259 : sequences = pg_malloc_array(SequenceItem, nsequences);
19446 :
19447 906 : for (int i = 0; i < nsequences; i++)
19448 : {
19449 647 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19450 647 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
19451 647 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19452 647 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19453 647 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19454 647 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19455 647 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19456 647 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19457 647 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19458 647 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19459 647 : sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19460 : }
19461 :
19462 259 : PQclear(res);
19463 : }
19464 :
19465 : /*
19466 : * dumpSequence
19467 : * write the declaration (not data) of one user-defined sequence
19468 : */
19469 : static void
19470 384 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
19471 : {
19472 384 : DumpOptions *dopt = fout->dopt;
19473 : SequenceItem *seq;
19474 : bool is_ascending;
19475 : int64 default_minv,
19476 : default_maxv;
19477 384 : PQExpBuffer query = createPQExpBuffer();
19478 384 : PQExpBuffer delqry = createPQExpBuffer();
19479 : char *qseqname;
19480 384 : TableInfo *owning_tab = NULL;
19481 :
19482 384 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19483 :
19484 : /*
19485 : * For versions >= 10, the sequence information is gathered in a sorted
19486 : * table before any calls to dumpSequence(). See collectSequences() for
19487 : * more information.
19488 : */
19489 384 : if (fout->remoteVersion >= 100000)
19490 : {
19491 384 : SequenceItem key = {0};
19492 :
19493 : Assert(sequences);
19494 :
19495 384 : key.oid = tbinfo->dobj.catId.oid;
19496 384 : seq = bsearch(&key, sequences, nsequences,
19497 : sizeof(SequenceItem), SequenceItemCmp);
19498 : }
19499 : else
19500 : {
19501 : PGresult *res;
19502 :
19503 : /*
19504 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19505 : *
19506 : * Note: it might seem that 'bigint' potentially needs to be
19507 : * schema-qualified, but actually that's a keyword.
19508 : */
19509 0 : appendPQExpBuffer(query,
19510 : "SELECT 'bigint' AS sequence_type, "
19511 : "start_value, increment_by, max_value, min_value, "
19512 : "cache_value, is_cycled FROM %s",
19513 0 : fmtQualifiedDumpable(tbinfo));
19514 :
19515 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19516 :
19517 0 : if (PQntuples(res) != 1)
19518 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19519 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19520 : PQntuples(res)),
19521 : tbinfo->dobj.name, PQntuples(res));
19522 :
19523 0 : seq = pg_malloc0_object(SequenceItem);
19524 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19525 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19526 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19527 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19528 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19529 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19530 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19531 :
19532 0 : PQclear(res);
19533 : }
19534 :
19535 : /* Calculate default limits for a sequence of this type */
19536 384 : is_ascending = (seq->incby >= 0);
19537 384 : if (seq->seqtype == SEQTYPE_SMALLINT)
19538 : {
19539 25 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19540 25 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19541 : }
19542 359 : else if (seq->seqtype == SEQTYPE_INTEGER)
19543 : {
19544 284 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19545 284 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19546 : }
19547 75 : else if (seq->seqtype == SEQTYPE_BIGINT)
19548 : {
19549 75 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19550 75 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19551 : }
19552 : else
19553 : {
19554 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19555 : default_minv = default_maxv = 0; /* keep compiler quiet */
19556 : }
19557 :
19558 : /*
19559 : * Identity sequences are not to be dropped separately.
19560 : */
19561 384 : if (!tbinfo->is_identity_sequence)
19562 : {
19563 242 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19564 242 : fmtQualifiedDumpable(tbinfo));
19565 : }
19566 :
19567 384 : resetPQExpBuffer(query);
19568 :
19569 384 : if (dopt->binary_upgrade)
19570 : {
19571 66 : binary_upgrade_set_pg_class_oids(fout, query,
19572 66 : tbinfo->dobj.catId.oid);
19573 :
19574 : /*
19575 : * In older PG versions a sequence will have a pg_type entry, but v14
19576 : * and up don't use that, so don't attempt to preserve the type OID.
19577 : */
19578 : }
19579 :
19580 384 : if (tbinfo->is_identity_sequence)
19581 : {
19582 142 : owning_tab = findTableByOid(tbinfo->owning_tab);
19583 :
19584 142 : appendPQExpBuffer(query,
19585 : "ALTER TABLE %s ",
19586 142 : fmtQualifiedDumpable(owning_tab));
19587 142 : appendPQExpBuffer(query,
19588 : "ALTER COLUMN %s ADD GENERATED ",
19589 142 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19590 142 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19591 102 : appendPQExpBufferStr(query, "ALWAYS");
19592 40 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19593 40 : appendPQExpBufferStr(query, "BY DEFAULT");
19594 142 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19595 142 : fmtQualifiedDumpable(tbinfo));
19596 :
19597 : /*
19598 : * Emit persistence option only if it's different from the owning
19599 : * table's. This avoids using this new syntax unnecessarily.
19600 : */
19601 142 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19602 10 : appendPQExpBuffer(query, " %s\n",
19603 10 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19604 : "UNLOGGED" : "LOGGED");
19605 : }
19606 : else
19607 : {
19608 242 : appendPQExpBuffer(query,
19609 : "CREATE %sSEQUENCE %s\n",
19610 242 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19611 : "UNLOGGED " : "",
19612 242 : fmtQualifiedDumpable(tbinfo));
19613 :
19614 242 : if (seq->seqtype != SEQTYPE_BIGINT)
19615 182 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19616 : }
19617 :
19618 384 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19619 :
19620 384 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19621 :
19622 384 : if (seq->minv != default_minv)
19623 15 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19624 : else
19625 369 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19626 :
19627 384 : if (seq->maxv != default_maxv)
19628 15 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19629 : else
19630 369 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19631 :
19632 384 : appendPQExpBuffer(query,
19633 : " CACHE " INT64_FORMAT "%s",
19634 384 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19635 :
19636 384 : if (tbinfo->is_identity_sequence)
19637 142 : appendPQExpBufferStr(query, "\n);\n");
19638 : else
19639 242 : appendPQExpBufferStr(query, ";\n");
19640 :
19641 : /* binary_upgrade: no need to clear TOAST table oid */
19642 :
19643 384 : if (dopt->binary_upgrade)
19644 66 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19645 : "SEQUENCE", qseqname,
19646 66 : tbinfo->dobj.namespace->dobj.name);
19647 :
19648 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19649 384 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19650 384 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19651 : .namespace = tbinfo->dobj.namespace->dobj.name,
19652 : .owner = tbinfo->rolname,
19653 : .description = "SEQUENCE",
19654 : .section = SECTION_PRE_DATA,
19655 : .createStmt = query->data,
19656 : .dropStmt = delqry->data));
19657 :
19658 : /*
19659 : * If the sequence is owned by a table column, emit the ALTER for it as a
19660 : * separate TOC entry immediately following the sequence's own entry. It's
19661 : * OK to do this rather than using full sorting logic, because the
19662 : * dependency that tells us it's owned will have forced the table to be
19663 : * created first. We can't just include the ALTER in the TOC entry
19664 : * because it will fail if we haven't reassigned the sequence owner to
19665 : * match the table's owner.
19666 : *
19667 : * We need not schema-qualify the table reference because both sequence
19668 : * and table must be in the same schema.
19669 : */
19670 384 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19671 : {
19672 137 : owning_tab = findTableByOid(tbinfo->owning_tab);
19673 :
19674 137 : if (owning_tab == NULL)
19675 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19676 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19677 :
19678 137 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19679 : {
19680 135 : resetPQExpBuffer(query);
19681 135 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19682 135 : fmtQualifiedDumpable(tbinfo));
19683 135 : appendPQExpBuffer(query, " OWNED BY %s",
19684 135 : fmtQualifiedDumpable(owning_tab));
19685 135 : appendPQExpBuffer(query, ".%s;\n",
19686 135 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19687 :
19688 135 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19689 135 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19690 135 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19691 : .namespace = tbinfo->dobj.namespace->dobj.name,
19692 : .owner = tbinfo->rolname,
19693 : .description = "SEQUENCE OWNED BY",
19694 : .section = SECTION_PRE_DATA,
19695 : .createStmt = query->data,
19696 : .deps = &(tbinfo->dobj.dumpId),
19697 : .nDeps = 1));
19698 : }
19699 : }
19700 :
19701 : /* Dump Sequence Comments and Security Labels */
19702 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19703 0 : dumpComment(fout, "SEQUENCE", qseqname,
19704 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19705 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19706 :
19707 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19708 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19709 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19710 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19711 :
19712 384 : if (fout->remoteVersion < 100000)
19713 0 : pg_free(seq);
19714 384 : destroyPQExpBuffer(query);
19715 384 : destroyPQExpBuffer(delqry);
19716 384 : free(qseqname);
19717 384 : }
19718 :
19719 : /*
19720 : * dumpSequenceData
19721 : * write the data of one user-defined sequence
19722 : */
19723 : static void
19724 402 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19725 : {
19726 402 : TableInfo *tbinfo = tdinfo->tdtable;
19727 : int64 last;
19728 : bool called;
19729 : PQExpBuffer query;
19730 :
19731 : /* needn't bother if not dumping sequence data */
19732 402 : if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19733 1 : return;
19734 :
19735 401 : query = createPQExpBuffer();
19736 :
19737 : /*
19738 : * For versions >= 18, the sequence information is gathered in the sorted
19739 : * array before any calls to dumpSequenceData(). See collectSequences()
19740 : * for more information.
19741 : *
19742 : * For older versions, we have to query the sequence relations
19743 : * individually.
19744 : */
19745 401 : if (fout->remoteVersion < 180000)
19746 : {
19747 : PGresult *res;
19748 :
19749 0 : appendPQExpBuffer(query,
19750 : "SELECT last_value, is_called FROM %s",
19751 0 : fmtQualifiedDumpable(tbinfo));
19752 :
19753 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19754 :
19755 0 : if (PQntuples(res) != 1)
19756 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19757 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19758 : PQntuples(res)),
19759 : tbinfo->dobj.name, PQntuples(res));
19760 :
19761 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19762 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19763 :
19764 0 : PQclear(res);
19765 : }
19766 : else
19767 : {
19768 401 : SequenceItem key = {0};
19769 : SequenceItem *entry;
19770 :
19771 : Assert(sequences);
19772 : Assert(tbinfo->dobj.catId.oid);
19773 :
19774 401 : key.oid = tbinfo->dobj.catId.oid;
19775 401 : entry = bsearch(&key, sequences, nsequences,
19776 : sizeof(SequenceItem), SequenceItemCmp);
19777 :
19778 401 : if (entry->null_seqtuple)
19779 0 : pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19780 : "SELECT privilege on the sequence or the sequence may "
19781 : "have been concurrently dropped",
19782 : tbinfo->dobj.name);
19783 :
19784 401 : last = entry->last_value;
19785 401 : called = entry->is_called;
19786 : }
19787 :
19788 401 : resetPQExpBuffer(query);
19789 401 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19790 401 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19791 401 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19792 : last, (called ? "true" : "false"));
19793 :
19794 401 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19795 401 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19796 401 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19797 : .namespace = tbinfo->dobj.namespace->dobj.name,
19798 : .owner = tbinfo->rolname,
19799 : .description = "SEQUENCE SET",
19800 : .section = SECTION_DATA,
19801 : .createStmt = query->data,
19802 : .deps = &(tbinfo->dobj.dumpId),
19803 : .nDeps = 1));
19804 :
19805 401 : destroyPQExpBuffer(query);
19806 : }
19807 :
19808 : /*
19809 : * dumpTrigger
19810 : * write the declaration of one user-defined table trigger
19811 : */
19812 : static void
19813 523 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19814 : {
19815 523 : DumpOptions *dopt = fout->dopt;
19816 523 : TableInfo *tbinfo = tginfo->tgtable;
19817 : PQExpBuffer query;
19818 : PQExpBuffer delqry;
19819 : PQExpBuffer trigprefix;
19820 : PQExpBuffer trigidentity;
19821 : char *qtabname;
19822 : char *tag;
19823 :
19824 : /* Do nothing if not dumping schema */
19825 523 : if (!dopt->dumpSchema)
19826 31 : return;
19827 :
19828 492 : query = createPQExpBuffer();
19829 492 : delqry = createPQExpBuffer();
19830 492 : trigprefix = createPQExpBuffer();
19831 492 : trigidentity = createPQExpBuffer();
19832 :
19833 492 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19834 :
19835 492 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19836 492 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19837 :
19838 492 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19839 492 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19840 :
19841 : /* Triggers can depend on extensions */
19842 492 : append_depends_on_extension(fout, query, &tginfo->dobj,
19843 : "pg_catalog.pg_trigger", "TRIGGER",
19844 492 : trigidentity->data);
19845 :
19846 492 : if (tginfo->tgispartition)
19847 : {
19848 : Assert(tbinfo->ispartition);
19849 :
19850 : /*
19851 : * Partition triggers only appear here because their 'tgenabled' flag
19852 : * differs from its parent's. The trigger is created already, so
19853 : * remove the CREATE and replace it with an ALTER. (Clear out the
19854 : * DROP query too, so that pg_dump --create does not cause errors.)
19855 : */
19856 109 : resetPQExpBuffer(query);
19857 109 : resetPQExpBuffer(delqry);
19858 109 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19859 109 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19860 109 : fmtQualifiedDumpable(tbinfo));
19861 109 : switch (tginfo->tgenabled)
19862 : {
19863 38 : case 'f':
19864 : case 'D':
19865 38 : appendPQExpBufferStr(query, "DISABLE");
19866 38 : break;
19867 0 : case 't':
19868 : case 'O':
19869 0 : appendPQExpBufferStr(query, "ENABLE");
19870 0 : break;
19871 33 : case 'R':
19872 33 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19873 33 : break;
19874 38 : case 'A':
19875 38 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19876 38 : break;
19877 : }
19878 109 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19879 109 : fmtId(tginfo->dobj.name));
19880 : }
19881 383 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19882 : {
19883 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19884 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19885 0 : fmtQualifiedDumpable(tbinfo));
19886 0 : switch (tginfo->tgenabled)
19887 : {
19888 0 : case 'D':
19889 : case 'f':
19890 0 : appendPQExpBufferStr(query, "DISABLE");
19891 0 : break;
19892 0 : case 'A':
19893 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19894 0 : break;
19895 0 : case 'R':
19896 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19897 0 : break;
19898 0 : default:
19899 0 : appendPQExpBufferStr(query, "ENABLE");
19900 0 : break;
19901 : }
19902 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19903 0 : fmtId(tginfo->dobj.name));
19904 : }
19905 :
19906 492 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19907 492 : fmtId(tginfo->dobj.name));
19908 :
19909 492 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19910 :
19911 492 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19912 492 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19913 492 : ARCHIVE_OPTS(.tag = tag,
19914 : .namespace = tbinfo->dobj.namespace->dobj.name,
19915 : .owner = tbinfo->rolname,
19916 : .description = "TRIGGER",
19917 : .section = SECTION_POST_DATA,
19918 : .createStmt = query->data,
19919 : .dropStmt = delqry->data));
19920 :
19921 492 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19922 0 : dumpComment(fout, trigprefix->data, qtabname,
19923 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19924 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19925 :
19926 492 : free(tag);
19927 492 : destroyPQExpBuffer(query);
19928 492 : destroyPQExpBuffer(delqry);
19929 492 : destroyPQExpBuffer(trigprefix);
19930 492 : destroyPQExpBuffer(trigidentity);
19931 492 : free(qtabname);
19932 : }
19933 :
19934 : /*
19935 : * dumpEventTrigger
19936 : * write the declaration of one user-defined event trigger
19937 : */
19938 : static void
19939 42 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19940 : {
19941 42 : DumpOptions *dopt = fout->dopt;
19942 : PQExpBuffer query;
19943 : PQExpBuffer delqry;
19944 : char *qevtname;
19945 :
19946 : /* Do nothing if not dumping schema */
19947 42 : if (!dopt->dumpSchema)
19948 6 : return;
19949 :
19950 36 : query = createPQExpBuffer();
19951 36 : delqry = createPQExpBuffer();
19952 :
19953 36 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19954 :
19955 36 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19956 36 : appendPQExpBufferStr(query, qevtname);
19957 36 : appendPQExpBufferStr(query, " ON ");
19958 36 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19959 :
19960 36 : if (strcmp("", evtinfo->evttags) != 0)
19961 : {
19962 5 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19963 5 : appendPQExpBufferStr(query, evtinfo->evttags);
19964 5 : appendPQExpBufferChar(query, ')');
19965 : }
19966 :
19967 36 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19968 36 : appendPQExpBufferStr(query, evtinfo->evtfname);
19969 36 : appendPQExpBufferStr(query, "();\n");
19970 :
19971 36 : if (evtinfo->evtenabled != 'O')
19972 : {
19973 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19974 : qevtname);
19975 0 : switch (evtinfo->evtenabled)
19976 : {
19977 0 : case 'D':
19978 0 : appendPQExpBufferStr(query, "DISABLE");
19979 0 : break;
19980 0 : case 'A':
19981 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19982 0 : break;
19983 0 : case 'R':
19984 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19985 0 : break;
19986 0 : default:
19987 0 : appendPQExpBufferStr(query, "ENABLE");
19988 0 : break;
19989 : }
19990 0 : appendPQExpBufferStr(query, ";\n");
19991 : }
19992 :
19993 36 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19994 : qevtname);
19995 :
19996 36 : if (dopt->binary_upgrade)
19997 2 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19998 : "EVENT TRIGGER", qevtname, NULL);
19999 :
20000 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20001 36 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
20002 36 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
20003 : .owner = evtinfo->evtowner,
20004 : .description = "EVENT TRIGGER",
20005 : .section = SECTION_POST_DATA,
20006 : .createStmt = query->data,
20007 : .dropStmt = delqry->data));
20008 :
20009 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20010 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
20011 0 : NULL, evtinfo->evtowner,
20012 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20013 :
20014 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
20015 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
20016 0 : NULL, evtinfo->evtowner,
20017 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20018 :
20019 36 : destroyPQExpBuffer(query);
20020 36 : destroyPQExpBuffer(delqry);
20021 36 : free(qevtname);
20022 : }
20023 :
20024 : /*
20025 : * dumpRule
20026 : * Dump a rule
20027 : */
20028 : static void
20029 1150 : dumpRule(Archive *fout, const RuleInfo *rinfo)
20030 : {
20031 1150 : DumpOptions *dopt = fout->dopt;
20032 1150 : TableInfo *tbinfo = rinfo->ruletable;
20033 : bool is_view;
20034 : PQExpBuffer query;
20035 : PQExpBuffer cmd;
20036 : PQExpBuffer delcmd;
20037 : PQExpBuffer ruleprefix;
20038 : char *qtabname;
20039 : PGresult *res;
20040 : char *tag;
20041 :
20042 : /* Do nothing if not dumping schema */
20043 1150 : if (!dopt->dumpSchema)
20044 60 : return;
20045 :
20046 : /*
20047 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
20048 : * we do not want to dump it as a separate object.
20049 : */
20050 1090 : if (!rinfo->separate)
20051 879 : return;
20052 :
20053 : /*
20054 : * If it's an ON SELECT rule, we want to print it as a view definition,
20055 : * instead of a rule.
20056 : */
20057 211 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
20058 :
20059 211 : query = createPQExpBuffer();
20060 211 : cmd = createPQExpBuffer();
20061 211 : delcmd = createPQExpBuffer();
20062 211 : ruleprefix = createPQExpBuffer();
20063 :
20064 211 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
20065 :
20066 211 : if (is_view)
20067 : {
20068 : PQExpBuffer result;
20069 :
20070 : /*
20071 : * We need OR REPLACE here because we'll be replacing a dummy view.
20072 : * Otherwise this should look largely like the regular view dump code.
20073 : */
20074 10 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
20075 10 : fmtQualifiedDumpable(tbinfo));
20076 10 : if (nonemptyReloptions(tbinfo->reloptions))
20077 : {
20078 0 : appendPQExpBufferStr(cmd, " WITH (");
20079 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
20080 0 : appendPQExpBufferChar(cmd, ')');
20081 : }
20082 10 : result = createViewAsClause(fout, tbinfo);
20083 10 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
20084 10 : destroyPQExpBuffer(result);
20085 10 : if (tbinfo->checkoption != NULL)
20086 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
20087 : tbinfo->checkoption);
20088 10 : appendPQExpBufferStr(cmd, ";\n");
20089 : }
20090 : else
20091 : {
20092 : /* In the rule case, just print pg_get_ruledef's result verbatim */
20093 201 : appendPQExpBuffer(query,
20094 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
20095 201 : rinfo->dobj.catId.oid);
20096 :
20097 201 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20098 :
20099 201 : if (PQntuples(res) != 1)
20100 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
20101 : rinfo->dobj.name, tbinfo->dobj.name);
20102 :
20103 201 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
20104 :
20105 201 : PQclear(res);
20106 : }
20107 :
20108 : /*
20109 : * Add the command to alter the rules replication firing semantics if it
20110 : * differs from the default.
20111 : */
20112 211 : if (rinfo->ev_enabled != 'O')
20113 : {
20114 15 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
20115 15 : switch (rinfo->ev_enabled)
20116 : {
20117 0 : case 'A':
20118 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
20119 0 : fmtId(rinfo->dobj.name));
20120 0 : break;
20121 0 : case 'R':
20122 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
20123 0 : fmtId(rinfo->dobj.name));
20124 0 : break;
20125 15 : case 'D':
20126 15 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
20127 15 : fmtId(rinfo->dobj.name));
20128 15 : break;
20129 : }
20130 : }
20131 :
20132 211 : if (is_view)
20133 : {
20134 : /*
20135 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
20136 : * REPLACE VIEW to replace the rule with something with minimal
20137 : * dependencies.
20138 : */
20139 : PQExpBuffer result;
20140 :
20141 10 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
20142 10 : fmtQualifiedDumpable(tbinfo));
20143 10 : result = createDummyViewAsClause(fout, tbinfo);
20144 10 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
20145 10 : destroyPQExpBuffer(result);
20146 : }
20147 : else
20148 : {
20149 201 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
20150 201 : fmtId(rinfo->dobj.name));
20151 201 : appendPQExpBuffer(delcmd, "ON %s;\n",
20152 201 : fmtQualifiedDumpable(tbinfo));
20153 : }
20154 :
20155 211 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
20156 211 : fmtId(rinfo->dobj.name));
20157 :
20158 211 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
20159 :
20160 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20161 211 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
20162 211 : ARCHIVE_OPTS(.tag = tag,
20163 : .namespace = tbinfo->dobj.namespace->dobj.name,
20164 : .owner = tbinfo->rolname,
20165 : .description = "RULE",
20166 : .section = SECTION_POST_DATA,
20167 : .createStmt = cmd->data,
20168 : .dropStmt = delcmd->data));
20169 :
20170 : /* Dump rule comments */
20171 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20172 0 : dumpComment(fout, ruleprefix->data, qtabname,
20173 0 : tbinfo->dobj.namespace->dobj.name,
20174 : tbinfo->rolname,
20175 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
20176 :
20177 211 : free(tag);
20178 211 : destroyPQExpBuffer(query);
20179 211 : destroyPQExpBuffer(cmd);
20180 211 : destroyPQExpBuffer(delcmd);
20181 211 : destroyPQExpBuffer(ruleprefix);
20182 211 : free(qtabname);
20183 : }
20184 :
20185 : /*
20186 : * getExtensionMembership --- obtain extension membership data
20187 : *
20188 : * We need to identify objects that are extension members as soon as they're
20189 : * loaded, so that we can correctly determine whether they need to be dumped.
20190 : * Generally speaking, extension member objects will get marked as *not* to
20191 : * be dumped, as they will be recreated by the single CREATE EXTENSION
20192 : * command. However, in binary upgrade mode we still need to dump the members
20193 : * individually.
20194 : */
20195 : void
20196 260 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
20197 : int numExtensions)
20198 : {
20199 : PQExpBuffer query;
20200 : PGresult *res;
20201 : int ntups,
20202 : i;
20203 : int i_classid,
20204 : i_objid,
20205 : i_refobjid;
20206 : ExtensionInfo *ext;
20207 :
20208 : /* Nothing to do if no extensions */
20209 260 : if (numExtensions == 0)
20210 0 : return;
20211 :
20212 260 : query = createPQExpBuffer();
20213 :
20214 : /* refclassid constraint is redundant but may speed the search */
20215 260 : appendPQExpBufferStr(query, "SELECT "
20216 : "classid, objid, refobjid "
20217 : "FROM pg_depend "
20218 : "WHERE refclassid = 'pg_extension'::regclass "
20219 : "AND deptype = 'e' "
20220 : "ORDER BY 3");
20221 :
20222 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20223 :
20224 260 : ntups = PQntuples(res);
20225 :
20226 260 : i_classid = PQfnumber(res, "classid");
20227 260 : i_objid = PQfnumber(res, "objid");
20228 260 : i_refobjid = PQfnumber(res, "refobjid");
20229 :
20230 : /*
20231 : * Since we ordered the SELECT by referenced ID, we can expect that
20232 : * multiple entries for the same extension will appear together; this
20233 : * saves on searches.
20234 : */
20235 260 : ext = NULL;
20236 :
20237 1906 : for (i = 0; i < ntups; i++)
20238 : {
20239 : CatalogId objId;
20240 : Oid extId;
20241 :
20242 1646 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20243 1646 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20244 1646 : extId = atooid(PQgetvalue(res, i, i_refobjid));
20245 :
20246 1646 : if (ext == NULL ||
20247 1386 : ext->dobj.catId.oid != extId)
20248 291 : ext = findExtensionByOid(extId);
20249 :
20250 1646 : if (ext == NULL)
20251 : {
20252 : /* shouldn't happen */
20253 0 : pg_log_warning("could not find referenced extension %u", extId);
20254 0 : continue;
20255 : }
20256 :
20257 1646 : recordExtensionMembership(objId, ext);
20258 : }
20259 :
20260 260 : PQclear(res);
20261 :
20262 260 : destroyPQExpBuffer(query);
20263 : }
20264 :
20265 : /*
20266 : * processExtensionTables --- deal with extension configuration tables
20267 : *
20268 : * There are two parts to this process:
20269 : *
20270 : * 1. Identify and create dump records for extension configuration tables.
20271 : *
20272 : * Extensions can mark tables as "configuration", which means that the user
20273 : * is able and expected to modify those tables after the extension has been
20274 : * loaded. For these tables, we dump out only the data- the structure is
20275 : * expected to be handled at CREATE EXTENSION time, including any indexes or
20276 : * foreign keys, which brings us to-
20277 : *
20278 : * 2. Record FK dependencies between configuration tables.
20279 : *
20280 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
20281 : * the data is loaded, we have to work out what the best order for reloading
20282 : * the data is, to avoid FK violations when the tables are restored. This is
20283 : * not perfect- we can't handle circular dependencies and if any exist they
20284 : * will cause an invalid dump to be produced (though at least all of the data
20285 : * is included for a user to manually restore). This is currently documented
20286 : * but perhaps we can provide a better solution in the future.
20287 : */
20288 : void
20289 259 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
20290 : int numExtensions)
20291 : {
20292 259 : DumpOptions *dopt = fout->dopt;
20293 : PQExpBuffer query;
20294 : PGresult *res;
20295 : int ntups,
20296 : i;
20297 : int i_conrelid,
20298 : i_confrelid;
20299 :
20300 : /* Nothing to do if no extensions */
20301 259 : if (numExtensions == 0)
20302 0 : return;
20303 :
20304 : /*
20305 : * Identify extension configuration tables and create TableDataInfo
20306 : * objects for them, ensuring their data will be dumped even though the
20307 : * tables themselves won't be.
20308 : *
20309 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
20310 : * user data in a configuration table is treated like schema data. This
20311 : * seems appropriate since system data in a config table would get
20312 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
20313 : * list of extensions to be included, none of its data is dumped.
20314 : */
20315 549 : for (i = 0; i < numExtensions; i++)
20316 : {
20317 290 : ExtensionInfo *curext = &(extinfo[i]);
20318 290 : char *extconfig = curext->extconfig;
20319 290 : char *extcondition = curext->extcondition;
20320 290 : char **extconfigarray = NULL;
20321 290 : char **extconditionarray = NULL;
20322 290 : int nconfigitems = 0;
20323 290 : int nconditionitems = 0;
20324 :
20325 : /*
20326 : * Check if this extension is listed as to include in the dump. If
20327 : * not, any table data associated with it is discarded.
20328 : */
20329 290 : if (extension_include_oids.head != NULL &&
20330 8 : !simple_oid_list_member(&extension_include_oids,
20331 : curext->dobj.catId.oid))
20332 6 : continue;
20333 :
20334 : /*
20335 : * Check if this extension is listed as to exclude in the dump. If
20336 : * yes, any table data associated with it is discarded.
20337 : */
20338 290 : if (extension_exclude_oids.head != NULL &&
20339 4 : simple_oid_list_member(&extension_exclude_oids,
20340 : curext->dobj.catId.oid))
20341 2 : continue;
20342 :
20343 284 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20344 : {
20345 : int j;
20346 :
20347 20 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20348 0 : pg_fatal("could not parse %s array", "extconfig");
20349 20 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20350 0 : pg_fatal("could not parse %s array", "extcondition");
20351 20 : if (nconfigitems != nconditionitems)
20352 0 : pg_fatal("mismatched number of configurations and conditions for extension");
20353 :
20354 60 : for (j = 0; j < nconfigitems; j++)
20355 : {
20356 : TableInfo *configtbl;
20357 40 : Oid configtbloid = atooid(extconfigarray[j]);
20358 40 : bool dumpobj =
20359 40 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20360 :
20361 40 : configtbl = findTableByOid(configtbloid);
20362 40 : if (configtbl == NULL)
20363 0 : continue;
20364 :
20365 : /*
20366 : * Tables of not-to-be-dumped extensions shouldn't be dumped
20367 : * unless the table or its schema is explicitly included
20368 : */
20369 40 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20370 : {
20371 : /* check table explicitly requested */
20372 2 : if (table_include_oids.head != NULL &&
20373 0 : simple_oid_list_member(&table_include_oids,
20374 : configtbloid))
20375 0 : dumpobj = true;
20376 :
20377 : /* check table's schema explicitly requested */
20378 2 : if (configtbl->dobj.namespace->dobj.dump &
20379 : DUMP_COMPONENT_DATA)
20380 2 : dumpobj = true;
20381 : }
20382 :
20383 : /* check table excluded by an exclusion switch */
20384 44 : if (table_exclude_oids.head != NULL &&
20385 4 : simple_oid_list_member(&table_exclude_oids,
20386 : configtbloid))
20387 1 : dumpobj = false;
20388 :
20389 : /* check schema excluded by an exclusion switch */
20390 40 : if (simple_oid_list_member(&schema_exclude_oids,
20391 40 : configtbl->dobj.namespace->dobj.catId.oid))
20392 0 : dumpobj = false;
20393 :
20394 40 : if (dumpobj)
20395 : {
20396 39 : makeTableDataInfo(dopt, configtbl);
20397 39 : if (configtbl->dataObj != NULL)
20398 : {
20399 39 : if (strlen(extconditionarray[j]) > 0)
20400 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20401 : }
20402 : }
20403 : }
20404 : }
20405 284 : if (extconfigarray)
20406 20 : free(extconfigarray);
20407 284 : if (extconditionarray)
20408 20 : free(extconditionarray);
20409 : }
20410 :
20411 : /*
20412 : * Now that all the TableDataInfo objects have been created for all the
20413 : * extensions, check their FK dependencies and register them to try and
20414 : * dump the data out in an order that they can be restored in.
20415 : *
20416 : * Note that this is not a problem for user tables as their FKs are
20417 : * recreated after the data has been loaded.
20418 : */
20419 :
20420 259 : query = createPQExpBuffer();
20421 :
20422 259 : printfPQExpBuffer(query,
20423 : "SELECT conrelid, confrelid "
20424 : "FROM pg_constraint "
20425 : "JOIN pg_depend ON (objid = confrelid) "
20426 : "WHERE contype = 'f' "
20427 : "AND refclassid = 'pg_extension'::regclass "
20428 : "AND classid = 'pg_class'::regclass;");
20429 :
20430 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20431 259 : ntups = PQntuples(res);
20432 :
20433 259 : i_conrelid = PQfnumber(res, "conrelid");
20434 259 : i_confrelid = PQfnumber(res, "confrelid");
20435 :
20436 : /* Now get the dependencies and register them */
20437 259 : for (i = 0; i < ntups; i++)
20438 : {
20439 : Oid conrelid,
20440 : confrelid;
20441 : TableInfo *reftable,
20442 : *contable;
20443 :
20444 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20445 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20446 0 : contable = findTableByOid(conrelid);
20447 0 : reftable = findTableByOid(confrelid);
20448 :
20449 0 : if (reftable == NULL ||
20450 0 : reftable->dataObj == NULL ||
20451 0 : contable == NULL ||
20452 0 : contable->dataObj == NULL)
20453 0 : continue;
20454 :
20455 : /*
20456 : * Make referencing TABLE_DATA object depend on the referenced table's
20457 : * TABLE_DATA object.
20458 : */
20459 0 : addObjectDependency(&contable->dataObj->dobj,
20460 0 : reftable->dataObj->dobj.dumpId);
20461 : }
20462 259 : PQclear(res);
20463 259 : destroyPQExpBuffer(query);
20464 : }
20465 :
20466 : /*
20467 : * getDependencies --- obtain available dependency data
20468 : */
20469 : static void
20470 259 : getDependencies(Archive *fout)
20471 : {
20472 : PQExpBuffer query;
20473 : PGresult *res;
20474 : int ntups,
20475 : i;
20476 : int i_classid,
20477 : i_objid,
20478 : i_refclassid,
20479 : i_refobjid,
20480 : i_deptype;
20481 : DumpableObject *dobj,
20482 : *refdobj;
20483 :
20484 259 : pg_log_info("reading dependency data");
20485 :
20486 259 : query = createPQExpBuffer();
20487 :
20488 : /*
20489 : * Messy query to collect the dependency data we need. Note that we
20490 : * ignore the sub-object column, so that dependencies of or on a column
20491 : * look the same as dependencies of or on a whole table.
20492 : *
20493 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
20494 : * already processed by getExtensionMembership.
20495 : */
20496 259 : appendPQExpBufferStr(query, "SELECT "
20497 : "classid, objid, refclassid, refobjid, deptype "
20498 : "FROM pg_depend "
20499 : "WHERE deptype != 'p' AND deptype != 'e'\n");
20500 :
20501 : /*
20502 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
20503 : * have to translate their dependencies into dependencies of their parent
20504 : * opfamily. Ignore internal dependencies though, as those will point to
20505 : * their parent opclass, which we needn't consider here (and if we did,
20506 : * it'd just result in circular dependencies). Also, "loose" opfamily
20507 : * entries will have dependencies on their parent opfamily, which we
20508 : * should drop since they'd likewise become useless self-dependencies.
20509 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20510 : */
20511 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20512 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20513 : "FROM pg_depend d, pg_amop o "
20514 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20515 : "classid = 'pg_amop'::regclass AND objid = o.oid "
20516 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20517 :
20518 : /* Likewise for pg_amproc entries */
20519 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20520 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20521 : "FROM pg_depend d, pg_amproc p "
20522 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20523 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20524 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20525 :
20526 : /*
20527 : * Translate dependencies of pg_propgraph_element entries into
20528 : * dependencies of their parent pg_class entry.
20529 : */
20530 259 : if (fout->remoteVersion >= 190000)
20531 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20532 : "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20533 : "FROM pg_depend d, pg_propgraph_element pge "
20534 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20535 : "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20536 :
20537 : /* Sort the output for efficiency below */
20538 259 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20539 :
20540 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20541 :
20542 259 : ntups = PQntuples(res);
20543 :
20544 259 : i_classid = PQfnumber(res, "classid");
20545 259 : i_objid = PQfnumber(res, "objid");
20546 259 : i_refclassid = PQfnumber(res, "refclassid");
20547 259 : i_refobjid = PQfnumber(res, "refobjid");
20548 259 : i_deptype = PQfnumber(res, "deptype");
20549 :
20550 : /*
20551 : * Since we ordered the SELECT by referencing ID, we can expect that
20552 : * multiple entries for the same object will appear together; this saves
20553 : * on searches.
20554 : */
20555 259 : dobj = NULL;
20556 :
20557 586768 : for (i = 0; i < ntups; i++)
20558 : {
20559 : CatalogId objId;
20560 : CatalogId refobjId;
20561 : char deptype;
20562 :
20563 586509 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20564 586509 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20565 586509 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20566 586509 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20567 586509 : deptype = *(PQgetvalue(res, i, i_deptype));
20568 :
20569 586509 : if (dobj == NULL ||
20570 551103 : dobj->catId.tableoid != objId.tableoid ||
20571 548526 : dobj->catId.oid != objId.oid)
20572 245931 : dobj = findObjectByCatalogId(objId);
20573 :
20574 : /*
20575 : * Failure to find objects mentioned in pg_depend is not unexpected,
20576 : * since for example we don't collect info about TOAST tables.
20577 : */
20578 586509 : if (dobj == NULL)
20579 : {
20580 : #ifdef NOT_USED
20581 : pg_log_warning("no referencing object %u %u",
20582 : objId.tableoid, objId.oid);
20583 : #endif
20584 36711 : continue;
20585 : }
20586 :
20587 551355 : refdobj = findObjectByCatalogId(refobjId);
20588 :
20589 551355 : if (refdobj == NULL)
20590 : {
20591 : #ifdef NOT_USED
20592 : pg_log_warning("no referenced object %u %u",
20593 : refobjId.tableoid, refobjId.oid);
20594 : #endif
20595 1557 : continue;
20596 : }
20597 :
20598 : /*
20599 : * For 'x' dependencies, mark the object for later; we still add the
20600 : * normal dependency, for possible ordering purposes. Currently
20601 : * pg_dump_sort.c knows to put extensions ahead of all object types
20602 : * that could possibly depend on them, but this is safer.
20603 : */
20604 549798 : if (deptype == 'x')
20605 44 : dobj->depends_on_ext = true;
20606 :
20607 : /*
20608 : * Ordinarily, table rowtypes have implicit dependencies on their
20609 : * tables. However, for a composite type the implicit dependency goes
20610 : * the other way in pg_depend; which is the right thing for DROP but
20611 : * it doesn't produce the dependency ordering we need. So in that one
20612 : * case, we reverse the direction of the dependency.
20613 : */
20614 549798 : if (deptype == 'i' &&
20615 150021 : dobj->objType == DO_TABLE &&
20616 1279 : refdobj->objType == DO_TYPE)
20617 182 : addObjectDependency(refdobj, dobj->dumpId);
20618 : else
20619 : /* normal case */
20620 549616 : addObjectDependency(dobj, refdobj->dumpId);
20621 : }
20622 :
20623 259 : PQclear(res);
20624 :
20625 259 : destroyPQExpBuffer(query);
20626 259 : }
20627 :
20628 :
20629 : /*
20630 : * createBoundaryObjects - create dummy DumpableObjects to represent
20631 : * dump section boundaries.
20632 : */
20633 : static DumpableObject *
20634 259 : createBoundaryObjects(void)
20635 : {
20636 : DumpableObject *dobjs;
20637 :
20638 259 : dobjs = pg_malloc_array(DumpableObject, 2);
20639 :
20640 259 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20641 259 : dobjs[0].catId = nilCatalogId;
20642 259 : AssignDumpId(dobjs + 0);
20643 259 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20644 :
20645 259 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20646 259 : dobjs[1].catId = nilCatalogId;
20647 259 : AssignDumpId(dobjs + 1);
20648 259 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20649 :
20650 259 : return dobjs;
20651 : }
20652 :
20653 : /*
20654 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20655 : * section boundaries.
20656 : */
20657 : static void
20658 259 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20659 : DumpableObject *boundaryObjs)
20660 : {
20661 259 : DumpableObject *preDataBound = boundaryObjs + 0;
20662 259 : DumpableObject *postDataBound = boundaryObjs + 1;
20663 : int i;
20664 :
20665 980461 : for (i = 0; i < numObjs; i++)
20666 : {
20667 980202 : DumpableObject *dobj = dobjs[i];
20668 :
20669 : /*
20670 : * The classification of object types here must match the SECTION_xxx
20671 : * values assigned during subsequent ArchiveEntry calls!
20672 : */
20673 980202 : switch (dobj->objType)
20674 : {
20675 919288 : case DO_NAMESPACE:
20676 : case DO_EXTENSION:
20677 : case DO_TYPE:
20678 : case DO_SHELL_TYPE:
20679 : case DO_FUNC:
20680 : case DO_AGG:
20681 : case DO_OPERATOR:
20682 : case DO_ACCESS_METHOD:
20683 : case DO_OPCLASS:
20684 : case DO_OPFAMILY:
20685 : case DO_COLLATION:
20686 : case DO_CONVERSION:
20687 : case DO_TABLE:
20688 : case DO_TABLE_ATTACH:
20689 : case DO_ATTRDEF:
20690 : case DO_PROCLANG:
20691 : case DO_CAST:
20692 : case DO_DUMMY_TYPE:
20693 : case DO_TSPARSER:
20694 : case DO_TSDICT:
20695 : case DO_TSTEMPLATE:
20696 : case DO_TSCONFIG:
20697 : case DO_FDW:
20698 : case DO_FOREIGN_SERVER:
20699 : case DO_TRANSFORM:
20700 : /* Pre-data objects: must come before the pre-data boundary */
20701 919288 : addObjectDependency(preDataBound, dobj->dumpId);
20702 919288 : break;
20703 5206 : case DO_TABLE_DATA:
20704 : case DO_SEQUENCE_SET:
20705 : case DO_LARGE_OBJECT:
20706 : case DO_LARGE_OBJECT_DATA:
20707 : /* Data objects: must come between the boundaries */
20708 5206 : addObjectDependency(dobj, preDataBound->dumpId);
20709 5206 : addObjectDependency(postDataBound, dobj->dumpId);
20710 5206 : break;
20711 6111 : case DO_INDEX:
20712 : case DO_INDEX_ATTACH:
20713 : case DO_STATSEXT:
20714 : case DO_REFRESH_MATVIEW:
20715 : case DO_TRIGGER:
20716 : case DO_EVENT_TRIGGER:
20717 : case DO_DEFAULT_ACL:
20718 : case DO_POLICY:
20719 : case DO_PUBLICATION:
20720 : case DO_PUBLICATION_REL:
20721 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20722 : case DO_SUBSCRIPTION:
20723 : case DO_SUBSCRIPTION_REL:
20724 : /* Post-data objects: must come after the post-data boundary */
20725 6111 : addObjectDependency(dobj, postDataBound->dumpId);
20726 6111 : break;
20727 42863 : case DO_RULE:
20728 : /* Rules are post-data, but only if dumped separately */
20729 42863 : if (((RuleInfo *) dobj)->separate)
20730 791 : addObjectDependency(dobj, postDataBound->dumpId);
20731 42863 : break;
20732 2748 : case DO_CONSTRAINT:
20733 : case DO_FK_CONSTRAINT:
20734 : /* Constraints are post-data, but only if dumped separately */
20735 2748 : if (((ConstraintInfo *) dobj)->separate)
20736 2040 : addObjectDependency(dobj, postDataBound->dumpId);
20737 2748 : break;
20738 259 : case DO_PRE_DATA_BOUNDARY:
20739 : /* nothing to do */
20740 259 : break;
20741 259 : case DO_POST_DATA_BOUNDARY:
20742 : /* must come after the pre-data boundary */
20743 259 : addObjectDependency(dobj, preDataBound->dumpId);
20744 259 : break;
20745 3468 : case DO_REL_STATS:
20746 : /* stats section varies by parent object type, DATA or POST */
20747 3468 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20748 : {
20749 2263 : addObjectDependency(dobj, preDataBound->dumpId);
20750 2263 : addObjectDependency(postDataBound, dobj->dumpId);
20751 : }
20752 : else
20753 1205 : addObjectDependency(dobj, postDataBound->dumpId);
20754 3468 : break;
20755 : }
20756 : }
20757 259 : }
20758 :
20759 :
20760 : /*
20761 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20762 : *
20763 : * The raw dependency data obtained by getDependencies() is not terribly
20764 : * useful in an archive dump, because in many cases there are dependency
20765 : * chains linking through objects that don't appear explicitly in the dump.
20766 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20767 : * will depend on other objects --- but the rule will not appear as a separate
20768 : * object in the dump. We need to adjust the view's dependencies to include
20769 : * whatever the rule depends on that is included in the dump.
20770 : *
20771 : * Just to make things more complicated, there are also "special" dependencies
20772 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20773 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20774 : * its table. In these cases we must leave the dependencies strictly as-is
20775 : * even if they refer to not-to-be-dumped objects.
20776 : *
20777 : * To handle this, the convention is that "special" dependencies are created
20778 : * during ArchiveEntry calls, and an archive TOC item that has any such
20779 : * entries will not be touched here. Otherwise, we recursively search the
20780 : * DumpableObject data structures to build the correct dependencies for each
20781 : * archive TOC item.
20782 : */
20783 : static void
20784 131 : BuildArchiveDependencies(Archive *fout)
20785 : {
20786 131 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20787 : TocEntry *te;
20788 :
20789 : /* Scan all TOC entries in the archive */
20790 7720 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20791 : {
20792 : DumpableObject *dobj;
20793 : DumpId *dependencies;
20794 : int nDeps;
20795 : int allocDeps;
20796 :
20797 : /* No need to process entries that will not be dumped */
20798 7589 : if (te->reqs == 0)
20799 3885 : continue;
20800 : /* Ignore entries that already have "special" dependencies */
20801 7582 : if (te->nDeps > 0)
20802 3116 : continue;
20803 : /* Otherwise, look up the item's original DumpableObject, if any */
20804 4466 : dobj = findObjectByDumpId(te->dumpId);
20805 4466 : if (dobj == NULL)
20806 657 : continue;
20807 : /* No work if it has no dependencies */
20808 3809 : if (dobj->nDeps <= 0)
20809 105 : continue;
20810 : /* Set up work array */
20811 3704 : allocDeps = 64;
20812 3704 : dependencies = pg_malloc_array(DumpId, allocDeps);
20813 3704 : nDeps = 0;
20814 : /* Recursively find all dumpable dependencies */
20815 3704 : findDumpableDependencies(AH, dobj,
20816 : &dependencies, &nDeps, &allocDeps);
20817 : /* And save 'em ... */
20818 3704 : if (nDeps > 0)
20819 : {
20820 2655 : dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20821 2655 : te->dependencies = dependencies;
20822 2655 : te->nDeps = nDeps;
20823 : }
20824 : else
20825 1049 : free(dependencies);
20826 : }
20827 131 : }
20828 :
20829 : /* Recursive search subroutine for BuildArchiveDependencies */
20830 : static void
20831 8937 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20832 : DumpId **dependencies, int *nDeps, int *allocDeps)
20833 : {
20834 : int i;
20835 :
20836 : /*
20837 : * Ignore section boundary objects: if we search through them, we'll
20838 : * report lots of bogus dependencies.
20839 : */
20840 8937 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20841 8920 : dobj->objType == DO_POST_DATA_BOUNDARY)
20842 1495 : return;
20843 :
20844 18383 : for (i = 0; i < dobj->nDeps; i++)
20845 : {
20846 10941 : DumpId depid = dobj->dependencies[i];
20847 :
20848 10941 : if (TocIDRequired(AH, depid) != 0)
20849 : {
20850 : /* Object will be dumped, so just reference it as a dependency */
20851 5708 : if (*nDeps >= *allocDeps)
20852 : {
20853 0 : *allocDeps *= 2;
20854 0 : *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20855 : }
20856 5708 : (*dependencies)[*nDeps] = depid;
20857 5708 : (*nDeps)++;
20858 : }
20859 : else
20860 : {
20861 : /*
20862 : * Object will not be dumped, so recursively consider its deps. We
20863 : * rely on the assumption that sortDumpableObjects already broke
20864 : * any dependency loops, else we might recurse infinitely.
20865 : */
20866 5233 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20867 :
20868 5233 : if (otherdobj)
20869 5233 : findDumpableDependencies(AH, otherdobj,
20870 : dependencies, nDeps, allocDeps);
20871 : }
20872 : }
20873 : }
20874 :
20875 :
20876 : /*
20877 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20878 : * given type OID.
20879 : *
20880 : * This does not guarantee to schema-qualify the output, so it should not
20881 : * be used to create the target object name for CREATE or ALTER commands.
20882 : *
20883 : * Note that the result is cached and must not be freed by the caller.
20884 : */
20885 : static const char *
20886 2335 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20887 : {
20888 : TypeInfo *typeInfo;
20889 : char *result;
20890 : PQExpBuffer query;
20891 : PGresult *res;
20892 :
20893 2335 : if (oid == 0)
20894 : {
20895 0 : if ((opts & zeroAsStar) != 0)
20896 0 : return "*";
20897 0 : else if ((opts & zeroAsNone) != 0)
20898 0 : return "NONE";
20899 : }
20900 :
20901 : /* see if we have the result cached in the type's TypeInfo record */
20902 2335 : typeInfo = findTypeByOid(oid);
20903 2335 : if (typeInfo && typeInfo->ftypname)
20904 1868 : return typeInfo->ftypname;
20905 :
20906 467 : query = createPQExpBuffer();
20907 467 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20908 : oid);
20909 :
20910 467 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20911 :
20912 : /* result of format_type is already quoted */
20913 467 : result = pg_strdup(PQgetvalue(res, 0, 0));
20914 :
20915 467 : PQclear(res);
20916 467 : destroyPQExpBuffer(query);
20917 :
20918 : /*
20919 : * Cache the result for re-use in later requests, if possible. If we
20920 : * don't have a TypeInfo for the type, the string will be leaked once the
20921 : * caller is done with it ... but that case really should not happen, so
20922 : * leaking if it does seems acceptable.
20923 : */
20924 467 : if (typeInfo)
20925 467 : typeInfo->ftypname = result;
20926 :
20927 467 : return result;
20928 : }
20929 :
20930 : /*
20931 : * Return a column list clause for the given relation.
20932 : *
20933 : * Special case: if there are no undropped columns in the relation, return
20934 : * "", not an invalid "()" column list.
20935 : */
20936 : static const char *
20937 9012 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20938 : {
20939 9012 : int numatts = ti->numatts;
20940 9012 : char **attnames = ti->attnames;
20941 9012 : bool *attisdropped = ti->attisdropped;
20942 9012 : char *attgenerated = ti->attgenerated;
20943 : bool needComma;
20944 : int i;
20945 :
20946 9012 : appendPQExpBufferChar(buffer, '(');
20947 9012 : needComma = false;
20948 42904 : for (i = 0; i < numatts; i++)
20949 : {
20950 33892 : if (attisdropped[i])
20951 606 : continue;
20952 33286 : if (attgenerated[i])
20953 1104 : continue;
20954 32182 : if (needComma)
20955 23394 : appendPQExpBufferStr(buffer, ", ");
20956 32182 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20957 32182 : needComma = true;
20958 : }
20959 :
20960 9012 : if (!needComma)
20961 224 : return ""; /* no undropped columns */
20962 :
20963 8788 : appendPQExpBufferChar(buffer, ')');
20964 8788 : return buffer->data;
20965 : }
20966 :
20967 : /*
20968 : * Check if a reloptions array is nonempty.
20969 : */
20970 : static bool
20971 14672 : nonemptyReloptions(const char *reloptions)
20972 : {
20973 : /* Don't want to print it if it's just "{}" */
20974 14672 : return (reloptions != NULL && strlen(reloptions) > 2);
20975 : }
20976 :
20977 : /*
20978 : * Format a reloptions array and append it to the given buffer.
20979 : *
20980 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20981 : */
20982 : static void
20983 219 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20984 : const char *prefix, Archive *fout)
20985 : {
20986 : bool res;
20987 :
20988 219 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20989 219 : fout->std_strings);
20990 219 : if (!res)
20991 0 : pg_log_warning("could not parse %s array", "reloptions");
20992 219 : }
20993 :
20994 : /*
20995 : * read_dump_filters - retrieve object identifier patterns from file
20996 : *
20997 : * Parse the specified filter file for include and exclude patterns, and add
20998 : * them to the relevant lists. If the filename is "-" then filters will be
20999 : * read from STDIN rather than a file.
21000 : */
21001 : static void
21002 26 : read_dump_filters(const char *filename, DumpOptions *dopt)
21003 : {
21004 : FilterStateData fstate;
21005 : char *objname;
21006 : FilterCommandType comtype;
21007 : FilterObjectType objtype;
21008 :
21009 26 : filter_init(&fstate, filename, exit_nicely);
21010 :
21011 84 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
21012 : {
21013 33 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
21014 : {
21015 17 : switch (objtype)
21016 : {
21017 0 : case FILTER_OBJECT_TYPE_NONE:
21018 0 : break;
21019 0 : case FILTER_OBJECT_TYPE_DATABASE:
21020 : case FILTER_OBJECT_TYPE_FUNCTION:
21021 : case FILTER_OBJECT_TYPE_INDEX:
21022 : case FILTER_OBJECT_TYPE_TABLE_DATA:
21023 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
21024 : case FILTER_OBJECT_TYPE_TRIGGER:
21025 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21026 : "include",
21027 : filter_object_type_name(objtype));
21028 0 : exit_nicely(1);
21029 : break; /* unreachable */
21030 :
21031 1 : case FILTER_OBJECT_TYPE_EXTENSION:
21032 1 : simple_string_list_append(&extension_include_patterns, objname);
21033 1 : break;
21034 1 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
21035 1 : simple_string_list_append(&foreign_servers_include_patterns, objname);
21036 1 : break;
21037 1 : case FILTER_OBJECT_TYPE_SCHEMA:
21038 1 : simple_string_list_append(&schema_include_patterns, objname);
21039 1 : dopt->include_everything = false;
21040 1 : break;
21041 13 : case FILTER_OBJECT_TYPE_TABLE:
21042 13 : simple_string_list_append(&table_include_patterns, objname);
21043 13 : dopt->include_everything = false;
21044 13 : break;
21045 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
21046 1 : simple_string_list_append(&table_include_patterns_and_children,
21047 : objname);
21048 1 : dopt->include_everything = false;
21049 1 : break;
21050 : }
21051 : }
21052 16 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
21053 : {
21054 9 : switch (objtype)
21055 : {
21056 0 : case FILTER_OBJECT_TYPE_NONE:
21057 0 : break;
21058 1 : case FILTER_OBJECT_TYPE_DATABASE:
21059 : case FILTER_OBJECT_TYPE_FUNCTION:
21060 : case FILTER_OBJECT_TYPE_INDEX:
21061 : case FILTER_OBJECT_TYPE_TRIGGER:
21062 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
21063 1 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21064 : "exclude",
21065 : filter_object_type_name(objtype));
21066 1 : exit_nicely(1);
21067 : break;
21068 :
21069 1 : case FILTER_OBJECT_TYPE_EXTENSION:
21070 1 : simple_string_list_append(&extension_exclude_patterns, objname);
21071 1 : break;
21072 1 : case FILTER_OBJECT_TYPE_TABLE_DATA:
21073 1 : simple_string_list_append(&tabledata_exclude_patterns,
21074 : objname);
21075 1 : break;
21076 1 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
21077 1 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
21078 : objname);
21079 1 : break;
21080 2 : case FILTER_OBJECT_TYPE_SCHEMA:
21081 2 : simple_string_list_append(&schema_exclude_patterns, objname);
21082 2 : break;
21083 2 : case FILTER_OBJECT_TYPE_TABLE:
21084 2 : simple_string_list_append(&table_exclude_patterns, objname);
21085 2 : break;
21086 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
21087 1 : simple_string_list_append(&table_exclude_patterns_and_children,
21088 : objname);
21089 1 : break;
21090 : }
21091 : }
21092 : else
21093 : {
21094 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
21095 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
21096 : }
21097 :
21098 32 : if (objname)
21099 25 : free(objname);
21100 : }
21101 :
21102 22 : filter_free(&fstate);
21103 22 : }
|