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 976294 : for (i = 0; i < numObjs; i++)
1214 976035 : 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 834297 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1933 : {
1934 834297 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1935 :
1936 834297 : if (ext == NULL)
1937 833412 : 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 1771 : 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 1771 : 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 1771 : if (table_include_oids.head != NULL)
1997 50 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1998 1721 : 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 1526 : else if (fout->remoteVersion >= 90600 &&
2004 1526 : 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 1288 : 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 848 : 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 2460 : 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 1771 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2061 1771 : }
2062 :
2063 : /*
2064 : * selectDumpableTable: policy-setting subroutine
2065 : * Mark a table as to be dumped or not
2066 : */
2067 : static void
2068 70838 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2069 : {
2070 70838 : 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 70613 : if (table_include_oids.head != NULL)
2078 5646 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2079 : tbinfo->dobj.catId.oid) ?
2080 2823 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2081 : else
2082 67790 : 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 115424 : if (tbinfo->dobj.dump &&
2088 44811 : 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 193219 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2108 : {
2109 : /* skip complex types, except for standalone composite types */
2110 193219 : if (OidIsValid(tyinfo->typrelid) &&
2111 70080 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2112 : {
2113 69898 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2114 :
2115 69898 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2116 69898 : if (tytable != NULL)
2117 69898 : tyinfo->dobj.dump = tytable->dobj.dump;
2118 : else
2119 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2120 69898 : return;
2121 : }
2122 :
2123 : /* skip auto-generated array and multirange types */
2124 123321 : if (tyinfo->isArray || tyinfo->isMultirange)
2125 : {
2126 94521 : 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 123321 : 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 123171 : 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 572421 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2343 : {
2344 572421 : 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 572220 : if (dobj->namespace)
2352 571356 : 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 4483 : dumpTableData_copy(Archive *fout, const void *dcontext)
2365 : {
2366 4483 : const TableDataInfo *tdinfo = dcontext;
2367 4483 : const TableInfo *tbinfo = tdinfo->tdtable;
2368 4483 : const char *classname = tbinfo->dobj.name;
2369 4483 : PQExpBuffer q = createPQExpBuffer();
2370 :
2371 : /*
2372 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2373 : * which uses it already.
2374 : */
2375 4483 : PQExpBuffer clistBuf = createPQExpBuffer();
2376 4483 : PGconn *conn = GetConnection(fout);
2377 : PGresult *res;
2378 : int ret;
2379 : char *copybuf;
2380 : const char *column_list;
2381 :
2382 4483 : 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 4483 : 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 4483 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2400 4442 : (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 4442 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2424 4442 : fmtQualifiedDumpable(tbinfo),
2425 : column_list);
2426 : }
2427 4483 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2428 4482 : PQclear(res);
2429 4482 : destroyPQExpBuffer(clistBuf);
2430 :
2431 : for (;;)
2432 : {
2433 1823941 : ret = PQgetCopyData(conn, ©buf, 0);
2434 :
2435 1823941 : if (ret < 0)
2436 4482 : break; /* done or error */
2437 :
2438 1819459 : if (copybuf)
2439 : {
2440 1819459 : WriteData(fout, copybuf, ret);
2441 1819459 : 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 4482 : archprintf(fout, "\\.\n\n\n");
2491 :
2492 4482 : 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 4482 : res = PQgetResult(conn);
2503 4482 : 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 4482 : PQclear(res);
2511 :
2512 : /* Do this to ensure we've pumped libpq back to idle state */
2513 4482 : if (PQgetResult(conn) != NULL)
2514 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2515 : classname);
2516 :
2517 4482 : destroyPQExpBuffer(q);
2518 :
2519 : /* Revert back the setting */
2520 4482 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2521 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2522 :
2523 4482 : 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 1084 : forcePartitionRootLoad(const TableInfo *tbinfo)
2830 : {
2831 : TableInfo *parentTbinfo;
2832 :
2833 : Assert(tbinfo->ispartition);
2834 : Assert(tbinfo->numParents == 1);
2835 :
2836 1084 : parentTbinfo = tbinfo->parents[0];
2837 1084 : if (parentTbinfo->unsafe_partitions)
2838 79 : return true;
2839 1225 : 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 1005 : 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 4656 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2858 : {
2859 4656 : DumpOptions *dopt = fout->dopt;
2860 4656 : const TableInfo *tbinfo = tdinfo->tdtable;
2861 4656 : PQExpBuffer copyBuf = createPQExpBuffer();
2862 4656 : PQExpBuffer clistBuf = createPQExpBuffer();
2863 : DataDumperPtr dumpFn;
2864 4656 : 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 4656 : if (tbinfo->ispartition &&
2878 2120 : (dopt->load_via_partition_root ||
2879 1060 : 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 4584 : copyFrom = fmtQualifiedDumpable(tbinfo);
2894 :
2895 4656 : if (dopt->dump_inserts == 0)
2896 : {
2897 : /* Dump/restore using COPY */
2898 4569 : dumpFn = dumpTableData_copy;
2899 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2900 4569 : printfPQExpBuffer(copyBuf, "COPY %s ",
2901 : copyFrom);
2902 4569 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2903 : fmtCopyColumnList(tbinfo, clistBuf));
2904 4569 : 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 4656 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2919 : {
2920 : TocEntry *te;
2921 :
2922 4656 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2923 4656 : 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 4656 : te->dataLength = (BlockNumber) tbinfo->relpages;
2947 4656 : 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 4656 : destroyPQExpBuffer(copyBuf);
2961 4656 : destroyPQExpBuffer(clistBuf);
2962 4656 : }
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 68904 : for (i = 0; i < numTables; i++)
3012 : {
3013 68653 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3014 1009 : (!relkind || tblinfo[i].relkind == relkind))
3015 6500 : 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 6579 : 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 6579 : if (tbinfo->dataObj != NULL)
3035 1 : return;
3036 :
3037 : /* Skip property graphs (no data to dump) */
3038 6578 : if (tbinfo->relkind == RELKIND_PROPGRAPH)
3039 86 : return;
3040 : /* Skip VIEWs (no data to dump) */
3041 6492 : if (tbinfo->relkind == RELKIND_VIEW)
3042 515 : return;
3043 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3044 5977 : 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 5940 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3051 511 : return;
3052 :
3053 : /* Don't dump data in unlogged tables, if so requested */
3054 5429 : 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 5411 : if (simple_oid_list_member(&tabledata_exclude_oids,
3060 : tbinfo->dobj.catId.oid))
3061 8 : return;
3062 :
3063 : /* OK, let's dump it */
3064 5403 : tdinfo = pg_malloc_object(TableDataInfo);
3065 :
3066 5403 : if (tbinfo->relkind == RELKIND_MATVIEW)
3067 345 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3068 5058 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3069 402 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3070 : else
3071 4656 : 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 5403 : tdinfo->dobj.catId.tableoid = 0;
3078 5403 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3079 5403 : AssignDumpId(&tdinfo->dobj);
3080 5403 : tdinfo->dobj.name = tbinfo->dobj.name;
3081 5403 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3082 5403 : tdinfo->tdtable = tbinfo;
3083 5403 : tdinfo->filtercond = NULL; /* might get set later */
3084 5403 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3085 :
3086 : /* A TableDataInfo contains data, of course */
3087 5403 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3088 :
3089 5403 : 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 5403 : 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 5403 : 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 26803 : for (i = 0; i < numObjs; i++)
3240 : {
3241 26796 : 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 70691 : for (i = 0; i < numTables; i++)
4262 : {
4263 70433 : TableInfo *tbinfo = &tblinfo[i];
4264 :
4265 : /* Ignore row security on tables not to be dumped */
4266 70433 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4267 62866 : continue;
4268 :
4269 : /* It can't have RLS or policies if it's not a table */
4270 7567 : if (tbinfo->relkind != RELKIND_RELATION &&
4271 2112 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4272 1496 : continue;
4273 :
4274 : /* Add it to the list of table OIDs to be probed below */
4275 6071 : if (tbloids->len > 1) /* do we have more than the '{'? */
4276 5896 : appendPQExpBufferChar(tbloids, ',');
4277 6071 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4278 :
4279 : /* Is RLS enabled? (That's separate from whether it has policies) */
4280 6071 : 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 table entries in EXCEPT list 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 list.
4632 : *
4633 : * Therefore, the approach is to dump the complete EXCEPT list in a
4634 : * single CREATE PUBLICATION statement. PublicationInfo is used to
4635 : * 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 (");
4718 : else
4719 62 : appendPQExpBufferStr(query, ", ");
4720 155 : appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
4721 : }
4722 156 : if (n_except > 0)
4723 93 : appendPQExpBufferChar(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 : appendPQExpBufferStr(query, " 0 AS submaxretention,\n");
5276 :
5277 255 : if (fout->remoteVersion >= 190000)
5278 255 : appendPQExpBufferStr(query,
5279 : " s.subwalrcvtimeout,\n");
5280 : else
5281 0 : appendPQExpBufferStr(query,
5282 : " '-1' AS subwalrcvtimeout,\n");
5283 :
5284 255 : if (fout->remoteVersion >= 190000)
5285 255 : appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5286 : else
5287 0 : appendPQExpBufferStr(query, " NULL AS subservername\n");
5288 :
5289 255 : appendPQExpBufferStr(query,
5290 : "FROM pg_subscription s\n");
5291 :
5292 255 : if (fout->remoteVersion >= 190000)
5293 255 : appendPQExpBufferStr(query,
5294 : "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5295 : " ON fs.oid = s.subserver \n");
5296 :
5297 255 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5298 40 : appendPQExpBufferStr(query,
5299 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5300 : " ON o.external_id = 'pg_' || s.oid::text \n");
5301 :
5302 255 : appendPQExpBufferStr(query,
5303 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5304 : " WHERE datname = current_database())");
5305 :
5306 255 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5307 :
5308 255 : ntups = PQntuples(res);
5309 :
5310 : /*
5311 : * Get subscription fields. We don't include subskiplsn in the dump as
5312 : * after restoring the dump this value may no longer be relevant.
5313 : */
5314 255 : i_tableoid = PQfnumber(res, "tableoid");
5315 255 : i_oid = PQfnumber(res, "oid");
5316 255 : i_subname = PQfnumber(res, "subname");
5317 255 : i_subowner = PQfnumber(res, "subowner");
5318 255 : i_subenabled = PQfnumber(res, "subenabled");
5319 255 : i_subbinary = PQfnumber(res, "subbinary");
5320 255 : i_substream = PQfnumber(res, "substream");
5321 255 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5322 255 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5323 255 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5324 255 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5325 255 : i_subfailover = PQfnumber(res, "subfailover");
5326 255 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5327 255 : i_submaxretention = PQfnumber(res, "submaxretention");
5328 255 : i_subservername = PQfnumber(res, "subservername");
5329 255 : i_subconninfo = PQfnumber(res, "subconninfo");
5330 255 : i_subslotname = PQfnumber(res, "subslotname");
5331 255 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5332 255 : i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5333 255 : i_subpublications = PQfnumber(res, "subpublications");
5334 255 : i_suborigin = PQfnumber(res, "suborigin");
5335 255 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5336 :
5337 255 : subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5338 :
5339 383 : for (i = 0; i < ntups; i++)
5340 : {
5341 128 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5342 128 : subinfo[i].dobj.catId.tableoid =
5343 128 : atooid(PQgetvalue(res, i, i_tableoid));
5344 128 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5345 128 : AssignDumpId(&subinfo[i].dobj);
5346 128 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5347 128 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5348 :
5349 128 : subinfo[i].subenabled =
5350 128 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5351 128 : if (PQgetisnull(res, i, i_subservername))
5352 128 : subinfo[i].subservername = NULL;
5353 : else
5354 0 : subinfo[i].subservername = pg_strdup(PQgetvalue(res, i, i_subservername));
5355 128 : subinfo[i].subbinary =
5356 128 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5357 128 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5358 128 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5359 128 : subinfo[i].subdisableonerr =
5360 128 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5361 128 : subinfo[i].subpasswordrequired =
5362 128 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5363 128 : subinfo[i].subrunasowner =
5364 128 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5365 128 : subinfo[i].subfailover =
5366 128 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5367 128 : subinfo[i].subretaindeadtuples =
5368 128 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5369 128 : subinfo[i].submaxretention =
5370 128 : atoi(PQgetvalue(res, i, i_submaxretention));
5371 128 : if (PQgetisnull(res, i, i_subconninfo))
5372 0 : subinfo[i].subconninfo = NULL;
5373 : else
5374 128 : subinfo[i].subconninfo =
5375 128 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5376 128 : if (PQgetisnull(res, i, i_subslotname))
5377 0 : subinfo[i].subslotname = NULL;
5378 : else
5379 128 : subinfo[i].subslotname =
5380 128 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5381 256 : subinfo[i].subsynccommit =
5382 128 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5383 256 : subinfo[i].subwalrcvtimeout =
5384 128 : pg_strdup(PQgetvalue(res, i, i_subwalrcvtimeout));
5385 256 : subinfo[i].subpublications =
5386 128 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5387 128 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5388 128 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5389 127 : subinfo[i].suboriginremotelsn = NULL;
5390 : else
5391 1 : subinfo[i].suboriginremotelsn =
5392 1 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5393 :
5394 : /* Decide whether we want to dump it */
5395 128 : selectDumpableObject(&(subinfo[i].dobj), fout);
5396 : }
5397 255 : PQclear(res);
5398 :
5399 255 : destroyPQExpBuffer(query);
5400 : }
5401 :
5402 : /*
5403 : * getSubscriptionRelations
5404 : * Get information about subscription membership for dumpable relations. This
5405 : * will be used only in binary-upgrade mode for PG17 or later versions.
5406 : */
5407 : void
5408 259 : getSubscriptionRelations(Archive *fout)
5409 : {
5410 259 : DumpOptions *dopt = fout->dopt;
5411 259 : SubscriptionInfo *subinfo = NULL;
5412 : SubRelInfo *subrinfo;
5413 : PGresult *res;
5414 : int i_srsubid;
5415 : int i_srrelid;
5416 : int i_srsubstate;
5417 : int i_srsublsn;
5418 : int ntups;
5419 259 : Oid last_srsubid = InvalidOid;
5420 :
5421 259 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5422 40 : fout->remoteVersion < 170000)
5423 219 : return;
5424 :
5425 40 : res = ExecuteSqlQuery(fout,
5426 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5427 : "FROM pg_catalog.pg_subscription_rel "
5428 : "ORDER BY srsubid",
5429 : PGRES_TUPLES_OK);
5430 40 : ntups = PQntuples(res);
5431 40 : if (ntups == 0)
5432 39 : goto cleanup;
5433 :
5434 : /* Get pg_subscription_rel attributes */
5435 1 : i_srsubid = PQfnumber(res, "srsubid");
5436 1 : i_srrelid = PQfnumber(res, "srrelid");
5437 1 : i_srsubstate = PQfnumber(res, "srsubstate");
5438 1 : i_srsublsn = PQfnumber(res, "srsublsn");
5439 :
5440 1 : subrinfo = pg_malloc_array(SubRelInfo, ntups);
5441 4 : for (int i = 0; i < ntups; i++)
5442 : {
5443 3 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5444 3 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5445 : TableInfo *tblinfo;
5446 :
5447 : /*
5448 : * If we switched to a new subscription, check if the subscription
5449 : * exists.
5450 : */
5451 3 : if (cur_srsubid != last_srsubid)
5452 : {
5453 2 : subinfo = findSubscriptionByOid(cur_srsubid);
5454 2 : if (subinfo == NULL)
5455 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5456 :
5457 2 : last_srsubid = cur_srsubid;
5458 : }
5459 :
5460 3 : tblinfo = findTableByOid(relid);
5461 3 : if (tblinfo == NULL)
5462 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5463 : relid);
5464 :
5465 : /* OK, make a DumpableObject for this relationship */
5466 3 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5467 3 : subrinfo[i].dobj.catId.tableoid = relid;
5468 3 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5469 3 : AssignDumpId(&subrinfo[i].dobj);
5470 3 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5471 3 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5472 3 : subrinfo[i].subinfo = subinfo;
5473 3 : subrinfo[i].tblinfo = tblinfo;
5474 3 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5475 3 : if (PQgetisnull(res, i, i_srsublsn))
5476 1 : subrinfo[i].srsublsn = NULL;
5477 : else
5478 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5479 :
5480 : /* Decide whether we want to dump it */
5481 3 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5482 : }
5483 :
5484 1 : cleanup:
5485 40 : PQclear(res);
5486 : }
5487 :
5488 : /*
5489 : * dumpSubscriptionTable
5490 : * Dump the definition of the given subscription table mapping. This will be
5491 : * used only in binary-upgrade mode for PG17 or later versions.
5492 : */
5493 : static void
5494 3 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5495 : {
5496 3 : DumpOptions *dopt = fout->dopt;
5497 3 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5498 : PQExpBuffer query;
5499 : char *tag;
5500 :
5501 : /* Do nothing if not dumping schema */
5502 3 : if (!dopt->dumpSchema)
5503 0 : return;
5504 :
5505 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5506 :
5507 3 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5508 :
5509 3 : query = createPQExpBuffer();
5510 :
5511 3 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5512 : {
5513 : /*
5514 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5515 : * to pg_subscription_rel table. This will be used only in
5516 : * binary-upgrade mode.
5517 : */
5518 3 : appendPQExpBufferStr(query,
5519 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5520 3 : appendPQExpBufferStr(query,
5521 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5522 3 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5523 3 : appendPQExpBuffer(query,
5524 : ", %u, '%c'",
5525 3 : subrinfo->tblinfo->dobj.catId.oid,
5526 3 : subrinfo->srsubstate);
5527 :
5528 3 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5529 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5530 : else
5531 1 : appendPQExpBufferStr(query, ", NULL");
5532 :
5533 3 : appendPQExpBufferStr(query, ");\n");
5534 : }
5535 :
5536 : /*
5537 : * There is no point in creating a drop query as the drop is done by table
5538 : * drop. (If you think to change this, see also _printTocEntry().)
5539 : * Although this object doesn't really have ownership as such, set the
5540 : * owner field anyway to ensure that the command is run by the correct
5541 : * role at restore time.
5542 : */
5543 3 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5544 3 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5545 3 : ARCHIVE_OPTS(.tag = tag,
5546 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5547 : .owner = subinfo->rolname,
5548 : .description = "SUBSCRIPTION TABLE",
5549 : .section = SECTION_POST_DATA,
5550 : .createStmt = query->data));
5551 :
5552 : /* These objects can't currently have comments or seclabels */
5553 :
5554 3 : free(tag);
5555 3 : destroyPQExpBuffer(query);
5556 : }
5557 :
5558 : /*
5559 : * dumpSubscription
5560 : * dump the definition of the given subscription
5561 : */
5562 : static void
5563 110 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5564 : {
5565 110 : DumpOptions *dopt = fout->dopt;
5566 : PQExpBuffer delq;
5567 : PQExpBuffer query;
5568 : PQExpBuffer publications;
5569 : char *qsubname;
5570 110 : char **pubnames = NULL;
5571 110 : int npubnames = 0;
5572 : int i;
5573 :
5574 : /* Do nothing if not dumping schema */
5575 110 : if (!dopt->dumpSchema)
5576 18 : return;
5577 :
5578 92 : delq = createPQExpBuffer();
5579 92 : query = createPQExpBuffer();
5580 :
5581 92 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5582 :
5583 92 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5584 : qsubname);
5585 :
5586 92 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5587 : qsubname);
5588 92 : if (subinfo->subservername)
5589 : {
5590 0 : appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5591 : }
5592 : else
5593 : {
5594 92 : appendPQExpBufferStr(query, "CONNECTION ");
5595 92 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5596 : }
5597 :
5598 : /* Build list of quoted publications and append them to query. */
5599 92 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5600 0 : pg_fatal("could not parse %s array", "subpublications");
5601 :
5602 92 : publications = createPQExpBuffer();
5603 184 : for (i = 0; i < npubnames; i++)
5604 : {
5605 92 : if (i > 0)
5606 0 : appendPQExpBufferStr(publications, ", ");
5607 :
5608 92 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5609 : }
5610 :
5611 92 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5612 92 : if (subinfo->subslotname)
5613 92 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5614 : else
5615 0 : appendPQExpBufferStr(query, "NONE");
5616 :
5617 92 : if (subinfo->subbinary)
5618 0 : appendPQExpBufferStr(query, ", binary = true");
5619 :
5620 92 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5621 30 : appendPQExpBufferStr(query, ", streaming = on");
5622 62 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5623 32 : appendPQExpBufferStr(query, ", streaming = parallel");
5624 : else
5625 30 : appendPQExpBufferStr(query, ", streaming = off");
5626 :
5627 92 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5628 0 : appendPQExpBufferStr(query, ", two_phase = on");
5629 :
5630 92 : if (subinfo->subdisableonerr)
5631 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5632 :
5633 92 : if (!subinfo->subpasswordrequired)
5634 0 : appendPQExpBufferStr(query, ", password_required = false");
5635 :
5636 92 : if (subinfo->subrunasowner)
5637 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5638 :
5639 92 : if (subinfo->subfailover)
5640 1 : appendPQExpBufferStr(query, ", failover = true");
5641 :
5642 92 : if (subinfo->subretaindeadtuples)
5643 1 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5644 :
5645 92 : if (subinfo->submaxretention)
5646 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5647 :
5648 92 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5649 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5650 :
5651 92 : if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5652 0 : appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5653 :
5654 92 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5655 30 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5656 :
5657 92 : appendPQExpBufferStr(query, ");\n");
5658 :
5659 : /*
5660 : * In binary-upgrade mode, we allow the replication to continue after the
5661 : * upgrade.
5662 : */
5663 92 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5664 : {
5665 5 : if (subinfo->suboriginremotelsn)
5666 : {
5667 : /*
5668 : * Preserve the remote_lsn for the subscriber's replication
5669 : * origin. This value is required to start the replication from
5670 : * the position before the upgrade. This value will be stale if
5671 : * the publisher gets upgraded before the subscriber node.
5672 : * However, this shouldn't be a problem as the upgrade of the
5673 : * publisher ensures that all the transactions were replicated
5674 : * before upgrading it.
5675 : */
5676 1 : appendPQExpBufferStr(query,
5677 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5678 1 : appendPQExpBufferStr(query,
5679 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5680 1 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5681 1 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5682 : }
5683 :
5684 5 : if (subinfo->subenabled)
5685 : {
5686 : /*
5687 : * Enable the subscription to allow the replication to continue
5688 : * after the upgrade.
5689 : */
5690 1 : appendPQExpBufferStr(query,
5691 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5692 1 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5693 : }
5694 : }
5695 :
5696 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5697 92 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5698 92 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5699 : .owner = subinfo->rolname,
5700 : .description = "SUBSCRIPTION",
5701 : .section = SECTION_POST_DATA,
5702 : .createStmt = query->data,
5703 : .dropStmt = delq->data));
5704 :
5705 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5706 30 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5707 30 : NULL, subinfo->rolname,
5708 30 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5709 :
5710 92 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5711 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5712 0 : NULL, subinfo->rolname,
5713 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5714 :
5715 92 : destroyPQExpBuffer(publications);
5716 92 : free(pubnames);
5717 :
5718 92 : destroyPQExpBuffer(delq);
5719 92 : destroyPQExpBuffer(query);
5720 92 : free(qsubname);
5721 : }
5722 :
5723 : /*
5724 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5725 : * the object needs.
5726 : */
5727 : static void
5728 5314 : append_depends_on_extension(Archive *fout,
5729 : PQExpBuffer create,
5730 : const DumpableObject *dobj,
5731 : const char *catalog,
5732 : const char *keyword,
5733 : const char *objname)
5734 : {
5735 5314 : if (dobj->depends_on_ext)
5736 : {
5737 : char *nm;
5738 : PGresult *res;
5739 : PQExpBuffer query;
5740 : int ntups;
5741 : int i_extname;
5742 : int i;
5743 :
5744 : /* dodge fmtId() non-reentrancy */
5745 42 : nm = pg_strdup(objname);
5746 :
5747 42 : query = createPQExpBuffer();
5748 42 : appendPQExpBuffer(query,
5749 : "SELECT e.extname "
5750 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5751 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5752 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5753 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5754 : catalog,
5755 42 : dobj->catId.oid);
5756 42 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5757 42 : ntups = PQntuples(res);
5758 42 : i_extname = PQfnumber(res, "extname");
5759 84 : for (i = 0; i < ntups; i++)
5760 : {
5761 42 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5762 : keyword, nm,
5763 42 : fmtId(PQgetvalue(res, i, i_extname)));
5764 : }
5765 :
5766 42 : PQclear(res);
5767 42 : destroyPQExpBuffer(query);
5768 42 : pg_free(nm);
5769 : }
5770 5314 : }
5771 :
5772 : static Oid
5773 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5774 : {
5775 : /*
5776 : * If the old version didn't assign an array type, but the new version
5777 : * does, we must select an unused type OID to assign. This currently only
5778 : * happens for domains, when upgrading pre-v11 to v11 and up.
5779 : *
5780 : * Note: local state here is kind of ugly, but we must have some, since we
5781 : * mustn't choose the same unused OID more than once.
5782 : */
5783 : static Oid next_possible_free_oid = FirstNormalObjectId;
5784 : PGresult *res;
5785 : bool is_dup;
5786 :
5787 : do
5788 : {
5789 0 : ++next_possible_free_oid;
5790 0 : printfPQExpBuffer(upgrade_query,
5791 : "SELECT EXISTS(SELECT 1 "
5792 : "FROM pg_catalog.pg_type "
5793 : "WHERE oid = '%u'::pg_catalog.oid);",
5794 : next_possible_free_oid);
5795 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5796 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5797 0 : PQclear(res);
5798 0 : } while (is_dup);
5799 :
5800 0 : return next_possible_free_oid;
5801 : }
5802 :
5803 : static void
5804 1028 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5805 : PQExpBuffer upgrade_buffer,
5806 : Oid pg_type_oid,
5807 : bool force_array_type,
5808 : bool include_multirange_type)
5809 : {
5810 1028 : PQExpBuffer upgrade_query = createPQExpBuffer();
5811 : PGresult *res;
5812 : Oid pg_type_array_oid;
5813 : Oid pg_type_multirange_oid;
5814 : Oid pg_type_multirange_array_oid;
5815 : TypeInfo *tinfo;
5816 :
5817 1028 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5818 1028 : appendPQExpBuffer(upgrade_buffer,
5819 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5820 : pg_type_oid);
5821 :
5822 1028 : tinfo = findTypeByOid(pg_type_oid);
5823 1028 : if (tinfo)
5824 1028 : pg_type_array_oid = tinfo->typarray;
5825 : else
5826 0 : pg_type_array_oid = InvalidOid;
5827 :
5828 1028 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5829 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5830 :
5831 1028 : if (OidIsValid(pg_type_array_oid))
5832 : {
5833 1026 : appendPQExpBufferStr(upgrade_buffer,
5834 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5835 1026 : appendPQExpBuffer(upgrade_buffer,
5836 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5837 : pg_type_array_oid);
5838 : }
5839 :
5840 : /*
5841 : * Pre-set the multirange type oid and its own array type oid.
5842 : */
5843 1028 : if (include_multirange_type)
5844 : {
5845 9 : if (fout->remoteVersion >= 140000)
5846 : {
5847 9 : printfPQExpBuffer(upgrade_query,
5848 : "SELECT t.oid, t.typarray "
5849 : "FROM pg_catalog.pg_type t "
5850 : "JOIN pg_catalog.pg_range r "
5851 : "ON t.oid = r.rngmultitypid "
5852 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5853 : pg_type_oid);
5854 :
5855 9 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5856 :
5857 9 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5858 9 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5859 :
5860 9 : PQclear(res);
5861 : }
5862 : else
5863 : {
5864 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5865 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5866 : }
5867 :
5868 9 : appendPQExpBufferStr(upgrade_buffer,
5869 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5870 9 : appendPQExpBuffer(upgrade_buffer,
5871 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5872 : pg_type_multirange_oid);
5873 9 : appendPQExpBufferStr(upgrade_buffer,
5874 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5875 9 : appendPQExpBuffer(upgrade_buffer,
5876 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5877 : pg_type_multirange_array_oid);
5878 : }
5879 :
5880 1028 : destroyPQExpBuffer(upgrade_query);
5881 1028 : }
5882 :
5883 : static void
5884 948 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5885 : PQExpBuffer upgrade_buffer,
5886 : const TableInfo *tbinfo)
5887 : {
5888 948 : Oid pg_type_oid = tbinfo->reltype;
5889 :
5890 948 : if (OidIsValid(pg_type_oid))
5891 948 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5892 : pg_type_oid, false, false);
5893 948 : }
5894 :
5895 : /*
5896 : * bsearch() comparator for BinaryUpgradeClassOidItem
5897 : */
5898 : static int
5899 13668 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5900 : {
5901 13668 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5902 13668 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5903 :
5904 13668 : return pg_cmp_u32(v1.oid, v2.oid);
5905 : }
5906 :
5907 : /*
5908 : * collectBinaryUpgradeClassOids
5909 : *
5910 : * Construct a table of pg_class information required for
5911 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5912 : * lookup.
5913 : */
5914 : static void
5915 40 : collectBinaryUpgradeClassOids(Archive *fout)
5916 : {
5917 : PGresult *res;
5918 : const char *query;
5919 :
5920 40 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5921 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5922 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5923 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5924 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5925 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5926 : "ORDER BY c.oid;";
5927 :
5928 40 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5929 :
5930 40 : nbinaryUpgradeClassOids = PQntuples(res);
5931 40 : binaryUpgradeClassOids =
5932 40 : pg_malloc_array(BinaryUpgradeClassOidItem, nbinaryUpgradeClassOids);
5933 :
5934 20077 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5935 : {
5936 20037 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5937 20037 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5938 20037 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5939 20037 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5940 20037 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5941 20037 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5942 20037 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5943 : }
5944 :
5945 40 : PQclear(res);
5946 40 : }
5947 :
5948 : static void
5949 1367 : binary_upgrade_set_pg_class_oids(Archive *fout,
5950 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5951 : {
5952 1367 : BinaryUpgradeClassOidItem key = {0};
5953 : BinaryUpgradeClassOidItem *entry;
5954 :
5955 : Assert(binaryUpgradeClassOids);
5956 :
5957 : /*
5958 : * Preserve the OID and relfilenumber of the table, table's index, table's
5959 : * toast table and toast table's index if any.
5960 : *
5961 : * One complexity is that the current table definition might not require
5962 : * the creation of a TOAST table, but the old database might have a TOAST
5963 : * table that was created earlier, before some wide columns were dropped.
5964 : * By setting the TOAST oid we force creation of the TOAST heap and index
5965 : * by the new backend, so we can copy the files during binary upgrade
5966 : * without worrying about this case.
5967 : */
5968 1367 : key.oid = pg_class_oid;
5969 1367 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5970 : sizeof(BinaryUpgradeClassOidItem),
5971 : BinaryUpgradeClassOidItemCmp);
5972 :
5973 1367 : appendPQExpBufferStr(upgrade_buffer,
5974 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5975 :
5976 1367 : if (entry->relkind != RELKIND_INDEX &&
5977 1062 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5978 : {
5979 1032 : appendPQExpBuffer(upgrade_buffer,
5980 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5981 : pg_class_oid);
5982 :
5983 : /*
5984 : * Not every relation has storage. Also, in a pre-v12 database,
5985 : * partitioned tables have a relfilenumber, which should not be
5986 : * preserved when upgrading.
5987 : */
5988 1032 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5989 846 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5990 846 : appendPQExpBuffer(upgrade_buffer,
5991 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5992 : entry->relfilenumber);
5993 :
5994 : /*
5995 : * In a pre-v12 database, partitioned tables might be marked as having
5996 : * toast tables, but we should ignore them if so.
5997 : */
5998 1032 : if (OidIsValid(entry->toast_oid) &&
5999 295 : entry->relkind != RELKIND_PARTITIONED_TABLE)
6000 : {
6001 295 : appendPQExpBuffer(upgrade_buffer,
6002 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
6003 : entry->toast_oid);
6004 295 : appendPQExpBuffer(upgrade_buffer,
6005 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
6006 : entry->toast_relfilenumber);
6007 :
6008 : /* every toast table has an index */
6009 295 : appendPQExpBuffer(upgrade_buffer,
6010 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6011 : entry->toast_index_oid);
6012 295 : appendPQExpBuffer(upgrade_buffer,
6013 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6014 : entry->toast_index_relfilenumber);
6015 : }
6016 : }
6017 : else
6018 : {
6019 : /* Preserve the OID and relfilenumber of the index */
6020 335 : appendPQExpBuffer(upgrade_buffer,
6021 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6022 : pg_class_oid);
6023 335 : appendPQExpBuffer(upgrade_buffer,
6024 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6025 : entry->relfilenumber);
6026 : }
6027 :
6028 1367 : appendPQExpBufferChar(upgrade_buffer, '\n');
6029 1367 : }
6030 :
6031 : /*
6032 : * If the DumpableObject is a member of an extension, add a suitable
6033 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
6034 : *
6035 : * For somewhat historical reasons, objname should already be quoted,
6036 : * but not objnamespace (if any).
6037 : */
6038 : static void
6039 1616 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
6040 : const DumpableObject *dobj,
6041 : const char *objtype,
6042 : const char *objname,
6043 : const char *objnamespace)
6044 : {
6045 1616 : DumpableObject *extobj = NULL;
6046 : int i;
6047 :
6048 1616 : if (!dobj->ext_member)
6049 1594 : return;
6050 :
6051 : /*
6052 : * Find the parent extension. We could avoid this search if we wanted to
6053 : * add a link field to DumpableObject, but the space costs of that would
6054 : * be considerable. We assume that member objects could only have a
6055 : * direct dependency on their own extension, not any others.
6056 : */
6057 22 : for (i = 0; i < dobj->nDeps; i++)
6058 : {
6059 22 : extobj = findObjectByDumpId(dobj->dependencies[i]);
6060 22 : if (extobj && extobj->objType == DO_EXTENSION)
6061 22 : break;
6062 0 : extobj = NULL;
6063 : }
6064 22 : if (extobj == NULL)
6065 0 : pg_fatal("could not find parent extension for %s %s",
6066 : objtype, objname);
6067 :
6068 22 : appendPQExpBufferStr(upgrade_buffer,
6069 : "\n-- For binary upgrade, handle extension membership the hard way\n");
6070 22 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6071 22 : fmtId(extobj->name),
6072 : objtype);
6073 22 : if (objnamespace && *objnamespace)
6074 19 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
6075 22 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6076 : }
6077 :
6078 : /*
6079 : * getNamespaces:
6080 : * get information about all namespaces in the system catalogs
6081 : */
6082 : void
6083 260 : getNamespaces(Archive *fout)
6084 : {
6085 : PGresult *res;
6086 : int ntups;
6087 : int i;
6088 : PQExpBuffer query;
6089 : NamespaceInfo *nsinfo;
6090 : int i_tableoid;
6091 : int i_oid;
6092 : int i_nspname;
6093 : int i_nspowner;
6094 : int i_nspacl;
6095 : int i_acldefault;
6096 :
6097 260 : query = createPQExpBuffer();
6098 :
6099 : /*
6100 : * we fetch all namespaces including system ones, so that every object we
6101 : * read in can be linked to a containing namespace.
6102 : */
6103 260 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6104 : "n.nspowner, "
6105 : "n.nspacl, "
6106 : "acldefault('n', n.nspowner) AS acldefault "
6107 : "FROM pg_namespace n");
6108 :
6109 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6110 :
6111 260 : ntups = PQntuples(res);
6112 :
6113 260 : nsinfo = pg_malloc_array(NamespaceInfo, ntups);
6114 :
6115 260 : i_tableoid = PQfnumber(res, "tableoid");
6116 260 : i_oid = PQfnumber(res, "oid");
6117 260 : i_nspname = PQfnumber(res, "nspname");
6118 260 : i_nspowner = PQfnumber(res, "nspowner");
6119 260 : i_nspacl = PQfnumber(res, "nspacl");
6120 260 : i_acldefault = PQfnumber(res, "acldefault");
6121 :
6122 2031 : for (i = 0; i < ntups; i++)
6123 : {
6124 : const char *nspowner;
6125 :
6126 1771 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6127 1771 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6128 1771 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6129 1771 : AssignDumpId(&nsinfo[i].dobj);
6130 1771 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6131 1771 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6132 1771 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6133 1771 : nsinfo[i].dacl.privtype = 0;
6134 1771 : nsinfo[i].dacl.initprivs = NULL;
6135 1771 : nspowner = PQgetvalue(res, i, i_nspowner);
6136 1771 : nsinfo[i].nspowner = atooid(nspowner);
6137 1771 : nsinfo[i].rolname = getRoleName(nspowner);
6138 :
6139 : /* Decide whether to dump this namespace */
6140 1771 : selectDumpableNamespace(&nsinfo[i], fout);
6141 :
6142 : /* Mark whether namespace has an ACL */
6143 1771 : if (!PQgetisnull(res, i, i_nspacl))
6144 879 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6145 :
6146 : /*
6147 : * We ignore any pg_init_privs.initprivs entry for the public schema
6148 : * and assume a predetermined default, for several reasons. First,
6149 : * dropping and recreating the schema removes its pg_init_privs entry,
6150 : * but an empty destination database starts with this ACL nonetheless.
6151 : * Second, we support dump/reload of public schema ownership changes.
6152 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6153 : * initprivs continues to reflect the initial owner. Hence,
6154 : * synthesize the value that nspacl will have after the restore's
6155 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6156 : * match the source's ACL, even if the latter was an initdb-default
6157 : * ACL, which changed in v15. An upgrade pulls in changes to most
6158 : * system object ACLs that the DBA had not customized. We've made the
6159 : * public schema depart from that, because changing its ACL so easily
6160 : * breaks applications.
6161 : */
6162 1771 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6163 : {
6164 256 : PQExpBuffer aclarray = createPQExpBuffer();
6165 256 : PQExpBuffer aclitem = createPQExpBuffer();
6166 :
6167 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6168 256 : appendPQExpBufferChar(aclarray, '{');
6169 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6170 256 : appendPQExpBufferStr(aclitem, "=UC/");
6171 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6172 256 : appendPGArray(aclarray, aclitem->data);
6173 256 : resetPQExpBuffer(aclitem);
6174 256 : appendPQExpBufferStr(aclitem, "=U/");
6175 256 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6176 256 : appendPGArray(aclarray, aclitem->data);
6177 256 : appendPQExpBufferChar(aclarray, '}');
6178 :
6179 256 : nsinfo[i].dacl.privtype = 'i';
6180 256 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6181 256 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6182 :
6183 256 : destroyPQExpBuffer(aclarray);
6184 256 : destroyPQExpBuffer(aclitem);
6185 : }
6186 : }
6187 :
6188 260 : PQclear(res);
6189 260 : destroyPQExpBuffer(query);
6190 260 : }
6191 :
6192 : /*
6193 : * findNamespace:
6194 : * given a namespace OID, look up the info read by getNamespaces
6195 : */
6196 : static NamespaceInfo *
6197 836092 : findNamespace(Oid nsoid)
6198 : {
6199 : NamespaceInfo *nsinfo;
6200 :
6201 836092 : nsinfo = findNamespaceByOid(nsoid);
6202 836092 : if (nsinfo == NULL)
6203 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6204 836092 : return nsinfo;
6205 : }
6206 :
6207 : /*
6208 : * getExtensions:
6209 : * read all extensions in the system catalogs and return them in the
6210 : * ExtensionInfo* structure
6211 : *
6212 : * numExtensions is set to the number of extensions read in
6213 : */
6214 : ExtensionInfo *
6215 260 : getExtensions(Archive *fout, int *numExtensions)
6216 : {
6217 260 : DumpOptions *dopt = fout->dopt;
6218 : PGresult *res;
6219 : int ntups;
6220 : int i;
6221 : PQExpBuffer query;
6222 260 : ExtensionInfo *extinfo = NULL;
6223 : int i_tableoid;
6224 : int i_oid;
6225 : int i_extname;
6226 : int i_nspname;
6227 : int i_extrelocatable;
6228 : int i_extversion;
6229 : int i_extconfig;
6230 : int i_extcondition;
6231 :
6232 260 : query = createPQExpBuffer();
6233 :
6234 260 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6235 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6236 : "FROM pg_extension x "
6237 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6238 :
6239 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6240 :
6241 260 : ntups = PQntuples(res);
6242 260 : if (ntups == 0)
6243 0 : goto cleanup;
6244 :
6245 260 : extinfo = pg_malloc_array(ExtensionInfo, ntups);
6246 :
6247 260 : i_tableoid = PQfnumber(res, "tableoid");
6248 260 : i_oid = PQfnumber(res, "oid");
6249 260 : i_extname = PQfnumber(res, "extname");
6250 260 : i_nspname = PQfnumber(res, "nspname");
6251 260 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6252 260 : i_extversion = PQfnumber(res, "extversion");
6253 260 : i_extconfig = PQfnumber(res, "extconfig");
6254 260 : i_extcondition = PQfnumber(res, "extcondition");
6255 :
6256 551 : for (i = 0; i < ntups; i++)
6257 : {
6258 291 : extinfo[i].dobj.objType = DO_EXTENSION;
6259 291 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6260 291 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6261 291 : AssignDumpId(&extinfo[i].dobj);
6262 291 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6263 291 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6264 291 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6265 291 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6266 291 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6267 291 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6268 :
6269 : /* Decide whether we want to dump it */
6270 291 : selectDumpableExtension(&(extinfo[i]), dopt);
6271 : }
6272 :
6273 260 : cleanup:
6274 260 : PQclear(res);
6275 260 : destroyPQExpBuffer(query);
6276 :
6277 260 : *numExtensions = ntups;
6278 :
6279 260 : return extinfo;
6280 : }
6281 :
6282 : /*
6283 : * getTypes:
6284 : * get information about all types in the system catalogs
6285 : *
6286 : * NB: this must run after getFuncs() because we assume we can do
6287 : * findFuncByOid().
6288 : */
6289 : void
6290 259 : getTypes(Archive *fout)
6291 : {
6292 : PGresult *res;
6293 : int ntups;
6294 : int i;
6295 259 : PQExpBuffer query = createPQExpBuffer();
6296 : TypeInfo *tyinfo;
6297 : ShellTypeInfo *stinfo;
6298 : int i_tableoid;
6299 : int i_oid;
6300 : int i_typname;
6301 : int i_typnamespace;
6302 : int i_typacl;
6303 : int i_acldefault;
6304 : int i_typowner;
6305 : int i_typelem;
6306 : int i_typrelid;
6307 : int i_typrelkind;
6308 : int i_typtype;
6309 : int i_typisdefined;
6310 : int i_isarray;
6311 : int i_typarray;
6312 :
6313 : /*
6314 : * we include even the built-in types because those may be used as array
6315 : * elements by user-defined types
6316 : *
6317 : * we filter out the built-in types when we dump out the types
6318 : *
6319 : * same approach for undefined (shell) types and array types
6320 : *
6321 : * Note: as of 8.3 we can reliably detect whether a type is an
6322 : * auto-generated array type by checking the element type's typarray.
6323 : * (Before that the test is capable of generating false positives.) We
6324 : * still check for name beginning with '_', though, so as to avoid the
6325 : * cost of the subselect probe for all standard types. This would have to
6326 : * be revisited if the backend ever allows renaming of array types.
6327 : */
6328 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6329 : "typnamespace, typacl, "
6330 : "acldefault('T', typowner) AS acldefault, "
6331 : "typowner, "
6332 : "typelem, typrelid, typarray, "
6333 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6334 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6335 : "typtype, typisdefined, "
6336 : "typname[0] = '_' AND typelem != 0 AND "
6337 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6338 : "FROM pg_type");
6339 :
6340 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6341 :
6342 259 : ntups = PQntuples(res);
6343 :
6344 259 : tyinfo = pg_malloc_array(TypeInfo, ntups);
6345 :
6346 259 : i_tableoid = PQfnumber(res, "tableoid");
6347 259 : i_oid = PQfnumber(res, "oid");
6348 259 : i_typname = PQfnumber(res, "typname");
6349 259 : i_typnamespace = PQfnumber(res, "typnamespace");
6350 259 : i_typacl = PQfnumber(res, "typacl");
6351 259 : i_acldefault = PQfnumber(res, "acldefault");
6352 259 : i_typowner = PQfnumber(res, "typowner");
6353 259 : i_typelem = PQfnumber(res, "typelem");
6354 259 : i_typrelid = PQfnumber(res, "typrelid");
6355 259 : i_typrelkind = PQfnumber(res, "typrelkind");
6356 259 : i_typtype = PQfnumber(res, "typtype");
6357 259 : i_typisdefined = PQfnumber(res, "typisdefined");
6358 259 : i_isarray = PQfnumber(res, "isarray");
6359 259 : i_typarray = PQfnumber(res, "typarray");
6360 :
6361 193478 : for (i = 0; i < ntups; i++)
6362 : {
6363 193219 : tyinfo[i].dobj.objType = DO_TYPE;
6364 193219 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6365 193219 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6366 193219 : AssignDumpId(&tyinfo[i].dobj);
6367 193219 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6368 386438 : tyinfo[i].dobj.namespace =
6369 193219 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6370 193219 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6371 193219 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6372 193219 : tyinfo[i].dacl.privtype = 0;
6373 193219 : tyinfo[i].dacl.initprivs = NULL;
6374 193219 : tyinfo[i].ftypname = NULL; /* may get filled later */
6375 193219 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6376 193219 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6377 193219 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6378 193219 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6379 193219 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6380 193219 : tyinfo[i].shellType = NULL;
6381 :
6382 193219 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6383 193167 : tyinfo[i].isDefined = true;
6384 : else
6385 52 : tyinfo[i].isDefined = false;
6386 :
6387 193219 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6388 92828 : tyinfo[i].isArray = true;
6389 : else
6390 100391 : tyinfo[i].isArray = false;
6391 :
6392 193219 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6393 :
6394 193219 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6395 1693 : tyinfo[i].isMultirange = true;
6396 : else
6397 191526 : tyinfo[i].isMultirange = false;
6398 :
6399 : /* Decide whether we want to dump it */
6400 193219 : selectDumpableType(&tyinfo[i], fout);
6401 :
6402 : /* Mark whether type has an ACL */
6403 193219 : if (!PQgetisnull(res, i, i_typacl))
6404 205 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6405 :
6406 : /*
6407 : * If it's a domain, fetch info about its constraints, if any
6408 : */
6409 193219 : tyinfo[i].nDomChecks = 0;
6410 193219 : tyinfo[i].domChecks = NULL;
6411 193219 : tyinfo[i].notnull = NULL;
6412 193219 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6413 16423 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6414 178 : getDomainConstraints(fout, &(tyinfo[i]));
6415 :
6416 : /*
6417 : * If it's a base type, make a DumpableObject representing a shell
6418 : * definition of the type. We will need to dump that ahead of the I/O
6419 : * functions for the type. Similarly, range types need a shell
6420 : * definition in case they have a canonicalize function.
6421 : *
6422 : * Note: the shell type doesn't have a catId. You might think it
6423 : * should copy the base type's catId, but then it might capture the
6424 : * pg_depend entries for the type, which we don't want.
6425 : */
6426 193219 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6427 16423 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6428 7995 : tyinfo[i].typtype == TYPTYPE_RANGE))
6429 : {
6430 8557 : stinfo = pg_malloc_object(ShellTypeInfo);
6431 8557 : stinfo->dobj.objType = DO_SHELL_TYPE;
6432 8557 : stinfo->dobj.catId = nilCatalogId;
6433 8557 : AssignDumpId(&stinfo->dobj);
6434 8557 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6435 8557 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6436 8557 : stinfo->baseType = &(tyinfo[i]);
6437 8557 : tyinfo[i].shellType = stinfo;
6438 :
6439 : /*
6440 : * Initially mark the shell type as not to be dumped. We'll only
6441 : * dump it if the I/O or canonicalize functions need to be dumped;
6442 : * this is taken care of while sorting dependencies.
6443 : */
6444 8557 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6445 : }
6446 : }
6447 :
6448 259 : PQclear(res);
6449 :
6450 259 : destroyPQExpBuffer(query);
6451 259 : }
6452 :
6453 : /*
6454 : * getOperators:
6455 : * get information about all operators in the system catalogs
6456 : */
6457 : void
6458 259 : getOperators(Archive *fout)
6459 : {
6460 : PGresult *res;
6461 : int ntups;
6462 : int i;
6463 259 : PQExpBuffer query = createPQExpBuffer();
6464 : OprInfo *oprinfo;
6465 : int i_tableoid;
6466 : int i_oid;
6467 : int i_oprname;
6468 : int i_oprnamespace;
6469 : int i_oprowner;
6470 : int i_oprkind;
6471 : int i_oprleft;
6472 : int i_oprright;
6473 : int i_oprcode;
6474 :
6475 : /*
6476 : * find all operators, including builtin operators; we filter out
6477 : * system-defined operators at dump-out time.
6478 : */
6479 :
6480 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6481 : "oprnamespace, "
6482 : "oprowner, "
6483 : "oprkind, "
6484 : "oprleft, "
6485 : "oprright, "
6486 : "oprcode::oid AS oprcode "
6487 : "FROM pg_operator");
6488 :
6489 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6490 :
6491 259 : ntups = PQntuples(res);
6492 :
6493 259 : oprinfo = pg_malloc_array(OprInfo, ntups);
6494 :
6495 259 : i_tableoid = PQfnumber(res, "tableoid");
6496 259 : i_oid = PQfnumber(res, "oid");
6497 259 : i_oprname = PQfnumber(res, "oprname");
6498 259 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6499 259 : i_oprowner = PQfnumber(res, "oprowner");
6500 259 : i_oprkind = PQfnumber(res, "oprkind");
6501 259 : i_oprleft = PQfnumber(res, "oprleft");
6502 259 : i_oprright = PQfnumber(res, "oprright");
6503 259 : i_oprcode = PQfnumber(res, "oprcode");
6504 :
6505 208896 : for (i = 0; i < ntups; i++)
6506 : {
6507 208637 : oprinfo[i].dobj.objType = DO_OPERATOR;
6508 208637 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6509 208637 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6510 208637 : AssignDumpId(&oprinfo[i].dobj);
6511 208637 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6512 417274 : oprinfo[i].dobj.namespace =
6513 208637 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6514 208637 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6515 208637 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6516 208637 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6517 208637 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6518 208637 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6519 :
6520 : /* Decide whether we want to dump it */
6521 208637 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6522 : }
6523 :
6524 259 : PQclear(res);
6525 :
6526 259 : destroyPQExpBuffer(query);
6527 259 : }
6528 :
6529 : /*
6530 : * getCollations:
6531 : * get information about all collations in the system catalogs
6532 : */
6533 : void
6534 259 : getCollations(Archive *fout)
6535 : {
6536 : PGresult *res;
6537 : int ntups;
6538 : int i;
6539 : PQExpBuffer query;
6540 : CollInfo *collinfo;
6541 : int i_tableoid;
6542 : int i_oid;
6543 : int i_collname;
6544 : int i_collnamespace;
6545 : int i_collowner;
6546 : int i_collencoding;
6547 :
6548 259 : query = createPQExpBuffer();
6549 :
6550 : /*
6551 : * find all collations, including builtin collations; we filter out
6552 : * system-defined collations at dump-out time.
6553 : */
6554 :
6555 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6556 : "collnamespace, "
6557 : "collowner, "
6558 : "collencoding "
6559 : "FROM pg_collation");
6560 :
6561 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6562 :
6563 259 : ntups = PQntuples(res);
6564 :
6565 259 : collinfo = pg_malloc_array(CollInfo, ntups);
6566 :
6567 259 : i_tableoid = PQfnumber(res, "tableoid");
6568 259 : i_oid = PQfnumber(res, "oid");
6569 259 : i_collname = PQfnumber(res, "collname");
6570 259 : i_collnamespace = PQfnumber(res, "collnamespace");
6571 259 : i_collowner = PQfnumber(res, "collowner");
6572 259 : i_collencoding = PQfnumber(res, "collencoding");
6573 :
6574 228290 : for (i = 0; i < ntups; i++)
6575 : {
6576 228031 : collinfo[i].dobj.objType = DO_COLLATION;
6577 228031 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6578 228031 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6579 228031 : AssignDumpId(&collinfo[i].dobj);
6580 228031 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6581 456062 : collinfo[i].dobj.namespace =
6582 228031 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6583 228031 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6584 228031 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6585 :
6586 : /* Decide whether we want to dump it */
6587 228031 : selectDumpableObject(&(collinfo[i].dobj), fout);
6588 : }
6589 :
6590 259 : PQclear(res);
6591 :
6592 259 : destroyPQExpBuffer(query);
6593 259 : }
6594 :
6595 : /*
6596 : * getConversions:
6597 : * get information about all conversions in the system catalogs
6598 : */
6599 : void
6600 259 : getConversions(Archive *fout)
6601 : {
6602 : PGresult *res;
6603 : int ntups;
6604 : int i;
6605 : PQExpBuffer query;
6606 : ConvInfo *convinfo;
6607 : int i_tableoid;
6608 : int i_oid;
6609 : int i_conname;
6610 : int i_connamespace;
6611 : int i_conowner;
6612 :
6613 259 : query = createPQExpBuffer();
6614 :
6615 : /*
6616 : * find all conversions, including builtin conversions; we filter out
6617 : * system-defined conversions at dump-out time.
6618 : */
6619 :
6620 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6621 : "connamespace, "
6622 : "conowner "
6623 : "FROM pg_conversion");
6624 :
6625 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6626 :
6627 259 : ntups = PQntuples(res);
6628 :
6629 259 : convinfo = pg_malloc_array(ConvInfo, ntups);
6630 :
6631 259 : i_tableoid = PQfnumber(res, "tableoid");
6632 259 : i_oid = PQfnumber(res, "oid");
6633 259 : i_conname = PQfnumber(res, "conname");
6634 259 : i_connamespace = PQfnumber(res, "connamespace");
6635 259 : i_conowner = PQfnumber(res, "conowner");
6636 :
6637 25686 : for (i = 0; i < ntups; i++)
6638 : {
6639 25427 : convinfo[i].dobj.objType = DO_CONVERSION;
6640 25427 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6641 25427 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6642 25427 : AssignDumpId(&convinfo[i].dobj);
6643 25427 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6644 50854 : convinfo[i].dobj.namespace =
6645 25427 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6646 25427 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6647 :
6648 : /* Decide whether we want to dump it */
6649 25427 : selectDumpableObject(&(convinfo[i].dobj), fout);
6650 : }
6651 :
6652 259 : PQclear(res);
6653 :
6654 259 : destroyPQExpBuffer(query);
6655 259 : }
6656 :
6657 : /*
6658 : * getAccessMethods:
6659 : * get information about all user-defined access methods
6660 : */
6661 : void
6662 259 : getAccessMethods(Archive *fout)
6663 : {
6664 : PGresult *res;
6665 : int ntups;
6666 : int i;
6667 : PQExpBuffer query;
6668 : AccessMethodInfo *aminfo;
6669 : int i_tableoid;
6670 : int i_oid;
6671 : int i_amname;
6672 : int i_amhandler;
6673 : int i_amtype;
6674 :
6675 259 : query = createPQExpBuffer();
6676 :
6677 : /*
6678 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6679 : * ACCESS METHOD, so earlier versions usually have only built-in access
6680 : * methods. v9.6 also changed the access method API, replacing dozens of
6681 : * pg_am columns with amhandler. Even if a user created an access method
6682 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6683 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6684 : * pg_am just to facilitate findAccessMethodByOid() providing the
6685 : * OID-to-name mapping.
6686 : */
6687 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6688 259 : if (fout->remoteVersion >= 90600)
6689 259 : appendPQExpBufferStr(query,
6690 : "amtype, "
6691 : "amhandler::pg_catalog.regproc AS amhandler ");
6692 : else
6693 0 : appendPQExpBufferStr(query,
6694 : "'i'::pg_catalog.\"char\" AS amtype, "
6695 : "'-'::pg_catalog.regproc AS amhandler ");
6696 259 : appendPQExpBufferStr(query, "FROM pg_am");
6697 :
6698 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6699 :
6700 259 : ntups = PQntuples(res);
6701 :
6702 259 : aminfo = pg_malloc_array(AccessMethodInfo, ntups);
6703 :
6704 259 : i_tableoid = PQfnumber(res, "tableoid");
6705 259 : i_oid = PQfnumber(res, "oid");
6706 259 : i_amname = PQfnumber(res, "amname");
6707 259 : i_amhandler = PQfnumber(res, "amhandler");
6708 259 : i_amtype = PQfnumber(res, "amtype");
6709 :
6710 2194 : for (i = 0; i < ntups; i++)
6711 : {
6712 1935 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6713 1935 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6714 1935 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6715 1935 : AssignDumpId(&aminfo[i].dobj);
6716 1935 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6717 1935 : aminfo[i].dobj.namespace = NULL;
6718 1935 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6719 1935 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6720 :
6721 : /* Decide whether we want to dump it */
6722 1935 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6723 : }
6724 :
6725 259 : PQclear(res);
6726 :
6727 259 : destroyPQExpBuffer(query);
6728 259 : }
6729 :
6730 :
6731 : /*
6732 : * getOpclasses:
6733 : * get information about all opclasses in the system catalogs
6734 : */
6735 : void
6736 259 : getOpclasses(Archive *fout)
6737 : {
6738 : PGresult *res;
6739 : int ntups;
6740 : int i;
6741 259 : PQExpBuffer query = createPQExpBuffer();
6742 : OpclassInfo *opcinfo;
6743 : int i_tableoid;
6744 : int i_oid;
6745 : int i_opcmethod;
6746 : int i_opcname;
6747 : int i_opcnamespace;
6748 : int i_opcowner;
6749 :
6750 : /*
6751 : * find all opclasses, including builtin opclasses; we filter out
6752 : * system-defined opclasses at dump-out time.
6753 : */
6754 :
6755 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6756 : "opcnamespace, "
6757 : "opcowner "
6758 : "FROM pg_opclass");
6759 :
6760 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6761 :
6762 259 : ntups = PQntuples(res);
6763 :
6764 259 : opcinfo = pg_malloc_array(OpclassInfo, ntups);
6765 :
6766 259 : i_tableoid = PQfnumber(res, "tableoid");
6767 259 : i_oid = PQfnumber(res, "oid");
6768 259 : i_opcmethod = PQfnumber(res, "opcmethod");
6769 259 : i_opcname = PQfnumber(res, "opcname");
6770 259 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6771 259 : i_opcowner = PQfnumber(res, "opcowner");
6772 :
6773 46776 : for (i = 0; i < ntups; i++)
6774 : {
6775 46517 : opcinfo[i].dobj.objType = DO_OPCLASS;
6776 46517 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6777 46517 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6778 46517 : AssignDumpId(&opcinfo[i].dobj);
6779 46517 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6780 93034 : opcinfo[i].dobj.namespace =
6781 46517 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6782 46517 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6783 46517 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6784 :
6785 : /* Decide whether we want to dump it */
6786 46517 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6787 : }
6788 :
6789 259 : PQclear(res);
6790 :
6791 259 : destroyPQExpBuffer(query);
6792 259 : }
6793 :
6794 : /*
6795 : * getOpfamilies:
6796 : * get information about all opfamilies in the system catalogs
6797 : */
6798 : void
6799 259 : getOpfamilies(Archive *fout)
6800 : {
6801 : PGresult *res;
6802 : int ntups;
6803 : int i;
6804 : PQExpBuffer query;
6805 : OpfamilyInfo *opfinfo;
6806 : int i_tableoid;
6807 : int i_oid;
6808 : int i_opfmethod;
6809 : int i_opfname;
6810 : int i_opfnamespace;
6811 : int i_opfowner;
6812 :
6813 259 : query = createPQExpBuffer();
6814 :
6815 : /*
6816 : * find all opfamilies, including builtin opfamilies; we filter out
6817 : * system-defined opfamilies at dump-out time.
6818 : */
6819 :
6820 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6821 : "opfnamespace, "
6822 : "opfowner "
6823 : "FROM pg_opfamily");
6824 :
6825 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6826 :
6827 259 : ntups = PQntuples(res);
6828 :
6829 259 : opfinfo = pg_malloc_array(OpfamilyInfo, ntups);
6830 :
6831 259 : i_tableoid = PQfnumber(res, "tableoid");
6832 259 : i_oid = PQfnumber(res, "oid");
6833 259 : i_opfname = PQfnumber(res, "opfname");
6834 259 : i_opfmethod = PQfnumber(res, "opfmethod");
6835 259 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6836 259 : i_opfowner = PQfnumber(res, "opfowner");
6837 :
6838 38730 : for (i = 0; i < ntups; i++)
6839 : {
6840 38471 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6841 38471 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6842 38471 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6843 38471 : AssignDumpId(&opfinfo[i].dobj);
6844 38471 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6845 76942 : opfinfo[i].dobj.namespace =
6846 38471 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6847 38471 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6848 38471 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6849 :
6850 : /* Decide whether we want to dump it */
6851 38471 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6852 : }
6853 :
6854 259 : PQclear(res);
6855 :
6856 259 : destroyPQExpBuffer(query);
6857 259 : }
6858 :
6859 : /*
6860 : * getAggregates:
6861 : * get information about all user-defined aggregates in the system catalogs
6862 : */
6863 : void
6864 259 : getAggregates(Archive *fout)
6865 : {
6866 259 : DumpOptions *dopt = fout->dopt;
6867 : PGresult *res;
6868 : int ntups;
6869 : int i;
6870 259 : PQExpBuffer query = createPQExpBuffer();
6871 : AggInfo *agginfo;
6872 : int i_tableoid;
6873 : int i_oid;
6874 : int i_aggname;
6875 : int i_aggnamespace;
6876 : int i_pronargs;
6877 : int i_proargtypes;
6878 : int i_proowner;
6879 : int i_aggacl;
6880 : int i_acldefault;
6881 :
6882 : /*
6883 : * Find all interesting aggregates. See comment in getFuncs() for the
6884 : * rationale behind the filtering logic.
6885 : */
6886 259 : if (fout->remoteVersion >= 90600)
6887 : {
6888 : const char *agg_check;
6889 :
6890 518 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6891 259 : : "p.proisagg");
6892 :
6893 259 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6894 : "p.proname AS aggname, "
6895 : "p.pronamespace AS aggnamespace, "
6896 : "p.pronargs, p.proargtypes, "
6897 : "p.proowner, "
6898 : "p.proacl AS aggacl, "
6899 : "acldefault('f', p.proowner) AS acldefault "
6900 : "FROM pg_proc p "
6901 : "LEFT JOIN pg_init_privs pip ON "
6902 : "(p.oid = pip.objoid "
6903 : "AND pip.classoid = 'pg_proc'::regclass "
6904 : "AND pip.objsubid = 0) "
6905 : "WHERE %s AND ("
6906 : "p.pronamespace != "
6907 : "(SELECT oid FROM pg_namespace "
6908 : "WHERE nspname = 'pg_catalog') OR "
6909 : "p.proacl IS DISTINCT FROM pip.initprivs",
6910 : agg_check);
6911 259 : if (dopt->binary_upgrade)
6912 40 : appendPQExpBufferStr(query,
6913 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6914 : "classid = 'pg_proc'::regclass AND "
6915 : "objid = p.oid AND "
6916 : "refclassid = 'pg_extension'::regclass AND "
6917 : "deptype = 'e')");
6918 259 : appendPQExpBufferChar(query, ')');
6919 : }
6920 : else
6921 : {
6922 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6923 : "pronamespace AS aggnamespace, "
6924 : "pronargs, proargtypes, "
6925 : "proowner, "
6926 : "proacl AS aggacl, "
6927 : "acldefault('f', proowner) AS acldefault "
6928 : "FROM pg_proc p "
6929 : "WHERE proisagg AND ("
6930 : "pronamespace != "
6931 : "(SELECT oid FROM pg_namespace "
6932 : "WHERE nspname = 'pg_catalog')");
6933 0 : if (dopt->binary_upgrade)
6934 0 : appendPQExpBufferStr(query,
6935 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6936 : "classid = 'pg_proc'::regclass AND "
6937 : "objid = p.oid AND "
6938 : "refclassid = 'pg_extension'::regclass AND "
6939 : "deptype = 'e')");
6940 0 : appendPQExpBufferChar(query, ')');
6941 : }
6942 :
6943 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6944 :
6945 259 : ntups = PQntuples(res);
6946 :
6947 259 : agginfo = pg_malloc_array(AggInfo, ntups);
6948 :
6949 259 : i_tableoid = PQfnumber(res, "tableoid");
6950 259 : i_oid = PQfnumber(res, "oid");
6951 259 : i_aggname = PQfnumber(res, "aggname");
6952 259 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6953 259 : i_pronargs = PQfnumber(res, "pronargs");
6954 259 : i_proargtypes = PQfnumber(res, "proargtypes");
6955 259 : i_proowner = PQfnumber(res, "proowner");
6956 259 : i_aggacl = PQfnumber(res, "aggacl");
6957 259 : i_acldefault = PQfnumber(res, "acldefault");
6958 :
6959 658 : for (i = 0; i < ntups; i++)
6960 : {
6961 399 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6962 399 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6963 399 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6964 399 : AssignDumpId(&agginfo[i].aggfn.dobj);
6965 399 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6966 798 : agginfo[i].aggfn.dobj.namespace =
6967 399 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6968 399 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6969 399 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6970 399 : agginfo[i].aggfn.dacl.privtype = 0;
6971 399 : agginfo[i].aggfn.dacl.initprivs = NULL;
6972 399 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6973 399 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6974 399 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6975 399 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6976 399 : if (agginfo[i].aggfn.nargs == 0)
6977 56 : agginfo[i].aggfn.argtypes = NULL;
6978 : else
6979 : {
6980 343 : agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6981 343 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6982 343 : agginfo[i].aggfn.argtypes,
6983 343 : agginfo[i].aggfn.nargs);
6984 : }
6985 399 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6986 :
6987 : /* Decide whether we want to dump it */
6988 399 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6989 :
6990 : /* Mark whether aggregate has an ACL */
6991 399 : if (!PQgetisnull(res, i, i_aggacl))
6992 25 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6993 : }
6994 :
6995 259 : PQclear(res);
6996 :
6997 259 : destroyPQExpBuffer(query);
6998 259 : }
6999 :
7000 : /*
7001 : * getFuncs:
7002 : * get information about all user-defined functions in the system catalogs
7003 : */
7004 : void
7005 259 : getFuncs(Archive *fout)
7006 : {
7007 259 : DumpOptions *dopt = fout->dopt;
7008 : PGresult *res;
7009 : int ntups;
7010 : int i;
7011 259 : PQExpBuffer query = createPQExpBuffer();
7012 : FuncInfo *finfo;
7013 : int i_tableoid;
7014 : int i_oid;
7015 : int i_proname;
7016 : int i_pronamespace;
7017 : int i_proowner;
7018 : int i_prolang;
7019 : int i_pronargs;
7020 : int i_proargtypes;
7021 : int i_prorettype;
7022 : int i_proacl;
7023 : int i_acldefault;
7024 :
7025 : /*
7026 : * Find all interesting functions. This is a bit complicated:
7027 : *
7028 : * 1. Always exclude aggregates; those are handled elsewhere.
7029 : *
7030 : * 2. Always exclude functions that are internally dependent on something
7031 : * else, since presumably those will be created as a result of creating
7032 : * the something else. This currently acts only to suppress constructor
7033 : * functions for range types. Note this is OK only because the
7034 : * constructors don't have any dependencies the range type doesn't have;
7035 : * otherwise we might not get creation ordering correct.
7036 : *
7037 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
7038 : * they're members of extensions and we are in binary-upgrade mode then
7039 : * include them, since we want to dump extension members individually in
7040 : * that mode. Also, if they are used by casts or transforms then we need
7041 : * to gather the information about them, though they won't be dumped if
7042 : * they are built-in. Also, in 9.6 and up, include functions in
7043 : * pg_catalog if they have an ACL different from what's shown in
7044 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
7045 : */
7046 259 : if (fout->remoteVersion >= 90600)
7047 : {
7048 : const char *not_agg_check;
7049 :
7050 518 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
7051 259 : : "NOT p.proisagg");
7052 :
7053 259 : appendPQExpBuffer(query,
7054 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
7055 : "p.pronargs, p.proargtypes, p.prorettype, "
7056 : "p.proacl, "
7057 : "acldefault('f', p.proowner) AS acldefault, "
7058 : "p.pronamespace, "
7059 : "p.proowner "
7060 : "FROM pg_proc p "
7061 : "LEFT JOIN pg_init_privs pip ON "
7062 : "(p.oid = pip.objoid "
7063 : "AND pip.classoid = 'pg_proc'::regclass "
7064 : "AND pip.objsubid = 0) "
7065 : "WHERE %s"
7066 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7067 : "WHERE classid = 'pg_proc'::regclass AND "
7068 : "objid = p.oid AND deptype = 'i')"
7069 : "\n AND ("
7070 : "\n pronamespace != "
7071 : "(SELECT oid FROM pg_namespace "
7072 : "WHERE nspname = 'pg_catalog')"
7073 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7074 : "\n WHERE pg_cast.oid > %u "
7075 : "\n AND p.oid = pg_cast.castfunc)"
7076 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7077 : "\n WHERE pg_transform.oid > %u AND "
7078 : "\n (p.oid = pg_transform.trffromsql"
7079 : "\n OR p.oid = pg_transform.trftosql))",
7080 : not_agg_check,
7081 : g_last_builtin_oid,
7082 : g_last_builtin_oid);
7083 259 : if (dopt->binary_upgrade)
7084 40 : appendPQExpBufferStr(query,
7085 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7086 : "classid = 'pg_proc'::regclass AND "
7087 : "objid = p.oid AND "
7088 : "refclassid = 'pg_extension'::regclass AND "
7089 : "deptype = 'e')");
7090 259 : appendPQExpBufferStr(query,
7091 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7092 259 : appendPQExpBufferChar(query, ')');
7093 : }
7094 : else
7095 : {
7096 0 : appendPQExpBuffer(query,
7097 : "SELECT tableoid, oid, proname, prolang, "
7098 : "pronargs, proargtypes, prorettype, proacl, "
7099 : "acldefault('f', proowner) AS acldefault, "
7100 : "pronamespace, "
7101 : "proowner "
7102 : "FROM pg_proc p "
7103 : "WHERE NOT proisagg"
7104 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7105 : "WHERE classid = 'pg_proc'::regclass AND "
7106 : "objid = p.oid AND deptype = 'i')"
7107 : "\n AND ("
7108 : "\n pronamespace != "
7109 : "(SELECT oid FROM pg_namespace "
7110 : "WHERE nspname = 'pg_catalog')"
7111 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7112 : "\n WHERE pg_cast.oid > '%u'::oid"
7113 : "\n AND p.oid = pg_cast.castfunc)",
7114 : g_last_builtin_oid);
7115 :
7116 0 : if (fout->remoteVersion >= 90500)
7117 0 : appendPQExpBuffer(query,
7118 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7119 : "\n WHERE pg_transform.oid > '%u'::oid"
7120 : "\n AND (p.oid = pg_transform.trffromsql"
7121 : "\n OR p.oid = pg_transform.trftosql))",
7122 : g_last_builtin_oid);
7123 :
7124 0 : if (dopt->binary_upgrade)
7125 0 : appendPQExpBufferStr(query,
7126 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7127 : "classid = 'pg_proc'::regclass AND "
7128 : "objid = p.oid AND "
7129 : "refclassid = 'pg_extension'::regclass AND "
7130 : "deptype = 'e')");
7131 0 : appendPQExpBufferChar(query, ')');
7132 : }
7133 :
7134 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7135 :
7136 259 : ntups = PQntuples(res);
7137 :
7138 259 : finfo = pg_malloc0_array(FuncInfo, ntups);
7139 :
7140 259 : i_tableoid = PQfnumber(res, "tableoid");
7141 259 : i_oid = PQfnumber(res, "oid");
7142 259 : i_proname = PQfnumber(res, "proname");
7143 259 : i_pronamespace = PQfnumber(res, "pronamespace");
7144 259 : i_proowner = PQfnumber(res, "proowner");
7145 259 : i_prolang = PQfnumber(res, "prolang");
7146 259 : i_pronargs = PQfnumber(res, "pronargs");
7147 259 : i_proargtypes = PQfnumber(res, "proargtypes");
7148 259 : i_prorettype = PQfnumber(res, "prorettype");
7149 259 : i_proacl = PQfnumber(res, "proacl");
7150 259 : i_acldefault = PQfnumber(res, "acldefault");
7151 :
7152 5930 : for (i = 0; i < ntups; i++)
7153 : {
7154 5671 : finfo[i].dobj.objType = DO_FUNC;
7155 5671 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7156 5671 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7157 5671 : AssignDumpId(&finfo[i].dobj);
7158 5671 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7159 11342 : finfo[i].dobj.namespace =
7160 5671 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7161 5671 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7162 5671 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7163 5671 : finfo[i].dacl.privtype = 0;
7164 5671 : finfo[i].dacl.initprivs = NULL;
7165 5671 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7166 5671 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7167 5671 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7168 5671 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7169 5671 : if (finfo[i].nargs == 0)
7170 1112 : finfo[i].argtypes = NULL;
7171 : else
7172 : {
7173 4559 : finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7174 4559 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7175 4559 : finfo[i].argtypes, finfo[i].nargs);
7176 : }
7177 5671 : finfo[i].postponed_def = false; /* might get set during sort */
7178 :
7179 : /* Decide whether we want to dump it */
7180 5671 : selectDumpableObject(&(finfo[i].dobj), fout);
7181 :
7182 : /* Mark whether function has an ACL */
7183 5671 : if (!PQgetisnull(res, i, i_proacl))
7184 149 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7185 : }
7186 :
7187 259 : PQclear(res);
7188 :
7189 259 : destroyPQExpBuffer(query);
7190 259 : }
7191 :
7192 : /*
7193 : * getRelationStatistics
7194 : * register the statistics object as a dependent of the relation.
7195 : *
7196 : * reltuples is passed as a string to avoid complexities in converting from/to
7197 : * floating point.
7198 : */
7199 : static RelStatsInfo *
7200 10457 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7201 : char *reltuples, int32 relallvisible,
7202 : int32 relallfrozen, char relkind,
7203 : char **indAttNames, int nindAttNames)
7204 : {
7205 10457 : if (!fout->dopt->dumpStatistics)
7206 6695 : return NULL;
7207 :
7208 3762 : if ((relkind == RELKIND_RELATION) ||
7209 1560 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7210 939 : (relkind == RELKIND_INDEX) ||
7211 621 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7212 318 : (relkind == RELKIND_MATVIEW ||
7213 : relkind == RELKIND_FOREIGN_TABLE))
7214 : {
7215 3476 : RelStatsInfo *info = pg_malloc0_object(RelStatsInfo);
7216 3476 : DumpableObject *dobj = &info->dobj;
7217 :
7218 3476 : dobj->objType = DO_REL_STATS;
7219 3476 : dobj->catId.tableoid = 0;
7220 3476 : dobj->catId.oid = 0;
7221 3476 : AssignDumpId(dobj);
7222 3476 : dobj->dependencies = pg_malloc_object(DumpId);
7223 3476 : dobj->dependencies[0] = rel->dumpId;
7224 3476 : dobj->nDeps = 1;
7225 3476 : dobj->allocDeps = 1;
7226 3476 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7227 3476 : dobj->name = pg_strdup(rel->name);
7228 3476 : dobj->namespace = rel->namespace;
7229 3476 : info->relid = rel->catId.oid;
7230 3476 : info->relpages = relpages;
7231 3476 : info->reltuples = pstrdup(reltuples);
7232 3476 : info->relallvisible = relallvisible;
7233 3476 : info->relallfrozen = relallfrozen;
7234 3476 : info->relkind = relkind;
7235 3476 : info->indAttNames = indAttNames;
7236 3476 : info->nindAttNames = nindAttNames;
7237 :
7238 : /*
7239 : * Ordinarily, stats go in SECTION_DATA for tables and
7240 : * SECTION_POST_DATA for indexes.
7241 : *
7242 : * However, the section may be updated later for materialized view
7243 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7244 : * the stats, so the stats must be restored after the data. Also, the
7245 : * materialized view definition may be postponed to SECTION_POST_DATA
7246 : * (see repairMatViewBoundaryMultiLoop()).
7247 : */
7248 3476 : switch (info->relkind)
7249 : {
7250 2537 : case RELKIND_RELATION:
7251 : case RELKIND_PARTITIONED_TABLE:
7252 : case RELKIND_MATVIEW:
7253 : case RELKIND_FOREIGN_TABLE:
7254 2537 : info->section = SECTION_DATA;
7255 2537 : break;
7256 939 : case RELKIND_INDEX:
7257 : case RELKIND_PARTITIONED_INDEX:
7258 939 : info->section = SECTION_POST_DATA;
7259 939 : break;
7260 0 : default:
7261 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7262 : info->relkind);
7263 : }
7264 :
7265 3476 : return info;
7266 : }
7267 286 : return NULL;
7268 : }
7269 :
7270 : /*
7271 : * getTables
7272 : * read all the tables (no indexes) in the system catalogs,
7273 : * and return them as an array of TableInfo structures
7274 : *
7275 : * *numTables is set to the number of tables read in
7276 : */
7277 : TableInfo *
7278 260 : getTables(Archive *fout, int *numTables)
7279 : {
7280 260 : DumpOptions *dopt = fout->dopt;
7281 : PGresult *res;
7282 : int ntups;
7283 : int i;
7284 260 : PQExpBuffer query = createPQExpBuffer();
7285 : TableInfo *tblinfo;
7286 : int i_reltableoid;
7287 : int i_reloid;
7288 : int i_relname;
7289 : int i_relnamespace;
7290 : int i_relkind;
7291 : int i_reltype;
7292 : int i_relowner;
7293 : int i_relchecks;
7294 : int i_relhasindex;
7295 : int i_relhasrules;
7296 : int i_relpages;
7297 : int i_reltuples;
7298 : int i_relallvisible;
7299 : int i_relallfrozen;
7300 : int i_toastpages;
7301 : int i_owning_tab;
7302 : int i_owning_col;
7303 : int i_reltablespace;
7304 : int i_relhasoids;
7305 : int i_relhastriggers;
7306 : int i_relpersistence;
7307 : int i_relispopulated;
7308 : int i_relreplident;
7309 : int i_relrowsec;
7310 : int i_relforcerowsec;
7311 : int i_relfrozenxid;
7312 : int i_toastfrozenxid;
7313 : int i_toastoid;
7314 : int i_relminmxid;
7315 : int i_toastminmxid;
7316 : int i_reloptions;
7317 : int i_checkoption;
7318 : int i_toastreloptions;
7319 : int i_reloftype;
7320 : int i_foreignserver;
7321 : int i_amname;
7322 : int i_is_identity_sequence;
7323 : int i_relacl;
7324 : int i_acldefault;
7325 : int i_ispartition;
7326 :
7327 : /*
7328 : * Find all the tables and table-like objects.
7329 : *
7330 : * We must fetch all tables in this phase because otherwise we cannot
7331 : * correctly identify inherited columns, owned sequences, etc.
7332 : *
7333 : * We include system catalogs, so that we can work if a user table is
7334 : * defined to inherit from a system catalog (pretty weird, but...)
7335 : *
7336 : * Note: in this phase we should collect only a minimal amount of
7337 : * information about each table, basically just enough to decide if it is
7338 : * interesting. In particular, since we do not yet have lock on any user
7339 : * table, we MUST NOT invoke any server-side data collection functions
7340 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7341 : * wrong answers if any concurrent DDL is happening.
7342 : */
7343 :
7344 260 : appendPQExpBufferStr(query,
7345 : "SELECT c.tableoid, c.oid, c.relname, "
7346 : "c.relnamespace, c.relkind, c.reltype, "
7347 : "c.relowner, "
7348 : "c.relchecks, "
7349 : "c.relhasindex, c.relhasrules, c.relpages, "
7350 : "c.reltuples, c.relallvisible, ");
7351 :
7352 260 : if (fout->remoteVersion >= 180000)
7353 260 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7354 : else
7355 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7356 :
7357 260 : appendPQExpBufferStr(query,
7358 : "c.relhastriggers, c.relpersistence, "
7359 : "c.reloftype, "
7360 : "c.relacl, "
7361 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7362 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7363 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7364 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7365 : "ELSE 0 END AS foreignserver, "
7366 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7367 : "tc.oid AS toid, "
7368 : "tc.relpages AS toastpages, "
7369 : "tc.reloptions AS toast_reloptions, "
7370 : "d.refobjid AS owning_tab, "
7371 : "d.refobjsubid AS owning_col, "
7372 : "tsp.spcname AS reltablespace, ");
7373 :
7374 260 : if (fout->remoteVersion >= 120000)
7375 260 : appendPQExpBufferStr(query,
7376 : "false AS relhasoids, ");
7377 : else
7378 0 : appendPQExpBufferStr(query,
7379 : "c.relhasoids, ");
7380 :
7381 260 : if (fout->remoteVersion >= 90300)
7382 260 : appendPQExpBufferStr(query,
7383 : "c.relispopulated, ");
7384 : else
7385 0 : appendPQExpBufferStr(query,
7386 : "'t' as relispopulated, ");
7387 :
7388 260 : if (fout->remoteVersion >= 90400)
7389 260 : appendPQExpBufferStr(query,
7390 : "c.relreplident, ");
7391 : else
7392 0 : appendPQExpBufferStr(query,
7393 : "'d' AS relreplident, ");
7394 :
7395 260 : if (fout->remoteVersion >= 90500)
7396 260 : appendPQExpBufferStr(query,
7397 : "c.relrowsecurity, c.relforcerowsecurity, ");
7398 : else
7399 0 : appendPQExpBufferStr(query,
7400 : "false AS relrowsecurity, "
7401 : "false AS relforcerowsecurity, ");
7402 :
7403 260 : if (fout->remoteVersion >= 90300)
7404 260 : appendPQExpBufferStr(query,
7405 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7406 : else
7407 0 : appendPQExpBufferStr(query,
7408 : "0 AS relminmxid, 0 AS tminmxid, ");
7409 :
7410 260 : if (fout->remoteVersion >= 90300)
7411 260 : appendPQExpBufferStr(query,
7412 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7413 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7414 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7415 : else
7416 0 : appendPQExpBufferStr(query,
7417 : "c.reloptions, NULL AS checkoption, ");
7418 :
7419 260 : if (fout->remoteVersion >= 90600)
7420 260 : appendPQExpBufferStr(query,
7421 : "am.amname, ");
7422 : else
7423 0 : appendPQExpBufferStr(query,
7424 : "NULL AS amname, ");
7425 :
7426 260 : if (fout->remoteVersion >= 90600)
7427 260 : appendPQExpBufferStr(query,
7428 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7429 : else
7430 0 : appendPQExpBufferStr(query,
7431 : "false AS is_identity_sequence, ");
7432 :
7433 260 : if (fout->remoteVersion >= 100000)
7434 260 : appendPQExpBufferStr(query,
7435 : "c.relispartition AS ispartition ");
7436 : else
7437 0 : appendPQExpBufferStr(query,
7438 : "false AS ispartition ");
7439 :
7440 : /*
7441 : * Left join to pg_depend to pick up dependency info linking sequences to
7442 : * their owning column, if any (note this dependency is AUTO except for
7443 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7444 : * collect the spcname.
7445 : */
7446 260 : appendPQExpBufferStr(query,
7447 : "\nFROM pg_class c\n"
7448 : "LEFT JOIN pg_depend d ON "
7449 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7450 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7451 : "d.objsubid = 0 AND "
7452 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7453 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7454 :
7455 : /*
7456 : * In 9.6 and up, left join to pg_am to pick up the amname.
7457 : */
7458 260 : if (fout->remoteVersion >= 90600)
7459 260 : appendPQExpBufferStr(query,
7460 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7461 :
7462 : /*
7463 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7464 : * that versions 10 and 11 have them, but later versions do not, so
7465 : * emitting them causes the upgrade to fail.
7466 : */
7467 260 : appendPQExpBufferStr(query,
7468 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7469 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7470 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7471 :
7472 : /*
7473 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7474 : * relkinds are possible in older servers, but it's not worth the trouble
7475 : * to emit a version-dependent list.
7476 : *
7477 : * Composite-type table entries won't be dumped as such, but we have to
7478 : * make a DumpableObject for them so that we can track dependencies of the
7479 : * composite type (pg_depend entries for columns of the composite type
7480 : * link to the pg_class entry not the pg_type entry).
7481 : */
7482 260 : appendPQExpBufferStr(query,
7483 : "WHERE c.relkind IN ("
7484 : CppAsString2(RELKIND_RELATION) ", "
7485 : CppAsString2(RELKIND_SEQUENCE) ", "
7486 : CppAsString2(RELKIND_VIEW) ", "
7487 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7488 : CppAsString2(RELKIND_MATVIEW) ", "
7489 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7490 : CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
7491 : CppAsString2(RELKIND_PROPGRAPH) ")\n"
7492 : "ORDER BY c.oid");
7493 :
7494 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7495 :
7496 260 : ntups = PQntuples(res);
7497 :
7498 260 : *numTables = ntups;
7499 :
7500 : /*
7501 : * Extract data from result and lock dumpable tables. We do the locking
7502 : * before anything else, to minimize the window wherein a table could
7503 : * disappear under us.
7504 : *
7505 : * Note that we have to save info about all tables here, even when dumping
7506 : * only one, because we don't yet know which tables might be inheritance
7507 : * ancestors of the target table.
7508 : */
7509 260 : tblinfo = pg_malloc0_array(TableInfo, ntups);
7510 :
7511 260 : i_reltableoid = PQfnumber(res, "tableoid");
7512 260 : i_reloid = PQfnumber(res, "oid");
7513 260 : i_relname = PQfnumber(res, "relname");
7514 260 : i_relnamespace = PQfnumber(res, "relnamespace");
7515 260 : i_relkind = PQfnumber(res, "relkind");
7516 260 : i_reltype = PQfnumber(res, "reltype");
7517 260 : i_relowner = PQfnumber(res, "relowner");
7518 260 : i_relchecks = PQfnumber(res, "relchecks");
7519 260 : i_relhasindex = PQfnumber(res, "relhasindex");
7520 260 : i_relhasrules = PQfnumber(res, "relhasrules");
7521 260 : i_relpages = PQfnumber(res, "relpages");
7522 260 : i_reltuples = PQfnumber(res, "reltuples");
7523 260 : i_relallvisible = PQfnumber(res, "relallvisible");
7524 260 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7525 260 : i_toastpages = PQfnumber(res, "toastpages");
7526 260 : i_owning_tab = PQfnumber(res, "owning_tab");
7527 260 : i_owning_col = PQfnumber(res, "owning_col");
7528 260 : i_reltablespace = PQfnumber(res, "reltablespace");
7529 260 : i_relhasoids = PQfnumber(res, "relhasoids");
7530 260 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7531 260 : i_relpersistence = PQfnumber(res, "relpersistence");
7532 260 : i_relispopulated = PQfnumber(res, "relispopulated");
7533 260 : i_relreplident = PQfnumber(res, "relreplident");
7534 260 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7535 260 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7536 260 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7537 260 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7538 260 : i_toastoid = PQfnumber(res, "toid");
7539 260 : i_relminmxid = PQfnumber(res, "relminmxid");
7540 260 : i_toastminmxid = PQfnumber(res, "tminmxid");
7541 260 : i_reloptions = PQfnumber(res, "reloptions");
7542 260 : i_checkoption = PQfnumber(res, "checkoption");
7543 260 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7544 260 : i_reloftype = PQfnumber(res, "reloftype");
7545 260 : i_foreignserver = PQfnumber(res, "foreignserver");
7546 260 : i_amname = PQfnumber(res, "amname");
7547 260 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7548 260 : i_relacl = PQfnumber(res, "relacl");
7549 260 : i_acldefault = PQfnumber(res, "acldefault");
7550 260 : i_ispartition = PQfnumber(res, "ispartition");
7551 :
7552 260 : if (dopt->lockWaitTimeout)
7553 : {
7554 : /*
7555 : * Arrange to fail instead of waiting forever for a table lock.
7556 : *
7557 : * NB: this coding assumes that the only queries issued within the
7558 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7559 : * applied to other things too.
7560 : */
7561 2 : resetPQExpBuffer(query);
7562 2 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7563 2 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7564 2 : ExecuteSqlStatement(fout, query->data);
7565 : }
7566 :
7567 260 : resetPQExpBuffer(query);
7568 :
7569 71281 : for (i = 0; i < ntups; i++)
7570 : {
7571 71021 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7572 71021 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7573 :
7574 71021 : tblinfo[i].dobj.objType = DO_TABLE;
7575 71021 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7576 71021 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7577 71021 : AssignDumpId(&tblinfo[i].dobj);
7578 71021 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7579 142042 : tblinfo[i].dobj.namespace =
7580 71021 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7581 71021 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7582 71021 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7583 71021 : tblinfo[i].dacl.privtype = 0;
7584 71021 : tblinfo[i].dacl.initprivs = NULL;
7585 71021 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7586 71021 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7587 71021 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7588 71021 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7589 71021 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7590 71021 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7591 71021 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7592 71021 : if (PQgetisnull(res, i, i_toastpages))
7593 57820 : tblinfo[i].toastpages = 0;
7594 : else
7595 13201 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7596 71021 : if (PQgetisnull(res, i, i_owning_tab))
7597 : {
7598 70606 : tblinfo[i].owning_tab = InvalidOid;
7599 70606 : tblinfo[i].owning_col = 0;
7600 : }
7601 : else
7602 : {
7603 415 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7604 415 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7605 : }
7606 71021 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7607 71021 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7608 71021 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7609 71021 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7610 71021 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7611 71021 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7612 71021 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7613 71021 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7614 71021 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7615 71021 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7616 71021 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7617 71021 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7618 71021 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7619 71021 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7620 71021 : if (PQgetisnull(res, i, i_checkoption))
7621 70975 : tblinfo[i].checkoption = NULL;
7622 : else
7623 46 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7624 71021 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7625 71021 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7626 71021 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7627 71021 : if (PQgetisnull(res, i, i_amname))
7628 44364 : tblinfo[i].amname = NULL;
7629 : else
7630 26657 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7631 71021 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7632 71021 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7633 :
7634 : /* other fields were zeroed above */
7635 :
7636 : /*
7637 : * Decide whether we want to dump this table.
7638 : */
7639 71021 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7640 183 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7641 : else
7642 70838 : selectDumpableTable(&tblinfo[i], fout);
7643 :
7644 : /*
7645 : * Now, consider the table "interesting" if we need to dump its
7646 : * definition, data or its statistics. Later on, we'll skip a lot of
7647 : * data collection for uninteresting tables.
7648 : *
7649 : * Note: the "interesting" flag will also be set by flagInhTables for
7650 : * parents of interesting tables, so that we collect necessary
7651 : * inheritance info even when the parents are not themselves being
7652 : * dumped. This is the main reason why we need an "interesting" flag
7653 : * that's separate from the components-to-dump bitmask.
7654 : */
7655 71021 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7656 : (DUMP_COMPONENT_DEFINITION |
7657 : DUMP_COMPONENT_DATA |
7658 71021 : DUMP_COMPONENT_STATISTICS)) != 0;
7659 :
7660 71021 : tblinfo[i].dummy_view = false; /* might get set during sort */
7661 71021 : tblinfo[i].postponed_def = false; /* might get set during sort */
7662 :
7663 : /* Tables have data */
7664 71021 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7665 :
7666 : /* Mark whether table has an ACL */
7667 71021 : if (!PQgetisnull(res, i, i_relacl))
7668 59954 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7669 71021 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7670 :
7671 : /* Add statistics */
7672 71021 : if (tblinfo[i].interesting)
7673 : {
7674 : RelStatsInfo *stats;
7675 :
7676 15360 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7677 7680 : tblinfo[i].relpages,
7678 : PQgetvalue(res, i, i_reltuples),
7679 : relallvisible, relallfrozen,
7680 7680 : tblinfo[i].relkind, NULL, 0);
7681 7680 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7682 398 : tblinfo[i].stats = stats;
7683 : }
7684 :
7685 : /*
7686 : * Read-lock target tables to make sure they aren't DROPPED or altered
7687 : * in schema before we get around to dumping them.
7688 : *
7689 : * Note that we don't explicitly lock parents of the target tables; we
7690 : * assume our lock on the child is enough to prevent schema
7691 : * alterations to parent tables.
7692 : *
7693 : * NOTE: it'd be kinda nice to lock other relations too, not only
7694 : * plain or partitioned tables, but the backend doesn't presently
7695 : * allow that.
7696 : *
7697 : * We only need to lock the table for certain components; see
7698 : * pg_dump.h
7699 : */
7700 71021 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7701 7680 : (tblinfo[i].relkind == RELKIND_RELATION ||
7702 2147 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7703 : {
7704 : /*
7705 : * Tables are locked in batches. When dumping from a remote
7706 : * server this can save a significant amount of time by reducing
7707 : * the number of round trips.
7708 : */
7709 6157 : if (query->len == 0)
7710 177 : appendPQExpBuffer(query, "LOCK TABLE %s",
7711 177 : fmtQualifiedDumpable(&tblinfo[i]));
7712 : else
7713 : {
7714 5980 : appendPQExpBuffer(query, ", %s",
7715 5980 : fmtQualifiedDumpable(&tblinfo[i]));
7716 :
7717 : /* Arbitrarily end a batch when query length reaches 100K. */
7718 5980 : if (query->len >= 100000)
7719 : {
7720 : /* Lock another batch of tables. */
7721 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7722 0 : ExecuteSqlStatement(fout, query->data);
7723 0 : resetPQExpBuffer(query);
7724 : }
7725 : }
7726 : }
7727 : }
7728 :
7729 260 : if (query->len != 0)
7730 : {
7731 : /* Lock the tables in the last batch. */
7732 177 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7733 177 : ExecuteSqlStatement(fout, query->data);
7734 : }
7735 :
7736 259 : if (dopt->lockWaitTimeout)
7737 : {
7738 2 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7739 : }
7740 :
7741 259 : PQclear(res);
7742 :
7743 259 : destroyPQExpBuffer(query);
7744 :
7745 259 : return tblinfo;
7746 : }
7747 :
7748 : /*
7749 : * getOwnedSeqs
7750 : * identify owned sequences and mark them as dumpable if owning table is
7751 : *
7752 : * We used to do this in getTables(), but it's better to do it after the
7753 : * index used by findTableByOid() has been set up.
7754 : */
7755 : void
7756 259 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7757 : {
7758 : int i;
7759 :
7760 : /*
7761 : * Force sequences that are "owned" by table columns to be dumped whenever
7762 : * their owning table is being dumped.
7763 : */
7764 70986 : for (i = 0; i < numTables; i++)
7765 : {
7766 70727 : TableInfo *seqinfo = &tblinfo[i];
7767 : TableInfo *owning_tab;
7768 :
7769 70727 : if (!OidIsValid(seqinfo->owning_tab))
7770 70315 : continue; /* not an owned sequence */
7771 :
7772 412 : owning_tab = findTableByOid(seqinfo->owning_tab);
7773 412 : if (owning_tab == NULL)
7774 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7775 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7776 :
7777 : /*
7778 : * For an identity sequence, dump exactly the same components for the
7779 : * sequence as for the owning table. This is important because we
7780 : * treat the identity sequence as an integral part of the table. For
7781 : * example, there is not any DDL command that allows creation of such
7782 : * a sequence independently of the table.
7783 : *
7784 : * For other owned sequences such as serial sequences, we need to dump
7785 : * the components that are being dumped for the table and any
7786 : * components that the sequence is explicitly marked with.
7787 : *
7788 : * We can't simply use the set of components which are being dumped
7789 : * for the table as the table might be in an extension (and only the
7790 : * non-extension components, eg: ACLs if changed, security labels, and
7791 : * policies, are being dumped) while the sequence is not (and
7792 : * therefore the definition and other components should also be
7793 : * dumped).
7794 : *
7795 : * If the sequence is part of the extension then it should be properly
7796 : * marked by checkExtensionMembership() and this will be a no-op as
7797 : * the table will be equivalently marked.
7798 : */
7799 412 : if (seqinfo->is_identity_sequence)
7800 199 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7801 : else
7802 213 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7803 :
7804 : /* Make sure that necessary data is available if we're dumping it */
7805 412 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7806 : {
7807 316 : seqinfo->interesting = true;
7808 316 : owning_tab->interesting = true;
7809 : }
7810 : }
7811 259 : }
7812 :
7813 : /*
7814 : * getInherits
7815 : * read all the inheritance information
7816 : * from the system catalogs return them in the InhInfo* structure
7817 : *
7818 : * numInherits is set to the number of pairs read in
7819 : */
7820 : InhInfo *
7821 259 : getInherits(Archive *fout, int *numInherits)
7822 : {
7823 : PGresult *res;
7824 : int ntups;
7825 : int i;
7826 259 : PQExpBuffer query = createPQExpBuffer();
7827 : InhInfo *inhinfo;
7828 :
7829 : int i_inhrelid;
7830 : int i_inhparent;
7831 :
7832 : /* find all the inheritance information */
7833 259 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7834 :
7835 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7836 :
7837 259 : ntups = PQntuples(res);
7838 :
7839 259 : *numInherits = ntups;
7840 :
7841 259 : inhinfo = pg_malloc_array(InhInfo, ntups);
7842 :
7843 259 : i_inhrelid = PQfnumber(res, "inhrelid");
7844 259 : i_inhparent = PQfnumber(res, "inhparent");
7845 :
7846 3812 : for (i = 0; i < ntups; i++)
7847 : {
7848 3553 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7849 3553 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7850 : }
7851 :
7852 259 : PQclear(res);
7853 :
7854 259 : destroyPQExpBuffer(query);
7855 :
7856 259 : return inhinfo;
7857 : }
7858 :
7859 : /*
7860 : * getPartitioningInfo
7861 : * get information about partitioning
7862 : *
7863 : * For the most part, we only collect partitioning info about tables we
7864 : * intend to dump. However, this function has to consider all partitioned
7865 : * tables in the database, because we need to know about parents of partitions
7866 : * we are going to dump even if the parents themselves won't be dumped.
7867 : *
7868 : * Specifically, what we need to know is whether each partitioned table
7869 : * has an "unsafe" partitioning scheme that requires us to force
7870 : * load-via-partition-root mode for its children. Currently the only case
7871 : * for which we force that is hash partitioning on enum columns, since the
7872 : * hash codes depend on enum value OIDs which won't be replicated across
7873 : * dump-and-reload. There are other cases in which load-via-partition-root
7874 : * might be necessary, but we expect users to cope with them.
7875 : */
7876 : void
7877 259 : getPartitioningInfo(Archive *fout)
7878 : {
7879 : PQExpBuffer query;
7880 : PGresult *res;
7881 : int ntups;
7882 :
7883 : /* hash partitioning didn't exist before v11 */
7884 259 : if (fout->remoteVersion < 110000)
7885 0 : return;
7886 : /* needn't bother if not dumping data */
7887 259 : if (!fout->dopt->dumpData)
7888 44 : return;
7889 :
7890 215 : query = createPQExpBuffer();
7891 :
7892 : /*
7893 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7894 : * appears among the partition opclasses. We needn't check partstrat.
7895 : *
7896 : * Note that this query may well retrieve info about tables we aren't
7897 : * going to dump and hence have no lock on. That's okay since we need not
7898 : * invoke any unsafe server-side functions.
7899 : */
7900 215 : appendPQExpBufferStr(query,
7901 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7902 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7903 : "ON c.opcmethod = a.oid\n"
7904 : "WHERE opcname = 'enum_ops' "
7905 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7906 : "AND amname = 'hash') = ANY(partclass)");
7907 :
7908 215 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7909 :
7910 215 : ntups = PQntuples(res);
7911 :
7912 258 : for (int i = 0; i < ntups; i++)
7913 : {
7914 43 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7915 : TableInfo *tbinfo;
7916 :
7917 43 : tbinfo = findTableByOid(tabrelid);
7918 43 : if (tbinfo == NULL)
7919 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7920 : tabrelid);
7921 43 : tbinfo->unsafe_partitions = true;
7922 : }
7923 :
7924 215 : PQclear(res);
7925 :
7926 215 : destroyPQExpBuffer(query);
7927 : }
7928 :
7929 : /*
7930 : * getIndexes
7931 : * get information about every index on a dumpable table
7932 : *
7933 : * Note: index data is not returned directly to the caller, but it
7934 : * does get entered into the DumpableObject tables.
7935 : */
7936 : void
7937 259 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7938 : {
7939 259 : PQExpBuffer query = createPQExpBuffer();
7940 259 : PQExpBuffer tbloids = createPQExpBuffer();
7941 : PGresult *res;
7942 : int ntups;
7943 : int curtblindx;
7944 : IndxInfo *indxinfo;
7945 : int i_tableoid,
7946 : i_oid,
7947 : i_indrelid,
7948 : i_indexname,
7949 : i_relpages,
7950 : i_reltuples,
7951 : i_relallvisible,
7952 : i_relallfrozen,
7953 : i_parentidx,
7954 : i_indexdef,
7955 : i_indnkeyatts,
7956 : i_indnatts,
7957 : i_indkey,
7958 : i_indisclustered,
7959 : i_indisreplident,
7960 : i_indnullsnotdistinct,
7961 : i_contype,
7962 : i_conname,
7963 : i_condeferrable,
7964 : i_condeferred,
7965 : i_conperiod,
7966 : i_contableoid,
7967 : i_conoid,
7968 : i_condef,
7969 : i_indattnames,
7970 : i_tablespace,
7971 : i_indreloptions,
7972 : i_indstatcols,
7973 : i_indstatvals;
7974 :
7975 : /*
7976 : * We want to perform just one query against pg_index. However, we
7977 : * mustn't try to select every row of the catalog and then sort it out on
7978 : * the client side, because some of the server-side functions we need
7979 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7980 : * build an array of the OIDs of tables we care about (and now have lock
7981 : * on!), and use a WHERE clause to constrain which rows are selected.
7982 : */
7983 259 : appendPQExpBufferChar(tbloids, '{');
7984 70986 : for (int i = 0; i < numTables; i++)
7985 : {
7986 70727 : TableInfo *tbinfo = &tblinfo[i];
7987 :
7988 70727 : if (!tbinfo->hasindex)
7989 50271 : continue;
7990 :
7991 : /*
7992 : * We can ignore indexes of uninteresting tables.
7993 : */
7994 20456 : if (!tbinfo->interesting)
7995 18299 : continue;
7996 :
7997 : /* OK, we need info for this table */
7998 2157 : if (tbloids->len > 1) /* do we have more than the '{'? */
7999 2078 : appendPQExpBufferChar(tbloids, ',');
8000 2157 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8001 : }
8002 259 : appendPQExpBufferChar(tbloids, '}');
8003 :
8004 259 : appendPQExpBufferStr(query,
8005 : "SELECT t.tableoid, t.oid, i.indrelid, "
8006 : "t.relname AS indexname, "
8007 : "t.relpages, t.reltuples, t.relallvisible, ");
8008 :
8009 259 : if (fout->remoteVersion >= 180000)
8010 259 : appendPQExpBufferStr(query, "t.relallfrozen, ");
8011 : else
8012 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
8013 :
8014 259 : appendPQExpBufferStr(query,
8015 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
8016 : "i.indkey, i.indisclustered, "
8017 : "c.contype, c.conname, "
8018 : "c.condeferrable, c.condeferred, "
8019 : "c.tableoid AS contableoid, "
8020 : "c.oid AS conoid, "
8021 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
8022 : "CASE WHEN i.indexprs IS NOT NULL THEN "
8023 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
8024 : " FROM pg_catalog.pg_attribute "
8025 : " WHERE attrelid = i.indexrelid) "
8026 : "ELSE NULL END AS indattnames, "
8027 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
8028 : "t.reloptions AS indreloptions, ");
8029 :
8030 :
8031 259 : if (fout->remoteVersion >= 90400)
8032 259 : appendPQExpBufferStr(query,
8033 : "i.indisreplident, ");
8034 : else
8035 0 : appendPQExpBufferStr(query,
8036 : "false AS indisreplident, ");
8037 :
8038 259 : if (fout->remoteVersion >= 110000)
8039 259 : appendPQExpBufferStr(query,
8040 : "inh.inhparent AS parentidx, "
8041 : "i.indnkeyatts AS indnkeyatts, "
8042 : "i.indnatts AS indnatts, "
8043 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
8044 : " FROM pg_catalog.pg_attribute "
8045 : " WHERE attrelid = i.indexrelid AND "
8046 : " attstattarget >= 0) AS indstatcols, "
8047 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
8048 : " FROM pg_catalog.pg_attribute "
8049 : " WHERE attrelid = i.indexrelid AND "
8050 : " attstattarget >= 0) AS indstatvals, ");
8051 : else
8052 0 : appendPQExpBufferStr(query,
8053 : "0 AS parentidx, "
8054 : "i.indnatts AS indnkeyatts, "
8055 : "i.indnatts AS indnatts, "
8056 : "'' AS indstatcols, "
8057 : "'' AS indstatvals, ");
8058 :
8059 259 : if (fout->remoteVersion >= 150000)
8060 259 : appendPQExpBufferStr(query,
8061 : "i.indnullsnotdistinct, ");
8062 : else
8063 0 : appendPQExpBufferStr(query,
8064 : "false AS indnullsnotdistinct, ");
8065 :
8066 259 : if (fout->remoteVersion >= 180000)
8067 259 : appendPQExpBufferStr(query,
8068 : "c.conperiod ");
8069 : else
8070 0 : appendPQExpBufferStr(query,
8071 : "NULL AS conperiod ");
8072 :
8073 : /*
8074 : * The point of the messy-looking outer join is to find a constraint that
8075 : * is related by an internal dependency link to the index. If we find one,
8076 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
8077 : * index won't have more than one internal dependency.
8078 : *
8079 : * Note: the check on conrelid is redundant, but useful because that
8080 : * column is indexed while conindid is not.
8081 : */
8082 259 : if (fout->remoteVersion >= 110000)
8083 : {
8084 259 : appendPQExpBuffer(query,
8085 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8086 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8087 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8088 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
8089 : "LEFT JOIN pg_catalog.pg_constraint c "
8090 : "ON (i.indrelid = c.conrelid AND "
8091 : "i.indexrelid = c.conindid AND "
8092 : "c.contype IN ('p','u','x')) "
8093 : "LEFT JOIN pg_catalog.pg_inherits inh "
8094 : "ON (inh.inhrelid = indexrelid) "
8095 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
8096 : "AND i.indisready "
8097 : "ORDER BY i.indrelid, indexname",
8098 : tbloids->data);
8099 : }
8100 : else
8101 : {
8102 : /*
8103 : * the test on indisready is necessary in 9.2, and harmless in
8104 : * earlier/later versions
8105 : */
8106 0 : appendPQExpBuffer(query,
8107 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8108 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8109 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8110 : "LEFT JOIN pg_catalog.pg_constraint c "
8111 : "ON (i.indrelid = c.conrelid AND "
8112 : "i.indexrelid = c.conindid AND "
8113 : "c.contype IN ('p','u','x')) "
8114 : "WHERE i.indisvalid AND i.indisready "
8115 : "ORDER BY i.indrelid, indexname",
8116 : tbloids->data);
8117 : }
8118 :
8119 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8120 :
8121 259 : ntups = PQntuples(res);
8122 :
8123 259 : i_tableoid = PQfnumber(res, "tableoid");
8124 259 : i_oid = PQfnumber(res, "oid");
8125 259 : i_indrelid = PQfnumber(res, "indrelid");
8126 259 : i_indexname = PQfnumber(res, "indexname");
8127 259 : i_relpages = PQfnumber(res, "relpages");
8128 259 : i_reltuples = PQfnumber(res, "reltuples");
8129 259 : i_relallvisible = PQfnumber(res, "relallvisible");
8130 259 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8131 259 : i_parentidx = PQfnumber(res, "parentidx");
8132 259 : i_indexdef = PQfnumber(res, "indexdef");
8133 259 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8134 259 : i_indnatts = PQfnumber(res, "indnatts");
8135 259 : i_indkey = PQfnumber(res, "indkey");
8136 259 : i_indisclustered = PQfnumber(res, "indisclustered");
8137 259 : i_indisreplident = PQfnumber(res, "indisreplident");
8138 259 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8139 259 : i_contype = PQfnumber(res, "contype");
8140 259 : i_conname = PQfnumber(res, "conname");
8141 259 : i_condeferrable = PQfnumber(res, "condeferrable");
8142 259 : i_condeferred = PQfnumber(res, "condeferred");
8143 259 : i_conperiod = PQfnumber(res, "conperiod");
8144 259 : i_contableoid = PQfnumber(res, "contableoid");
8145 259 : i_conoid = PQfnumber(res, "conoid");
8146 259 : i_condef = PQfnumber(res, "condef");
8147 259 : i_indattnames = PQfnumber(res, "indattnames");
8148 259 : i_tablespace = PQfnumber(res, "tablespace");
8149 259 : i_indreloptions = PQfnumber(res, "indreloptions");
8150 259 : i_indstatcols = PQfnumber(res, "indstatcols");
8151 259 : i_indstatvals = PQfnumber(res, "indstatvals");
8152 :
8153 259 : indxinfo = pg_malloc_array(IndxInfo, ntups);
8154 :
8155 : /*
8156 : * Outer loop iterates once per table, not once per row. Incrementing of
8157 : * j is handled by the inner loop.
8158 : */
8159 259 : curtblindx = -1;
8160 2396 : for (int j = 0; j < ntups;)
8161 : {
8162 2137 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8163 2137 : TableInfo *tbinfo = NULL;
8164 2137 : char **indAttNames = NULL;
8165 2137 : int nindAttNames = 0;
8166 : int numinds;
8167 :
8168 : /* Count rows for this table */
8169 2777 : for (numinds = 1; numinds < ntups - j; numinds++)
8170 2698 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8171 2058 : break;
8172 :
8173 : /*
8174 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8175 : * order.
8176 : */
8177 25241 : while (++curtblindx < numTables)
8178 : {
8179 25241 : tbinfo = &tblinfo[curtblindx];
8180 25241 : if (tbinfo->dobj.catId.oid == indrelid)
8181 2137 : break;
8182 : }
8183 2137 : if (curtblindx >= numTables)
8184 0 : pg_fatal("unrecognized table OID %u", indrelid);
8185 : /* cross-check that we only got requested tables */
8186 2137 : if (!tbinfo->hasindex ||
8187 2137 : !tbinfo->interesting)
8188 0 : pg_fatal("unexpected index data for table \"%s\"",
8189 : tbinfo->dobj.name);
8190 :
8191 : /* Save data for this table */
8192 2137 : tbinfo->indexes = indxinfo + j;
8193 2137 : tbinfo->numIndexes = numinds;
8194 :
8195 4914 : for (int c = 0; c < numinds; c++, j++)
8196 : {
8197 : char contype;
8198 : char indexkind;
8199 : RelStatsInfo *relstats;
8200 2777 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8201 2777 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8202 2777 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8203 :
8204 2777 : indxinfo[j].dobj.objType = DO_INDEX;
8205 2777 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8206 2777 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8207 2777 : AssignDumpId(&indxinfo[j].dobj);
8208 2777 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8209 2777 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8210 2777 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8211 2777 : indxinfo[j].indextable = tbinfo;
8212 2777 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8213 2777 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8214 2777 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8215 2777 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8216 2777 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8217 2777 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8218 2777 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8219 2777 : indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8220 2777 : parseOidArray(PQgetvalue(res, j, i_indkey),
8221 2777 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8222 2777 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8223 2777 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8224 2777 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8225 2777 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8226 2777 : indxinfo[j].partattaches = (SimplePtrList)
8227 : {
8228 : NULL, NULL
8229 : };
8230 :
8231 2777 : if (indxinfo[j].parentidx == 0)
8232 2183 : indexkind = RELKIND_INDEX;
8233 : else
8234 594 : indexkind = RELKIND_PARTITIONED_INDEX;
8235 :
8236 2777 : if (!PQgetisnull(res, j, i_indattnames))
8237 : {
8238 146 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8239 : &indAttNames, &nindAttNames))
8240 0 : pg_fatal("could not parse %s array", "indattnames");
8241 : }
8242 :
8243 2777 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8244 : PQgetvalue(res, j, i_reltuples),
8245 : relallvisible, relallfrozen, indexkind,
8246 : indAttNames, nindAttNames);
8247 :
8248 2777 : contype = *(PQgetvalue(res, j, i_contype));
8249 2777 : if (contype == 'p' || contype == 'u' || contype == 'x')
8250 1682 : {
8251 : /*
8252 : * If we found a constraint matching the index, create an
8253 : * entry for it.
8254 : */
8255 : ConstraintInfo *constrinfo;
8256 :
8257 1682 : constrinfo = pg_malloc_object(ConstraintInfo);
8258 1682 : constrinfo->dobj.objType = DO_CONSTRAINT;
8259 1682 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8260 1682 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8261 1682 : AssignDumpId(&constrinfo->dobj);
8262 1682 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8263 1682 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8264 1682 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8265 1682 : constrinfo->contable = tbinfo;
8266 1682 : constrinfo->condomain = NULL;
8267 1682 : constrinfo->contype = contype;
8268 1682 : if (contype == 'x')
8269 10 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8270 : else
8271 1672 : constrinfo->condef = NULL;
8272 1682 : constrinfo->confrelid = InvalidOid;
8273 1682 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8274 1682 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8275 1682 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8276 1682 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8277 1682 : constrinfo->conislocal = true;
8278 1682 : constrinfo->separate = true;
8279 :
8280 1682 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8281 1682 : if (relstats != NULL)
8282 557 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8283 : }
8284 : else
8285 : {
8286 : /* Plain secondary index */
8287 1095 : indxinfo[j].indexconstraint = 0;
8288 : }
8289 : }
8290 : }
8291 :
8292 259 : PQclear(res);
8293 :
8294 259 : destroyPQExpBuffer(query);
8295 259 : destroyPQExpBuffer(tbloids);
8296 259 : }
8297 :
8298 : /*
8299 : * getExtendedStatistics
8300 : * get information about extended-statistics objects.
8301 : *
8302 : * Note: extended statistics data is not returned directly to the caller, but
8303 : * it does get entered into the DumpableObject tables.
8304 : */
8305 : void
8306 259 : getExtendedStatistics(Archive *fout)
8307 : {
8308 : PQExpBuffer query;
8309 : PGresult *res;
8310 : StatsExtInfo *statsextinfo;
8311 : int ntups;
8312 : int i_tableoid;
8313 : int i_oid;
8314 : int i_stxname;
8315 : int i_stxnamespace;
8316 : int i_stxowner;
8317 : int i_stxrelid;
8318 : int i_stattarget;
8319 : int i;
8320 :
8321 : /* Extended statistics were new in v10 */
8322 259 : if (fout->remoteVersion < 100000)
8323 0 : return;
8324 :
8325 259 : query = createPQExpBuffer();
8326 :
8327 259 : if (fout->remoteVersion < 130000)
8328 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8329 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8330 : "FROM pg_catalog.pg_statistic_ext");
8331 : else
8332 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8333 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8334 : "FROM pg_catalog.pg_statistic_ext");
8335 :
8336 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8337 :
8338 259 : ntups = PQntuples(res);
8339 :
8340 259 : i_tableoid = PQfnumber(res, "tableoid");
8341 259 : i_oid = PQfnumber(res, "oid");
8342 259 : i_stxname = PQfnumber(res, "stxname");
8343 259 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8344 259 : i_stxowner = PQfnumber(res, "stxowner");
8345 259 : i_stxrelid = PQfnumber(res, "stxrelid");
8346 259 : i_stattarget = PQfnumber(res, "stxstattarget");
8347 :
8348 259 : statsextinfo = pg_malloc_array(StatsExtInfo, ntups);
8349 :
8350 467 : for (i = 0; i < ntups; i++)
8351 : {
8352 208 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8353 208 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8354 208 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8355 208 : AssignDumpId(&statsextinfo[i].dobj);
8356 208 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8357 416 : statsextinfo[i].dobj.namespace =
8358 208 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8359 208 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8360 416 : statsextinfo[i].stattable =
8361 208 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8362 208 : if (PQgetisnull(res, i, i_stattarget))
8363 163 : statsextinfo[i].stattarget = -1;
8364 : else
8365 45 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8366 :
8367 : /* Decide whether we want to dump it */
8368 208 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8369 :
8370 208 : if (fout->dopt->dumpStatistics)
8371 152 : statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8372 : }
8373 :
8374 259 : PQclear(res);
8375 259 : destroyPQExpBuffer(query);
8376 : }
8377 :
8378 : /*
8379 : * getConstraints
8380 : *
8381 : * Get info about constraints on dumpable tables.
8382 : *
8383 : * Currently handles foreign keys only.
8384 : * Unique and primary key constraints are handled with indexes,
8385 : * while check constraints are processed in getTableAttrs().
8386 : */
8387 : void
8388 259 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8389 : {
8390 259 : PQExpBuffer query = createPQExpBuffer();
8391 259 : PQExpBuffer tbloids = createPQExpBuffer();
8392 : PGresult *res;
8393 : int ntups;
8394 : int curtblindx;
8395 259 : TableInfo *tbinfo = NULL;
8396 : ConstraintInfo *constrinfo;
8397 : int i_contableoid,
8398 : i_conoid,
8399 : i_conrelid,
8400 : i_conname,
8401 : i_confrelid,
8402 : i_conindid,
8403 : i_condef;
8404 :
8405 : /*
8406 : * We want to perform just one query against pg_constraint. However, we
8407 : * mustn't try to select every row of the catalog and then sort it out on
8408 : * the client side, because some of the server-side functions we need
8409 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8410 : * build an array of the OIDs of tables we care about (and now have lock
8411 : * on!), and use a WHERE clause to constrain which rows are selected.
8412 : */
8413 259 : appendPQExpBufferChar(tbloids, '{');
8414 70986 : for (int i = 0; i < numTables; i++)
8415 : {
8416 70727 : TableInfo *tinfo = &tblinfo[i];
8417 :
8418 70727 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8419 63102 : continue;
8420 :
8421 : /* OK, we need info for this table */
8422 7625 : if (tbloids->len > 1) /* do we have more than the '{'? */
8423 7447 : appendPQExpBufferChar(tbloids, ',');
8424 7625 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8425 : }
8426 259 : appendPQExpBufferChar(tbloids, '}');
8427 :
8428 259 : appendPQExpBufferStr(query,
8429 : "SELECT c.tableoid, c.oid, "
8430 : "conrelid, conname, confrelid, ");
8431 259 : if (fout->remoteVersion >= 110000)
8432 259 : appendPQExpBufferStr(query, "conindid, ");
8433 : else
8434 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8435 259 : appendPQExpBuffer(query,
8436 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8437 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8438 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8439 : "WHERE contype = 'f' ",
8440 : tbloids->data);
8441 259 : if (fout->remoteVersion >= 110000)
8442 259 : appendPQExpBufferStr(query,
8443 : "AND conparentid = 0 ");
8444 259 : appendPQExpBufferStr(query,
8445 : "ORDER BY conrelid, conname");
8446 :
8447 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8448 :
8449 259 : ntups = PQntuples(res);
8450 :
8451 259 : i_contableoid = PQfnumber(res, "tableoid");
8452 259 : i_conoid = PQfnumber(res, "oid");
8453 259 : i_conrelid = PQfnumber(res, "conrelid");
8454 259 : i_conname = PQfnumber(res, "conname");
8455 259 : i_confrelid = PQfnumber(res, "confrelid");
8456 259 : i_conindid = PQfnumber(res, "conindid");
8457 259 : i_condef = PQfnumber(res, "condef");
8458 :
8459 259 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8460 :
8461 259 : curtblindx = -1;
8462 490 : for (int j = 0; j < ntups; j++)
8463 : {
8464 231 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8465 : TableInfo *reftable;
8466 :
8467 : /*
8468 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8469 : * order.
8470 : */
8471 231 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8472 : {
8473 15665 : while (++curtblindx < numTables)
8474 : {
8475 15665 : tbinfo = &tblinfo[curtblindx];
8476 15665 : if (tbinfo->dobj.catId.oid == conrelid)
8477 191 : break;
8478 : }
8479 191 : if (curtblindx >= numTables)
8480 0 : pg_fatal("unrecognized table OID %u", conrelid);
8481 : }
8482 :
8483 231 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8484 231 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8485 231 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8486 231 : AssignDumpId(&constrinfo[j].dobj);
8487 231 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8488 231 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8489 231 : constrinfo[j].contable = tbinfo;
8490 231 : constrinfo[j].condomain = NULL;
8491 231 : constrinfo[j].contype = 'f';
8492 231 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8493 231 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8494 231 : constrinfo[j].conindex = 0;
8495 231 : constrinfo[j].condeferrable = false;
8496 231 : constrinfo[j].condeferred = false;
8497 231 : constrinfo[j].conislocal = true;
8498 231 : constrinfo[j].separate = true;
8499 :
8500 : /*
8501 : * Restoring an FK that points to a partitioned table requires that
8502 : * all partition indexes have been attached beforehand. Ensure that
8503 : * happens by making the constraint depend on each index partition
8504 : * attach object.
8505 : */
8506 231 : reftable = findTableByOid(constrinfo[j].confrelid);
8507 231 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8508 : {
8509 30 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8510 :
8511 30 : if (indexOid != InvalidOid)
8512 : {
8513 30 : for (int k = 0; k < reftable->numIndexes; k++)
8514 : {
8515 : IndxInfo *refidx;
8516 :
8517 : /* not our index? */
8518 30 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8519 0 : continue;
8520 :
8521 30 : refidx = &reftable->indexes[k];
8522 30 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8523 30 : break;
8524 : }
8525 : }
8526 : }
8527 : }
8528 :
8529 259 : PQclear(res);
8530 :
8531 259 : destroyPQExpBuffer(query);
8532 259 : destroyPQExpBuffer(tbloids);
8533 259 : }
8534 :
8535 : /*
8536 : * addConstrChildIdxDeps
8537 : *
8538 : * Recursive subroutine for getConstraints
8539 : *
8540 : * Given an object representing a foreign key constraint and an index on the
8541 : * partitioned table it references, mark the constraint object as dependent
8542 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8543 : * drilling down to their partitions if any. This ensures that the FK is not
8544 : * restored until the index is fully marked valid.
8545 : */
8546 : static void
8547 55 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8548 : {
8549 : SimplePtrListCell *cell;
8550 :
8551 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8552 :
8553 185 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8554 : {
8555 130 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8556 :
8557 130 : addObjectDependency(dobj, attach->dobj.dumpId);
8558 :
8559 130 : if (attach->partitionIdx->partattaches.head != NULL)
8560 25 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8561 : }
8562 55 : }
8563 :
8564 : /*
8565 : * getDomainConstraints
8566 : *
8567 : * Get info about constraints on a domain.
8568 : */
8569 : static void
8570 178 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8571 : {
8572 : ConstraintInfo *constrinfo;
8573 178 : PQExpBuffer query = createPQExpBuffer();
8574 : PGresult *res;
8575 : int i_tableoid,
8576 : i_oid,
8577 : i_conname,
8578 : i_consrc,
8579 : i_convalidated,
8580 : i_contype;
8581 : int ntups;
8582 :
8583 178 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8584 : {
8585 : /*
8586 : * Set up query for constraint-specific details. For servers 17 and
8587 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8588 : * just the latter.
8589 : */
8590 43 : appendPQExpBuffer(query,
8591 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8592 : "SELECT tableoid, oid, conname, "
8593 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8594 : "convalidated, contype "
8595 : "FROM pg_catalog.pg_constraint "
8596 : "WHERE contypid = $1 AND contype IN (%s) "
8597 : "ORDER BY conname",
8598 43 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8599 :
8600 43 : ExecuteSqlStatement(fout, query->data);
8601 :
8602 43 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8603 : }
8604 :
8605 178 : printfPQExpBuffer(query,
8606 : "EXECUTE getDomainConstraints('%u')",
8607 : tyinfo->dobj.catId.oid);
8608 :
8609 178 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8610 :
8611 178 : ntups = PQntuples(res);
8612 :
8613 178 : i_tableoid = PQfnumber(res, "tableoid");
8614 178 : i_oid = PQfnumber(res, "oid");
8615 178 : i_conname = PQfnumber(res, "conname");
8616 178 : i_consrc = PQfnumber(res, "consrc");
8617 178 : i_convalidated = PQfnumber(res, "convalidated");
8618 178 : i_contype = PQfnumber(res, "contype");
8619 :
8620 178 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8621 178 : tyinfo->domChecks = constrinfo;
8622 :
8623 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8624 364 : for (int i = 0, j = 0; i < ntups; i++)
8625 : {
8626 186 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8627 186 : char contype = (PQgetvalue(res, i, i_contype))[0];
8628 : ConstraintInfo *constraint;
8629 :
8630 186 : if (contype == CONSTRAINT_CHECK)
8631 : {
8632 133 : constraint = &constrinfo[j++];
8633 133 : tyinfo->nDomChecks++;
8634 : }
8635 : else
8636 : {
8637 : Assert(contype == CONSTRAINT_NOTNULL);
8638 : Assert(tyinfo->notnull == NULL);
8639 : /* use last item in array for the not-null constraint */
8640 53 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8641 53 : constraint = tyinfo->notnull;
8642 : }
8643 :
8644 186 : constraint->dobj.objType = DO_CONSTRAINT;
8645 186 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8646 186 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8647 186 : AssignDumpId(&(constraint->dobj));
8648 186 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8649 186 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8650 186 : constraint->contable = NULL;
8651 186 : constraint->condomain = tyinfo;
8652 186 : constraint->contype = contype;
8653 186 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8654 186 : constraint->confrelid = InvalidOid;
8655 186 : constraint->conindex = 0;
8656 186 : constraint->condeferrable = false;
8657 186 : constraint->condeferred = false;
8658 186 : constraint->conislocal = true;
8659 :
8660 186 : constraint->separate = !validated;
8661 :
8662 : /*
8663 : * Make the domain depend on the constraint, ensuring it won't be
8664 : * output till any constraint dependencies are OK. If the constraint
8665 : * has not been validated, it's going to be dumped after the domain
8666 : * anyway, so this doesn't matter.
8667 : */
8668 186 : if (validated)
8669 181 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8670 : }
8671 :
8672 178 : PQclear(res);
8673 :
8674 178 : destroyPQExpBuffer(query);
8675 178 : }
8676 :
8677 : /*
8678 : * getRules
8679 : * get basic information about every rule in the system
8680 : */
8681 : void
8682 259 : getRules(Archive *fout)
8683 : {
8684 : PGresult *res;
8685 : int ntups;
8686 : int i;
8687 259 : PQExpBuffer query = createPQExpBuffer();
8688 : RuleInfo *ruleinfo;
8689 : int i_tableoid;
8690 : int i_oid;
8691 : int i_rulename;
8692 : int i_ruletable;
8693 : int i_ev_type;
8694 : int i_is_instead;
8695 : int i_ev_enabled;
8696 :
8697 259 : appendPQExpBufferStr(query, "SELECT "
8698 : "tableoid, oid, rulename, "
8699 : "ev_class AS ruletable, ev_type, is_instead, "
8700 : "ev_enabled "
8701 : "FROM pg_rewrite "
8702 : "ORDER BY oid");
8703 :
8704 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8705 :
8706 259 : ntups = PQntuples(res);
8707 :
8708 259 : ruleinfo = pg_malloc_array(RuleInfo, ntups);
8709 :
8710 259 : i_tableoid = PQfnumber(res, "tableoid");
8711 259 : i_oid = PQfnumber(res, "oid");
8712 259 : i_rulename = PQfnumber(res, "rulename");
8713 259 : i_ruletable = PQfnumber(res, "ruletable");
8714 259 : i_ev_type = PQfnumber(res, "ev_type");
8715 259 : i_is_instead = PQfnumber(res, "is_instead");
8716 259 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8717 :
8718 43906 : for (i = 0; i < ntups; i++)
8719 : {
8720 : Oid ruletableoid;
8721 :
8722 43647 : ruleinfo[i].dobj.objType = DO_RULE;
8723 43647 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8724 43647 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8725 43647 : AssignDumpId(&ruleinfo[i].dobj);
8726 43647 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8727 43647 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8728 43647 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8729 43647 : if (ruleinfo[i].ruletable == NULL)
8730 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8731 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8732 43647 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8733 43647 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8734 43647 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8735 43647 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8736 43647 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8737 43647 : if (ruleinfo[i].ruletable)
8738 : {
8739 : /*
8740 : * If the table is a view or materialized view, force its ON
8741 : * SELECT rule to be sorted before the view itself --- this
8742 : * ensures that any dependencies for the rule affect the table's
8743 : * positioning. Other rules are forced to appear after their
8744 : * table.
8745 : */
8746 43647 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8747 699 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8748 43416 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8749 : {
8750 42856 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8751 42856 : ruleinfo[i].dobj.dumpId);
8752 : /* We'll merge the rule into CREATE VIEW, if possible */
8753 42856 : ruleinfo[i].separate = false;
8754 : }
8755 : else
8756 : {
8757 791 : addObjectDependency(&ruleinfo[i].dobj,
8758 791 : ruleinfo[i].ruletable->dobj.dumpId);
8759 791 : ruleinfo[i].separate = true;
8760 : }
8761 : }
8762 : else
8763 0 : ruleinfo[i].separate = true;
8764 : }
8765 :
8766 259 : PQclear(res);
8767 :
8768 259 : destroyPQExpBuffer(query);
8769 259 : }
8770 :
8771 : /*
8772 : * getTriggers
8773 : * get information about every trigger on a dumpable table
8774 : *
8775 : * Note: trigger data is not returned directly to the caller, but it
8776 : * does get entered into the DumpableObject tables.
8777 : */
8778 : void
8779 259 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8780 : {
8781 259 : PQExpBuffer query = createPQExpBuffer();
8782 259 : PQExpBuffer tbloids = createPQExpBuffer();
8783 : PGresult *res;
8784 : int ntups;
8785 : int curtblindx;
8786 : TriggerInfo *tginfo;
8787 : int i_tableoid,
8788 : i_oid,
8789 : i_tgrelid,
8790 : i_tgname,
8791 : i_tgenabled,
8792 : i_tgispartition,
8793 : i_tgdef;
8794 :
8795 : /*
8796 : * We want to perform just one query against pg_trigger. However, we
8797 : * mustn't try to select every row of the catalog and then sort it out on
8798 : * the client side, because some of the server-side functions we need
8799 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8800 : * build an array of the OIDs of tables we care about (and now have lock
8801 : * on!), and use a WHERE clause to constrain which rows are selected.
8802 : */
8803 259 : appendPQExpBufferChar(tbloids, '{');
8804 70986 : for (int i = 0; i < numTables; i++)
8805 : {
8806 70727 : TableInfo *tbinfo = &tblinfo[i];
8807 :
8808 70727 : if (!tbinfo->hastriggers ||
8809 1233 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8810 69790 : continue;
8811 :
8812 : /* OK, we need info for this table */
8813 937 : if (tbloids->len > 1) /* do we have more than the '{'? */
8814 886 : appendPQExpBufferChar(tbloids, ',');
8815 937 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8816 : }
8817 259 : appendPQExpBufferChar(tbloids, '}');
8818 :
8819 259 : if (fout->remoteVersion >= 150000)
8820 : {
8821 : /*
8822 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8823 : * result in non-forward-compatible dumps of WHEN clauses due to
8824 : * under-parenthesization.
8825 : *
8826 : * NB: We need to see partition triggers in case the tgenabled flag
8827 : * has been changed from the parent.
8828 : */
8829 259 : appendPQExpBuffer(query,
8830 : "SELECT t.tgrelid, t.tgname, "
8831 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8832 : "t.tgenabled, t.tableoid, t.oid, "
8833 : "t.tgparentid <> 0 AS tgispartition\n"
8834 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8835 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8836 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8837 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8838 : "OR t.tgenabled != u.tgenabled) "
8839 : "ORDER BY t.tgrelid, t.tgname",
8840 : tbloids->data);
8841 : }
8842 0 : else if (fout->remoteVersion >= 130000)
8843 : {
8844 : /*
8845 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8846 : * result in non-forward-compatible dumps of WHEN clauses due to
8847 : * under-parenthesization.
8848 : *
8849 : * NB: We need to see tgisinternal triggers in partitions, in case the
8850 : * tgenabled flag has been changed from the parent.
8851 : */
8852 0 : appendPQExpBuffer(query,
8853 : "SELECT t.tgrelid, t.tgname, "
8854 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8855 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8856 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8857 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8858 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8859 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8860 : "ORDER BY t.tgrelid, t.tgname",
8861 : tbloids->data);
8862 : }
8863 0 : else if (fout->remoteVersion >= 110000)
8864 : {
8865 : /*
8866 : * NB: We need to see tgisinternal triggers in partitions, in case the
8867 : * tgenabled flag has been changed from the parent. No tgparentid in
8868 : * version 11-12, so we have to match them via pg_depend.
8869 : *
8870 : * See above about pretty=true in pg_get_triggerdef.
8871 : */
8872 0 : appendPQExpBuffer(query,
8873 : "SELECT t.tgrelid, t.tgname, "
8874 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8875 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8876 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8877 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8878 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8879 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8880 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8881 : " d.objid = t.oid "
8882 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8883 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8884 : "ORDER BY t.tgrelid, t.tgname",
8885 : tbloids->data);
8886 : }
8887 : else
8888 : {
8889 : /* See above about pretty=true in pg_get_triggerdef */
8890 0 : appendPQExpBuffer(query,
8891 : "SELECT t.tgrelid, t.tgname, "
8892 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8893 : "t.tgenabled, false as tgispartition, "
8894 : "t.tableoid, t.oid "
8895 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8896 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8897 : "WHERE NOT tgisinternal "
8898 : "ORDER BY t.tgrelid, t.tgname",
8899 : tbloids->data);
8900 : }
8901 :
8902 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8903 :
8904 259 : ntups = PQntuples(res);
8905 :
8906 259 : i_tableoid = PQfnumber(res, "tableoid");
8907 259 : i_oid = PQfnumber(res, "oid");
8908 259 : i_tgrelid = PQfnumber(res, "tgrelid");
8909 259 : i_tgname = PQfnumber(res, "tgname");
8910 259 : i_tgenabled = PQfnumber(res, "tgenabled");
8911 259 : i_tgispartition = PQfnumber(res, "tgispartition");
8912 259 : i_tgdef = PQfnumber(res, "tgdef");
8913 :
8914 259 : tginfo = pg_malloc_array(TriggerInfo, ntups);
8915 :
8916 : /*
8917 : * Outer loop iterates once per table, not once per row. Incrementing of
8918 : * j is handled by the inner loop.
8919 : */
8920 259 : curtblindx = -1;
8921 565 : for (int j = 0; j < ntups;)
8922 : {
8923 306 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8924 306 : TableInfo *tbinfo = NULL;
8925 : int numtrigs;
8926 :
8927 : /* Count rows for this table */
8928 523 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8929 472 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8930 255 : break;
8931 :
8932 : /*
8933 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8934 : * order.
8935 : */
8936 17416 : while (++curtblindx < numTables)
8937 : {
8938 17416 : tbinfo = &tblinfo[curtblindx];
8939 17416 : if (tbinfo->dobj.catId.oid == tgrelid)
8940 306 : break;
8941 : }
8942 306 : if (curtblindx >= numTables)
8943 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8944 :
8945 : /* Save data for this table */
8946 306 : tbinfo->triggers = tginfo + j;
8947 306 : tbinfo->numTriggers = numtrigs;
8948 :
8949 829 : for (int c = 0; c < numtrigs; c++, j++)
8950 : {
8951 523 : tginfo[j].dobj.objType = DO_TRIGGER;
8952 523 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8953 523 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8954 523 : AssignDumpId(&tginfo[j].dobj);
8955 523 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8956 523 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8957 523 : tginfo[j].tgtable = tbinfo;
8958 523 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8959 523 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8960 523 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8961 : }
8962 : }
8963 :
8964 259 : PQclear(res);
8965 :
8966 259 : destroyPQExpBuffer(query);
8967 259 : destroyPQExpBuffer(tbloids);
8968 259 : }
8969 :
8970 : /*
8971 : * getEventTriggers
8972 : * get information about event triggers
8973 : */
8974 : void
8975 259 : getEventTriggers(Archive *fout)
8976 : {
8977 : int i;
8978 : PQExpBuffer query;
8979 : PGresult *res;
8980 : EventTriggerInfo *evtinfo;
8981 : int i_tableoid,
8982 : i_oid,
8983 : i_evtname,
8984 : i_evtevent,
8985 : i_evtowner,
8986 : i_evttags,
8987 : i_evtfname,
8988 : i_evtenabled;
8989 : int ntups;
8990 :
8991 : /* Before 9.3, there are no event triggers */
8992 259 : if (fout->remoteVersion < 90300)
8993 0 : return;
8994 :
8995 259 : query = createPQExpBuffer();
8996 :
8997 259 : appendPQExpBufferStr(query,
8998 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8999 : "evtevent, evtowner, "
9000 : "array_to_string(array("
9001 : "select quote_literal(x) "
9002 : " from unnest(evttags) as t(x)), ', ') as evttags, "
9003 : "e.evtfoid::regproc as evtfname "
9004 : "FROM pg_event_trigger e "
9005 : "ORDER BY e.oid");
9006 :
9007 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9008 :
9009 259 : ntups = PQntuples(res);
9010 :
9011 259 : evtinfo = pg_malloc_array(EventTriggerInfo, ntups);
9012 :
9013 259 : i_tableoid = PQfnumber(res, "tableoid");
9014 259 : i_oid = PQfnumber(res, "oid");
9015 259 : i_evtname = PQfnumber(res, "evtname");
9016 259 : i_evtevent = PQfnumber(res, "evtevent");
9017 259 : i_evtowner = PQfnumber(res, "evtowner");
9018 259 : i_evttags = PQfnumber(res, "evttags");
9019 259 : i_evtfname = PQfnumber(res, "evtfname");
9020 259 : i_evtenabled = PQfnumber(res, "evtenabled");
9021 :
9022 311 : for (i = 0; i < ntups; i++)
9023 : {
9024 52 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
9025 52 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9026 52 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9027 52 : AssignDumpId(&evtinfo[i].dobj);
9028 52 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
9029 52 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
9030 52 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
9031 52 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
9032 52 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
9033 52 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
9034 52 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
9035 :
9036 : /* Decide whether we want to dump it */
9037 52 : selectDumpableObject(&(evtinfo[i].dobj), fout);
9038 : }
9039 :
9040 259 : PQclear(res);
9041 :
9042 259 : destroyPQExpBuffer(query);
9043 : }
9044 :
9045 : /*
9046 : * getProcLangs
9047 : * get basic information about every procedural language in the system
9048 : *
9049 : * NB: this must run after getFuncs() because we assume we can do
9050 : * findFuncByOid().
9051 : */
9052 : void
9053 259 : getProcLangs(Archive *fout)
9054 : {
9055 : PGresult *res;
9056 : int ntups;
9057 : int i;
9058 259 : PQExpBuffer query = createPQExpBuffer();
9059 : ProcLangInfo *planginfo;
9060 : int i_tableoid;
9061 : int i_oid;
9062 : int i_lanname;
9063 : int i_lanpltrusted;
9064 : int i_lanplcallfoid;
9065 : int i_laninline;
9066 : int i_lanvalidator;
9067 : int i_lanacl;
9068 : int i_acldefault;
9069 : int i_lanowner;
9070 :
9071 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9072 : "lanname, lanpltrusted, lanplcallfoid, "
9073 : "laninline, lanvalidator, "
9074 : "lanacl, "
9075 : "acldefault('l', lanowner) AS acldefault, "
9076 : "lanowner "
9077 : "FROM pg_language "
9078 : "WHERE lanispl "
9079 : "ORDER BY oid");
9080 :
9081 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9082 :
9083 259 : ntups = PQntuples(res);
9084 :
9085 259 : planginfo = pg_malloc_array(ProcLangInfo, ntups);
9086 :
9087 259 : i_tableoid = PQfnumber(res, "tableoid");
9088 259 : i_oid = PQfnumber(res, "oid");
9089 259 : i_lanname = PQfnumber(res, "lanname");
9090 259 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
9091 259 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
9092 259 : i_laninline = PQfnumber(res, "laninline");
9093 259 : i_lanvalidator = PQfnumber(res, "lanvalidator");
9094 259 : i_lanacl = PQfnumber(res, "lanacl");
9095 259 : i_acldefault = PQfnumber(res, "acldefault");
9096 259 : i_lanowner = PQfnumber(res, "lanowner");
9097 :
9098 563 : for (i = 0; i < ntups; i++)
9099 : {
9100 304 : planginfo[i].dobj.objType = DO_PROCLANG;
9101 304 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9102 304 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9103 304 : AssignDumpId(&planginfo[i].dobj);
9104 :
9105 304 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9106 304 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9107 304 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9108 304 : planginfo[i].dacl.privtype = 0;
9109 304 : planginfo[i].dacl.initprivs = NULL;
9110 304 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9111 304 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9112 304 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9113 304 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9114 304 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9115 :
9116 : /* Decide whether we want to dump it */
9117 304 : selectDumpableProcLang(&(planginfo[i]), fout);
9118 :
9119 : /* Mark whether language has an ACL */
9120 304 : if (!PQgetisnull(res, i, i_lanacl))
9121 45 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9122 : }
9123 :
9124 259 : PQclear(res);
9125 :
9126 259 : destroyPQExpBuffer(query);
9127 259 : }
9128 :
9129 : /*
9130 : * getCasts
9131 : * get basic information about most casts in the system
9132 : *
9133 : * Skip casts from a range to its multirange, since we'll create those
9134 : * automatically.
9135 : */
9136 : void
9137 259 : getCasts(Archive *fout)
9138 : {
9139 : PGresult *res;
9140 : int ntups;
9141 : int i;
9142 259 : PQExpBuffer query = createPQExpBuffer();
9143 : CastInfo *castinfo;
9144 : int i_tableoid;
9145 : int i_oid;
9146 : int i_castsource;
9147 : int i_casttarget;
9148 : int i_castfunc;
9149 : int i_castcontext;
9150 : int i_castmethod;
9151 :
9152 259 : if (fout->remoteVersion >= 140000)
9153 : {
9154 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9155 : "castsource, casttarget, castfunc, castcontext, "
9156 : "castmethod "
9157 : "FROM pg_cast c "
9158 : "WHERE NOT EXISTS ( "
9159 : "SELECT 1 FROM pg_range r "
9160 : "WHERE c.castsource = r.rngtypid "
9161 : "AND c.casttarget = r.rngmultitypid "
9162 : ") "
9163 : "ORDER BY 3,4");
9164 : }
9165 : else
9166 : {
9167 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9168 : "castsource, casttarget, castfunc, castcontext, "
9169 : "castmethod "
9170 : "FROM pg_cast ORDER BY 3,4");
9171 : }
9172 :
9173 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9174 :
9175 259 : ntups = PQntuples(res);
9176 :
9177 259 : castinfo = pg_malloc_array(CastInfo, ntups);
9178 :
9179 259 : i_tableoid = PQfnumber(res, "tableoid");
9180 259 : i_oid = PQfnumber(res, "oid");
9181 259 : i_castsource = PQfnumber(res, "castsource");
9182 259 : i_casttarget = PQfnumber(res, "casttarget");
9183 259 : i_castfunc = PQfnumber(res, "castfunc");
9184 259 : i_castcontext = PQfnumber(res, "castcontext");
9185 259 : i_castmethod = PQfnumber(res, "castmethod");
9186 :
9187 63283 : for (i = 0; i < ntups; i++)
9188 : {
9189 : PQExpBufferData namebuf;
9190 : TypeInfo *sTypeInfo;
9191 : TypeInfo *tTypeInfo;
9192 :
9193 63024 : castinfo[i].dobj.objType = DO_CAST;
9194 63024 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9195 63024 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9196 63024 : AssignDumpId(&castinfo[i].dobj);
9197 63024 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9198 63024 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9199 63024 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9200 63024 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9201 63024 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9202 :
9203 : /*
9204 : * Try to name cast as concatenation of typnames. This is only used
9205 : * for purposes of sorting. If we fail to find either type, the name
9206 : * will be an empty string.
9207 : */
9208 63024 : initPQExpBuffer(&namebuf);
9209 63024 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9210 63024 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9211 63024 : if (sTypeInfo && tTypeInfo)
9212 63024 : appendPQExpBuffer(&namebuf, "%s %s",
9213 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9214 63024 : castinfo[i].dobj.name = namebuf.data;
9215 :
9216 : /* Decide whether we want to dump it */
9217 63024 : selectDumpableCast(&(castinfo[i]), fout);
9218 : }
9219 :
9220 259 : PQclear(res);
9221 :
9222 259 : destroyPQExpBuffer(query);
9223 259 : }
9224 :
9225 : static char *
9226 88 : get_language_name(Archive *fout, Oid langid)
9227 : {
9228 : PQExpBuffer query;
9229 : PGresult *res;
9230 : char *lanname;
9231 :
9232 88 : query = createPQExpBuffer();
9233 88 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9234 88 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9235 88 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9236 88 : destroyPQExpBuffer(query);
9237 88 : PQclear(res);
9238 :
9239 88 : return lanname;
9240 : }
9241 :
9242 : /*
9243 : * getTransforms
9244 : * get basic information about every transform in the system
9245 : */
9246 : void
9247 259 : getTransforms(Archive *fout)
9248 : {
9249 : PGresult *res;
9250 : int ntups;
9251 : int i;
9252 : PQExpBuffer query;
9253 : TransformInfo *transforminfo;
9254 : int i_tableoid;
9255 : int i_oid;
9256 : int i_trftype;
9257 : int i_trflang;
9258 : int i_trffromsql;
9259 : int i_trftosql;
9260 :
9261 : /* Transforms didn't exist pre-9.5 */
9262 259 : if (fout->remoteVersion < 90500)
9263 0 : return;
9264 :
9265 259 : query = createPQExpBuffer();
9266 :
9267 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9268 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9269 : "FROM pg_transform "
9270 : "ORDER BY 3,4");
9271 :
9272 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9273 :
9274 259 : ntups = PQntuples(res);
9275 :
9276 259 : transforminfo = pg_malloc_array(TransformInfo, ntups);
9277 :
9278 259 : i_tableoid = PQfnumber(res, "tableoid");
9279 259 : i_oid = PQfnumber(res, "oid");
9280 259 : i_trftype = PQfnumber(res, "trftype");
9281 259 : i_trflang = PQfnumber(res, "trflang");
9282 259 : i_trffromsql = PQfnumber(res, "trffromsql");
9283 259 : i_trftosql = PQfnumber(res, "trftosql");
9284 :
9285 311 : for (i = 0; i < ntups; i++)
9286 : {
9287 : PQExpBufferData namebuf;
9288 : TypeInfo *typeInfo;
9289 : char *lanname;
9290 :
9291 52 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9292 52 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9293 52 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9294 52 : AssignDumpId(&transforminfo[i].dobj);
9295 52 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9296 52 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9297 52 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9298 52 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9299 :
9300 : /*
9301 : * Try to name transform as concatenation of type and language name.
9302 : * This is only used for purposes of sorting. If we fail to find
9303 : * either, the name will be an empty string.
9304 : */
9305 52 : initPQExpBuffer(&namebuf);
9306 52 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9307 52 : lanname = get_language_name(fout, transforminfo[i].trflang);
9308 52 : if (typeInfo && lanname)
9309 52 : appendPQExpBuffer(&namebuf, "%s %s",
9310 : typeInfo->dobj.name, lanname);
9311 52 : transforminfo[i].dobj.name = namebuf.data;
9312 52 : free(lanname);
9313 :
9314 : /* Decide whether we want to dump it */
9315 52 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9316 : }
9317 :
9318 259 : PQclear(res);
9319 :
9320 259 : destroyPQExpBuffer(query);
9321 : }
9322 :
9323 : /*
9324 : * getTableAttrs -
9325 : * for each interesting table, read info about its attributes
9326 : * (names, types, default values, CHECK constraints, etc)
9327 : *
9328 : * modifies tblinfo
9329 : */
9330 : void
9331 259 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9332 : {
9333 259 : DumpOptions *dopt = fout->dopt;
9334 259 : PQExpBuffer q = createPQExpBuffer();
9335 259 : PQExpBuffer tbloids = createPQExpBuffer();
9336 259 : PQExpBuffer checkoids = createPQExpBuffer();
9337 259 : PQExpBuffer invalidnotnulloids = NULL;
9338 : PGresult *res;
9339 : int ntups;
9340 : int curtblindx;
9341 : int i_attrelid;
9342 : int i_attnum;
9343 : int i_attname;
9344 : int i_atttypname;
9345 : int i_attstattarget;
9346 : int i_attstorage;
9347 : int i_typstorage;
9348 : int i_attidentity;
9349 : int i_attgenerated;
9350 : int i_attisdropped;
9351 : int i_attlen;
9352 : int i_attalign;
9353 : int i_attislocal;
9354 : int i_notnull_name;
9355 : int i_notnull_comment;
9356 : int i_notnull_noinherit;
9357 : int i_notnull_islocal;
9358 : int i_notnull_invalidoid;
9359 : int i_attoptions;
9360 : int i_attcollation;
9361 : int i_attcompression;
9362 : int i_attfdwoptions;
9363 : int i_attmissingval;
9364 : int i_atthasdef;
9365 :
9366 : /*
9367 : * We want to perform just one query against pg_attribute, and then just
9368 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9369 : * (for CHECK constraints and for NOT NULL constraints). However, we
9370 : * mustn't try to select every row of those catalogs and then sort it out
9371 : * on the client side, because some of the server-side functions we need
9372 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9373 : * build an array of the OIDs of tables we care about (and now have lock
9374 : * on!), and use a WHERE clause to constrain which rows are selected.
9375 : */
9376 259 : appendPQExpBufferChar(tbloids, '{');
9377 259 : appendPQExpBufferChar(checkoids, '{');
9378 70986 : for (int i = 0; i < numTables; i++)
9379 : {
9380 70727 : TableInfo *tbinfo = &tblinfo[i];
9381 :
9382 : /* Don't bother to collect info for sequences */
9383 70727 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9384 647 : continue;
9385 :
9386 : /*
9387 : * Don't bother with uninteresting tables, either. For binary
9388 : * upgrades, this is bypassed for pg_largeobject_metadata and
9389 : * pg_shdepend so that the columns names are collected for the
9390 : * corresponding COPY commands. Restoring the data for those catalogs
9391 : * is faster than restoring the equivalent set of large object
9392 : * commands.
9393 : */
9394 70080 : if (!tbinfo->interesting &&
9395 62817 : !(fout->dopt->binary_upgrade &&
9396 9418 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9397 9378 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9398 62737 : continue;
9399 :
9400 : /* OK, we need info for this table */
9401 7343 : if (tbloids->len > 1) /* do we have more than the '{'? */
9402 7144 : appendPQExpBufferChar(tbloids, ',');
9403 7343 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9404 :
9405 7343 : if (tbinfo->ncheck > 0)
9406 : {
9407 : /* Also make a list of the ones with check constraints */
9408 528 : if (checkoids->len > 1) /* do we have more than the '{'? */
9409 459 : appendPQExpBufferChar(checkoids, ',');
9410 528 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9411 : }
9412 : }
9413 259 : appendPQExpBufferChar(tbloids, '}');
9414 259 : appendPQExpBufferChar(checkoids, '}');
9415 :
9416 : /*
9417 : * Find all the user attributes and their types.
9418 : *
9419 : * Since we only want to dump COLLATE clauses for attributes whose
9420 : * collation is different from their type's default, we use a CASE here to
9421 : * suppress uninteresting attcollations cheaply.
9422 : */
9423 259 : appendPQExpBufferStr(q,
9424 : "SELECT\n"
9425 : "a.attrelid,\n"
9426 : "a.attnum,\n"
9427 : "a.attname,\n"
9428 : "a.attstattarget,\n"
9429 : "a.attstorage,\n"
9430 : "t.typstorage,\n"
9431 : "a.atthasdef,\n"
9432 : "a.attisdropped,\n"
9433 : "a.attlen,\n"
9434 : "a.attalign,\n"
9435 : "a.attislocal,\n"
9436 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9437 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9438 : "CASE WHEN a.attcollation <> t.typcollation "
9439 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9440 : "pg_catalog.array_to_string(ARRAY("
9441 : "SELECT pg_catalog.quote_ident(option_name) || "
9442 : "' ' || pg_catalog.quote_literal(option_value) "
9443 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9444 : "ORDER BY option_name"
9445 : "), E',\n ') AS attfdwoptions,\n");
9446 :
9447 : /*
9448 : * Find out any NOT NULL markings for each column. In 18 and up we read
9449 : * pg_constraint to obtain the constraint name, and for valid constraints
9450 : * also pg_description to obtain its comment. notnull_noinherit is set
9451 : * according to the NO INHERIT property. For versions prior to 18, we
9452 : * store an empty string as the name when a constraint is marked as
9453 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9454 : * without a name); also, such cases are never NO INHERIT.
9455 : *
9456 : * For invalid constraints, we need to store their OIDs for processing
9457 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9458 : * is invalid, and NULL otherwise. Their comments are handled not here
9459 : * but by collectComments, because they're their own dumpable object.
9460 : *
9461 : * We track in notnull_islocal whether the constraint was defined directly
9462 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9463 : * might modify this later.
9464 : */
9465 259 : if (fout->remoteVersion >= 180000)
9466 259 : appendPQExpBufferStr(q,
9467 : "co.conname AS notnull_name,\n"
9468 : "CASE WHEN co.convalidated THEN pt.description"
9469 : " ELSE NULL END AS notnull_comment,\n"
9470 : "CASE WHEN NOT co.convalidated THEN co.oid "
9471 : "ELSE NULL END AS notnull_invalidoid,\n"
9472 : "co.connoinherit AS notnull_noinherit,\n"
9473 : "co.conislocal AS notnull_islocal,\n");
9474 : else
9475 0 : appendPQExpBufferStr(q,
9476 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9477 : "NULL AS notnull_comment,\n"
9478 : "NULL AS notnull_invalidoid,\n"
9479 : "false AS notnull_noinherit,\n"
9480 : "CASE WHEN a.attislocal THEN true\n"
9481 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9482 : " ELSE false\n"
9483 : "END AS notnull_islocal,\n");
9484 :
9485 259 : if (fout->remoteVersion >= 140000)
9486 259 : appendPQExpBufferStr(q,
9487 : "a.attcompression AS attcompression,\n");
9488 : else
9489 0 : appendPQExpBufferStr(q,
9490 : "'' AS attcompression,\n");
9491 :
9492 259 : if (fout->remoteVersion >= 100000)
9493 259 : appendPQExpBufferStr(q,
9494 : "a.attidentity,\n");
9495 : else
9496 0 : appendPQExpBufferStr(q,
9497 : "'' AS attidentity,\n");
9498 :
9499 259 : if (fout->remoteVersion >= 110000)
9500 259 : appendPQExpBufferStr(q,
9501 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9502 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9503 : else
9504 0 : appendPQExpBufferStr(q,
9505 : "NULL AS attmissingval,\n");
9506 :
9507 259 : if (fout->remoteVersion >= 120000)
9508 259 : appendPQExpBufferStr(q,
9509 : "a.attgenerated\n");
9510 : else
9511 0 : appendPQExpBufferStr(q,
9512 : "'' AS attgenerated\n");
9513 :
9514 : /* need left join to pg_type to not fail on dropped columns ... */
9515 259 : appendPQExpBuffer(q,
9516 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9517 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9518 : "LEFT JOIN pg_catalog.pg_type t "
9519 : "ON (a.atttypid = t.oid)\n",
9520 : tbloids->data);
9521 :
9522 : /*
9523 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9524 : * entries and pg_description to get their comments.
9525 : */
9526 259 : if (fout->remoteVersion >= 180000)
9527 259 : appendPQExpBufferStr(q,
9528 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9529 : "(a.attrelid = co.conrelid\n"
9530 : " AND co.contype = 'n' AND "
9531 : "co.conkey = array[a.attnum])\n"
9532 : " LEFT JOIN pg_catalog.pg_description pt ON "
9533 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9534 :
9535 259 : appendPQExpBufferStr(q,
9536 : "WHERE a.attnum > 0::pg_catalog.int2\n");
9537 :
9538 : /*
9539 : * For binary upgrades from <v12, be sure to pick up
9540 : * pg_largeobject_metadata's oid column.
9541 : */
9542 259 : if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9543 0 : appendPQExpBufferStr(q,
9544 : "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9545 : CppAsString2(LargeObjectMetadataRelationId) ")\n");
9546 :
9547 259 : appendPQExpBufferStr(q,
9548 : "ORDER BY a.attrelid, a.attnum");
9549 :
9550 259 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9551 :
9552 259 : ntups = PQntuples(res);
9553 :
9554 259 : i_attrelid = PQfnumber(res, "attrelid");
9555 259 : i_attnum = PQfnumber(res, "attnum");
9556 259 : i_attname = PQfnumber(res, "attname");
9557 259 : i_atttypname = PQfnumber(res, "atttypname");
9558 259 : i_attstattarget = PQfnumber(res, "attstattarget");
9559 259 : i_attstorage = PQfnumber(res, "attstorage");
9560 259 : i_typstorage = PQfnumber(res, "typstorage");
9561 259 : i_attidentity = PQfnumber(res, "attidentity");
9562 259 : i_attgenerated = PQfnumber(res, "attgenerated");
9563 259 : i_attisdropped = PQfnumber(res, "attisdropped");
9564 259 : i_attlen = PQfnumber(res, "attlen");
9565 259 : i_attalign = PQfnumber(res, "attalign");
9566 259 : i_attislocal = PQfnumber(res, "attislocal");
9567 259 : i_notnull_name = PQfnumber(res, "notnull_name");
9568 259 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9569 259 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9570 259 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9571 259 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9572 259 : i_attoptions = PQfnumber(res, "attoptions");
9573 259 : i_attcollation = PQfnumber(res, "attcollation");
9574 259 : i_attcompression = PQfnumber(res, "attcompression");
9575 259 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9576 259 : i_attmissingval = PQfnumber(res, "attmissingval");
9577 259 : i_atthasdef = PQfnumber(res, "atthasdef");
9578 :
9579 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9580 259 : resetPQExpBuffer(tbloids);
9581 259 : appendPQExpBufferChar(tbloids, '{');
9582 :
9583 : /*
9584 : * Outer loop iterates once per table, not once per row. Incrementing of
9585 : * r is handled by the inner loop.
9586 : */
9587 259 : curtblindx = -1;
9588 7361 : for (int r = 0; r < ntups;)
9589 : {
9590 7102 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9591 7102 : TableInfo *tbinfo = NULL;
9592 : int numatts;
9593 : bool hasdefaults;
9594 :
9595 : /* Count rows for this table */
9596 26412 : for (numatts = 1; numatts < ntups - r; numatts++)
9597 26216 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9598 6906 : break;
9599 :
9600 : /*
9601 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9602 : * order.
9603 : */
9604 49784 : while (++curtblindx < numTables)
9605 : {
9606 49784 : tbinfo = &tblinfo[curtblindx];
9607 49784 : if (tbinfo->dobj.catId.oid == attrelid)
9608 7102 : break;
9609 : }
9610 7102 : if (curtblindx >= numTables)
9611 0 : pg_fatal("unrecognized table OID %u", attrelid);
9612 : /* cross-check that we only got requested tables */
9613 7102 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9614 7102 : (!tbinfo->interesting &&
9615 80 : !(fout->dopt->binary_upgrade &&
9616 80 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9617 40 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9618 0 : pg_fatal("unexpected column data for table \"%s\"",
9619 : tbinfo->dobj.name);
9620 :
9621 : /* Save data for this table */
9622 7102 : tbinfo->numatts = numatts;
9623 7102 : tbinfo->attnames = pg_malloc_array(char *, numatts);
9624 7102 : tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9625 7102 : tbinfo->attstattarget = pg_malloc_array(int, numatts);
9626 7102 : tbinfo->attstorage = pg_malloc_array(char, numatts);
9627 7102 : tbinfo->typstorage = pg_malloc_array(char, numatts);
9628 7102 : tbinfo->attidentity = pg_malloc_array(char, numatts);
9629 7102 : tbinfo->attgenerated = pg_malloc_array(char, numatts);
9630 7102 : tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9631 7102 : tbinfo->attlen = pg_malloc_array(int, numatts);
9632 7102 : tbinfo->attalign = pg_malloc_array(char, numatts);
9633 7102 : tbinfo->attislocal = pg_malloc_array(bool, numatts);
9634 7102 : tbinfo->attoptions = pg_malloc_array(char *, numatts);
9635 7102 : tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9636 7102 : tbinfo->attcompression = pg_malloc_array(char, numatts);
9637 7102 : tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9638 7102 : tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9639 7102 : tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9640 7102 : tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9641 7102 : tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9642 7102 : tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9643 7102 : tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9644 7102 : tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9645 7102 : hasdefaults = false;
9646 :
9647 33514 : for (int j = 0; j < numatts; j++, r++)
9648 : {
9649 26412 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9650 0 : !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9651 0 : tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9652 0 : pg_fatal("invalid column numbering in table \"%s\"",
9653 : tbinfo->dobj.name);
9654 26412 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9655 26412 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9656 26412 : if (PQgetisnull(res, r, i_attstattarget))
9657 26372 : tbinfo->attstattarget[j] = -1;
9658 : else
9659 40 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9660 26412 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9661 26412 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9662 26412 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9663 26412 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9664 26412 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9665 26412 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9666 26412 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9667 26412 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9668 26412 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9669 :
9670 : /* Handle not-null constraint name and flags */
9671 26412 : determineNotNullFlags(fout, res, r,
9672 : tbinfo, j,
9673 : i_notnull_name,
9674 : i_notnull_comment,
9675 : i_notnull_invalidoid,
9676 : i_notnull_noinherit,
9677 : i_notnull_islocal,
9678 : &invalidnotnulloids);
9679 :
9680 26412 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9681 26412 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9682 26412 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9683 26412 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9684 26412 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9685 26412 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9686 26412 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9687 26412 : tbinfo->attrdefs[j] = NULL; /* fix below */
9688 26412 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9689 1323 : hasdefaults = true;
9690 : }
9691 :
9692 7102 : if (hasdefaults)
9693 : {
9694 : /* Collect OIDs of interesting tables that have defaults */
9695 987 : if (tbloids->len > 1) /* do we have more than the '{'? */
9696 919 : appendPQExpBufferChar(tbloids, ',');
9697 987 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9698 : }
9699 : }
9700 :
9701 : /* If invalidnotnulloids has any data, finalize it */
9702 259 : if (invalidnotnulloids != NULL)
9703 43 : appendPQExpBufferChar(invalidnotnulloids, '}');
9704 :
9705 259 : PQclear(res);
9706 :
9707 : /*
9708 : * Now get info about column defaults. This is skipped for a data-only
9709 : * dump, as it is only needed for table schemas.
9710 : */
9711 259 : if (dopt->dumpSchema && tbloids->len > 1)
9712 : {
9713 : AttrDefInfo *attrdefs;
9714 : int numDefaults;
9715 60 : TableInfo *tbinfo = NULL;
9716 :
9717 60 : pg_log_info("finding table default expressions");
9718 :
9719 60 : appendPQExpBufferChar(tbloids, '}');
9720 :
9721 60 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9722 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9723 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9724 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9725 : "ORDER BY a.adrelid, a.adnum",
9726 : tbloids->data);
9727 :
9728 60 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9729 :
9730 60 : numDefaults = PQntuples(res);
9731 60 : attrdefs = pg_malloc_array(AttrDefInfo, numDefaults);
9732 :
9733 60 : curtblindx = -1;
9734 1285 : for (int j = 0; j < numDefaults; j++)
9735 : {
9736 1225 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9737 1225 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9738 1225 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9739 1225 : int adnum = atoi(PQgetvalue(res, j, 3));
9740 1225 : char *adsrc = PQgetvalue(res, j, 4);
9741 :
9742 : /*
9743 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9744 : * OID order.
9745 : */
9746 1225 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9747 : {
9748 20329 : while (++curtblindx < numTables)
9749 : {
9750 20329 : tbinfo = &tblinfo[curtblindx];
9751 20329 : if (tbinfo->dobj.catId.oid == adrelid)
9752 919 : break;
9753 : }
9754 919 : if (curtblindx >= numTables)
9755 0 : pg_fatal("unrecognized table OID %u", adrelid);
9756 : }
9757 :
9758 1225 : if (adnum <= 0 || adnum > tbinfo->numatts)
9759 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9760 : adnum, tbinfo->dobj.name);
9761 :
9762 : /*
9763 : * dropped columns shouldn't have defaults, but just in case,
9764 : * ignore 'em
9765 : */
9766 1225 : if (tbinfo->attisdropped[adnum - 1])
9767 0 : continue;
9768 :
9769 1225 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9770 1225 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9771 1225 : attrdefs[j].dobj.catId.oid = adoid;
9772 1225 : AssignDumpId(&attrdefs[j].dobj);
9773 1225 : attrdefs[j].adtable = tbinfo;
9774 1225 : attrdefs[j].adnum = adnum;
9775 1225 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9776 :
9777 1225 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9778 1225 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9779 :
9780 1225 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9781 :
9782 : /*
9783 : * Figure out whether the default/generation expression should be
9784 : * dumped as part of the main CREATE TABLE (or similar) command or
9785 : * as a separate ALTER TABLE (or similar) command. The preference
9786 : * is to put it into the CREATE command, but in some cases that's
9787 : * not possible.
9788 : */
9789 1225 : if (tbinfo->attgenerated[adnum - 1])
9790 : {
9791 : /*
9792 : * Column generation expressions cannot be dumped separately,
9793 : * because there is no syntax for it. By setting separate to
9794 : * false here we prevent the "default" from being processed as
9795 : * its own dumpable object. Later, flagInhAttrs() will mark
9796 : * it as not to be dumped at all, if possible (that is, if it
9797 : * can be inherited from a parent).
9798 : */
9799 696 : attrdefs[j].separate = false;
9800 : }
9801 529 : else if (tbinfo->relkind == RELKIND_VIEW)
9802 : {
9803 : /*
9804 : * Defaults on a VIEW must always be dumped as separate ALTER
9805 : * TABLE commands.
9806 : */
9807 32 : attrdefs[j].separate = true;
9808 : }
9809 497 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9810 : {
9811 : /* column will be suppressed, print default separately */
9812 4 : attrdefs[j].separate = true;
9813 : }
9814 : else
9815 : {
9816 493 : attrdefs[j].separate = false;
9817 : }
9818 :
9819 1225 : if (!attrdefs[j].separate)
9820 : {
9821 : /*
9822 : * Mark the default as needing to appear before the table, so
9823 : * that any dependencies it has must be emitted before the
9824 : * CREATE TABLE. If this is not possible, we'll change to
9825 : * "separate" mode while sorting dependencies.
9826 : */
9827 1189 : addObjectDependency(&tbinfo->dobj,
9828 1189 : attrdefs[j].dobj.dumpId);
9829 : }
9830 :
9831 1225 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9832 : }
9833 :
9834 60 : PQclear(res);
9835 : }
9836 :
9837 : /*
9838 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9839 : * data-only dump, as it is only needed for table schemas.
9840 : */
9841 259 : if (dopt->dumpSchema && invalidnotnulloids)
9842 : {
9843 : ConstraintInfo *constrs;
9844 : int numConstrs;
9845 : int i_tableoid;
9846 : int i_oid;
9847 : int i_conrelid;
9848 : int i_conname;
9849 : int i_consrc;
9850 : int i_conislocal;
9851 :
9852 37 : pg_log_info("finding invalid not-null constraints");
9853 :
9854 37 : resetPQExpBuffer(q);
9855 37 : appendPQExpBuffer(q,
9856 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9857 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9858 : "conislocal, convalidated "
9859 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9860 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9861 : "ORDER BY c.conrelid, c.conname",
9862 37 : invalidnotnulloids->data);
9863 :
9864 37 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9865 :
9866 37 : numConstrs = PQntuples(res);
9867 37 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9868 :
9869 37 : i_tableoid = PQfnumber(res, "tableoid");
9870 37 : i_oid = PQfnumber(res, "oid");
9871 37 : i_conrelid = PQfnumber(res, "conrelid");
9872 37 : i_conname = PQfnumber(res, "conname");
9873 37 : i_consrc = PQfnumber(res, "consrc");
9874 37 : i_conislocal = PQfnumber(res, "conislocal");
9875 :
9876 : /* As above, this loop iterates once per table, not once per row */
9877 37 : curtblindx = -1;
9878 104 : for (int j = 0; j < numConstrs;)
9879 : {
9880 67 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9881 67 : TableInfo *tbinfo = NULL;
9882 : int numcons;
9883 :
9884 : /* Count rows for this table */
9885 67 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9886 30 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9887 30 : break;
9888 :
9889 : /*
9890 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9891 : * OID order.
9892 : */
9893 13567 : while (++curtblindx < numTables)
9894 : {
9895 13567 : tbinfo = &tblinfo[curtblindx];
9896 13567 : if (tbinfo->dobj.catId.oid == conrelid)
9897 67 : break;
9898 : }
9899 67 : if (curtblindx >= numTables)
9900 0 : pg_fatal("unrecognized table OID %u", conrelid);
9901 :
9902 134 : for (int c = 0; c < numcons; c++, j++)
9903 : {
9904 67 : constrs[j].dobj.objType = DO_CONSTRAINT;
9905 67 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9906 67 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9907 67 : AssignDumpId(&constrs[j].dobj);
9908 67 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9909 67 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9910 67 : constrs[j].contable = tbinfo;
9911 67 : constrs[j].condomain = NULL;
9912 67 : constrs[j].contype = 'n';
9913 67 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9914 67 : constrs[j].confrelid = InvalidOid;
9915 67 : constrs[j].conindex = 0;
9916 67 : constrs[j].condeferrable = false;
9917 67 : constrs[j].condeferred = false;
9918 67 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9919 :
9920 : /*
9921 : * All invalid not-null constraints must be dumped separately,
9922 : * because CREATE TABLE would not create them as invalid, and
9923 : * also because they must be created after potentially
9924 : * violating data has been loaded.
9925 : */
9926 67 : constrs[j].separate = true;
9927 :
9928 67 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9929 : }
9930 : }
9931 37 : PQclear(res);
9932 : }
9933 :
9934 : /*
9935 : * Get info about table CHECK constraints. This is skipped for a
9936 : * data-only dump, as it is only needed for table schemas.
9937 : */
9938 259 : if (dopt->dumpSchema && checkoids->len > 2)
9939 : {
9940 : ConstraintInfo *constrs;
9941 : int numConstrs;
9942 : int i_tableoid;
9943 : int i_oid;
9944 : int i_conrelid;
9945 : int i_conname;
9946 : int i_consrc;
9947 : int i_conislocal;
9948 : int i_convalidated;
9949 :
9950 61 : pg_log_info("finding table check constraints");
9951 :
9952 61 : resetPQExpBuffer(q);
9953 61 : appendPQExpBuffer(q,
9954 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9955 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9956 : "conislocal, convalidated "
9957 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9958 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9959 : "WHERE contype = 'c' "
9960 : "ORDER BY c.conrelid, c.conname",
9961 : checkoids->data);
9962 :
9963 61 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9964 :
9965 61 : numConstrs = PQntuples(res);
9966 61 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9967 :
9968 61 : i_tableoid = PQfnumber(res, "tableoid");
9969 61 : i_oid = PQfnumber(res, "oid");
9970 61 : i_conrelid = PQfnumber(res, "conrelid");
9971 61 : i_conname = PQfnumber(res, "conname");
9972 61 : i_consrc = PQfnumber(res, "consrc");
9973 61 : i_conislocal = PQfnumber(res, "conislocal");
9974 61 : i_convalidated = PQfnumber(res, "convalidated");
9975 :
9976 : /* As above, this loop iterates once per table, not once per row */
9977 61 : curtblindx = -1;
9978 538 : for (int j = 0; j < numConstrs;)
9979 : {
9980 477 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9981 477 : TableInfo *tbinfo = NULL;
9982 : int numcons;
9983 :
9984 : /* Count rows for this table */
9985 612 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9986 551 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9987 416 : break;
9988 :
9989 : /*
9990 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9991 : * OID order.
9992 : */
9993 19603 : while (++curtblindx < numTables)
9994 : {
9995 19603 : tbinfo = &tblinfo[curtblindx];
9996 19603 : if (tbinfo->dobj.catId.oid == conrelid)
9997 477 : break;
9998 : }
9999 477 : if (curtblindx >= numTables)
10000 0 : pg_fatal("unrecognized table OID %u", conrelid);
10001 :
10002 477 : if (numcons != tbinfo->ncheck)
10003 : {
10004 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
10005 : "expected %d check constraints on table \"%s\" but found %d",
10006 : tbinfo->ncheck),
10007 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
10008 0 : pg_log_error_hint("The system catalogs might be corrupted.");
10009 0 : exit_nicely(1);
10010 : }
10011 :
10012 477 : tbinfo->checkexprs = constrs + j;
10013 :
10014 1089 : for (int c = 0; c < numcons; c++, j++)
10015 : {
10016 612 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
10017 :
10018 612 : constrs[j].dobj.objType = DO_CONSTRAINT;
10019 612 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
10020 612 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
10021 612 : AssignDumpId(&constrs[j].dobj);
10022 612 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
10023 612 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
10024 612 : constrs[j].contable = tbinfo;
10025 612 : constrs[j].condomain = NULL;
10026 612 : constrs[j].contype = 'c';
10027 612 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
10028 612 : constrs[j].confrelid = InvalidOid;
10029 612 : constrs[j].conindex = 0;
10030 612 : constrs[j].condeferrable = false;
10031 612 : constrs[j].condeferred = false;
10032 612 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
10033 :
10034 : /*
10035 : * An unvalidated constraint needs to be dumped separately, so
10036 : * that potentially-violating existing data is loaded before
10037 : * the constraint.
10038 : */
10039 612 : constrs[j].separate = !validated;
10040 :
10041 612 : constrs[j].dobj.dump = tbinfo->dobj.dump;
10042 :
10043 : /*
10044 : * Mark the constraint as needing to appear before the table
10045 : * --- this is so that any other dependencies of the
10046 : * constraint will be emitted before we try to create the
10047 : * table. If the constraint is to be dumped separately, it
10048 : * will be dumped after data is loaded anyway, so don't do it.
10049 : * (There's an automatic dependency in the opposite direction
10050 : * anyway, so don't need to add one manually here.)
10051 : */
10052 612 : if (!constrs[j].separate)
10053 547 : addObjectDependency(&tbinfo->dobj,
10054 547 : constrs[j].dobj.dumpId);
10055 :
10056 : /*
10057 : * We will detect later whether the constraint must be split
10058 : * out from the table definition.
10059 : */
10060 : }
10061 : }
10062 :
10063 61 : PQclear(res);
10064 : }
10065 :
10066 259 : destroyPQExpBuffer(q);
10067 259 : destroyPQExpBuffer(tbloids);
10068 259 : destroyPQExpBuffer(checkoids);
10069 259 : }
10070 :
10071 : /*
10072 : * Based on the getTableAttrs query's row corresponding to one column, set
10073 : * the name and flags to handle a not-null constraint for that column in
10074 : * the tbinfo struct.
10075 : *
10076 : * Result row 'r' is for tbinfo's attribute 'j'.
10077 : *
10078 : * There are four possibilities:
10079 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
10080 : * (the constraint name) remains NULL.
10081 : * 2) The column has a constraint with no name (this is the case when
10082 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
10083 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
10084 : * 3) The column has an invalid not-null constraint. This must be treated
10085 : * as a separate object (because it must be created after the table data
10086 : * is loaded). So we add its OID to invalidnotnulloids for processing
10087 : * elsewhere and do nothing further with it here. We distinguish this
10088 : * case because the "notnull_invalidoid" column has been set to a non-NULL
10089 : * value, which is the constraint OID. Valid constraints have a null OID.
10090 : * 4) The column has a constraint with a known name; in that case
10091 : * notnull_constrs carries that name and dumpTableSchema will print
10092 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
10093 : * (table_column_not_null) and there's no comment on the constraint,
10094 : * there's no need to print that name in the dump, so notnull_constrs
10095 : * is set to the empty string and it behaves as case 2.
10096 : *
10097 : * In a child table that inherits from a parent already containing NOT NULL
10098 : * constraints and the columns in the child don't have their own NOT NULL
10099 : * declarations, we suppress printing constraints in the child: the
10100 : * constraints are acquired at the point where the child is attached to the
10101 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
10102 : * set not here but in flagInhAttrs. That flag is also used when the
10103 : * constraint was validated in a child but all its parent have it as NOT
10104 : * VALID.
10105 : *
10106 : * Any of these constraints might have the NO INHERIT bit. If so we set
10107 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10108 : *
10109 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
10110 : * to do the right thing in all but the trivial case. However, the downside
10111 : * of getting it wrong is simply that the name is printed rather than
10112 : * suppressed, so it's not a big deal.
10113 : *
10114 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10115 : * constraints are found, it is initialized and filled with the array of
10116 : * OIDs of such constraints, for later processing.
10117 : */
10118 : static void
10119 26412 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
10120 : TableInfo *tbinfo, int j,
10121 : int i_notnull_name,
10122 : int i_notnull_comment,
10123 : int i_notnull_invalidoid,
10124 : int i_notnull_noinherit,
10125 : int i_notnull_islocal,
10126 : PQExpBuffer *invalidnotnulloids)
10127 : {
10128 26412 : DumpOptions *dopt = fout->dopt;
10129 :
10130 : /*
10131 : * If this not-null constraint is not valid, list its OID in
10132 : * invalidnotnulloids and do nothing further. It'll be processed
10133 : * elsewhere later.
10134 : *
10135 : * Because invalid not-null constraints are rare, we don't want to malloc
10136 : * invalidnotnulloids until we're sure we're going it need it, which
10137 : * happens here.
10138 : */
10139 26412 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10140 : {
10141 73 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10142 :
10143 73 : if (*invalidnotnulloids == NULL)
10144 : {
10145 43 : *invalidnotnulloids = createPQExpBuffer();
10146 43 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10147 43 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10148 : }
10149 : else
10150 30 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10151 :
10152 : /*
10153 : * Track when a parent constraint is invalid for the cases where a
10154 : * child constraint has been validated independenly.
10155 : */
10156 73 : tbinfo->notnull_invalid[j] = true;
10157 :
10158 : /* nothing else to do */
10159 73 : tbinfo->notnull_constrs[j] = NULL;
10160 73 : return;
10161 : }
10162 :
10163 : /*
10164 : * notnull_noinh is straight from the query result. notnull_islocal also,
10165 : * though flagInhAttrs may change that one later.
10166 : */
10167 26339 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10168 26339 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10169 26339 : tbinfo->notnull_invalid[j] = false;
10170 :
10171 : /*
10172 : * Determine a constraint name to use. If the column is not marked not-
10173 : * null, we set NULL which cues ... to do nothing. An empty string says
10174 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10175 : * use.
10176 : */
10177 26339 : if (fout->remoteVersion < 180000)
10178 : {
10179 : /*
10180 : * < 18 doesn't have not-null names, so an unnamed constraint is
10181 : * sufficient.
10182 : */
10183 0 : if (PQgetisnull(res, r, i_notnull_name))
10184 0 : tbinfo->notnull_constrs[j] = NULL;
10185 : else
10186 0 : tbinfo->notnull_constrs[j] = "";
10187 : }
10188 : else
10189 : {
10190 26339 : if (PQgetisnull(res, r, i_notnull_name))
10191 23441 : tbinfo->notnull_constrs[j] = NULL;
10192 : else
10193 : {
10194 : /*
10195 : * In binary upgrade of inheritance child tables, must have a
10196 : * constraint name that we can UPDATE later; same if there's a
10197 : * comment on the constraint.
10198 : */
10199 2898 : if ((dopt->binary_upgrade &&
10200 373 : !tbinfo->ispartition &&
10201 3162 : !tbinfo->notnull_islocal[j]) ||
10202 2877 : !PQgetisnull(res, r, i_notnull_comment))
10203 : {
10204 67 : tbinfo->notnull_constrs[j] =
10205 67 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10206 : }
10207 : else
10208 : {
10209 : char *default_name;
10210 :
10211 : /* XXX should match ChooseConstraintName better */
10212 2831 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10213 2831 : tbinfo->attnames[j]);
10214 2831 : if (strcmp(default_name,
10215 2831 : PQgetvalue(res, r, i_notnull_name)) == 0)
10216 1898 : tbinfo->notnull_constrs[j] = "";
10217 : else
10218 : {
10219 933 : tbinfo->notnull_constrs[j] =
10220 933 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10221 : }
10222 2831 : free(default_name);
10223 : }
10224 : }
10225 : }
10226 : }
10227 :
10228 : /*
10229 : * Test whether a column should be printed as part of table's CREATE TABLE.
10230 : * Column number is zero-based.
10231 : *
10232 : * Normally this is always true, but it's false for dropped columns, as well
10233 : * as those that were inherited without any local definition. (If we print
10234 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10235 : * For partitions, it's always true, because we want the partitions to be
10236 : * created independently and ATTACH PARTITION used afterwards.
10237 : *
10238 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10239 : * attisdropped state later, so as to keep control of the physical column
10240 : * order.
10241 : *
10242 : * This function exists because there are scattered nonobvious places that
10243 : * must be kept in sync with this decision.
10244 : */
10245 : bool
10246 42898 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10247 : {
10248 42898 : if (dopt->binary_upgrade)
10249 6600 : return true;
10250 36298 : if (tbinfo->attisdropped[colno])
10251 734 : return false;
10252 35564 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10253 : }
10254 :
10255 :
10256 : /*
10257 : * getTSParsers:
10258 : * get information about all text search parsers in the system catalogs
10259 : */
10260 : void
10261 259 : getTSParsers(Archive *fout)
10262 : {
10263 : PGresult *res;
10264 : int ntups;
10265 : int i;
10266 : PQExpBuffer query;
10267 : TSParserInfo *prsinfo;
10268 : int i_tableoid;
10269 : int i_oid;
10270 : int i_prsname;
10271 : int i_prsnamespace;
10272 : int i_prsstart;
10273 : int i_prstoken;
10274 : int i_prsend;
10275 : int i_prsheadline;
10276 : int i_prslextype;
10277 :
10278 259 : query = createPQExpBuffer();
10279 :
10280 : /*
10281 : * find all text search objects, including builtin ones; we filter out
10282 : * system-defined objects at dump-out time.
10283 : */
10284 :
10285 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10286 : "prsstart::oid, prstoken::oid, "
10287 : "prsend::oid, prsheadline::oid, prslextype::oid "
10288 : "FROM pg_ts_parser");
10289 :
10290 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10291 :
10292 259 : ntups = PQntuples(res);
10293 :
10294 259 : prsinfo = pg_malloc_array(TSParserInfo, ntups);
10295 :
10296 259 : i_tableoid = PQfnumber(res, "tableoid");
10297 259 : i_oid = PQfnumber(res, "oid");
10298 259 : i_prsname = PQfnumber(res, "prsname");
10299 259 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10300 259 : i_prsstart = PQfnumber(res, "prsstart");
10301 259 : i_prstoken = PQfnumber(res, "prstoken");
10302 259 : i_prsend = PQfnumber(res, "prsend");
10303 259 : i_prsheadline = PQfnumber(res, "prsheadline");
10304 259 : i_prslextype = PQfnumber(res, "prslextype");
10305 :
10306 563 : for (i = 0; i < ntups; i++)
10307 : {
10308 304 : prsinfo[i].dobj.objType = DO_TSPARSER;
10309 304 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10310 304 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10311 304 : AssignDumpId(&prsinfo[i].dobj);
10312 304 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10313 608 : prsinfo[i].dobj.namespace =
10314 304 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10315 304 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10316 304 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10317 304 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10318 304 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10319 304 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10320 :
10321 : /* Decide whether we want to dump it */
10322 304 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10323 : }
10324 :
10325 259 : PQclear(res);
10326 :
10327 259 : destroyPQExpBuffer(query);
10328 259 : }
10329 :
10330 : /*
10331 : * getTSDictionaries:
10332 : * get information about all text search dictionaries in the system catalogs
10333 : */
10334 : void
10335 259 : getTSDictionaries(Archive *fout)
10336 : {
10337 : PGresult *res;
10338 : int ntups;
10339 : int i;
10340 : PQExpBuffer query;
10341 : TSDictInfo *dictinfo;
10342 : int i_tableoid;
10343 : int i_oid;
10344 : int i_dictname;
10345 : int i_dictnamespace;
10346 : int i_dictowner;
10347 : int i_dicttemplate;
10348 : int i_dictinitoption;
10349 :
10350 259 : query = createPQExpBuffer();
10351 :
10352 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10353 : "dictnamespace, dictowner, "
10354 : "dicttemplate, dictinitoption "
10355 : "FROM pg_ts_dict");
10356 :
10357 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10358 :
10359 259 : ntups = PQntuples(res);
10360 :
10361 259 : dictinfo = pg_malloc_array(TSDictInfo, ntups);
10362 :
10363 259 : i_tableoid = PQfnumber(res, "tableoid");
10364 259 : i_oid = PQfnumber(res, "oid");
10365 259 : i_dictname = PQfnumber(res, "dictname");
10366 259 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10367 259 : i_dictowner = PQfnumber(res, "dictowner");
10368 259 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10369 259 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10370 :
10371 8655 : for (i = 0; i < ntups; i++)
10372 : {
10373 8396 : dictinfo[i].dobj.objType = DO_TSDICT;
10374 8396 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10375 8396 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10376 8396 : AssignDumpId(&dictinfo[i].dobj);
10377 8396 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10378 16792 : dictinfo[i].dobj.namespace =
10379 8396 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10380 8396 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10381 8396 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10382 8396 : if (PQgetisnull(res, i, i_dictinitoption))
10383 304 : dictinfo[i].dictinitoption = NULL;
10384 : else
10385 8092 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10386 :
10387 : /* Decide whether we want to dump it */
10388 8396 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10389 : }
10390 :
10391 259 : PQclear(res);
10392 :
10393 259 : destroyPQExpBuffer(query);
10394 259 : }
10395 :
10396 : /*
10397 : * getTSTemplates:
10398 : * get information about all text search templates in the system catalogs
10399 : */
10400 : void
10401 259 : getTSTemplates(Archive *fout)
10402 : {
10403 : PGresult *res;
10404 : int ntups;
10405 : int i;
10406 : PQExpBuffer query;
10407 : TSTemplateInfo *tmplinfo;
10408 : int i_tableoid;
10409 : int i_oid;
10410 : int i_tmplname;
10411 : int i_tmplnamespace;
10412 : int i_tmplinit;
10413 : int i_tmpllexize;
10414 :
10415 259 : query = createPQExpBuffer();
10416 :
10417 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10418 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10419 : "FROM pg_ts_template");
10420 :
10421 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10422 :
10423 259 : ntups = PQntuples(res);
10424 :
10425 259 : tmplinfo = pg_malloc_array(TSTemplateInfo, ntups);
10426 :
10427 259 : i_tableoid = PQfnumber(res, "tableoid");
10428 259 : i_oid = PQfnumber(res, "oid");
10429 259 : i_tmplname = PQfnumber(res, "tmplname");
10430 259 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10431 259 : i_tmplinit = PQfnumber(res, "tmplinit");
10432 259 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10433 :
10434 1599 : for (i = 0; i < ntups; i++)
10435 : {
10436 1340 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10437 1340 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10438 1340 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10439 1340 : AssignDumpId(&tmplinfo[i].dobj);
10440 1340 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10441 2680 : tmplinfo[i].dobj.namespace =
10442 1340 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10443 1340 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10444 1340 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10445 :
10446 : /* Decide whether we want to dump it */
10447 1340 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10448 : }
10449 :
10450 259 : PQclear(res);
10451 :
10452 259 : destroyPQExpBuffer(query);
10453 259 : }
10454 :
10455 : /*
10456 : * getTSConfigurations:
10457 : * get information about all text search configurations
10458 : */
10459 : void
10460 259 : getTSConfigurations(Archive *fout)
10461 : {
10462 : PGresult *res;
10463 : int ntups;
10464 : int i;
10465 : PQExpBuffer query;
10466 : TSConfigInfo *cfginfo;
10467 : int i_tableoid;
10468 : int i_oid;
10469 : int i_cfgname;
10470 : int i_cfgnamespace;
10471 : int i_cfgowner;
10472 : int i_cfgparser;
10473 :
10474 259 : query = createPQExpBuffer();
10475 :
10476 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10477 : "cfgnamespace, cfgowner, cfgparser "
10478 : "FROM pg_ts_config");
10479 :
10480 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10481 :
10482 259 : ntups = PQntuples(res);
10483 :
10484 259 : cfginfo = pg_malloc_array(TSConfigInfo, ntups);
10485 :
10486 259 : i_tableoid = PQfnumber(res, "tableoid");
10487 259 : i_oid = PQfnumber(res, "oid");
10488 259 : i_cfgname = PQfnumber(res, "cfgname");
10489 259 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10490 259 : i_cfgowner = PQfnumber(res, "cfgowner");
10491 259 : i_cfgparser = PQfnumber(res, "cfgparser");
10492 :
10493 8620 : for (i = 0; i < ntups; i++)
10494 : {
10495 8361 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10496 8361 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10497 8361 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10498 8361 : AssignDumpId(&cfginfo[i].dobj);
10499 8361 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10500 16722 : cfginfo[i].dobj.namespace =
10501 8361 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10502 8361 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10503 8361 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10504 :
10505 : /* Decide whether we want to dump it */
10506 8361 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10507 : }
10508 :
10509 259 : PQclear(res);
10510 :
10511 259 : destroyPQExpBuffer(query);
10512 259 : }
10513 :
10514 : /*
10515 : * getForeignDataWrappers:
10516 : * get information about all foreign-data wrappers in the system catalogs
10517 : */
10518 : void
10519 259 : getForeignDataWrappers(Archive *fout)
10520 : {
10521 : PGresult *res;
10522 : int ntups;
10523 : int i;
10524 : PQExpBuffer query;
10525 : FdwInfo *fdwinfo;
10526 : int i_tableoid;
10527 : int i_oid;
10528 : int i_fdwname;
10529 : int i_fdwowner;
10530 : int i_fdwhandler;
10531 : int i_fdwvalidator;
10532 : int i_fdwconnection;
10533 : int i_fdwacl;
10534 : int i_acldefault;
10535 : int i_fdwoptions;
10536 :
10537 259 : query = createPQExpBuffer();
10538 :
10539 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10540 : "fdwowner, "
10541 : "fdwhandler::pg_catalog.regproc, "
10542 : "fdwvalidator::pg_catalog.regproc, ");
10543 :
10544 259 : if (fout->remoteVersion >= 190000)
10545 259 : appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10546 : else
10547 0 : appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10548 :
10549 259 : appendPQExpBufferStr(query,
10550 : "fdwacl, "
10551 : "acldefault('F', fdwowner) AS acldefault, "
10552 : "array_to_string(ARRAY("
10553 : "SELECT quote_ident(option_name) || ' ' || "
10554 : "quote_literal(option_value) "
10555 : "FROM pg_options_to_table(fdwoptions) "
10556 : "ORDER BY option_name"
10557 : "), E',\n ') AS fdwoptions "
10558 : "FROM pg_foreign_data_wrapper");
10559 :
10560 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10561 :
10562 259 : ntups = PQntuples(res);
10563 :
10564 259 : fdwinfo = pg_malloc_array(FdwInfo, ntups);
10565 :
10566 259 : i_tableoid = PQfnumber(res, "tableoid");
10567 259 : i_oid = PQfnumber(res, "oid");
10568 259 : i_fdwname = PQfnumber(res, "fdwname");
10569 259 : i_fdwowner = PQfnumber(res, "fdwowner");
10570 259 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10571 259 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10572 259 : i_fdwconnection = PQfnumber(res, "fdwconnection");
10573 259 : i_fdwacl = PQfnumber(res, "fdwacl");
10574 259 : i_acldefault = PQfnumber(res, "acldefault");
10575 259 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10576 :
10577 330 : for (i = 0; i < ntups; i++)
10578 : {
10579 71 : fdwinfo[i].dobj.objType = DO_FDW;
10580 71 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10581 71 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10582 71 : AssignDumpId(&fdwinfo[i].dobj);
10583 71 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10584 71 : fdwinfo[i].dobj.namespace = NULL;
10585 71 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10586 71 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10587 71 : fdwinfo[i].dacl.privtype = 0;
10588 71 : fdwinfo[i].dacl.initprivs = NULL;
10589 71 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10590 71 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10591 71 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10592 71 : fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10593 71 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10594 :
10595 : /* Decide whether we want to dump it */
10596 71 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10597 :
10598 : /* Mark whether FDW has an ACL */
10599 71 : if (!PQgetisnull(res, i, i_fdwacl))
10600 45 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10601 : }
10602 :
10603 259 : PQclear(res);
10604 :
10605 259 : destroyPQExpBuffer(query);
10606 259 : }
10607 :
10608 : /*
10609 : * getForeignServers:
10610 : * get information about all foreign servers in the system catalogs
10611 : */
10612 : void
10613 259 : getForeignServers(Archive *fout)
10614 : {
10615 : PGresult *res;
10616 : int ntups;
10617 : int i;
10618 : PQExpBuffer query;
10619 : ForeignServerInfo *srvinfo;
10620 : int i_tableoid;
10621 : int i_oid;
10622 : int i_srvname;
10623 : int i_srvowner;
10624 : int i_srvfdw;
10625 : int i_srvtype;
10626 : int i_srvversion;
10627 : int i_srvacl;
10628 : int i_acldefault;
10629 : int i_srvoptions;
10630 :
10631 259 : query = createPQExpBuffer();
10632 :
10633 259 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10634 : "srvowner, "
10635 : "srvfdw, srvtype, srvversion, srvacl, "
10636 : "acldefault('S', srvowner) AS acldefault, "
10637 : "array_to_string(ARRAY("
10638 : "SELECT quote_ident(option_name) || ' ' || "
10639 : "quote_literal(option_value) "
10640 : "FROM pg_options_to_table(srvoptions) "
10641 : "ORDER BY option_name"
10642 : "), E',\n ') AS srvoptions "
10643 : "FROM pg_foreign_server");
10644 :
10645 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10646 :
10647 259 : ntups = PQntuples(res);
10648 :
10649 259 : srvinfo = pg_malloc_array(ForeignServerInfo, ntups);
10650 :
10651 259 : i_tableoid = PQfnumber(res, "tableoid");
10652 259 : i_oid = PQfnumber(res, "oid");
10653 259 : i_srvname = PQfnumber(res, "srvname");
10654 259 : i_srvowner = PQfnumber(res, "srvowner");
10655 259 : i_srvfdw = PQfnumber(res, "srvfdw");
10656 259 : i_srvtype = PQfnumber(res, "srvtype");
10657 259 : i_srvversion = PQfnumber(res, "srvversion");
10658 259 : i_srvacl = PQfnumber(res, "srvacl");
10659 259 : i_acldefault = PQfnumber(res, "acldefault");
10660 259 : i_srvoptions = PQfnumber(res, "srvoptions");
10661 :
10662 334 : for (i = 0; i < ntups; i++)
10663 : {
10664 75 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10665 75 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10666 75 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10667 75 : AssignDumpId(&srvinfo[i].dobj);
10668 75 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10669 75 : srvinfo[i].dobj.namespace = NULL;
10670 75 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10671 75 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10672 75 : srvinfo[i].dacl.privtype = 0;
10673 75 : srvinfo[i].dacl.initprivs = NULL;
10674 75 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10675 75 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10676 75 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10677 75 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10678 75 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10679 :
10680 : /* Decide whether we want to dump it */
10681 75 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10682 :
10683 : /* Servers have user mappings */
10684 75 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10685 :
10686 : /* Mark whether server has an ACL */
10687 75 : if (!PQgetisnull(res, i, i_srvacl))
10688 45 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10689 : }
10690 :
10691 259 : PQclear(res);
10692 :
10693 259 : destroyPQExpBuffer(query);
10694 259 : }
10695 :
10696 : /*
10697 : * getDefaultACLs:
10698 : * get information about all default ACL information in the system catalogs
10699 : */
10700 : void
10701 259 : getDefaultACLs(Archive *fout)
10702 : {
10703 259 : DumpOptions *dopt = fout->dopt;
10704 : DefaultACLInfo *daclinfo;
10705 : PQExpBuffer query;
10706 : PGresult *res;
10707 : int i_oid;
10708 : int i_tableoid;
10709 : int i_defaclrole;
10710 : int i_defaclnamespace;
10711 : int i_defaclobjtype;
10712 : int i_defaclacl;
10713 : int i_acldefault;
10714 : int i,
10715 : ntups;
10716 :
10717 259 : query = createPQExpBuffer();
10718 :
10719 : /*
10720 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10721 : * ACL for their object type. We should dump them as deltas from the
10722 : * default ACL, since that will be used as a starting point for
10723 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10724 : * non-global entries can only add privileges not revoke them. We must
10725 : * dump those as-is (i.e., as deltas from an empty ACL).
10726 : *
10727 : * We can use defaclobjtype as the object type for acldefault(), except
10728 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10729 : * 's'.
10730 : */
10731 259 : appendPQExpBufferStr(query,
10732 : "SELECT oid, tableoid, "
10733 : "defaclrole, "
10734 : "defaclnamespace, "
10735 : "defaclobjtype, "
10736 : "defaclacl, "
10737 : "CASE WHEN defaclnamespace = 0 THEN "
10738 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10739 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10740 : "defaclrole) ELSE '{}' END AS acldefault "
10741 : "FROM pg_default_acl");
10742 :
10743 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10744 :
10745 259 : ntups = PQntuples(res);
10746 :
10747 259 : daclinfo = pg_malloc_array(DefaultACLInfo, ntups);
10748 :
10749 259 : i_oid = PQfnumber(res, "oid");
10750 259 : i_tableoid = PQfnumber(res, "tableoid");
10751 259 : i_defaclrole = PQfnumber(res, "defaclrole");
10752 259 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10753 259 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10754 259 : i_defaclacl = PQfnumber(res, "defaclacl");
10755 259 : i_acldefault = PQfnumber(res, "acldefault");
10756 :
10757 453 : for (i = 0; i < ntups; i++)
10758 : {
10759 194 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10760 :
10761 194 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10762 194 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10763 194 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10764 194 : AssignDumpId(&daclinfo[i].dobj);
10765 : /* cheesy ... is it worth coming up with a better object name? */
10766 194 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10767 :
10768 194 : if (nspid != InvalidOid)
10769 90 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10770 : else
10771 104 : daclinfo[i].dobj.namespace = NULL;
10772 :
10773 194 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10774 194 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10775 194 : daclinfo[i].dacl.privtype = 0;
10776 194 : daclinfo[i].dacl.initprivs = NULL;
10777 194 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10778 194 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10779 :
10780 : /* Default ACLs are ACLs, of course */
10781 194 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10782 :
10783 : /* Decide whether we want to dump it */
10784 194 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10785 : }
10786 :
10787 259 : PQclear(res);
10788 :
10789 259 : destroyPQExpBuffer(query);
10790 259 : }
10791 :
10792 : /*
10793 : * getRoleName -- look up the name of a role, given its OID
10794 : *
10795 : * In current usage, we don't expect failures, so error out for a bad OID.
10796 : */
10797 : static const char *
10798 837683 : getRoleName(const char *roleoid_str)
10799 : {
10800 837683 : Oid roleoid = atooid(roleoid_str);
10801 :
10802 : /*
10803 : * Do binary search to find the appropriate item.
10804 : */
10805 837683 : if (nrolenames > 0)
10806 : {
10807 837683 : RoleNameItem *low = &rolenames[0];
10808 837683 : RoleNameItem *high = &rolenames[nrolenames - 1];
10809 :
10810 3350969 : while (low <= high)
10811 : {
10812 3350969 : RoleNameItem *middle = low + (high - low) / 2;
10813 :
10814 3350969 : if (roleoid < middle->roleoid)
10815 2511827 : high = middle - 1;
10816 839142 : else if (roleoid > middle->roleoid)
10817 1459 : low = middle + 1;
10818 : else
10819 837683 : return middle->rolename; /* found a match */
10820 : }
10821 : }
10822 :
10823 0 : pg_fatal("role with OID %u does not exist", roleoid);
10824 : return NULL; /* keep compiler quiet */
10825 : }
10826 :
10827 : /*
10828 : * collectRoleNames --
10829 : *
10830 : * Construct a table of all known roles.
10831 : * The table is sorted by OID for speed in lookup.
10832 : */
10833 : static void
10834 260 : collectRoleNames(Archive *fout)
10835 : {
10836 : PGresult *res;
10837 : const char *query;
10838 : int i;
10839 :
10840 260 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10841 :
10842 260 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10843 :
10844 260 : nrolenames = PQntuples(res);
10845 :
10846 260 : rolenames = pg_malloc_array(RoleNameItem, nrolenames);
10847 :
10848 5772 : for (i = 0; i < nrolenames; i++)
10849 : {
10850 5512 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10851 5512 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10852 : }
10853 :
10854 260 : PQclear(res);
10855 260 : }
10856 :
10857 : /*
10858 : * getAdditionalACLs
10859 : *
10860 : * We have now created all the DumpableObjects, and collected the ACL data
10861 : * that appears in the directly-associated catalog entries. However, there's
10862 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10863 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10864 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10865 : * Also, in versions having the pg_init_privs catalog, read that and load the
10866 : * information into the relevant DumpableObjects.
10867 : */
10868 : static void
10869 257 : getAdditionalACLs(Archive *fout)
10870 : {
10871 257 : PQExpBuffer query = createPQExpBuffer();
10872 : PGresult *res;
10873 : int ntups,
10874 : i;
10875 :
10876 : /* Check for per-column ACLs */
10877 257 : appendPQExpBufferStr(query,
10878 : "SELECT DISTINCT attrelid FROM pg_attribute "
10879 : "WHERE attacl IS NOT NULL");
10880 :
10881 257 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10882 :
10883 257 : ntups = PQntuples(res);
10884 684 : for (i = 0; i < ntups; i++)
10885 : {
10886 427 : Oid relid = atooid(PQgetvalue(res, i, 0));
10887 : TableInfo *tblinfo;
10888 :
10889 427 : tblinfo = findTableByOid(relid);
10890 : /* OK to ignore tables we haven't got a DumpableObject for */
10891 427 : if (tblinfo)
10892 : {
10893 427 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10894 427 : tblinfo->hascolumnACLs = true;
10895 : }
10896 : }
10897 257 : PQclear(res);
10898 :
10899 : /* Fetch initial-privileges data */
10900 257 : if (fout->remoteVersion >= 90600)
10901 : {
10902 257 : printfPQExpBuffer(query,
10903 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10904 : "FROM pg_init_privs");
10905 :
10906 257 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10907 :
10908 257 : ntups = PQntuples(res);
10909 64301 : for (i = 0; i < ntups; i++)
10910 : {
10911 64044 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10912 64044 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10913 64044 : int objsubid = atoi(PQgetvalue(res, i, 2));
10914 64044 : char privtype = *(PQgetvalue(res, i, 3));
10915 64044 : char *initprivs = PQgetvalue(res, i, 4);
10916 : CatalogId objId;
10917 : DumpableObject *dobj;
10918 :
10919 64044 : objId.tableoid = classoid;
10920 64044 : objId.oid = objoid;
10921 64044 : dobj = findObjectByCatalogId(objId);
10922 : /* OK to ignore entries we haven't got a DumpableObject for */
10923 64044 : if (dobj)
10924 : {
10925 : /* Cope with sub-object initprivs */
10926 46355 : if (objsubid != 0)
10927 : {
10928 5421 : if (dobj->objType == DO_TABLE)
10929 : {
10930 : /* For a column initprivs, set the table's ACL flags */
10931 5421 : dobj->components |= DUMP_COMPONENT_ACL;
10932 5421 : ((TableInfo *) dobj)->hascolumnACLs = true;
10933 : }
10934 : else
10935 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10936 : classoid, objoid, objsubid);
10937 5674 : continue;
10938 : }
10939 :
10940 : /*
10941 : * We ignore any pg_init_privs.initprivs entry for the public
10942 : * schema, as explained in getNamespaces().
10943 : */
10944 40934 : if (dobj->objType == DO_NAMESPACE &&
10945 510 : strcmp(dobj->name, "public") == 0)
10946 253 : continue;
10947 :
10948 : /* Else it had better be of a type we think has ACLs */
10949 40681 : if (dobj->objType == DO_NAMESPACE ||
10950 40424 : dobj->objType == DO_TYPE ||
10951 40400 : dobj->objType == DO_FUNC ||
10952 40308 : dobj->objType == DO_AGG ||
10953 40284 : dobj->objType == DO_TABLE ||
10954 0 : dobj->objType == DO_PROCLANG ||
10955 0 : dobj->objType == DO_FDW ||
10956 0 : dobj->objType == DO_FOREIGN_SERVER)
10957 40681 : {
10958 40681 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10959 :
10960 40681 : daobj->dacl.privtype = privtype;
10961 40681 : daobj->dacl.initprivs = pstrdup(initprivs);
10962 : }
10963 : else
10964 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10965 : classoid, objoid, objsubid);
10966 : }
10967 : }
10968 257 : PQclear(res);
10969 : }
10970 :
10971 257 : destroyPQExpBuffer(query);
10972 257 : }
10973 :
10974 : /*
10975 : * dumpCommentExtended --
10976 : *
10977 : * This routine is used to dump any comments associated with the
10978 : * object handed to this routine. The routine takes the object type
10979 : * and object name (ready to print, except for schema decoration), plus
10980 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10981 : * plus catalog ID and subid which are the lookup key for pg_description,
10982 : * plus the dump ID for the object (for setting a dependency).
10983 : * If a matching pg_description entry is found, it is dumped.
10984 : *
10985 : * Note: in some cases, such as comments for triggers and rules, the "type"
10986 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10987 : * but it doesn't seem worth complicating the API for all callers to make
10988 : * it cleaner.
10989 : *
10990 : * Note: although this routine takes a dumpId for dependency purposes,
10991 : * that purpose is just to mark the dependency in the emitted dump file
10992 : * for possible future use by pg_restore. We do NOT use it for determining
10993 : * ordering of the comment in the dump file, because this routine is called
10994 : * after dependency sorting occurs. This routine should be called just after
10995 : * calling ArchiveEntry() for the specified object.
10996 : */
10997 : static void
10998 6674 : dumpCommentExtended(Archive *fout, const char *type,
10999 : const char *name, const char *namespace,
11000 : const char *owner, CatalogId catalogId,
11001 : int subid, DumpId dumpId,
11002 : const char *initdb_comment)
11003 : {
11004 6674 : DumpOptions *dopt = fout->dopt;
11005 : CommentItem *comments;
11006 : int ncomments;
11007 :
11008 : /* do nothing, if --no-comments is supplied */
11009 6674 : if (dopt->no_comments)
11010 0 : return;
11011 :
11012 : /* Comments are schema not data ... except LO comments are data */
11013 6674 : if (strcmp(type, "LARGE OBJECT") != 0)
11014 : {
11015 6616 : if (!dopt->dumpSchema)
11016 0 : return;
11017 : }
11018 : else
11019 : {
11020 : /* We do dump LO comments in binary-upgrade mode */
11021 58 : if (!dopt->dumpData && !dopt->binary_upgrade)
11022 0 : return;
11023 : }
11024 :
11025 : /* Search for comments associated with catalogId, using table */
11026 6674 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
11027 : &comments);
11028 :
11029 : /* Is there one matching the subid? */
11030 6674 : while (ncomments > 0)
11031 : {
11032 6629 : if (comments->objsubid == subid)
11033 6629 : break;
11034 0 : comments++;
11035 0 : ncomments--;
11036 : }
11037 :
11038 6674 : if (initdb_comment != NULL)
11039 : {
11040 : static CommentItem empty_comment = {.descr = ""};
11041 :
11042 : /*
11043 : * initdb creates this object with a comment. Skip dumping the
11044 : * initdb-provided comment, which would complicate matters for
11045 : * non-superuser use of pg_dump. When the DBA has removed initdb's
11046 : * comment, replicate that.
11047 : */
11048 186 : if (ncomments == 0)
11049 : {
11050 4 : comments = &empty_comment;
11051 4 : ncomments = 1;
11052 : }
11053 182 : else if (strcmp(comments->descr, initdb_comment) == 0)
11054 182 : ncomments = 0;
11055 : }
11056 :
11057 : /* If a comment exists, build COMMENT ON statement */
11058 6674 : if (ncomments > 0)
11059 : {
11060 6451 : PQExpBuffer query = createPQExpBuffer();
11061 6451 : PQExpBuffer tag = createPQExpBuffer();
11062 :
11063 6451 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
11064 6451 : if (namespace && *namespace)
11065 6270 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
11066 6451 : appendPQExpBuffer(query, "%s IS ", name);
11067 6451 : appendStringLiteralAH(query, comments->descr, fout);
11068 6451 : appendPQExpBufferStr(query, ";\n");
11069 :
11070 6451 : appendPQExpBuffer(tag, "%s %s", type, name);
11071 :
11072 : /*
11073 : * We mark comments as SECTION_NONE because they really belong in the
11074 : * same section as their parent, whether that is pre-data or
11075 : * post-data.
11076 : */
11077 6451 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11078 6451 : ARCHIVE_OPTS(.tag = tag->data,
11079 : .namespace = namespace,
11080 : .owner = owner,
11081 : .description = "COMMENT",
11082 : .section = SECTION_NONE,
11083 : .createStmt = query->data,
11084 : .deps = &dumpId,
11085 : .nDeps = 1));
11086 :
11087 6451 : destroyPQExpBuffer(query);
11088 6451 : destroyPQExpBuffer(tag);
11089 : }
11090 : }
11091 :
11092 : /*
11093 : * dumpComment --
11094 : *
11095 : * Typical simplification of the above function.
11096 : */
11097 : static inline void
11098 6445 : dumpComment(Archive *fout, const char *type,
11099 : const char *name, const char *namespace,
11100 : const char *owner, CatalogId catalogId,
11101 : int subid, DumpId dumpId)
11102 : {
11103 6445 : dumpCommentExtended(fout, type, name, namespace, owner,
11104 : catalogId, subid, dumpId, NULL);
11105 6445 : }
11106 :
11107 : /*
11108 : * appendNamedArgument --
11109 : *
11110 : * Convenience routine for constructing parameters of the form:
11111 : * 'paraname', 'value'::type
11112 : */
11113 : static void
11114 5646 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
11115 : const char *argtype, const char *argval)
11116 : {
11117 5646 : appendPQExpBufferStr(out, ",\n\t");
11118 :
11119 5646 : appendStringLiteralAH(out, argname, fout);
11120 5646 : appendPQExpBufferStr(out, ", ");
11121 :
11122 5646 : appendStringLiteralAH(out, argval, fout);
11123 5646 : appendPQExpBuffer(out, "::%s", argtype);
11124 5646 : }
11125 :
11126 : /*
11127 : * fetchAttributeStats --
11128 : *
11129 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11130 : */
11131 : static PGresult *
11132 1040 : fetchAttributeStats(Archive *fout)
11133 : {
11134 1040 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11135 1040 : PQExpBuffer relids = createPQExpBuffer();
11136 1040 : PQExpBuffer nspnames = createPQExpBuffer();
11137 1040 : PQExpBuffer relnames = createPQExpBuffer();
11138 1040 : int count = 0;
11139 1040 : PGresult *res = NULL;
11140 : static TocEntry *te;
11141 : static bool restarted;
11142 1040 : int max_rels = MAX_ATTR_STATS_RELS;
11143 :
11144 : /*
11145 : * Our query for retrieving statistics for multiple relations uses WITH
11146 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11147 : * in v9.4. For older versions, we resort to gathering statistics for a
11148 : * single relation at a time.
11149 : */
11150 1040 : if (fout->remoteVersion < 90400)
11151 0 : max_rels = 1;
11152 :
11153 : /* If we're just starting, set our TOC pointer. */
11154 1040 : if (!te)
11155 62 : te = AH->toc->next;
11156 :
11157 : /*
11158 : * We can't easily avoid a second TOC scan for the tar format because it
11159 : * writes restore.sql separately, which means we must execute the queries
11160 : * twice. This feels risky, but there is no known reason it should
11161 : * generate different output than the first pass. Even if it does, the
11162 : * worst-case scenario is that restore.sql might have different statistics
11163 : * data than the archive.
11164 : */
11165 1040 : if (!restarted && te == AH->toc && AH->format == archTar)
11166 : {
11167 1 : te = AH->toc->next;
11168 1 : restarted = true;
11169 : }
11170 :
11171 1040 : appendPQExpBufferChar(relids, '{');
11172 1040 : appendPQExpBufferChar(nspnames, '{');
11173 1040 : appendPQExpBufferChar(relnames, '{');
11174 :
11175 : /*
11176 : * Scan the TOC for the next set of relevant stats entries. We assume
11177 : * that statistics are dumped in the order they are listed in the TOC.
11178 : * This is perhaps not the sturdiest assumption, so we verify it matches
11179 : * reality in dumpRelationStats_dumper().
11180 : */
11181 16356 : for (; te != AH->toc && count < max_rels; te = te->next)
11182 : {
11183 15316 : if ((te->reqs & REQ_STATS) == 0 ||
11184 3439 : strcmp(te->desc, "STATISTICS DATA") != 0)
11185 11912 : continue;
11186 :
11187 3404 : if (fout->remoteVersion >= 190000)
11188 : {
11189 3404 : const RelStatsInfo *rsinfo = (const RelStatsInfo *) te->defnDumperArg;
11190 : char relid[32];
11191 :
11192 3404 : sprintf(relid, "%u", rsinfo->relid);
11193 3404 : appendPGArray(relids, relid);
11194 : }
11195 : else
11196 : {
11197 0 : appendPGArray(nspnames, te->namespace);
11198 0 : appendPGArray(relnames, te->tag);
11199 : }
11200 :
11201 3404 : count++;
11202 : }
11203 :
11204 1040 : appendPQExpBufferChar(relids, '}');
11205 1040 : appendPQExpBufferChar(nspnames, '}');
11206 1040 : appendPQExpBufferChar(relnames, '}');
11207 :
11208 : /* Execute the query for the next batch of relations. */
11209 1040 : if (count > 0)
11210 : {
11211 107 : PQExpBuffer query = createPQExpBuffer();
11212 :
11213 107 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11214 :
11215 107 : if (fout->remoteVersion >= 190000)
11216 : {
11217 107 : appendStringLiteralAH(query, relids->data, fout);
11218 107 : appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11219 : }
11220 : else
11221 : {
11222 0 : appendStringLiteralAH(query, nspnames->data, fout);
11223 0 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11224 0 : appendStringLiteralAH(query, relnames->data, fout);
11225 0 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11226 : }
11227 :
11228 107 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11229 107 : destroyPQExpBuffer(query);
11230 : }
11231 :
11232 1040 : destroyPQExpBuffer(relids);
11233 1040 : destroyPQExpBuffer(nspnames);
11234 1040 : destroyPQExpBuffer(relnames);
11235 1040 : return res;
11236 : }
11237 :
11238 : /*
11239 : * dumpRelationStats_dumper --
11240 : *
11241 : * Generate command to import stats into the relation on the new database.
11242 : * This routine is called by the Archiver when it wants the statistics to be
11243 : * dumped.
11244 : */
11245 : static char *
11246 3404 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11247 : {
11248 3404 : const RelStatsInfo *rsinfo = userArg;
11249 : static PGresult *res;
11250 : static int rownum;
11251 : PQExpBuffer query;
11252 : PQExpBufferData out_data;
11253 3404 : PQExpBuffer out = &out_data;
11254 : int i_schemaname;
11255 : int i_tablename;
11256 : int i_attname;
11257 : int i_inherited;
11258 : int i_null_frac;
11259 : int i_avg_width;
11260 : int i_n_distinct;
11261 : int i_most_common_vals;
11262 : int i_most_common_freqs;
11263 : int i_histogram_bounds;
11264 : int i_correlation;
11265 : int i_most_common_elems;
11266 : int i_most_common_elem_freqs;
11267 : int i_elem_count_histogram;
11268 : int i_range_length_histogram;
11269 : int i_range_empty_frac;
11270 : int i_range_bounds_histogram;
11271 : static TocEntry *expected_te;
11272 :
11273 : /*
11274 : * fetchAttributeStats() assumes that the statistics are dumped in the
11275 : * order they are listed in the TOC. We verify that here for safety.
11276 : */
11277 3404 : if (!expected_te)
11278 62 : expected_te = ((ArchiveHandle *) fout)->toc;
11279 :
11280 3404 : expected_te = expected_te->next;
11281 13383 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11282 3405 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11283 9979 : expected_te = expected_te->next;
11284 :
11285 3404 : if (te != expected_te)
11286 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11287 : te->dumpId, te->desc, te->tag,
11288 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11289 :
11290 3404 : query = createPQExpBuffer();
11291 3404 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11292 : {
11293 62 : if (fout->remoteVersion >= 190000)
11294 62 : appendPQExpBufferStr(query,
11295 : "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11296 : else
11297 0 : appendPQExpBufferStr(query,
11298 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11299 :
11300 62 : appendPQExpBufferStr(query,
11301 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11302 : "s.null_frac, s.avg_width, s.n_distinct, "
11303 : "s.most_common_vals, s.most_common_freqs, "
11304 : "s.histogram_bounds, s.correlation, "
11305 : "s.most_common_elems, s.most_common_elem_freqs, "
11306 : "s.elem_count_histogram, ");
11307 :
11308 62 : if (fout->remoteVersion >= 170000)
11309 62 : appendPQExpBufferStr(query,
11310 : "s.range_length_histogram, "
11311 : "s.range_empty_frac, "
11312 : "s.range_bounds_histogram ");
11313 : else
11314 0 : appendPQExpBufferStr(query,
11315 : "NULL AS range_length_histogram,"
11316 : "NULL AS range_empty_frac,"
11317 : "NULL AS range_bounds_histogram ");
11318 :
11319 : /*
11320 : * The results must be in the order of the relations supplied in the
11321 : * parameters to ensure we remain in sync as we walk through the TOC.
11322 : *
11323 : * For v9.4 through v18, the redundant filter clause on s.tablename =
11324 : * ANY(...) seems sufficient to convince the planner to use
11325 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11326 : * In newer versions, pg_stats returns the table OIDs, eliminating the
11327 : * need for that hack.
11328 : *
11329 : * Our query for retrieving statistics for multiple relations uses
11330 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11331 : * introduced in v9.4. For older versions, we resort to gathering
11332 : * statistics for a single relation at a time.
11333 : */
11334 62 : if (fout->remoteVersion >= 190000)
11335 62 : appendPQExpBufferStr(query,
11336 : "FROM pg_catalog.pg_stats s "
11337 : "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11338 : "ON s.tableid = u.tableid "
11339 : "ORDER BY u.ord, s.attname, s.inherited");
11340 0 : else if (fout->remoteVersion >= 90400)
11341 0 : appendPQExpBufferStr(query,
11342 : "FROM pg_catalog.pg_stats s "
11343 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11344 : "ON s.schemaname = u.schemaname "
11345 : "AND s.tablename = u.tablename "
11346 : "WHERE s.tablename = ANY($2) "
11347 : "ORDER BY u.ord, s.attname, s.inherited");
11348 : else
11349 0 : appendPQExpBufferStr(query,
11350 : "FROM pg_catalog.pg_stats s "
11351 : "WHERE s.schemaname = $1[1] "
11352 : "AND s.tablename = $2[1] "
11353 : "ORDER BY s.attname, s.inherited");
11354 :
11355 62 : ExecuteSqlStatement(fout, query->data);
11356 :
11357 62 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11358 62 : resetPQExpBuffer(query);
11359 : }
11360 :
11361 3404 : initPQExpBuffer(out);
11362 :
11363 : /* restore relation stats */
11364 3404 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11365 3404 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11366 : fout->remoteVersion);
11367 3404 : appendPQExpBufferStr(out, "\t'schemaname', ");
11368 3404 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11369 3404 : appendPQExpBufferStr(out, ",\n");
11370 3404 : appendPQExpBufferStr(out, "\t'relname', ");
11371 3404 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11372 3404 : appendPQExpBufferStr(out, ",\n");
11373 3404 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11374 :
11375 : /*
11376 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11377 : * the relation is empty, or it could mean that it hadn't yet been
11378 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11379 : * This ambiguity allegedly can cause the planner to choose inefficient
11380 : * plans after restoring to v18 or newer. To deal with this, let's just
11381 : * set reltuples to -1 in that case.
11382 : */
11383 3404 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11384 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11385 : else
11386 3404 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11387 :
11388 3404 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11389 3404 : rsinfo->relallvisible);
11390 :
11391 3404 : if (fout->remoteVersion >= 180000)
11392 3404 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11393 :
11394 3404 : appendPQExpBufferStr(out, "\n);\n");
11395 :
11396 : /* Fetch the next batch of attribute statistics if needed. */
11397 3404 : if (rownum >= PQntuples(res))
11398 : {
11399 1040 : PQclear(res);
11400 1040 : res = fetchAttributeStats(fout);
11401 1040 : rownum = 0;
11402 : }
11403 :
11404 3404 : i_schemaname = PQfnumber(res, "schemaname");
11405 3404 : i_tablename = PQfnumber(res, "tablename");
11406 3404 : i_attname = PQfnumber(res, "attname");
11407 3404 : i_inherited = PQfnumber(res, "inherited");
11408 3404 : i_null_frac = PQfnumber(res, "null_frac");
11409 3404 : i_avg_width = PQfnumber(res, "avg_width");
11410 3404 : i_n_distinct = PQfnumber(res, "n_distinct");
11411 3404 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11412 3404 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11413 3404 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11414 3404 : i_correlation = PQfnumber(res, "correlation");
11415 3404 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11416 3404 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11417 3404 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11418 3404 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11419 3404 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11420 3404 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11421 :
11422 : /* restore attribute stats */
11423 4221 : for (; rownum < PQntuples(res); rownum++)
11424 : {
11425 : const char *attname;
11426 :
11427 : /* Stop if the next stat row in our cache isn't for this relation. */
11428 3181 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11429 817 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11430 : break;
11431 :
11432 817 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11433 817 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11434 : fout->remoteVersion);
11435 817 : appendPQExpBufferStr(out, "\t'schemaname', ");
11436 817 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11437 817 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11438 817 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11439 :
11440 817 : if (PQgetisnull(res, rownum, i_attname))
11441 0 : pg_fatal("unexpected null attname");
11442 817 : attname = PQgetvalue(res, rownum, i_attname);
11443 :
11444 : /*
11445 : * Indexes look up attname in indAttNames to derive attnum, all others
11446 : * use attname directly. We must specify attnum for indexes, since
11447 : * their attnames are not necessarily stable across dump/reload.
11448 : */
11449 817 : if (rsinfo->nindAttNames == 0)
11450 : {
11451 782 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11452 782 : appendStringLiteralAH(out, attname, fout);
11453 : }
11454 : else
11455 : {
11456 35 : bool found = false;
11457 :
11458 66 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11459 : {
11460 66 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11461 : {
11462 35 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11463 : i + 1);
11464 35 : found = true;
11465 35 : break;
11466 : }
11467 : }
11468 :
11469 35 : if (!found)
11470 0 : pg_fatal("could not find index attname \"%s\"", attname);
11471 : }
11472 :
11473 817 : if (!PQgetisnull(res, rownum, i_inherited))
11474 817 : appendNamedArgument(out, fout, "inherited", "boolean",
11475 817 : PQgetvalue(res, rownum, i_inherited));
11476 817 : if (!PQgetisnull(res, rownum, i_null_frac))
11477 817 : appendNamedArgument(out, fout, "null_frac", "real",
11478 817 : PQgetvalue(res, rownum, i_null_frac));
11479 817 : if (!PQgetisnull(res, rownum, i_avg_width))
11480 817 : appendNamedArgument(out, fout, "avg_width", "integer",
11481 817 : PQgetvalue(res, rownum, i_avg_width));
11482 817 : if (!PQgetisnull(res, rownum, i_n_distinct))
11483 817 : appendNamedArgument(out, fout, "n_distinct", "real",
11484 817 : PQgetvalue(res, rownum, i_n_distinct));
11485 817 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11486 405 : appendNamedArgument(out, fout, "most_common_vals", "text",
11487 405 : PQgetvalue(res, rownum, i_most_common_vals));
11488 817 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11489 405 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11490 405 : PQgetvalue(res, rownum, i_most_common_freqs));
11491 817 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11492 513 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11493 513 : PQgetvalue(res, rownum, i_histogram_bounds));
11494 817 : if (!PQgetisnull(res, rownum, i_correlation))
11495 784 : appendNamedArgument(out, fout, "correlation", "real",
11496 784 : PQgetvalue(res, rownum, i_correlation));
11497 817 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11498 8 : appendNamedArgument(out, fout, "most_common_elems", "text",
11499 8 : PQgetvalue(res, rownum, i_most_common_elems));
11500 817 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11501 8 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11502 8 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11503 817 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11504 7 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11505 7 : PQgetvalue(res, rownum, i_elem_count_histogram));
11506 817 : if (fout->remoteVersion >= 170000)
11507 : {
11508 817 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11509 3 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11510 3 : PQgetvalue(res, rownum, i_range_length_histogram));
11511 817 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11512 3 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11513 3 : PQgetvalue(res, rownum, i_range_empty_frac));
11514 817 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11515 3 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11516 3 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11517 : }
11518 817 : appendPQExpBufferStr(out, "\n);\n");
11519 : }
11520 :
11521 3404 : destroyPQExpBuffer(query);
11522 3404 : return out->data;
11523 : }
11524 :
11525 : /*
11526 : * dumpRelationStats --
11527 : *
11528 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11529 : * care of gathering the statistics and generating the restore commands when
11530 : * they are needed.
11531 : */
11532 : static void
11533 3476 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11534 : {
11535 3476 : const DumpableObject *dobj = &rsinfo->dobj;
11536 :
11537 : /* nothing to do if we are not dumping statistics */
11538 3476 : if (!fout->dopt->dumpStatistics)
11539 0 : return;
11540 :
11541 3476 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11542 3476 : ARCHIVE_OPTS(.tag = dobj->name,
11543 : .namespace = dobj->namespace->dobj.name,
11544 : .description = "STATISTICS DATA",
11545 : .section = rsinfo->section,
11546 : .defnFn = dumpRelationStats_dumper,
11547 : .defnArg = rsinfo,
11548 : .deps = dobj->dependencies,
11549 : .nDeps = dobj->nDeps));
11550 : }
11551 :
11552 : /*
11553 : * dumpTableComment --
11554 : *
11555 : * As above, but dump comments for both the specified table (or view)
11556 : * and its columns.
11557 : */
11558 : static void
11559 74 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11560 : const char *reltypename)
11561 : {
11562 74 : DumpOptions *dopt = fout->dopt;
11563 : CommentItem *comments;
11564 : int ncomments;
11565 : PQExpBuffer query;
11566 : PQExpBuffer tag;
11567 :
11568 : /* do nothing, if --no-comments is supplied */
11569 74 : if (dopt->no_comments)
11570 0 : return;
11571 :
11572 : /* Comments are SCHEMA not data */
11573 74 : if (!dopt->dumpSchema)
11574 0 : return;
11575 :
11576 : /* Search for comments associated with relation, using table */
11577 74 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11578 74 : tbinfo->dobj.catId.oid,
11579 : &comments);
11580 :
11581 : /* If comments exist, build COMMENT ON statements */
11582 74 : if (ncomments <= 0)
11583 0 : return;
11584 :
11585 74 : query = createPQExpBuffer();
11586 74 : tag = createPQExpBuffer();
11587 :
11588 212 : while (ncomments > 0)
11589 : {
11590 138 : const char *descr = comments->descr;
11591 138 : int objsubid = comments->objsubid;
11592 :
11593 138 : if (objsubid == 0)
11594 : {
11595 32 : resetPQExpBuffer(tag);
11596 32 : appendPQExpBuffer(tag, "%s %s", reltypename,
11597 32 : fmtId(tbinfo->dobj.name));
11598 :
11599 32 : resetPQExpBuffer(query);
11600 32 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11601 32 : fmtQualifiedDumpable(tbinfo));
11602 32 : appendStringLiteralAH(query, descr, fout);
11603 32 : appendPQExpBufferStr(query, ";\n");
11604 :
11605 32 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11606 32 : ARCHIVE_OPTS(.tag = tag->data,
11607 : .namespace = tbinfo->dobj.namespace->dobj.name,
11608 : .owner = tbinfo->rolname,
11609 : .description = "COMMENT",
11610 : .section = SECTION_NONE,
11611 : .createStmt = query->data,
11612 : .deps = &(tbinfo->dobj.dumpId),
11613 : .nDeps = 1));
11614 : }
11615 106 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11616 : {
11617 106 : resetPQExpBuffer(tag);
11618 106 : appendPQExpBuffer(tag, "COLUMN %s.",
11619 106 : fmtId(tbinfo->dobj.name));
11620 106 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11621 :
11622 106 : resetPQExpBuffer(query);
11623 106 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11624 106 : fmtQualifiedDumpable(tbinfo));
11625 106 : appendPQExpBuffer(query, "%s IS ",
11626 106 : fmtId(tbinfo->attnames[objsubid - 1]));
11627 106 : appendStringLiteralAH(query, descr, fout);
11628 106 : appendPQExpBufferStr(query, ";\n");
11629 :
11630 106 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11631 106 : ARCHIVE_OPTS(.tag = tag->data,
11632 : .namespace = tbinfo->dobj.namespace->dobj.name,
11633 : .owner = tbinfo->rolname,
11634 : .description = "COMMENT",
11635 : .section = SECTION_NONE,
11636 : .createStmt = query->data,
11637 : .deps = &(tbinfo->dobj.dumpId),
11638 : .nDeps = 1));
11639 : }
11640 :
11641 138 : comments++;
11642 138 : ncomments--;
11643 : }
11644 :
11645 74 : destroyPQExpBuffer(query);
11646 74 : destroyPQExpBuffer(tag);
11647 : }
11648 :
11649 : /*
11650 : * findComments --
11651 : *
11652 : * Find the comment(s), if any, associated with the given object. All the
11653 : * objsubid values associated with the given classoid/objoid are found with
11654 : * one search.
11655 : */
11656 : static int
11657 6780 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11658 : {
11659 6780 : CommentItem *middle = NULL;
11660 : CommentItem *low;
11661 : CommentItem *high;
11662 : int nmatch;
11663 :
11664 : /*
11665 : * Do binary search to find some item matching the object.
11666 : */
11667 6780 : low = &comments[0];
11668 6780 : high = &comments[ncomments - 1];
11669 67655 : while (low <= high)
11670 : {
11671 67610 : middle = low + (high - low) / 2;
11672 :
11673 67610 : if (classoid < middle->classoid)
11674 7153 : high = middle - 1;
11675 60457 : else if (classoid > middle->classoid)
11676 7386 : low = middle + 1;
11677 53071 : else if (objoid < middle->objoid)
11678 22607 : high = middle - 1;
11679 30464 : else if (objoid > middle->objoid)
11680 23729 : low = middle + 1;
11681 : else
11682 6735 : break; /* found a match */
11683 : }
11684 :
11685 6780 : if (low > high) /* no matches */
11686 : {
11687 45 : *items = NULL;
11688 45 : return 0;
11689 : }
11690 :
11691 : /*
11692 : * Now determine how many items match the object. The search loop
11693 : * invariant still holds: only items between low and high inclusive could
11694 : * match.
11695 : */
11696 6735 : nmatch = 1;
11697 6735 : while (middle > low)
11698 : {
11699 3356 : if (classoid != middle[-1].classoid ||
11700 3201 : objoid != middle[-1].objoid)
11701 : break;
11702 0 : middle--;
11703 0 : nmatch++;
11704 : }
11705 :
11706 6735 : *items = middle;
11707 :
11708 6735 : middle += nmatch;
11709 6799 : while (middle <= high)
11710 : {
11711 3620 : if (classoid != middle->classoid ||
11712 3166 : objoid != middle->objoid)
11713 : break;
11714 64 : middle++;
11715 64 : nmatch++;
11716 : }
11717 :
11718 6735 : return nmatch;
11719 : }
11720 :
11721 : /*
11722 : * collectComments --
11723 : *
11724 : * Construct a table of all comments available for database objects;
11725 : * also set the has-comment component flag for each relevant object.
11726 : *
11727 : * We used to do per-object queries for the comments, but it's much faster
11728 : * to pull them all over at once, and on most databases the memory cost
11729 : * isn't high.
11730 : *
11731 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11732 : */
11733 : static void
11734 259 : collectComments(Archive *fout)
11735 : {
11736 : PGresult *res;
11737 : PQExpBuffer query;
11738 : int i_description;
11739 : int i_classoid;
11740 : int i_objoid;
11741 : int i_objsubid;
11742 : int ntups;
11743 : int i;
11744 : DumpableObject *dobj;
11745 :
11746 259 : query = createPQExpBuffer();
11747 :
11748 259 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11749 : "FROM pg_catalog.pg_description "
11750 : "ORDER BY classoid, objoid, objsubid");
11751 :
11752 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11753 :
11754 : /* Construct lookup table containing OIDs in numeric form */
11755 :
11756 259 : i_description = PQfnumber(res, "description");
11757 259 : i_classoid = PQfnumber(res, "classoid");
11758 259 : i_objoid = PQfnumber(res, "objoid");
11759 259 : i_objsubid = PQfnumber(res, "objsubid");
11760 :
11761 259 : ntups = PQntuples(res);
11762 :
11763 259 : comments = pg_malloc_array(CommentItem, ntups);
11764 259 : ncomments = 0;
11765 259 : dobj = NULL;
11766 :
11767 1394387 : for (i = 0; i < ntups; i++)
11768 : {
11769 : CatalogId objId;
11770 : int subid;
11771 :
11772 1394128 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11773 1394128 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11774 1394128 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11775 :
11776 : /* We needn't remember comments that don't match any dumpable object */
11777 1394128 : if (dobj == NULL ||
11778 507153 : dobj->catId.tableoid != objId.tableoid ||
11779 504067 : dobj->catId.oid != objId.oid)
11780 1394038 : dobj = findObjectByCatalogId(objId);
11781 1394128 : if (dobj == NULL)
11782 886722 : continue;
11783 :
11784 : /*
11785 : * Comments on columns of composite types are linked to the type's
11786 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11787 : * in the type's own DumpableObject.
11788 : */
11789 507406 : if (subid != 0 && dobj->objType == DO_TABLE &&
11790 194 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11791 45 : {
11792 : TypeInfo *cTypeInfo;
11793 :
11794 45 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11795 45 : if (cTypeInfo)
11796 45 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11797 : }
11798 : else
11799 507361 : dobj->components |= DUMP_COMPONENT_COMMENT;
11800 :
11801 507406 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11802 507406 : comments[ncomments].classoid = objId.tableoid;
11803 507406 : comments[ncomments].objoid = objId.oid;
11804 507406 : comments[ncomments].objsubid = subid;
11805 507406 : ncomments++;
11806 : }
11807 :
11808 259 : PQclear(res);
11809 259 : destroyPQExpBuffer(query);
11810 259 : }
11811 :
11812 : /*
11813 : * dumpDumpableObject
11814 : *
11815 : * This routine and its subsidiaries are responsible for creating
11816 : * ArchiveEntries (TOC objects) for each object to be dumped.
11817 : */
11818 : static void
11819 976035 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11820 : {
11821 : /*
11822 : * Clear any dump-request bits for components that don't exist for this
11823 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11824 : * request for every kind of object.)
11825 : */
11826 976035 : dobj->dump &= dobj->components;
11827 :
11828 : /* Now, short-circuit if there's nothing to be done here. */
11829 976035 : if (dobj->dump == 0)
11830 882810 : return;
11831 :
11832 93225 : switch (dobj->objType)
11833 : {
11834 660 : case DO_NAMESPACE:
11835 660 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11836 660 : break;
11837 25 : case DO_EXTENSION:
11838 25 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11839 25 : break;
11840 948 : case DO_TYPE:
11841 948 : dumpType(fout, (const TypeInfo *) dobj);
11842 948 : break;
11843 73 : case DO_SHELL_TYPE:
11844 73 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11845 73 : break;
11846 1898 : case DO_FUNC:
11847 1898 : dumpFunc(fout, (const FuncInfo *) dobj);
11848 1898 : break;
11849 292 : case DO_AGG:
11850 292 : dumpAgg(fout, (const AggInfo *) dobj);
11851 292 : break;
11852 2522 : case DO_OPERATOR:
11853 2522 : dumpOpr(fout, (const OprInfo *) dobj);
11854 2522 : break;
11855 80 : case DO_ACCESS_METHOD:
11856 80 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11857 80 : break;
11858 666 : case DO_OPCLASS:
11859 666 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11860 666 : break;
11861 555 : case DO_OPFAMILY:
11862 555 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11863 555 : break;
11864 2729 : case DO_COLLATION:
11865 2729 : dumpCollation(fout, (const CollInfo *) dobj);
11866 2729 : break;
11867 332 : case DO_CONVERSION:
11868 332 : dumpConversion(fout, (const ConvInfo *) dobj);
11869 332 : break;
11870 44759 : case DO_TABLE:
11871 44759 : dumpTable(fout, (const TableInfo *) dobj);
11872 44759 : break;
11873 1431 : case DO_TABLE_ATTACH:
11874 1431 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11875 1431 : break;
11876 1087 : case DO_ATTRDEF:
11877 1087 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11878 1087 : break;
11879 2768 : case DO_INDEX:
11880 2768 : dumpIndex(fout, (const IndxInfo *) dobj);
11881 2768 : break;
11882 594 : case DO_INDEX_ATTACH:
11883 594 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11884 594 : break;
11885 171 : case DO_STATSEXT:
11886 171 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11887 171 : dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11888 171 : break;
11889 345 : case DO_REFRESH_MATVIEW:
11890 345 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11891 345 : break;
11892 1164 : case DO_RULE:
11893 1164 : dumpRule(fout, (const RuleInfo *) dobj);
11894 1164 : break;
11895 523 : case DO_TRIGGER:
11896 523 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11897 523 : break;
11898 42 : case DO_EVENT_TRIGGER:
11899 42 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11900 42 : break;
11901 2503 : case DO_CONSTRAINT:
11902 2503 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11903 2503 : break;
11904 231 : case DO_FK_CONSTRAINT:
11905 231 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11906 231 : break;
11907 82 : case DO_PROCLANG:
11908 82 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11909 82 : break;
11910 67 : case DO_CAST:
11911 67 : dumpCast(fout, (const CastInfo *) dobj);
11912 67 : break;
11913 42 : case DO_TRANSFORM:
11914 42 : dumpTransform(fout, (const TransformInfo *) dobj);
11915 42 : break;
11916 402 : case DO_SEQUENCE_SET:
11917 402 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11918 402 : break;
11919 4656 : case DO_TABLE_DATA:
11920 4656 : dumpTableData(fout, (const TableDataInfo *) dobj);
11921 4656 : break;
11922 15493 : case DO_DUMMY_TYPE:
11923 : /* table rowtypes and array types are never dumped separately */
11924 15493 : break;
11925 41 : case DO_TSPARSER:
11926 41 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11927 41 : break;
11928 179 : case DO_TSDICT:
11929 179 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11930 179 : break;
11931 53 : case DO_TSTEMPLATE:
11932 53 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11933 53 : break;
11934 154 : case DO_TSCONFIG:
11935 154 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11936 154 : break;
11937 52 : case DO_FDW:
11938 52 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11939 52 : break;
11940 56 : case DO_FOREIGN_SERVER:
11941 56 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11942 56 : break;
11943 160 : case DO_DEFAULT_ACL:
11944 160 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11945 160 : break;
11946 84 : case DO_LARGE_OBJECT:
11947 84 : dumpLO(fout, (const LoInfo *) dobj);
11948 84 : break;
11949 84 : case DO_LARGE_OBJECT_DATA:
11950 84 : if (dobj->dump & DUMP_COMPONENT_DATA)
11951 : {
11952 : LoInfo *loinfo;
11953 : TocEntry *te;
11954 :
11955 84 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11956 84 : if (loinfo == NULL)
11957 0 : pg_fatal("missing metadata for large objects \"%s\"",
11958 : dobj->name);
11959 :
11960 84 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11961 84 : ARCHIVE_OPTS(.tag = dobj->name,
11962 : .owner = loinfo->rolname,
11963 : .description = "BLOBS",
11964 : .section = SECTION_DATA,
11965 : .deps = dobj->dependencies,
11966 : .nDeps = dobj->nDeps,
11967 : .dumpFn = dumpLOs,
11968 : .dumpArg = loinfo));
11969 :
11970 : /*
11971 : * Set the TocEntry's dataLength in case we are doing a
11972 : * parallel dump and want to order dump jobs by table size.
11973 : * (We need some size estimate for every TocEntry with a
11974 : * DataDumper function.) We don't currently have any cheap
11975 : * way to estimate the size of LOs, but fortunately it doesn't
11976 : * matter too much as long as we get large batches of LOs
11977 : * processed reasonably early. Assume 8K per blob.
11978 : */
11979 84 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11980 : }
11981 84 : break;
11982 336 : case DO_POLICY:
11983 336 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11984 336 : break;
11985 396 : case DO_PUBLICATION:
11986 396 : dumpPublication(fout, (const PublicationInfo *) dobj);
11987 396 : break;
11988 284 : case DO_PUBLICATION_REL:
11989 284 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11990 284 : break;
11991 99 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11992 99 : dumpPublicationNamespace(fout,
11993 : (const PublicationSchemaInfo *) dobj);
11994 99 : break;
11995 110 : case DO_SUBSCRIPTION:
11996 110 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11997 110 : break;
11998 3 : case DO_SUBSCRIPTION_REL:
11999 3 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
12000 3 : break;
12001 3476 : case DO_REL_STATS:
12002 3476 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
12003 3476 : break;
12004 518 : case DO_PRE_DATA_BOUNDARY:
12005 : case DO_POST_DATA_BOUNDARY:
12006 : /* never dumped, nothing to do */
12007 518 : break;
12008 : }
12009 : }
12010 :
12011 : /*
12012 : * dumpNamespace
12013 : * writes out to fout the queries to recreate a user-defined namespace
12014 : */
12015 : static void
12016 660 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
12017 : {
12018 660 : DumpOptions *dopt = fout->dopt;
12019 : PQExpBuffer q;
12020 : PQExpBuffer delq;
12021 : char *qnspname;
12022 :
12023 : /* Do nothing if not dumping schema */
12024 660 : if (!dopt->dumpSchema)
12025 28 : return;
12026 :
12027 632 : q = createPQExpBuffer();
12028 632 : delq = createPQExpBuffer();
12029 :
12030 632 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
12031 :
12032 632 : if (nspinfo->create)
12033 : {
12034 408 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
12035 408 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
12036 : }
12037 : else
12038 : {
12039 : /* see selectDumpableNamespace() */
12040 224 : appendPQExpBufferStr(delq,
12041 : "-- *not* dropping schema, since initdb creates it\n");
12042 224 : appendPQExpBufferStr(q,
12043 : "-- *not* creating schema, since initdb creates it\n");
12044 : }
12045 :
12046 632 : if (dopt->binary_upgrade)
12047 102 : binary_upgrade_extension_member(q, &nspinfo->dobj,
12048 : "SCHEMA", qnspname, NULL);
12049 :
12050 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12051 214 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
12052 214 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
12053 : .owner = nspinfo->rolname,
12054 : .description = "SCHEMA",
12055 : .section = SECTION_PRE_DATA,
12056 : .createStmt = q->data,
12057 : .dropStmt = delq->data));
12058 :
12059 : /* Dump Schema Comments and Security Labels */
12060 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12061 : {
12062 229 : const char *initdb_comment = NULL;
12063 :
12064 229 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
12065 186 : initdb_comment = "standard public schema";
12066 229 : dumpCommentExtended(fout, "SCHEMA", qnspname,
12067 229 : NULL, nspinfo->rolname,
12068 229 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
12069 : initdb_comment);
12070 : }
12071 :
12072 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12073 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
12074 0 : NULL, nspinfo->rolname,
12075 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
12076 :
12077 632 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
12078 530 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
12079 : qnspname, NULL, NULL,
12080 530 : NULL, nspinfo->rolname, &nspinfo->dacl);
12081 :
12082 632 : free(qnspname);
12083 :
12084 632 : destroyPQExpBuffer(q);
12085 632 : destroyPQExpBuffer(delq);
12086 : }
12087 :
12088 : /*
12089 : * dumpExtension
12090 : * writes out to fout the queries to recreate an extension
12091 : */
12092 : static void
12093 25 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
12094 : {
12095 25 : DumpOptions *dopt = fout->dopt;
12096 : PQExpBuffer q;
12097 : PQExpBuffer delq;
12098 : char *qextname;
12099 :
12100 : /* Do nothing if not dumping schema */
12101 25 : if (!dopt->dumpSchema)
12102 1 : return;
12103 :
12104 24 : q = createPQExpBuffer();
12105 24 : delq = createPQExpBuffer();
12106 :
12107 24 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
12108 :
12109 24 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
12110 :
12111 24 : if (!dopt->binary_upgrade)
12112 : {
12113 : /*
12114 : * In a regular dump, we simply create the extension, intentionally
12115 : * not specifying a version, so that the destination installation's
12116 : * default version is used.
12117 : *
12118 : * Use of IF NOT EXISTS here is unlike our behavior for other object
12119 : * types; but there are various scenarios in which it's convenient to
12120 : * manually create the desired extension before restoring, so we
12121 : * prefer to allow it to exist already.
12122 : */
12123 17 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
12124 17 : qextname, fmtId(extinfo->namespace));
12125 : }
12126 : else
12127 : {
12128 : /*
12129 : * In binary-upgrade mode, it's critical to reproduce the state of the
12130 : * database exactly, so our procedure is to create an empty extension,
12131 : * restore all the contained objects normally, and add them to the
12132 : * extension one by one. This function performs just the first of
12133 : * those steps. binary_upgrade_extension_member() takes care of
12134 : * adding member objects as they're created.
12135 : */
12136 : int i;
12137 : int n;
12138 :
12139 7 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
12140 :
12141 : /*
12142 : * We unconditionally create the extension, so we must drop it if it
12143 : * exists. This could happen if the user deleted 'plpgsql' and then
12144 : * readded it, causing its oid to be greater than g_last_builtin_oid.
12145 : */
12146 7 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
12147 :
12148 7 : appendPQExpBufferStr(q,
12149 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
12150 7 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
12151 7 : appendPQExpBufferStr(q, ", ");
12152 7 : appendStringLiteralAH(q, extinfo->namespace, fout);
12153 7 : appendPQExpBufferStr(q, ", ");
12154 7 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
12155 7 : appendStringLiteralAH(q, extinfo->extversion, fout);
12156 7 : appendPQExpBufferStr(q, ", ");
12157 :
12158 : /*
12159 : * Note that we're pushing extconfig (an OID array) back into
12160 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
12161 : * preserved in binary upgrade.
12162 : */
12163 7 : if (strlen(extinfo->extconfig) > 2)
12164 1 : appendStringLiteralAH(q, extinfo->extconfig, fout);
12165 : else
12166 6 : appendPQExpBufferStr(q, "NULL");
12167 7 : appendPQExpBufferStr(q, ", ");
12168 7 : if (strlen(extinfo->extcondition) > 2)
12169 1 : appendStringLiteralAH(q, extinfo->extcondition, fout);
12170 : else
12171 6 : appendPQExpBufferStr(q, "NULL");
12172 7 : appendPQExpBufferStr(q, ", ");
12173 7 : appendPQExpBufferStr(q, "ARRAY[");
12174 7 : n = 0;
12175 14 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12176 : {
12177 : DumpableObject *extobj;
12178 :
12179 7 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12180 7 : if (extobj && extobj->objType == DO_EXTENSION)
12181 : {
12182 0 : if (n++ > 0)
12183 0 : appendPQExpBufferChar(q, ',');
12184 0 : appendStringLiteralAH(q, extobj->name, fout);
12185 : }
12186 : }
12187 7 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12188 7 : appendPQExpBufferStr(q, ");\n");
12189 : }
12190 :
12191 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12192 24 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12193 24 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12194 : .description = "EXTENSION",
12195 : .section = SECTION_PRE_DATA,
12196 : .createStmt = q->data,
12197 : .dropStmt = delq->data));
12198 :
12199 : /* Dump Extension Comments */
12200 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12201 24 : dumpComment(fout, "EXTENSION", qextname,
12202 : NULL, "",
12203 24 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12204 :
12205 24 : free(qextname);
12206 :
12207 24 : destroyPQExpBuffer(q);
12208 24 : destroyPQExpBuffer(delq);
12209 : }
12210 :
12211 : /*
12212 : * dumpType
12213 : * writes out to fout the queries to recreate a user-defined type
12214 : */
12215 : static void
12216 948 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12217 : {
12218 948 : DumpOptions *dopt = fout->dopt;
12219 :
12220 : /* Do nothing if not dumping schema */
12221 948 : if (!dopt->dumpSchema)
12222 49 : return;
12223 :
12224 : /* Dump out in proper style */
12225 899 : if (tyinfo->typtype == TYPTYPE_BASE)
12226 283 : dumpBaseType(fout, tyinfo);
12227 616 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12228 172 : dumpDomain(fout, tyinfo);
12229 444 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12230 130 : dumpCompositeType(fout, tyinfo);
12231 314 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12232 85 : dumpEnumType(fout, tyinfo);
12233 229 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12234 117 : dumpRangeType(fout, tyinfo);
12235 112 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12236 37 : dumpUndefinedType(fout, tyinfo);
12237 : else
12238 75 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12239 : tyinfo->dobj.name);
12240 : }
12241 :
12242 : /*
12243 : * dumpEnumType
12244 : * writes out to fout the queries to recreate a user-defined enum type
12245 : */
12246 : static void
12247 85 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12248 : {
12249 85 : DumpOptions *dopt = fout->dopt;
12250 85 : PQExpBuffer q = createPQExpBuffer();
12251 85 : PQExpBuffer delq = createPQExpBuffer();
12252 85 : PQExpBuffer query = createPQExpBuffer();
12253 : PGresult *res;
12254 : int num,
12255 : i;
12256 : Oid enum_oid;
12257 : char *qtypname;
12258 : char *qualtypname;
12259 : char *label;
12260 : int i_enumlabel;
12261 : int i_oid;
12262 :
12263 85 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12264 : {
12265 : /* Set up query for enum-specific details */
12266 40 : appendPQExpBufferStr(query,
12267 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12268 : "SELECT oid, enumlabel "
12269 : "FROM pg_catalog.pg_enum "
12270 : "WHERE enumtypid = $1 "
12271 : "ORDER BY enumsortorder");
12272 :
12273 40 : ExecuteSqlStatement(fout, query->data);
12274 :
12275 40 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12276 : }
12277 :
12278 85 : printfPQExpBuffer(query,
12279 : "EXECUTE dumpEnumType('%u')",
12280 85 : tyinfo->dobj.catId.oid);
12281 :
12282 85 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12283 :
12284 85 : num = PQntuples(res);
12285 :
12286 85 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12287 85 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12288 :
12289 : /*
12290 : * CASCADE shouldn't be required here as for normal types since the I/O
12291 : * functions are generic and do not get dropped.
12292 : */
12293 85 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12294 :
12295 85 : if (dopt->binary_upgrade)
12296 6 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12297 6 : tyinfo->dobj.catId.oid,
12298 : false, false);
12299 :
12300 85 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12301 : qualtypname);
12302 :
12303 85 : if (!dopt->binary_upgrade)
12304 : {
12305 79 : i_enumlabel = PQfnumber(res, "enumlabel");
12306 :
12307 : /* Labels with server-assigned oids */
12308 482 : for (i = 0; i < num; i++)
12309 : {
12310 403 : label = PQgetvalue(res, i, i_enumlabel);
12311 403 : if (i > 0)
12312 324 : appendPQExpBufferChar(q, ',');
12313 403 : appendPQExpBufferStr(q, "\n ");
12314 403 : appendStringLiteralAH(q, label, fout);
12315 : }
12316 : }
12317 :
12318 85 : appendPQExpBufferStr(q, "\n);\n");
12319 :
12320 85 : if (dopt->binary_upgrade)
12321 : {
12322 6 : i_oid = PQfnumber(res, "oid");
12323 6 : i_enumlabel = PQfnumber(res, "enumlabel");
12324 :
12325 : /* Labels with dump-assigned (preserved) oids */
12326 62 : for (i = 0; i < num; i++)
12327 : {
12328 56 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12329 56 : label = PQgetvalue(res, i, i_enumlabel);
12330 :
12331 56 : if (i == 0)
12332 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12333 56 : appendPQExpBuffer(q,
12334 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12335 : enum_oid);
12336 56 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12337 56 : appendStringLiteralAH(q, label, fout);
12338 56 : appendPQExpBufferStr(q, ";\n\n");
12339 : }
12340 : }
12341 :
12342 85 : if (dopt->binary_upgrade)
12343 6 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12344 : "TYPE", qtypname,
12345 6 : tyinfo->dobj.namespace->dobj.name);
12346 :
12347 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12348 85 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12349 85 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12350 : .namespace = tyinfo->dobj.namespace->dobj.name,
12351 : .owner = tyinfo->rolname,
12352 : .description = "TYPE",
12353 : .section = SECTION_PRE_DATA,
12354 : .createStmt = q->data,
12355 : .dropStmt = delq->data));
12356 :
12357 : /* Dump Type Comments and Security Labels */
12358 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12359 32 : dumpComment(fout, "TYPE", qtypname,
12360 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12361 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12362 :
12363 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12364 0 : dumpSecLabel(fout, "TYPE", qtypname,
12365 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12366 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12367 :
12368 85 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12369 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12370 : qtypname, NULL,
12371 32 : tyinfo->dobj.namespace->dobj.name,
12372 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12373 :
12374 85 : PQclear(res);
12375 85 : destroyPQExpBuffer(q);
12376 85 : destroyPQExpBuffer(delq);
12377 85 : destroyPQExpBuffer(query);
12378 85 : free(qtypname);
12379 85 : free(qualtypname);
12380 85 : }
12381 :
12382 : /*
12383 : * dumpRangeType
12384 : * writes out to fout the queries to recreate a user-defined range type
12385 : */
12386 : static void
12387 117 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12388 : {
12389 117 : DumpOptions *dopt = fout->dopt;
12390 117 : PQExpBuffer q = createPQExpBuffer();
12391 117 : PQExpBuffer delq = createPQExpBuffer();
12392 117 : PQExpBuffer query = createPQExpBuffer();
12393 : PGresult *res;
12394 : Oid collationOid;
12395 : char *qtypname;
12396 : char *qualtypname;
12397 : char *procname;
12398 :
12399 117 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12400 : {
12401 : /* Set up query for range-specific details */
12402 40 : appendPQExpBufferStr(query,
12403 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12404 :
12405 40 : appendPQExpBufferStr(query,
12406 : "SELECT ");
12407 :
12408 40 : if (fout->remoteVersion >= 140000)
12409 40 : appendPQExpBufferStr(query,
12410 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12411 : else
12412 0 : appendPQExpBufferStr(query,
12413 : "NULL AS rngmultitype, ");
12414 :
12415 40 : appendPQExpBufferStr(query,
12416 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12417 : "opc.opcname AS opcname, "
12418 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12419 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12420 : "opc.opcdefault, "
12421 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12422 : " ELSE rngcollation END AS collation, "
12423 : "rngcanonical, rngsubdiff "
12424 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12425 : " pg_catalog.pg_opclass opc "
12426 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12427 : "rngtypid = $1");
12428 :
12429 40 : ExecuteSqlStatement(fout, query->data);
12430 :
12431 40 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12432 : }
12433 :
12434 117 : printfPQExpBuffer(query,
12435 : "EXECUTE dumpRangeType('%u')",
12436 117 : tyinfo->dobj.catId.oid);
12437 :
12438 117 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12439 :
12440 117 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12441 117 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12442 :
12443 : /*
12444 : * CASCADE shouldn't be required here as for normal types since the I/O
12445 : * functions are generic and do not get dropped.
12446 : */
12447 117 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12448 :
12449 117 : if (dopt->binary_upgrade)
12450 9 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12451 9 : tyinfo->dobj.catId.oid,
12452 : false, true);
12453 :
12454 117 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12455 : qualtypname);
12456 :
12457 117 : appendPQExpBuffer(q, "\n subtype = %s",
12458 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12459 :
12460 117 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12461 117 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12462 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12463 :
12464 : /* print subtype_opclass only if not default for subtype */
12465 117 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12466 : {
12467 32 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12468 32 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12469 :
12470 32 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12471 : fmtId(nspname));
12472 32 : appendPQExpBufferStr(q, fmtId(opcname));
12473 : }
12474 :
12475 117 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12476 117 : if (OidIsValid(collationOid))
12477 : {
12478 37 : CollInfo *coll = findCollationByOid(collationOid);
12479 :
12480 37 : if (coll)
12481 37 : appendPQExpBuffer(q, ",\n collation = %s",
12482 37 : fmtQualifiedDumpable(coll));
12483 : }
12484 :
12485 117 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12486 117 : if (strcmp(procname, "-") != 0)
12487 9 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12488 :
12489 117 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12490 117 : if (strcmp(procname, "-") != 0)
12491 23 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12492 :
12493 117 : appendPQExpBufferStr(q, "\n);\n");
12494 :
12495 117 : if (dopt->binary_upgrade)
12496 9 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12497 : "TYPE", qtypname,
12498 9 : tyinfo->dobj.namespace->dobj.name);
12499 :
12500 117 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12501 117 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12502 117 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12503 : .namespace = tyinfo->dobj.namespace->dobj.name,
12504 : .owner = tyinfo->rolname,
12505 : .description = "TYPE",
12506 : .section = SECTION_PRE_DATA,
12507 : .createStmt = q->data,
12508 : .dropStmt = delq->data));
12509 :
12510 : /* Dump Type Comments and Security Labels */
12511 117 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12512 50 : dumpComment(fout, "TYPE", qtypname,
12513 50 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12514 50 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12515 :
12516 117 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12517 0 : dumpSecLabel(fout, "TYPE", qtypname,
12518 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12519 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12520 :
12521 117 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12522 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12523 : qtypname, NULL,
12524 32 : tyinfo->dobj.namespace->dobj.name,
12525 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12526 :
12527 117 : PQclear(res);
12528 117 : destroyPQExpBuffer(q);
12529 117 : destroyPQExpBuffer(delq);
12530 117 : destroyPQExpBuffer(query);
12531 117 : free(qtypname);
12532 117 : free(qualtypname);
12533 117 : }
12534 :
12535 : /*
12536 : * dumpUndefinedType
12537 : * writes out to fout the queries to recreate a !typisdefined type
12538 : *
12539 : * This is a shell type, but we use different terminology to distinguish
12540 : * this case from where we have to emit a shell type definition to break
12541 : * circular dependencies. An undefined type shouldn't ever have anything
12542 : * depending on it.
12543 : */
12544 : static void
12545 37 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12546 : {
12547 37 : DumpOptions *dopt = fout->dopt;
12548 37 : PQExpBuffer q = createPQExpBuffer();
12549 37 : PQExpBuffer delq = createPQExpBuffer();
12550 : char *qtypname;
12551 : char *qualtypname;
12552 :
12553 37 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12554 37 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12555 :
12556 37 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12557 :
12558 37 : if (dopt->binary_upgrade)
12559 2 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12560 2 : tyinfo->dobj.catId.oid,
12561 : false, false);
12562 :
12563 37 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12564 : qualtypname);
12565 :
12566 37 : if (dopt->binary_upgrade)
12567 2 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12568 : "TYPE", qtypname,
12569 2 : tyinfo->dobj.namespace->dobj.name);
12570 :
12571 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12572 37 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12573 37 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12574 : .namespace = tyinfo->dobj.namespace->dobj.name,
12575 : .owner = tyinfo->rolname,
12576 : .description = "TYPE",
12577 : .section = SECTION_PRE_DATA,
12578 : .createStmt = q->data,
12579 : .dropStmt = delq->data));
12580 :
12581 : /* Dump Type Comments and Security Labels */
12582 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12583 32 : dumpComment(fout, "TYPE", qtypname,
12584 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12585 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12586 :
12587 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12588 0 : dumpSecLabel(fout, "TYPE", qtypname,
12589 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12590 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12591 :
12592 37 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12593 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12594 : qtypname, NULL,
12595 0 : tyinfo->dobj.namespace->dobj.name,
12596 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12597 :
12598 37 : destroyPQExpBuffer(q);
12599 37 : destroyPQExpBuffer(delq);
12600 37 : free(qtypname);
12601 37 : free(qualtypname);
12602 37 : }
12603 :
12604 : /*
12605 : * dumpBaseType
12606 : * writes out to fout the queries to recreate a user-defined base type
12607 : */
12608 : static void
12609 283 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12610 : {
12611 283 : DumpOptions *dopt = fout->dopt;
12612 283 : PQExpBuffer q = createPQExpBuffer();
12613 283 : PQExpBuffer delq = createPQExpBuffer();
12614 283 : PQExpBuffer query = createPQExpBuffer();
12615 : PGresult *res;
12616 : char *qtypname;
12617 : char *qualtypname;
12618 : char *typlen;
12619 : char *typinput;
12620 : char *typoutput;
12621 : char *typreceive;
12622 : char *typsend;
12623 : char *typmodin;
12624 : char *typmodout;
12625 : char *typanalyze;
12626 : char *typsubscript;
12627 : Oid typreceiveoid;
12628 : Oid typsendoid;
12629 : Oid typmodinoid;
12630 : Oid typmodoutoid;
12631 : Oid typanalyzeoid;
12632 : Oid typsubscriptoid;
12633 : char *typcategory;
12634 : char *typispreferred;
12635 : char *typdelim;
12636 : char *typbyval;
12637 : char *typalign;
12638 : char *typstorage;
12639 : char *typcollatable;
12640 : char *typdefault;
12641 283 : bool typdefault_is_literal = false;
12642 :
12643 283 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12644 : {
12645 : /* Set up query for type-specific details */
12646 40 : appendPQExpBufferStr(query,
12647 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12648 : "SELECT typlen, "
12649 : "typinput, typoutput, typreceive, typsend, "
12650 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12651 : "typsend::pg_catalog.oid AS typsendoid, "
12652 : "typanalyze, "
12653 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12654 : "typdelim, typbyval, typalign, typstorage, "
12655 : "typmodin, typmodout, "
12656 : "typmodin::pg_catalog.oid AS typmodinoid, "
12657 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12658 : "typcategory, typispreferred, "
12659 : "(typcollation <> 0) AS typcollatable, "
12660 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12661 :
12662 40 : if (fout->remoteVersion >= 140000)
12663 40 : appendPQExpBufferStr(query,
12664 : "typsubscript, "
12665 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12666 : else
12667 0 : appendPQExpBufferStr(query,
12668 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12669 :
12670 40 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12671 : "WHERE oid = $1");
12672 :
12673 40 : ExecuteSqlStatement(fout, query->data);
12674 :
12675 40 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12676 : }
12677 :
12678 283 : printfPQExpBuffer(query,
12679 : "EXECUTE dumpBaseType('%u')",
12680 283 : tyinfo->dobj.catId.oid);
12681 :
12682 283 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12683 :
12684 283 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12685 283 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12686 283 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12687 283 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12688 283 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12689 283 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12690 283 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12691 283 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12692 283 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12693 283 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12694 283 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12695 283 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12696 283 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12697 283 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12698 283 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12699 283 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12700 283 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12701 283 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12702 283 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12703 283 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12704 283 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12705 283 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12706 283 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12707 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12708 283 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12709 : {
12710 42 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12711 42 : typdefault_is_literal = true; /* it needs quotes */
12712 : }
12713 : else
12714 241 : typdefault = NULL;
12715 :
12716 283 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12717 283 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12718 :
12719 : /*
12720 : * The reason we include CASCADE is that the circular dependency between
12721 : * the type and its I/O functions makes it impossible to drop the type any
12722 : * other way.
12723 : */
12724 283 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12725 :
12726 : /*
12727 : * We might already have a shell type, but setting pg_type_oid is
12728 : * harmless, and in any case we'd better set the array type OID.
12729 : */
12730 283 : if (dopt->binary_upgrade)
12731 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12732 8 : tyinfo->dobj.catId.oid,
12733 : false, false);
12734 :
12735 283 : appendPQExpBuffer(q,
12736 : "CREATE TYPE %s (\n"
12737 : " INTERNALLENGTH = %s",
12738 : qualtypname,
12739 283 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12740 :
12741 : /* regproc result is sufficiently quoted already */
12742 283 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12743 283 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12744 283 : if (OidIsValid(typreceiveoid))
12745 210 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12746 283 : if (OidIsValid(typsendoid))
12747 210 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12748 283 : if (OidIsValid(typmodinoid))
12749 35 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12750 283 : if (OidIsValid(typmodoutoid))
12751 35 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12752 283 : if (OidIsValid(typanalyzeoid))
12753 3 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12754 :
12755 283 : if (strcmp(typcollatable, "t") == 0)
12756 30 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12757 :
12758 283 : if (typdefault != NULL)
12759 : {
12760 42 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12761 42 : if (typdefault_is_literal)
12762 42 : appendStringLiteralAH(q, typdefault, fout);
12763 : else
12764 0 : appendPQExpBufferStr(q, typdefault);
12765 : }
12766 :
12767 283 : if (OidIsValid(typsubscriptoid))
12768 29 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12769 :
12770 283 : if (OidIsValid(tyinfo->typelem))
12771 26 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12772 26 : getFormattedTypeName(fout, tyinfo->typelem,
12773 : zeroIsError));
12774 :
12775 283 : if (strcmp(typcategory, "U") != 0)
12776 : {
12777 161 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12778 161 : appendStringLiteralAH(q, typcategory, fout);
12779 : }
12780 :
12781 283 : if (strcmp(typispreferred, "t") == 0)
12782 29 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12783 :
12784 283 : if (typdelim && strcmp(typdelim, ",") != 0)
12785 : {
12786 3 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12787 3 : appendStringLiteralAH(q, typdelim, fout);
12788 : }
12789 :
12790 283 : if (*typalign == TYPALIGN_CHAR)
12791 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12792 271 : else if (*typalign == TYPALIGN_SHORT)
12793 6 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12794 265 : else if (*typalign == TYPALIGN_INT)
12795 187 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12796 78 : else if (*typalign == TYPALIGN_DOUBLE)
12797 78 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12798 :
12799 283 : if (*typstorage == TYPSTORAGE_PLAIN)
12800 208 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12801 75 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12802 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12803 75 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12804 66 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12805 9 : else if (*typstorage == TYPSTORAGE_MAIN)
12806 9 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12807 :
12808 283 : if (strcmp(typbyval, "t") == 0)
12809 137 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12810 :
12811 283 : appendPQExpBufferStr(q, "\n);\n");
12812 :
12813 283 : if (dopt->binary_upgrade)
12814 8 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12815 : "TYPE", qtypname,
12816 8 : tyinfo->dobj.namespace->dobj.name);
12817 :
12818 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12819 283 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12820 283 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12821 : .namespace = tyinfo->dobj.namespace->dobj.name,
12822 : .owner = tyinfo->rolname,
12823 : .description = "TYPE",
12824 : .section = SECTION_PRE_DATA,
12825 : .createStmt = q->data,
12826 : .dropStmt = delq->data));
12827 :
12828 : /* Dump Type Comments and Security Labels */
12829 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12830 248 : dumpComment(fout, "TYPE", qtypname,
12831 248 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12832 248 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12833 :
12834 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12835 0 : dumpSecLabel(fout, "TYPE", qtypname,
12836 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12837 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12838 :
12839 283 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12840 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12841 : qtypname, NULL,
12842 32 : tyinfo->dobj.namespace->dobj.name,
12843 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
12844 :
12845 283 : PQclear(res);
12846 283 : destroyPQExpBuffer(q);
12847 283 : destroyPQExpBuffer(delq);
12848 283 : destroyPQExpBuffer(query);
12849 283 : free(qtypname);
12850 283 : free(qualtypname);
12851 283 : }
12852 :
12853 : /*
12854 : * dumpDomain
12855 : * writes out to fout the queries to recreate a user-defined domain
12856 : */
12857 : static void
12858 172 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12859 : {
12860 172 : DumpOptions *dopt = fout->dopt;
12861 172 : PQExpBuffer q = createPQExpBuffer();
12862 172 : PQExpBuffer delq = createPQExpBuffer();
12863 172 : PQExpBuffer query = createPQExpBuffer();
12864 : PGresult *res;
12865 : int i;
12866 : char *qtypname;
12867 : char *qualtypname;
12868 : char *typnotnull;
12869 : char *typdefn;
12870 : char *typdefault;
12871 : Oid typcollation;
12872 172 : bool typdefault_is_literal = false;
12873 :
12874 172 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12875 : {
12876 : /* Set up query for domain-specific details */
12877 37 : appendPQExpBufferStr(query,
12878 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12879 :
12880 37 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12881 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12882 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12883 : "t.typdefault, "
12884 : "CASE WHEN t.typcollation <> u.typcollation "
12885 : "THEN t.typcollation ELSE 0 END AS typcollation "
12886 : "FROM pg_catalog.pg_type t "
12887 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12888 : "WHERE t.oid = $1");
12889 :
12890 37 : ExecuteSqlStatement(fout, query->data);
12891 :
12892 37 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12893 : }
12894 :
12895 172 : printfPQExpBuffer(query,
12896 : "EXECUTE dumpDomain('%u')",
12897 172 : tyinfo->dobj.catId.oid);
12898 :
12899 172 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12900 :
12901 172 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12902 172 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12903 172 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12904 37 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12905 135 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12906 : {
12907 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12908 0 : typdefault_is_literal = true; /* it needs quotes */
12909 : }
12910 : else
12911 135 : typdefault = NULL;
12912 172 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12913 :
12914 172 : if (dopt->binary_upgrade)
12915 29 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12916 29 : tyinfo->dobj.catId.oid,
12917 : true, /* force array type */
12918 : false); /* force multirange type */
12919 :
12920 172 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12921 172 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12922 :
12923 172 : appendPQExpBuffer(q,
12924 : "CREATE DOMAIN %s AS %s",
12925 : qualtypname,
12926 : typdefn);
12927 :
12928 : /* Print collation only if different from base type's collation */
12929 172 : if (OidIsValid(typcollation))
12930 : {
12931 : CollInfo *coll;
12932 :
12933 32 : coll = findCollationByOid(typcollation);
12934 32 : if (coll)
12935 32 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12936 : }
12937 :
12938 : /*
12939 : * Print a not-null constraint if there's one. In servers older than 17
12940 : * these don't have names, so just print it unadorned; in newer ones they
12941 : * do, but most of the time it's going to be the standard generated one,
12942 : * so omit the name in that case also.
12943 : */
12944 172 : if (typnotnull[0] == 't')
12945 : {
12946 47 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12947 0 : appendPQExpBufferStr(q, " NOT NULL");
12948 : else
12949 : {
12950 47 : ConstraintInfo *notnull = tyinfo->notnull;
12951 :
12952 47 : if (!notnull->separate)
12953 : {
12954 : char *default_name;
12955 :
12956 : /* XXX should match ChooseConstraintName better */
12957 47 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12958 :
12959 47 : if (strcmp(default_name, notnull->dobj.name) == 0)
12960 15 : appendPQExpBufferStr(q, " NOT NULL");
12961 : else
12962 32 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12963 32 : fmtId(notnull->dobj.name), notnull->condef);
12964 47 : free(default_name);
12965 : }
12966 : }
12967 : }
12968 :
12969 172 : if (typdefault != NULL)
12970 : {
12971 37 : appendPQExpBufferStr(q, " DEFAULT ");
12972 37 : if (typdefault_is_literal)
12973 0 : appendStringLiteralAH(q, typdefault, fout);
12974 : else
12975 37 : appendPQExpBufferStr(q, typdefault);
12976 : }
12977 :
12978 172 : PQclear(res);
12979 :
12980 : /*
12981 : * Add any CHECK constraints for the domain
12982 : */
12983 299 : for (i = 0; i < tyinfo->nDomChecks; i++)
12984 : {
12985 127 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12986 :
12987 127 : if (!domcheck->separate && domcheck->contype == 'c')
12988 122 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12989 122 : fmtId(domcheck->dobj.name), domcheck->condef);
12990 : }
12991 :
12992 172 : appendPQExpBufferStr(q, ";\n");
12993 :
12994 172 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12995 :
12996 172 : if (dopt->binary_upgrade)
12997 29 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12998 : "DOMAIN", qtypname,
12999 29 : tyinfo->dobj.namespace->dobj.name);
13000 :
13001 172 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13002 172 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13003 172 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13004 : .namespace = tyinfo->dobj.namespace->dobj.name,
13005 : .owner = tyinfo->rolname,
13006 : .description = "DOMAIN",
13007 : .section = SECTION_PRE_DATA,
13008 : .createStmt = q->data,
13009 : .dropStmt = delq->data));
13010 :
13011 : /* Dump Domain Comments and Security Labels */
13012 172 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13013 0 : dumpComment(fout, "DOMAIN", qtypname,
13014 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13015 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13016 :
13017 172 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13018 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
13019 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13020 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13021 :
13022 172 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13023 32 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13024 : qtypname, NULL,
13025 32 : tyinfo->dobj.namespace->dobj.name,
13026 32 : NULL, tyinfo->rolname, &tyinfo->dacl);
13027 :
13028 : /* Dump any per-constraint comments */
13029 299 : for (i = 0; i < tyinfo->nDomChecks; i++)
13030 : {
13031 127 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
13032 : PQExpBuffer conprefix;
13033 :
13034 : /* but only if the constraint itself was dumped here */
13035 127 : if (domcheck->separate)
13036 5 : continue;
13037 :
13038 122 : conprefix = createPQExpBuffer();
13039 122 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13040 122 : fmtId(domcheck->dobj.name));
13041 :
13042 122 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
13043 32 : dumpComment(fout, conprefix->data, qtypname,
13044 32 : tyinfo->dobj.namespace->dobj.name,
13045 32 : tyinfo->rolname,
13046 32 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
13047 :
13048 122 : destroyPQExpBuffer(conprefix);
13049 : }
13050 :
13051 : /*
13052 : * And a comment on the not-null constraint, if there's one -- but only if
13053 : * the constraint itself was dumped here
13054 : */
13055 172 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
13056 : {
13057 47 : PQExpBuffer conprefix = createPQExpBuffer();
13058 :
13059 47 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13060 47 : fmtId(tyinfo->notnull->dobj.name));
13061 :
13062 47 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
13063 32 : dumpComment(fout, conprefix->data, qtypname,
13064 32 : tyinfo->dobj.namespace->dobj.name,
13065 32 : tyinfo->rolname,
13066 32 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
13067 47 : destroyPQExpBuffer(conprefix);
13068 : }
13069 :
13070 172 : destroyPQExpBuffer(q);
13071 172 : destroyPQExpBuffer(delq);
13072 172 : destroyPQExpBuffer(query);
13073 172 : free(qtypname);
13074 172 : free(qualtypname);
13075 172 : }
13076 :
13077 : /*
13078 : * dumpCompositeType
13079 : * writes out to fout the queries to recreate a user-defined stand-alone
13080 : * composite type
13081 : */
13082 : static void
13083 130 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
13084 : {
13085 130 : DumpOptions *dopt = fout->dopt;
13086 130 : PQExpBuffer q = createPQExpBuffer();
13087 130 : PQExpBuffer dropped = createPQExpBuffer();
13088 130 : PQExpBuffer delq = createPQExpBuffer();
13089 130 : PQExpBuffer query = createPQExpBuffer();
13090 : PGresult *res;
13091 : char *qtypname;
13092 : char *qualtypname;
13093 : int ntups;
13094 : int i_attname;
13095 : int i_atttypdefn;
13096 : int i_attlen;
13097 : int i_attalign;
13098 : int i_attisdropped;
13099 : int i_attcollation;
13100 : int i;
13101 : int actual_atts;
13102 :
13103 130 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
13104 : {
13105 : /*
13106 : * Set up query for type-specific details.
13107 : *
13108 : * Since we only want to dump COLLATE clauses for attributes whose
13109 : * collation is different from their type's default, we use a CASE
13110 : * here to suppress uninteresting attcollations cheaply. atttypid
13111 : * will be 0 for dropped columns; collation does not matter for those.
13112 : */
13113 55 : appendPQExpBufferStr(query,
13114 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
13115 : "SELECT a.attname, a.attnum, "
13116 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
13117 : "a.attlen, a.attalign, a.attisdropped, "
13118 : "CASE WHEN a.attcollation <> at.typcollation "
13119 : "THEN a.attcollation ELSE 0 END AS attcollation "
13120 : "FROM pg_catalog.pg_type ct "
13121 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
13122 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
13123 : "WHERE ct.oid = $1 "
13124 : "ORDER BY a.attnum");
13125 :
13126 55 : ExecuteSqlStatement(fout, query->data);
13127 :
13128 55 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
13129 : }
13130 :
13131 130 : printfPQExpBuffer(query,
13132 : "EXECUTE dumpCompositeType('%u')",
13133 130 : tyinfo->dobj.catId.oid);
13134 :
13135 130 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13136 :
13137 130 : ntups = PQntuples(res);
13138 :
13139 130 : i_attname = PQfnumber(res, "attname");
13140 130 : i_atttypdefn = PQfnumber(res, "atttypdefn");
13141 130 : i_attlen = PQfnumber(res, "attlen");
13142 130 : i_attalign = PQfnumber(res, "attalign");
13143 130 : i_attisdropped = PQfnumber(res, "attisdropped");
13144 130 : i_attcollation = PQfnumber(res, "attcollation");
13145 :
13146 130 : if (dopt->binary_upgrade)
13147 : {
13148 18 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13149 18 : tyinfo->dobj.catId.oid,
13150 : false, false);
13151 18 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
13152 : }
13153 :
13154 130 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
13155 130 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
13156 :
13157 130 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13158 : qualtypname);
13159 :
13160 130 : actual_atts = 0;
13161 412 : for (i = 0; i < ntups; i++)
13162 : {
13163 : char *attname;
13164 : char *atttypdefn;
13165 : char *attlen;
13166 : char *attalign;
13167 : bool attisdropped;
13168 : Oid attcollation;
13169 :
13170 282 : attname = PQgetvalue(res, i, i_attname);
13171 282 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13172 282 : attlen = PQgetvalue(res, i, i_attlen);
13173 282 : attalign = PQgetvalue(res, i, i_attalign);
13174 282 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13175 282 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13176 :
13177 282 : if (attisdropped && !dopt->binary_upgrade)
13178 8 : continue;
13179 :
13180 : /* Format properly if not first attr */
13181 274 : if (actual_atts++ > 0)
13182 144 : appendPQExpBufferChar(q, ',');
13183 274 : appendPQExpBufferStr(q, "\n\t");
13184 :
13185 274 : if (!attisdropped)
13186 : {
13187 272 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13188 :
13189 : /* Add collation if not default for the column type */
13190 272 : if (OidIsValid(attcollation))
13191 : {
13192 : CollInfo *coll;
13193 :
13194 0 : coll = findCollationByOid(attcollation);
13195 0 : if (coll)
13196 0 : appendPQExpBuffer(q, " COLLATE %s",
13197 0 : fmtQualifiedDumpable(coll));
13198 : }
13199 : }
13200 : else
13201 : {
13202 : /*
13203 : * This is a dropped attribute and we're in binary_upgrade mode.
13204 : * Insert a placeholder for it in the CREATE TYPE command, and set
13205 : * length and alignment with direct UPDATE to the catalogs
13206 : * afterwards. See similar code in dumpTableSchema().
13207 : */
13208 2 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13209 :
13210 : /* stash separately for insertion after the CREATE TYPE */
13211 2 : appendPQExpBufferStr(dropped,
13212 : "\n-- For binary upgrade, recreate dropped column.\n");
13213 2 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13214 : "SET attlen = %s, "
13215 : "attalign = '%s', attbyval = false\n"
13216 : "WHERE attname = ", attlen, attalign);
13217 2 : appendStringLiteralAH(dropped, attname, fout);
13218 2 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13219 2 : appendStringLiteralAH(dropped, qualtypname, fout);
13220 2 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13221 :
13222 2 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13223 : qualtypname);
13224 2 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13225 : fmtId(attname));
13226 : }
13227 : }
13228 130 : appendPQExpBufferStr(q, "\n);\n");
13229 130 : appendPQExpBufferStr(q, dropped->data);
13230 :
13231 130 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13232 :
13233 130 : if (dopt->binary_upgrade)
13234 18 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13235 : "TYPE", qtypname,
13236 18 : tyinfo->dobj.namespace->dobj.name);
13237 :
13238 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13239 113 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13240 113 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13241 : .namespace = tyinfo->dobj.namespace->dobj.name,
13242 : .owner = tyinfo->rolname,
13243 : .description = "TYPE",
13244 : .section = SECTION_PRE_DATA,
13245 : .createStmt = q->data,
13246 : .dropStmt = delq->data));
13247 :
13248 :
13249 : /* Dump Type Comments and Security Labels */
13250 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13251 32 : dumpComment(fout, "TYPE", qtypname,
13252 32 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13253 32 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13254 :
13255 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13256 0 : dumpSecLabel(fout, "TYPE", qtypname,
13257 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13258 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13259 :
13260 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13261 18 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13262 : qtypname, NULL,
13263 18 : tyinfo->dobj.namespace->dobj.name,
13264 18 : NULL, tyinfo->rolname, &tyinfo->dacl);
13265 :
13266 : /* Dump any per-column comments */
13267 130 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13268 32 : dumpCompositeTypeColComments(fout, tyinfo, res);
13269 :
13270 130 : PQclear(res);
13271 130 : destroyPQExpBuffer(q);
13272 130 : destroyPQExpBuffer(dropped);
13273 130 : destroyPQExpBuffer(delq);
13274 130 : destroyPQExpBuffer(query);
13275 130 : free(qtypname);
13276 130 : free(qualtypname);
13277 130 : }
13278 :
13279 : /*
13280 : * dumpCompositeTypeColComments
13281 : * writes out to fout the queries to recreate comments on the columns of
13282 : * a user-defined stand-alone composite type.
13283 : *
13284 : * The caller has already made a query to collect the names and attnums
13285 : * of the type's columns, so we just pass that result into here rather
13286 : * than reading them again.
13287 : */
13288 : static void
13289 32 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13290 : PGresult *res)
13291 : {
13292 : CommentItem *comments;
13293 : int ncomments;
13294 : PQExpBuffer query;
13295 : PQExpBuffer target;
13296 : int i;
13297 : int ntups;
13298 : int i_attname;
13299 : int i_attnum;
13300 : int i_attisdropped;
13301 :
13302 : /* do nothing, if --no-comments is supplied */
13303 32 : if (fout->dopt->no_comments)
13304 0 : return;
13305 :
13306 : /* Search for comments associated with type's pg_class OID */
13307 32 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13308 : &comments);
13309 :
13310 : /* If no comments exist, we're done */
13311 32 : if (ncomments <= 0)
13312 0 : return;
13313 :
13314 : /* Build COMMENT ON statements */
13315 32 : query = createPQExpBuffer();
13316 32 : target = createPQExpBuffer();
13317 :
13318 32 : ntups = PQntuples(res);
13319 32 : i_attnum = PQfnumber(res, "attnum");
13320 32 : i_attname = PQfnumber(res, "attname");
13321 32 : i_attisdropped = PQfnumber(res, "attisdropped");
13322 64 : while (ncomments > 0)
13323 : {
13324 : const char *attname;
13325 :
13326 32 : attname = NULL;
13327 32 : for (i = 0; i < ntups; i++)
13328 : {
13329 32 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13330 32 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13331 : {
13332 32 : attname = PQgetvalue(res, i, i_attname);
13333 32 : break;
13334 : }
13335 : }
13336 32 : if (attname) /* just in case we don't find it */
13337 : {
13338 32 : const char *descr = comments->descr;
13339 :
13340 32 : resetPQExpBuffer(target);
13341 32 : appendPQExpBuffer(target, "COLUMN %s.",
13342 32 : fmtId(tyinfo->dobj.name));
13343 32 : appendPQExpBufferStr(target, fmtId(attname));
13344 :
13345 32 : resetPQExpBuffer(query);
13346 32 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13347 32 : fmtQualifiedDumpable(tyinfo));
13348 32 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13349 32 : appendStringLiteralAH(query, descr, fout);
13350 32 : appendPQExpBufferStr(query, ";\n");
13351 :
13352 32 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13353 32 : ARCHIVE_OPTS(.tag = target->data,
13354 : .namespace = tyinfo->dobj.namespace->dobj.name,
13355 : .owner = tyinfo->rolname,
13356 : .description = "COMMENT",
13357 : .section = SECTION_NONE,
13358 : .createStmt = query->data,
13359 : .deps = &(tyinfo->dobj.dumpId),
13360 : .nDeps = 1));
13361 : }
13362 :
13363 32 : comments++;
13364 32 : ncomments--;
13365 : }
13366 :
13367 32 : destroyPQExpBuffer(query);
13368 32 : destroyPQExpBuffer(target);
13369 : }
13370 :
13371 : /*
13372 : * dumpShellType
13373 : * writes out to fout the queries to create a shell type
13374 : *
13375 : * We dump a shell definition in advance of the I/O functions for the type.
13376 : */
13377 : static void
13378 73 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13379 : {
13380 73 : DumpOptions *dopt = fout->dopt;
13381 : PQExpBuffer q;
13382 :
13383 : /* Do nothing if not dumping schema */
13384 73 : if (!dopt->dumpSchema)
13385 6 : return;
13386 :
13387 67 : q = createPQExpBuffer();
13388 :
13389 : /*
13390 : * Note the lack of a DROP command for the shell type; any required DROP
13391 : * is driven off the base type entry, instead. This interacts with
13392 : * _printTocEntry()'s use of the presence of a DROP command to decide
13393 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13394 : * the shell type's owner immediately on creation; that should happen only
13395 : * after it's filled in, otherwise the backend complains.
13396 : */
13397 :
13398 67 : if (dopt->binary_upgrade)
13399 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13400 8 : stinfo->baseType->dobj.catId.oid,
13401 : false, false);
13402 :
13403 67 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13404 67 : fmtQualifiedDumpable(stinfo));
13405 :
13406 67 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13407 67 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13408 67 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13409 : .namespace = stinfo->dobj.namespace->dobj.name,
13410 : .owner = stinfo->baseType->rolname,
13411 : .description = "SHELL TYPE",
13412 : .section = SECTION_PRE_DATA,
13413 : .createStmt = q->data));
13414 :
13415 67 : destroyPQExpBuffer(q);
13416 : }
13417 :
13418 : /*
13419 : * dumpProcLang
13420 : * writes out to fout the queries to recreate a user-defined
13421 : * procedural language
13422 : */
13423 : static void
13424 82 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13425 : {
13426 82 : DumpOptions *dopt = fout->dopt;
13427 : PQExpBuffer defqry;
13428 : PQExpBuffer delqry;
13429 : bool useParams;
13430 : char *qlanname;
13431 : FuncInfo *funcInfo;
13432 82 : FuncInfo *inlineInfo = NULL;
13433 82 : FuncInfo *validatorInfo = NULL;
13434 :
13435 : /* Do nothing if not dumping schema */
13436 82 : if (!dopt->dumpSchema)
13437 13 : return;
13438 :
13439 : /*
13440 : * Try to find the support function(s). It is not an error if we don't
13441 : * find them --- if the functions are in the pg_catalog schema, as is
13442 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13443 : * we will emit a parameterless CREATE LANGUAGE command, which will
13444 : * require PL template knowledge in the backend to reload.)
13445 : */
13446 :
13447 69 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13448 69 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13449 2 : funcInfo = NULL; /* treat not-dumped same as not-found */
13450 :
13451 69 : if (OidIsValid(plang->laninline))
13452 : {
13453 38 : inlineInfo = findFuncByOid(plang->laninline);
13454 38 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13455 1 : inlineInfo = NULL;
13456 : }
13457 :
13458 69 : if (OidIsValid(plang->lanvalidator))
13459 : {
13460 38 : validatorInfo = findFuncByOid(plang->lanvalidator);
13461 38 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13462 1 : validatorInfo = NULL;
13463 : }
13464 :
13465 : /*
13466 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13467 : * parameters. Otherwise, we'll write a parameterless command, which will
13468 : * be interpreted as CREATE EXTENSION.
13469 : */
13470 30 : useParams = (funcInfo != NULL &&
13471 129 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13472 30 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13473 :
13474 69 : defqry = createPQExpBuffer();
13475 69 : delqry = createPQExpBuffer();
13476 :
13477 69 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13478 :
13479 69 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13480 : qlanname);
13481 :
13482 69 : if (useParams)
13483 : {
13484 30 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13485 30 : plang->lanpltrusted ? "TRUSTED " : "",
13486 : qlanname);
13487 30 : appendPQExpBuffer(defqry, " HANDLER %s",
13488 30 : fmtQualifiedDumpable(funcInfo));
13489 30 : if (OidIsValid(plang->laninline))
13490 0 : appendPQExpBuffer(defqry, " INLINE %s",
13491 0 : fmtQualifiedDumpable(inlineInfo));
13492 30 : if (OidIsValid(plang->lanvalidator))
13493 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13494 0 : fmtQualifiedDumpable(validatorInfo));
13495 : }
13496 : else
13497 : {
13498 : /*
13499 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13500 : * command will not fail if the language is preinstalled in the target
13501 : * database.
13502 : *
13503 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13504 : * EXISTS; perhaps we should emit that instead? But it might just add
13505 : * confusion.
13506 : */
13507 39 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13508 : qlanname);
13509 : }
13510 69 : appendPQExpBufferStr(defqry, ";\n");
13511 :
13512 69 : if (dopt->binary_upgrade)
13513 2 : binary_upgrade_extension_member(defqry, &plang->dobj,
13514 : "LANGUAGE", qlanname, NULL);
13515 :
13516 69 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13517 31 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13518 31 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13519 : .owner = plang->lanowner,
13520 : .description = "PROCEDURAL LANGUAGE",
13521 : .section = SECTION_PRE_DATA,
13522 : .createStmt = defqry->data,
13523 : .dropStmt = delqry->data,
13524 : ));
13525 :
13526 : /* Dump Proc Lang Comments and Security Labels */
13527 69 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13528 0 : dumpComment(fout, "LANGUAGE", qlanname,
13529 0 : NULL, plang->lanowner,
13530 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13531 :
13532 69 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13533 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13534 0 : NULL, plang->lanowner,
13535 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13536 :
13537 69 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13538 38 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13539 : qlanname, NULL, NULL,
13540 38 : NULL, plang->lanowner, &plang->dacl);
13541 :
13542 69 : free(qlanname);
13543 :
13544 69 : destroyPQExpBuffer(defqry);
13545 69 : destroyPQExpBuffer(delqry);
13546 : }
13547 :
13548 : /*
13549 : * format_function_arguments: generate function name and argument list
13550 : *
13551 : * This is used when we can rely on pg_get_function_arguments to format
13552 : * the argument list. Note, however, that pg_get_function_arguments
13553 : * does not special-case zero-argument aggregates.
13554 : */
13555 : static char *
13556 4242 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13557 : {
13558 : PQExpBufferData fn;
13559 :
13560 4242 : initPQExpBuffer(&fn);
13561 4242 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13562 4242 : if (is_agg && finfo->nargs == 0)
13563 80 : appendPQExpBufferStr(&fn, "(*)");
13564 : else
13565 4162 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13566 4242 : return fn.data;
13567 : }
13568 :
13569 : /*
13570 : * format_function_signature: generate function name and argument list
13571 : *
13572 : * Only a minimal list of input argument types is generated; this is
13573 : * sufficient to reference the function, but not to define it.
13574 : *
13575 : * If honor_quotes is false then the function name is never quoted.
13576 : * This is appropriate for use in TOC tags, but not in SQL commands.
13577 : */
13578 : static char *
13579 2229 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13580 : {
13581 : PQExpBufferData fn;
13582 : int j;
13583 :
13584 2229 : initPQExpBuffer(&fn);
13585 2229 : if (honor_quotes)
13586 393 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13587 : else
13588 1836 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13589 4100 : for (j = 0; j < finfo->nargs; j++)
13590 : {
13591 1871 : if (j > 0)
13592 452 : appendPQExpBufferStr(&fn, ", ");
13593 :
13594 1871 : appendPQExpBufferStr(&fn,
13595 1871 : getFormattedTypeName(fout, finfo->argtypes[j],
13596 : zeroIsError));
13597 : }
13598 2229 : appendPQExpBufferChar(&fn, ')');
13599 2229 : return fn.data;
13600 : }
13601 :
13602 :
13603 : /*
13604 : * dumpFunc:
13605 : * dump out one function
13606 : */
13607 : static void
13608 1898 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13609 : {
13610 1898 : DumpOptions *dopt = fout->dopt;
13611 : PQExpBuffer query;
13612 : PQExpBuffer q;
13613 : PQExpBuffer delqry;
13614 : PQExpBuffer asPart;
13615 : PGresult *res;
13616 : char *funcsig; /* identity signature */
13617 1898 : char *funcfullsig = NULL; /* full signature */
13618 : char *funcsig_tag;
13619 : char *qual_funcsig;
13620 : char *proretset;
13621 : char *prosrc;
13622 : char *probin;
13623 : char *prosqlbody;
13624 : char *funcargs;
13625 : char *funciargs;
13626 : char *funcresult;
13627 : char *protrftypes;
13628 : char *prokind;
13629 : char *provolatile;
13630 : char *proisstrict;
13631 : char *prosecdef;
13632 : char *proleakproof;
13633 : char *proconfig;
13634 : char *procost;
13635 : char *prorows;
13636 : char *prosupport;
13637 : char *proparallel;
13638 : char *lanname;
13639 1898 : char **configitems = NULL;
13640 1898 : int nconfigitems = 0;
13641 : const char *keyword;
13642 :
13643 : /* Do nothing if not dumping schema */
13644 1898 : if (!dopt->dumpSchema)
13645 62 : return;
13646 :
13647 1836 : query = createPQExpBuffer();
13648 1836 : q = createPQExpBuffer();
13649 1836 : delqry = createPQExpBuffer();
13650 1836 : asPart = createPQExpBuffer();
13651 :
13652 1836 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13653 : {
13654 : /* Set up query for function-specific details */
13655 76 : appendPQExpBufferStr(query,
13656 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13657 :
13658 76 : appendPQExpBufferStr(query,
13659 : "SELECT\n"
13660 : "proretset,\n"
13661 : "prosrc,\n"
13662 : "probin,\n"
13663 : "provolatile,\n"
13664 : "proisstrict,\n"
13665 : "prosecdef,\n"
13666 : "lanname,\n"
13667 : "proconfig,\n"
13668 : "procost,\n"
13669 : "prorows,\n"
13670 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13671 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13672 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13673 : "proleakproof,\n");
13674 :
13675 76 : if (fout->remoteVersion >= 90500)
13676 76 : appendPQExpBufferStr(query,
13677 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13678 : else
13679 0 : appendPQExpBufferStr(query,
13680 : "NULL AS protrftypes,\n");
13681 :
13682 76 : if (fout->remoteVersion >= 90600)
13683 76 : appendPQExpBufferStr(query,
13684 : "proparallel,\n");
13685 : else
13686 0 : appendPQExpBufferStr(query,
13687 : "'u' AS proparallel,\n");
13688 :
13689 76 : if (fout->remoteVersion >= 110000)
13690 76 : appendPQExpBufferStr(query,
13691 : "prokind,\n");
13692 : else
13693 0 : appendPQExpBufferStr(query,
13694 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13695 :
13696 76 : if (fout->remoteVersion >= 120000)
13697 76 : appendPQExpBufferStr(query,
13698 : "prosupport,\n");
13699 : else
13700 0 : appendPQExpBufferStr(query,
13701 : "'-' AS prosupport,\n");
13702 :
13703 76 : if (fout->remoteVersion >= 140000)
13704 76 : appendPQExpBufferStr(query,
13705 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13706 : else
13707 0 : appendPQExpBufferStr(query,
13708 : "NULL AS prosqlbody\n");
13709 :
13710 76 : appendPQExpBufferStr(query,
13711 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13712 : "WHERE p.oid = $1 "
13713 : "AND l.oid = p.prolang");
13714 :
13715 76 : ExecuteSqlStatement(fout, query->data);
13716 :
13717 76 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13718 : }
13719 :
13720 1836 : printfPQExpBuffer(query,
13721 : "EXECUTE dumpFunc('%u')",
13722 1836 : finfo->dobj.catId.oid);
13723 :
13724 1836 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13725 :
13726 1836 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13727 1836 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13728 : {
13729 1788 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13730 1788 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13731 1788 : prosqlbody = NULL;
13732 : }
13733 : else
13734 : {
13735 48 : prosrc = NULL;
13736 48 : probin = NULL;
13737 48 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13738 : }
13739 1836 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13740 1836 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13741 1836 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13742 1836 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13743 1836 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13744 1836 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13745 1836 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13746 1836 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13747 1836 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13748 1836 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13749 1836 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13750 1836 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13751 1836 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13752 1836 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13753 1836 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13754 :
13755 : /*
13756 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13757 : * is used.
13758 : */
13759 1836 : if (prosqlbody)
13760 : {
13761 48 : appendPQExpBufferStr(asPart, prosqlbody);
13762 : }
13763 1788 : else if (probin[0] != '\0')
13764 : {
13765 158 : appendPQExpBufferStr(asPart, "AS ");
13766 158 : appendStringLiteralAH(asPart, probin, fout);
13767 158 : if (prosrc[0] != '\0')
13768 : {
13769 158 : appendPQExpBufferStr(asPart, ", ");
13770 :
13771 : /*
13772 : * where we have bin, use dollar quoting if allowed and src
13773 : * contains quote or backslash; else use regular quoting.
13774 : */
13775 158 : if (dopt->disable_dollar_quoting ||
13776 158 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13777 158 : appendStringLiteralAH(asPart, prosrc, fout);
13778 : else
13779 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13780 : }
13781 : }
13782 : else
13783 : {
13784 1630 : appendPQExpBufferStr(asPart, "AS ");
13785 : /* with no bin, dollar quote src unconditionally if allowed */
13786 1630 : if (dopt->disable_dollar_quoting)
13787 0 : appendStringLiteralAH(asPart, prosrc, fout);
13788 : else
13789 1630 : appendStringLiteralDQ(asPart, prosrc, NULL);
13790 : }
13791 :
13792 1836 : if (*proconfig)
13793 : {
13794 15 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13795 0 : pg_fatal("could not parse %s array", "proconfig");
13796 : }
13797 : else
13798 : {
13799 1821 : configitems = NULL;
13800 1821 : nconfigitems = 0;
13801 : }
13802 :
13803 1836 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13804 1836 : funcsig = format_function_arguments(finfo, funciargs, false);
13805 :
13806 1836 : funcsig_tag = format_function_signature(fout, finfo, false);
13807 :
13808 1836 : qual_funcsig = psprintf("%s.%s",
13809 1836 : fmtId(finfo->dobj.namespace->dobj.name),
13810 : funcsig);
13811 :
13812 1836 : if (prokind[0] == PROKIND_PROCEDURE)
13813 92 : keyword = "PROCEDURE";
13814 : else
13815 1744 : keyword = "FUNCTION"; /* works for window functions too */
13816 :
13817 1836 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13818 : keyword, qual_funcsig);
13819 :
13820 3672 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13821 : keyword,
13822 1836 : fmtId(finfo->dobj.namespace->dobj.name),
13823 : funcfullsig ? funcfullsig :
13824 : funcsig);
13825 :
13826 1836 : if (prokind[0] == PROKIND_PROCEDURE)
13827 : /* no result type to output */ ;
13828 1744 : else if (funcresult)
13829 1744 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13830 : else
13831 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13832 0 : (proretset[0] == 't') ? "SETOF " : "",
13833 0 : getFormattedTypeName(fout, finfo->prorettype,
13834 : zeroIsError));
13835 :
13836 1836 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13837 :
13838 1836 : if (*protrftypes)
13839 : {
13840 0 : Oid *typeids = pg_malloc_array(Oid, FUNC_MAX_ARGS);
13841 : int i;
13842 :
13843 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13844 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13845 0 : for (i = 0; typeids[i]; i++)
13846 : {
13847 0 : if (i != 0)
13848 0 : appendPQExpBufferStr(q, ", ");
13849 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13850 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13851 : }
13852 :
13853 0 : free(typeids);
13854 : }
13855 :
13856 1836 : if (prokind[0] == PROKIND_WINDOW)
13857 5 : appendPQExpBufferStr(q, " WINDOW");
13858 :
13859 1836 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13860 : {
13861 351 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13862 330 : appendPQExpBufferStr(q, " IMMUTABLE");
13863 21 : else if (provolatile[0] == PROVOLATILE_STABLE)
13864 21 : appendPQExpBufferStr(q, " STABLE");
13865 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13866 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13867 : finfo->dobj.name);
13868 : }
13869 :
13870 1836 : if (proisstrict[0] == 't')
13871 358 : appendPQExpBufferStr(q, " STRICT");
13872 :
13873 1836 : if (prosecdef[0] == 't')
13874 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13875 :
13876 1836 : if (proleakproof[0] == 't')
13877 10 : appendPQExpBufferStr(q, " LEAKPROOF");
13878 :
13879 : /*
13880 : * COST and ROWS are emitted only if present and not default, so as not to
13881 : * break backwards-compatibility of the dump without need. Keep this code
13882 : * in sync with the defaults in functioncmds.c.
13883 : */
13884 1836 : if (strcmp(procost, "0") != 0)
13885 : {
13886 1836 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13887 : {
13888 : /* default cost is 1 */
13889 387 : if (strcmp(procost, "1") != 0)
13890 0 : appendPQExpBuffer(q, " COST %s", procost);
13891 : }
13892 : else
13893 : {
13894 : /* default cost is 100 */
13895 1449 : if (strcmp(procost, "100") != 0)
13896 11 : appendPQExpBuffer(q, " COST %s", procost);
13897 : }
13898 : }
13899 1836 : if (proretset[0] == 't' &&
13900 192 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13901 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13902 :
13903 1836 : if (strcmp(prosupport, "-") != 0)
13904 : {
13905 : /* We rely on regprocout to provide quoting and qualification */
13906 42 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13907 : }
13908 :
13909 1836 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13910 : {
13911 116 : if (proparallel[0] == PROPARALLEL_SAFE)
13912 111 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13913 5 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13914 5 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13915 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13916 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13917 : finfo->dobj.name);
13918 : }
13919 :
13920 1876 : for (int i = 0; i < nconfigitems; i++)
13921 : {
13922 : /* we feel free to scribble on configitems[] here */
13923 40 : char *configitem = configitems[i];
13924 : char *pos;
13925 :
13926 40 : pos = strchr(configitem, '=');
13927 40 : if (pos == NULL)
13928 0 : continue;
13929 40 : *pos++ = '\0';
13930 40 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13931 :
13932 : /*
13933 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13934 : * by flatten_set_variable_args() before they were put into the
13935 : * proconfig array. However, because the quoting rules used there
13936 : * aren't exactly like SQL's, we have to break the list value apart
13937 : * and then quote the elements as string literals. (The elements may
13938 : * be double-quoted as-is, but we can't just feed them to the SQL
13939 : * parser; it would do the wrong thing with elements that are
13940 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13941 : * case for empty lists.
13942 : *
13943 : * Variables that are not so marked should just be emitted as simple
13944 : * string literals. If the variable is not known to
13945 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13946 : * to use GUC_LIST_QUOTE for extension variables.
13947 : */
13948 40 : if (variable_is_guc_list_quote(configitem))
13949 : {
13950 : char **namelist;
13951 : char **nameptr;
13952 :
13953 : /* Parse string into list of identifiers */
13954 : /* this shouldn't fail really */
13955 15 : if (SplitGUCList(pos, ',', &namelist))
13956 : {
13957 : /* Special case: represent an empty list as NULL */
13958 15 : if (*namelist == NULL)
13959 5 : appendPQExpBufferStr(q, "NULL");
13960 40 : for (nameptr = namelist; *nameptr; nameptr++)
13961 : {
13962 25 : if (nameptr != namelist)
13963 15 : appendPQExpBufferStr(q, ", ");
13964 25 : appendStringLiteralAH(q, *nameptr, fout);
13965 : }
13966 : }
13967 15 : pg_free(namelist);
13968 : }
13969 : else
13970 25 : appendStringLiteralAH(q, pos, fout);
13971 : }
13972 :
13973 1836 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13974 :
13975 1836 : append_depends_on_extension(fout, q, &finfo->dobj,
13976 : "pg_catalog.pg_proc", keyword,
13977 : qual_funcsig);
13978 :
13979 1836 : if (dopt->binary_upgrade)
13980 308 : binary_upgrade_extension_member(q, &finfo->dobj,
13981 : keyword, funcsig,
13982 308 : finfo->dobj.namespace->dobj.name);
13983 :
13984 1836 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13985 1740 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13986 1740 : ARCHIVE_OPTS(.tag = funcsig_tag,
13987 : .namespace = finfo->dobj.namespace->dobj.name,
13988 : .owner = finfo->rolname,
13989 : .description = keyword,
13990 : .section = finfo->postponed_def ?
13991 : SECTION_POST_DATA : SECTION_PRE_DATA,
13992 : .createStmt = q->data,
13993 : .dropStmt = delqry->data));
13994 :
13995 : /* Dump Function Comments and Security Labels */
13996 1836 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13997 9 : dumpComment(fout, keyword, funcsig,
13998 9 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13999 9 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
14000 :
14001 1836 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
14002 0 : dumpSecLabel(fout, keyword, funcsig,
14003 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
14004 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
14005 :
14006 1836 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
14007 109 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
14008 : funcsig, NULL,
14009 109 : finfo->dobj.namespace->dobj.name,
14010 109 : NULL, finfo->rolname, &finfo->dacl);
14011 :
14012 1836 : PQclear(res);
14013 :
14014 1836 : destroyPQExpBuffer(query);
14015 1836 : destroyPQExpBuffer(q);
14016 1836 : destroyPQExpBuffer(delqry);
14017 1836 : destroyPQExpBuffer(asPart);
14018 1836 : free(funcsig);
14019 1836 : free(funcfullsig);
14020 1836 : free(funcsig_tag);
14021 1836 : free(qual_funcsig);
14022 1836 : free(configitems);
14023 : }
14024 :
14025 :
14026 : /*
14027 : * Dump a user-defined cast
14028 : */
14029 : static void
14030 67 : dumpCast(Archive *fout, const CastInfo *cast)
14031 : {
14032 67 : DumpOptions *dopt = fout->dopt;
14033 : PQExpBuffer defqry;
14034 : PQExpBuffer delqry;
14035 : PQExpBuffer labelq;
14036 : PQExpBuffer castargs;
14037 67 : FuncInfo *funcInfo = NULL;
14038 : const char *sourceType;
14039 : const char *targetType;
14040 :
14041 : /* Do nothing if not dumping schema */
14042 67 : if (!dopt->dumpSchema)
14043 6 : return;
14044 :
14045 : /* Cannot dump if we don't have the cast function's info */
14046 61 : if (OidIsValid(cast->castfunc))
14047 : {
14048 36 : funcInfo = findFuncByOid(cast->castfunc);
14049 36 : if (funcInfo == NULL)
14050 0 : pg_fatal("could not find function definition for function with OID %u",
14051 : cast->castfunc);
14052 : }
14053 :
14054 61 : defqry = createPQExpBuffer();
14055 61 : delqry = createPQExpBuffer();
14056 61 : labelq = createPQExpBuffer();
14057 61 : castargs = createPQExpBuffer();
14058 :
14059 61 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
14060 61 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
14061 61 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
14062 : sourceType, targetType);
14063 :
14064 61 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
14065 : sourceType, targetType);
14066 :
14067 61 : switch (cast->castmethod)
14068 : {
14069 25 : case COERCION_METHOD_BINARY:
14070 25 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
14071 25 : break;
14072 0 : case COERCION_METHOD_INOUT:
14073 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
14074 0 : break;
14075 36 : case COERCION_METHOD_FUNCTION:
14076 36 : if (funcInfo)
14077 : {
14078 36 : char *fsig = format_function_signature(fout, funcInfo, true);
14079 :
14080 : /*
14081 : * Always qualify the function name (format_function_signature
14082 : * won't qualify it).
14083 : */
14084 36 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
14085 36 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
14086 36 : free(fsig);
14087 : }
14088 : else
14089 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
14090 36 : break;
14091 0 : default:
14092 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
14093 : }
14094 :
14095 61 : if (cast->castcontext == 'a')
14096 31 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
14097 30 : else if (cast->castcontext == 'i')
14098 10 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
14099 61 : appendPQExpBufferStr(defqry, ";\n");
14100 :
14101 61 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
14102 : sourceType, targetType);
14103 :
14104 61 : appendPQExpBuffer(castargs, "(%s AS %s)",
14105 : sourceType, targetType);
14106 :
14107 61 : if (dopt->binary_upgrade)
14108 7 : binary_upgrade_extension_member(defqry, &cast->dobj,
14109 7 : "CAST", castargs->data, NULL);
14110 :
14111 61 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
14112 61 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
14113 61 : ARCHIVE_OPTS(.tag = labelq->data,
14114 : .description = "CAST",
14115 : .section = SECTION_PRE_DATA,
14116 : .createStmt = defqry->data,
14117 : .dropStmt = delqry->data));
14118 :
14119 : /* Dump Cast Comments */
14120 61 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
14121 0 : dumpComment(fout, "CAST", castargs->data,
14122 : NULL, "",
14123 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
14124 :
14125 61 : destroyPQExpBuffer(defqry);
14126 61 : destroyPQExpBuffer(delqry);
14127 61 : destroyPQExpBuffer(labelq);
14128 61 : destroyPQExpBuffer(castargs);
14129 : }
14130 :
14131 : /*
14132 : * Dump a transform
14133 : */
14134 : static void
14135 42 : dumpTransform(Archive *fout, const TransformInfo *transform)
14136 : {
14137 42 : DumpOptions *dopt = fout->dopt;
14138 : PQExpBuffer defqry;
14139 : PQExpBuffer delqry;
14140 : PQExpBuffer labelq;
14141 : PQExpBuffer transformargs;
14142 42 : FuncInfo *fromsqlFuncInfo = NULL;
14143 42 : FuncInfo *tosqlFuncInfo = NULL;
14144 : char *lanname;
14145 : const char *transformType;
14146 :
14147 : /* Do nothing if not dumping schema */
14148 42 : if (!dopt->dumpSchema)
14149 6 : return;
14150 :
14151 : /* Cannot dump if we don't have the transform functions' info */
14152 36 : if (OidIsValid(transform->trffromsql))
14153 : {
14154 36 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
14155 36 : if (fromsqlFuncInfo == NULL)
14156 0 : pg_fatal("could not find function definition for function with OID %u",
14157 : transform->trffromsql);
14158 : }
14159 36 : if (OidIsValid(transform->trftosql))
14160 : {
14161 36 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
14162 36 : if (tosqlFuncInfo == NULL)
14163 0 : pg_fatal("could not find function definition for function with OID %u",
14164 : transform->trftosql);
14165 : }
14166 :
14167 36 : defqry = createPQExpBuffer();
14168 36 : delqry = createPQExpBuffer();
14169 36 : labelq = createPQExpBuffer();
14170 36 : transformargs = createPQExpBuffer();
14171 :
14172 36 : lanname = get_language_name(fout, transform->trflang);
14173 36 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14174 :
14175 36 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14176 : transformType, lanname);
14177 :
14178 36 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14179 : transformType, lanname);
14180 :
14181 36 : if (!transform->trffromsql && !transform->trftosql)
14182 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14183 :
14184 36 : if (transform->trffromsql)
14185 : {
14186 36 : if (fromsqlFuncInfo)
14187 : {
14188 36 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14189 :
14190 : /*
14191 : * Always qualify the function name (format_function_signature
14192 : * won't qualify it).
14193 : */
14194 36 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14195 36 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14196 36 : free(fsig);
14197 : }
14198 : else
14199 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14200 : }
14201 :
14202 36 : if (transform->trftosql)
14203 : {
14204 36 : if (transform->trffromsql)
14205 36 : appendPQExpBufferStr(defqry, ", ");
14206 :
14207 36 : if (tosqlFuncInfo)
14208 : {
14209 36 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14210 :
14211 : /*
14212 : * Always qualify the function name (format_function_signature
14213 : * won't qualify it).
14214 : */
14215 36 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14216 36 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14217 36 : free(fsig);
14218 : }
14219 : else
14220 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14221 : }
14222 :
14223 36 : appendPQExpBufferStr(defqry, ");\n");
14224 :
14225 36 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14226 : transformType, lanname);
14227 :
14228 36 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14229 : transformType, lanname);
14230 :
14231 36 : if (dopt->binary_upgrade)
14232 2 : binary_upgrade_extension_member(defqry, &transform->dobj,
14233 2 : "TRANSFORM", transformargs->data, NULL);
14234 :
14235 36 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14236 36 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14237 36 : ARCHIVE_OPTS(.tag = labelq->data,
14238 : .description = "TRANSFORM",
14239 : .section = SECTION_PRE_DATA,
14240 : .createStmt = defqry->data,
14241 : .dropStmt = delqry->data,
14242 : .deps = transform->dobj.dependencies,
14243 : .nDeps = transform->dobj.nDeps));
14244 :
14245 : /* Dump Transform Comments */
14246 36 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14247 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14248 : NULL, "",
14249 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14250 :
14251 36 : free(lanname);
14252 36 : destroyPQExpBuffer(defqry);
14253 36 : destroyPQExpBuffer(delqry);
14254 36 : destroyPQExpBuffer(labelq);
14255 36 : destroyPQExpBuffer(transformargs);
14256 : }
14257 :
14258 :
14259 : /*
14260 : * dumpOpr
14261 : * write out a single operator definition
14262 : */
14263 : static void
14264 2522 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14265 : {
14266 2522 : DumpOptions *dopt = fout->dopt;
14267 : PQExpBuffer query;
14268 : PQExpBuffer q;
14269 : PQExpBuffer delq;
14270 : PQExpBuffer oprid;
14271 : PQExpBuffer details;
14272 : PGresult *res;
14273 : int i_oprkind;
14274 : int i_oprcode;
14275 : int i_oprleft;
14276 : int i_oprright;
14277 : int i_oprcom;
14278 : int i_oprnegate;
14279 : int i_oprrest;
14280 : int i_oprjoin;
14281 : int i_oprcanmerge;
14282 : int i_oprcanhash;
14283 : char *oprkind;
14284 : char *oprcode;
14285 : char *oprleft;
14286 : char *oprright;
14287 : char *oprcom;
14288 : char *oprnegate;
14289 : char *oprrest;
14290 : char *oprjoin;
14291 : char *oprcanmerge;
14292 : char *oprcanhash;
14293 : char *oprregproc;
14294 : char *oprref;
14295 :
14296 : /* Do nothing if not dumping schema */
14297 2522 : if (!dopt->dumpSchema)
14298 6 : return;
14299 :
14300 : /*
14301 : * some operators are invalid because they were the result of user
14302 : * defining operators before commutators exist
14303 : */
14304 2516 : if (!OidIsValid(oprinfo->oprcode))
14305 14 : return;
14306 :
14307 2502 : query = createPQExpBuffer();
14308 2502 : q = createPQExpBuffer();
14309 2502 : delq = createPQExpBuffer();
14310 2502 : oprid = createPQExpBuffer();
14311 2502 : details = createPQExpBuffer();
14312 :
14313 2502 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14314 : {
14315 : /* Set up query for operator-specific details */
14316 40 : appendPQExpBufferStr(query,
14317 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14318 : "SELECT oprkind, "
14319 : "oprcode::pg_catalog.regprocedure, "
14320 : "oprleft::pg_catalog.regtype, "
14321 : "oprright::pg_catalog.regtype, "
14322 : "oprcom, "
14323 : "oprnegate, "
14324 : "oprrest::pg_catalog.regprocedure, "
14325 : "oprjoin::pg_catalog.regprocedure, "
14326 : "oprcanmerge, oprcanhash "
14327 : "FROM pg_catalog.pg_operator "
14328 : "WHERE oid = $1");
14329 :
14330 40 : ExecuteSqlStatement(fout, query->data);
14331 :
14332 40 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14333 : }
14334 :
14335 2502 : printfPQExpBuffer(query,
14336 : "EXECUTE dumpOpr('%u')",
14337 2502 : oprinfo->dobj.catId.oid);
14338 :
14339 2502 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14340 :
14341 2502 : i_oprkind = PQfnumber(res, "oprkind");
14342 2502 : i_oprcode = PQfnumber(res, "oprcode");
14343 2502 : i_oprleft = PQfnumber(res, "oprleft");
14344 2502 : i_oprright = PQfnumber(res, "oprright");
14345 2502 : i_oprcom = PQfnumber(res, "oprcom");
14346 2502 : i_oprnegate = PQfnumber(res, "oprnegate");
14347 2502 : i_oprrest = PQfnumber(res, "oprrest");
14348 2502 : i_oprjoin = PQfnumber(res, "oprjoin");
14349 2502 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14350 2502 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14351 :
14352 2502 : oprkind = PQgetvalue(res, 0, i_oprkind);
14353 2502 : oprcode = PQgetvalue(res, 0, i_oprcode);
14354 2502 : oprleft = PQgetvalue(res, 0, i_oprleft);
14355 2502 : oprright = PQgetvalue(res, 0, i_oprright);
14356 2502 : oprcom = PQgetvalue(res, 0, i_oprcom);
14357 2502 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14358 2502 : oprrest = PQgetvalue(res, 0, i_oprrest);
14359 2502 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14360 2502 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14361 2502 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14362 :
14363 : /* In PG14 upwards postfix operator support does not exist anymore. */
14364 2502 : if (strcmp(oprkind, "r") == 0)
14365 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14366 : oprcode);
14367 :
14368 2502 : oprregproc = convertRegProcReference(oprcode);
14369 2502 : if (oprregproc)
14370 : {
14371 2502 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14372 2502 : free(oprregproc);
14373 : }
14374 :
14375 2502 : appendPQExpBuffer(oprid, "%s (",
14376 2502 : oprinfo->dobj.name);
14377 :
14378 : /*
14379 : * right unary means there's a left arg and left unary means there's a
14380 : * right arg. (Although the "r" case is dead code for PG14 and later,
14381 : * continue to support it in case we're dumping from an old server.)
14382 : */
14383 2502 : if (strcmp(oprkind, "r") == 0 ||
14384 2502 : strcmp(oprkind, "b") == 0)
14385 : {
14386 2359 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14387 2359 : appendPQExpBufferStr(oprid, oprleft);
14388 : }
14389 : else
14390 143 : appendPQExpBufferStr(oprid, "NONE");
14391 :
14392 2502 : if (strcmp(oprkind, "l") == 0 ||
14393 2359 : strcmp(oprkind, "b") == 0)
14394 : {
14395 2502 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14396 2502 : appendPQExpBuffer(oprid, ", %s)", oprright);
14397 : }
14398 : else
14399 0 : appendPQExpBufferStr(oprid, ", NONE)");
14400 :
14401 2502 : oprref = getFormattedOperatorName(oprcom);
14402 2502 : if (oprref)
14403 : {
14404 1679 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14405 1679 : free(oprref);
14406 : }
14407 :
14408 2502 : oprref = getFormattedOperatorName(oprnegate);
14409 2502 : if (oprref)
14410 : {
14411 1181 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14412 1181 : free(oprref);
14413 : }
14414 :
14415 2502 : if (strcmp(oprcanmerge, "t") == 0)
14416 188 : appendPQExpBufferStr(details, ",\n MERGES");
14417 :
14418 2502 : if (strcmp(oprcanhash, "t") == 0)
14419 141 : appendPQExpBufferStr(details, ",\n HASHES");
14420 :
14421 2502 : oprregproc = convertRegProcReference(oprrest);
14422 2502 : if (oprregproc)
14423 : {
14424 1532 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14425 1532 : free(oprregproc);
14426 : }
14427 :
14428 2502 : oprregproc = convertRegProcReference(oprjoin);
14429 2502 : if (oprregproc)
14430 : {
14431 1532 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14432 1532 : free(oprregproc);
14433 : }
14434 :
14435 2502 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14436 2502 : fmtId(oprinfo->dobj.namespace->dobj.name),
14437 : oprid->data);
14438 :
14439 2502 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14440 2502 : fmtId(oprinfo->dobj.namespace->dobj.name),
14441 2502 : oprinfo->dobj.name, details->data);
14442 :
14443 2502 : if (dopt->binary_upgrade)
14444 12 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14445 12 : "OPERATOR", oprid->data,
14446 12 : oprinfo->dobj.namespace->dobj.name);
14447 :
14448 2502 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14449 2502 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14450 2502 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14451 : .namespace = oprinfo->dobj.namespace->dobj.name,
14452 : .owner = oprinfo->rolname,
14453 : .description = "OPERATOR",
14454 : .section = SECTION_PRE_DATA,
14455 : .createStmt = q->data,
14456 : .dropStmt = delq->data));
14457 :
14458 : /* Dump Operator Comments */
14459 2502 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14460 2415 : dumpComment(fout, "OPERATOR", oprid->data,
14461 2415 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14462 2415 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14463 :
14464 2502 : PQclear(res);
14465 :
14466 2502 : destroyPQExpBuffer(query);
14467 2502 : destroyPQExpBuffer(q);
14468 2502 : destroyPQExpBuffer(delq);
14469 2502 : destroyPQExpBuffer(oprid);
14470 2502 : destroyPQExpBuffer(details);
14471 : }
14472 :
14473 : /*
14474 : * Convert a function reference obtained from pg_operator
14475 : *
14476 : * Returns allocated string of what to print, or NULL if function references
14477 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14478 : *
14479 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14480 : * part.
14481 : */
14482 : static char *
14483 7506 : convertRegProcReference(const char *proc)
14484 : {
14485 : char *name;
14486 : char *paren;
14487 : bool inquote;
14488 :
14489 : /* In all cases "-" means a null reference */
14490 7506 : if (strcmp(proc, "-") == 0)
14491 1940 : return NULL;
14492 :
14493 5566 : name = pg_strdup(proc);
14494 : /* find non-double-quoted left paren */
14495 5566 : inquote = false;
14496 66996 : for (paren = name; *paren; paren++)
14497 : {
14498 66996 : if (*paren == '(' && !inquote)
14499 : {
14500 5566 : *paren = '\0';
14501 5566 : break;
14502 : }
14503 61430 : if (*paren == '"')
14504 50 : inquote = !inquote;
14505 : }
14506 5566 : return name;
14507 : }
14508 :
14509 : /*
14510 : * getFormattedOperatorName - retrieve the operator name for the
14511 : * given operator OID (presented in string form).
14512 : *
14513 : * Returns an allocated string, or NULL if the given OID is invalid.
14514 : * Caller is responsible for free'ing result string.
14515 : *
14516 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14517 : * useful in commands where the operator's argument types can be inferred from
14518 : * context. We always schema-qualify the name, though. The predecessor to
14519 : * this code tried to skip the schema qualification if possible, but that led
14520 : * to wrong results in corner cases, such as if an operator and its negator
14521 : * are in different schemas.
14522 : */
14523 : static char *
14524 5289 : getFormattedOperatorName(const char *oproid)
14525 : {
14526 : OprInfo *oprInfo;
14527 :
14528 : /* In all cases "0" means a null reference */
14529 5289 : if (strcmp(oproid, "0") == 0)
14530 2429 : return NULL;
14531 :
14532 2860 : oprInfo = findOprByOid(atooid(oproid));
14533 2860 : if (oprInfo == NULL)
14534 : {
14535 0 : pg_log_warning("could not find operator with OID %s",
14536 : oproid);
14537 0 : return NULL;
14538 : }
14539 :
14540 2860 : return psprintf("OPERATOR(%s.%s)",
14541 2860 : fmtId(oprInfo->dobj.namespace->dobj.name),
14542 : oprInfo->dobj.name);
14543 : }
14544 :
14545 : /*
14546 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14547 : *
14548 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14549 : * argument lists of these functions are predetermined. Note that the
14550 : * caller should ensure we are in the proper schema, because the results
14551 : * are search path dependent!
14552 : */
14553 : static char *
14554 205 : convertTSFunction(Archive *fout, Oid funcOid)
14555 : {
14556 : char *result;
14557 : char query[128];
14558 : PGresult *res;
14559 :
14560 205 : snprintf(query, sizeof(query),
14561 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14562 205 : res = ExecuteSqlQueryForSingleRow(fout, query);
14563 :
14564 205 : result = pg_strdup(PQgetvalue(res, 0, 0));
14565 :
14566 205 : PQclear(res);
14567 :
14568 205 : return result;
14569 : }
14570 :
14571 : /*
14572 : * dumpAccessMethod
14573 : * write out a single access method definition
14574 : */
14575 : static void
14576 80 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14577 : {
14578 80 : DumpOptions *dopt = fout->dopt;
14579 : PQExpBuffer q;
14580 : PQExpBuffer delq;
14581 : char *qamname;
14582 :
14583 : /* Do nothing if not dumping schema */
14584 80 : if (!dopt->dumpSchema)
14585 12 : return;
14586 :
14587 68 : q = createPQExpBuffer();
14588 68 : delq = createPQExpBuffer();
14589 :
14590 68 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14591 :
14592 68 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14593 :
14594 68 : switch (aminfo->amtype)
14595 : {
14596 32 : case AMTYPE_INDEX:
14597 32 : appendPQExpBufferStr(q, "TYPE INDEX ");
14598 32 : break;
14599 36 : case AMTYPE_TABLE:
14600 36 : appendPQExpBufferStr(q, "TYPE TABLE ");
14601 36 : break;
14602 0 : default:
14603 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14604 : aminfo->amtype, qamname);
14605 0 : destroyPQExpBuffer(q);
14606 0 : destroyPQExpBuffer(delq);
14607 0 : free(qamname);
14608 0 : return;
14609 : }
14610 :
14611 68 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14612 :
14613 68 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14614 : qamname);
14615 :
14616 68 : if (dopt->binary_upgrade)
14617 4 : binary_upgrade_extension_member(q, &aminfo->dobj,
14618 : "ACCESS METHOD", qamname, NULL);
14619 :
14620 68 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 68 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14622 68 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14623 : .description = "ACCESS METHOD",
14624 : .section = SECTION_PRE_DATA,
14625 : .createStmt = q->data,
14626 : .dropStmt = delq->data));
14627 :
14628 : /* Dump Access Method Comments */
14629 68 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14630 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14631 : NULL, "",
14632 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14633 :
14634 68 : destroyPQExpBuffer(q);
14635 68 : destroyPQExpBuffer(delq);
14636 68 : free(qamname);
14637 : }
14638 :
14639 : /*
14640 : * dumpOpclass
14641 : * write out a single operator class definition
14642 : */
14643 : static void
14644 666 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14645 : {
14646 666 : DumpOptions *dopt = fout->dopt;
14647 : PQExpBuffer query;
14648 : PQExpBuffer q;
14649 : PQExpBuffer delq;
14650 : PQExpBuffer nameusing;
14651 : PGresult *res;
14652 : int ntups;
14653 : int i_opcintype;
14654 : int i_opckeytype;
14655 : int i_opcdefault;
14656 : int i_opcfamily;
14657 : int i_opcfamilyname;
14658 : int i_opcfamilynsp;
14659 : int i_amname;
14660 : int i_amopstrategy;
14661 : int i_amopopr;
14662 : int i_sortfamily;
14663 : int i_sortfamilynsp;
14664 : int i_amprocnum;
14665 : int i_amproc;
14666 : int i_amproclefttype;
14667 : int i_amprocrighttype;
14668 : char *opcintype;
14669 : char *opckeytype;
14670 : char *opcdefault;
14671 : char *opcfamily;
14672 : char *opcfamilyname;
14673 : char *opcfamilynsp;
14674 : char *amname;
14675 : char *amopstrategy;
14676 : char *amopopr;
14677 : char *sortfamily;
14678 : char *sortfamilynsp;
14679 : char *amprocnum;
14680 : char *amproc;
14681 : char *amproclefttype;
14682 : char *amprocrighttype;
14683 : bool needComma;
14684 : int i;
14685 :
14686 : /* Do nothing if not dumping schema */
14687 666 : if (!dopt->dumpSchema)
14688 18 : return;
14689 :
14690 648 : query = createPQExpBuffer();
14691 648 : q = createPQExpBuffer();
14692 648 : delq = createPQExpBuffer();
14693 648 : nameusing = createPQExpBuffer();
14694 :
14695 : /* Get additional fields from the pg_opclass row */
14696 648 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14697 : "opckeytype::pg_catalog.regtype, "
14698 : "opcdefault, opcfamily, "
14699 : "opfname AS opcfamilyname, "
14700 : "nspname AS opcfamilynsp, "
14701 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14702 : "FROM pg_catalog.pg_opclass c "
14703 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14704 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14705 : "WHERE c.oid = '%u'::pg_catalog.oid",
14706 648 : opcinfo->dobj.catId.oid);
14707 :
14708 648 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14709 :
14710 648 : i_opcintype = PQfnumber(res, "opcintype");
14711 648 : i_opckeytype = PQfnumber(res, "opckeytype");
14712 648 : i_opcdefault = PQfnumber(res, "opcdefault");
14713 648 : i_opcfamily = PQfnumber(res, "opcfamily");
14714 648 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14715 648 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14716 648 : i_amname = PQfnumber(res, "amname");
14717 :
14718 : /* opcintype may still be needed after we PQclear res */
14719 648 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14720 648 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14721 648 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14722 : /* opcfamily will still be needed after we PQclear res */
14723 648 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14724 648 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14725 648 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14726 : /* amname will still be needed after we PQclear res */
14727 648 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14728 :
14729 648 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14730 648 : fmtQualifiedDumpable(opcinfo));
14731 648 : appendPQExpBuffer(delq, " USING %s;\n",
14732 : fmtId(amname));
14733 :
14734 : /* Build the fixed portion of the CREATE command */
14735 648 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14736 648 : fmtQualifiedDumpable(opcinfo));
14737 648 : if (strcmp(opcdefault, "t") == 0)
14738 366 : appendPQExpBufferStr(q, "DEFAULT ");
14739 648 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14740 : opcintype,
14741 : fmtId(amname));
14742 648 : if (strlen(opcfamilyname) > 0)
14743 : {
14744 648 : appendPQExpBufferStr(q, " FAMILY ");
14745 648 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14746 648 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14747 : }
14748 648 : appendPQExpBufferStr(q, " AS\n ");
14749 :
14750 648 : needComma = false;
14751 :
14752 648 : if (strcmp(opckeytype, "-") != 0)
14753 : {
14754 252 : appendPQExpBuffer(q, "STORAGE %s",
14755 : opckeytype);
14756 252 : needComma = true;
14757 : }
14758 :
14759 648 : PQclear(res);
14760 :
14761 : /*
14762 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14763 : *
14764 : * Print only those opfamily members that are tied to the opclass by
14765 : * pg_depend entries.
14766 : */
14767 648 : resetPQExpBuffer(query);
14768 648 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14769 : "amopopr::pg_catalog.regoperator, "
14770 : "opfname AS sortfamily, "
14771 : "nspname AS sortfamilynsp "
14772 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14773 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14774 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14775 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14776 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14777 : "AND refobjid = '%u'::pg_catalog.oid "
14778 : "AND amopfamily = '%s'::pg_catalog.oid "
14779 : "ORDER BY amopstrategy",
14780 648 : opcinfo->dobj.catId.oid,
14781 : opcfamily);
14782 :
14783 648 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14784 :
14785 648 : ntups = PQntuples(res);
14786 :
14787 648 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14788 648 : i_amopopr = PQfnumber(res, "amopopr");
14789 648 : i_sortfamily = PQfnumber(res, "sortfamily");
14790 648 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14791 :
14792 850 : for (i = 0; i < ntups; i++)
14793 : {
14794 202 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14795 202 : amopopr = PQgetvalue(res, i, i_amopopr);
14796 202 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14797 202 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14798 :
14799 202 : if (needComma)
14800 128 : appendPQExpBufferStr(q, " ,\n ");
14801 :
14802 202 : appendPQExpBuffer(q, "OPERATOR %s %s",
14803 : amopstrategy, amopopr);
14804 :
14805 202 : if (strlen(sortfamily) > 0)
14806 : {
14807 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14808 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14809 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14810 : }
14811 :
14812 202 : needComma = true;
14813 : }
14814 :
14815 648 : PQclear(res);
14816 :
14817 : /*
14818 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14819 : *
14820 : * Print only those opfamily members that are tied to the opclass by
14821 : * pg_depend entries.
14822 : *
14823 : * We print the amproclefttype/amprocrighttype even though in most cases
14824 : * the backend could deduce the right values, because of the corner case
14825 : * of a btree sort support function for a cross-type comparison.
14826 : */
14827 648 : resetPQExpBuffer(query);
14828 :
14829 648 : appendPQExpBuffer(query, "SELECT amprocnum, "
14830 : "amproc::pg_catalog.regprocedure, "
14831 : "amproclefttype::pg_catalog.regtype, "
14832 : "amprocrighttype::pg_catalog.regtype "
14833 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14834 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14835 : "AND refobjid = '%u'::pg_catalog.oid "
14836 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14837 : "AND objid = ap.oid "
14838 : "ORDER BY amprocnum",
14839 648 : opcinfo->dobj.catId.oid);
14840 :
14841 648 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14842 :
14843 648 : ntups = PQntuples(res);
14844 :
14845 648 : i_amprocnum = PQfnumber(res, "amprocnum");
14846 648 : i_amproc = PQfnumber(res, "amproc");
14847 648 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14848 648 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14849 :
14850 680 : for (i = 0; i < ntups; i++)
14851 : {
14852 32 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14853 32 : amproc = PQgetvalue(res, i, i_amproc);
14854 32 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14855 32 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14856 :
14857 32 : if (needComma)
14858 32 : appendPQExpBufferStr(q, " ,\n ");
14859 :
14860 32 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14861 :
14862 32 : if (*amproclefttype && *amprocrighttype)
14863 32 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14864 :
14865 32 : appendPQExpBuffer(q, " %s", amproc);
14866 :
14867 32 : needComma = true;
14868 : }
14869 :
14870 648 : PQclear(res);
14871 :
14872 : /*
14873 : * If needComma is still false it means we haven't added anything after
14874 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14875 : * clause with the same datatype. This isn't sanctioned by the
14876 : * documentation, but actually DefineOpClass will treat it as a no-op.
14877 : */
14878 648 : if (!needComma)
14879 322 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14880 :
14881 648 : appendPQExpBufferStr(q, ";\n");
14882 :
14883 648 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14884 648 : appendPQExpBuffer(nameusing, " USING %s",
14885 : fmtId(amname));
14886 :
14887 648 : if (dopt->binary_upgrade)
14888 6 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14889 6 : "OPERATOR CLASS", nameusing->data,
14890 6 : opcinfo->dobj.namespace->dobj.name);
14891 :
14892 648 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14893 648 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14894 648 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14895 : .namespace = opcinfo->dobj.namespace->dobj.name,
14896 : .owner = opcinfo->rolname,
14897 : .description = "OPERATOR CLASS",
14898 : .section = SECTION_PRE_DATA,
14899 : .createStmt = q->data,
14900 : .dropStmt = delq->data));
14901 :
14902 : /* Dump Operator Class Comments */
14903 648 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14904 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14905 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14906 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14907 :
14908 648 : free(opcintype);
14909 648 : free(opcfamily);
14910 648 : free(amname);
14911 648 : destroyPQExpBuffer(query);
14912 648 : destroyPQExpBuffer(q);
14913 648 : destroyPQExpBuffer(delq);
14914 648 : destroyPQExpBuffer(nameusing);
14915 : }
14916 :
14917 : /*
14918 : * dumpOpfamily
14919 : * write out a single operator family definition
14920 : *
14921 : * Note: this also dumps any "loose" operator members that aren't bound to a
14922 : * specific opclass within the opfamily.
14923 : */
14924 : static void
14925 555 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14926 : {
14927 555 : DumpOptions *dopt = fout->dopt;
14928 : PQExpBuffer query;
14929 : PQExpBuffer q;
14930 : PQExpBuffer delq;
14931 : PQExpBuffer nameusing;
14932 : PGresult *res;
14933 : PGresult *res_ops;
14934 : PGresult *res_procs;
14935 : int ntups;
14936 : int i_amname;
14937 : int i_amopstrategy;
14938 : int i_amopopr;
14939 : int i_sortfamily;
14940 : int i_sortfamilynsp;
14941 : int i_amprocnum;
14942 : int i_amproc;
14943 : int i_amproclefttype;
14944 : int i_amprocrighttype;
14945 : char *amname;
14946 : char *amopstrategy;
14947 : char *amopopr;
14948 : char *sortfamily;
14949 : char *sortfamilynsp;
14950 : char *amprocnum;
14951 : char *amproc;
14952 : char *amproclefttype;
14953 : char *amprocrighttype;
14954 : bool needComma;
14955 : int i;
14956 :
14957 : /* Do nothing if not dumping schema */
14958 555 : if (!dopt->dumpSchema)
14959 12 : return;
14960 :
14961 543 : query = createPQExpBuffer();
14962 543 : q = createPQExpBuffer();
14963 543 : delq = createPQExpBuffer();
14964 543 : nameusing = createPQExpBuffer();
14965 :
14966 : /*
14967 : * Fetch only those opfamily members that are tied directly to the
14968 : * opfamily by pg_depend entries.
14969 : */
14970 543 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14971 : "amopopr::pg_catalog.regoperator, "
14972 : "opfname AS sortfamily, "
14973 : "nspname AS sortfamilynsp "
14974 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14975 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14976 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14977 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14978 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14979 : "AND refobjid = '%u'::pg_catalog.oid "
14980 : "AND amopfamily = '%u'::pg_catalog.oid "
14981 : "ORDER BY amopstrategy",
14982 543 : opfinfo->dobj.catId.oid,
14983 543 : opfinfo->dobj.catId.oid);
14984 :
14985 543 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14986 :
14987 543 : resetPQExpBuffer(query);
14988 :
14989 543 : appendPQExpBuffer(query, "SELECT amprocnum, "
14990 : "amproc::pg_catalog.regprocedure, "
14991 : "amproclefttype::pg_catalog.regtype, "
14992 : "amprocrighttype::pg_catalog.regtype "
14993 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14994 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14995 : "AND refobjid = '%u'::pg_catalog.oid "
14996 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14997 : "AND objid = ap.oid "
14998 : "ORDER BY amprocnum",
14999 543 : opfinfo->dobj.catId.oid);
15000 :
15001 543 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15002 :
15003 : /* Get additional fields from the pg_opfamily row */
15004 543 : resetPQExpBuffer(query);
15005 :
15006 543 : appendPQExpBuffer(query, "SELECT "
15007 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
15008 : "FROM pg_catalog.pg_opfamily "
15009 : "WHERE oid = '%u'::pg_catalog.oid",
15010 543 : opfinfo->dobj.catId.oid);
15011 :
15012 543 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15013 :
15014 543 : i_amname = PQfnumber(res, "amname");
15015 :
15016 : /* amname will still be needed after we PQclear res */
15017 543 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
15018 :
15019 543 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
15020 543 : fmtQualifiedDumpable(opfinfo));
15021 543 : appendPQExpBuffer(delq, " USING %s;\n",
15022 : fmtId(amname));
15023 :
15024 : /* Build the fixed portion of the CREATE command */
15025 543 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
15026 543 : fmtQualifiedDumpable(opfinfo));
15027 543 : appendPQExpBuffer(q, " USING %s;\n",
15028 : fmtId(amname));
15029 :
15030 543 : PQclear(res);
15031 :
15032 : /* Do we need an ALTER to add loose members? */
15033 543 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
15034 : {
15035 47 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
15036 47 : fmtQualifiedDumpable(opfinfo));
15037 47 : appendPQExpBuffer(q, " USING %s ADD\n ",
15038 : fmtId(amname));
15039 :
15040 47 : needComma = false;
15041 :
15042 : /*
15043 : * Now fetch and print the OPERATOR entries (pg_amop rows).
15044 : */
15045 47 : ntups = PQntuples(res_ops);
15046 :
15047 47 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
15048 47 : i_amopopr = PQfnumber(res_ops, "amopopr");
15049 47 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
15050 47 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
15051 :
15052 207 : for (i = 0; i < ntups; i++)
15053 : {
15054 160 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
15055 160 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
15056 160 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
15057 160 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
15058 :
15059 160 : if (needComma)
15060 128 : appendPQExpBufferStr(q, " ,\n ");
15061 :
15062 160 : appendPQExpBuffer(q, "OPERATOR %s %s",
15063 : amopstrategy, amopopr);
15064 :
15065 160 : if (strlen(sortfamily) > 0)
15066 : {
15067 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
15068 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
15069 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
15070 : }
15071 :
15072 160 : needComma = true;
15073 : }
15074 :
15075 : /*
15076 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
15077 : */
15078 47 : ntups = PQntuples(res_procs);
15079 :
15080 47 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
15081 47 : i_amproc = PQfnumber(res_procs, "amproc");
15082 47 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
15083 47 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
15084 :
15085 222 : for (i = 0; i < ntups; i++)
15086 : {
15087 175 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
15088 175 : amproc = PQgetvalue(res_procs, i, i_amproc);
15089 175 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
15090 175 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
15091 :
15092 175 : if (needComma)
15093 160 : appendPQExpBufferStr(q, " ,\n ");
15094 :
15095 175 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
15096 : amprocnum, amproclefttype, amprocrighttype,
15097 : amproc);
15098 :
15099 175 : needComma = true;
15100 : }
15101 :
15102 47 : appendPQExpBufferStr(q, ";\n");
15103 : }
15104 :
15105 543 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
15106 543 : appendPQExpBuffer(nameusing, " USING %s",
15107 : fmtId(amname));
15108 :
15109 543 : if (dopt->binary_upgrade)
15110 9 : binary_upgrade_extension_member(q, &opfinfo->dobj,
15111 9 : "OPERATOR FAMILY", nameusing->data,
15112 9 : opfinfo->dobj.namespace->dobj.name);
15113 :
15114 543 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15115 543 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
15116 543 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
15117 : .namespace = opfinfo->dobj.namespace->dobj.name,
15118 : .owner = opfinfo->rolname,
15119 : .description = "OPERATOR FAMILY",
15120 : .section = SECTION_PRE_DATA,
15121 : .createStmt = q->data,
15122 : .dropStmt = delq->data));
15123 :
15124 : /* Dump Operator Family Comments */
15125 543 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15126 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
15127 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
15128 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
15129 :
15130 543 : free(amname);
15131 543 : PQclear(res_ops);
15132 543 : PQclear(res_procs);
15133 543 : destroyPQExpBuffer(query);
15134 543 : destroyPQExpBuffer(q);
15135 543 : destroyPQExpBuffer(delq);
15136 543 : destroyPQExpBuffer(nameusing);
15137 : }
15138 :
15139 : /*
15140 : * dumpCollation
15141 : * write out a single collation definition
15142 : */
15143 : static void
15144 2729 : dumpCollation(Archive *fout, const CollInfo *collinfo)
15145 : {
15146 2729 : DumpOptions *dopt = fout->dopt;
15147 : PQExpBuffer query;
15148 : PQExpBuffer q;
15149 : PQExpBuffer delq;
15150 : char *qcollname;
15151 : PGresult *res;
15152 : int i_collprovider;
15153 : int i_collisdeterministic;
15154 : int i_collcollate;
15155 : int i_collctype;
15156 : int i_colllocale;
15157 : int i_collicurules;
15158 : const char *collprovider;
15159 : const char *collcollate;
15160 : const char *collctype;
15161 : const char *colllocale;
15162 : const char *collicurules;
15163 :
15164 : /* Do nothing if not dumping schema */
15165 2729 : if (!dopt->dumpSchema)
15166 12 : return;
15167 :
15168 2717 : query = createPQExpBuffer();
15169 2717 : q = createPQExpBuffer();
15170 2717 : delq = createPQExpBuffer();
15171 :
15172 2717 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15173 :
15174 : /* Get collation-specific details */
15175 2717 : appendPQExpBufferStr(query, "SELECT ");
15176 :
15177 2717 : if (fout->remoteVersion >= 100000)
15178 2717 : appendPQExpBufferStr(query,
15179 : "collprovider, "
15180 : "collversion, ");
15181 : else
15182 0 : appendPQExpBufferStr(query,
15183 : "'c' AS collprovider, "
15184 : "NULL AS collversion, ");
15185 :
15186 2717 : if (fout->remoteVersion >= 120000)
15187 2717 : appendPQExpBufferStr(query,
15188 : "collisdeterministic, ");
15189 : else
15190 0 : appendPQExpBufferStr(query,
15191 : "true AS collisdeterministic, ");
15192 :
15193 2717 : if (fout->remoteVersion >= 170000)
15194 2717 : appendPQExpBufferStr(query,
15195 : "colllocale, ");
15196 0 : else if (fout->remoteVersion >= 150000)
15197 0 : appendPQExpBufferStr(query,
15198 : "colliculocale AS colllocale, ");
15199 : else
15200 0 : appendPQExpBufferStr(query,
15201 : "NULL AS colllocale, ");
15202 :
15203 2717 : if (fout->remoteVersion >= 160000)
15204 2717 : appendPQExpBufferStr(query,
15205 : "collicurules, ");
15206 : else
15207 0 : appendPQExpBufferStr(query,
15208 : "NULL AS collicurules, ");
15209 :
15210 2717 : appendPQExpBuffer(query,
15211 : "collcollate, "
15212 : "collctype "
15213 : "FROM pg_catalog.pg_collation c "
15214 : "WHERE c.oid = '%u'::pg_catalog.oid",
15215 2717 : collinfo->dobj.catId.oid);
15216 :
15217 2717 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15218 :
15219 2717 : i_collprovider = PQfnumber(res, "collprovider");
15220 2717 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15221 2717 : i_collcollate = PQfnumber(res, "collcollate");
15222 2717 : i_collctype = PQfnumber(res, "collctype");
15223 2717 : i_colllocale = PQfnumber(res, "colllocale");
15224 2717 : i_collicurules = PQfnumber(res, "collicurules");
15225 :
15226 2717 : collprovider = PQgetvalue(res, 0, i_collprovider);
15227 :
15228 2717 : if (!PQgetisnull(res, 0, i_collcollate))
15229 46 : collcollate = PQgetvalue(res, 0, i_collcollate);
15230 : else
15231 2671 : collcollate = NULL;
15232 :
15233 2717 : if (!PQgetisnull(res, 0, i_collctype))
15234 46 : collctype = PQgetvalue(res, 0, i_collctype);
15235 : else
15236 2671 : collctype = NULL;
15237 :
15238 : /*
15239 : * Before version 15, collcollate and collctype were of type NAME and
15240 : * non-nullable. Treat empty strings as NULL for consistency.
15241 : */
15242 2717 : if (fout->remoteVersion < 150000)
15243 : {
15244 0 : if (collcollate[0] == '\0')
15245 0 : collcollate = NULL;
15246 0 : if (collctype[0] == '\0')
15247 0 : collctype = NULL;
15248 : }
15249 :
15250 2717 : if (!PQgetisnull(res, 0, i_colllocale))
15251 2668 : colllocale = PQgetvalue(res, 0, i_colllocale);
15252 : else
15253 49 : colllocale = NULL;
15254 :
15255 2717 : if (!PQgetisnull(res, 0, i_collicurules))
15256 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15257 : else
15258 2717 : collicurules = NULL;
15259 :
15260 2717 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15261 2717 : fmtQualifiedDumpable(collinfo));
15262 :
15263 2717 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15264 2717 : fmtQualifiedDumpable(collinfo));
15265 :
15266 2717 : appendPQExpBufferStr(q, "provider = ");
15267 2717 : if (collprovider[0] == 'b')
15268 19 : appendPQExpBufferStr(q, "builtin");
15269 2698 : else if (collprovider[0] == 'c')
15270 46 : appendPQExpBufferStr(q, "libc");
15271 2652 : else if (collprovider[0] == 'i')
15272 2649 : appendPQExpBufferStr(q, "icu");
15273 3 : else if (collprovider[0] == 'd')
15274 : /* to allow dumping pg_catalog; not accepted on input */
15275 3 : appendPQExpBufferStr(q, "default");
15276 : else
15277 0 : pg_fatal("unrecognized collation provider: %s",
15278 : collprovider);
15279 :
15280 2717 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15281 0 : appendPQExpBufferStr(q, ", deterministic = false");
15282 :
15283 2717 : if (collprovider[0] == 'd')
15284 : {
15285 3 : if (collcollate || collctype || colllocale || collicurules)
15286 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15287 :
15288 : /* no locale -- the default collation cannot be reloaded anyway */
15289 : }
15290 2714 : else if (collprovider[0] == 'b')
15291 : {
15292 19 : if (collcollate || collctype || !colllocale || collicurules)
15293 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15294 :
15295 19 : appendPQExpBufferStr(q, ", locale = ");
15296 19 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15297 : fout);
15298 : }
15299 2695 : else if (collprovider[0] == 'i')
15300 : {
15301 2649 : if (fout->remoteVersion >= 150000)
15302 : {
15303 2649 : if (collcollate || collctype || !colllocale)
15304 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15305 :
15306 2649 : appendPQExpBufferStr(q, ", locale = ");
15307 2649 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15308 : fout);
15309 : }
15310 : else
15311 : {
15312 0 : if (!collcollate || !collctype || colllocale ||
15313 0 : strcmp(collcollate, collctype) != 0)
15314 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15315 :
15316 0 : appendPQExpBufferStr(q, ", locale = ");
15317 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15318 : }
15319 :
15320 2649 : if (collicurules)
15321 : {
15322 0 : appendPQExpBufferStr(q, ", rules = ");
15323 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15324 : }
15325 : }
15326 46 : else if (collprovider[0] == 'c')
15327 : {
15328 46 : if (colllocale || collicurules || !collcollate || !collctype)
15329 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15330 :
15331 46 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15332 : {
15333 46 : appendPQExpBufferStr(q, ", locale = ");
15334 46 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15335 : }
15336 : else
15337 : {
15338 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15339 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15340 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15341 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15342 : }
15343 : }
15344 : else
15345 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15346 :
15347 : /*
15348 : * For binary upgrade, carry over the collation version. For normal
15349 : * dump/restore, omit the version, so that it is computed upon restore.
15350 : */
15351 2717 : if (dopt->binary_upgrade)
15352 : {
15353 : int i_collversion;
15354 :
15355 5 : i_collversion = PQfnumber(res, "collversion");
15356 5 : if (!PQgetisnull(res, 0, i_collversion))
15357 : {
15358 4 : appendPQExpBufferStr(q, ", version = ");
15359 4 : appendStringLiteralAH(q,
15360 : PQgetvalue(res, 0, i_collversion),
15361 : fout);
15362 : }
15363 : }
15364 :
15365 2717 : appendPQExpBufferStr(q, ");\n");
15366 :
15367 2717 : if (dopt->binary_upgrade)
15368 5 : binary_upgrade_extension_member(q, &collinfo->dobj,
15369 : "COLLATION", qcollname,
15370 5 : collinfo->dobj.namespace->dobj.name);
15371 :
15372 2717 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15373 2717 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15374 2717 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15375 : .namespace = collinfo->dobj.namespace->dobj.name,
15376 : .owner = collinfo->rolname,
15377 : .description = "COLLATION",
15378 : .section = SECTION_PRE_DATA,
15379 : .createStmt = q->data,
15380 : .dropStmt = delq->data));
15381 :
15382 : /* Dump Collation Comments */
15383 2717 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15384 2611 : dumpComment(fout, "COLLATION", qcollname,
15385 2611 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15386 2611 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15387 :
15388 2717 : PQclear(res);
15389 :
15390 2717 : destroyPQExpBuffer(query);
15391 2717 : destroyPQExpBuffer(q);
15392 2717 : destroyPQExpBuffer(delq);
15393 2717 : free(qcollname);
15394 : }
15395 :
15396 : /*
15397 : * dumpConversion
15398 : * write out a single conversion definition
15399 : */
15400 : static void
15401 332 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15402 : {
15403 332 : DumpOptions *dopt = fout->dopt;
15404 : PQExpBuffer query;
15405 : PQExpBuffer q;
15406 : PQExpBuffer delq;
15407 : char *qconvname;
15408 : PGresult *res;
15409 : int i_conforencoding;
15410 : int i_contoencoding;
15411 : int i_conproc;
15412 : int i_condefault;
15413 : const char *conforencoding;
15414 : const char *contoencoding;
15415 : const char *conproc;
15416 : bool condefault;
15417 :
15418 : /* Do nothing if not dumping schema */
15419 332 : if (!dopt->dumpSchema)
15420 6 : return;
15421 :
15422 326 : query = createPQExpBuffer();
15423 326 : q = createPQExpBuffer();
15424 326 : delq = createPQExpBuffer();
15425 :
15426 326 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15427 :
15428 : /* Get conversion-specific details */
15429 326 : appendPQExpBuffer(query, "SELECT "
15430 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15431 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15432 : "conproc, condefault "
15433 : "FROM pg_catalog.pg_conversion c "
15434 : "WHERE c.oid = '%u'::pg_catalog.oid",
15435 326 : convinfo->dobj.catId.oid);
15436 :
15437 326 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15438 :
15439 326 : i_conforencoding = PQfnumber(res, "conforencoding");
15440 326 : i_contoencoding = PQfnumber(res, "contoencoding");
15441 326 : i_conproc = PQfnumber(res, "conproc");
15442 326 : i_condefault = PQfnumber(res, "condefault");
15443 :
15444 326 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15445 326 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15446 326 : conproc = PQgetvalue(res, 0, i_conproc);
15447 326 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15448 :
15449 326 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15450 326 : fmtQualifiedDumpable(convinfo));
15451 :
15452 326 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15453 : (condefault) ? "DEFAULT " : "",
15454 326 : fmtQualifiedDumpable(convinfo));
15455 326 : appendStringLiteralAH(q, conforencoding, fout);
15456 326 : appendPQExpBufferStr(q, " TO ");
15457 326 : appendStringLiteralAH(q, contoencoding, fout);
15458 : /* regproc output is already sufficiently quoted */
15459 326 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15460 :
15461 326 : if (dopt->binary_upgrade)
15462 1 : binary_upgrade_extension_member(q, &convinfo->dobj,
15463 : "CONVERSION", qconvname,
15464 1 : convinfo->dobj.namespace->dobj.name);
15465 :
15466 326 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15467 326 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15468 326 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15469 : .namespace = convinfo->dobj.namespace->dobj.name,
15470 : .owner = convinfo->rolname,
15471 : .description = "CONVERSION",
15472 : .section = SECTION_PRE_DATA,
15473 : .createStmt = q->data,
15474 : .dropStmt = delq->data));
15475 :
15476 : /* Dump Conversion Comments */
15477 326 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15478 326 : dumpComment(fout, "CONVERSION", qconvname,
15479 326 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15480 326 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15481 :
15482 326 : PQclear(res);
15483 :
15484 326 : destroyPQExpBuffer(query);
15485 326 : destroyPQExpBuffer(q);
15486 326 : destroyPQExpBuffer(delq);
15487 326 : free(qconvname);
15488 : }
15489 :
15490 : /*
15491 : * format_aggregate_signature: generate aggregate name and argument list
15492 : *
15493 : * The argument type names are qualified if needed. The aggregate name
15494 : * is never qualified.
15495 : */
15496 : static char *
15497 285 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15498 : {
15499 : PQExpBufferData buf;
15500 : int j;
15501 :
15502 285 : initPQExpBuffer(&buf);
15503 285 : if (honor_quotes)
15504 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15505 : else
15506 285 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15507 :
15508 285 : if (agginfo->aggfn.nargs == 0)
15509 40 : appendPQExpBufferStr(&buf, "(*)");
15510 : else
15511 : {
15512 245 : appendPQExpBufferChar(&buf, '(');
15513 535 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15514 290 : appendPQExpBuffer(&buf, "%s%s",
15515 : (j > 0) ? ", " : "",
15516 : getFormattedTypeName(fout,
15517 290 : agginfo->aggfn.argtypes[j],
15518 : zeroIsError));
15519 245 : appendPQExpBufferChar(&buf, ')');
15520 : }
15521 285 : return buf.data;
15522 : }
15523 :
15524 : /*
15525 : * dumpAgg
15526 : * write out a single aggregate definition
15527 : */
15528 : static void
15529 292 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15530 : {
15531 292 : DumpOptions *dopt = fout->dopt;
15532 : PQExpBuffer query;
15533 : PQExpBuffer q;
15534 : PQExpBuffer delq;
15535 : PQExpBuffer details;
15536 : char *aggsig; /* identity signature */
15537 292 : char *aggfullsig = NULL; /* full signature */
15538 : char *aggsig_tag;
15539 : PGresult *res;
15540 : int i_agginitval;
15541 : int i_aggminitval;
15542 : const char *aggtransfn;
15543 : const char *aggfinalfn;
15544 : const char *aggcombinefn;
15545 : const char *aggserialfn;
15546 : const char *aggdeserialfn;
15547 : const char *aggmtransfn;
15548 : const char *aggminvtransfn;
15549 : const char *aggmfinalfn;
15550 : bool aggfinalextra;
15551 : bool aggmfinalextra;
15552 : char aggfinalmodify;
15553 : char aggmfinalmodify;
15554 : const char *aggsortop;
15555 : char *aggsortconvop;
15556 : char aggkind;
15557 : const char *aggtranstype;
15558 : const char *aggtransspace;
15559 : const char *aggmtranstype;
15560 : const char *aggmtransspace;
15561 : const char *agginitval;
15562 : const char *aggminitval;
15563 : const char *proparallel;
15564 : char defaultfinalmodify;
15565 :
15566 : /* Do nothing if not dumping schema */
15567 292 : if (!dopt->dumpSchema)
15568 7 : return;
15569 :
15570 285 : query = createPQExpBuffer();
15571 285 : q = createPQExpBuffer();
15572 285 : delq = createPQExpBuffer();
15573 285 : details = createPQExpBuffer();
15574 :
15575 285 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15576 : {
15577 : /* Set up query for aggregate-specific details */
15578 55 : appendPQExpBufferStr(query,
15579 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15580 :
15581 55 : appendPQExpBufferStr(query,
15582 : "SELECT "
15583 : "aggtransfn,\n"
15584 : "aggfinalfn,\n"
15585 : "aggtranstype::pg_catalog.regtype,\n"
15586 : "agginitval,\n"
15587 : "aggsortop,\n"
15588 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15589 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15590 :
15591 55 : if (fout->remoteVersion >= 90400)
15592 55 : appendPQExpBufferStr(query,
15593 : "aggkind,\n"
15594 : "aggmtransfn,\n"
15595 : "aggminvtransfn,\n"
15596 : "aggmfinalfn,\n"
15597 : "aggmtranstype::pg_catalog.regtype,\n"
15598 : "aggfinalextra,\n"
15599 : "aggmfinalextra,\n"
15600 : "aggtransspace,\n"
15601 : "aggmtransspace,\n"
15602 : "aggminitval,\n");
15603 : else
15604 0 : appendPQExpBufferStr(query,
15605 : "'n' AS aggkind,\n"
15606 : "'-' AS aggmtransfn,\n"
15607 : "'-' AS aggminvtransfn,\n"
15608 : "'-' AS aggmfinalfn,\n"
15609 : "0 AS aggmtranstype,\n"
15610 : "false AS aggfinalextra,\n"
15611 : "false AS aggmfinalextra,\n"
15612 : "0 AS aggtransspace,\n"
15613 : "0 AS aggmtransspace,\n"
15614 : "NULL AS aggminitval,\n");
15615 :
15616 55 : if (fout->remoteVersion >= 90600)
15617 55 : appendPQExpBufferStr(query,
15618 : "aggcombinefn,\n"
15619 : "aggserialfn,\n"
15620 : "aggdeserialfn,\n"
15621 : "proparallel,\n");
15622 : else
15623 0 : appendPQExpBufferStr(query,
15624 : "'-' AS aggcombinefn,\n"
15625 : "'-' AS aggserialfn,\n"
15626 : "'-' AS aggdeserialfn,\n"
15627 : "'u' AS proparallel,\n");
15628 :
15629 55 : if (fout->remoteVersion >= 110000)
15630 55 : appendPQExpBufferStr(query,
15631 : "aggfinalmodify,\n"
15632 : "aggmfinalmodify\n");
15633 : else
15634 0 : appendPQExpBufferStr(query,
15635 : "'0' AS aggfinalmodify,\n"
15636 : "'0' AS aggmfinalmodify\n");
15637 :
15638 55 : appendPQExpBufferStr(query,
15639 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15640 : "WHERE a.aggfnoid = p.oid "
15641 : "AND p.oid = $1");
15642 :
15643 55 : ExecuteSqlStatement(fout, query->data);
15644 :
15645 55 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15646 : }
15647 :
15648 285 : printfPQExpBuffer(query,
15649 : "EXECUTE dumpAgg('%u')",
15650 285 : agginfo->aggfn.dobj.catId.oid);
15651 :
15652 285 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15653 :
15654 285 : i_agginitval = PQfnumber(res, "agginitval");
15655 285 : i_aggminitval = PQfnumber(res, "aggminitval");
15656 :
15657 285 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15658 285 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15659 285 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15660 285 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15661 285 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15662 285 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15663 285 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15664 285 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15665 285 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15666 285 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15667 285 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15668 285 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15669 285 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15670 285 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15671 285 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15672 285 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15673 285 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15674 285 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15675 285 : agginitval = PQgetvalue(res, 0, i_agginitval);
15676 285 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15677 285 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15678 :
15679 : {
15680 : char *funcargs;
15681 : char *funciargs;
15682 :
15683 285 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15684 285 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15685 285 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15686 285 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15687 : }
15688 :
15689 285 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15690 :
15691 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15692 285 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15693 : /* replace omitted flags for old versions */
15694 285 : if (aggfinalmodify == '0')
15695 0 : aggfinalmodify = defaultfinalmodify;
15696 285 : if (aggmfinalmodify == '0')
15697 0 : aggmfinalmodify = defaultfinalmodify;
15698 :
15699 : /* regproc and regtype output is already sufficiently quoted */
15700 285 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15701 : aggtransfn, aggtranstype);
15702 :
15703 285 : if (strcmp(aggtransspace, "0") != 0)
15704 : {
15705 5 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15706 : aggtransspace);
15707 : }
15708 :
15709 285 : if (!PQgetisnull(res, 0, i_agginitval))
15710 : {
15711 207 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15712 207 : appendStringLiteralAH(details, agginitval, fout);
15713 : }
15714 :
15715 285 : if (strcmp(aggfinalfn, "-") != 0)
15716 : {
15717 132 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15718 : aggfinalfn);
15719 132 : if (aggfinalextra)
15720 10 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15721 132 : if (aggfinalmodify != defaultfinalmodify)
15722 : {
15723 32 : switch (aggfinalmodify)
15724 : {
15725 0 : case AGGMODIFY_READ_ONLY:
15726 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15727 0 : break;
15728 32 : case AGGMODIFY_SHAREABLE:
15729 32 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15730 32 : break;
15731 0 : case AGGMODIFY_READ_WRITE:
15732 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15733 0 : break;
15734 0 : default:
15735 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15736 : agginfo->aggfn.dobj.name);
15737 : break;
15738 : }
15739 : }
15740 : }
15741 :
15742 285 : if (strcmp(aggcombinefn, "-") != 0)
15743 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15744 :
15745 285 : if (strcmp(aggserialfn, "-") != 0)
15746 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15747 :
15748 285 : if (strcmp(aggdeserialfn, "-") != 0)
15749 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15750 :
15751 285 : if (strcmp(aggmtransfn, "-") != 0)
15752 : {
15753 30 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15754 : aggmtransfn,
15755 : aggminvtransfn,
15756 : aggmtranstype);
15757 : }
15758 :
15759 285 : if (strcmp(aggmtransspace, "0") != 0)
15760 : {
15761 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15762 : aggmtransspace);
15763 : }
15764 :
15765 285 : if (!PQgetisnull(res, 0, i_aggminitval))
15766 : {
15767 10 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15768 10 : appendStringLiteralAH(details, aggminitval, fout);
15769 : }
15770 :
15771 285 : if (strcmp(aggmfinalfn, "-") != 0)
15772 : {
15773 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15774 : aggmfinalfn);
15775 0 : if (aggmfinalextra)
15776 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15777 0 : if (aggmfinalmodify != defaultfinalmodify)
15778 : {
15779 0 : switch (aggmfinalmodify)
15780 : {
15781 0 : case AGGMODIFY_READ_ONLY:
15782 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15783 0 : break;
15784 0 : case AGGMODIFY_SHAREABLE:
15785 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15786 0 : break;
15787 0 : case AGGMODIFY_READ_WRITE:
15788 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15789 0 : break;
15790 0 : default:
15791 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15792 : agginfo->aggfn.dobj.name);
15793 : break;
15794 : }
15795 : }
15796 : }
15797 :
15798 285 : aggsortconvop = getFormattedOperatorName(aggsortop);
15799 285 : if (aggsortconvop)
15800 : {
15801 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15802 : aggsortconvop);
15803 0 : free(aggsortconvop);
15804 : }
15805 :
15806 285 : if (aggkind == AGGKIND_HYPOTHETICAL)
15807 5 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15808 :
15809 285 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15810 : {
15811 5 : if (proparallel[0] == PROPARALLEL_SAFE)
15812 5 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15813 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15814 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15815 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15816 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15817 : agginfo->aggfn.dobj.name);
15818 : }
15819 :
15820 285 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15821 285 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15822 : aggsig);
15823 :
15824 570 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15825 285 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15826 : aggfullsig ? aggfullsig : aggsig, details->data);
15827 :
15828 285 : if (dopt->binary_upgrade)
15829 49 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15830 : "AGGREGATE", aggsig,
15831 49 : agginfo->aggfn.dobj.namespace->dobj.name);
15832 :
15833 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15834 268 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15835 268 : agginfo->aggfn.dobj.dumpId,
15836 268 : ARCHIVE_OPTS(.tag = aggsig_tag,
15837 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15838 : .owner = agginfo->aggfn.rolname,
15839 : .description = "AGGREGATE",
15840 : .section = SECTION_PRE_DATA,
15841 : .createStmt = q->data,
15842 : .dropStmt = delq->data));
15843 :
15844 : /* Dump Aggregate Comments */
15845 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15846 10 : dumpComment(fout, "AGGREGATE", aggsig,
15847 10 : agginfo->aggfn.dobj.namespace->dobj.name,
15848 10 : agginfo->aggfn.rolname,
15849 10 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15850 :
15851 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15852 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15853 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15854 0 : agginfo->aggfn.rolname,
15855 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15856 :
15857 : /*
15858 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15859 : * command look like a function's GRANT; in particular this affects the
15860 : * syntax for zero-argument aggregates and ordered-set aggregates.
15861 : */
15862 285 : free(aggsig);
15863 :
15864 285 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15865 :
15866 285 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15867 18 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15868 : "FUNCTION", aggsig, NULL,
15869 18 : agginfo->aggfn.dobj.namespace->dobj.name,
15870 18 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15871 :
15872 285 : free(aggsig);
15873 285 : free(aggfullsig);
15874 285 : free(aggsig_tag);
15875 :
15876 285 : PQclear(res);
15877 :
15878 285 : destroyPQExpBuffer(query);
15879 285 : destroyPQExpBuffer(q);
15880 285 : destroyPQExpBuffer(delq);
15881 285 : destroyPQExpBuffer(details);
15882 : }
15883 :
15884 : /*
15885 : * dumpTSParser
15886 : * write out a single text search parser
15887 : */
15888 : static void
15889 41 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15890 : {
15891 41 : DumpOptions *dopt = fout->dopt;
15892 : PQExpBuffer q;
15893 : PQExpBuffer delq;
15894 : char *qprsname;
15895 :
15896 : /* Do nothing if not dumping schema */
15897 41 : if (!dopt->dumpSchema)
15898 6 : return;
15899 :
15900 35 : q = createPQExpBuffer();
15901 35 : delq = createPQExpBuffer();
15902 :
15903 35 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15904 :
15905 35 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15906 35 : fmtQualifiedDumpable(prsinfo));
15907 :
15908 35 : appendPQExpBuffer(q, " START = %s,\n",
15909 35 : convertTSFunction(fout, prsinfo->prsstart));
15910 35 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15911 35 : convertTSFunction(fout, prsinfo->prstoken));
15912 35 : appendPQExpBuffer(q, " END = %s,\n",
15913 35 : convertTSFunction(fout, prsinfo->prsend));
15914 35 : if (prsinfo->prsheadline != InvalidOid)
15915 3 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15916 3 : convertTSFunction(fout, prsinfo->prsheadline));
15917 35 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15918 35 : convertTSFunction(fout, prsinfo->prslextype));
15919 :
15920 35 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15921 35 : fmtQualifiedDumpable(prsinfo));
15922 :
15923 35 : if (dopt->binary_upgrade)
15924 1 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15925 : "TEXT SEARCH PARSER", qprsname,
15926 1 : prsinfo->dobj.namespace->dobj.name);
15927 :
15928 35 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15929 35 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15930 35 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15931 : .namespace = prsinfo->dobj.namespace->dobj.name,
15932 : .description = "TEXT SEARCH PARSER",
15933 : .section = SECTION_PRE_DATA,
15934 : .createStmt = q->data,
15935 : .dropStmt = delq->data));
15936 :
15937 : /* Dump Parser Comments */
15938 35 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15939 35 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15940 35 : prsinfo->dobj.namespace->dobj.name, "",
15941 35 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15942 :
15943 35 : destroyPQExpBuffer(q);
15944 35 : destroyPQExpBuffer(delq);
15945 35 : free(qprsname);
15946 : }
15947 :
15948 : /*
15949 : * dumpTSDictionary
15950 : * write out a single text search dictionary
15951 : */
15952 : static void
15953 179 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15954 : {
15955 179 : DumpOptions *dopt = fout->dopt;
15956 : PQExpBuffer q;
15957 : PQExpBuffer delq;
15958 : PQExpBuffer query;
15959 : char *qdictname;
15960 : PGresult *res;
15961 : char *nspname;
15962 : char *tmplname;
15963 :
15964 : /* Do nothing if not dumping schema */
15965 179 : if (!dopt->dumpSchema)
15966 6 : return;
15967 :
15968 173 : q = createPQExpBuffer();
15969 173 : delq = createPQExpBuffer();
15970 173 : query = createPQExpBuffer();
15971 :
15972 173 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15973 :
15974 : /* Fetch name and namespace of the dictionary's template */
15975 173 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15976 : "FROM pg_ts_template p, pg_namespace n "
15977 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15978 173 : dictinfo->dicttemplate);
15979 173 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15980 173 : nspname = PQgetvalue(res, 0, 0);
15981 173 : tmplname = PQgetvalue(res, 0, 1);
15982 :
15983 173 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15984 173 : fmtQualifiedDumpable(dictinfo));
15985 :
15986 173 : appendPQExpBufferStr(q, " TEMPLATE = ");
15987 173 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15988 173 : appendPQExpBufferStr(q, fmtId(tmplname));
15989 :
15990 173 : PQclear(res);
15991 :
15992 : /* the dictinitoption can be dumped straight into the command */
15993 173 : if (dictinfo->dictinitoption)
15994 138 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15995 :
15996 173 : appendPQExpBufferStr(q, " );\n");
15997 :
15998 173 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15999 173 : fmtQualifiedDumpable(dictinfo));
16000 :
16001 173 : if (dopt->binary_upgrade)
16002 10 : binary_upgrade_extension_member(q, &dictinfo->dobj,
16003 : "TEXT SEARCH DICTIONARY", qdictname,
16004 10 : dictinfo->dobj.namespace->dobj.name);
16005 :
16006 173 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16007 173 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
16008 173 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
16009 : .namespace = dictinfo->dobj.namespace->dobj.name,
16010 : .owner = dictinfo->rolname,
16011 : .description = "TEXT SEARCH DICTIONARY",
16012 : .section = SECTION_PRE_DATA,
16013 : .createStmt = q->data,
16014 : .dropStmt = delq->data));
16015 :
16016 : /* Dump Dictionary Comments */
16017 173 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16018 128 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
16019 128 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
16020 128 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
16021 :
16022 173 : destroyPQExpBuffer(q);
16023 173 : destroyPQExpBuffer(delq);
16024 173 : destroyPQExpBuffer(query);
16025 173 : free(qdictname);
16026 : }
16027 :
16028 : /*
16029 : * dumpTSTemplate
16030 : * write out a single text search template
16031 : */
16032 : static void
16033 53 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
16034 : {
16035 53 : DumpOptions *dopt = fout->dopt;
16036 : PQExpBuffer q;
16037 : PQExpBuffer delq;
16038 : char *qtmplname;
16039 :
16040 : /* Do nothing if not dumping schema */
16041 53 : if (!dopt->dumpSchema)
16042 6 : return;
16043 :
16044 47 : q = createPQExpBuffer();
16045 47 : delq = createPQExpBuffer();
16046 :
16047 47 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
16048 :
16049 47 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
16050 47 : fmtQualifiedDumpable(tmplinfo));
16051 :
16052 47 : if (tmplinfo->tmplinit != InvalidOid)
16053 15 : appendPQExpBuffer(q, " INIT = %s,\n",
16054 15 : convertTSFunction(fout, tmplinfo->tmplinit));
16055 47 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
16056 47 : convertTSFunction(fout, tmplinfo->tmpllexize));
16057 :
16058 47 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
16059 47 : fmtQualifiedDumpable(tmplinfo));
16060 :
16061 47 : if (dopt->binary_upgrade)
16062 1 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
16063 : "TEXT SEARCH TEMPLATE", qtmplname,
16064 1 : tmplinfo->dobj.namespace->dobj.name);
16065 :
16066 47 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16067 47 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
16068 47 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
16069 : .namespace = tmplinfo->dobj.namespace->dobj.name,
16070 : .description = "TEXT SEARCH TEMPLATE",
16071 : .section = SECTION_PRE_DATA,
16072 : .createStmt = q->data,
16073 : .dropStmt = delq->data));
16074 :
16075 : /* Dump Template Comments */
16076 47 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16077 47 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
16078 47 : tmplinfo->dobj.namespace->dobj.name, "",
16079 47 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
16080 :
16081 47 : destroyPQExpBuffer(q);
16082 47 : destroyPQExpBuffer(delq);
16083 47 : free(qtmplname);
16084 : }
16085 :
16086 : /*
16087 : * dumpTSConfig
16088 : * write out a single text search configuration
16089 : */
16090 : static void
16091 154 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
16092 : {
16093 154 : DumpOptions *dopt = fout->dopt;
16094 : PQExpBuffer q;
16095 : PQExpBuffer delq;
16096 : PQExpBuffer query;
16097 : char *qcfgname;
16098 : PGresult *res;
16099 : char *nspname;
16100 : char *prsname;
16101 : int ntups,
16102 : i;
16103 : int i_tokenname;
16104 : int i_dictname;
16105 :
16106 : /* Do nothing if not dumping schema */
16107 154 : if (!dopt->dumpSchema)
16108 6 : return;
16109 :
16110 148 : q = createPQExpBuffer();
16111 148 : delq = createPQExpBuffer();
16112 148 : query = createPQExpBuffer();
16113 :
16114 148 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
16115 :
16116 : /* Fetch name and namespace of the config's parser */
16117 148 : appendPQExpBuffer(query, "SELECT nspname, prsname "
16118 : "FROM pg_ts_parser p, pg_namespace n "
16119 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
16120 148 : cfginfo->cfgparser);
16121 148 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16122 148 : nspname = PQgetvalue(res, 0, 0);
16123 148 : prsname = PQgetvalue(res, 0, 1);
16124 :
16125 148 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
16126 148 : fmtQualifiedDumpable(cfginfo));
16127 :
16128 148 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
16129 148 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
16130 :
16131 148 : PQclear(res);
16132 :
16133 148 : resetPQExpBuffer(query);
16134 148 : appendPQExpBuffer(query,
16135 : "SELECT\n"
16136 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
16137 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
16138 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
16139 : "FROM pg_catalog.pg_ts_config_map AS m\n"
16140 : "WHERE m.mapcfg = '%u'\n"
16141 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
16142 148 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
16143 :
16144 148 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16145 148 : ntups = PQntuples(res);
16146 :
16147 148 : i_tokenname = PQfnumber(res, "tokenname");
16148 148 : i_dictname = PQfnumber(res, "dictname");
16149 :
16150 3095 : for (i = 0; i < ntups; i++)
16151 : {
16152 2947 : char *tokenname = PQgetvalue(res, i, i_tokenname);
16153 2947 : char *dictname = PQgetvalue(res, i, i_dictname);
16154 :
16155 2947 : if (i == 0 ||
16156 2799 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
16157 : {
16158 : /* starting a new token type, so start a new command */
16159 2812 : if (i > 0)
16160 2664 : appendPQExpBufferStr(q, ";\n");
16161 2812 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16162 2812 : fmtQualifiedDumpable(cfginfo));
16163 : /* tokenname needs quoting, dictname does NOT */
16164 2812 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16165 : fmtId(tokenname), dictname);
16166 : }
16167 : else
16168 135 : appendPQExpBuffer(q, ", %s", dictname);
16169 : }
16170 :
16171 148 : if (ntups > 0)
16172 148 : appendPQExpBufferStr(q, ";\n");
16173 :
16174 148 : PQclear(res);
16175 :
16176 148 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16177 148 : fmtQualifiedDumpable(cfginfo));
16178 :
16179 148 : if (dopt->binary_upgrade)
16180 5 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16181 : "TEXT SEARCH CONFIGURATION", qcfgname,
16182 5 : cfginfo->dobj.namespace->dobj.name);
16183 :
16184 148 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16185 148 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16186 148 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16187 : .namespace = cfginfo->dobj.namespace->dobj.name,
16188 : .owner = cfginfo->rolname,
16189 : .description = "TEXT SEARCH CONFIGURATION",
16190 : .section = SECTION_PRE_DATA,
16191 : .createStmt = q->data,
16192 : .dropStmt = delq->data));
16193 :
16194 : /* Dump Configuration Comments */
16195 148 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16196 128 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16197 128 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16198 128 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16199 :
16200 148 : destroyPQExpBuffer(q);
16201 148 : destroyPQExpBuffer(delq);
16202 148 : destroyPQExpBuffer(query);
16203 148 : free(qcfgname);
16204 : }
16205 :
16206 : /*
16207 : * dumpForeignDataWrapper
16208 : * write out a single foreign-data wrapper definition
16209 : */
16210 : static void
16211 52 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16212 : {
16213 52 : DumpOptions *dopt = fout->dopt;
16214 : PQExpBuffer q;
16215 : PQExpBuffer delq;
16216 : char *qfdwname;
16217 :
16218 : /* Do nothing if not dumping schema */
16219 52 : if (!dopt->dumpSchema)
16220 7 : return;
16221 :
16222 45 : q = createPQExpBuffer();
16223 45 : delq = createPQExpBuffer();
16224 :
16225 45 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16226 :
16227 45 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16228 : qfdwname);
16229 :
16230 45 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16231 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16232 :
16233 45 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16234 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16235 :
16236 45 : if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16237 0 : appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16238 :
16239 45 : if (strlen(fdwinfo->fdwoptions) > 0)
16240 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16241 :
16242 45 : appendPQExpBufferStr(q, ";\n");
16243 :
16244 45 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16245 : qfdwname);
16246 :
16247 45 : if (dopt->binary_upgrade)
16248 2 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16249 : "FOREIGN DATA WRAPPER", qfdwname,
16250 : NULL);
16251 :
16252 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16253 45 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16254 45 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16255 : .owner = fdwinfo->rolname,
16256 : .description = "FOREIGN DATA WRAPPER",
16257 : .section = SECTION_PRE_DATA,
16258 : .createStmt = q->data,
16259 : .dropStmt = delq->data));
16260 :
16261 : /* Dump Foreign Data Wrapper Comments */
16262 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16263 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16264 0 : NULL, fdwinfo->rolname,
16265 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16266 :
16267 : /* Handle the ACL */
16268 45 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16269 31 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16270 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16271 31 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16272 :
16273 45 : free(qfdwname);
16274 :
16275 45 : destroyPQExpBuffer(q);
16276 45 : destroyPQExpBuffer(delq);
16277 : }
16278 :
16279 : /*
16280 : * dumpForeignServer
16281 : * write out a foreign server definition
16282 : */
16283 : static void
16284 56 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16285 : {
16286 56 : DumpOptions *dopt = fout->dopt;
16287 : PQExpBuffer q;
16288 : PQExpBuffer delq;
16289 : PQExpBuffer query;
16290 : PGresult *res;
16291 : char *qsrvname;
16292 : char *fdwname;
16293 :
16294 : /* Do nothing if not dumping schema */
16295 56 : if (!dopt->dumpSchema)
16296 9 : return;
16297 :
16298 47 : q = createPQExpBuffer();
16299 47 : delq = createPQExpBuffer();
16300 47 : query = createPQExpBuffer();
16301 :
16302 47 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16303 :
16304 : /* look up the foreign-data wrapper */
16305 47 : appendPQExpBuffer(query, "SELECT fdwname "
16306 : "FROM pg_foreign_data_wrapper w "
16307 : "WHERE w.oid = '%u'",
16308 47 : srvinfo->srvfdw);
16309 47 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16310 47 : fdwname = PQgetvalue(res, 0, 0);
16311 :
16312 47 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16313 47 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16314 : {
16315 0 : appendPQExpBufferStr(q, " TYPE ");
16316 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16317 : }
16318 47 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16319 : {
16320 0 : appendPQExpBufferStr(q, " VERSION ");
16321 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16322 : }
16323 :
16324 47 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16325 47 : appendPQExpBufferStr(q, fmtId(fdwname));
16326 :
16327 47 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16328 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16329 :
16330 47 : appendPQExpBufferStr(q, ";\n");
16331 :
16332 47 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16333 : qsrvname);
16334 :
16335 47 : if (dopt->binary_upgrade)
16336 2 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16337 : "SERVER", qsrvname, NULL);
16338 :
16339 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16340 47 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16341 47 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16342 : .owner = srvinfo->rolname,
16343 : .description = "SERVER",
16344 : .section = SECTION_PRE_DATA,
16345 : .createStmt = q->data,
16346 : .dropStmt = delq->data));
16347 :
16348 : /* Dump Foreign Server Comments */
16349 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16350 0 : dumpComment(fout, "SERVER", qsrvname,
16351 0 : NULL, srvinfo->rolname,
16352 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16353 :
16354 : /* Handle the ACL */
16355 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16356 31 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16357 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16358 31 : NULL, srvinfo->rolname, &srvinfo->dacl);
16359 :
16360 : /* Dump user mappings */
16361 47 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16362 47 : dumpUserMappings(fout,
16363 47 : srvinfo->dobj.name, NULL,
16364 47 : srvinfo->rolname,
16365 47 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16366 :
16367 47 : PQclear(res);
16368 :
16369 47 : free(qsrvname);
16370 :
16371 47 : destroyPQExpBuffer(q);
16372 47 : destroyPQExpBuffer(delq);
16373 47 : destroyPQExpBuffer(query);
16374 : }
16375 :
16376 : /*
16377 : * dumpUserMappings
16378 : *
16379 : * This routine is used to dump any user mappings associated with the
16380 : * server handed to this routine. Should be called after ArchiveEntry()
16381 : * for the server.
16382 : */
16383 : static void
16384 47 : dumpUserMappings(Archive *fout,
16385 : const char *servername, const char *namespace,
16386 : const char *owner,
16387 : CatalogId catalogId, DumpId dumpId)
16388 : {
16389 : PQExpBuffer q;
16390 : PQExpBuffer delq;
16391 : PQExpBuffer query;
16392 : PQExpBuffer tag;
16393 : PGresult *res;
16394 : int ntups;
16395 : int i_usename;
16396 : int i_umoptions;
16397 : int i;
16398 :
16399 47 : q = createPQExpBuffer();
16400 47 : tag = createPQExpBuffer();
16401 47 : delq = createPQExpBuffer();
16402 47 : query = createPQExpBuffer();
16403 :
16404 : /*
16405 : * We read from the publicly accessible view pg_user_mappings, so as not
16406 : * to fail if run by a non-superuser. Note that the view will show
16407 : * umoptions as null if the user hasn't got privileges for the associated
16408 : * server; this means that pg_dump will dump such a mapping, but with no
16409 : * OPTIONS clause. A possible alternative is to skip such mappings
16410 : * altogether, but it's not clear that that's an improvement.
16411 : */
16412 47 : appendPQExpBuffer(query,
16413 : "SELECT usename, "
16414 : "array_to_string(ARRAY("
16415 : "SELECT quote_ident(option_name) || ' ' || "
16416 : "quote_literal(option_value) "
16417 : "FROM pg_options_to_table(umoptions) "
16418 : "ORDER BY option_name"
16419 : "), E',\n ') AS umoptions "
16420 : "FROM pg_user_mappings "
16421 : "WHERE srvid = '%u' "
16422 : "ORDER BY usename",
16423 : catalogId.oid);
16424 :
16425 47 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16426 :
16427 47 : ntups = PQntuples(res);
16428 47 : i_usename = PQfnumber(res, "usename");
16429 47 : i_umoptions = PQfnumber(res, "umoptions");
16430 :
16431 78 : for (i = 0; i < ntups; i++)
16432 : {
16433 : char *usename;
16434 : char *umoptions;
16435 :
16436 31 : usename = PQgetvalue(res, i, i_usename);
16437 31 : umoptions = PQgetvalue(res, i, i_umoptions);
16438 :
16439 31 : resetPQExpBuffer(q);
16440 31 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16441 31 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16442 :
16443 31 : if (umoptions && strlen(umoptions) > 0)
16444 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16445 :
16446 31 : appendPQExpBufferStr(q, ";\n");
16447 :
16448 31 : resetPQExpBuffer(delq);
16449 31 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16450 31 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16451 :
16452 31 : resetPQExpBuffer(tag);
16453 31 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16454 : usename, servername);
16455 :
16456 31 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16457 31 : ARCHIVE_OPTS(.tag = tag->data,
16458 : .namespace = namespace,
16459 : .owner = owner,
16460 : .description = "USER MAPPING",
16461 : .section = SECTION_PRE_DATA,
16462 : .createStmt = q->data,
16463 : .dropStmt = delq->data));
16464 : }
16465 :
16466 47 : PQclear(res);
16467 :
16468 47 : destroyPQExpBuffer(query);
16469 47 : destroyPQExpBuffer(delq);
16470 47 : destroyPQExpBuffer(tag);
16471 47 : destroyPQExpBuffer(q);
16472 47 : }
16473 :
16474 : /*
16475 : * Write out default privileges information
16476 : */
16477 : static void
16478 160 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16479 : {
16480 160 : DumpOptions *dopt = fout->dopt;
16481 : PQExpBuffer q;
16482 : PQExpBuffer tag;
16483 : const char *type;
16484 :
16485 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16486 160 : if (!dopt->dumpSchema || dopt->aclsSkip)
16487 28 : return;
16488 :
16489 132 : q = createPQExpBuffer();
16490 132 : tag = createPQExpBuffer();
16491 :
16492 132 : switch (daclinfo->defaclobjtype)
16493 : {
16494 61 : case DEFACLOBJ_RELATION:
16495 61 : type = "TABLES";
16496 61 : break;
16497 0 : case DEFACLOBJ_SEQUENCE:
16498 0 : type = "SEQUENCES";
16499 0 : break;
16500 61 : case DEFACLOBJ_FUNCTION:
16501 61 : type = "FUNCTIONS";
16502 61 : break;
16503 10 : case DEFACLOBJ_TYPE:
16504 10 : type = "TYPES";
16505 10 : break;
16506 0 : case DEFACLOBJ_NAMESPACE:
16507 0 : type = "SCHEMAS";
16508 0 : break;
16509 0 : case DEFACLOBJ_LARGEOBJECT:
16510 0 : type = "LARGE OBJECTS";
16511 0 : break;
16512 0 : default:
16513 : /* shouldn't get here */
16514 0 : pg_fatal("unrecognized object type in default privileges: %d",
16515 : (int) daclinfo->defaclobjtype);
16516 : type = ""; /* keep compiler quiet */
16517 : }
16518 :
16519 132 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16520 :
16521 : /* build the actual command(s) for this tuple */
16522 132 : if (!buildDefaultACLCommands(type,
16523 132 : daclinfo->dobj.namespace != NULL ?
16524 62 : daclinfo->dobj.namespace->dobj.name : NULL,
16525 132 : daclinfo->dacl.acl,
16526 132 : daclinfo->dacl.acldefault,
16527 132 : daclinfo->defaclrole,
16528 : fout->remoteVersion,
16529 : q))
16530 0 : pg_fatal("could not parse default ACL list (%s)",
16531 : daclinfo->dacl.acl);
16532 :
16533 132 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16534 132 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16535 132 : ARCHIVE_OPTS(.tag = tag->data,
16536 : .namespace = daclinfo->dobj.namespace ?
16537 : daclinfo->dobj.namespace->dobj.name : NULL,
16538 : .owner = daclinfo->defaclrole,
16539 : .description = "DEFAULT ACL",
16540 : .section = SECTION_POST_DATA,
16541 : .createStmt = q->data));
16542 :
16543 132 : destroyPQExpBuffer(tag);
16544 132 : destroyPQExpBuffer(q);
16545 : }
16546 :
16547 : /*----------
16548 : * Write out grant/revoke information
16549 : *
16550 : * 'objDumpId' is the dump ID of the underlying object.
16551 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16552 : * or InvalidDumpId if there is no need for a second dependency.
16553 : * 'type' must be one of
16554 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16555 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16556 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16557 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16558 : * (Currently we assume that subname is only provided for table columns.)
16559 : * 'nspname' is the namespace the object is in (NULL if none).
16560 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16561 : * to use the default for the object type.
16562 : * 'owner' is the owner, NULL if there is no owner (for languages).
16563 : * 'dacl' is the DumpableAcl struct for the object.
16564 : *
16565 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16566 : * no ACL entry was created.
16567 : *----------
16568 : */
16569 : static DumpId
16570 43681 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16571 : const char *type, const char *name, const char *subname,
16572 : const char *nspname, const char *tag, const char *owner,
16573 : const DumpableAcl *dacl)
16574 : {
16575 43681 : DumpId aclDumpId = InvalidDumpId;
16576 43681 : DumpOptions *dopt = fout->dopt;
16577 43681 : const char *acls = dacl->acl;
16578 43681 : const char *acldefault = dacl->acldefault;
16579 43681 : char privtype = dacl->privtype;
16580 43681 : const char *initprivs = dacl->initprivs;
16581 : const char *baseacls;
16582 : PQExpBuffer sql;
16583 :
16584 : /* Do nothing if ACL dump is not enabled */
16585 43681 : if (dopt->aclsSkip)
16586 347 : return InvalidDumpId;
16587 :
16588 : /* --data-only skips ACLs *except* large object ACLs */
16589 43334 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16590 0 : return InvalidDumpId;
16591 :
16592 43334 : sql = createPQExpBuffer();
16593 :
16594 : /*
16595 : * In binary upgrade mode, we don't run an extension's script but instead
16596 : * dump out the objects independently and then recreate them. To preserve
16597 : * any initial privileges which were set on extension objects, we need to
16598 : * compute the set of GRANT and REVOKE commands necessary to get from the
16599 : * default privileges of an object to its initial privileges as recorded
16600 : * in pg_init_privs.
16601 : *
16602 : * At restore time, we apply these commands after having called
16603 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16604 : * copy the results into pg_init_privs. This is how we preserve the
16605 : * contents of that catalog across binary upgrades.
16606 : */
16607 43334 : if (dopt->binary_upgrade && privtype == 'e' &&
16608 13 : initprivs && *initprivs != '\0')
16609 : {
16610 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16611 13 : if (!buildACLCommands(name, subname, nspname, type,
16612 : initprivs, acldefault, owner,
16613 : "", fout->remoteVersion, sql))
16614 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16615 : initprivs, acldefault, name, type);
16616 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16617 : }
16618 :
16619 : /*
16620 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16621 : * actual current ACL, starting from the initprivs if given, else from the
16622 : * object-type-specific default. Also, while buildACLCommands will assume
16623 : * that a NULL/empty acls string means it needn't do anything, what that
16624 : * actually represents is the object-type-specific default; so we need to
16625 : * substitute the acldefault string to get the right results in that case.
16626 : */
16627 43334 : if (initprivs && *initprivs != '\0')
16628 : {
16629 41416 : baseacls = initprivs;
16630 41416 : if (acls == NULL || *acls == '\0')
16631 17 : acls = acldefault;
16632 : }
16633 : else
16634 1918 : baseacls = acldefault;
16635 :
16636 43334 : if (!buildACLCommands(name, subname, nspname, type,
16637 : acls, baseacls, owner,
16638 : "", fout->remoteVersion, sql))
16639 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16640 : acls, baseacls, name, type);
16641 :
16642 43334 : if (sql->len > 0)
16643 : {
16644 1928 : PQExpBuffer tagbuf = createPQExpBuffer();
16645 : DumpId aclDeps[2];
16646 1928 : int nDeps = 0;
16647 :
16648 1928 : if (tag)
16649 0 : appendPQExpBufferStr(tagbuf, tag);
16650 1928 : else if (subname)
16651 1047 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16652 : else
16653 881 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16654 :
16655 1928 : aclDeps[nDeps++] = objDumpId;
16656 1928 : if (altDumpId != InvalidDumpId)
16657 977 : aclDeps[nDeps++] = altDumpId;
16658 :
16659 1928 : aclDumpId = createDumpId();
16660 :
16661 1928 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16662 1928 : ARCHIVE_OPTS(.tag = tagbuf->data,
16663 : .namespace = nspname,
16664 : .owner = owner,
16665 : .description = "ACL",
16666 : .section = SECTION_NONE,
16667 : .createStmt = sql->data,
16668 : .deps = aclDeps,
16669 : .nDeps = nDeps));
16670 :
16671 1928 : destroyPQExpBuffer(tagbuf);
16672 : }
16673 :
16674 43334 : destroyPQExpBuffer(sql);
16675 :
16676 43334 : return aclDumpId;
16677 : }
16678 :
16679 : /*
16680 : * dumpSecLabel
16681 : *
16682 : * This routine is used to dump any security labels associated with the
16683 : * object handed to this routine. The routine takes the object type
16684 : * and object name (ready to print, except for schema decoration), plus
16685 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16686 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16687 : * plus the dump ID for the object (for setting a dependency).
16688 : * If a matching pg_seclabel entry is found, it is dumped.
16689 : *
16690 : * Note: although this routine takes a dumpId for dependency purposes,
16691 : * that purpose is just to mark the dependency in the emitted dump file
16692 : * for possible future use by pg_restore. We do NOT use it for determining
16693 : * ordering of the label in the dump file, because this routine is called
16694 : * after dependency sorting occurs. This routine should be called just after
16695 : * calling ArchiveEntry() for the specified object.
16696 : */
16697 : static void
16698 10 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16699 : const char *namespace, const char *owner,
16700 : CatalogId catalogId, int subid, DumpId dumpId)
16701 : {
16702 10 : DumpOptions *dopt = fout->dopt;
16703 : SecLabelItem *labels;
16704 : int nlabels;
16705 : int i;
16706 : PQExpBuffer query;
16707 :
16708 : /* do nothing, if --no-security-labels is supplied */
16709 10 : if (dopt->no_security_labels)
16710 0 : return;
16711 :
16712 : /*
16713 : * Security labels are schema not data ... except large object labels are
16714 : * data
16715 : */
16716 10 : if (strcmp(type, "LARGE OBJECT") != 0)
16717 : {
16718 0 : if (!dopt->dumpSchema)
16719 0 : return;
16720 : }
16721 : else
16722 : {
16723 : /* We do dump large object security labels in binary-upgrade mode */
16724 10 : if (!dopt->dumpData && !dopt->binary_upgrade)
16725 0 : return;
16726 : }
16727 :
16728 : /* Search for security labels associated with catalogId, using table */
16729 10 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16730 :
16731 10 : query = createPQExpBuffer();
16732 :
16733 15 : for (i = 0; i < nlabels; i++)
16734 : {
16735 : /*
16736 : * Ignore label entries for which the subid doesn't match.
16737 : */
16738 5 : if (labels[i].objsubid != subid)
16739 0 : continue;
16740 :
16741 5 : appendPQExpBuffer(query,
16742 : "SECURITY LABEL FOR %s ON %s ",
16743 5 : fmtId(labels[i].provider), type);
16744 5 : if (namespace && *namespace)
16745 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16746 5 : appendPQExpBuffer(query, "%s IS ", name);
16747 5 : appendStringLiteralAH(query, labels[i].label, fout);
16748 5 : appendPQExpBufferStr(query, ";\n");
16749 : }
16750 :
16751 10 : if (query->len > 0)
16752 : {
16753 5 : PQExpBuffer tag = createPQExpBuffer();
16754 :
16755 5 : appendPQExpBuffer(tag, "%s %s", type, name);
16756 5 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16757 5 : ARCHIVE_OPTS(.tag = tag->data,
16758 : .namespace = namespace,
16759 : .owner = owner,
16760 : .description = "SECURITY LABEL",
16761 : .section = SECTION_NONE,
16762 : .createStmt = query->data,
16763 : .deps = &dumpId,
16764 : .nDeps = 1));
16765 5 : destroyPQExpBuffer(tag);
16766 : }
16767 :
16768 10 : destroyPQExpBuffer(query);
16769 : }
16770 :
16771 : /*
16772 : * dumpTableSecLabel
16773 : *
16774 : * As above, but dump security label for both the specified table (or view)
16775 : * and its columns.
16776 : */
16777 : static void
16778 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16779 : {
16780 0 : DumpOptions *dopt = fout->dopt;
16781 : SecLabelItem *labels;
16782 : int nlabels;
16783 : int i;
16784 : PQExpBuffer query;
16785 : PQExpBuffer target;
16786 :
16787 : /* do nothing, if --no-security-labels is supplied */
16788 0 : if (dopt->no_security_labels)
16789 0 : return;
16790 :
16791 : /* SecLabel are SCHEMA not data */
16792 0 : if (!dopt->dumpSchema)
16793 0 : return;
16794 :
16795 : /* Search for comments associated with relation, using table */
16796 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16797 0 : tbinfo->dobj.catId.oid,
16798 : &labels);
16799 :
16800 : /* If security labels exist, build SECURITY LABEL statements */
16801 0 : if (nlabels <= 0)
16802 0 : return;
16803 :
16804 0 : query = createPQExpBuffer();
16805 0 : target = createPQExpBuffer();
16806 :
16807 0 : for (i = 0; i < nlabels; i++)
16808 : {
16809 : const char *colname;
16810 0 : const char *provider = labels[i].provider;
16811 0 : const char *label = labels[i].label;
16812 0 : int objsubid = labels[i].objsubid;
16813 :
16814 0 : resetPQExpBuffer(target);
16815 0 : if (objsubid == 0)
16816 : {
16817 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16818 0 : fmtQualifiedDumpable(tbinfo));
16819 : }
16820 : else
16821 : {
16822 0 : colname = getAttrName(objsubid, tbinfo);
16823 : /* first fmtXXX result must be consumed before calling again */
16824 0 : appendPQExpBuffer(target, "COLUMN %s",
16825 0 : fmtQualifiedDumpable(tbinfo));
16826 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16827 : }
16828 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16829 : fmtId(provider), target->data);
16830 0 : appendStringLiteralAH(query, label, fout);
16831 0 : appendPQExpBufferStr(query, ";\n");
16832 : }
16833 0 : if (query->len > 0)
16834 : {
16835 0 : resetPQExpBuffer(target);
16836 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16837 0 : fmtId(tbinfo->dobj.name));
16838 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16839 0 : ARCHIVE_OPTS(.tag = target->data,
16840 : .namespace = tbinfo->dobj.namespace->dobj.name,
16841 : .owner = tbinfo->rolname,
16842 : .description = "SECURITY LABEL",
16843 : .section = SECTION_NONE,
16844 : .createStmt = query->data,
16845 : .deps = &(tbinfo->dobj.dumpId),
16846 : .nDeps = 1));
16847 : }
16848 0 : destroyPQExpBuffer(query);
16849 0 : destroyPQExpBuffer(target);
16850 : }
16851 :
16852 : /*
16853 : * findSecLabels
16854 : *
16855 : * Find the security label(s), if any, associated with the given object.
16856 : * All the objsubid values associated with the given classoid/objoid are
16857 : * found with one search.
16858 : */
16859 : static int
16860 10 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16861 : {
16862 10 : SecLabelItem *middle = NULL;
16863 : SecLabelItem *low;
16864 : SecLabelItem *high;
16865 : int nmatch;
16866 :
16867 10 : if (nseclabels <= 0) /* no labels, so no match is possible */
16868 : {
16869 0 : *items = NULL;
16870 0 : return 0;
16871 : }
16872 :
16873 : /*
16874 : * Do binary search to find some item matching the object.
16875 : */
16876 10 : low = &seclabels[0];
16877 10 : high = &seclabels[nseclabels - 1];
16878 15 : while (low <= high)
16879 : {
16880 10 : middle = low + (high - low) / 2;
16881 :
16882 10 : if (classoid < middle->classoid)
16883 0 : high = middle - 1;
16884 10 : else if (classoid > middle->classoid)
16885 0 : low = middle + 1;
16886 10 : else if (objoid < middle->objoid)
16887 5 : high = middle - 1;
16888 5 : else if (objoid > middle->objoid)
16889 0 : low = middle + 1;
16890 : else
16891 5 : break; /* found a match */
16892 : }
16893 :
16894 10 : if (low > high) /* no matches */
16895 : {
16896 5 : *items = NULL;
16897 5 : return 0;
16898 : }
16899 :
16900 : /*
16901 : * Now determine how many items match the object. The search loop
16902 : * invariant still holds: only items between low and high inclusive could
16903 : * match.
16904 : */
16905 5 : nmatch = 1;
16906 5 : while (middle > low)
16907 : {
16908 0 : if (classoid != middle[-1].classoid ||
16909 0 : objoid != middle[-1].objoid)
16910 : break;
16911 0 : middle--;
16912 0 : nmatch++;
16913 : }
16914 :
16915 5 : *items = middle;
16916 :
16917 5 : middle += nmatch;
16918 5 : while (middle <= high)
16919 : {
16920 0 : if (classoid != middle->classoid ||
16921 0 : objoid != middle->objoid)
16922 : break;
16923 0 : middle++;
16924 0 : nmatch++;
16925 : }
16926 :
16927 5 : return nmatch;
16928 : }
16929 :
16930 : /*
16931 : * collectSecLabels
16932 : *
16933 : * Construct a table of all security labels available for database objects;
16934 : * also set the has-seclabel component flag for each relevant object.
16935 : *
16936 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16937 : */
16938 : static void
16939 259 : collectSecLabels(Archive *fout)
16940 : {
16941 : PGresult *res;
16942 : PQExpBuffer query;
16943 : int i_label;
16944 : int i_provider;
16945 : int i_classoid;
16946 : int i_objoid;
16947 : int i_objsubid;
16948 : int ntups;
16949 : int i;
16950 : DumpableObject *dobj;
16951 :
16952 259 : query = createPQExpBuffer();
16953 :
16954 259 : appendPQExpBufferStr(query,
16955 : "SELECT label, provider, classoid, objoid, objsubid "
16956 : "FROM pg_catalog.pg_seclabels "
16957 : "ORDER BY classoid, objoid, objsubid");
16958 :
16959 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16960 :
16961 : /* Construct lookup table containing OIDs in numeric form */
16962 259 : i_label = PQfnumber(res, "label");
16963 259 : i_provider = PQfnumber(res, "provider");
16964 259 : i_classoid = PQfnumber(res, "classoid");
16965 259 : i_objoid = PQfnumber(res, "objoid");
16966 259 : i_objsubid = PQfnumber(res, "objsubid");
16967 :
16968 259 : ntups = PQntuples(res);
16969 :
16970 259 : seclabels = pg_malloc_array(SecLabelItem, ntups);
16971 259 : nseclabels = 0;
16972 259 : dobj = NULL;
16973 :
16974 264 : for (i = 0; i < ntups; i++)
16975 : {
16976 : CatalogId objId;
16977 : int subid;
16978 :
16979 5 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16980 5 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16981 5 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16982 :
16983 : /* We needn't remember labels that don't match any dumpable object */
16984 5 : if (dobj == NULL ||
16985 0 : dobj->catId.tableoid != objId.tableoid ||
16986 0 : dobj->catId.oid != objId.oid)
16987 5 : dobj = findObjectByCatalogId(objId);
16988 5 : if (dobj == NULL)
16989 0 : continue;
16990 :
16991 : /*
16992 : * Labels on columns of composite types are linked to the type's
16993 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16994 : * in the type's own DumpableObject.
16995 : */
16996 5 : if (subid != 0 && dobj->objType == DO_TABLE &&
16997 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16998 0 : {
16999 : TypeInfo *cTypeInfo;
17000 :
17001 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
17002 0 : if (cTypeInfo)
17003 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
17004 : }
17005 : else
17006 5 : dobj->components |= DUMP_COMPONENT_SECLABEL;
17007 :
17008 5 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
17009 5 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
17010 5 : seclabels[nseclabels].classoid = objId.tableoid;
17011 5 : seclabels[nseclabels].objoid = objId.oid;
17012 5 : seclabels[nseclabels].objsubid = subid;
17013 5 : nseclabels++;
17014 : }
17015 :
17016 259 : PQclear(res);
17017 259 : destroyPQExpBuffer(query);
17018 259 : }
17019 :
17020 : /*
17021 : * dumpTable
17022 : * write out to fout the declarations (not data) of a user-defined table
17023 : */
17024 : static void
17025 44759 : dumpTable(Archive *fout, const TableInfo *tbinfo)
17026 : {
17027 44759 : DumpOptions *dopt = fout->dopt;
17028 44759 : DumpId tableAclDumpId = InvalidDumpId;
17029 : char *namecopy;
17030 :
17031 : /* Do nothing if not dumping schema */
17032 44759 : if (!dopt->dumpSchema)
17033 1612 : return;
17034 :
17035 43147 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17036 : {
17037 7270 : if (tbinfo->relkind == RELKIND_SEQUENCE)
17038 384 : dumpSequence(fout, tbinfo);
17039 : else
17040 6886 : dumpTableSchema(fout, tbinfo);
17041 : }
17042 :
17043 : /* Handle the ACL here */
17044 43147 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
17045 43147 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
17046 : {
17047 : const char *objtype;
17048 :
17049 36708 : switch (tbinfo->relkind)
17050 : {
17051 99 : case RELKIND_SEQUENCE:
17052 99 : objtype = "SEQUENCE";
17053 99 : break;
17054 37 : case RELKIND_PROPGRAPH:
17055 37 : objtype = "PROPERTY GRAPH";
17056 37 : break;
17057 36572 : default:
17058 36572 : objtype = "TABLE";
17059 36572 : break;
17060 : }
17061 :
17062 : tableAclDumpId =
17063 36708 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
17064 : objtype, namecopy, NULL,
17065 36708 : tbinfo->dobj.namespace->dobj.name,
17066 36708 : NULL, tbinfo->rolname, &tbinfo->dacl);
17067 : }
17068 :
17069 : /*
17070 : * Handle column ACLs, if any. Note: we pull these with a separate query
17071 : * rather than trying to fetch them during getTableAttrs, so that we won't
17072 : * miss ACLs on system columns. Doing it this way also allows us to dump
17073 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
17074 : */
17075 43147 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
17076 : {
17077 354 : PQExpBuffer query = createPQExpBuffer();
17078 : PGresult *res;
17079 : int i;
17080 :
17081 354 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
17082 : {
17083 : /* Set up query for column ACLs */
17084 233 : appendPQExpBufferStr(query,
17085 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
17086 :
17087 233 : if (fout->remoteVersion >= 90600)
17088 : {
17089 : /*
17090 : * In principle we should call acldefault('c', relowner) to
17091 : * get the default ACL for a column. However, we don't
17092 : * currently store the numeric OID of the relowner in
17093 : * TableInfo. We could convert the owner name using regrole,
17094 : * but that creates a risk of failure due to concurrent role
17095 : * renames. Given that the default ACL for columns is empty
17096 : * and is likely to stay that way, it's not worth extra cycles
17097 : * and risk to avoid hard-wiring that knowledge here.
17098 : */
17099 233 : appendPQExpBufferStr(query,
17100 : "SELECT at.attname, "
17101 : "at.attacl, "
17102 : "'{}' AS acldefault, "
17103 : "pip.privtype, pip.initprivs "
17104 : "FROM pg_catalog.pg_attribute at "
17105 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
17106 : "(at.attrelid = pip.objoid "
17107 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
17108 : "AND at.attnum = pip.objsubid) "
17109 : "WHERE at.attrelid = $1 AND "
17110 : "NOT at.attisdropped "
17111 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
17112 : "ORDER BY at.attnum");
17113 : }
17114 : else
17115 : {
17116 0 : appendPQExpBufferStr(query,
17117 : "SELECT attname, attacl, '{}' AS acldefault, "
17118 : "NULL AS privtype, NULL AS initprivs "
17119 : "FROM pg_catalog.pg_attribute "
17120 : "WHERE attrelid = $1 AND NOT attisdropped "
17121 : "AND attacl IS NOT NULL "
17122 : "ORDER BY attnum");
17123 : }
17124 :
17125 233 : ExecuteSqlStatement(fout, query->data);
17126 :
17127 233 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
17128 : }
17129 :
17130 354 : printfPQExpBuffer(query,
17131 : "EXECUTE getColumnACLs('%u')",
17132 354 : tbinfo->dobj.catId.oid);
17133 :
17134 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17135 :
17136 6231 : for (i = 0; i < PQntuples(res); i++)
17137 : {
17138 5877 : char *attname = PQgetvalue(res, i, 0);
17139 5877 : char *attacl = PQgetvalue(res, i, 1);
17140 5877 : char *acldefault = PQgetvalue(res, i, 2);
17141 5877 : char privtype = *(PQgetvalue(res, i, 3));
17142 5877 : char *initprivs = PQgetvalue(res, i, 4);
17143 : DumpableAcl coldacl;
17144 : char *attnamecopy;
17145 :
17146 5877 : coldacl.acl = attacl;
17147 5877 : coldacl.acldefault = acldefault;
17148 5877 : coldacl.privtype = privtype;
17149 5877 : coldacl.initprivs = initprivs;
17150 5877 : attnamecopy = pg_strdup(fmtId(attname));
17151 :
17152 : /*
17153 : * Column's GRANT type is always TABLE. Each column ACL depends
17154 : * on the table-level ACL, since we can restore column ACLs in
17155 : * parallel but the table-level ACL has to be done first.
17156 : */
17157 5877 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
17158 : "TABLE", namecopy, attnamecopy,
17159 5877 : tbinfo->dobj.namespace->dobj.name,
17160 5877 : NULL, tbinfo->rolname, &coldacl);
17161 5877 : free(attnamecopy);
17162 : }
17163 354 : PQclear(res);
17164 354 : destroyPQExpBuffer(query);
17165 : }
17166 :
17167 43147 : free(namecopy);
17168 : }
17169 :
17170 : /*
17171 : * Create the AS clause for a view or materialized view. The semicolon is
17172 : * stripped because a materialized view must add a WITH NO DATA clause.
17173 : *
17174 : * This returns a new buffer which must be freed by the caller.
17175 : */
17176 : static PQExpBuffer
17177 903 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
17178 : {
17179 903 : PQExpBuffer query = createPQExpBuffer();
17180 903 : PQExpBuffer result = createPQExpBuffer();
17181 : PGresult *res;
17182 : int len;
17183 :
17184 : /* Fetch the view definition */
17185 903 : appendPQExpBuffer(query,
17186 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17187 903 : tbinfo->dobj.catId.oid);
17188 :
17189 903 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17190 :
17191 903 : if (PQntuples(res) != 1)
17192 : {
17193 0 : if (PQntuples(res) < 1)
17194 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17195 : tbinfo->dobj.name);
17196 : else
17197 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17198 : tbinfo->dobj.name);
17199 : }
17200 :
17201 903 : len = PQgetlength(res, 0, 0);
17202 :
17203 903 : if (len == 0)
17204 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17205 : tbinfo->dobj.name);
17206 :
17207 : /* Strip off the trailing semicolon so that other things may follow. */
17208 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17209 903 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17210 :
17211 903 : PQclear(res);
17212 903 : destroyPQExpBuffer(query);
17213 :
17214 903 : return result;
17215 : }
17216 :
17217 : /*
17218 : * Create a dummy AS clause for a view. This is used when the real view
17219 : * definition has to be postponed because of circular dependencies.
17220 : * We must duplicate the view's external properties -- column names and types
17221 : * (including collation) -- so that it works for subsequent references.
17222 : *
17223 : * This returns a new buffer which must be freed by the caller.
17224 : */
17225 : static PQExpBuffer
17226 20 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17227 : {
17228 20 : PQExpBuffer result = createPQExpBuffer();
17229 : int j;
17230 :
17231 20 : appendPQExpBufferStr(result, "SELECT");
17232 :
17233 40 : for (j = 0; j < tbinfo->numatts; j++)
17234 : {
17235 20 : if (j > 0)
17236 10 : appendPQExpBufferChar(result, ',');
17237 20 : appendPQExpBufferStr(result, "\n ");
17238 :
17239 20 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17240 :
17241 : /*
17242 : * Must add collation if not default for the type, because CREATE OR
17243 : * REPLACE VIEW won't change it
17244 : */
17245 20 : if (OidIsValid(tbinfo->attcollation[j]))
17246 : {
17247 : CollInfo *coll;
17248 :
17249 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17250 0 : if (coll)
17251 0 : appendPQExpBuffer(result, " COLLATE %s",
17252 0 : fmtQualifiedDumpable(coll));
17253 : }
17254 :
17255 20 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17256 : }
17257 :
17258 20 : return result;
17259 : }
17260 :
17261 : /*
17262 : * dumpTableSchema
17263 : * write the declaration (not data) of one user-defined table or view
17264 : */
17265 : static void
17266 6886 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17267 : {
17268 6886 : DumpOptions *dopt = fout->dopt;
17269 6886 : PQExpBuffer q = createPQExpBuffer();
17270 6886 : PQExpBuffer delq = createPQExpBuffer();
17271 6886 : PQExpBuffer extra = createPQExpBuffer();
17272 : char *qrelname;
17273 : char *qualrelname;
17274 : int numParents;
17275 : TableInfo **parents;
17276 : int actual_atts; /* number of attrs in this CREATE statement */
17277 : const char *reltypename;
17278 : char *storage;
17279 : int j,
17280 : k;
17281 :
17282 : /* We had better have loaded per-column details about this table */
17283 : Assert(tbinfo->interesting);
17284 :
17285 6886 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17286 6886 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17287 :
17288 6886 : if (tbinfo->hasoids)
17289 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17290 : qrelname);
17291 :
17292 6886 : if (dopt->binary_upgrade)
17293 948 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17294 :
17295 : /* Is it a table or a view? */
17296 6886 : if (tbinfo->relkind == RELKIND_VIEW)
17297 : {
17298 : PQExpBuffer result;
17299 :
17300 : /*
17301 : * Note: keep this code in sync with the is_view case in dumpRule()
17302 : */
17303 :
17304 568 : reltypename = "VIEW";
17305 :
17306 568 : if (dopt->binary_upgrade)
17307 56 : binary_upgrade_set_pg_class_oids(fout, q,
17308 56 : tbinfo->dobj.catId.oid);
17309 :
17310 568 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17311 :
17312 568 : if (tbinfo->dummy_view)
17313 10 : result = createDummyViewAsClause(fout, tbinfo);
17314 : else
17315 : {
17316 558 : if (nonemptyReloptions(tbinfo->reloptions))
17317 : {
17318 61 : appendPQExpBufferStr(q, " WITH (");
17319 61 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17320 61 : appendPQExpBufferChar(q, ')');
17321 : }
17322 558 : result = createViewAsClause(fout, tbinfo);
17323 : }
17324 568 : appendPQExpBuffer(q, " AS\n%s", result->data);
17325 568 : destroyPQExpBuffer(result);
17326 :
17327 568 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17328 32 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17329 568 : appendPQExpBufferStr(q, ";\n");
17330 : }
17331 6318 : else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17332 : {
17333 97 : PQExpBuffer query = createPQExpBuffer();
17334 : PGresult *res;
17335 : int len;
17336 :
17337 97 : reltypename = "PROPERTY GRAPH";
17338 :
17339 97 : if (dopt->binary_upgrade)
17340 14 : binary_upgrade_set_pg_class_oids(fout, q,
17341 14 : tbinfo->dobj.catId.oid);
17342 :
17343 97 : appendPQExpBuffer(query,
17344 : "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17345 97 : tbinfo->dobj.catId.oid);
17346 :
17347 97 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17348 :
17349 97 : if (PQntuples(res) != 1)
17350 : {
17351 0 : if (PQntuples(res) < 1)
17352 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17353 : tbinfo->dobj.name);
17354 : else
17355 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17356 : tbinfo->dobj.name);
17357 : }
17358 :
17359 97 : len = PQgetlength(res, 0, 0);
17360 :
17361 97 : if (len == 0)
17362 0 : pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17363 : tbinfo->dobj.name);
17364 :
17365 97 : appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17366 :
17367 97 : PQclear(res);
17368 97 : destroyPQExpBuffer(query);
17369 :
17370 97 : appendPQExpBufferStr(q, ";\n");
17371 : }
17372 : else
17373 : {
17374 6221 : char *partkeydef = NULL;
17375 6221 : char *ftoptions = NULL;
17376 6221 : char *srvname = NULL;
17377 6221 : const char *foreign = "";
17378 :
17379 : /*
17380 : * Set reltypename, and collect any relkind-specific data that we
17381 : * didn't fetch during getTables().
17382 : */
17383 6221 : switch (tbinfo->relkind)
17384 : {
17385 596 : case RELKIND_PARTITIONED_TABLE:
17386 : {
17387 596 : PQExpBuffer query = createPQExpBuffer();
17388 : PGresult *res;
17389 :
17390 596 : reltypename = "TABLE";
17391 :
17392 : /* retrieve partition key definition */
17393 596 : appendPQExpBuffer(query,
17394 : "SELECT pg_get_partkeydef('%u')",
17395 596 : tbinfo->dobj.catId.oid);
17396 596 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17397 596 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17398 596 : PQclear(res);
17399 596 : destroyPQExpBuffer(query);
17400 596 : break;
17401 : }
17402 34 : case RELKIND_FOREIGN_TABLE:
17403 : {
17404 34 : PQExpBuffer query = createPQExpBuffer();
17405 : PGresult *res;
17406 : int i_srvname;
17407 : int i_ftoptions;
17408 :
17409 34 : reltypename = "FOREIGN TABLE";
17410 :
17411 : /* retrieve name of foreign server and generic options */
17412 34 : appendPQExpBuffer(query,
17413 : "SELECT fs.srvname, "
17414 : "pg_catalog.array_to_string(ARRAY("
17415 : "SELECT pg_catalog.quote_ident(option_name) || "
17416 : "' ' || pg_catalog.quote_literal(option_value) "
17417 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17418 : "ORDER BY option_name"
17419 : "), E',\n ') AS ftoptions "
17420 : "FROM pg_catalog.pg_foreign_table ft "
17421 : "JOIN pg_catalog.pg_foreign_server fs "
17422 : "ON (fs.oid = ft.ftserver) "
17423 : "WHERE ft.ftrelid = '%u'",
17424 34 : tbinfo->dobj.catId.oid);
17425 34 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17426 34 : i_srvname = PQfnumber(res, "srvname");
17427 34 : i_ftoptions = PQfnumber(res, "ftoptions");
17428 34 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17429 34 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17430 34 : PQclear(res);
17431 34 : destroyPQExpBuffer(query);
17432 :
17433 34 : foreign = "FOREIGN ";
17434 34 : break;
17435 : }
17436 335 : case RELKIND_MATVIEW:
17437 335 : reltypename = "MATERIALIZED VIEW";
17438 335 : break;
17439 5256 : default:
17440 5256 : reltypename = "TABLE";
17441 5256 : break;
17442 : }
17443 :
17444 6221 : numParents = tbinfo->numParents;
17445 6221 : parents = tbinfo->parents;
17446 :
17447 6221 : if (dopt->binary_upgrade)
17448 878 : binary_upgrade_set_pg_class_oids(fout, q,
17449 878 : tbinfo->dobj.catId.oid);
17450 :
17451 : /*
17452 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17453 : * ignore it when dumping if it was set in this case.
17454 : */
17455 6221 : appendPQExpBuffer(q, "CREATE %s%s %s",
17456 6221 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17457 20 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17458 : "UNLOGGED " : "",
17459 : reltypename,
17460 : qualrelname);
17461 :
17462 : /*
17463 : * Attach to type, if reloftype; except in case of a binary upgrade,
17464 : * we dump the table normally and attach it to the type afterward.
17465 : */
17466 6221 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17467 24 : appendPQExpBuffer(q, " OF %s",
17468 24 : getFormattedTypeName(fout, tbinfo->reloftype,
17469 : zeroIsError));
17470 :
17471 6221 : if (tbinfo->relkind != RELKIND_MATVIEW)
17472 : {
17473 : /* Dump the attributes */
17474 5886 : actual_atts = 0;
17475 27121 : for (j = 0; j < tbinfo->numatts; j++)
17476 : {
17477 : /*
17478 : * Normally, dump if it's locally defined in this table, and
17479 : * not dropped. But for binary upgrade, we'll dump all the
17480 : * columns, and then fix up the dropped and nonlocal cases
17481 : * below.
17482 : */
17483 21235 : if (shouldPrintColumn(dopt, tbinfo, j))
17484 : {
17485 : bool print_default;
17486 : bool print_notnull;
17487 :
17488 : /*
17489 : * Default value --- suppress if to be printed separately
17490 : * or not at all.
17491 : */
17492 41374 : print_default = (tbinfo->attrdefs[j] != NULL &&
17493 21189 : tbinfo->attrdefs[j]->dobj.dump &&
17494 1051 : !tbinfo->attrdefs[j]->separate);
17495 :
17496 : /*
17497 : * Not Null constraint --- print it if it is locally
17498 : * defined, or if binary upgrade. (In the latter case, we
17499 : * reset conislocal below.)
17500 : */
17501 22597 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17502 2459 : (tbinfo->notnull_islocal[j] ||
17503 657 : dopt->binary_upgrade ||
17504 563 : tbinfo->ispartition));
17505 :
17506 : /*
17507 : * Skip column if fully defined by reloftype, except in
17508 : * binary upgrade
17509 : */
17510 20138 : if (OidIsValid(tbinfo->reloftype) &&
17511 50 : !print_default && !print_notnull &&
17512 30 : !dopt->binary_upgrade)
17513 24 : continue;
17514 :
17515 : /* Format properly if not first attr */
17516 20114 : if (actual_atts == 0)
17517 5513 : appendPQExpBufferStr(q, " (");
17518 : else
17519 14601 : appendPQExpBufferChar(q, ',');
17520 20114 : appendPQExpBufferStr(q, "\n ");
17521 20114 : actual_atts++;
17522 :
17523 : /* Attribute name */
17524 20114 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17525 :
17526 20114 : if (tbinfo->attisdropped[j])
17527 : {
17528 : /*
17529 : * ALTER TABLE DROP COLUMN clears
17530 : * pg_attribute.atttypid, so we will not have gotten a
17531 : * valid type name; insert INTEGER as a stopgap. We'll
17532 : * clean things up later.
17533 : */
17534 85 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17535 : /* and skip to the next column */
17536 85 : continue;
17537 : }
17538 :
17539 : /*
17540 : * Attribute type; print it except when creating a typed
17541 : * table ('OF type_name'), but in binary-upgrade mode,
17542 : * print it in that case too.
17543 : */
17544 20029 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17545 : {
17546 20013 : appendPQExpBuffer(q, " %s",
17547 20013 : tbinfo->atttypnames[j]);
17548 : }
17549 :
17550 20029 : if (print_default)
17551 : {
17552 921 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17553 317 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17554 317 : tbinfo->attrdefs[j]->adef_expr);
17555 604 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17556 223 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17557 223 : tbinfo->attrdefs[j]->adef_expr);
17558 : else
17559 381 : appendPQExpBuffer(q, " DEFAULT %s",
17560 381 : tbinfo->attrdefs[j]->adef_expr);
17561 : }
17562 :
17563 20029 : if (print_notnull)
17564 : {
17565 2428 : if (tbinfo->notnull_constrs[j][0] == '\0')
17566 1743 : appendPQExpBufferStr(q, " NOT NULL");
17567 : else
17568 685 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17569 685 : fmtId(tbinfo->notnull_constrs[j]));
17570 :
17571 2428 : if (tbinfo->notnull_noinh[j])
17572 33 : appendPQExpBufferStr(q, " NO INHERIT");
17573 : }
17574 :
17575 : /* Add collation if not default for the type */
17576 20029 : if (OidIsValid(tbinfo->attcollation[j]))
17577 : {
17578 : CollInfo *coll;
17579 :
17580 213 : coll = findCollationByOid(tbinfo->attcollation[j]);
17581 213 : if (coll)
17582 213 : appendPQExpBuffer(q, " COLLATE %s",
17583 213 : fmtQualifiedDumpable(coll));
17584 : }
17585 : }
17586 :
17587 : /*
17588 : * On the other hand, if we choose not to print a column
17589 : * (likely because it is created by inheritance), but the
17590 : * column has a locally-defined not-null constraint, we need
17591 : * to dump the constraint as a standalone object.
17592 : *
17593 : * This syntax isn't SQL-conforming, but if you wanted
17594 : * standard output you wouldn't be creating non-standard
17595 : * objects to begin with.
17596 : */
17597 21126 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17598 1097 : !tbinfo->attisdropped[j] &&
17599 730 : tbinfo->notnull_constrs[j] != NULL &&
17600 208 : tbinfo->notnull_islocal[j])
17601 : {
17602 : /* Format properly if not first attr */
17603 90 : if (actual_atts == 0)
17604 86 : appendPQExpBufferStr(q, " (");
17605 : else
17606 4 : appendPQExpBufferChar(q, ',');
17607 90 : appendPQExpBufferStr(q, "\n ");
17608 90 : actual_atts++;
17609 :
17610 90 : if (tbinfo->notnull_constrs[j][0] == '\0')
17611 8 : appendPQExpBuffer(q, "NOT NULL %s",
17612 8 : fmtId(tbinfo->attnames[j]));
17613 : else
17614 164 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17615 82 : tbinfo->notnull_constrs[j],
17616 82 : fmtId(tbinfo->attnames[j]));
17617 :
17618 90 : if (tbinfo->notnull_noinh[j])
17619 31 : appendPQExpBufferStr(q, " NO INHERIT");
17620 : }
17621 : }
17622 :
17623 : /*
17624 : * Add non-inherited CHECK constraints, if any.
17625 : *
17626 : * For partitions, we need to include check constraints even if
17627 : * they're not defined locally, because the ALTER TABLE ATTACH
17628 : * PARTITION that we'll emit later expects the constraint to be
17629 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17630 : */
17631 6459 : for (j = 0; j < tbinfo->ncheck; j++)
17632 : {
17633 573 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17634 :
17635 573 : if (constr->separate ||
17636 503 : (!constr->conislocal && !tbinfo->ispartition))
17637 107 : continue;
17638 :
17639 466 : if (actual_atts == 0)
17640 16 : appendPQExpBufferStr(q, " (\n ");
17641 : else
17642 450 : appendPQExpBufferStr(q, ",\n ");
17643 :
17644 466 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17645 466 : fmtId(constr->dobj.name));
17646 466 : appendPQExpBufferStr(q, constr->condef);
17647 :
17648 466 : actual_atts++;
17649 : }
17650 :
17651 5886 : if (actual_atts)
17652 5615 : appendPQExpBufferStr(q, "\n)");
17653 271 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17654 : {
17655 : /*
17656 : * No attributes? we must have a parenthesized attribute list,
17657 : * even though empty, when not using the OF TYPE syntax.
17658 : */
17659 259 : appendPQExpBufferStr(q, " (\n)");
17660 : }
17661 :
17662 : /*
17663 : * Emit the INHERITS clause (not for partitions), except in
17664 : * binary-upgrade mode.
17665 : */
17666 5886 : if (numParents > 0 && !tbinfo->ispartition &&
17667 541 : !dopt->binary_upgrade)
17668 : {
17669 472 : appendPQExpBufferStr(q, "\nINHERITS (");
17670 1015 : for (k = 0; k < numParents; k++)
17671 : {
17672 543 : TableInfo *parentRel = parents[k];
17673 :
17674 543 : if (k > 0)
17675 71 : appendPQExpBufferStr(q, ", ");
17676 543 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17677 : }
17678 472 : appendPQExpBufferChar(q, ')');
17679 : }
17680 :
17681 5886 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17682 596 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17683 :
17684 5886 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17685 34 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17686 : }
17687 :
17688 12289 : if (nonemptyReloptions(tbinfo->reloptions) ||
17689 6068 : nonemptyReloptions(tbinfo->toast_reloptions))
17690 : {
17691 153 : bool addcomma = false;
17692 :
17693 153 : appendPQExpBufferStr(q, "\nWITH (");
17694 153 : if (nonemptyReloptions(tbinfo->reloptions))
17695 : {
17696 153 : addcomma = true;
17697 153 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17698 : }
17699 153 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17700 : {
17701 5 : if (addcomma)
17702 5 : appendPQExpBufferStr(q, ", ");
17703 5 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17704 : fout);
17705 : }
17706 153 : appendPQExpBufferChar(q, ')');
17707 : }
17708 :
17709 : /* Dump generic options if any */
17710 6221 : if (ftoptions && ftoptions[0])
17711 32 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17712 :
17713 : /*
17714 : * For materialized views, create the AS clause just like a view. At
17715 : * this point, we always mark the view as not populated.
17716 : */
17717 6221 : if (tbinfo->relkind == RELKIND_MATVIEW)
17718 : {
17719 : PQExpBuffer result;
17720 :
17721 335 : result = createViewAsClause(fout, tbinfo);
17722 335 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17723 : result->data);
17724 335 : destroyPQExpBuffer(result);
17725 : }
17726 : else
17727 5886 : appendPQExpBufferStr(q, ";\n");
17728 :
17729 : /* Materialized views can depend on extensions */
17730 6221 : if (tbinfo->relkind == RELKIND_MATVIEW)
17731 335 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17732 : "pg_catalog.pg_class",
17733 : "MATERIALIZED VIEW",
17734 : qualrelname);
17735 :
17736 : /*
17737 : * in binary upgrade mode, update the catalog with any missing values
17738 : * that might be present.
17739 : */
17740 6221 : if (dopt->binary_upgrade)
17741 : {
17742 4209 : for (j = 0; j < tbinfo->numatts; j++)
17743 : {
17744 3331 : if (tbinfo->attmissingval[j][0] != '\0')
17745 : {
17746 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17747 4 : appendPQExpBufferStr(q,
17748 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17749 4 : appendStringLiteralAH(q, qualrelname, fout);
17750 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17751 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17752 4 : appendPQExpBufferChar(q, ',');
17753 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17754 4 : appendPQExpBufferStr(q, ");\n\n");
17755 : }
17756 : }
17757 : }
17758 :
17759 : /*
17760 : * To create binary-compatible heap files, we have to ensure the same
17761 : * physical column order, including dropped columns, as in the
17762 : * original. Therefore, we create dropped columns above and drop them
17763 : * here, also updating their attlen/attalign values so that the
17764 : * dropped column can be skipped properly. (We do not bother with
17765 : * restoring the original attbyval setting.) Also, inheritance
17766 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17767 : * using an INHERITS clause --- the latter would possibly mess up the
17768 : * column order. That also means we have to take care about setting
17769 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17770 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17771 : *
17772 : * We process foreign and partitioned tables here, even though they
17773 : * lack heap storage, because they can participate in inheritance
17774 : * relationships and we want this stuff to be consistent across the
17775 : * inheritance tree. We can exclude indexes, toast tables, sequences
17776 : * and matviews, even though they have storage, because we don't
17777 : * support altering or dropping columns in them, nor can they be part
17778 : * of inheritance trees.
17779 : */
17780 6221 : if (dopt->binary_upgrade &&
17781 878 : (tbinfo->relkind == RELKIND_RELATION ||
17782 115 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17783 114 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17784 : {
17785 : bool firstitem;
17786 : bool firstitem_extra;
17787 :
17788 : /*
17789 : * Drop any dropped columns. Merge the pg_attribute manipulations
17790 : * into a single SQL command, so that we don't cause repeated
17791 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17792 : * relcache bloat while dropping N columns.
17793 : */
17794 861 : resetPQExpBuffer(extra);
17795 861 : firstitem = true;
17796 4171 : for (j = 0; j < tbinfo->numatts; j++)
17797 : {
17798 3310 : if (tbinfo->attisdropped[j])
17799 : {
17800 85 : if (firstitem)
17801 : {
17802 39 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17803 : "UPDATE pg_catalog.pg_attribute\n"
17804 : "SET attlen = v.dlen, "
17805 : "attalign = v.dalign, "
17806 : "attbyval = false\n"
17807 : "FROM (VALUES ");
17808 39 : firstitem = false;
17809 : }
17810 : else
17811 46 : appendPQExpBufferStr(q, ",\n ");
17812 85 : appendPQExpBufferChar(q, '(');
17813 85 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17814 85 : appendPQExpBuffer(q, ", %d, '%c')",
17815 85 : tbinfo->attlen[j],
17816 85 : tbinfo->attalign[j]);
17817 : /* The ALTER ... DROP COLUMN commands must come after */
17818 85 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17819 : foreign, qualrelname);
17820 85 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17821 85 : fmtId(tbinfo->attnames[j]));
17822 : }
17823 : }
17824 861 : if (!firstitem)
17825 : {
17826 39 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17827 : "WHERE attrelid = ");
17828 39 : appendStringLiteralAH(q, qualrelname, fout);
17829 39 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17830 : " AND attname = v.dname;\n");
17831 : /* Now we can issue the actual DROP COLUMN commands */
17832 39 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17833 : }
17834 :
17835 : /*
17836 : * Fix up inherited columns. As above, do the pg_attribute
17837 : * manipulations in a single SQL command.
17838 : */
17839 861 : firstitem = true;
17840 4171 : for (j = 0; j < tbinfo->numatts; j++)
17841 : {
17842 3310 : if (!tbinfo->attisdropped[j] &&
17843 3225 : !tbinfo->attislocal[j])
17844 : {
17845 648 : if (firstitem)
17846 : {
17847 282 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17848 282 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17849 : "SET attislocal = false\n"
17850 : "WHERE attrelid = ");
17851 282 : appendStringLiteralAH(q, qualrelname, fout);
17852 282 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17853 : " AND attname IN (");
17854 282 : firstitem = false;
17855 : }
17856 : else
17857 366 : appendPQExpBufferStr(q, ", ");
17858 648 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17859 : }
17860 : }
17861 861 : if (!firstitem)
17862 282 : appendPQExpBufferStr(q, ");\n");
17863 :
17864 : /*
17865 : * Fix up not-null constraints that come from inheritance. As
17866 : * above, do the pg_constraint manipulations in a single SQL
17867 : * command. (Actually, two in special cases, if we're doing an
17868 : * upgrade from < 18).
17869 : */
17870 861 : firstitem = true;
17871 861 : firstitem_extra = true;
17872 861 : resetPQExpBuffer(extra);
17873 4171 : for (j = 0; j < tbinfo->numatts; j++)
17874 : {
17875 : /*
17876 : * If a not-null constraint comes from inheritance, reset
17877 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17878 : * below. Special hack: in versions < 18, columns with no
17879 : * local definition need their constraint to be matched by
17880 : * column number in conkeys instead of by constraint name,
17881 : * because the latter is not available. (We distinguish the
17882 : * case because the constraint name is the empty string.)
17883 : */
17884 3310 : if (tbinfo->notnull_constrs[j] != NULL &&
17885 333 : !tbinfo->notnull_islocal[j])
17886 : {
17887 94 : if (tbinfo->notnull_constrs[j][0] != '\0')
17888 : {
17889 81 : if (firstitem)
17890 : {
17891 69 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17892 : "SET conislocal = false\n"
17893 : "WHERE contype = 'n' AND conrelid = ");
17894 69 : appendStringLiteralAH(q, qualrelname, fout);
17895 69 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17896 : "conname IN (");
17897 69 : firstitem = false;
17898 : }
17899 : else
17900 12 : appendPQExpBufferStr(q, ", ");
17901 81 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17902 : }
17903 : else
17904 : {
17905 13 : if (firstitem_extra)
17906 : {
17907 13 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17908 : "SET conislocal = false\n"
17909 : "WHERE contype = 'n' AND conrelid = ");
17910 13 : appendStringLiteralAH(extra, qualrelname, fout);
17911 13 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17912 : "conkey IN (");
17913 13 : firstitem_extra = false;
17914 : }
17915 : else
17916 0 : appendPQExpBufferStr(extra, ", ");
17917 13 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17918 : }
17919 : }
17920 : }
17921 861 : if (!firstitem)
17922 69 : appendPQExpBufferStr(q, ");\n");
17923 861 : if (!firstitem_extra)
17924 13 : appendPQExpBufferStr(extra, ");\n");
17925 :
17926 861 : if (extra->len > 0)
17927 13 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17928 :
17929 : /*
17930 : * Add inherited CHECK constraints, if any.
17931 : *
17932 : * For partitions, they were already dumped, and conislocal
17933 : * doesn't need fixing.
17934 : *
17935 : * As above, issue only one direct manipulation of pg_constraint.
17936 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17937 : * commands into one as well, refrain for now due to concern about
17938 : * possible backend memory bloat if there are many such
17939 : * constraints.
17940 : */
17941 861 : resetPQExpBuffer(extra);
17942 861 : firstitem = true;
17943 923 : for (k = 0; k < tbinfo->ncheck; k++)
17944 : {
17945 62 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17946 :
17947 62 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17948 60 : continue;
17949 :
17950 2 : if (firstitem)
17951 2 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17952 2 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17953 : foreign, qualrelname,
17954 2 : fmtId(constr->dobj.name),
17955 : constr->condef);
17956 : /* Update pg_constraint after all the ALTER TABLEs */
17957 2 : if (firstitem)
17958 : {
17959 2 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17960 : "SET conislocal = false\n"
17961 : "WHERE contype = 'c' AND conrelid = ");
17962 2 : appendStringLiteralAH(extra, qualrelname, fout);
17963 2 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17964 2 : appendPQExpBufferStr(extra, " AND conname IN (");
17965 2 : firstitem = false;
17966 : }
17967 : else
17968 0 : appendPQExpBufferStr(extra, ", ");
17969 2 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17970 : }
17971 861 : if (!firstitem)
17972 : {
17973 2 : appendPQExpBufferStr(extra, ");\n");
17974 2 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17975 : }
17976 :
17977 861 : if (numParents > 0 && !tbinfo->ispartition)
17978 : {
17979 69 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17980 149 : for (k = 0; k < numParents; k++)
17981 : {
17982 80 : TableInfo *parentRel = parents[k];
17983 :
17984 80 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17985 : qualrelname,
17986 80 : fmtQualifiedDumpable(parentRel));
17987 : }
17988 : }
17989 :
17990 861 : if (OidIsValid(tbinfo->reloftype))
17991 : {
17992 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17993 6 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17994 : qualrelname,
17995 6 : getFormattedTypeName(fout, tbinfo->reloftype,
17996 : zeroIsError));
17997 : }
17998 : }
17999 :
18000 : /*
18001 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
18002 : * relminmxid of all vacuumable relations. (While vacuum.c processes
18003 : * TOAST tables semi-independently, here we see them only as children
18004 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
18005 : * child toast table is handled below.)
18006 : */
18007 6221 : if (dopt->binary_upgrade &&
18008 878 : (tbinfo->relkind == RELKIND_RELATION ||
18009 115 : tbinfo->relkind == RELKIND_MATVIEW))
18010 : {
18011 780 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
18012 780 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18013 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18014 : "WHERE oid = ",
18015 780 : tbinfo->frozenxid, tbinfo->minmxid);
18016 780 : appendStringLiteralAH(q, qualrelname, fout);
18017 780 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18018 :
18019 780 : if (tbinfo->toast_oid)
18020 : {
18021 : /*
18022 : * The toast table will have the same OID at restore, so we
18023 : * can safely target it by OID.
18024 : */
18025 295 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
18026 295 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18027 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18028 : "WHERE oid = '%u';\n",
18029 295 : tbinfo->toast_frozenxid,
18030 295 : tbinfo->toast_minmxid, tbinfo->toast_oid);
18031 : }
18032 : }
18033 :
18034 : /*
18035 : * In binary_upgrade mode, restore matviews' populated status by
18036 : * poking pg_class directly. This is pretty ugly, but we can't use
18037 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
18038 : * matview is not populated even though this matview is; in any case,
18039 : * we want to transfer the matview's heap storage, not run REFRESH.
18040 : */
18041 6221 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
18042 17 : tbinfo->relispopulated)
18043 : {
18044 15 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
18045 15 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
18046 : "SET relispopulated = 't'\n"
18047 : "WHERE oid = ");
18048 15 : appendStringLiteralAH(q, qualrelname, fout);
18049 15 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18050 : }
18051 :
18052 : /*
18053 : * Dump additional per-column properties that we can't handle in the
18054 : * main CREATE TABLE command.
18055 : */
18056 27865 : for (j = 0; j < tbinfo->numatts; j++)
18057 : {
18058 : /* None of this applies to dropped columns */
18059 21644 : if (tbinfo->attisdropped[j])
18060 452 : continue;
18061 :
18062 : /*
18063 : * Dump per-column statistics information. We only issue an ALTER
18064 : * TABLE statement if the attstattarget entry for this column is
18065 : * not the default value.
18066 : */
18067 21192 : if (tbinfo->attstattarget[j] >= 0)
18068 32 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
18069 : foreign, qualrelname,
18070 32 : fmtId(tbinfo->attnames[j]),
18071 32 : tbinfo->attstattarget[j]);
18072 :
18073 : /*
18074 : * Dump per-column storage information. The statement is only
18075 : * dumped if the storage has been changed from the type's default.
18076 : */
18077 21192 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
18078 : {
18079 79 : switch (tbinfo->attstorage[j])
18080 : {
18081 10 : case TYPSTORAGE_PLAIN:
18082 10 : storage = "PLAIN";
18083 10 : break;
18084 37 : case TYPSTORAGE_EXTERNAL:
18085 37 : storage = "EXTERNAL";
18086 37 : break;
18087 0 : case TYPSTORAGE_EXTENDED:
18088 0 : storage = "EXTENDED";
18089 0 : break;
18090 32 : case TYPSTORAGE_MAIN:
18091 32 : storage = "MAIN";
18092 32 : break;
18093 0 : default:
18094 0 : storage = NULL;
18095 : }
18096 :
18097 : /*
18098 : * Only dump the statement if it's a storage type we recognize
18099 : */
18100 79 : if (storage != NULL)
18101 79 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
18102 : foreign, qualrelname,
18103 79 : fmtId(tbinfo->attnames[j]),
18104 : storage);
18105 : }
18106 :
18107 : /*
18108 : * Dump per-column compression, if it's been set.
18109 : */
18110 21192 : if (!dopt->no_toast_compression)
18111 : {
18112 : const char *cmname;
18113 :
18114 21092 : switch (tbinfo->attcompression[j])
18115 : {
18116 71 : case 'p':
18117 71 : cmname = "pglz";
18118 71 : break;
18119 39 : case 'l':
18120 39 : cmname = "lz4";
18121 39 : break;
18122 20982 : default:
18123 20982 : cmname = NULL;
18124 20982 : break;
18125 : }
18126 :
18127 21092 : if (cmname != NULL)
18128 110 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
18129 : foreign, qualrelname,
18130 110 : fmtId(tbinfo->attnames[j]),
18131 : cmname);
18132 : }
18133 :
18134 : /*
18135 : * Dump per-column attributes.
18136 : */
18137 21192 : if (tbinfo->attoptions[j][0] != '\0')
18138 32 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
18139 : foreign, qualrelname,
18140 32 : fmtId(tbinfo->attnames[j]),
18141 32 : tbinfo->attoptions[j]);
18142 :
18143 : /*
18144 : * Dump per-column fdw options.
18145 : */
18146 21192 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
18147 34 : tbinfo->attfdwoptions[j][0] != '\0')
18148 32 : appendPQExpBuffer(q,
18149 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
18150 : " %s\n"
18151 : ");\n",
18152 : qualrelname,
18153 32 : fmtId(tbinfo->attnames[j]),
18154 32 : tbinfo->attfdwoptions[j]);
18155 : } /* end loop over columns */
18156 :
18157 6221 : free(partkeydef);
18158 6221 : free(ftoptions);
18159 6221 : free(srvname);
18160 : }
18161 :
18162 : /*
18163 : * dump properties we only have ALTER TABLE syntax for
18164 : */
18165 6886 : if ((tbinfo->relkind == RELKIND_RELATION ||
18166 1630 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
18167 1034 : tbinfo->relkind == RELKIND_MATVIEW) &&
18168 6187 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
18169 : {
18170 207 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
18171 : {
18172 : /* nothing to do, will be set when the index is dumped */
18173 : }
18174 207 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
18175 : {
18176 207 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
18177 : qualrelname);
18178 : }
18179 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
18180 : {
18181 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
18182 : qualrelname);
18183 : }
18184 : }
18185 :
18186 6886 : if (tbinfo->forcerowsec)
18187 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
18188 : qualrelname);
18189 :
18190 6886 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
18191 :
18192 6886 : if (dopt->binary_upgrade)
18193 948 : binary_upgrade_extension_member(q, &tbinfo->dobj,
18194 : reltypename, qrelname,
18195 948 : tbinfo->dobj.namespace->dobj.name);
18196 :
18197 6886 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18198 : {
18199 6886 : char *tablespace = NULL;
18200 6886 : char *tableam = NULL;
18201 :
18202 : /*
18203 : * _selectTablespace() relies on tablespace-enabled objects in the
18204 : * default tablespace to have a tablespace of "" (empty string) versus
18205 : * non-tablespace-enabled objects to have a tablespace of NULL.
18206 : * getTables() sets tbinfo->reltablespace to "" for the default
18207 : * tablespace (not NULL).
18208 : */
18209 6886 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
18210 6187 : tablespace = tbinfo->reltablespace;
18211 :
18212 6886 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
18213 1295 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
18214 6187 : tableam = tbinfo->amname;
18215 :
18216 6886 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18217 6886 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18218 : .namespace = tbinfo->dobj.namespace->dobj.name,
18219 : .tablespace = tablespace,
18220 : .tableam = tableam,
18221 : .relkind = tbinfo->relkind,
18222 : .owner = tbinfo->rolname,
18223 : .description = reltypename,
18224 : .section = tbinfo->postponed_def ?
18225 : SECTION_POST_DATA : SECTION_PRE_DATA,
18226 : .createStmt = q->data,
18227 : .dropStmt = delq->data));
18228 : }
18229 :
18230 : /* Dump Table Comments */
18231 6886 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18232 74 : dumpTableComment(fout, tbinfo, reltypename);
18233 :
18234 : /* Dump Table Security Labels */
18235 6886 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18236 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18237 :
18238 : /*
18239 : * Dump comments for not-null constraints that aren't to be dumped
18240 : * separately (those are processed by collectComments/dumpComment).
18241 : */
18242 6886 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18243 6886 : fout->remoteVersion >= 180000)
18244 : {
18245 6886 : PQExpBuffer comment = NULL;
18246 6886 : PQExpBuffer tag = NULL;
18247 :
18248 32203 : for (j = 0; j < tbinfo->numatts; j++)
18249 : {
18250 25317 : if (tbinfo->notnull_constrs[j] != NULL &&
18251 2667 : tbinfo->notnull_comment[j] != NULL)
18252 : {
18253 42 : if (comment == NULL)
18254 : {
18255 42 : comment = createPQExpBuffer();
18256 42 : tag = createPQExpBuffer();
18257 : }
18258 : else
18259 : {
18260 0 : resetPQExpBuffer(comment);
18261 0 : resetPQExpBuffer(tag);
18262 : }
18263 :
18264 42 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18265 42 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18266 42 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18267 42 : appendPQExpBufferStr(comment, ";\n");
18268 :
18269 42 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18270 42 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18271 :
18272 42 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18273 42 : ARCHIVE_OPTS(.tag = tag->data,
18274 : .namespace = tbinfo->dobj.namespace->dobj.name,
18275 : .owner = tbinfo->rolname,
18276 : .description = "COMMENT",
18277 : .section = SECTION_NONE,
18278 : .createStmt = comment->data,
18279 : .deps = &(tbinfo->dobj.dumpId),
18280 : .nDeps = 1));
18281 : }
18282 : }
18283 :
18284 6886 : destroyPQExpBuffer(comment);
18285 6886 : destroyPQExpBuffer(tag);
18286 : }
18287 :
18288 : /* Dump comments on inlined table constraints */
18289 7459 : for (j = 0; j < tbinfo->ncheck; j++)
18290 : {
18291 573 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18292 :
18293 573 : if (constr->separate || !constr->conislocal)
18294 244 : continue;
18295 :
18296 329 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18297 37 : dumpTableConstraintComment(fout, constr);
18298 : }
18299 :
18300 6886 : destroyPQExpBuffer(q);
18301 6886 : destroyPQExpBuffer(delq);
18302 6886 : destroyPQExpBuffer(extra);
18303 6886 : free(qrelname);
18304 6886 : free(qualrelname);
18305 6886 : }
18306 :
18307 : /*
18308 : * dumpTableAttach
18309 : * write to fout the commands to attach a child partition
18310 : *
18311 : * Child partitions are always made by creating them separately
18312 : * and then using ATTACH PARTITION, rather than using
18313 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18314 : * any possible discrepancy in column layout, to allow assigning the
18315 : * correct tablespace if different, and so that it's possible to restore
18316 : * a partition without restoring its parent. (You'll get an error from
18317 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18318 : * using "pg_restore -L" if you prefer.) The last point motivates
18319 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18320 : * rather than emitting it within the child partition's ArchiveEntry.
18321 : */
18322 : static void
18323 1431 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18324 : {
18325 1431 : DumpOptions *dopt = fout->dopt;
18326 : PQExpBuffer q;
18327 : PGresult *res;
18328 : char *partbound;
18329 :
18330 : /* Do nothing if not dumping schema */
18331 1431 : if (!dopt->dumpSchema)
18332 54 : return;
18333 :
18334 1377 : q = createPQExpBuffer();
18335 :
18336 1377 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18337 : {
18338 : /* Set up query for partbound details */
18339 43 : appendPQExpBufferStr(q,
18340 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18341 :
18342 43 : appendPQExpBufferStr(q,
18343 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18344 : "FROM pg_class c "
18345 : "WHERE c.oid = $1");
18346 :
18347 43 : ExecuteSqlStatement(fout, q->data);
18348 :
18349 43 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18350 : }
18351 :
18352 1377 : printfPQExpBuffer(q,
18353 : "EXECUTE dumpTableAttach('%u')",
18354 1377 : attachinfo->partitionTbl->dobj.catId.oid);
18355 :
18356 1377 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18357 1377 : partbound = PQgetvalue(res, 0, 0);
18358 :
18359 : /* Perform ALTER TABLE on the parent */
18360 1377 : printfPQExpBuffer(q,
18361 : "ALTER TABLE ONLY %s ",
18362 1377 : fmtQualifiedDumpable(attachinfo->parentTbl));
18363 1377 : appendPQExpBuffer(q,
18364 : "ATTACH PARTITION %s %s;\n",
18365 1377 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18366 : partbound);
18367 :
18368 : /*
18369 : * There is no point in creating a drop query as the drop is done by table
18370 : * drop. (If you think to change this, see also _printTocEntry().)
18371 : * Although this object doesn't really have ownership as such, set the
18372 : * owner field anyway to ensure that the command is run by the correct
18373 : * role at restore time.
18374 : */
18375 1377 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18376 1377 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18377 : .namespace = attachinfo->dobj.namespace->dobj.name,
18378 : .owner = attachinfo->partitionTbl->rolname,
18379 : .description = "TABLE ATTACH",
18380 : .section = SECTION_PRE_DATA,
18381 : .createStmt = q->data));
18382 :
18383 1377 : PQclear(res);
18384 1377 : destroyPQExpBuffer(q);
18385 : }
18386 :
18387 : /*
18388 : * dumpAttrDef --- dump an attribute's default-value declaration
18389 : */
18390 : static void
18391 1087 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18392 : {
18393 1087 : DumpOptions *dopt = fout->dopt;
18394 1087 : TableInfo *tbinfo = adinfo->adtable;
18395 1087 : int adnum = adinfo->adnum;
18396 : PQExpBuffer q;
18397 : PQExpBuffer delq;
18398 : char *qualrelname;
18399 : char *tag;
18400 : char *foreign;
18401 :
18402 : /* Do nothing if not dumping schema */
18403 1087 : if (!dopt->dumpSchema)
18404 0 : return;
18405 :
18406 : /* Skip if not "separate"; it was dumped in the table's definition */
18407 1087 : if (!adinfo->separate)
18408 921 : return;
18409 :
18410 166 : q = createPQExpBuffer();
18411 166 : delq = createPQExpBuffer();
18412 :
18413 166 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18414 :
18415 166 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18416 :
18417 166 : appendPQExpBuffer(q,
18418 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18419 166 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18420 166 : adinfo->adef_expr);
18421 :
18422 166 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18423 : foreign, qualrelname,
18424 166 : fmtId(tbinfo->attnames[adnum - 1]));
18425 :
18426 166 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18427 :
18428 166 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18429 166 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18430 166 : ARCHIVE_OPTS(.tag = tag,
18431 : .namespace = tbinfo->dobj.namespace->dobj.name,
18432 : .owner = tbinfo->rolname,
18433 : .description = "DEFAULT",
18434 : .section = SECTION_PRE_DATA,
18435 : .createStmt = q->data,
18436 : .dropStmt = delq->data));
18437 :
18438 166 : free(tag);
18439 166 : destroyPQExpBuffer(q);
18440 166 : destroyPQExpBuffer(delq);
18441 166 : free(qualrelname);
18442 : }
18443 :
18444 : /*
18445 : * getAttrName: extract the correct name for an attribute
18446 : *
18447 : * The array tblInfo->attnames[] only provides names of user attributes;
18448 : * if a system attribute number is supplied, we have to fake it.
18449 : * We also do a little bit of bounds checking for safety's sake.
18450 : */
18451 : static const char *
18452 2250 : getAttrName(int attrnum, const TableInfo *tblInfo)
18453 : {
18454 2250 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18455 2250 : return tblInfo->attnames[attrnum - 1];
18456 0 : switch (attrnum)
18457 : {
18458 0 : case SelfItemPointerAttributeNumber:
18459 0 : return "ctid";
18460 0 : case MinTransactionIdAttributeNumber:
18461 0 : return "xmin";
18462 0 : case MinCommandIdAttributeNumber:
18463 0 : return "cmin";
18464 0 : case MaxTransactionIdAttributeNumber:
18465 0 : return "xmax";
18466 0 : case MaxCommandIdAttributeNumber:
18467 0 : return "cmax";
18468 0 : case TableOidAttributeNumber:
18469 0 : return "tableoid";
18470 : }
18471 0 : pg_fatal("invalid column number %d for table \"%s\"",
18472 : attrnum, tblInfo->dobj.name);
18473 : return NULL; /* keep compiler quiet */
18474 : }
18475 :
18476 : /*
18477 : * dumpIndex
18478 : * write out to fout a user-defined index
18479 : */
18480 : static void
18481 2768 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18482 : {
18483 2768 : DumpOptions *dopt = fout->dopt;
18484 2768 : TableInfo *tbinfo = indxinfo->indextable;
18485 2768 : bool is_constraint = (indxinfo->indexconstraint != 0);
18486 : PQExpBuffer q;
18487 : PQExpBuffer delq;
18488 : char *qindxname;
18489 : char *qqindxname;
18490 :
18491 : /* Do nothing if not dumping schema */
18492 2768 : if (!dopt->dumpSchema)
18493 117 : return;
18494 :
18495 2651 : q = createPQExpBuffer();
18496 2651 : delq = createPQExpBuffer();
18497 :
18498 2651 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18499 2651 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18500 :
18501 : /*
18502 : * If there's an associated constraint, don't dump the index per se, but
18503 : * do dump any comment for it. (This is safe because dependency ordering
18504 : * will have ensured the constraint is emitted first.) Note that the
18505 : * emitted comment has to be shown as depending on the constraint, not the
18506 : * index, in such cases.
18507 : */
18508 2651 : if (!is_constraint)
18509 : {
18510 1048 : char *indstatcols = indxinfo->indstatcols;
18511 1048 : char *indstatvals = indxinfo->indstatvals;
18512 1048 : char **indstatcolsarray = NULL;
18513 1048 : char **indstatvalsarray = NULL;
18514 1048 : int nstatcols = 0;
18515 1048 : int nstatvals = 0;
18516 :
18517 1048 : if (dopt->binary_upgrade)
18518 158 : binary_upgrade_set_pg_class_oids(fout, q,
18519 158 : indxinfo->dobj.catId.oid);
18520 :
18521 : /* Plain secondary index */
18522 1048 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18523 :
18524 : /*
18525 : * Append ALTER TABLE commands as needed to set properties that we
18526 : * only have ALTER TABLE syntax for. Keep this in sync with the
18527 : * similar code in dumpConstraint!
18528 : */
18529 :
18530 : /* If the index is clustered, we need to record that. */
18531 1048 : if (indxinfo->indisclustered)
18532 : {
18533 5 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18534 5 : fmtQualifiedDumpable(tbinfo));
18535 : /* index name is not qualified in this syntax */
18536 5 : appendPQExpBuffer(q, " ON %s;\n",
18537 : qindxname);
18538 : }
18539 :
18540 : /*
18541 : * If the index has any statistics on some of its columns, generate
18542 : * the associated ALTER INDEX queries.
18543 : */
18544 1048 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18545 : {
18546 : int j;
18547 :
18548 32 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18549 0 : pg_fatal("could not parse index statistic columns");
18550 32 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18551 0 : pg_fatal("could not parse index statistic values");
18552 32 : if (nstatcols != nstatvals)
18553 0 : pg_fatal("mismatched number of columns and values for index statistics");
18554 :
18555 96 : for (j = 0; j < nstatcols; j++)
18556 : {
18557 64 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18558 :
18559 : /*
18560 : * Note that this is a column number, so no quotes should be
18561 : * used.
18562 : */
18563 64 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18564 64 : indstatcolsarray[j]);
18565 64 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18566 64 : indstatvalsarray[j]);
18567 : }
18568 : }
18569 :
18570 : /* Indexes can depend on extensions */
18571 1048 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18572 : "pg_catalog.pg_class",
18573 : "INDEX", qqindxname);
18574 :
18575 : /* If the index defines identity, we need to record that. */
18576 1048 : if (indxinfo->indisreplident)
18577 : {
18578 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18579 0 : fmtQualifiedDumpable(tbinfo));
18580 : /* index name is not qualified in this syntax */
18581 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18582 : qindxname);
18583 : }
18584 :
18585 : /*
18586 : * If this index is a member of a partitioned index, the backend will
18587 : * not allow us to drop it separately, so don't try. It will go away
18588 : * automatically when we drop either the index's table or the
18589 : * partitioned index. (If, in a selective restore with --clean, we
18590 : * drop neither of those, then this index will not be dropped either.
18591 : * But that's fine, and even if you think it's not, the backend won't
18592 : * let us do differently.)
18593 : */
18594 1048 : if (indxinfo->parentidx == 0)
18595 866 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18596 :
18597 1048 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18598 1048 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18599 1048 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18600 : .namespace = tbinfo->dobj.namespace->dobj.name,
18601 : .tablespace = indxinfo->tablespace,
18602 : .owner = tbinfo->rolname,
18603 : .description = "INDEX",
18604 : .section = SECTION_POST_DATA,
18605 : .createStmt = q->data,
18606 : .dropStmt = delq->data));
18607 :
18608 1048 : free(indstatcolsarray);
18609 1048 : free(indstatvalsarray);
18610 : }
18611 :
18612 : /* Dump Index Comments */
18613 2651 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18614 15 : dumpComment(fout, "INDEX", qindxname,
18615 15 : tbinfo->dobj.namespace->dobj.name,
18616 : tbinfo->rolname,
18617 : indxinfo->dobj.catId, 0,
18618 : is_constraint ? indxinfo->indexconstraint :
18619 : indxinfo->dobj.dumpId);
18620 :
18621 2651 : destroyPQExpBuffer(q);
18622 2651 : destroyPQExpBuffer(delq);
18623 2651 : free(qindxname);
18624 2651 : free(qqindxname);
18625 : }
18626 :
18627 : /*
18628 : * dumpIndexAttach
18629 : * write out to fout a partitioned-index attachment clause
18630 : */
18631 : static void
18632 594 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18633 : {
18634 : /* Do nothing if not dumping schema */
18635 594 : if (!fout->dopt->dumpSchema)
18636 48 : return;
18637 :
18638 546 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18639 : {
18640 546 : PQExpBuffer q = createPQExpBuffer();
18641 :
18642 546 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18643 546 : fmtQualifiedDumpable(attachinfo->parentIdx));
18644 546 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18645 546 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18646 :
18647 : /*
18648 : * There is no need for a dropStmt since the drop is done implicitly
18649 : * when we drop either the index's table or the partitioned index.
18650 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18651 : * there's no way to do it anyway. (If you think to change this,
18652 : * consider also what to do with --if-exists.)
18653 : *
18654 : * Although this object doesn't really have ownership as such, set the
18655 : * owner field anyway to ensure that the command is run by the correct
18656 : * role at restore time.
18657 : */
18658 546 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18659 546 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18660 : .namespace = attachinfo->dobj.namespace->dobj.name,
18661 : .owner = attachinfo->parentIdx->indextable->rolname,
18662 : .description = "INDEX ATTACH",
18663 : .section = SECTION_POST_DATA,
18664 : .createStmt = q->data));
18665 :
18666 546 : destroyPQExpBuffer(q);
18667 : }
18668 : }
18669 :
18670 : /*
18671 : * dumpStatisticsExt
18672 : * write out to fout an extended statistics object
18673 : */
18674 : static void
18675 171 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18676 : {
18677 171 : DumpOptions *dopt = fout->dopt;
18678 : PQExpBuffer q;
18679 : PQExpBuffer delq;
18680 : PQExpBuffer query;
18681 : char *qstatsextname;
18682 : PGresult *res;
18683 : char *stxdef;
18684 :
18685 : /* Do nothing if not dumping schema */
18686 171 : if (!dopt->dumpSchema)
18687 24 : return;
18688 :
18689 147 : q = createPQExpBuffer();
18690 147 : delq = createPQExpBuffer();
18691 147 : query = createPQExpBuffer();
18692 :
18693 147 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18694 :
18695 147 : appendPQExpBuffer(query, "SELECT "
18696 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18697 147 : statsextinfo->dobj.catId.oid);
18698 :
18699 147 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18700 :
18701 147 : stxdef = PQgetvalue(res, 0, 0);
18702 :
18703 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18704 147 : appendPQExpBuffer(q, "%s;\n", stxdef);
18705 :
18706 : /*
18707 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18708 : * for this statistics object is not the default value.
18709 : */
18710 147 : if (statsextinfo->stattarget >= 0)
18711 : {
18712 32 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18713 32 : fmtQualifiedDumpable(statsextinfo));
18714 32 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18715 32 : statsextinfo->stattarget);
18716 : }
18717 :
18718 147 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18719 147 : fmtQualifiedDumpable(statsextinfo));
18720 :
18721 147 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18722 147 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18723 147 : statsextinfo->dobj.dumpId,
18724 147 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18725 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18726 : .owner = statsextinfo->rolname,
18727 : .description = "STATISTICS",
18728 : .section = SECTION_POST_DATA,
18729 : .createStmt = q->data,
18730 : .dropStmt = delq->data));
18731 :
18732 : /* Dump Statistics Comments */
18733 147 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18734 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18735 0 : statsextinfo->dobj.namespace->dobj.name,
18736 0 : statsextinfo->rolname,
18737 : statsextinfo->dobj.catId, 0,
18738 0 : statsextinfo->dobj.dumpId);
18739 :
18740 147 : PQclear(res);
18741 147 : destroyPQExpBuffer(q);
18742 147 : destroyPQExpBuffer(delq);
18743 147 : destroyPQExpBuffer(query);
18744 147 : free(qstatsextname);
18745 : }
18746 :
18747 : /*
18748 : * dumpStatisticsExtStats
18749 : * write out to fout the stats for an extended statistics object
18750 : */
18751 : static void
18752 171 : dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
18753 : {
18754 171 : DumpOptions *dopt = fout->dopt;
18755 : PQExpBuffer query;
18756 : PGresult *res;
18757 : int nstats;
18758 :
18759 : /* Do nothing if not dumping statistics */
18760 171 : if (!dopt->dumpStatistics)
18761 40 : return;
18762 :
18763 131 : if (!fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS])
18764 : {
18765 33 : PQExpBuffer pq = createPQExpBuffer();
18766 :
18767 : /*---------
18768 : * Set up query for details about extended statistics objects.
18769 : *
18770 : * The query depends on the backend version:
18771 : * - In v19 and newer versions, query directly the pg_stats_ext*
18772 : * catalogs.
18773 : * - In v18 and older versions, ndistinct and dependencies have a
18774 : * different format that needs translation.
18775 : * - In v14 and older versions, inherited does not exist.
18776 : * - In v11 and older versions, there is no pg_stats_ext, hence
18777 : * the logic joins pg_statistic_ext and pg_namespace.
18778 : *---------
18779 : */
18780 :
18781 33 : appendPQExpBufferStr(pq,
18782 : "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18783 : "SELECT ");
18784 :
18785 : /*
18786 : * Versions 15 and newer have inherited stats.
18787 : *
18788 : * Create this column in all versions because we need to order by it
18789 : * later.
18790 : */
18791 33 : if (fout->remoteVersion >= 150000)
18792 33 : appendPQExpBufferStr(pq, "e.inherited, ");
18793 : else
18794 0 : appendPQExpBufferStr(pq, "false AS inherited, ");
18795 :
18796 : /*--------
18797 : * The ndistinct and dependencies formats changed in v19, so
18798 : * everything before that needs to be translated.
18799 : *
18800 : * The ndistinct translation converts this kind of data:
18801 : * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18802 : *
18803 : * to this:
18804 : * [ {"attributes": [3,4], "ndistinct": 11},
18805 : * {"attributes": [3,6], "ndistinct": 11},
18806 : * {"attributes": [4,6], "ndistinct": 11},
18807 : * {"attributes": [3,4,6], "ndistinct": 11} ]
18808 : *
18809 : * The dependencies translation converts this kind of data:
18810 : * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18811 : * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18812 : * "3, 6 => 4": 1.000000}
18813 : *
18814 : * to this:
18815 : * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18816 : * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18817 : * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18818 : * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18819 : * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18820 : *--------
18821 : */
18822 33 : if (fout->remoteVersion >= 190000)
18823 33 : appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18824 : else
18825 0 : appendPQExpBufferStr(pq,
18826 : "( "
18827 : "SELECT json_agg( "
18828 : " json_build_object( "
18829 : " '" PG_NDISTINCT_KEY_ATTRIBUTES "', "
18830 : " string_to_array(kv.key, ', ')::integer[], "
18831 : " '" PG_NDISTINCT_KEY_NDISTINCT "', "
18832 : " kv.value::bigint )) "
18833 : "FROM json_each_text(e.n_distinct::text::json) AS kv"
18834 : ") AS n_distinct, "
18835 : "( "
18836 : "SELECT json_agg( "
18837 : " json_build_object( "
18838 : " '" PG_DEPENDENCIES_KEY_ATTRIBUTES "', "
18839 : " string_to_array( "
18840 : " split_part(kv.key, ' => ', 1), "
18841 : " ', ')::integer[], "
18842 : " '" PG_DEPENDENCIES_KEY_DEPENDENCY "', "
18843 : " split_part(kv.key, ' => ', 2)::integer, "
18844 : " '" PG_DEPENDENCIES_KEY_DEGREE "', "
18845 : " kv.value::double precision )) "
18846 : "FROM json_each_text(e.dependencies::text::json) AS kv "
18847 : ") AS dependencies, ");
18848 :
18849 : /* MCV was introduced v13 */
18850 33 : if (fout->remoteVersion >= 130000)
18851 33 : appendPQExpBufferStr(pq,
18852 : "e.most_common_vals, e.most_common_freqs, "
18853 : "e.most_common_base_freqs, ");
18854 : else
18855 0 : appendPQExpBufferStr(pq,
18856 : "NULL AS most_common_vals, NULL AS most_common_freqs, "
18857 : "NULL AS most_common_base_freqs, ");
18858 :
18859 : /* Expressions were introduced in v14 */
18860 33 : if (fout->remoteVersion >= 140000)
18861 : {
18862 : /*
18863 : * There is no ordering column in pg_stats_ext_exprs. However, we
18864 : * can rely on the unnesting of pg_statistic_ext_data.stxdexpr to
18865 : * maintain the desired order of expression elements.
18866 : */
18867 33 : appendPQExpBufferStr(pq,
18868 : "( "
18869 : "SELECT jsonb_pretty(jsonb_agg("
18870 : "nullif(j.obj, '{}'::jsonb))) "
18871 : "FROM pg_stats_ext_exprs AS ee "
18872 : "CROSS JOIN LATERAL jsonb_strip_nulls("
18873 : " jsonb_build_object( "
18874 : " 'null_frac', ee.null_frac::text, "
18875 : " 'avg_width', ee.avg_width::text, "
18876 : " 'n_distinct', ee.n_distinct::text, "
18877 : " 'most_common_vals', ee.most_common_vals::text, "
18878 : " 'most_common_freqs', ee.most_common_freqs::text, "
18879 : " 'histogram_bounds', ee.histogram_bounds::text, "
18880 : " 'correlation', ee.correlation::text, "
18881 : " 'most_common_elems', ee.most_common_elems::text, "
18882 : " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18883 : " 'elem_count_histogram', ee.elem_count_histogram::text");
18884 :
18885 : /* These three have been added to pg_stats_ext_exprs in v19. */
18886 33 : if (fout->remoteVersion >= 190000)
18887 33 : appendPQExpBufferStr(pq,
18888 : ", "
18889 : " 'range_length_histogram', ee.range_length_histogram::text, "
18890 : " 'range_empty_frac', ee.range_empty_frac::text, "
18891 : " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18892 :
18893 33 : appendPQExpBufferStr(pq,
18894 : " )) AS j(obj)"
18895 : "WHERE ee.statistics_schemaname = $1 "
18896 : "AND ee.statistics_name = $2 ");
18897 : /* Inherited expressions introduced in v15 */
18898 33 : if (fout->remoteVersion >= 150000)
18899 33 : appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18900 :
18901 33 : appendPQExpBufferStr(pq, ") AS exprs ");
18902 : }
18903 : else
18904 0 : appendPQExpBufferStr(pq, "NULL AS exprs ");
18905 :
18906 : /* pg_stats_ext introduced in v12 */
18907 33 : if (fout->remoteVersion >= 120000)
18908 33 : appendPQExpBufferStr(pq,
18909 : "FROM pg_catalog.pg_stats_ext AS e "
18910 : "WHERE e.statistics_schemaname = $1 "
18911 : "AND e.statistics_name = $2 ");
18912 : else
18913 0 : appendPQExpBufferStr(pq,
18914 : "FROM ( "
18915 : "SELECT s.stxndistinct AS n_distinct, "
18916 : " s.stxdependencies AS dependencies "
18917 : "FROM pg_catalog.pg_statistic_ext AS s "
18918 : "JOIN pg_catalog.pg_namespace AS n "
18919 : "ON n.oid = s.stxnamespace "
18920 : "WHERE n.nspname = $1 "
18921 : "AND s.stxname = $2 "
18922 : ") AS e ");
18923 :
18924 : /* we always have an inherited column, but it may be a constant */
18925 33 : appendPQExpBufferStr(pq, "ORDER BY inherited");
18926 :
18927 33 : ExecuteSqlStatement(fout, pq->data);
18928 :
18929 33 : fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS] = true;
18930 :
18931 33 : destroyPQExpBuffer(pq);
18932 : }
18933 :
18934 131 : query = createPQExpBuffer();
18935 :
18936 131 : appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18937 131 : appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18938 131 : appendPQExpBufferStr(query, "::pg_catalog.name, ");
18939 131 : appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18940 131 : appendPQExpBufferStr(query, "::pg_catalog.name)");
18941 :
18942 131 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18943 :
18944 131 : destroyPQExpBuffer(query);
18945 :
18946 131 : nstats = PQntuples(res);
18947 :
18948 131 : if (nstats > 0)
18949 : {
18950 36 : PQExpBuffer out = createPQExpBuffer();
18951 :
18952 36 : int i_inherited = PQfnumber(res, "inherited");
18953 36 : int i_ndistinct = PQfnumber(res, "n_distinct");
18954 36 : int i_dependencies = PQfnumber(res, "dependencies");
18955 36 : int i_mcv = PQfnumber(res, "most_common_vals");
18956 36 : int i_mcf = PQfnumber(res, "most_common_freqs");
18957 36 : int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18958 36 : int i_exprs = PQfnumber(res, "exprs");
18959 :
18960 72 : for (int i = 0; i < nstats; i++)
18961 : {
18962 36 : TableInfo *tbinfo = statsextinfo->stattable;
18963 :
18964 36 : if (PQgetisnull(res, i, i_inherited))
18965 0 : pg_fatal("inherited cannot be NULL");
18966 :
18967 36 : appendPQExpBufferStr(out,
18968 : "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18969 36 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18970 : fout->remoteVersion);
18971 :
18972 : /* Relation information */
18973 36 : appendPQExpBufferStr(out, "\t'schemaname', ");
18974 36 : appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18975 36 : appendPQExpBufferStr(out, ",\n\t'relname', ");
18976 36 : appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18977 :
18978 : /* Extended statistics information */
18979 36 : appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18980 36 : appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18981 36 : appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18982 36 : appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18983 36 : appendNamedArgument(out, fout, "inherited", "boolean",
18984 36 : PQgetvalue(res, i, i_inherited));
18985 :
18986 36 : if (!PQgetisnull(res, i, i_ndistinct))
18987 32 : appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18988 32 : PQgetvalue(res, i, i_ndistinct));
18989 :
18990 36 : if (!PQgetisnull(res, i, i_dependencies))
18991 33 : appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18992 33 : PQgetvalue(res, i, i_dependencies));
18993 :
18994 36 : if (!PQgetisnull(res, i, i_mcv))
18995 35 : appendNamedArgument(out, fout, "most_common_vals", "text[]",
18996 35 : PQgetvalue(res, i, i_mcv));
18997 :
18998 36 : if (!PQgetisnull(res, i, i_mcf))
18999 35 : appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
19000 35 : PQgetvalue(res, i, i_mcf));
19001 :
19002 36 : if (!PQgetisnull(res, i, i_mcbf))
19003 35 : appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
19004 35 : PQgetvalue(res, i, i_mcbf));
19005 :
19006 36 : if (!PQgetisnull(res, i, i_exprs))
19007 33 : appendNamedArgument(out, fout, "exprs", "jsonb",
19008 33 : PQgetvalue(res, i, i_exprs));
19009 :
19010 36 : appendPQExpBufferStr(out, "\n);\n");
19011 : }
19012 :
19013 36 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19014 36 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
19015 : .namespace = statsextinfo->dobj.namespace->dobj.name,
19016 : .owner = statsextinfo->rolname,
19017 : .description = "EXTENDED STATISTICS DATA",
19018 : .section = SECTION_POST_DATA,
19019 : .createStmt = out->data,
19020 : .deps = &statsextinfo->dobj.dumpId,
19021 : .nDeps = 1));
19022 36 : destroyPQExpBuffer(out);
19023 : }
19024 131 : PQclear(res);
19025 : }
19026 :
19027 : /*
19028 : * dumpConstraint
19029 : * write out to fout a user-defined constraint
19030 : */
19031 : static void
19032 2734 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
19033 : {
19034 2734 : DumpOptions *dopt = fout->dopt;
19035 2734 : TableInfo *tbinfo = coninfo->contable;
19036 : PQExpBuffer q;
19037 : PQExpBuffer delq;
19038 2734 : char *tag = NULL;
19039 : char *foreign;
19040 :
19041 : /* Do nothing if not dumping schema */
19042 2734 : if (!dopt->dumpSchema)
19043 98 : return;
19044 :
19045 2636 : q = createPQExpBuffer();
19046 2636 : delq = createPQExpBuffer();
19047 :
19048 5098 : foreign = tbinfo &&
19049 2636 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
19050 :
19051 2636 : if (coninfo->contype == 'p' ||
19052 1287 : coninfo->contype == 'u' ||
19053 1043 : coninfo->contype == 'x')
19054 1603 : {
19055 : /* Index-related constraint */
19056 : IndxInfo *indxinfo;
19057 : int k;
19058 :
19059 1603 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
19060 :
19061 1603 : if (indxinfo == NULL)
19062 0 : pg_fatal("missing index for constraint \"%s\"",
19063 : coninfo->dobj.name);
19064 :
19065 1603 : if (dopt->binary_upgrade)
19066 177 : binary_upgrade_set_pg_class_oids(fout, q,
19067 : indxinfo->dobj.catId.oid);
19068 :
19069 1603 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
19070 1603 : fmtQualifiedDumpable(tbinfo));
19071 1603 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
19072 1603 : fmtId(coninfo->dobj.name));
19073 :
19074 1603 : if (coninfo->condef)
19075 : {
19076 : /* pg_get_constraintdef should have provided everything */
19077 10 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
19078 : }
19079 : else
19080 : {
19081 1593 : appendPQExpBufferStr(q,
19082 1593 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
19083 :
19084 : /*
19085 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
19086 : * indexes. Being able to create this was fixed, but we need to
19087 : * make the index distinct in order to be able to restore the
19088 : * dump.
19089 : */
19090 1593 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
19091 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
19092 1593 : appendPQExpBufferStr(q, " (");
19093 3803 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
19094 : {
19095 2210 : int indkey = (int) indxinfo->indkeys[k];
19096 : const char *attname;
19097 :
19098 2210 : if (indkey == InvalidAttrNumber)
19099 0 : break;
19100 2210 : attname = getAttrName(indkey, tbinfo);
19101 :
19102 2210 : appendPQExpBuffer(q, "%s%s",
19103 : (k == 0) ? "" : ", ",
19104 : fmtId(attname));
19105 : }
19106 1593 : if (coninfo->conperiod)
19107 109 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
19108 :
19109 1593 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
19110 20 : appendPQExpBufferStr(q, ") INCLUDE (");
19111 :
19112 1633 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
19113 : {
19114 40 : int indkey = (int) indxinfo->indkeys[k];
19115 : const char *attname;
19116 :
19117 40 : if (indkey == InvalidAttrNumber)
19118 0 : break;
19119 40 : attname = getAttrName(indkey, tbinfo);
19120 :
19121 80 : appendPQExpBuffer(q, "%s%s",
19122 40 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
19123 : fmtId(attname));
19124 : }
19125 :
19126 1593 : appendPQExpBufferChar(q, ')');
19127 :
19128 1593 : if (nonemptyReloptions(indxinfo->indreloptions))
19129 : {
19130 0 : appendPQExpBufferStr(q, " WITH (");
19131 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
19132 0 : appendPQExpBufferChar(q, ')');
19133 : }
19134 :
19135 1593 : if (coninfo->condeferrable)
19136 : {
19137 25 : appendPQExpBufferStr(q, " DEFERRABLE");
19138 25 : if (coninfo->condeferred)
19139 15 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
19140 : }
19141 :
19142 1593 : appendPQExpBufferStr(q, ";\n");
19143 : }
19144 :
19145 : /*
19146 : * Append ALTER TABLE commands as needed to set properties that we
19147 : * only have ALTER TABLE syntax for. Keep this in sync with the
19148 : * similar code in dumpIndex!
19149 : */
19150 :
19151 : /* If the index is clustered, we need to record that. */
19152 1603 : if (indxinfo->indisclustered)
19153 : {
19154 32 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
19155 32 : fmtQualifiedDumpable(tbinfo));
19156 : /* index name is not qualified in this syntax */
19157 32 : appendPQExpBuffer(q, " ON %s;\n",
19158 32 : fmtId(indxinfo->dobj.name));
19159 : }
19160 :
19161 : /* If the index defines identity, we need to record that. */
19162 1603 : if (indxinfo->indisreplident)
19163 : {
19164 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
19165 0 : fmtQualifiedDumpable(tbinfo));
19166 : /* index name is not qualified in this syntax */
19167 0 : appendPQExpBuffer(q, " INDEX %s;\n",
19168 0 : fmtId(indxinfo->dobj.name));
19169 : }
19170 :
19171 : /* Indexes can depend on extensions */
19172 1603 : append_depends_on_extension(fout, q, &indxinfo->dobj,
19173 : "pg_catalog.pg_class", "INDEX",
19174 1603 : fmtQualifiedDumpable(indxinfo));
19175 :
19176 1603 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
19177 1603 : fmtQualifiedDumpable(tbinfo));
19178 1603 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19179 1603 : fmtId(coninfo->dobj.name));
19180 :
19181 1603 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19182 :
19183 1603 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19184 1603 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19185 1603 : ARCHIVE_OPTS(.tag = tag,
19186 : .namespace = tbinfo->dobj.namespace->dobj.name,
19187 : .tablespace = indxinfo->tablespace,
19188 : .owner = tbinfo->rolname,
19189 : .description = "CONSTRAINT",
19190 : .section = SECTION_POST_DATA,
19191 : .createStmt = q->data,
19192 : .dropStmt = delq->data));
19193 : }
19194 1033 : else if (coninfo->contype == 'f')
19195 : {
19196 : char *only;
19197 :
19198 : /*
19199 : * Foreign keys on partitioned tables are always declared as
19200 : * inheriting to partitions; for all other cases, emit them as
19201 : * applying ONLY directly to the named table, because that's how they
19202 : * work for regular inherited tables.
19203 : */
19204 219 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
19205 :
19206 : /*
19207 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
19208 : * current table data is not processed
19209 : */
19210 219 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
19211 219 : only, fmtQualifiedDumpable(tbinfo));
19212 219 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19213 219 : fmtId(coninfo->dobj.name),
19214 219 : coninfo->condef);
19215 :
19216 219 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
19217 219 : only, fmtQualifiedDumpable(tbinfo));
19218 219 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19219 219 : fmtId(coninfo->dobj.name));
19220 :
19221 219 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19222 :
19223 219 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19224 219 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19225 219 : ARCHIVE_OPTS(.tag = tag,
19226 : .namespace = tbinfo->dobj.namespace->dobj.name,
19227 : .owner = tbinfo->rolname,
19228 : .description = "FK CONSTRAINT",
19229 : .section = SECTION_POST_DATA,
19230 : .createStmt = q->data,
19231 : .dropStmt = delq->data));
19232 : }
19233 814 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
19234 : {
19235 : /* CHECK or invalid not-null constraint on a table */
19236 :
19237 : /* Ignore if not to be dumped separately, or if it was inherited */
19238 640 : if (coninfo->separate && coninfo->conislocal)
19239 : {
19240 : const char *keyword;
19241 :
19242 107 : if (coninfo->contype == 'c')
19243 45 : keyword = "CHECK CONSTRAINT";
19244 : else
19245 62 : keyword = "CONSTRAINT";
19246 :
19247 : /* not ONLY since we want it to propagate to children */
19248 107 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19249 107 : fmtQualifiedDumpable(tbinfo));
19250 107 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19251 107 : fmtId(coninfo->dobj.name),
19252 107 : coninfo->condef);
19253 :
19254 107 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19255 107 : fmtQualifiedDumpable(tbinfo));
19256 107 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19257 107 : fmtId(coninfo->dobj.name));
19258 :
19259 107 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19260 :
19261 107 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19262 107 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19263 107 : ARCHIVE_OPTS(.tag = tag,
19264 : .namespace = tbinfo->dobj.namespace->dobj.name,
19265 : .owner = tbinfo->rolname,
19266 : .description = keyword,
19267 : .section = SECTION_POST_DATA,
19268 : .createStmt = q->data,
19269 : .dropStmt = delq->data));
19270 : }
19271 : }
19272 174 : else if (tbinfo == NULL)
19273 : {
19274 : /* CHECK, NOT NULL constraint on a domain */
19275 174 : TypeInfo *tyinfo = coninfo->condomain;
19276 :
19277 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19278 :
19279 : /* Ignore if not to be dumped separately */
19280 174 : if (coninfo->separate)
19281 : {
19282 : const char *keyword;
19283 :
19284 5 : if (coninfo->contype == 'c')
19285 5 : keyword = "CHECK CONSTRAINT";
19286 : else
19287 0 : keyword = "CONSTRAINT";
19288 :
19289 5 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19290 5 : fmtQualifiedDumpable(tyinfo));
19291 5 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19292 5 : fmtId(coninfo->dobj.name),
19293 5 : coninfo->condef);
19294 :
19295 5 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19296 5 : fmtQualifiedDumpable(tyinfo));
19297 5 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19298 5 : fmtId(coninfo->dobj.name));
19299 :
19300 5 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19301 :
19302 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19303 5 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19304 5 : ARCHIVE_OPTS(.tag = tag,
19305 : .namespace = tyinfo->dobj.namespace->dobj.name,
19306 : .owner = tyinfo->rolname,
19307 : .description = keyword,
19308 : .section = SECTION_POST_DATA,
19309 : .createStmt = q->data,
19310 : .dropStmt = delq->data));
19311 :
19312 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19313 : {
19314 5 : PQExpBuffer conprefix = createPQExpBuffer();
19315 5 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19316 :
19317 5 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19318 5 : fmtId(coninfo->dobj.name));
19319 :
19320 5 : dumpComment(fout, conprefix->data, qtypname,
19321 5 : tyinfo->dobj.namespace->dobj.name,
19322 : tyinfo->rolname,
19323 5 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19324 5 : destroyPQExpBuffer(conprefix);
19325 5 : free(qtypname);
19326 : }
19327 : }
19328 : }
19329 : else
19330 : {
19331 0 : pg_fatal("unrecognized constraint type: %c",
19332 : coninfo->contype);
19333 : }
19334 :
19335 : /* Dump Constraint Comments --- only works for table constraints */
19336 2636 : if (tbinfo && coninfo->separate &&
19337 1959 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19338 47 : dumpTableConstraintComment(fout, coninfo);
19339 :
19340 2636 : free(tag);
19341 2636 : destroyPQExpBuffer(q);
19342 2636 : destroyPQExpBuffer(delq);
19343 : }
19344 :
19345 : /*
19346 : * dumpTableConstraintComment --- dump a constraint's comment if any
19347 : *
19348 : * This is split out because we need the function in two different places
19349 : * depending on whether the constraint is dumped as part of CREATE TABLE
19350 : * or as a separate ALTER command.
19351 : */
19352 : static void
19353 84 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
19354 : {
19355 84 : TableInfo *tbinfo = coninfo->contable;
19356 84 : PQExpBuffer conprefix = createPQExpBuffer();
19357 : char *qtabname;
19358 :
19359 84 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19360 :
19361 84 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19362 84 : fmtId(coninfo->dobj.name));
19363 :
19364 84 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19365 84 : dumpComment(fout, conprefix->data, qtabname,
19366 84 : tbinfo->dobj.namespace->dobj.name,
19367 : tbinfo->rolname,
19368 : coninfo->dobj.catId, 0,
19369 84 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19370 :
19371 84 : destroyPQExpBuffer(conprefix);
19372 84 : free(qtabname);
19373 84 : }
19374 :
19375 : static inline SeqType
19376 647 : parse_sequence_type(const char *name)
19377 : {
19378 1452 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
19379 : {
19380 1452 : if (strcmp(SeqTypeNames[i], name) == 0)
19381 647 : return (SeqType) i;
19382 : }
19383 :
19384 0 : pg_fatal("unrecognized sequence type: %s", name);
19385 : return (SeqType) 0; /* keep compiler quiet */
19386 : }
19387 :
19388 : /*
19389 : * bsearch() comparator for SequenceItem
19390 : */
19391 : static int
19392 2971 : SequenceItemCmp(const void *p1, const void *p2)
19393 : {
19394 2971 : SequenceItem v1 = *((const SequenceItem *) p1);
19395 2971 : SequenceItem v2 = *((const SequenceItem *) p2);
19396 :
19397 2971 : return pg_cmp_u32(v1.oid, v2.oid);
19398 : }
19399 :
19400 : /*
19401 : * collectSequences
19402 : *
19403 : * Construct a table of sequence information. This table is sorted by OID for
19404 : * speed in lookup.
19405 : */
19406 : static void
19407 259 : collectSequences(Archive *fout)
19408 : {
19409 : PGresult *res;
19410 : const char *query;
19411 :
19412 : /*
19413 : * Before Postgres 10, sequence metadata is in the sequence itself. With
19414 : * some extra effort, we might be able to use the sorted table for those
19415 : * versions, but for now it seems unlikely to be worth it.
19416 : *
19417 : * Since version 18, we can gather the sequence data in this query with
19418 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19419 : */
19420 259 : if (fout->remoteVersion < 100000)
19421 0 : return;
19422 259 : else if (fout->remoteVersion < 180000 ||
19423 259 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
19424 8 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19425 : "seqstart, seqincrement, "
19426 : "seqmax, seqmin, "
19427 : "seqcache, seqcycle, "
19428 : "NULL, 'f' "
19429 : "FROM pg_catalog.pg_sequence "
19430 : "ORDER BY seqrelid";
19431 : else
19432 251 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19433 : "seqstart, seqincrement, "
19434 : "seqmax, seqmin, "
19435 : "seqcache, seqcycle, "
19436 : "last_value, is_called "
19437 : "FROM pg_catalog.pg_sequence, "
19438 : "pg_get_sequence_data(seqrelid) "
19439 : "ORDER BY seqrelid;";
19440 :
19441 259 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19442 :
19443 259 : nsequences = PQntuples(res);
19444 259 : sequences = pg_malloc_array(SequenceItem, nsequences);
19445 :
19446 906 : for (int i = 0; i < nsequences; i++)
19447 : {
19448 647 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19449 647 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
19450 647 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19451 647 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19452 647 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19453 647 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19454 647 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19455 647 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19456 647 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19457 647 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19458 647 : sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19459 : }
19460 :
19461 259 : PQclear(res);
19462 : }
19463 :
19464 : /*
19465 : * dumpSequence
19466 : * write the declaration (not data) of one user-defined sequence
19467 : */
19468 : static void
19469 384 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
19470 : {
19471 384 : DumpOptions *dopt = fout->dopt;
19472 : SequenceItem *seq;
19473 : bool is_ascending;
19474 : int64 default_minv,
19475 : default_maxv;
19476 384 : PQExpBuffer query = createPQExpBuffer();
19477 384 : PQExpBuffer delqry = createPQExpBuffer();
19478 : char *qseqname;
19479 384 : TableInfo *owning_tab = NULL;
19480 :
19481 384 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19482 :
19483 : /*
19484 : * For versions >= 10, the sequence information is gathered in a sorted
19485 : * table before any calls to dumpSequence(). See collectSequences() for
19486 : * more information.
19487 : */
19488 384 : if (fout->remoteVersion >= 100000)
19489 : {
19490 384 : SequenceItem key = {0};
19491 :
19492 : Assert(sequences);
19493 :
19494 384 : key.oid = tbinfo->dobj.catId.oid;
19495 384 : seq = bsearch(&key, sequences, nsequences,
19496 : sizeof(SequenceItem), SequenceItemCmp);
19497 : }
19498 : else
19499 : {
19500 : PGresult *res;
19501 :
19502 : /*
19503 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19504 : *
19505 : * Note: it might seem that 'bigint' potentially needs to be
19506 : * schema-qualified, but actually that's a keyword.
19507 : */
19508 0 : appendPQExpBuffer(query,
19509 : "SELECT 'bigint' AS sequence_type, "
19510 : "start_value, increment_by, max_value, min_value, "
19511 : "cache_value, is_cycled FROM %s",
19512 0 : fmtQualifiedDumpable(tbinfo));
19513 :
19514 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19515 :
19516 0 : if (PQntuples(res) != 1)
19517 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19518 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19519 : PQntuples(res)),
19520 : tbinfo->dobj.name, PQntuples(res));
19521 :
19522 0 : seq = pg_malloc0_object(SequenceItem);
19523 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19524 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19525 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19526 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19527 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19528 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19529 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19530 :
19531 0 : PQclear(res);
19532 : }
19533 :
19534 : /* Calculate default limits for a sequence of this type */
19535 384 : is_ascending = (seq->incby >= 0);
19536 384 : if (seq->seqtype == SEQTYPE_SMALLINT)
19537 : {
19538 25 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19539 25 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19540 : }
19541 359 : else if (seq->seqtype == SEQTYPE_INTEGER)
19542 : {
19543 284 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19544 284 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19545 : }
19546 75 : else if (seq->seqtype == SEQTYPE_BIGINT)
19547 : {
19548 75 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19549 75 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19550 : }
19551 : else
19552 : {
19553 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19554 : default_minv = default_maxv = 0; /* keep compiler quiet */
19555 : }
19556 :
19557 : /*
19558 : * Identity sequences are not to be dropped separately.
19559 : */
19560 384 : if (!tbinfo->is_identity_sequence)
19561 : {
19562 242 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19563 242 : fmtQualifiedDumpable(tbinfo));
19564 : }
19565 :
19566 384 : resetPQExpBuffer(query);
19567 :
19568 384 : if (dopt->binary_upgrade)
19569 : {
19570 66 : binary_upgrade_set_pg_class_oids(fout, query,
19571 66 : tbinfo->dobj.catId.oid);
19572 :
19573 : /*
19574 : * In older PG versions a sequence will have a pg_type entry, but v14
19575 : * and up don't use that, so don't attempt to preserve the type OID.
19576 : */
19577 : }
19578 :
19579 384 : if (tbinfo->is_identity_sequence)
19580 : {
19581 142 : owning_tab = findTableByOid(tbinfo->owning_tab);
19582 :
19583 142 : appendPQExpBuffer(query,
19584 : "ALTER TABLE %s ",
19585 142 : fmtQualifiedDumpable(owning_tab));
19586 142 : appendPQExpBuffer(query,
19587 : "ALTER COLUMN %s ADD GENERATED ",
19588 142 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19589 142 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19590 102 : appendPQExpBufferStr(query, "ALWAYS");
19591 40 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19592 40 : appendPQExpBufferStr(query, "BY DEFAULT");
19593 142 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19594 142 : fmtQualifiedDumpable(tbinfo));
19595 :
19596 : /*
19597 : * Emit persistence option only if it's different from the owning
19598 : * table's. This avoids using this new syntax unnecessarily.
19599 : */
19600 142 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19601 10 : appendPQExpBuffer(query, " %s\n",
19602 10 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19603 : "UNLOGGED" : "LOGGED");
19604 : }
19605 : else
19606 : {
19607 242 : appendPQExpBuffer(query,
19608 : "CREATE %sSEQUENCE %s\n",
19609 242 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19610 : "UNLOGGED " : "",
19611 242 : fmtQualifiedDumpable(tbinfo));
19612 :
19613 242 : if (seq->seqtype != SEQTYPE_BIGINT)
19614 182 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19615 : }
19616 :
19617 384 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19618 :
19619 384 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19620 :
19621 384 : if (seq->minv != default_minv)
19622 15 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19623 : else
19624 369 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19625 :
19626 384 : if (seq->maxv != default_maxv)
19627 15 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19628 : else
19629 369 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19630 :
19631 384 : appendPQExpBuffer(query,
19632 : " CACHE " INT64_FORMAT "%s",
19633 384 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19634 :
19635 384 : if (tbinfo->is_identity_sequence)
19636 142 : appendPQExpBufferStr(query, "\n);\n");
19637 : else
19638 242 : appendPQExpBufferStr(query, ";\n");
19639 :
19640 : /* binary_upgrade: no need to clear TOAST table oid */
19641 :
19642 384 : if (dopt->binary_upgrade)
19643 66 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19644 : "SEQUENCE", qseqname,
19645 66 : tbinfo->dobj.namespace->dobj.name);
19646 :
19647 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19648 384 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19649 384 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19650 : .namespace = tbinfo->dobj.namespace->dobj.name,
19651 : .owner = tbinfo->rolname,
19652 : .description = "SEQUENCE",
19653 : .section = SECTION_PRE_DATA,
19654 : .createStmt = query->data,
19655 : .dropStmt = delqry->data));
19656 :
19657 : /*
19658 : * If the sequence is owned by a table column, emit the ALTER for it as a
19659 : * separate TOC entry immediately following the sequence's own entry. It's
19660 : * OK to do this rather than using full sorting logic, because the
19661 : * dependency that tells us it's owned will have forced the table to be
19662 : * created first. We can't just include the ALTER in the TOC entry
19663 : * because it will fail if we haven't reassigned the sequence owner to
19664 : * match the table's owner.
19665 : *
19666 : * We need not schema-qualify the table reference because both sequence
19667 : * and table must be in the same schema.
19668 : */
19669 384 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19670 : {
19671 137 : owning_tab = findTableByOid(tbinfo->owning_tab);
19672 :
19673 137 : if (owning_tab == NULL)
19674 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19675 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19676 :
19677 137 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19678 : {
19679 135 : resetPQExpBuffer(query);
19680 135 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19681 135 : fmtQualifiedDumpable(tbinfo));
19682 135 : appendPQExpBuffer(query, " OWNED BY %s",
19683 135 : fmtQualifiedDumpable(owning_tab));
19684 135 : appendPQExpBuffer(query, ".%s;\n",
19685 135 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19686 :
19687 135 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19688 135 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19689 135 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19690 : .namespace = tbinfo->dobj.namespace->dobj.name,
19691 : .owner = tbinfo->rolname,
19692 : .description = "SEQUENCE OWNED BY",
19693 : .section = SECTION_PRE_DATA,
19694 : .createStmt = query->data,
19695 : .deps = &(tbinfo->dobj.dumpId),
19696 : .nDeps = 1));
19697 : }
19698 : }
19699 :
19700 : /* Dump Sequence Comments and Security Labels */
19701 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19702 0 : dumpComment(fout, "SEQUENCE", qseqname,
19703 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19704 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19705 :
19706 384 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19707 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19708 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19709 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19710 :
19711 384 : if (fout->remoteVersion < 100000)
19712 0 : pg_free(seq);
19713 384 : destroyPQExpBuffer(query);
19714 384 : destroyPQExpBuffer(delqry);
19715 384 : free(qseqname);
19716 384 : }
19717 :
19718 : /*
19719 : * dumpSequenceData
19720 : * write the data of one user-defined sequence
19721 : */
19722 : static void
19723 402 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19724 : {
19725 402 : TableInfo *tbinfo = tdinfo->tdtable;
19726 : int64 last;
19727 : bool called;
19728 : PQExpBuffer query;
19729 :
19730 : /* needn't bother if not dumping sequence data */
19731 402 : if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19732 1 : return;
19733 :
19734 401 : query = createPQExpBuffer();
19735 :
19736 : /*
19737 : * For versions >= 18, the sequence information is gathered in the sorted
19738 : * array before any calls to dumpSequenceData(). See collectSequences()
19739 : * for more information.
19740 : *
19741 : * For older versions, we have to query the sequence relations
19742 : * individually.
19743 : */
19744 401 : if (fout->remoteVersion < 180000)
19745 : {
19746 : PGresult *res;
19747 :
19748 0 : appendPQExpBuffer(query,
19749 : "SELECT last_value, is_called FROM %s",
19750 0 : fmtQualifiedDumpable(tbinfo));
19751 :
19752 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19753 :
19754 0 : if (PQntuples(res) != 1)
19755 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19756 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19757 : PQntuples(res)),
19758 : tbinfo->dobj.name, PQntuples(res));
19759 :
19760 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19761 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19762 :
19763 0 : PQclear(res);
19764 : }
19765 : else
19766 : {
19767 401 : SequenceItem key = {0};
19768 : SequenceItem *entry;
19769 :
19770 : Assert(sequences);
19771 : Assert(tbinfo->dobj.catId.oid);
19772 :
19773 401 : key.oid = tbinfo->dobj.catId.oid;
19774 401 : entry = bsearch(&key, sequences, nsequences,
19775 : sizeof(SequenceItem), SequenceItemCmp);
19776 :
19777 401 : if (entry->null_seqtuple)
19778 0 : pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19779 : "SELECT privilege on the sequence or the sequence may "
19780 : "have been concurrently dropped",
19781 : tbinfo->dobj.name);
19782 :
19783 401 : last = entry->last_value;
19784 401 : called = entry->is_called;
19785 : }
19786 :
19787 401 : resetPQExpBuffer(query);
19788 401 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19789 401 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19790 401 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19791 : last, (called ? "true" : "false"));
19792 :
19793 401 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19794 401 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19795 401 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19796 : .namespace = tbinfo->dobj.namespace->dobj.name,
19797 : .owner = tbinfo->rolname,
19798 : .description = "SEQUENCE SET",
19799 : .section = SECTION_DATA,
19800 : .createStmt = query->data,
19801 : .deps = &(tbinfo->dobj.dumpId),
19802 : .nDeps = 1));
19803 :
19804 401 : destroyPQExpBuffer(query);
19805 : }
19806 :
19807 : /*
19808 : * dumpTrigger
19809 : * write the declaration of one user-defined table trigger
19810 : */
19811 : static void
19812 523 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19813 : {
19814 523 : DumpOptions *dopt = fout->dopt;
19815 523 : TableInfo *tbinfo = tginfo->tgtable;
19816 : PQExpBuffer query;
19817 : PQExpBuffer delqry;
19818 : PQExpBuffer trigprefix;
19819 : PQExpBuffer trigidentity;
19820 : char *qtabname;
19821 : char *tag;
19822 :
19823 : /* Do nothing if not dumping schema */
19824 523 : if (!dopt->dumpSchema)
19825 31 : return;
19826 :
19827 492 : query = createPQExpBuffer();
19828 492 : delqry = createPQExpBuffer();
19829 492 : trigprefix = createPQExpBuffer();
19830 492 : trigidentity = createPQExpBuffer();
19831 :
19832 492 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19833 :
19834 492 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19835 492 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19836 :
19837 492 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19838 492 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19839 :
19840 : /* Triggers can depend on extensions */
19841 492 : append_depends_on_extension(fout, query, &tginfo->dobj,
19842 : "pg_catalog.pg_trigger", "TRIGGER",
19843 492 : trigidentity->data);
19844 :
19845 492 : if (tginfo->tgispartition)
19846 : {
19847 : Assert(tbinfo->ispartition);
19848 :
19849 : /*
19850 : * Partition triggers only appear here because their 'tgenabled' flag
19851 : * differs from its parent's. The trigger is created already, so
19852 : * remove the CREATE and replace it with an ALTER. (Clear out the
19853 : * DROP query too, so that pg_dump --create does not cause errors.)
19854 : */
19855 109 : resetPQExpBuffer(query);
19856 109 : resetPQExpBuffer(delqry);
19857 109 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19858 109 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19859 109 : fmtQualifiedDumpable(tbinfo));
19860 109 : switch (tginfo->tgenabled)
19861 : {
19862 38 : case 'f':
19863 : case 'D':
19864 38 : appendPQExpBufferStr(query, "DISABLE");
19865 38 : break;
19866 0 : case 't':
19867 : case 'O':
19868 0 : appendPQExpBufferStr(query, "ENABLE");
19869 0 : break;
19870 33 : case 'R':
19871 33 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19872 33 : break;
19873 38 : case 'A':
19874 38 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19875 38 : break;
19876 : }
19877 109 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19878 109 : fmtId(tginfo->dobj.name));
19879 : }
19880 383 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19881 : {
19882 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19883 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19884 0 : fmtQualifiedDumpable(tbinfo));
19885 0 : switch (tginfo->tgenabled)
19886 : {
19887 0 : case 'D':
19888 : case 'f':
19889 0 : appendPQExpBufferStr(query, "DISABLE");
19890 0 : break;
19891 0 : case 'A':
19892 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19893 0 : break;
19894 0 : case 'R':
19895 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19896 0 : break;
19897 0 : default:
19898 0 : appendPQExpBufferStr(query, "ENABLE");
19899 0 : break;
19900 : }
19901 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19902 0 : fmtId(tginfo->dobj.name));
19903 : }
19904 :
19905 492 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19906 492 : fmtId(tginfo->dobj.name));
19907 :
19908 492 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19909 :
19910 492 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19911 492 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19912 492 : ARCHIVE_OPTS(.tag = tag,
19913 : .namespace = tbinfo->dobj.namespace->dobj.name,
19914 : .owner = tbinfo->rolname,
19915 : .description = "TRIGGER",
19916 : .section = SECTION_POST_DATA,
19917 : .createStmt = query->data,
19918 : .dropStmt = delqry->data));
19919 :
19920 492 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19921 0 : dumpComment(fout, trigprefix->data, qtabname,
19922 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19923 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19924 :
19925 492 : free(tag);
19926 492 : destroyPQExpBuffer(query);
19927 492 : destroyPQExpBuffer(delqry);
19928 492 : destroyPQExpBuffer(trigprefix);
19929 492 : destroyPQExpBuffer(trigidentity);
19930 492 : free(qtabname);
19931 : }
19932 :
19933 : /*
19934 : * dumpEventTrigger
19935 : * write the declaration of one user-defined event trigger
19936 : */
19937 : static void
19938 42 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19939 : {
19940 42 : DumpOptions *dopt = fout->dopt;
19941 : PQExpBuffer query;
19942 : PQExpBuffer delqry;
19943 : char *qevtname;
19944 :
19945 : /* Do nothing if not dumping schema */
19946 42 : if (!dopt->dumpSchema)
19947 6 : return;
19948 :
19949 36 : query = createPQExpBuffer();
19950 36 : delqry = createPQExpBuffer();
19951 :
19952 36 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19953 :
19954 36 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19955 36 : appendPQExpBufferStr(query, qevtname);
19956 36 : appendPQExpBufferStr(query, " ON ");
19957 36 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19958 :
19959 36 : if (strcmp("", evtinfo->evttags) != 0)
19960 : {
19961 5 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19962 5 : appendPQExpBufferStr(query, evtinfo->evttags);
19963 5 : appendPQExpBufferChar(query, ')');
19964 : }
19965 :
19966 36 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19967 36 : appendPQExpBufferStr(query, evtinfo->evtfname);
19968 36 : appendPQExpBufferStr(query, "();\n");
19969 :
19970 36 : if (evtinfo->evtenabled != 'O')
19971 : {
19972 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19973 : qevtname);
19974 0 : switch (evtinfo->evtenabled)
19975 : {
19976 0 : case 'D':
19977 0 : appendPQExpBufferStr(query, "DISABLE");
19978 0 : break;
19979 0 : case 'A':
19980 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19981 0 : break;
19982 0 : case 'R':
19983 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19984 0 : break;
19985 0 : default:
19986 0 : appendPQExpBufferStr(query, "ENABLE");
19987 0 : break;
19988 : }
19989 0 : appendPQExpBufferStr(query, ";\n");
19990 : }
19991 :
19992 36 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19993 : qevtname);
19994 :
19995 36 : if (dopt->binary_upgrade)
19996 2 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19997 : "EVENT TRIGGER", qevtname, NULL);
19998 :
19999 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20000 36 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
20001 36 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
20002 : .owner = evtinfo->evtowner,
20003 : .description = "EVENT TRIGGER",
20004 : .section = SECTION_POST_DATA,
20005 : .createStmt = query->data,
20006 : .dropStmt = delqry->data));
20007 :
20008 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20009 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
20010 0 : NULL, evtinfo->evtowner,
20011 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20012 :
20013 36 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
20014 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
20015 0 : NULL, evtinfo->evtowner,
20016 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20017 :
20018 36 : destroyPQExpBuffer(query);
20019 36 : destroyPQExpBuffer(delqry);
20020 36 : free(qevtname);
20021 : }
20022 :
20023 : /*
20024 : * dumpRule
20025 : * Dump a rule
20026 : */
20027 : static void
20028 1164 : dumpRule(Archive *fout, const RuleInfo *rinfo)
20029 : {
20030 1164 : DumpOptions *dopt = fout->dopt;
20031 1164 : TableInfo *tbinfo = rinfo->ruletable;
20032 : bool is_view;
20033 : PQExpBuffer query;
20034 : PQExpBuffer cmd;
20035 : PQExpBuffer delcmd;
20036 : PQExpBuffer ruleprefix;
20037 : char *qtabname;
20038 : PGresult *res;
20039 : char *tag;
20040 :
20041 : /* Do nothing if not dumping schema */
20042 1164 : if (!dopt->dumpSchema)
20043 60 : return;
20044 :
20045 : /*
20046 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
20047 : * we do not want to dump it as a separate object.
20048 : */
20049 1104 : if (!rinfo->separate)
20050 893 : return;
20051 :
20052 : /*
20053 : * If it's an ON SELECT rule, we want to print it as a view definition,
20054 : * instead of a rule.
20055 : */
20056 211 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
20057 :
20058 211 : query = createPQExpBuffer();
20059 211 : cmd = createPQExpBuffer();
20060 211 : delcmd = createPQExpBuffer();
20061 211 : ruleprefix = createPQExpBuffer();
20062 :
20063 211 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
20064 :
20065 211 : if (is_view)
20066 : {
20067 : PQExpBuffer result;
20068 :
20069 : /*
20070 : * We need OR REPLACE here because we'll be replacing a dummy view.
20071 : * Otherwise this should look largely like the regular view dump code.
20072 : */
20073 10 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
20074 10 : fmtQualifiedDumpable(tbinfo));
20075 10 : if (nonemptyReloptions(tbinfo->reloptions))
20076 : {
20077 0 : appendPQExpBufferStr(cmd, " WITH (");
20078 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
20079 0 : appendPQExpBufferChar(cmd, ')');
20080 : }
20081 10 : result = createViewAsClause(fout, tbinfo);
20082 10 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
20083 10 : destroyPQExpBuffer(result);
20084 10 : if (tbinfo->checkoption != NULL)
20085 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
20086 : tbinfo->checkoption);
20087 10 : appendPQExpBufferStr(cmd, ";\n");
20088 : }
20089 : else
20090 : {
20091 : /* In the rule case, just print pg_get_ruledef's result verbatim */
20092 201 : appendPQExpBuffer(query,
20093 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
20094 201 : rinfo->dobj.catId.oid);
20095 :
20096 201 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20097 :
20098 201 : if (PQntuples(res) != 1)
20099 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
20100 : rinfo->dobj.name, tbinfo->dobj.name);
20101 :
20102 201 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
20103 :
20104 201 : PQclear(res);
20105 : }
20106 :
20107 : /*
20108 : * Add the command to alter the rules replication firing semantics if it
20109 : * differs from the default.
20110 : */
20111 211 : if (rinfo->ev_enabled != 'O')
20112 : {
20113 15 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
20114 15 : switch (rinfo->ev_enabled)
20115 : {
20116 0 : case 'A':
20117 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
20118 0 : fmtId(rinfo->dobj.name));
20119 0 : break;
20120 0 : case 'R':
20121 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
20122 0 : fmtId(rinfo->dobj.name));
20123 0 : break;
20124 15 : case 'D':
20125 15 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
20126 15 : fmtId(rinfo->dobj.name));
20127 15 : break;
20128 : }
20129 : }
20130 :
20131 211 : if (is_view)
20132 : {
20133 : /*
20134 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
20135 : * REPLACE VIEW to replace the rule with something with minimal
20136 : * dependencies.
20137 : */
20138 : PQExpBuffer result;
20139 :
20140 10 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
20141 10 : fmtQualifiedDumpable(tbinfo));
20142 10 : result = createDummyViewAsClause(fout, tbinfo);
20143 10 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
20144 10 : destroyPQExpBuffer(result);
20145 : }
20146 : else
20147 : {
20148 201 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
20149 201 : fmtId(rinfo->dobj.name));
20150 201 : appendPQExpBuffer(delcmd, "ON %s;\n",
20151 201 : fmtQualifiedDumpable(tbinfo));
20152 : }
20153 :
20154 211 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
20155 211 : fmtId(rinfo->dobj.name));
20156 :
20157 211 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
20158 :
20159 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20160 211 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
20161 211 : ARCHIVE_OPTS(.tag = tag,
20162 : .namespace = tbinfo->dobj.namespace->dobj.name,
20163 : .owner = tbinfo->rolname,
20164 : .description = "RULE",
20165 : .section = SECTION_POST_DATA,
20166 : .createStmt = cmd->data,
20167 : .dropStmt = delcmd->data));
20168 :
20169 : /* Dump rule comments */
20170 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20171 0 : dumpComment(fout, ruleprefix->data, qtabname,
20172 0 : tbinfo->dobj.namespace->dobj.name,
20173 : tbinfo->rolname,
20174 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
20175 :
20176 211 : free(tag);
20177 211 : destroyPQExpBuffer(query);
20178 211 : destroyPQExpBuffer(cmd);
20179 211 : destroyPQExpBuffer(delcmd);
20180 211 : destroyPQExpBuffer(ruleprefix);
20181 211 : free(qtabname);
20182 : }
20183 :
20184 : /*
20185 : * getExtensionMembership --- obtain extension membership data
20186 : *
20187 : * We need to identify objects that are extension members as soon as they're
20188 : * loaded, so that we can correctly determine whether they need to be dumped.
20189 : * Generally speaking, extension member objects will get marked as *not* to
20190 : * be dumped, as they will be recreated by the single CREATE EXTENSION
20191 : * command. However, in binary upgrade mode we still need to dump the members
20192 : * individually.
20193 : */
20194 : void
20195 260 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
20196 : int numExtensions)
20197 : {
20198 : PQExpBuffer query;
20199 : PGresult *res;
20200 : int ntups,
20201 : i;
20202 : int i_classid,
20203 : i_objid,
20204 : i_refobjid;
20205 : ExtensionInfo *ext;
20206 :
20207 : /* Nothing to do if no extensions */
20208 260 : if (numExtensions == 0)
20209 0 : return;
20210 :
20211 260 : query = createPQExpBuffer();
20212 :
20213 : /* refclassid constraint is redundant but may speed the search */
20214 260 : appendPQExpBufferStr(query, "SELECT "
20215 : "classid, objid, refobjid "
20216 : "FROM pg_depend "
20217 : "WHERE refclassid = 'pg_extension'::regclass "
20218 : "AND deptype = 'e' "
20219 : "ORDER BY 3");
20220 :
20221 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20222 :
20223 260 : ntups = PQntuples(res);
20224 :
20225 260 : i_classid = PQfnumber(res, "classid");
20226 260 : i_objid = PQfnumber(res, "objid");
20227 260 : i_refobjid = PQfnumber(res, "refobjid");
20228 :
20229 : /*
20230 : * Since we ordered the SELECT by referenced ID, we can expect that
20231 : * multiple entries for the same extension will appear together; this
20232 : * saves on searches.
20233 : */
20234 260 : ext = NULL;
20235 :
20236 1906 : for (i = 0; i < ntups; i++)
20237 : {
20238 : CatalogId objId;
20239 : Oid extId;
20240 :
20241 1646 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20242 1646 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20243 1646 : extId = atooid(PQgetvalue(res, i, i_refobjid));
20244 :
20245 1646 : if (ext == NULL ||
20246 1386 : ext->dobj.catId.oid != extId)
20247 291 : ext = findExtensionByOid(extId);
20248 :
20249 1646 : if (ext == NULL)
20250 : {
20251 : /* shouldn't happen */
20252 0 : pg_log_warning("could not find referenced extension %u", extId);
20253 0 : continue;
20254 : }
20255 :
20256 1646 : recordExtensionMembership(objId, ext);
20257 : }
20258 :
20259 260 : PQclear(res);
20260 :
20261 260 : destroyPQExpBuffer(query);
20262 : }
20263 :
20264 : /*
20265 : * processExtensionTables --- deal with extension configuration tables
20266 : *
20267 : * There are two parts to this process:
20268 : *
20269 : * 1. Identify and create dump records for extension configuration tables.
20270 : *
20271 : * Extensions can mark tables as "configuration", which means that the user
20272 : * is able and expected to modify those tables after the extension has been
20273 : * loaded. For these tables, we dump out only the data- the structure is
20274 : * expected to be handled at CREATE EXTENSION time, including any indexes or
20275 : * foreign keys, which brings us to-
20276 : *
20277 : * 2. Record FK dependencies between configuration tables.
20278 : *
20279 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
20280 : * the data is loaded, we have to work out what the best order for reloading
20281 : * the data is, to avoid FK violations when the tables are restored. This is
20282 : * not perfect- we can't handle circular dependencies and if any exist they
20283 : * will cause an invalid dump to be produced (though at least all of the data
20284 : * is included for a user to manually restore). This is currently documented
20285 : * but perhaps we can provide a better solution in the future.
20286 : */
20287 : void
20288 259 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
20289 : int numExtensions)
20290 : {
20291 259 : DumpOptions *dopt = fout->dopt;
20292 : PQExpBuffer query;
20293 : PGresult *res;
20294 : int ntups,
20295 : i;
20296 : int i_conrelid,
20297 : i_confrelid;
20298 :
20299 : /* Nothing to do if no extensions */
20300 259 : if (numExtensions == 0)
20301 0 : return;
20302 :
20303 : /*
20304 : * Identify extension configuration tables and create TableDataInfo
20305 : * objects for them, ensuring their data will be dumped even though the
20306 : * tables themselves won't be.
20307 : *
20308 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
20309 : * user data in a configuration table is treated like schema data. This
20310 : * seems appropriate since system data in a config table would get
20311 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
20312 : * list of extensions to be included, none of its data is dumped.
20313 : */
20314 549 : for (i = 0; i < numExtensions; i++)
20315 : {
20316 290 : ExtensionInfo *curext = &(extinfo[i]);
20317 290 : char *extconfig = curext->extconfig;
20318 290 : char *extcondition = curext->extcondition;
20319 290 : char **extconfigarray = NULL;
20320 290 : char **extconditionarray = NULL;
20321 290 : int nconfigitems = 0;
20322 290 : int nconditionitems = 0;
20323 :
20324 : /*
20325 : * Check if this extension is listed as to include in the dump. If
20326 : * not, any table data associated with it is discarded.
20327 : */
20328 290 : if (extension_include_oids.head != NULL &&
20329 8 : !simple_oid_list_member(&extension_include_oids,
20330 : curext->dobj.catId.oid))
20331 6 : continue;
20332 :
20333 : /*
20334 : * Check if this extension is listed as to exclude in the dump. If
20335 : * yes, any table data associated with it is discarded.
20336 : */
20337 290 : if (extension_exclude_oids.head != NULL &&
20338 4 : simple_oid_list_member(&extension_exclude_oids,
20339 : curext->dobj.catId.oid))
20340 2 : continue;
20341 :
20342 284 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20343 : {
20344 : int j;
20345 :
20346 20 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20347 0 : pg_fatal("could not parse %s array", "extconfig");
20348 20 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20349 0 : pg_fatal("could not parse %s array", "extcondition");
20350 20 : if (nconfigitems != nconditionitems)
20351 0 : pg_fatal("mismatched number of configurations and conditions for extension");
20352 :
20353 60 : for (j = 0; j < nconfigitems; j++)
20354 : {
20355 : TableInfo *configtbl;
20356 40 : Oid configtbloid = atooid(extconfigarray[j]);
20357 40 : bool dumpobj =
20358 40 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20359 :
20360 40 : configtbl = findTableByOid(configtbloid);
20361 40 : if (configtbl == NULL)
20362 0 : continue;
20363 :
20364 : /*
20365 : * Tables of not-to-be-dumped extensions shouldn't be dumped
20366 : * unless the table or its schema is explicitly included
20367 : */
20368 40 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20369 : {
20370 : /* check table explicitly requested */
20371 2 : if (table_include_oids.head != NULL &&
20372 0 : simple_oid_list_member(&table_include_oids,
20373 : configtbloid))
20374 0 : dumpobj = true;
20375 :
20376 : /* check table's schema explicitly requested */
20377 2 : if (configtbl->dobj.namespace->dobj.dump &
20378 : DUMP_COMPONENT_DATA)
20379 2 : dumpobj = true;
20380 : }
20381 :
20382 : /* check table excluded by an exclusion switch */
20383 44 : if (table_exclude_oids.head != NULL &&
20384 4 : simple_oid_list_member(&table_exclude_oids,
20385 : configtbloid))
20386 1 : dumpobj = false;
20387 :
20388 : /* check schema excluded by an exclusion switch */
20389 40 : if (simple_oid_list_member(&schema_exclude_oids,
20390 40 : configtbl->dobj.namespace->dobj.catId.oid))
20391 0 : dumpobj = false;
20392 :
20393 40 : if (dumpobj)
20394 : {
20395 39 : makeTableDataInfo(dopt, configtbl);
20396 39 : if (configtbl->dataObj != NULL)
20397 : {
20398 39 : if (strlen(extconditionarray[j]) > 0)
20399 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20400 : }
20401 : }
20402 : }
20403 : }
20404 284 : if (extconfigarray)
20405 20 : free(extconfigarray);
20406 284 : if (extconditionarray)
20407 20 : free(extconditionarray);
20408 : }
20409 :
20410 : /*
20411 : * Now that all the TableDataInfo objects have been created for all the
20412 : * extensions, check their FK dependencies and register them to try and
20413 : * dump the data out in an order that they can be restored in.
20414 : *
20415 : * Note that this is not a problem for user tables as their FKs are
20416 : * recreated after the data has been loaded.
20417 : */
20418 :
20419 259 : query = createPQExpBuffer();
20420 :
20421 259 : printfPQExpBuffer(query,
20422 : "SELECT conrelid, confrelid "
20423 : "FROM pg_constraint "
20424 : "JOIN pg_depend ON (objid = confrelid) "
20425 : "WHERE contype = 'f' "
20426 : "AND refclassid = 'pg_extension'::regclass "
20427 : "AND classid = 'pg_class'::regclass;");
20428 :
20429 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20430 259 : ntups = PQntuples(res);
20431 :
20432 259 : i_conrelid = PQfnumber(res, "conrelid");
20433 259 : i_confrelid = PQfnumber(res, "confrelid");
20434 :
20435 : /* Now get the dependencies and register them */
20436 259 : for (i = 0; i < ntups; i++)
20437 : {
20438 : Oid conrelid,
20439 : confrelid;
20440 : TableInfo *reftable,
20441 : *contable;
20442 :
20443 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20444 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20445 0 : contable = findTableByOid(conrelid);
20446 0 : reftable = findTableByOid(confrelid);
20447 :
20448 0 : if (reftable == NULL ||
20449 0 : reftable->dataObj == NULL ||
20450 0 : contable == NULL ||
20451 0 : contable->dataObj == NULL)
20452 0 : continue;
20453 :
20454 : /*
20455 : * Make referencing TABLE_DATA object depend on the referenced table's
20456 : * TABLE_DATA object.
20457 : */
20458 0 : addObjectDependency(&contable->dataObj->dobj,
20459 0 : reftable->dataObj->dobj.dumpId);
20460 : }
20461 259 : PQclear(res);
20462 259 : destroyPQExpBuffer(query);
20463 : }
20464 :
20465 : /*
20466 : * getDependencies --- obtain available dependency data
20467 : */
20468 : static void
20469 259 : getDependencies(Archive *fout)
20470 : {
20471 : PQExpBuffer query;
20472 : PGresult *res;
20473 : int ntups,
20474 : i;
20475 : int i_classid,
20476 : i_objid,
20477 : i_refclassid,
20478 : i_refobjid,
20479 : i_deptype;
20480 : DumpableObject *dobj,
20481 : *refdobj;
20482 :
20483 259 : pg_log_info("reading dependency data");
20484 :
20485 259 : query = createPQExpBuffer();
20486 :
20487 : /*
20488 : * Messy query to collect the dependency data we need. Note that we
20489 : * ignore the sub-object column, so that dependencies of or on a column
20490 : * look the same as dependencies of or on a whole table.
20491 : *
20492 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
20493 : * already processed by getExtensionMembership.
20494 : */
20495 259 : appendPQExpBufferStr(query, "SELECT "
20496 : "classid, objid, refclassid, refobjid, deptype "
20497 : "FROM pg_depend "
20498 : "WHERE deptype != 'p' AND deptype != 'e'\n");
20499 :
20500 : /*
20501 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
20502 : * have to translate their dependencies into dependencies of their parent
20503 : * opfamily. Ignore internal dependencies though, as those will point to
20504 : * their parent opclass, which we needn't consider here (and if we did,
20505 : * it'd just result in circular dependencies). Also, "loose" opfamily
20506 : * entries will have dependencies on their parent opfamily, which we
20507 : * should drop since they'd likewise become useless self-dependencies.
20508 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20509 : */
20510 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20511 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20512 : "FROM pg_depend d, pg_amop o "
20513 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20514 : "classid = 'pg_amop'::regclass AND objid = o.oid "
20515 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20516 :
20517 : /* Likewise for pg_amproc entries */
20518 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20519 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20520 : "FROM pg_depend d, pg_amproc p "
20521 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20522 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20523 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20524 :
20525 : /*
20526 : * Translate dependencies of pg_propgraph_element entries into
20527 : * dependencies of their parent pg_class entry.
20528 : */
20529 259 : if (fout->remoteVersion >= 190000)
20530 259 : appendPQExpBufferStr(query, "UNION ALL\n"
20531 : "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20532 : "FROM pg_depend d, pg_propgraph_element pge "
20533 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20534 : "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20535 :
20536 : /* Sort the output for efficiency below */
20537 259 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20538 :
20539 259 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20540 :
20541 259 : ntups = PQntuples(res);
20542 :
20543 259 : i_classid = PQfnumber(res, "classid");
20544 259 : i_objid = PQfnumber(res, "objid");
20545 259 : i_refclassid = PQfnumber(res, "refclassid");
20546 259 : i_refobjid = PQfnumber(res, "refobjid");
20547 259 : i_deptype = PQfnumber(res, "deptype");
20548 :
20549 : /*
20550 : * Since we ordered the SELECT by referencing ID, we can expect that
20551 : * multiple entries for the same object will appear together; this saves
20552 : * on searches.
20553 : */
20554 259 : dobj = NULL;
20555 :
20556 590233 : for (i = 0; i < ntups; i++)
20557 : {
20558 : CatalogId objId;
20559 : CatalogId refobjId;
20560 : char deptype;
20561 :
20562 589974 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20563 589974 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20564 589974 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20565 589974 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20566 589974 : deptype = *(PQgetvalue(res, i, i_deptype));
20567 :
20568 589974 : if (dobj == NULL ||
20569 554262 : dobj->catId.tableoid != objId.tableoid ||
20570 551689 : dobj->catId.oid != objId.oid)
20571 248942 : dobj = findObjectByCatalogId(objId);
20572 :
20573 : /*
20574 : * Failure to find objects mentioned in pg_depend is not unexpected,
20575 : * since for example we don't collect info about TOAST tables.
20576 : */
20577 589974 : if (dobj == NULL)
20578 : {
20579 : #ifdef NOT_USED
20580 : pg_log_warning("no referencing object %u %u",
20581 : objId.tableoid, objId.oid);
20582 : #endif
20583 37017 : continue;
20584 : }
20585 :
20586 554514 : refdobj = findObjectByCatalogId(refobjId);
20587 :
20588 554514 : if (refdobj == NULL)
20589 : {
20590 : #ifdef NOT_USED
20591 : pg_log_warning("no referenced object %u %u",
20592 : refobjId.tableoid, refobjId.oid);
20593 : #endif
20594 1557 : continue;
20595 : }
20596 :
20597 : /*
20598 : * For 'x' dependencies, mark the object for later; we still add the
20599 : * normal dependency, for possible ordering purposes. Currently
20600 : * pg_dump_sort.c knows to put extensions ahead of all object types
20601 : * that could possibly depend on them, but this is safer.
20602 : */
20603 552957 : if (deptype == 'x')
20604 44 : dobj->depends_on_ext = true;
20605 :
20606 : /*
20607 : * Ordinarily, table rowtypes have implicit dependencies on their
20608 : * tables. However, for a composite type the implicit dependency goes
20609 : * the other way in pg_depend; which is the right thing for DROP but
20610 : * it doesn't produce the dependency ordering we need. So in that one
20611 : * case, we reverse the direction of the dependency.
20612 : */
20613 552957 : if (deptype == 'i' &&
20614 152563 : dobj->objType == DO_TABLE &&
20615 1286 : refdobj->objType == DO_TYPE)
20616 182 : addObjectDependency(refdobj, dobj->dumpId);
20617 : else
20618 : /* normal case */
20619 552775 : addObjectDependency(dobj, refdobj->dumpId);
20620 : }
20621 :
20622 259 : PQclear(res);
20623 :
20624 259 : destroyPQExpBuffer(query);
20625 259 : }
20626 :
20627 :
20628 : /*
20629 : * createBoundaryObjects - create dummy DumpableObjects to represent
20630 : * dump section boundaries.
20631 : */
20632 : static DumpableObject *
20633 259 : createBoundaryObjects(void)
20634 : {
20635 : DumpableObject *dobjs;
20636 :
20637 259 : dobjs = pg_malloc_array(DumpableObject, 2);
20638 :
20639 259 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20640 259 : dobjs[0].catId = nilCatalogId;
20641 259 : AssignDumpId(dobjs + 0);
20642 259 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20643 :
20644 259 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20645 259 : dobjs[1].catId = nilCatalogId;
20646 259 : AssignDumpId(dobjs + 1);
20647 259 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20648 :
20649 259 : return dobjs;
20650 : }
20651 :
20652 : /*
20653 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20654 : * section boundaries.
20655 : */
20656 : static void
20657 259 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20658 : DumpableObject *boundaryObjs)
20659 : {
20660 259 : DumpableObject *preDataBound = boundaryObjs + 0;
20661 259 : DumpableObject *postDataBound = boundaryObjs + 1;
20662 : int i;
20663 :
20664 976294 : for (i = 0; i < numObjs; i++)
20665 : {
20666 976035 : DumpableObject *dobj = dobjs[i];
20667 :
20668 : /*
20669 : * The classification of object types here must match the SECTION_xxx
20670 : * values assigned during subsequent ArchiveEntry calls!
20671 : */
20672 976035 : switch (dobj->objType)
20673 : {
20674 914269 : case DO_NAMESPACE:
20675 : case DO_EXTENSION:
20676 : case DO_TYPE:
20677 : case DO_SHELL_TYPE:
20678 : case DO_FUNC:
20679 : case DO_AGG:
20680 : case DO_OPERATOR:
20681 : case DO_ACCESS_METHOD:
20682 : case DO_OPCLASS:
20683 : case DO_OPFAMILY:
20684 : case DO_COLLATION:
20685 : case DO_CONVERSION:
20686 : case DO_TABLE:
20687 : case DO_TABLE_ATTACH:
20688 : case DO_ATTRDEF:
20689 : case DO_PROCLANG:
20690 : case DO_CAST:
20691 : case DO_DUMMY_TYPE:
20692 : case DO_TSPARSER:
20693 : case DO_TSDICT:
20694 : case DO_TSTEMPLATE:
20695 : case DO_TSCONFIG:
20696 : case DO_FDW:
20697 : case DO_FOREIGN_SERVER:
20698 : case DO_TRANSFORM:
20699 : /* Pre-data objects: must come before the pre-data boundary */
20700 914269 : addObjectDependency(preDataBound, dobj->dumpId);
20701 914269 : break;
20702 5226 : case DO_TABLE_DATA:
20703 : case DO_SEQUENCE_SET:
20704 : case DO_LARGE_OBJECT:
20705 : case DO_LARGE_OBJECT_DATA:
20706 : /* Data objects: must come between the boundaries */
20707 5226 : addObjectDependency(dobj, preDataBound->dumpId);
20708 5226 : addObjectDependency(postDataBound, dobj->dumpId);
20709 5226 : break;
20710 6121 : case DO_INDEX:
20711 : case DO_INDEX_ATTACH:
20712 : case DO_STATSEXT:
20713 : case DO_REFRESH_MATVIEW:
20714 : case DO_TRIGGER:
20715 : case DO_EVENT_TRIGGER:
20716 : case DO_DEFAULT_ACL:
20717 : case DO_POLICY:
20718 : case DO_PUBLICATION:
20719 : case DO_PUBLICATION_REL:
20720 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20721 : case DO_SUBSCRIPTION:
20722 : case DO_SUBSCRIPTION_REL:
20723 : /* Post-data objects: must come after the post-data boundary */
20724 6121 : addObjectDependency(dobj, postDataBound->dumpId);
20725 6121 : break;
20726 43647 : case DO_RULE:
20727 : /* Rules are post-data, but only if dumped separately */
20728 43647 : if (((RuleInfo *) dobj)->separate)
20729 791 : addObjectDependency(dobj, postDataBound->dumpId);
20730 43647 : break;
20731 2778 : case DO_CONSTRAINT:
20732 : case DO_FK_CONSTRAINT:
20733 : /* Constraints are post-data, but only if dumped separately */
20734 2778 : if (((ConstraintInfo *) dobj)->separate)
20735 2050 : addObjectDependency(dobj, postDataBound->dumpId);
20736 2778 : break;
20737 259 : case DO_PRE_DATA_BOUNDARY:
20738 : /* nothing to do */
20739 259 : break;
20740 259 : case DO_POST_DATA_BOUNDARY:
20741 : /* must come after the pre-data boundary */
20742 259 : addObjectDependency(dobj, preDataBound->dumpId);
20743 259 : break;
20744 3476 : case DO_REL_STATS:
20745 : /* stats section varies by parent object type, DATA or POST */
20746 3476 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20747 : {
20748 2269 : addObjectDependency(dobj, preDataBound->dumpId);
20749 2269 : addObjectDependency(postDataBound, dobj->dumpId);
20750 : }
20751 : else
20752 1207 : addObjectDependency(dobj, postDataBound->dumpId);
20753 3476 : break;
20754 : }
20755 : }
20756 259 : }
20757 :
20758 :
20759 : /*
20760 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20761 : *
20762 : * The raw dependency data obtained by getDependencies() is not terribly
20763 : * useful in an archive dump, because in many cases there are dependency
20764 : * chains linking through objects that don't appear explicitly in the dump.
20765 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20766 : * will depend on other objects --- but the rule will not appear as a separate
20767 : * object in the dump. We need to adjust the view's dependencies to include
20768 : * whatever the rule depends on that is included in the dump.
20769 : *
20770 : * Just to make things more complicated, there are also "special" dependencies
20771 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20772 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20773 : * its table. In these cases we must leave the dependencies strictly as-is
20774 : * even if they refer to not-to-be-dumped objects.
20775 : *
20776 : * To handle this, the convention is that "special" dependencies are created
20777 : * during ArchiveEntry calls, and an archive TOC item that has any such
20778 : * entries will not be touched here. Otherwise, we recursively search the
20779 : * DumpableObject data structures to build the correct dependencies for each
20780 : * archive TOC item.
20781 : */
20782 : static void
20783 131 : BuildArchiveDependencies(Archive *fout)
20784 : {
20785 131 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20786 : TocEntry *te;
20787 :
20788 : /* Scan all TOC entries in the archive */
20789 7753 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20790 : {
20791 : DumpableObject *dobj;
20792 : DumpId *dependencies;
20793 : int nDeps;
20794 : int allocDeps;
20795 :
20796 : /* No need to process entries that will not be dumped */
20797 7622 : if (te->reqs == 0)
20798 3894 : continue;
20799 : /* Ignore entries that already have "special" dependencies */
20800 7615 : if (te->nDeps > 0)
20801 3125 : continue;
20802 : /* Otherwise, look up the item's original DumpableObject, if any */
20803 4490 : dobj = findObjectByDumpId(te->dumpId);
20804 4490 : if (dobj == NULL)
20805 657 : continue;
20806 : /* No work if it has no dependencies */
20807 3833 : if (dobj->nDeps <= 0)
20808 105 : continue;
20809 : /* Set up work array */
20810 3728 : allocDeps = 64;
20811 3728 : dependencies = pg_malloc_array(DumpId, allocDeps);
20812 3728 : nDeps = 0;
20813 : /* Recursively find all dumpable dependencies */
20814 3728 : findDumpableDependencies(AH, dobj,
20815 : &dependencies, &nDeps, &allocDeps);
20816 : /* And save 'em ... */
20817 3728 : if (nDeps > 0)
20818 : {
20819 2667 : dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20820 2667 : te->dependencies = dependencies;
20821 2667 : te->nDeps = nDeps;
20822 : }
20823 : else
20824 1061 : free(dependencies);
20825 : }
20826 131 : }
20827 :
20828 : /* Recursive search subroutine for BuildArchiveDependencies */
20829 : static void
20830 9031 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20831 : DumpId **dependencies, int *nDeps, int *allocDeps)
20832 : {
20833 : int i;
20834 :
20835 : /*
20836 : * Ignore section boundary objects: if we search through them, we'll
20837 : * report lots of bogus dependencies.
20838 : */
20839 9031 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20840 9014 : dobj->objType == DO_POST_DATA_BOUNDARY)
20841 1497 : return;
20842 :
20843 18568 : for (i = 0; i < dobj->nDeps; i++)
20844 : {
20845 11034 : DumpId depid = dobj->dependencies[i];
20846 :
20847 11034 : if (TocIDRequired(AH, depid) != 0)
20848 : {
20849 : /* Object will be dumped, so just reference it as a dependency */
20850 5731 : if (*nDeps >= *allocDeps)
20851 : {
20852 0 : *allocDeps *= 2;
20853 0 : *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20854 : }
20855 5731 : (*dependencies)[*nDeps] = depid;
20856 5731 : (*nDeps)++;
20857 : }
20858 : else
20859 : {
20860 : /*
20861 : * Object will not be dumped, so recursively consider its deps. We
20862 : * rely on the assumption that sortDumpableObjects already broke
20863 : * any dependency loops, else we might recurse infinitely.
20864 : */
20865 5303 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20866 :
20867 5303 : if (otherdobj)
20868 5303 : findDumpableDependencies(AH, otherdobj,
20869 : dependencies, nDeps, allocDeps);
20870 : }
20871 : }
20872 : }
20873 :
20874 :
20875 : /*
20876 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20877 : * given type OID.
20878 : *
20879 : * This does not guarantee to schema-qualify the output, so it should not
20880 : * be used to create the target object name for CREATE or ALTER commands.
20881 : *
20882 : * Note that the result is cached and must not be freed by the caller.
20883 : */
20884 : static const char *
20885 2375 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20886 : {
20887 : TypeInfo *typeInfo;
20888 : char *result;
20889 : PQExpBuffer query;
20890 : PGresult *res;
20891 :
20892 2375 : if (oid == 0)
20893 : {
20894 0 : if ((opts & zeroAsStar) != 0)
20895 0 : return "*";
20896 0 : else if ((opts & zeroAsNone) != 0)
20897 0 : return "NONE";
20898 : }
20899 :
20900 : /* see if we have the result cached in the type's TypeInfo record */
20901 2375 : typeInfo = findTypeByOid(oid);
20902 2375 : if (typeInfo && typeInfo->ftypname)
20903 1898 : return typeInfo->ftypname;
20904 :
20905 477 : query = createPQExpBuffer();
20906 477 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20907 : oid);
20908 :
20909 477 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20910 :
20911 : /* result of format_type is already quoted */
20912 477 : result = pg_strdup(PQgetvalue(res, 0, 0));
20913 :
20914 477 : PQclear(res);
20915 477 : destroyPQExpBuffer(query);
20916 :
20917 : /*
20918 : * Cache the result for re-use in later requests, if possible. If we
20919 : * don't have a TypeInfo for the type, the string will be leaked once the
20920 : * caller is done with it ... but that case really should not happen, so
20921 : * leaking if it does seems acceptable.
20922 : */
20923 477 : if (typeInfo)
20924 477 : typeInfo->ftypname = result;
20925 :
20926 477 : return result;
20927 : }
20928 :
20929 : /*
20930 : * Return a column list clause for the given relation.
20931 : *
20932 : * Special case: if there are no undropped columns in the relation, return
20933 : * "", not an invalid "()" column list.
20934 : */
20935 : static const char *
20936 9052 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20937 : {
20938 9052 : int numatts = ti->numatts;
20939 9052 : char **attnames = ti->attnames;
20940 9052 : bool *attisdropped = ti->attisdropped;
20941 9052 : char *attgenerated = ti->attgenerated;
20942 : bool needComma;
20943 : int i;
20944 :
20945 9052 : appendPQExpBufferChar(buffer, '(');
20946 9052 : needComma = false;
20947 43072 : for (i = 0; i < numatts; i++)
20948 : {
20949 34020 : if (attisdropped[i])
20950 606 : continue;
20951 33414 : if (attgenerated[i])
20952 1152 : continue;
20953 32262 : if (needComma)
20954 23434 : appendPQExpBufferStr(buffer, ", ");
20955 32262 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20956 32262 : needComma = true;
20957 : }
20958 :
20959 9052 : if (!needComma)
20960 224 : return ""; /* no undropped columns */
20961 :
20962 8828 : appendPQExpBufferChar(buffer, ')');
20963 8828 : return buffer->data;
20964 : }
20965 :
20966 : /*
20967 : * Check if a reloptions array is nonempty.
20968 : */
20969 : static bool
20970 14756 : nonemptyReloptions(const char *reloptions)
20971 : {
20972 : /* Don't want to print it if it's just "{}" */
20973 14756 : return (reloptions != NULL && strlen(reloptions) > 2);
20974 : }
20975 :
20976 : /*
20977 : * Format a reloptions array and append it to the given buffer.
20978 : *
20979 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20980 : */
20981 : static void
20982 219 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20983 : const char *prefix, Archive *fout)
20984 : {
20985 : bool res;
20986 :
20987 219 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20988 219 : fout->std_strings);
20989 219 : if (!res)
20990 0 : pg_log_warning("could not parse %s array", "reloptions");
20991 219 : }
20992 :
20993 : /*
20994 : * read_dump_filters - retrieve object identifier patterns from file
20995 : *
20996 : * Parse the specified filter file for include and exclude patterns, and add
20997 : * them to the relevant lists. If the filename is "-" then filters will be
20998 : * read from STDIN rather than a file.
20999 : */
21000 : static void
21001 26 : read_dump_filters(const char *filename, DumpOptions *dopt)
21002 : {
21003 : FilterStateData fstate;
21004 : char *objname;
21005 : FilterCommandType comtype;
21006 : FilterObjectType objtype;
21007 :
21008 26 : filter_init(&fstate, filename, exit_nicely);
21009 :
21010 84 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
21011 : {
21012 33 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
21013 : {
21014 17 : switch (objtype)
21015 : {
21016 0 : case FILTER_OBJECT_TYPE_NONE:
21017 0 : break;
21018 0 : case FILTER_OBJECT_TYPE_DATABASE:
21019 : case FILTER_OBJECT_TYPE_FUNCTION:
21020 : case FILTER_OBJECT_TYPE_INDEX:
21021 : case FILTER_OBJECT_TYPE_TABLE_DATA:
21022 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
21023 : case FILTER_OBJECT_TYPE_TRIGGER:
21024 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21025 : "include",
21026 : filter_object_type_name(objtype));
21027 0 : exit_nicely(1);
21028 : break; /* unreachable */
21029 :
21030 1 : case FILTER_OBJECT_TYPE_EXTENSION:
21031 1 : simple_string_list_append(&extension_include_patterns, objname);
21032 1 : break;
21033 1 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
21034 1 : simple_string_list_append(&foreign_servers_include_patterns, objname);
21035 1 : break;
21036 1 : case FILTER_OBJECT_TYPE_SCHEMA:
21037 1 : simple_string_list_append(&schema_include_patterns, objname);
21038 1 : dopt->include_everything = false;
21039 1 : break;
21040 13 : case FILTER_OBJECT_TYPE_TABLE:
21041 13 : simple_string_list_append(&table_include_patterns, objname);
21042 13 : dopt->include_everything = false;
21043 13 : break;
21044 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
21045 1 : simple_string_list_append(&table_include_patterns_and_children,
21046 : objname);
21047 1 : dopt->include_everything = false;
21048 1 : break;
21049 : }
21050 : }
21051 16 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
21052 : {
21053 9 : switch (objtype)
21054 : {
21055 0 : case FILTER_OBJECT_TYPE_NONE:
21056 0 : break;
21057 1 : case FILTER_OBJECT_TYPE_DATABASE:
21058 : case FILTER_OBJECT_TYPE_FUNCTION:
21059 : case FILTER_OBJECT_TYPE_INDEX:
21060 : case FILTER_OBJECT_TYPE_TRIGGER:
21061 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
21062 1 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21063 : "exclude",
21064 : filter_object_type_name(objtype));
21065 1 : exit_nicely(1);
21066 : break;
21067 :
21068 1 : case FILTER_OBJECT_TYPE_EXTENSION:
21069 1 : simple_string_list_append(&extension_exclude_patterns, objname);
21070 1 : break;
21071 1 : case FILTER_OBJECT_TYPE_TABLE_DATA:
21072 1 : simple_string_list_append(&tabledata_exclude_patterns,
21073 : objname);
21074 1 : break;
21075 1 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
21076 1 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
21077 : objname);
21078 1 : break;
21079 2 : case FILTER_OBJECT_TYPE_SCHEMA:
21080 2 : simple_string_list_append(&schema_exclude_patterns, objname);
21081 2 : break;
21082 2 : case FILTER_OBJECT_TYPE_TABLE:
21083 2 : simple_string_list_append(&table_exclude_patterns, objname);
21084 2 : break;
21085 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
21086 1 : simple_string_list_append(&table_exclude_patterns_and_children,
21087 : objname);
21088 1 : break;
21089 : }
21090 : }
21091 : else
21092 : {
21093 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
21094 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
21095 : }
21096 :
21097 32 : if (objname)
21098 25 : free(objname);
21099 : }
21100 :
21101 22 : filter_free(&fstate);
21102 22 : }
|