Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_dump.c
4 : * pg_dump is a utility for dumping out a postgres database
5 : * into a script file.
6 : *
7 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_default_acl_d.h"
51 : #include "catalog/pg_largeobject_d.h"
52 : #include "catalog/pg_proc_d.h"
53 : #include "catalog/pg_publication_d.h"
54 : #include "catalog/pg_subscription_d.h"
55 : #include "catalog/pg_type_d.h"
56 : #include "common/connect.h"
57 : #include "common/int.h"
58 : #include "common/relpath.h"
59 : #include "common/shortest_dec.h"
60 : #include "compress_io.h"
61 : #include "dumputils.h"
62 : #include "fe_utils/option_utils.h"
63 : #include "fe_utils/string_utils.h"
64 : #include "filter.h"
65 : #include "getopt_long.h"
66 : #include "libpq/libpq-fs.h"
67 : #include "parallel.h"
68 : #include "pg_backup_db.h"
69 : #include "pg_backup_utils.h"
70 : #include "pg_dump.h"
71 : #include "storage/block.h"
72 :
73 : typedef struct
74 : {
75 : Oid roleoid; /* role's OID */
76 : const char *rolename; /* role's name */
77 : } RoleNameItem;
78 :
79 : typedef struct
80 : {
81 : const char *descr; /* comment for an object */
82 : Oid classoid; /* object class (catalog OID) */
83 : Oid objoid; /* object OID */
84 : int objsubid; /* subobject (table column #) */
85 : } CommentItem;
86 :
87 : typedef struct
88 : {
89 : const char *provider; /* label provider of this security label */
90 : const char *label; /* security label for an object */
91 : Oid classoid; /* object class (catalog OID) */
92 : Oid objoid; /* object OID */
93 : int objsubid; /* subobject (table column #) */
94 : } SecLabelItem;
95 :
96 : typedef struct
97 : {
98 : Oid oid; /* object OID */
99 : char relkind; /* object kind */
100 : RelFileNumber relfilenumber; /* object filenode */
101 : Oid toast_oid; /* toast table OID */
102 : RelFileNumber toast_relfilenumber; /* toast table filenode */
103 : Oid toast_index_oid; /* toast table index OID */
104 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
105 : } BinaryUpgradeClassOidItem;
106 :
107 : /* sequence types */
108 : typedef enum SeqType
109 : {
110 : SEQTYPE_SMALLINT,
111 : SEQTYPE_INTEGER,
112 : SEQTYPE_BIGINT,
113 : } SeqType;
114 :
115 : static const char *const SeqTypeNames[] =
116 : {
117 : [SEQTYPE_SMALLINT] = "smallint",
118 : [SEQTYPE_INTEGER] = "integer",
119 : [SEQTYPE_BIGINT] = "bigint",
120 : };
121 :
122 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
123 : "array length mismatch");
124 :
125 : typedef struct
126 : {
127 : Oid oid; /* sequence OID */
128 : SeqType seqtype; /* data type of sequence */
129 : bool cycled; /* whether sequence cycles */
130 : int64 minv; /* minimum value */
131 : int64 maxv; /* maximum value */
132 : int64 startv; /* start value */
133 : int64 incby; /* increment value */
134 : int64 cache; /* cache size */
135 : int64 last_value; /* last value of sequence */
136 : bool is_called; /* whether nextval advances before returning */
137 : } SequenceItem;
138 :
139 : typedef enum OidOptions
140 : {
141 : zeroIsError = 1,
142 : zeroAsStar = 2,
143 : zeroAsNone = 4,
144 : } OidOptions;
145 :
146 : /* global decls */
147 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
148 :
149 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
150 :
151 : /* The specified names/patterns should to match at least one entity */
152 : static int strict_names = 0;
153 :
154 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
155 :
156 : /*
157 : * Object inclusion/exclusion lists
158 : *
159 : * The string lists record the patterns given by command-line switches,
160 : * which we then convert to lists of OIDs of matching objects.
161 : */
162 : static SimpleStringList schema_include_patterns = {NULL, NULL};
163 : static SimpleOidList schema_include_oids = {NULL, NULL};
164 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
165 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
166 :
167 : static SimpleStringList table_include_patterns = {NULL, NULL};
168 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
169 : static SimpleOidList table_include_oids = {NULL, NULL};
170 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
171 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
172 : static SimpleOidList table_exclude_oids = {NULL, NULL};
173 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
174 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
175 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
176 :
177 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
178 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
179 :
180 : static SimpleStringList extension_include_patterns = {NULL, NULL};
181 : static SimpleOidList extension_include_oids = {NULL, NULL};
182 :
183 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
184 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
185 :
186 : static const CatalogId nilCatalogId = {0, 0};
187 :
188 : /* override for standard extra_float_digits setting */
189 : static bool have_extra_float_digits = false;
190 : static int extra_float_digits;
191 :
192 : /* sorted table of role names */
193 : static RoleNameItem *rolenames = NULL;
194 : static int nrolenames = 0;
195 :
196 : /* sorted table of comments */
197 : static CommentItem *comments = NULL;
198 : static int ncomments = 0;
199 :
200 : /* sorted table of security labels */
201 : static SecLabelItem *seclabels = NULL;
202 : static int nseclabels = 0;
203 :
204 : /* sorted table of pg_class information for binary upgrade */
205 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
206 : static int nbinaryUpgradeClassOids = 0;
207 :
208 : /* sorted table of sequences */
209 : static SequenceItem *sequences = NULL;
210 : static int nsequences = 0;
211 :
212 : /*
213 : * The default number of rows per INSERT when
214 : * --inserts is specified without --rows-per-insert
215 : */
216 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
217 :
218 : /*
219 : * Maximum number of large objects to group into a single ArchiveEntry.
220 : * At some point we might want to make this user-controllable, but for now
221 : * a hard-wired setting will suffice.
222 : */
223 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
224 :
225 : /*
226 : * Macro for producing quoted, schema-qualified name of a dumpable object.
227 : */
228 : #define fmtQualifiedDumpable(obj) \
229 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
230 : (obj)->dobj.name)
231 :
232 : static void help(const char *progname);
233 : static void setup_connection(Archive *AH,
234 : const char *dumpencoding, const char *dumpsnapshot,
235 : char *use_role);
236 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
237 : static void expand_schema_name_patterns(Archive *fout,
238 : SimpleStringList *patterns,
239 : SimpleOidList *oids,
240 : bool strict_names);
241 : static void expand_extension_name_patterns(Archive *fout,
242 : SimpleStringList *patterns,
243 : SimpleOidList *oids,
244 : bool strict_names);
245 : static void expand_foreign_server_name_patterns(Archive *fout,
246 : SimpleStringList *patterns,
247 : SimpleOidList *oids);
248 : static void expand_table_name_patterns(Archive *fout,
249 : SimpleStringList *patterns,
250 : SimpleOidList *oids,
251 : bool strict_names,
252 : bool with_child_tables);
253 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
254 : const char *pattern);
255 :
256 : static NamespaceInfo *findNamespace(Oid nsoid);
257 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
258 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
259 : static const char *getRoleName(const char *roleoid_str);
260 : static void collectRoleNames(Archive *fout);
261 : static void getAdditionalACLs(Archive *fout);
262 : static void dumpCommentExtended(Archive *fout, const char *type,
263 : const char *name, const char *namespace,
264 : const char *owner, CatalogId catalogId,
265 : int subid, DumpId dumpId,
266 : const char *initdb_comment);
267 : static inline void dumpComment(Archive *fout, const char *type,
268 : const char *name, const char *namespace,
269 : const char *owner, CatalogId catalogId,
270 : int subid, DumpId dumpId);
271 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
272 : static void collectComments(Archive *fout);
273 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
274 : const char *namespace, const char *owner,
275 : CatalogId catalogId, int subid, DumpId dumpId);
276 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
277 : static void collectSecLabels(Archive *fout);
278 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
279 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
280 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
281 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
282 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
283 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
284 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
285 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
286 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
287 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
288 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
289 : PGresult *res);
290 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
291 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
292 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
293 : static void dumpCast(Archive *fout, const CastInfo *cast);
294 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
295 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
296 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
297 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
298 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
299 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
300 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
301 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
302 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
303 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
304 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
305 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
306 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
307 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
308 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
309 : static void collectSequences(Archive *fout);
310 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
311 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
312 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
313 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
314 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
315 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
316 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
317 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
318 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
319 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
320 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
321 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
322 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
323 : static void dumpUserMappings(Archive *fout,
324 : const char *servername, const char *namespace,
325 : const char *owner, CatalogId catalogId, DumpId dumpId);
326 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
327 :
328 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
329 : const char *type, const char *name, const char *subname,
330 : const char *nspname, const char *tag, const char *owner,
331 : const DumpableAcl *dacl);
332 :
333 : static void getDependencies(Archive *fout);
334 : static void BuildArchiveDependencies(Archive *fout);
335 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
336 : DumpId **dependencies, int *nDeps, int *allocDeps);
337 :
338 : static DumpableObject *createBoundaryObjects(void);
339 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
340 : DumpableObject *boundaryObjs);
341 :
342 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
343 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
344 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
345 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
346 : static void buildMatViewRefreshDependencies(Archive *fout);
347 : static void getTableDataFKConstraints(void);
348 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
349 : TableInfo *tbinfo, int j,
350 : int i_notnull_name, int i_notnull_noinherit,
351 : int i_notnull_islocal);
352 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
353 : bool is_agg);
354 : static char *format_function_signature(Archive *fout,
355 : const FuncInfo *finfo, bool honor_quotes);
356 : static char *convertRegProcReference(const char *proc);
357 : static char *getFormattedOperatorName(const char *oproid);
358 : static char *convertTSFunction(Archive *fout, Oid funcOid);
359 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
360 : static void getLOs(Archive *fout);
361 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
362 : static int dumpLOs(Archive *fout, const void *arg);
363 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
364 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
365 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
366 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
367 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
368 : static void dumpDatabase(Archive *fout);
369 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
370 : const char *dbname, Oid dboid);
371 : static void dumpEncoding(Archive *AH);
372 : static void dumpStdStrings(Archive *AH);
373 : static void dumpSearchPath(Archive *AH);
374 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
375 : PQExpBuffer upgrade_buffer,
376 : Oid pg_type_oid,
377 : bool force_array_type,
378 : bool include_multirange_type);
379 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
380 : PQExpBuffer upgrade_buffer,
381 : const TableInfo *tbinfo);
382 : static void collectBinaryUpgradeClassOids(Archive *fout);
383 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
384 : PQExpBuffer upgrade_buffer,
385 : Oid pg_class_oid);
386 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
387 : const DumpableObject *dobj,
388 : const char *objtype,
389 : const char *objname,
390 : const char *objnamespace);
391 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
392 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
393 : static bool nonemptyReloptions(const char *reloptions);
394 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
395 : const char *prefix, Archive *fout);
396 : static char *get_synchronized_snapshot(Archive *fout);
397 : static void set_restrict_relation_kind(Archive *AH, const char *value);
398 : static void setupDumpWorker(Archive *AH);
399 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
400 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
401 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
402 :
403 :
404 : int
405 564 : main(int argc, char **argv)
406 : {
407 : int c;
408 564 : const char *filename = NULL;
409 564 : const char *format = "p";
410 : TableInfo *tblinfo;
411 : int numTables;
412 : DumpableObject **dobjs;
413 : int numObjs;
414 : DumpableObject *boundaryObjs;
415 : int i;
416 : int optindex;
417 : RestoreOptions *ropt;
418 : Archive *fout; /* the script file */
419 564 : bool g_verbose = false;
420 564 : const char *dumpencoding = NULL;
421 564 : const char *dumpsnapshot = NULL;
422 564 : char *use_role = NULL;
423 564 : int numWorkers = 1;
424 564 : int plainText = 0;
425 564 : ArchiveFormat archiveFormat = archUnknown;
426 : ArchiveMode archiveMode;
427 564 : pg_compress_specification compression_spec = {0};
428 564 : char *compression_detail = NULL;
429 564 : char *compression_algorithm_str = "none";
430 564 : char *error_detail = NULL;
431 564 : bool user_compression_defined = false;
432 564 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
433 564 : bool data_only = false;
434 564 : bool schema_only = false;
435 564 : bool statistics_only = false;
436 564 : bool with_data = false;
437 564 : bool with_schema = false;
438 564 : bool with_statistics = false;
439 564 : bool no_data = false;
440 564 : bool no_schema = false;
441 564 : bool no_statistics = false;
442 :
443 : static DumpOptions dopt;
444 :
445 : static struct option long_options[] = {
446 : {"data-only", no_argument, NULL, 'a'},
447 : {"blobs", no_argument, NULL, 'b'},
448 : {"large-objects", no_argument, NULL, 'b'},
449 : {"no-blobs", no_argument, NULL, 'B'},
450 : {"no-large-objects", no_argument, NULL, 'B'},
451 : {"clean", no_argument, NULL, 'c'},
452 : {"create", no_argument, NULL, 'C'},
453 : {"dbname", required_argument, NULL, 'd'},
454 : {"extension", required_argument, NULL, 'e'},
455 : {"file", required_argument, NULL, 'f'},
456 : {"format", required_argument, NULL, 'F'},
457 : {"host", required_argument, NULL, 'h'},
458 : {"jobs", 1, NULL, 'j'},
459 : {"no-reconnect", no_argument, NULL, 'R'},
460 : {"no-owner", no_argument, NULL, 'O'},
461 : {"port", required_argument, NULL, 'p'},
462 : {"schema", required_argument, NULL, 'n'},
463 : {"exclude-schema", required_argument, NULL, 'N'},
464 : {"schema-only", no_argument, NULL, 's'},
465 : {"superuser", required_argument, NULL, 'S'},
466 : {"table", required_argument, NULL, 't'},
467 : {"exclude-table", required_argument, NULL, 'T'},
468 : {"no-password", no_argument, NULL, 'w'},
469 : {"password", no_argument, NULL, 'W'},
470 : {"username", required_argument, NULL, 'U'},
471 : {"verbose", no_argument, NULL, 'v'},
472 : {"no-privileges", no_argument, NULL, 'x'},
473 : {"no-acl", no_argument, NULL, 'x'},
474 : {"compress", required_argument, NULL, 'Z'},
475 : {"encoding", required_argument, NULL, 'E'},
476 : {"help", no_argument, NULL, '?'},
477 : {"version", no_argument, NULL, 'V'},
478 :
479 : /*
480 : * the following options don't have an equivalent short option letter
481 : */
482 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
483 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
484 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
485 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
486 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
487 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
488 : {"exclude-table-data", required_argument, NULL, 4},
489 : {"extra-float-digits", required_argument, NULL, 8},
490 : {"if-exists", no_argument, &dopt.if_exists, 1},
491 : {"inserts", no_argument, NULL, 9},
492 : {"lock-wait-timeout", required_argument, NULL, 2},
493 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
494 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
495 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
496 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
497 : {"role", required_argument, NULL, 3},
498 : {"section", required_argument, NULL, 5},
499 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
500 : {"snapshot", required_argument, NULL, 6},
501 : {"statistics-only", no_argument, NULL, 18},
502 : {"strict-names", no_argument, &strict_names, 1},
503 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
504 : {"no-comments", no_argument, &dopt.no_comments, 1},
505 : {"no-data", no_argument, NULL, 19},
506 : {"no-policies", no_argument, &dopt.no_policies, 1},
507 : {"no-publications", no_argument, &dopt.no_publications, 1},
508 : {"no-schema", no_argument, NULL, 20},
509 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
510 : {"no-statistics", no_argument, NULL, 21},
511 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
512 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
513 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
514 : {"no-sync", no_argument, NULL, 7},
515 : {"with-data", no_argument, NULL, 22},
516 : {"with-schema", no_argument, NULL, 23},
517 : {"with-statistics", no_argument, NULL, 24},
518 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
519 : {"rows-per-insert", required_argument, NULL, 10},
520 : {"include-foreign-data", required_argument, NULL, 11},
521 : {"table-and-children", required_argument, NULL, 12},
522 : {"exclude-table-and-children", required_argument, NULL, 13},
523 : {"exclude-table-data-and-children", required_argument, NULL, 14},
524 : {"sync-method", required_argument, NULL, 15},
525 : {"filter", required_argument, NULL, 16},
526 : {"exclude-extension", required_argument, NULL, 17},
527 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
528 :
529 : {NULL, 0, NULL, 0}
530 : };
531 :
532 564 : pg_logging_init(argv[0]);
533 564 : pg_logging_set_level(PG_LOG_WARNING);
534 564 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
535 :
536 : /*
537 : * Initialize what we need for parallel execution, especially for thread
538 : * support on Windows.
539 : */
540 564 : init_parallel_dump_utils();
541 :
542 564 : progname = get_progname(argv[0]);
543 :
544 564 : if (argc > 1)
545 : {
546 564 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
547 : {
548 2 : help(progname);
549 2 : exit_nicely(0);
550 : }
551 562 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
552 : {
553 120 : puts("pg_dump (PostgreSQL) " PG_VERSION);
554 120 : exit_nicely(0);
555 : }
556 : }
557 :
558 442 : InitDumpOptions(&dopt);
559 :
560 2190 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
561 : long_options, &optindex)) != -1)
562 : {
563 1764 : switch (c)
564 : {
565 18 : case 'a': /* Dump data only */
566 18 : data_only = true;
567 18 : break;
568 :
569 2 : case 'b': /* Dump LOs */
570 2 : dopt.outputLOs = true;
571 2 : break;
572 :
573 4 : case 'B': /* Don't dump LOs */
574 4 : dopt.dontOutputLOs = true;
575 4 : break;
576 :
577 12 : case 'c': /* clean (i.e., drop) schema prior to create */
578 12 : dopt.outputClean = 1;
579 12 : break;
580 :
581 58 : case 'C': /* Create DB */
582 58 : dopt.outputCreateDB = 1;
583 58 : break;
584 :
585 10 : case 'd': /* database name */
586 10 : dopt.cparams.dbname = pg_strdup(optarg);
587 10 : break;
588 :
589 8 : case 'e': /* include extension(s) */
590 8 : simple_string_list_append(&extension_include_patterns, optarg);
591 8 : dopt.include_everything = false;
592 8 : break;
593 :
594 4 : case 'E': /* Dump encoding */
595 4 : dumpencoding = pg_strdup(optarg);
596 4 : break;
597 :
598 352 : case 'f':
599 352 : filename = pg_strdup(optarg);
600 352 : break;
601 :
602 204 : case 'F':
603 204 : format = pg_strdup(optarg);
604 204 : break;
605 :
606 58 : case 'h': /* server host */
607 58 : dopt.cparams.pghost = pg_strdup(optarg);
608 58 : break;
609 :
610 22 : case 'j': /* number of dump jobs */
611 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
612 : PG_MAX_JOBS,
613 : &numWorkers))
614 2 : exit_nicely(1);
615 20 : break;
616 :
617 34 : case 'n': /* include schema(s) */
618 34 : simple_string_list_append(&schema_include_patterns, optarg);
619 34 : dopt.include_everything = false;
620 34 : break;
621 :
622 2 : case 'N': /* exclude schema(s) */
623 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
624 2 : break;
625 :
626 4 : case 'O': /* Don't reconnect to match owner */
627 4 : dopt.outputNoOwner = 1;
628 4 : break;
629 :
630 134 : case 'p': /* server port */
631 134 : dopt.cparams.pgport = pg_strdup(optarg);
632 134 : break;
633 :
634 4 : case 'R':
635 : /* no-op, still accepted for backwards compatibility */
636 4 : break;
637 :
638 14 : case 's': /* dump schema only */
639 14 : schema_only = true;
640 14 : break;
641 :
642 2 : case 'S': /* Username for superuser in plain text output */
643 2 : dopt.outputSuperuser = pg_strdup(optarg);
644 2 : break;
645 :
646 16 : case 't': /* include table(s) */
647 16 : simple_string_list_append(&table_include_patterns, optarg);
648 16 : dopt.include_everything = false;
649 16 : break;
650 :
651 8 : case 'T': /* exclude table(s) */
652 8 : simple_string_list_append(&table_exclude_patterns, optarg);
653 8 : break;
654 :
655 62 : case 'U':
656 62 : dopt.cparams.username = pg_strdup(optarg);
657 62 : break;
658 :
659 12 : case 'v': /* verbose */
660 12 : g_verbose = true;
661 12 : pg_logging_increase_verbosity();
662 12 : break;
663 :
664 2 : case 'w':
665 2 : dopt.cparams.promptPassword = TRI_NO;
666 2 : break;
667 :
668 0 : case 'W':
669 0 : dopt.cparams.promptPassword = TRI_YES;
670 0 : break;
671 :
672 4 : case 'x': /* skip ACL dump */
673 4 : dopt.aclsSkip = true;
674 4 : break;
675 :
676 24 : case 'Z': /* Compression */
677 24 : parse_compress_options(optarg, &compression_algorithm_str,
678 : &compression_detail);
679 24 : user_compression_defined = true;
680 24 : break;
681 :
682 226 : case 0:
683 : /* This covers the long options. */
684 226 : break;
685 :
686 4 : case 2: /* lock-wait-timeout */
687 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
688 4 : break;
689 :
690 6 : case 3: /* SET ROLE */
691 6 : use_role = pg_strdup(optarg);
692 6 : break;
693 :
694 2 : case 4: /* exclude table(s) data */
695 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
696 2 : break;
697 :
698 12 : case 5: /* section */
699 12 : set_dump_section(optarg, &dopt.dumpSections);
700 12 : break;
701 :
702 0 : case 6: /* snapshot */
703 0 : dumpsnapshot = pg_strdup(optarg);
704 0 : break;
705 :
706 268 : case 7: /* no-sync */
707 268 : dosync = false;
708 268 : break;
709 :
710 2 : case 8:
711 2 : have_extra_float_digits = true;
712 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
713 : &extra_float_digits))
714 2 : exit_nicely(1);
715 0 : break;
716 :
717 4 : case 9: /* inserts */
718 :
719 : /*
720 : * dump_inserts also stores --rows-per-insert, careful not to
721 : * overwrite that.
722 : */
723 4 : if (dopt.dump_inserts == 0)
724 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
725 4 : break;
726 :
727 4 : case 10: /* rows per insert */
728 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
729 : &dopt.dump_inserts))
730 2 : exit_nicely(1);
731 2 : break;
732 :
733 8 : case 11: /* include foreign data */
734 8 : simple_string_list_append(&foreign_servers_include_patterns,
735 : optarg);
736 8 : break;
737 :
738 2 : case 12: /* include table(s) and their children */
739 2 : simple_string_list_append(&table_include_patterns_and_children,
740 : optarg);
741 2 : dopt.include_everything = false;
742 2 : break;
743 :
744 2 : case 13: /* exclude table(s) and their children */
745 2 : simple_string_list_append(&table_exclude_patterns_and_children,
746 : optarg);
747 2 : break;
748 :
749 2 : case 14: /* exclude data of table(s) and children */
750 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
751 : optarg);
752 2 : break;
753 :
754 0 : case 15:
755 0 : if (!parse_sync_method(optarg, &sync_method))
756 0 : exit_nicely(1);
757 0 : break;
758 :
759 52 : case 16: /* read object filters from file */
760 52 : read_dump_filters(optarg, &dopt);
761 44 : break;
762 :
763 2 : case 17: /* exclude extension(s) */
764 2 : simple_string_list_append(&extension_exclude_patterns,
765 : optarg);
766 2 : break;
767 :
768 8 : case 18:
769 8 : statistics_only = true;
770 8 : break;
771 :
772 62 : case 19:
773 62 : no_data = true;
774 62 : break;
775 :
776 4 : case 20:
777 4 : no_schema = true;
778 4 : break;
779 :
780 16 : case 21:
781 16 : no_statistics = true;
782 16 : break;
783 :
784 0 : case 22:
785 0 : with_data = true;
786 0 : break;
787 :
788 0 : case 23:
789 0 : with_schema = true;
790 0 : break;
791 :
792 2 : case 24:
793 2 : with_statistics = true;
794 2 : break;
795 :
796 2 : default:
797 : /* getopt_long already emitted a complaint */
798 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
799 2 : exit_nicely(1);
800 : }
801 : }
802 :
803 : /*
804 : * Non-option argument specifies database name as long as it wasn't
805 : * already specified with -d / --dbname
806 : */
807 426 : if (optind < argc && dopt.cparams.dbname == NULL)
808 356 : dopt.cparams.dbname = argv[optind++];
809 :
810 : /* Complain if any arguments remain */
811 426 : if (optind < argc)
812 : {
813 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
814 : argv[optind]);
815 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
816 2 : exit_nicely(1);
817 : }
818 :
819 : /* --column-inserts implies --inserts */
820 424 : if (dopt.column_inserts && dopt.dump_inserts == 0)
821 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
822 :
823 : /* reject conflicting "-only" options */
824 424 : if (data_only && schema_only)
825 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
826 422 : if (schema_only && statistics_only)
827 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
828 420 : if (data_only && statistics_only)
829 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
830 :
831 : /* reject conflicting "-only" and "no-" options */
832 418 : if (data_only && no_data)
833 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
834 418 : if (schema_only && no_schema)
835 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
836 418 : if (statistics_only && no_statistics)
837 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
838 :
839 : /* reject conflicting "with-" and "no-" options */
840 416 : if (with_data && no_data)
841 0 : pg_fatal("options --with-data and --no-data cannot be used together");
842 416 : if (with_schema && no_schema)
843 0 : pg_fatal("options --with-schema and --no-schema cannot be used together");
844 416 : if (with_statistics && no_statistics)
845 0 : pg_fatal("options --with-statistics and --no-statistics cannot be used together");
846 :
847 416 : if (schema_only && foreign_servers_include_patterns.head != NULL)
848 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
849 :
850 414 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
851 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
852 :
853 412 : if (data_only && dopt.outputClean)
854 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
855 :
856 410 : if (dopt.if_exists && !dopt.outputClean)
857 2 : pg_fatal("option --if-exists requires option -c/--clean");
858 :
859 : /*
860 : * Set derivative flags. An "-only" option may be overridden by an
861 : * explicit "with-" option; e.g. "--schema-only --with-statistics" will
862 : * include schema and statistics. Other ambiguous or nonsensical
863 : * combinations, e.g. "--schema-only --no-schema", will have already
864 : * caused an error in one of the checks above.
865 : */
866 408 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
867 816 : (data_only || with_data)) && !no_data;
868 408 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
869 816 : (schema_only || with_schema)) && !no_schema;
870 408 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
871 816 : (statistics_only || with_statistics)) && !no_statistics;
872 :
873 :
874 : /*
875 : * --inserts are already implied above if --column-inserts or
876 : * --rows-per-insert were specified.
877 : */
878 408 : if (dopt.do_nothing && dopt.dump_inserts == 0)
879 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
880 :
881 : /* Identify archive format to emit */
882 406 : archiveFormat = parseArchiveFormat(format, &archiveMode);
883 :
884 : /* archiveFormat specific setup */
885 404 : if (archiveFormat == archNull)
886 306 : plainText = 1;
887 :
888 : /*
889 : * Custom and directory formats are compressed by default with gzip when
890 : * available, not the others. If gzip is not available, no compression is
891 : * done by default.
892 : */
893 404 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
894 92 : !user_compression_defined)
895 : {
896 : #ifdef HAVE_LIBZ
897 82 : compression_algorithm_str = "gzip";
898 : #else
899 : compression_algorithm_str = "none";
900 : #endif
901 : }
902 :
903 : /*
904 : * Compression options
905 : */
906 404 : if (!parse_compress_algorithm(compression_algorithm_str,
907 : &compression_algorithm))
908 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
909 : compression_algorithm_str);
910 :
911 402 : parse_compress_specification(compression_algorithm, compression_detail,
912 : &compression_spec);
913 402 : error_detail = validate_compress_specification(&compression_spec);
914 402 : if (error_detail != NULL)
915 6 : pg_fatal("invalid compression specification: %s",
916 : error_detail);
917 :
918 396 : error_detail = supports_compression(compression_spec);
919 396 : if (error_detail != NULL)
920 0 : pg_fatal("%s", error_detail);
921 :
922 : /*
923 : * Disable support for zstd workers for now - these are based on
924 : * threading, and it's unclear how it interacts with parallel dumps on
925 : * platforms where that relies on threads too (e.g. Windows).
926 : */
927 396 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
928 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
929 : "workers");
930 :
931 : /*
932 : * If emitting an archive format, we always want to emit a DATABASE item,
933 : * in case --create is specified at pg_restore time.
934 : */
935 396 : if (!plainText)
936 98 : dopt.outputCreateDB = 1;
937 :
938 : /* Parallel backup only in the directory archive format so far */
939 396 : if (archiveFormat != archDirectory && numWorkers > 1)
940 2 : pg_fatal("parallel backup only supported by the directory format");
941 :
942 : /* Open the output file */
943 394 : fout = CreateArchive(filename, archiveFormat, compression_spec,
944 : dosync, archiveMode, setupDumpWorker, sync_method);
945 :
946 : /* Make dump options accessible right away */
947 392 : SetArchiveOptions(fout, &dopt, NULL);
948 :
949 : /* Register the cleanup hook */
950 392 : on_exit_close_archive(fout);
951 :
952 : /* Let the archiver know how noisy to be */
953 392 : fout->verbose = g_verbose;
954 :
955 :
956 : /*
957 : * We allow the server to be back to 9.2, and up to any minor release of
958 : * our own major version. (See also version check in pg_dumpall.c.)
959 : */
960 392 : fout->minRemoteVersion = 90200;
961 392 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
962 :
963 392 : fout->numWorkers = numWorkers;
964 :
965 : /*
966 : * Open the database using the Archiver, so it knows about it. Errors mean
967 : * death.
968 : */
969 392 : ConnectDatabase(fout, &dopt.cparams, false);
970 388 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
971 :
972 : /*
973 : * On hot standbys, never try to dump unlogged table data, since it will
974 : * just throw an error.
975 : */
976 388 : if (fout->isStandby)
977 8 : dopt.no_unlogged_table_data = true;
978 :
979 : /*
980 : * Find the last built-in OID, if needed (prior to 8.1)
981 : *
982 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
983 : */
984 388 : g_last_builtin_oid = FirstNormalObjectId - 1;
985 :
986 388 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
987 :
988 : /* Expand schema selection patterns into OID lists */
989 388 : if (schema_include_patterns.head != NULL)
990 : {
991 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
992 : &schema_include_oids,
993 : strict_names);
994 24 : if (schema_include_oids.head == NULL)
995 2 : pg_fatal("no matching schemas were found");
996 : }
997 374 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
998 : &schema_exclude_oids,
999 : false);
1000 : /* non-matching exclusion patterns aren't an error */
1001 :
1002 : /* Expand table selection patterns into OID lists */
1003 374 : expand_table_name_patterns(fout, &table_include_patterns,
1004 : &table_include_oids,
1005 : strict_names, false);
1006 364 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1007 : &table_include_oids,
1008 : strict_names, true);
1009 364 : if ((table_include_patterns.head != NULL ||
1010 342 : table_include_patterns_and_children.head != NULL) &&
1011 26 : table_include_oids.head == NULL)
1012 4 : pg_fatal("no matching tables were found");
1013 :
1014 360 : expand_table_name_patterns(fout, &table_exclude_patterns,
1015 : &table_exclude_oids,
1016 : false, false);
1017 360 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1018 : &table_exclude_oids,
1019 : false, true);
1020 :
1021 360 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1022 : &tabledata_exclude_oids,
1023 : false, false);
1024 360 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1025 : &tabledata_exclude_oids,
1026 : false, true);
1027 :
1028 360 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1029 : &foreign_servers_include_oids);
1030 :
1031 : /* non-matching exclusion patterns aren't an error */
1032 :
1033 : /* Expand extension selection patterns into OID lists */
1034 358 : if (extension_include_patterns.head != NULL)
1035 : {
1036 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1037 : &extension_include_oids,
1038 : strict_names);
1039 10 : if (extension_include_oids.head == NULL)
1040 2 : pg_fatal("no matching extensions were found");
1041 : }
1042 356 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1043 : &extension_exclude_oids,
1044 : false);
1045 : /* non-matching exclusion patterns aren't an error */
1046 :
1047 : /*
1048 : * Dumping LOs is the default for dumps where an inclusion switch is not
1049 : * used (an "include everything" dump). -B can be used to exclude LOs
1050 : * from those dumps. -b can be used to include LOs even when an inclusion
1051 : * switch is used.
1052 : *
1053 : * -s means "schema only" and LOs are data, not schema, so we never
1054 : * include LOs when -s is used.
1055 : */
1056 356 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1057 234 : dopt.outputLOs = true;
1058 :
1059 : /*
1060 : * Collect role names so we can map object owner OIDs to names.
1061 : */
1062 356 : collectRoleNames(fout);
1063 :
1064 : /*
1065 : * Now scan the database and create DumpableObject structs for all the
1066 : * objects we intend to dump.
1067 : */
1068 356 : tblinfo = getSchemaData(fout, &numTables);
1069 :
1070 354 : if (dopt.dumpData)
1071 : {
1072 282 : getTableData(&dopt, tblinfo, numTables, 0);
1073 282 : buildMatViewRefreshDependencies(fout);
1074 282 : if (!dopt.dumpSchema)
1075 14 : getTableDataFKConstraints();
1076 : }
1077 :
1078 354 : if (!dopt.dumpData && dopt.sequence_data)
1079 56 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1080 :
1081 : /*
1082 : * In binary-upgrade mode, we do not have to worry about the actual LO
1083 : * data or the associated metadata that resides in the pg_largeobject and
1084 : * pg_largeobject_metadata tables, respectively.
1085 : *
1086 : * However, we do need to collect LO information as there may be comments
1087 : * or other information on LOs that we do need to dump out.
1088 : */
1089 354 : if (dopt.outputLOs || dopt.binary_upgrade)
1090 296 : getLOs(fout);
1091 :
1092 : /*
1093 : * Collect dependency data to assist in ordering the objects.
1094 : */
1095 354 : getDependencies(fout);
1096 :
1097 : /*
1098 : * Collect ACLs, comments, and security labels, if wanted.
1099 : */
1100 354 : if (!dopt.aclsSkip)
1101 350 : getAdditionalACLs(fout);
1102 354 : if (!dopt.no_comments)
1103 354 : collectComments(fout);
1104 354 : if (!dopt.no_security_labels)
1105 354 : collectSecLabels(fout);
1106 :
1107 : /* For binary upgrade mode, collect required pg_class information. */
1108 354 : if (dopt.binary_upgrade)
1109 62 : collectBinaryUpgradeClassOids(fout);
1110 :
1111 : /* Collect sequence information. */
1112 354 : collectSequences(fout);
1113 :
1114 : /* Lastly, create dummy objects to represent the section boundaries */
1115 354 : boundaryObjs = createBoundaryObjects();
1116 :
1117 : /* Get pointers to all the known DumpableObjects */
1118 354 : getDumpableObjects(&dobjs, &numObjs);
1119 :
1120 : /*
1121 : * Add dummy dependencies to enforce the dump section ordering.
1122 : */
1123 354 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1124 :
1125 : /*
1126 : * Sort the objects into a safe dump order (no forward references).
1127 : *
1128 : * We rely on dependency information to help us determine a safe order, so
1129 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1130 : * ensure that logically identical schemas will dump identically.
1131 : */
1132 354 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1133 :
1134 354 : sortDumpableObjects(dobjs, numObjs,
1135 354 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1136 :
1137 : /*
1138 : * Create archive TOC entries for all the objects to be dumped, in a safe
1139 : * order.
1140 : */
1141 :
1142 : /*
1143 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1144 : */
1145 354 : dumpEncoding(fout);
1146 354 : dumpStdStrings(fout);
1147 354 : dumpSearchPath(fout);
1148 :
1149 : /* The database items are always next, unless we don't want them at all */
1150 354 : if (dopt.outputCreateDB)
1151 154 : dumpDatabase(fout);
1152 :
1153 : /* Now the rearrangeable objects. */
1154 1305290 : for (i = 0; i < numObjs; i++)
1155 1304936 : dumpDumpableObject(fout, dobjs[i]);
1156 :
1157 : /*
1158 : * Set up options info to ensure we dump what we want.
1159 : */
1160 354 : ropt = NewRestoreOptions();
1161 354 : ropt->filename = filename;
1162 :
1163 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1164 354 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1165 354 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1166 354 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1167 354 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1168 354 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1169 354 : ropt->dropSchema = dopt.outputClean;
1170 354 : ropt->dumpData = dopt.dumpData;
1171 354 : ropt->dumpSchema = dopt.dumpSchema;
1172 354 : ropt->dumpStatistics = dopt.dumpStatistics;
1173 354 : ropt->if_exists = dopt.if_exists;
1174 354 : ropt->column_inserts = dopt.column_inserts;
1175 354 : ropt->dumpSections = dopt.dumpSections;
1176 354 : ropt->aclsSkip = dopt.aclsSkip;
1177 354 : ropt->superuser = dopt.outputSuperuser;
1178 354 : ropt->createDB = dopt.outputCreateDB;
1179 354 : ropt->noOwner = dopt.outputNoOwner;
1180 354 : ropt->noTableAm = dopt.outputNoTableAm;
1181 354 : ropt->noTablespace = dopt.outputNoTablespaces;
1182 354 : ropt->disable_triggers = dopt.disable_triggers;
1183 354 : ropt->use_setsessauth = dopt.use_setsessauth;
1184 354 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1185 354 : ropt->dump_inserts = dopt.dump_inserts;
1186 354 : ropt->no_comments = dopt.no_comments;
1187 354 : ropt->no_policies = dopt.no_policies;
1188 354 : ropt->no_publications = dopt.no_publications;
1189 354 : ropt->no_security_labels = dopt.no_security_labels;
1190 354 : ropt->no_subscriptions = dopt.no_subscriptions;
1191 354 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1192 354 : ropt->include_everything = dopt.include_everything;
1193 354 : ropt->enable_row_security = dopt.enable_row_security;
1194 354 : ropt->sequence_data = dopt.sequence_data;
1195 354 : ropt->binary_upgrade = dopt.binary_upgrade;
1196 :
1197 354 : ropt->compression_spec = compression_spec;
1198 :
1199 354 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1200 :
1201 354 : SetArchiveOptions(fout, &dopt, ropt);
1202 :
1203 : /* Mark which entries should be output */
1204 354 : ProcessArchiveRestoreOptions(fout);
1205 :
1206 : /*
1207 : * The archive's TOC entries are now marked as to which ones will actually
1208 : * be output, so we can set up their dependency lists properly. This isn't
1209 : * necessary for plain-text output, though.
1210 : */
1211 354 : if (!plainText)
1212 96 : BuildArchiveDependencies(fout);
1213 :
1214 : /*
1215 : * And finally we can do the actual output.
1216 : *
1217 : * Note: for non-plain-text output formats, the output file is written
1218 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1219 : * right now.
1220 : */
1221 354 : if (plainText)
1222 258 : RestoreArchive(fout);
1223 :
1224 352 : CloseArchive(fout);
1225 :
1226 352 : exit_nicely(0);
1227 : }
1228 :
1229 :
1230 : static void
1231 2 : help(const char *progname)
1232 : {
1233 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1234 2 : printf(_("Usage:\n"));
1235 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1236 :
1237 2 : printf(_("\nGeneral options:\n"));
1238 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1239 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1240 : " plain text (default))\n"));
1241 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1242 2 : printf(_(" -v, --verbose verbose mode\n"));
1243 2 : printf(_(" -V, --version output version information, then exit\n"));
1244 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1245 : " compress as specified\n"));
1246 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1247 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1248 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1249 2 : printf(_(" -?, --help show this help, then exit\n"));
1250 :
1251 2 : printf(_("\nOptions controlling the output content:\n"));
1252 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1253 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1254 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1255 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1256 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1257 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1258 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1259 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1260 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1261 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1262 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1263 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1264 : " plain-text format\n"));
1265 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1266 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1267 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1268 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1269 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1270 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1271 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1272 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1273 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1274 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1275 : " access to)\n"));
1276 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1277 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1278 : " do NOT dump the specified table(s), including\n"
1279 : " child and partition tables\n"));
1280 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1281 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1282 : " do NOT dump data for the specified table(s),\n"
1283 : " including child and partition tables\n"));
1284 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1285 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1286 : " based on expressions in FILENAME\n"));
1287 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1288 2 : printf(_(" --include-foreign-data=PATTERN\n"
1289 : " include data of foreign tables on foreign\n"
1290 : " servers matching PATTERN\n"));
1291 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1292 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1293 2 : printf(_(" --no-comments do not dump comment commands\n"));
1294 2 : printf(_(" --no-data do not dump data\n"));
1295 2 : printf(_(" --no-policies do not dump row security policies\n"));
1296 2 : printf(_(" --no-publications do not dump publications\n"));
1297 2 : printf(_(" --no-schema do not dump schema\n"));
1298 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1299 2 : printf(_(" --no-statistics do not dump statistics\n"));
1300 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1301 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1302 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1303 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1304 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1305 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1306 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1307 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1308 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1309 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1310 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1311 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1312 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1313 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1314 : " match at least one entity each\n"));
1315 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1316 : " child and partition tables\n"));
1317 2 : printf(_(" --use-set-session-authorization\n"
1318 : " use SET SESSION AUTHORIZATION commands instead of\n"
1319 : " ALTER OWNER commands to set ownership\n"));
1320 2 : printf(_(" --with-data dump the data\n"));
1321 2 : printf(_(" --with-schema dump the schema\n"));
1322 2 : printf(_(" --with-statistics dump the statistics\n"));
1323 :
1324 2 : printf(_("\nConnection options:\n"));
1325 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1326 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1327 2 : printf(_(" -p, --port=PORT database server port number\n"));
1328 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1329 2 : printf(_(" -w, --no-password never prompt for password\n"));
1330 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1331 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1332 :
1333 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1334 : "variable value is used.\n\n"));
1335 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1336 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1337 2 : }
1338 :
1339 : static void
1340 420 : setup_connection(Archive *AH, const char *dumpencoding,
1341 : const char *dumpsnapshot, char *use_role)
1342 : {
1343 420 : DumpOptions *dopt = AH->dopt;
1344 420 : PGconn *conn = GetConnection(AH);
1345 : const char *std_strings;
1346 :
1347 420 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1348 :
1349 : /*
1350 : * Set the client encoding if requested.
1351 : */
1352 420 : if (dumpencoding)
1353 : {
1354 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1355 0 : pg_fatal("invalid client encoding \"%s\" specified",
1356 : dumpencoding);
1357 : }
1358 :
1359 : /*
1360 : * Get the active encoding and the standard_conforming_strings setting, so
1361 : * we know how to escape strings.
1362 : */
1363 420 : AH->encoding = PQclientEncoding(conn);
1364 420 : setFmtEncoding(AH->encoding);
1365 :
1366 420 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1367 420 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1368 :
1369 : /*
1370 : * Set the role if requested. In a parallel dump worker, we'll be passed
1371 : * use_role == NULL, but AH->use_role is already set (if user specified it
1372 : * originally) and we should use that.
1373 : */
1374 420 : if (!use_role && AH->use_role)
1375 4 : use_role = AH->use_role;
1376 :
1377 : /* Set the role if requested */
1378 420 : if (use_role)
1379 : {
1380 10 : PQExpBuffer query = createPQExpBuffer();
1381 :
1382 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1383 10 : ExecuteSqlStatement(AH, query->data);
1384 10 : destroyPQExpBuffer(query);
1385 :
1386 : /* save it for possible later use by parallel workers */
1387 10 : if (!AH->use_role)
1388 6 : AH->use_role = pg_strdup(use_role);
1389 : }
1390 :
1391 : /* Set the datestyle to ISO to ensure the dump's portability */
1392 420 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1393 :
1394 : /* Likewise, avoid using sql_standard intervalstyle */
1395 420 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1396 :
1397 : /*
1398 : * Use an explicitly specified extra_float_digits if it has been provided.
1399 : * Otherwise, set extra_float_digits so that we can dump float data
1400 : * exactly (given correctly implemented float I/O code, anyway).
1401 : */
1402 420 : if (have_extra_float_digits)
1403 : {
1404 0 : PQExpBuffer q = createPQExpBuffer();
1405 :
1406 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1407 : extra_float_digits);
1408 0 : ExecuteSqlStatement(AH, q->data);
1409 0 : destroyPQExpBuffer(q);
1410 : }
1411 : else
1412 420 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1413 :
1414 : /*
1415 : * Disable synchronized scanning, to prevent unpredictable changes in row
1416 : * ordering across a dump and reload.
1417 : */
1418 420 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1419 :
1420 : /*
1421 : * Disable timeouts if supported.
1422 : */
1423 420 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1424 420 : if (AH->remoteVersion >= 90300)
1425 420 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1426 420 : if (AH->remoteVersion >= 90600)
1427 420 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1428 420 : if (AH->remoteVersion >= 170000)
1429 420 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1430 :
1431 : /*
1432 : * Quote all identifiers, if requested.
1433 : */
1434 420 : if (quote_all_identifiers)
1435 58 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1436 :
1437 : /*
1438 : * Adjust row-security mode, if supported.
1439 : */
1440 420 : if (AH->remoteVersion >= 90500)
1441 : {
1442 420 : if (dopt->enable_row_security)
1443 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1444 : else
1445 420 : ExecuteSqlStatement(AH, "SET row_security = off");
1446 : }
1447 :
1448 : /*
1449 : * For security reasons, we restrict the expansion of non-system views and
1450 : * access to foreign tables during the pg_dump process. This restriction
1451 : * is adjusted when dumping foreign table data.
1452 : */
1453 420 : set_restrict_relation_kind(AH, "view, foreign-table");
1454 :
1455 : /*
1456 : * Initialize prepared-query state to "nothing prepared". We do this here
1457 : * so that a parallel dump worker will have its own state.
1458 : */
1459 420 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1460 :
1461 : /*
1462 : * Start transaction-snapshot mode transaction to dump consistent data.
1463 : */
1464 420 : ExecuteSqlStatement(AH, "BEGIN");
1465 :
1466 : /*
1467 : * To support the combination of serializable_deferrable with the jobs
1468 : * option we use REPEATABLE READ for the worker connections that are
1469 : * passed a snapshot. As long as the snapshot is acquired in a
1470 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1471 : * REPEATABLE READ transaction provides the appropriate integrity
1472 : * guarantees. This is a kluge, but safe for back-patching.
1473 : */
1474 420 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1475 0 : ExecuteSqlStatement(AH,
1476 : "SET TRANSACTION ISOLATION LEVEL "
1477 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1478 : else
1479 420 : ExecuteSqlStatement(AH,
1480 : "SET TRANSACTION ISOLATION LEVEL "
1481 : "REPEATABLE READ, READ ONLY");
1482 :
1483 : /*
1484 : * If user specified a snapshot to use, select that. In a parallel dump
1485 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1486 : * is already set (if the server can handle it) and we should use that.
1487 : */
1488 420 : if (dumpsnapshot)
1489 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1490 :
1491 420 : if (AH->sync_snapshot_id)
1492 : {
1493 32 : PQExpBuffer query = createPQExpBuffer();
1494 :
1495 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1496 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1497 32 : ExecuteSqlStatement(AH, query->data);
1498 32 : destroyPQExpBuffer(query);
1499 : }
1500 388 : else if (AH->numWorkers > 1)
1501 : {
1502 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1503 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1504 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1505 : }
1506 420 : }
1507 :
1508 : /* Set up connection for a parallel worker process */
1509 : static void
1510 32 : setupDumpWorker(Archive *AH)
1511 : {
1512 : /*
1513 : * We want to re-select all the same values the leader connection is
1514 : * using. We'll have inherited directly-usable values in
1515 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1516 : * inherited encoding value back to a string to pass to setup_connection.
1517 : */
1518 32 : setup_connection(AH,
1519 : pg_encoding_to_char(AH->encoding),
1520 : NULL,
1521 : NULL);
1522 32 : }
1523 :
1524 : static char *
1525 16 : get_synchronized_snapshot(Archive *fout)
1526 : {
1527 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1528 : char *result;
1529 : PGresult *res;
1530 :
1531 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1532 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1533 16 : PQclear(res);
1534 :
1535 16 : return result;
1536 : }
1537 :
1538 : static ArchiveFormat
1539 406 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1540 : {
1541 : ArchiveFormat archiveFormat;
1542 :
1543 406 : *mode = archModeWrite;
1544 :
1545 406 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1546 : {
1547 : /* This is used by pg_dumpall, and is not documented */
1548 86 : archiveFormat = archNull;
1549 86 : *mode = archModeAppend;
1550 : }
1551 320 : else if (pg_strcasecmp(format, "c") == 0)
1552 0 : archiveFormat = archCustom;
1553 320 : else if (pg_strcasecmp(format, "custom") == 0)
1554 72 : archiveFormat = archCustom;
1555 248 : else if (pg_strcasecmp(format, "d") == 0)
1556 0 : archiveFormat = archDirectory;
1557 248 : else if (pg_strcasecmp(format, "directory") == 0)
1558 20 : archiveFormat = archDirectory;
1559 228 : else if (pg_strcasecmp(format, "p") == 0)
1560 214 : archiveFormat = archNull;
1561 14 : else if (pg_strcasecmp(format, "plain") == 0)
1562 6 : archiveFormat = archNull;
1563 8 : else if (pg_strcasecmp(format, "t") == 0)
1564 0 : archiveFormat = archTar;
1565 8 : else if (pg_strcasecmp(format, "tar") == 0)
1566 6 : archiveFormat = archTar;
1567 : else
1568 2 : pg_fatal("invalid output format \"%s\" specified", format);
1569 404 : return archiveFormat;
1570 : }
1571 :
1572 : /*
1573 : * Find the OIDs of all schemas matching the given list of patterns,
1574 : * and append them to the given OID list.
1575 : */
1576 : static void
1577 410 : expand_schema_name_patterns(Archive *fout,
1578 : SimpleStringList *patterns,
1579 : SimpleOidList *oids,
1580 : bool strict_names)
1581 : {
1582 : PQExpBuffer query;
1583 : PGresult *res;
1584 : SimpleStringListCell *cell;
1585 : int i;
1586 :
1587 410 : if (patterns->head == NULL)
1588 368 : return; /* nothing to do */
1589 :
1590 42 : query = createPQExpBuffer();
1591 :
1592 : /*
1593 : * The loop below runs multiple SELECTs might sometimes result in
1594 : * duplicate entries in the OID list, but we don't care.
1595 : */
1596 :
1597 72 : for (cell = patterns->head; cell; cell = cell->next)
1598 : {
1599 : PQExpBufferData dbbuf;
1600 : int dotcnt;
1601 :
1602 42 : appendPQExpBufferStr(query,
1603 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1604 42 : initPQExpBuffer(&dbbuf);
1605 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1606 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1607 : &dotcnt);
1608 42 : if (dotcnt > 1)
1609 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1610 : cell->val);
1611 38 : else if (dotcnt == 1)
1612 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1613 32 : termPQExpBuffer(&dbbuf);
1614 :
1615 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1616 32 : if (strict_names && PQntuples(res) == 0)
1617 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1618 :
1619 58 : for (i = 0; i < PQntuples(res); i++)
1620 : {
1621 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1622 : }
1623 :
1624 30 : PQclear(res);
1625 30 : resetPQExpBuffer(query);
1626 : }
1627 :
1628 30 : destroyPQExpBuffer(query);
1629 : }
1630 :
1631 : /*
1632 : * Find the OIDs of all extensions matching the given list of patterns,
1633 : * and append them to the given OID list.
1634 : */
1635 : static void
1636 366 : expand_extension_name_patterns(Archive *fout,
1637 : SimpleStringList *patterns,
1638 : SimpleOidList *oids,
1639 : bool strict_names)
1640 : {
1641 : PQExpBuffer query;
1642 : PGresult *res;
1643 : SimpleStringListCell *cell;
1644 : int i;
1645 :
1646 366 : if (patterns->head == NULL)
1647 352 : return; /* nothing to do */
1648 :
1649 14 : query = createPQExpBuffer();
1650 :
1651 : /*
1652 : * The loop below runs multiple SELECTs might sometimes result in
1653 : * duplicate entries in the OID list, but we don't care.
1654 : */
1655 28 : for (cell = patterns->head; cell; cell = cell->next)
1656 : {
1657 : int dotcnt;
1658 :
1659 14 : appendPQExpBufferStr(query,
1660 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1661 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1662 : false, NULL, "e.extname", NULL, NULL, NULL,
1663 : &dotcnt);
1664 14 : if (dotcnt > 0)
1665 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1666 : cell->val);
1667 :
1668 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1669 14 : if (strict_names && PQntuples(res) == 0)
1670 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1671 :
1672 26 : for (i = 0; i < PQntuples(res); i++)
1673 : {
1674 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1675 : }
1676 :
1677 14 : PQclear(res);
1678 14 : resetPQExpBuffer(query);
1679 : }
1680 :
1681 14 : destroyPQExpBuffer(query);
1682 : }
1683 :
1684 : /*
1685 : * Find the OIDs of all foreign servers matching the given list of patterns,
1686 : * and append them to the given OID list.
1687 : */
1688 : static void
1689 360 : expand_foreign_server_name_patterns(Archive *fout,
1690 : SimpleStringList *patterns,
1691 : SimpleOidList *oids)
1692 : {
1693 : PQExpBuffer query;
1694 : PGresult *res;
1695 : SimpleStringListCell *cell;
1696 : int i;
1697 :
1698 360 : if (patterns->head == NULL)
1699 354 : return; /* nothing to do */
1700 :
1701 6 : query = createPQExpBuffer();
1702 :
1703 : /*
1704 : * The loop below runs multiple SELECTs might sometimes result in
1705 : * duplicate entries in the OID list, but we don't care.
1706 : */
1707 :
1708 10 : for (cell = patterns->head; cell; cell = cell->next)
1709 : {
1710 : int dotcnt;
1711 :
1712 6 : appendPQExpBufferStr(query,
1713 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1714 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1715 : false, NULL, "s.srvname", NULL, NULL, NULL,
1716 : &dotcnt);
1717 6 : if (dotcnt > 0)
1718 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1719 : cell->val);
1720 :
1721 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1722 6 : if (PQntuples(res) == 0)
1723 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1724 :
1725 8 : for (i = 0; i < PQntuples(res); i++)
1726 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1727 :
1728 4 : PQclear(res);
1729 4 : resetPQExpBuffer(query);
1730 : }
1731 :
1732 4 : destroyPQExpBuffer(query);
1733 : }
1734 :
1735 : /*
1736 : * Find the OIDs of all tables matching the given list of patterns,
1737 : * and append them to the given OID list. See also expand_dbname_patterns()
1738 : * in pg_dumpall.c
1739 : */
1740 : static void
1741 2178 : expand_table_name_patterns(Archive *fout,
1742 : SimpleStringList *patterns, SimpleOidList *oids,
1743 : bool strict_names, bool with_child_tables)
1744 : {
1745 : PQExpBuffer query;
1746 : PGresult *res;
1747 : SimpleStringListCell *cell;
1748 : int i;
1749 :
1750 2178 : if (patterns->head == NULL)
1751 2120 : return; /* nothing to do */
1752 :
1753 58 : query = createPQExpBuffer();
1754 :
1755 : /*
1756 : * this might sometimes result in duplicate entries in the OID list, but
1757 : * we don't care.
1758 : */
1759 :
1760 118 : for (cell = patterns->head; cell; cell = cell->next)
1761 : {
1762 : PQExpBufferData dbbuf;
1763 : int dotcnt;
1764 :
1765 : /*
1766 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1767 : * would be unnecessary given a pg_table_is_visible() variant taking a
1768 : * search_path argument.
1769 : *
1770 : * For with_child_tables, we start with the basic query's results and
1771 : * recursively search the inheritance tree to add child tables.
1772 : */
1773 70 : if (with_child_tables)
1774 : {
1775 12 : appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1776 : }
1777 :
1778 70 : appendPQExpBuffer(query,
1779 : "SELECT c.oid"
1780 : "\nFROM pg_catalog.pg_class c"
1781 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1782 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1783 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1784 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1785 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1786 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1787 : RELKIND_PARTITIONED_TABLE);
1788 70 : initPQExpBuffer(&dbbuf);
1789 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1790 : false, "n.nspname", "c.relname", NULL,
1791 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1792 : &dotcnt);
1793 70 : if (dotcnt > 2)
1794 2 : pg_fatal("improper relation name (too many dotted names): %s",
1795 : cell->val);
1796 68 : else if (dotcnt == 2)
1797 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1798 64 : termPQExpBuffer(&dbbuf);
1799 :
1800 64 : if (with_child_tables)
1801 : {
1802 12 : appendPQExpBuffer(query, "UNION"
1803 : "\nSELECT i.inhrelid"
1804 : "\nFROM partition_tree p"
1805 : "\n JOIN pg_catalog.pg_inherits i"
1806 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1807 : "\n)"
1808 : "\nSELECT relid FROM partition_tree");
1809 : }
1810 :
1811 64 : ExecuteSqlStatement(fout, "RESET search_path");
1812 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1813 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1814 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1815 64 : if (strict_names && PQntuples(res) == 0)
1816 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1817 :
1818 148 : for (i = 0; i < PQntuples(res); i++)
1819 : {
1820 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1821 : }
1822 :
1823 60 : PQclear(res);
1824 60 : resetPQExpBuffer(query);
1825 : }
1826 :
1827 48 : destroyPQExpBuffer(query);
1828 : }
1829 :
1830 : /*
1831 : * Verifies that the connected database name matches the given database name,
1832 : * and if not, dies with an error about the given pattern.
1833 : *
1834 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1835 : */
1836 : static void
1837 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1838 : {
1839 : const char *db;
1840 :
1841 10 : db = PQdb(conn);
1842 10 : if (db == NULL)
1843 0 : pg_fatal("You are currently not connected to a database.");
1844 :
1845 10 : if (strcmp(db, dbname) != 0)
1846 10 : pg_fatal("cross-database references are not implemented: %s",
1847 : pattern);
1848 0 : }
1849 :
1850 : /*
1851 : * checkExtensionMembership
1852 : * Determine whether object is an extension member, and if so,
1853 : * record an appropriate dependency and set the object's dump flag.
1854 : *
1855 : * It's important to call this for each object that could be an extension
1856 : * member. Generally, we integrate this with determining the object's
1857 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1858 : *
1859 : * Returns true if object is an extension member, else false.
1860 : */
1861 : static bool
1862 1103688 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1863 : {
1864 1103688 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1865 :
1866 1103688 : if (ext == NULL)
1867 1102148 : return false;
1868 :
1869 1540 : dobj->ext_member = true;
1870 :
1871 : /* Record dependency so that getDependencies needn't deal with that */
1872 1540 : addObjectDependency(dobj, ext->dobj.dumpId);
1873 :
1874 : /*
1875 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1876 : * dumped. (Any initial ACLs will be removed later, using data from
1877 : * pg_init_privs, so that we'll dump only the delta from the extension's
1878 : * initial setup.)
1879 : *
1880 : * Prior to 9.6, we do not include any extension member components.
1881 : *
1882 : * In binary upgrades, we still dump all components of the members
1883 : * individually, since the idea is to exactly reproduce the database
1884 : * contents rather than replace the extension contents with something
1885 : * different.
1886 : *
1887 : * Note: it might be interesting someday to implement storage and delta
1888 : * dumping of extension members' RLS policies and/or security labels.
1889 : * However there is a pitfall for RLS policies: trying to dump them
1890 : * requires getting a lock on their tables, and the calling user might not
1891 : * have privileges for that. We need no lock to examine a table's ACLs,
1892 : * so the current feature doesn't have a problem of that sort.
1893 : */
1894 1540 : if (fout->dopt->binary_upgrade)
1895 288 : dobj->dump = ext->dobj.dump;
1896 : else
1897 : {
1898 1252 : if (fout->remoteVersion < 90600)
1899 0 : dobj->dump = DUMP_COMPONENT_NONE;
1900 : else
1901 1252 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1902 : }
1903 :
1904 1540 : return true;
1905 : }
1906 :
1907 : /*
1908 : * selectDumpableNamespace: policy-setting subroutine
1909 : * Mark a namespace as to be dumped or not
1910 : */
1911 : static void
1912 2716 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1913 : {
1914 : /*
1915 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1916 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1917 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1918 : */
1919 2716 : nsinfo->create = true;
1920 :
1921 : /*
1922 : * If specific tables are being dumped, do not dump any complete
1923 : * namespaces. If specific namespaces are being dumped, dump just those
1924 : * namespaces. Otherwise, dump all non-system namespaces.
1925 : */
1926 2716 : if (table_include_oids.head != NULL)
1927 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1928 2616 : else if (schema_include_oids.head != NULL)
1929 358 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1930 358 : simple_oid_list_member(&schema_include_oids,
1931 : nsinfo->dobj.catId.oid) ?
1932 358 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1933 2258 : else if (fout->remoteVersion >= 90600 &&
1934 2258 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1935 : {
1936 : /*
1937 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1938 : * they are interesting (and not the original ACLs which were set at
1939 : * initdb time, see pg_init_privs).
1940 : */
1941 312 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1942 : }
1943 1946 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1944 946 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1945 : {
1946 : /* Other system schemas don't get dumped */
1947 1312 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1948 : }
1949 634 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1950 : {
1951 : /*
1952 : * The public schema is a strange beast that sits in a sort of
1953 : * no-mans-land between being a system object and a user object.
1954 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1955 : * a comment and an indication of ownership. If the owner is the
1956 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1957 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1958 : */
1959 304 : nsinfo->create = false;
1960 304 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1961 304 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1962 212 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1963 304 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1964 :
1965 : /*
1966 : * Also, make like it has a comment even if it doesn't; this is so
1967 : * that we'll emit a command to drop the comment, if appropriate.
1968 : * (Without this, we'd not call dumpCommentExtended for it.)
1969 : */
1970 304 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1971 : }
1972 : else
1973 330 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1974 :
1975 : /*
1976 : * In any case, a namespace can be excluded by an exclusion switch
1977 : */
1978 3684 : if (nsinfo->dobj.dump_contains &&
1979 968 : simple_oid_list_member(&schema_exclude_oids,
1980 : nsinfo->dobj.catId.oid))
1981 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1982 :
1983 : /*
1984 : * If the schema belongs to an extension, allow extension membership to
1985 : * override the dump decision for the schema itself. However, this does
1986 : * not change dump_contains, so this won't change what we do with objects
1987 : * within the schema. (If they belong to the extension, they'll get
1988 : * suppressed by it, otherwise not.)
1989 : */
1990 2716 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1991 2716 : }
1992 :
1993 : /*
1994 : * selectDumpableTable: policy-setting subroutine
1995 : * Mark a table as to be dumped or not
1996 : */
1997 : static void
1998 92334 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1999 : {
2000 92334 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2001 450 : return; /* extension membership overrides all else */
2002 :
2003 : /*
2004 : * If specific tables are being dumped, dump just those tables; else, dump
2005 : * according to the parent namespace's dump flag.
2006 : */
2007 91884 : if (table_include_oids.head != NULL)
2008 10128 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2009 : tbinfo->dobj.catId.oid) ?
2010 5064 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2011 : else
2012 86820 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2013 :
2014 : /*
2015 : * In any case, a table can be excluded by an exclusion switch
2016 : */
2017 149760 : if (tbinfo->dobj.dump &&
2018 57876 : simple_oid_list_member(&table_exclude_oids,
2019 : tbinfo->dobj.catId.oid))
2020 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2021 : }
2022 :
2023 : /*
2024 : * selectDumpableType: policy-setting subroutine
2025 : * Mark a type as to be dumped or not
2026 : *
2027 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2028 : * special type code to facilitate sorting into the desired order. (We don't
2029 : * want to consider those to be ordinary types because that would bring tables
2030 : * up into the datatype part of the dump order.) We still set the object's
2031 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2032 : * need it so that casts involving such types will be dumped correctly -- see
2033 : * dumpCast. This means the flag should be set the same as for the underlying
2034 : * object (the table or base type).
2035 : */
2036 : static void
2037 253526 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2038 : {
2039 : /* skip complex types, except for standalone composite types */
2040 253526 : if (OidIsValid(tyinfo->typrelid) &&
2041 90886 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2042 : {
2043 90514 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2044 :
2045 90514 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2046 90514 : if (tytable != NULL)
2047 90514 : tyinfo->dobj.dump = tytable->dobj.dump;
2048 : else
2049 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2050 90514 : return;
2051 : }
2052 :
2053 : /* skip auto-generated array and multirange types */
2054 163012 : if (tyinfo->isArray || tyinfo->isMultirange)
2055 : {
2056 123978 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2057 :
2058 : /*
2059 : * Fall through to set the dump flag; we assume that the subsequent
2060 : * rules will do the same thing as they would for the array's base
2061 : * type or multirange's range type. (We cannot reliably look up the
2062 : * base type here, since getTypes may not have processed it yet.)
2063 : */
2064 : }
2065 :
2066 163012 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2067 300 : return; /* extension membership overrides all else */
2068 :
2069 : /* Dump based on if the contents of the namespace are being dumped */
2070 162712 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2071 : }
2072 :
2073 : /*
2074 : * selectDumpableDefaultACL: policy-setting subroutine
2075 : * Mark a default ACL as to be dumped or not
2076 : *
2077 : * For per-schema default ACLs, dump if the schema is to be dumped.
2078 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2079 : * and aclsSkip are checked separately.
2080 : */
2081 : static void
2082 392 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2083 : {
2084 : /* Default ACLs can't be extension members */
2085 :
2086 392 : if (dinfo->dobj.namespace)
2087 : /* default ACLs are considered part of the namespace */
2088 196 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2089 : else
2090 196 : dinfo->dobj.dump = dopt->include_everything ?
2091 196 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2092 392 : }
2093 :
2094 : /*
2095 : * selectDumpableCast: policy-setting subroutine
2096 : * Mark a cast as to be dumped or not
2097 : *
2098 : * Casts do not belong to any particular namespace (since they haven't got
2099 : * names), nor do they have identifiable owners. To distinguish user-defined
2100 : * casts from built-in ones, we must resort to checking whether the cast's
2101 : * OID is in the range reserved for initdb.
2102 : */
2103 : static void
2104 81248 : selectDumpableCast(CastInfo *cast, Archive *fout)
2105 : {
2106 81248 : if (checkExtensionMembership(&cast->dobj, fout))
2107 0 : return; /* extension membership overrides all else */
2108 :
2109 : /*
2110 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2111 : * support ACLs currently.
2112 : */
2113 81248 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2114 81066 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2115 : else
2116 182 : cast->dobj.dump = fout->dopt->include_everything ?
2117 182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2118 : }
2119 :
2120 : /*
2121 : * selectDumpableProcLang: policy-setting subroutine
2122 : * Mark a procedural language as to be dumped or not
2123 : *
2124 : * Procedural languages do not belong to any particular namespace. To
2125 : * identify built-in languages, we must resort to checking whether the
2126 : * language's OID is in the range reserved for initdb.
2127 : */
2128 : static void
2129 452 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2130 : {
2131 452 : if (checkExtensionMembership(&plang->dobj, fout))
2132 354 : return; /* extension membership overrides all else */
2133 :
2134 : /*
2135 : * Only include procedural languages when we are dumping everything.
2136 : *
2137 : * For from-initdb procedural languages, only include ACLs, as we do for
2138 : * the pg_catalog namespace. We need this because procedural languages do
2139 : * not live in any namespace.
2140 : */
2141 98 : if (!fout->dopt->include_everything)
2142 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2143 : else
2144 : {
2145 82 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2146 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2147 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2148 : else
2149 82 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2150 : }
2151 : }
2152 :
2153 : /*
2154 : * selectDumpableAccessMethod: policy-setting subroutine
2155 : * Mark an access method as to be dumped or not
2156 : *
2157 : * Access methods do not belong to any particular namespace. To identify
2158 : * built-in access methods, we must resort to checking whether the
2159 : * method's OID is in the range reserved for initdb.
2160 : */
2161 : static void
2162 2738 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2163 : {
2164 2738 : if (checkExtensionMembership(&method->dobj, fout))
2165 50 : return; /* extension membership overrides all else */
2166 :
2167 : /*
2168 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2169 : * they do not support ACLs currently.
2170 : */
2171 2688 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2172 2478 : method->dobj.dump = DUMP_COMPONENT_NONE;
2173 : else
2174 210 : method->dobj.dump = fout->dopt->include_everything ?
2175 210 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2176 : }
2177 :
2178 : /*
2179 : * selectDumpableExtension: policy-setting subroutine
2180 : * Mark an extension as to be dumped or not
2181 : *
2182 : * Built-in extensions should be skipped except for checking ACLs, since we
2183 : * assume those will already be installed in the target database. We identify
2184 : * such extensions by their having OIDs in the range reserved for initdb.
2185 : * We dump all user-added extensions by default. No extensions are dumped
2186 : * if include_everything is false (i.e., a --schema or --table switch was
2187 : * given), except if --extension specifies a list of extensions to dump.
2188 : */
2189 : static void
2190 406 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2191 : {
2192 : /*
2193 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2194 : * change permissions on their member objects, if they wish to, and have
2195 : * those changes preserved.
2196 : */
2197 406 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2198 356 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2199 : else
2200 : {
2201 : /* check if there is a list of extensions to dump */
2202 50 : if (extension_include_oids.head != NULL)
2203 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2204 8 : simple_oid_list_member(&extension_include_oids,
2205 : extinfo->dobj.catId.oid) ?
2206 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2207 : else
2208 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2209 42 : dopt->include_everything ?
2210 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2211 :
2212 : /* check that the extension is not explicitly excluded */
2213 92 : if (extinfo->dobj.dump &&
2214 42 : simple_oid_list_member(&extension_exclude_oids,
2215 : extinfo->dobj.catId.oid))
2216 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2217 : }
2218 406 : }
2219 :
2220 : /*
2221 : * selectDumpablePublicationObject: policy-setting subroutine
2222 : * Mark a publication object as to be dumped or not
2223 : *
2224 : * A publication can have schemas and tables which have schemas, but those are
2225 : * ignored in decision making, because publications are only dumped when we are
2226 : * dumping everything.
2227 : */
2228 : static void
2229 882 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2230 : {
2231 882 : if (checkExtensionMembership(dobj, fout))
2232 0 : return; /* extension membership overrides all else */
2233 :
2234 882 : dobj->dump = fout->dopt->include_everything ?
2235 882 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2236 : }
2237 :
2238 : /*
2239 : * selectDumpableStatisticsObject: policy-setting subroutine
2240 : * Mark an extended statistics object as to be dumped or not
2241 : *
2242 : * We dump an extended statistics object if the schema it's in and the table
2243 : * it's for are being dumped. (This'll need more thought if statistics
2244 : * objects ever support cross-table stats.)
2245 : */
2246 : static void
2247 350 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2248 : {
2249 350 : if (checkExtensionMembership(&sobj->dobj, fout))
2250 0 : return; /* extension membership overrides all else */
2251 :
2252 350 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2253 350 : if (sobj->stattable == NULL ||
2254 350 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2255 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2256 : }
2257 :
2258 : /*
2259 : * selectDumpableObject: policy-setting subroutine
2260 : * Mark a generic dumpable object as to be dumped or not
2261 : *
2262 : * Use this only for object types without a special-case routine above.
2263 : */
2264 : static void
2265 759956 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2266 : {
2267 759956 : if (checkExtensionMembership(dobj, fout))
2268 336 : return; /* extension membership overrides all else */
2269 :
2270 : /*
2271 : * Default policy is to dump if parent namespace is dumpable, or for
2272 : * non-namespace-associated items, dump if we're dumping "everything".
2273 : */
2274 759620 : if (dobj->namespace)
2275 758306 : dobj->dump = dobj->namespace->dobj.dump_contains;
2276 : else
2277 1314 : dobj->dump = fout->dopt->include_everything ?
2278 1314 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2279 : }
2280 :
2281 : /*
2282 : * Dump a table's contents for loading using the COPY command
2283 : * - this routine is called by the Archiver when it wants the table
2284 : * to be dumped.
2285 : */
2286 : static int
2287 7632 : dumpTableData_copy(Archive *fout, const void *dcontext)
2288 : {
2289 7632 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2290 7632 : TableInfo *tbinfo = tdinfo->tdtable;
2291 7632 : const char *classname = tbinfo->dobj.name;
2292 7632 : PQExpBuffer q = createPQExpBuffer();
2293 :
2294 : /*
2295 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2296 : * which uses it already.
2297 : */
2298 7632 : PQExpBuffer clistBuf = createPQExpBuffer();
2299 7632 : PGconn *conn = GetConnection(fout);
2300 : PGresult *res;
2301 : int ret;
2302 : char *copybuf;
2303 : const char *column_list;
2304 :
2305 7632 : pg_log_info("dumping contents of table \"%s.%s\"",
2306 : tbinfo->dobj.namespace->dobj.name, classname);
2307 :
2308 : /*
2309 : * Specify the column list explicitly so that we have no possibility of
2310 : * retrieving data in the wrong column order. (The default column
2311 : * ordering of COPY will not be what we want in certain corner cases
2312 : * involving ADD COLUMN and inheritance.)
2313 : */
2314 7632 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2315 :
2316 : /*
2317 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2318 : * a filter condition was specified. For other cases a simple COPY
2319 : * suffices.
2320 : */
2321 7632 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2322 : {
2323 : /* Temporary allows to access to foreign tables to dump data */
2324 2 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2325 2 : set_restrict_relation_kind(fout, "view");
2326 :
2327 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2328 : /* klugery to get rid of parens in column list */
2329 2 : if (strlen(column_list) > 2)
2330 : {
2331 2 : appendPQExpBufferStr(q, column_list + 1);
2332 2 : q->data[q->len - 1] = ' ';
2333 : }
2334 : else
2335 0 : appendPQExpBufferStr(q, "* ");
2336 :
2337 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2338 2 : fmtQualifiedDumpable(tbinfo),
2339 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2340 : }
2341 : else
2342 : {
2343 7630 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2344 7630 : fmtQualifiedDumpable(tbinfo),
2345 : column_list);
2346 : }
2347 7632 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2348 7630 : PQclear(res);
2349 7630 : destroyPQExpBuffer(clistBuf);
2350 :
2351 : for (;;)
2352 : {
2353 3615628 : ret = PQgetCopyData(conn, ©buf, 0);
2354 :
2355 3615628 : if (ret < 0)
2356 7630 : break; /* done or error */
2357 :
2358 3607998 : if (copybuf)
2359 : {
2360 3607998 : WriteData(fout, copybuf, ret);
2361 3607998 : PQfreemem(copybuf);
2362 : }
2363 :
2364 : /* ----------
2365 : * THROTTLE:
2366 : *
2367 : * There was considerable discussion in late July, 2000 regarding
2368 : * slowing down pg_dump when backing up large tables. Users with both
2369 : * slow & fast (multi-processor) machines experienced performance
2370 : * degradation when doing a backup.
2371 : *
2372 : * Initial attempts based on sleeping for a number of ms for each ms
2373 : * of work were deemed too complex, then a simple 'sleep in each loop'
2374 : * implementation was suggested. The latter failed because the loop
2375 : * was too tight. Finally, the following was implemented:
2376 : *
2377 : * If throttle is non-zero, then
2378 : * See how long since the last sleep.
2379 : * Work out how long to sleep (based on ratio).
2380 : * If sleep is more than 100ms, then
2381 : * sleep
2382 : * reset timer
2383 : * EndIf
2384 : * EndIf
2385 : *
2386 : * where the throttle value was the number of ms to sleep per ms of
2387 : * work. The calculation was done in each loop.
2388 : *
2389 : * Most of the hard work is done in the backend, and this solution
2390 : * still did not work particularly well: on slow machines, the ratio
2391 : * was 50:1, and on medium paced machines, 1:1, and on fast
2392 : * multi-processor machines, it had little or no effect, for reasons
2393 : * that were unclear.
2394 : *
2395 : * Further discussion ensued, and the proposal was dropped.
2396 : *
2397 : * For those people who want this feature, it can be implemented using
2398 : * gettimeofday in each loop, calculating the time since last sleep,
2399 : * multiplying that by the sleep ratio, then if the result is more
2400 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2401 : * function to sleep for a subsecond period ie.
2402 : *
2403 : * select(0, NULL, NULL, NULL, &tvi);
2404 : *
2405 : * This will return after the interval specified in the structure tvi.
2406 : * Finally, call gettimeofday again to save the 'last sleep time'.
2407 : * ----------
2408 : */
2409 : }
2410 7630 : archprintf(fout, "\\.\n\n\n");
2411 :
2412 7630 : if (ret == -2)
2413 : {
2414 : /* copy data transfer failed */
2415 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2416 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2417 0 : pg_log_error_detail("Command was: %s", q->data);
2418 0 : exit_nicely(1);
2419 : }
2420 :
2421 : /* Check command status and return to normal libpq state */
2422 7630 : res = PQgetResult(conn);
2423 7630 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2424 : {
2425 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2426 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2427 0 : pg_log_error_detail("Command was: %s", q->data);
2428 0 : exit_nicely(1);
2429 : }
2430 7630 : PQclear(res);
2431 :
2432 : /* Do this to ensure we've pumped libpq back to idle state */
2433 7630 : if (PQgetResult(conn) != NULL)
2434 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2435 : classname);
2436 :
2437 7630 : destroyPQExpBuffer(q);
2438 :
2439 : /* Revert back the setting */
2440 7630 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2441 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2442 :
2443 7630 : return 1;
2444 : }
2445 :
2446 : /*
2447 : * Dump table data using INSERT commands.
2448 : *
2449 : * Caution: when we restore from an archive file direct to database, the
2450 : * INSERT commands emitted by this function have to be parsed by
2451 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2452 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2453 : */
2454 : static int
2455 142 : dumpTableData_insert(Archive *fout, const void *dcontext)
2456 : {
2457 142 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2458 142 : TableInfo *tbinfo = tdinfo->tdtable;
2459 142 : DumpOptions *dopt = fout->dopt;
2460 142 : PQExpBuffer q = createPQExpBuffer();
2461 142 : PQExpBuffer insertStmt = NULL;
2462 : char *attgenerated;
2463 : PGresult *res;
2464 : int nfields,
2465 : i;
2466 142 : int rows_per_statement = dopt->dump_inserts;
2467 142 : int rows_this_statement = 0;
2468 :
2469 : /* Temporary allows to access to foreign tables to dump data */
2470 142 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2471 0 : set_restrict_relation_kind(fout, "view");
2472 :
2473 : /*
2474 : * If we're going to emit INSERTs with column names, the most efficient
2475 : * way to deal with generated columns is to exclude them entirely. For
2476 : * INSERTs without column names, we have to emit DEFAULT rather than the
2477 : * actual column value --- but we can save a few cycles by fetching nulls
2478 : * rather than the uninteresting-to-us value.
2479 : */
2480 142 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2481 142 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2482 142 : nfields = 0;
2483 466 : for (i = 0; i < tbinfo->numatts; i++)
2484 : {
2485 324 : if (tbinfo->attisdropped[i])
2486 4 : continue;
2487 320 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2488 16 : continue;
2489 304 : if (nfields > 0)
2490 176 : appendPQExpBufferStr(q, ", ");
2491 304 : if (tbinfo->attgenerated[i])
2492 16 : appendPQExpBufferStr(q, "NULL");
2493 : else
2494 288 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2495 304 : attgenerated[nfields] = tbinfo->attgenerated[i];
2496 304 : nfields++;
2497 : }
2498 : /* Servers before 9.4 will complain about zero-column SELECT */
2499 142 : if (nfields == 0)
2500 14 : appendPQExpBufferStr(q, "NULL");
2501 142 : appendPQExpBuffer(q, " FROM ONLY %s",
2502 142 : fmtQualifiedDumpable(tbinfo));
2503 142 : if (tdinfo->filtercond)
2504 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2505 :
2506 142 : ExecuteSqlStatement(fout, q->data);
2507 :
2508 : while (1)
2509 : {
2510 246 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2511 : PGRES_TUPLES_OK);
2512 :
2513 : /* cross-check field count, allowing for dummy NULL if any */
2514 246 : if (nfields != PQnfields(res) &&
2515 20 : !(nfields == 0 && PQnfields(res) == 1))
2516 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2517 : tbinfo->dobj.name);
2518 :
2519 : /*
2520 : * First time through, we build as much of the INSERT statement as
2521 : * possible in "insertStmt", which we can then just print for each
2522 : * statement. If the table happens to have zero dumpable columns then
2523 : * this will be a complete statement, otherwise it will end in
2524 : * "VALUES" and be ready to have the row's column values printed.
2525 : */
2526 246 : if (insertStmt == NULL)
2527 : {
2528 : TableInfo *targettab;
2529 :
2530 142 : insertStmt = createPQExpBuffer();
2531 :
2532 : /*
2533 : * When load-via-partition-root is set or forced, get the root
2534 : * table name for the partition table, so that we can reload data
2535 : * through the root table.
2536 : */
2537 142 : if (tbinfo->ispartition &&
2538 80 : (dopt->load_via_partition_root ||
2539 40 : forcePartitionRootLoad(tbinfo)))
2540 6 : targettab = getRootTableInfo(tbinfo);
2541 : else
2542 136 : targettab = tbinfo;
2543 :
2544 142 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2545 142 : fmtQualifiedDumpable(targettab));
2546 :
2547 : /* corner case for zero-column table */
2548 142 : if (nfields == 0)
2549 : {
2550 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2551 : }
2552 : else
2553 : {
2554 : /* append the list of column names if required */
2555 128 : if (dopt->column_inserts)
2556 : {
2557 56 : appendPQExpBufferChar(insertStmt, '(');
2558 182 : for (int field = 0; field < nfields; field++)
2559 : {
2560 126 : if (field > 0)
2561 70 : appendPQExpBufferStr(insertStmt, ", ");
2562 126 : appendPQExpBufferStr(insertStmt,
2563 126 : fmtId(PQfname(res, field)));
2564 : }
2565 56 : appendPQExpBufferStr(insertStmt, ") ");
2566 : }
2567 :
2568 128 : if (tbinfo->needs_override)
2569 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2570 :
2571 128 : appendPQExpBufferStr(insertStmt, "VALUES");
2572 : }
2573 : }
2574 :
2575 6788 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2576 : {
2577 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2578 6542 : if (rows_this_statement == 0)
2579 6530 : archputs(insertStmt->data, fout);
2580 :
2581 : /*
2582 : * If it is zero-column table then we've already written the
2583 : * complete statement, which will mean we've disobeyed
2584 : * --rows-per-insert when it's set greater than 1. We do support
2585 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2586 : * UNION ALL ... but that's non-standard so we should avoid it
2587 : * given that using INSERTs is mostly only ever needed for
2588 : * cross-database exports.
2589 : */
2590 6542 : if (nfields == 0)
2591 12 : continue;
2592 :
2593 : /* Emit a row heading */
2594 6530 : if (rows_per_statement == 1)
2595 6512 : archputs(" (", fout);
2596 18 : else if (rows_this_statement > 0)
2597 12 : archputs(",\n\t(", fout);
2598 : else
2599 6 : archputs("\n\t(", fout);
2600 :
2601 19698 : for (int field = 0; field < nfields; field++)
2602 : {
2603 13168 : if (field > 0)
2604 6638 : archputs(", ", fout);
2605 13168 : if (attgenerated[field])
2606 : {
2607 4 : archputs("DEFAULT", fout);
2608 4 : continue;
2609 : }
2610 13164 : if (PQgetisnull(res, tuple, field))
2611 : {
2612 166 : archputs("NULL", fout);
2613 166 : continue;
2614 : }
2615 :
2616 : /* XXX This code is partially duplicated in ruleutils.c */
2617 12998 : switch (PQftype(res, field))
2618 : {
2619 8938 : case INT2OID:
2620 : case INT4OID:
2621 : case INT8OID:
2622 : case OIDOID:
2623 : case FLOAT4OID:
2624 : case FLOAT8OID:
2625 : case NUMERICOID:
2626 : {
2627 : /*
2628 : * These types are printed without quotes unless
2629 : * they contain values that aren't accepted by the
2630 : * scanner unquoted (e.g., 'NaN'). Note that
2631 : * strtod() and friends might accept NaN, so we
2632 : * can't use that to test.
2633 : *
2634 : * In reality we only need to defend against
2635 : * infinity and NaN, so we need not get too crazy
2636 : * about pattern matching here.
2637 : */
2638 8938 : const char *s = PQgetvalue(res, tuple, field);
2639 :
2640 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2641 8934 : archputs(s, fout);
2642 : else
2643 4 : archprintf(fout, "'%s'", s);
2644 : }
2645 8938 : break;
2646 :
2647 4 : case BITOID:
2648 : case VARBITOID:
2649 4 : archprintf(fout, "B'%s'",
2650 : PQgetvalue(res, tuple, field));
2651 4 : break;
2652 :
2653 8 : case BOOLOID:
2654 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2655 4 : archputs("true", fout);
2656 : else
2657 4 : archputs("false", fout);
2658 8 : break;
2659 :
2660 4048 : default:
2661 : /* All other types are printed as string literals. */
2662 4048 : resetPQExpBuffer(q);
2663 4048 : appendStringLiteralAH(q,
2664 : PQgetvalue(res, tuple, field),
2665 : fout);
2666 4048 : archputs(q->data, fout);
2667 4048 : break;
2668 : }
2669 : }
2670 :
2671 : /* Terminate the row ... */
2672 6530 : archputs(")", fout);
2673 :
2674 : /* ... and the statement, if the target no. of rows is reached */
2675 6530 : if (++rows_this_statement >= rows_per_statement)
2676 : {
2677 6516 : if (dopt->do_nothing)
2678 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2679 : else
2680 6516 : archputs(";\n", fout);
2681 : /* Reset the row counter */
2682 6516 : rows_this_statement = 0;
2683 : }
2684 : }
2685 :
2686 246 : if (PQntuples(res) <= 0)
2687 : {
2688 142 : PQclear(res);
2689 142 : break;
2690 : }
2691 104 : PQclear(res);
2692 : }
2693 :
2694 : /* Terminate any statements that didn't make the row count. */
2695 142 : if (rows_this_statement > 0)
2696 : {
2697 2 : if (dopt->do_nothing)
2698 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2699 : else
2700 2 : archputs(";\n", fout);
2701 : }
2702 :
2703 142 : archputs("\n\n", fout);
2704 :
2705 142 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2706 :
2707 142 : destroyPQExpBuffer(q);
2708 142 : if (insertStmt != NULL)
2709 142 : destroyPQExpBuffer(insertStmt);
2710 142 : free(attgenerated);
2711 :
2712 : /* Revert back the setting */
2713 142 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2714 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2715 :
2716 142 : return 1;
2717 : }
2718 :
2719 : /*
2720 : * getRootTableInfo:
2721 : * get the root TableInfo for the given partition table.
2722 : */
2723 : static TableInfo *
2724 18 : getRootTableInfo(const TableInfo *tbinfo)
2725 : {
2726 : TableInfo *parentTbinfo;
2727 :
2728 : Assert(tbinfo->ispartition);
2729 : Assert(tbinfo->numParents == 1);
2730 :
2731 18 : parentTbinfo = tbinfo->parents[0];
2732 18 : while (parentTbinfo->ispartition)
2733 : {
2734 : Assert(parentTbinfo->numParents == 1);
2735 0 : parentTbinfo = parentTbinfo->parents[0];
2736 : }
2737 :
2738 18 : return parentTbinfo;
2739 : }
2740 :
2741 : /*
2742 : * forcePartitionRootLoad
2743 : * Check if we must force load_via_partition_root for this partition.
2744 : *
2745 : * This is required if any level of ancestral partitioned table has an
2746 : * unsafe partitioning scheme.
2747 : */
2748 : static bool
2749 1966 : forcePartitionRootLoad(const TableInfo *tbinfo)
2750 : {
2751 : TableInfo *parentTbinfo;
2752 :
2753 : Assert(tbinfo->ispartition);
2754 : Assert(tbinfo->numParents == 1);
2755 :
2756 1966 : parentTbinfo = tbinfo->parents[0];
2757 1966 : if (parentTbinfo->unsafe_partitions)
2758 18 : return true;
2759 2380 : while (parentTbinfo->ispartition)
2760 : {
2761 : Assert(parentTbinfo->numParents == 1);
2762 432 : parentTbinfo = parentTbinfo->parents[0];
2763 432 : if (parentTbinfo->unsafe_partitions)
2764 0 : return true;
2765 : }
2766 :
2767 1948 : return false;
2768 : }
2769 :
2770 : /*
2771 : * dumpTableData -
2772 : * dump the contents of a single table
2773 : *
2774 : * Actually, this just makes an ArchiveEntry for the table contents.
2775 : */
2776 : static void
2777 7914 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2778 : {
2779 7914 : DumpOptions *dopt = fout->dopt;
2780 7914 : TableInfo *tbinfo = tdinfo->tdtable;
2781 7914 : PQExpBuffer copyBuf = createPQExpBuffer();
2782 7914 : PQExpBuffer clistBuf = createPQExpBuffer();
2783 : DataDumperPtr dumpFn;
2784 7914 : char *tdDefn = NULL;
2785 : char *copyStmt;
2786 : const char *copyFrom;
2787 :
2788 : /* We had better have loaded per-column details about this table */
2789 : Assert(tbinfo->interesting);
2790 :
2791 : /*
2792 : * When load-via-partition-root is set or forced, get the root table name
2793 : * for the partition table, so that we can reload data through the root
2794 : * table. Then construct a comment to be inserted into the TOC entry's
2795 : * defn field, so that such cases can be identified reliably.
2796 : */
2797 7914 : if (tbinfo->ispartition &&
2798 3852 : (dopt->load_via_partition_root ||
2799 1926 : forcePartitionRootLoad(tbinfo)))
2800 12 : {
2801 : TableInfo *parentTbinfo;
2802 :
2803 12 : parentTbinfo = getRootTableInfo(tbinfo);
2804 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2805 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2806 : copyFrom);
2807 12 : tdDefn = pg_strdup(copyBuf->data);
2808 : }
2809 : else
2810 7902 : copyFrom = fmtQualifiedDumpable(tbinfo);
2811 :
2812 7914 : if (dopt->dump_inserts == 0)
2813 : {
2814 : /* Dump/restore using COPY */
2815 7772 : dumpFn = dumpTableData_copy;
2816 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2817 7772 : printfPQExpBuffer(copyBuf, "COPY %s ",
2818 : copyFrom);
2819 7772 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2820 : fmtCopyColumnList(tbinfo, clistBuf));
2821 7772 : copyStmt = copyBuf->data;
2822 : }
2823 : else
2824 : {
2825 : /* Restore using INSERT */
2826 142 : dumpFn = dumpTableData_insert;
2827 142 : copyStmt = NULL;
2828 : }
2829 :
2830 : /*
2831 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2832 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2833 : * See comments for BuildArchiveDependencies.
2834 : */
2835 7914 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2836 : {
2837 : TocEntry *te;
2838 :
2839 7914 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2840 7914 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2841 : .namespace = tbinfo->dobj.namespace->dobj.name,
2842 : .owner = tbinfo->rolname,
2843 : .description = "TABLE DATA",
2844 : .section = SECTION_DATA,
2845 : .createStmt = tdDefn,
2846 : .copyStmt = copyStmt,
2847 : .deps = &(tbinfo->dobj.dumpId),
2848 : .nDeps = 1,
2849 : .dumpFn = dumpFn,
2850 : .dumpArg = tdinfo));
2851 :
2852 : /*
2853 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2854 : * and want to order dump jobs by table size. We choose to measure
2855 : * dataLength in table pages (including TOAST pages) during dump, so
2856 : * no scaling is needed.
2857 : *
2858 : * However, relpages is declared as "integer" in pg_class, and hence
2859 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2860 : * Cast so that we get the right interpretation of table sizes
2861 : * exceeding INT_MAX pages.
2862 : */
2863 7914 : te->dataLength = (BlockNumber) tbinfo->relpages;
2864 7914 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2865 :
2866 : /*
2867 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2868 : * and instead we'd better worry about integer overflow. Clamp to
2869 : * INT_MAX if the correct result exceeds that.
2870 : */
2871 : if (sizeof(te->dataLength) == 4 &&
2872 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2873 : te->dataLength < 0))
2874 : te->dataLength = INT_MAX;
2875 : }
2876 :
2877 7914 : destroyPQExpBuffer(copyBuf);
2878 7914 : destroyPQExpBuffer(clistBuf);
2879 7914 : }
2880 :
2881 : /*
2882 : * refreshMatViewData -
2883 : * load or refresh the contents of a single materialized view
2884 : *
2885 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2886 : * statement.
2887 : */
2888 : static void
2889 804 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2890 : {
2891 804 : TableInfo *tbinfo = tdinfo->tdtable;
2892 : PQExpBuffer q;
2893 :
2894 : /* If the materialized view is not flagged as populated, skip this. */
2895 804 : if (!tbinfo->relispopulated)
2896 148 : return;
2897 :
2898 656 : q = createPQExpBuffer();
2899 :
2900 656 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2901 656 : fmtQualifiedDumpable(tbinfo));
2902 :
2903 656 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2904 656 : ArchiveEntry(fout,
2905 : tdinfo->dobj.catId, /* catalog ID */
2906 : tdinfo->dobj.dumpId, /* dump ID */
2907 656 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2908 : .namespace = tbinfo->dobj.namespace->dobj.name,
2909 : .owner = tbinfo->rolname,
2910 : .description = "MATERIALIZED VIEW DATA",
2911 : .section = SECTION_POST_DATA,
2912 : .createStmt = q->data,
2913 : .deps = tdinfo->dobj.dependencies,
2914 : .nDeps = tdinfo->dobj.nDeps));
2915 :
2916 656 : destroyPQExpBuffer(q);
2917 : }
2918 :
2919 : /*
2920 : * getTableData -
2921 : * set up dumpable objects representing the contents of tables
2922 : */
2923 : static void
2924 338 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2925 : {
2926 : int i;
2927 :
2928 88718 : for (i = 0; i < numTables; i++)
2929 : {
2930 88380 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2931 1772 : (!relkind || tblinfo[i].relkind == relkind))
2932 11430 : makeTableDataInfo(dopt, &(tblinfo[i]));
2933 : }
2934 338 : }
2935 :
2936 : /*
2937 : * Make a dumpable object for the data of this specific table
2938 : *
2939 : * Note: we make a TableDataInfo if and only if we are going to dump the
2940 : * table data; the "dump" field in such objects isn't very interesting.
2941 : */
2942 : static void
2943 11508 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2944 : {
2945 : TableDataInfo *tdinfo;
2946 :
2947 : /*
2948 : * Nothing to do if we already decided to dump the table. This will
2949 : * happen for "config" tables.
2950 : */
2951 11508 : if (tbinfo->dataObj != NULL)
2952 2 : return;
2953 :
2954 : /* Skip VIEWs (no data to dump) */
2955 11506 : if (tbinfo->relkind == RELKIND_VIEW)
2956 942 : return;
2957 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2958 10564 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2959 82 : (foreign_servers_include_oids.head == NULL ||
2960 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2961 : tbinfo->foreign_server)))
2962 80 : return;
2963 : /* Skip partitioned tables (data in partitions) */
2964 10484 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2965 910 : return;
2966 :
2967 : /* Don't dump data in unlogged tables, if so requested */
2968 9574 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2969 82 : dopt->no_unlogged_table_data)
2970 36 : return;
2971 :
2972 : /* Check that the data is not explicitly excluded */
2973 9538 : if (simple_oid_list_member(&tabledata_exclude_oids,
2974 : tbinfo->dobj.catId.oid))
2975 16 : return;
2976 :
2977 : /* OK, let's dump it */
2978 9522 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2979 :
2980 9522 : if (tbinfo->relkind == RELKIND_MATVIEW)
2981 804 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2982 8718 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2983 804 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2984 : else
2985 7914 : tdinfo->dobj.objType = DO_TABLE_DATA;
2986 :
2987 : /*
2988 : * Note: use tableoid 0 so that this object won't be mistaken for
2989 : * something that pg_depend entries apply to.
2990 : */
2991 9522 : tdinfo->dobj.catId.tableoid = 0;
2992 9522 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2993 9522 : AssignDumpId(&tdinfo->dobj);
2994 9522 : tdinfo->dobj.name = tbinfo->dobj.name;
2995 9522 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2996 9522 : tdinfo->tdtable = tbinfo;
2997 9522 : tdinfo->filtercond = NULL; /* might get set later */
2998 9522 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2999 :
3000 : /* A TableDataInfo contains data, of course */
3001 9522 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3002 :
3003 9522 : tbinfo->dataObj = tdinfo;
3004 :
3005 : /*
3006 : * Materialized view statistics must be restored after the data, because
3007 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3008 : *
3009 : * The dependency is added here because the statistics objects are created
3010 : * first.
3011 : */
3012 9522 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3013 : {
3014 692 : tbinfo->stats->section = SECTION_POST_DATA;
3015 692 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3016 : }
3017 :
3018 : /* Make sure that we'll collect per-column info for this table. */
3019 9522 : tbinfo->interesting = true;
3020 : }
3021 :
3022 : /*
3023 : * The refresh for a materialized view must be dependent on the refresh for
3024 : * any materialized view that this one is dependent on.
3025 : *
3026 : * This must be called after all the objects are created, but before they are
3027 : * sorted.
3028 : */
3029 : static void
3030 282 : buildMatViewRefreshDependencies(Archive *fout)
3031 : {
3032 : PQExpBuffer query;
3033 : PGresult *res;
3034 : int ntups,
3035 : i;
3036 : int i_classid,
3037 : i_objid,
3038 : i_refobjid;
3039 :
3040 : /* No Mat Views before 9.3. */
3041 282 : if (fout->remoteVersion < 90300)
3042 0 : return;
3043 :
3044 282 : query = createPQExpBuffer();
3045 :
3046 282 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3047 : "( "
3048 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3049 : "FROM pg_depend d1 "
3050 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3051 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3052 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3053 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3054 : "AND d2.objid = r1.oid "
3055 : "AND d2.refobjid <> d1.objid "
3056 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3057 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3058 : CppAsString2(RELKIND_VIEW) ") "
3059 : "WHERE d1.classid = 'pg_class'::regclass "
3060 : "UNION "
3061 : "SELECT w.objid, d3.refobjid, c3.relkind "
3062 : "FROM w "
3063 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3064 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3065 : "AND d3.objid = r3.oid "
3066 : "AND d3.refobjid <> w.refobjid "
3067 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3068 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3069 : CppAsString2(RELKIND_VIEW) ") "
3070 : ") "
3071 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3072 : "FROM w "
3073 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3074 :
3075 282 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3076 :
3077 282 : ntups = PQntuples(res);
3078 :
3079 282 : i_classid = PQfnumber(res, "classid");
3080 282 : i_objid = PQfnumber(res, "objid");
3081 282 : i_refobjid = PQfnumber(res, "refobjid");
3082 :
3083 846 : for (i = 0; i < ntups; i++)
3084 : {
3085 : CatalogId objId;
3086 : CatalogId refobjId;
3087 : DumpableObject *dobj;
3088 : DumpableObject *refdobj;
3089 : TableInfo *tbinfo;
3090 : TableInfo *reftbinfo;
3091 :
3092 564 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3093 564 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3094 564 : refobjId.tableoid = objId.tableoid;
3095 564 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3096 :
3097 564 : dobj = findObjectByCatalogId(objId);
3098 564 : if (dobj == NULL)
3099 96 : continue;
3100 :
3101 : Assert(dobj->objType == DO_TABLE);
3102 564 : tbinfo = (TableInfo *) dobj;
3103 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3104 564 : dobj = (DumpableObject *) tbinfo->dataObj;
3105 564 : if (dobj == NULL)
3106 96 : continue;
3107 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3108 :
3109 468 : refdobj = findObjectByCatalogId(refobjId);
3110 468 : if (refdobj == NULL)
3111 0 : continue;
3112 :
3113 : Assert(refdobj->objType == DO_TABLE);
3114 468 : reftbinfo = (TableInfo *) refdobj;
3115 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3116 468 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3117 468 : if (refdobj == NULL)
3118 0 : continue;
3119 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3120 :
3121 468 : addObjectDependency(dobj, refdobj->dumpId);
3122 :
3123 468 : if (!reftbinfo->relispopulated)
3124 74 : tbinfo->relispopulated = false;
3125 : }
3126 :
3127 282 : PQclear(res);
3128 :
3129 282 : destroyPQExpBuffer(query);
3130 : }
3131 :
3132 : /*
3133 : * getTableDataFKConstraints -
3134 : * add dump-order dependencies reflecting foreign key constraints
3135 : *
3136 : * This code is executed only in a data-only dump --- in schema+data dumps
3137 : * we handle foreign key issues by not creating the FK constraints until
3138 : * after the data is loaded. In a data-only dump, however, we want to
3139 : * order the table data objects in such a way that a table's referenced
3140 : * tables are restored first. (In the presence of circular references or
3141 : * self-references this may be impossible; we'll detect and complain about
3142 : * that during the dependency sorting step.)
3143 : */
3144 : static void
3145 14 : getTableDataFKConstraints(void)
3146 : {
3147 : DumpableObject **dobjs;
3148 : int numObjs;
3149 : int i;
3150 :
3151 : /* Search through all the dumpable objects for FK constraints */
3152 14 : getDumpableObjects(&dobjs, &numObjs);
3153 50568 : for (i = 0; i < numObjs; i++)
3154 : {
3155 50554 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3156 : {
3157 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3158 : TableInfo *ftable;
3159 :
3160 : /* Not interesting unless both tables are to be dumped */
3161 16 : if (cinfo->contable == NULL ||
3162 16 : cinfo->contable->dataObj == NULL)
3163 8 : continue;
3164 8 : ftable = findTableByOid(cinfo->confrelid);
3165 8 : if (ftable == NULL ||
3166 8 : ftable->dataObj == NULL)
3167 0 : continue;
3168 :
3169 : /*
3170 : * Okay, make referencing table's TABLE_DATA object depend on the
3171 : * referenced table's TABLE_DATA object.
3172 : */
3173 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3174 8 : ftable->dataObj->dobj.dumpId);
3175 : }
3176 : }
3177 14 : free(dobjs);
3178 14 : }
3179 :
3180 :
3181 : /*
3182 : * dumpDatabase:
3183 : * dump the database definition
3184 : */
3185 : static void
3186 154 : dumpDatabase(Archive *fout)
3187 : {
3188 154 : DumpOptions *dopt = fout->dopt;
3189 154 : PQExpBuffer dbQry = createPQExpBuffer();
3190 154 : PQExpBuffer delQry = createPQExpBuffer();
3191 154 : PQExpBuffer creaQry = createPQExpBuffer();
3192 154 : PQExpBuffer labelq = createPQExpBuffer();
3193 154 : PGconn *conn = GetConnection(fout);
3194 : PGresult *res;
3195 : int i_tableoid,
3196 : i_oid,
3197 : i_datname,
3198 : i_datdba,
3199 : i_encoding,
3200 : i_datlocprovider,
3201 : i_collate,
3202 : i_ctype,
3203 : i_datlocale,
3204 : i_daticurules,
3205 : i_frozenxid,
3206 : i_minmxid,
3207 : i_datacl,
3208 : i_acldefault,
3209 : i_datistemplate,
3210 : i_datconnlimit,
3211 : i_datcollversion,
3212 : i_tablespace;
3213 : CatalogId dbCatId;
3214 : DumpId dbDumpId;
3215 : DumpableAcl dbdacl;
3216 : const char *datname,
3217 : *dba,
3218 : *encoding,
3219 : *datlocprovider,
3220 : *collate,
3221 : *ctype,
3222 : *locale,
3223 : *icurules,
3224 : *datistemplate,
3225 : *datconnlimit,
3226 : *tablespace;
3227 : uint32 frozenxid,
3228 : minmxid;
3229 : char *qdatname;
3230 :
3231 154 : pg_log_info("saving database definition");
3232 :
3233 : /*
3234 : * Fetch the database-level properties for this database.
3235 : */
3236 154 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3237 : "datdba, "
3238 : "pg_encoding_to_char(encoding) AS encoding, "
3239 : "datcollate, datctype, datfrozenxid, "
3240 : "datacl, acldefault('d', datdba) AS acldefault, "
3241 : "datistemplate, datconnlimit, ");
3242 154 : if (fout->remoteVersion >= 90300)
3243 154 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3244 : else
3245 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3246 154 : if (fout->remoteVersion >= 170000)
3247 154 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3248 0 : else if (fout->remoteVersion >= 150000)
3249 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3250 : else
3251 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3252 154 : if (fout->remoteVersion >= 160000)
3253 154 : appendPQExpBufferStr(dbQry, "daticurules, ");
3254 : else
3255 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3256 154 : appendPQExpBufferStr(dbQry,
3257 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3258 : "shobj_description(oid, 'pg_database') AS description "
3259 : "FROM pg_database "
3260 : "WHERE datname = current_database()");
3261 :
3262 154 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3263 :
3264 154 : i_tableoid = PQfnumber(res, "tableoid");
3265 154 : i_oid = PQfnumber(res, "oid");
3266 154 : i_datname = PQfnumber(res, "datname");
3267 154 : i_datdba = PQfnumber(res, "datdba");
3268 154 : i_encoding = PQfnumber(res, "encoding");
3269 154 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3270 154 : i_collate = PQfnumber(res, "datcollate");
3271 154 : i_ctype = PQfnumber(res, "datctype");
3272 154 : i_datlocale = PQfnumber(res, "datlocale");
3273 154 : i_daticurules = PQfnumber(res, "daticurules");
3274 154 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3275 154 : i_minmxid = PQfnumber(res, "datminmxid");
3276 154 : i_datacl = PQfnumber(res, "datacl");
3277 154 : i_acldefault = PQfnumber(res, "acldefault");
3278 154 : i_datistemplate = PQfnumber(res, "datistemplate");
3279 154 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3280 154 : i_datcollversion = PQfnumber(res, "datcollversion");
3281 154 : i_tablespace = PQfnumber(res, "tablespace");
3282 :
3283 154 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3284 154 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3285 154 : datname = PQgetvalue(res, 0, i_datname);
3286 154 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3287 154 : encoding = PQgetvalue(res, 0, i_encoding);
3288 154 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3289 154 : collate = PQgetvalue(res, 0, i_collate);
3290 154 : ctype = PQgetvalue(res, 0, i_ctype);
3291 154 : if (!PQgetisnull(res, 0, i_datlocale))
3292 28 : locale = PQgetvalue(res, 0, i_datlocale);
3293 : else
3294 126 : locale = NULL;
3295 154 : if (!PQgetisnull(res, 0, i_daticurules))
3296 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3297 : else
3298 154 : icurules = NULL;
3299 154 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3300 154 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3301 154 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3302 154 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3303 154 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3304 154 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3305 154 : tablespace = PQgetvalue(res, 0, i_tablespace);
3306 :
3307 154 : qdatname = pg_strdup(fmtId(datname));
3308 :
3309 : /*
3310 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3311 : * to preserve that), as well as the encoding, locale, and tablespace
3312 : * since those can't be altered later. Other DB properties are left to
3313 : * the DATABASE PROPERTIES entry, so that they can be applied after
3314 : * reconnecting to the target DB.
3315 : *
3316 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3317 : * shown it to be faster. When the server is in binary upgrade mode, it
3318 : * will also skip the checkpoints this strategy ordinarily performs.
3319 : */
3320 154 : if (dopt->binary_upgrade)
3321 : {
3322 60 : appendPQExpBuffer(creaQry,
3323 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3324 : "OID = %u STRATEGY = FILE_COPY",
3325 : qdatname, dbCatId.oid);
3326 : }
3327 : else
3328 : {
3329 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3330 : qdatname);
3331 : }
3332 154 : if (strlen(encoding) > 0)
3333 : {
3334 154 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3335 154 : appendStringLiteralAH(creaQry, encoding, fout);
3336 : }
3337 :
3338 154 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3339 154 : if (datlocprovider[0] == 'b')
3340 28 : appendPQExpBufferStr(creaQry, "builtin");
3341 126 : else if (datlocprovider[0] == 'c')
3342 126 : appendPQExpBufferStr(creaQry, "libc");
3343 0 : else if (datlocprovider[0] == 'i')
3344 0 : appendPQExpBufferStr(creaQry, "icu");
3345 : else
3346 0 : pg_fatal("unrecognized locale provider: %s",
3347 : datlocprovider);
3348 :
3349 154 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3350 : {
3351 154 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3352 154 : appendStringLiteralAH(creaQry, collate, fout);
3353 : }
3354 : else
3355 : {
3356 0 : if (strlen(collate) > 0)
3357 : {
3358 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3359 0 : appendStringLiteralAH(creaQry, collate, fout);
3360 : }
3361 0 : if (strlen(ctype) > 0)
3362 : {
3363 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3364 0 : appendStringLiteralAH(creaQry, ctype, fout);
3365 : }
3366 : }
3367 154 : if (locale)
3368 : {
3369 28 : if (datlocprovider[0] == 'b')
3370 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3371 : else
3372 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3373 :
3374 28 : appendStringLiteralAH(creaQry, locale, fout);
3375 : }
3376 :
3377 154 : if (icurules)
3378 : {
3379 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3380 0 : appendStringLiteralAH(creaQry, icurules, fout);
3381 : }
3382 :
3383 : /*
3384 : * For binary upgrade, carry over the collation version. For normal
3385 : * dump/restore, omit the version, so that it is computed upon restore.
3386 : */
3387 154 : if (dopt->binary_upgrade)
3388 : {
3389 60 : if (!PQgetisnull(res, 0, i_datcollversion))
3390 : {
3391 60 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3392 60 : appendStringLiteralAH(creaQry,
3393 : PQgetvalue(res, 0, i_datcollversion),
3394 : fout);
3395 : }
3396 : }
3397 :
3398 : /*
3399 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3400 : * thing; the decision whether to specify a tablespace should be left till
3401 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3402 : * label the DATABASE entry with the tablespace and let the normal
3403 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3404 : * attention to default_tablespace, so that won't work.
3405 : */
3406 154 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3407 0 : !dopt->outputNoTablespaces)
3408 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3409 : fmtId(tablespace));
3410 154 : appendPQExpBufferStr(creaQry, ";\n");
3411 :
3412 154 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3413 : qdatname);
3414 :
3415 154 : dbDumpId = createDumpId();
3416 :
3417 154 : ArchiveEntry(fout,
3418 : dbCatId, /* catalog ID */
3419 : dbDumpId, /* dump ID */
3420 154 : ARCHIVE_OPTS(.tag = datname,
3421 : .owner = dba,
3422 : .description = "DATABASE",
3423 : .section = SECTION_PRE_DATA,
3424 : .createStmt = creaQry->data,
3425 : .dropStmt = delQry->data));
3426 :
3427 : /* Compute correct tag for archive entry */
3428 154 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3429 :
3430 : /* Dump DB comment if any */
3431 : {
3432 : /*
3433 : * 8.2 and up keep comments on shared objects in a shared table, so we
3434 : * cannot use the dumpComment() code used for other database objects.
3435 : * Be careful that the ArchiveEntry parameters match that function.
3436 : */
3437 154 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3438 :
3439 154 : if (comment && *comment && !dopt->no_comments)
3440 : {
3441 74 : resetPQExpBuffer(dbQry);
3442 :
3443 : /*
3444 : * Generates warning when loaded into a differently-named
3445 : * database.
3446 : */
3447 74 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3448 74 : appendStringLiteralAH(dbQry, comment, fout);
3449 74 : appendPQExpBufferStr(dbQry, ";\n");
3450 :
3451 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3452 74 : ARCHIVE_OPTS(.tag = labelq->data,
3453 : .owner = dba,
3454 : .description = "COMMENT",
3455 : .section = SECTION_NONE,
3456 : .createStmt = dbQry->data,
3457 : .deps = &dbDumpId,
3458 : .nDeps = 1));
3459 : }
3460 : }
3461 :
3462 : /* Dump DB security label, if enabled */
3463 154 : if (!dopt->no_security_labels)
3464 : {
3465 : PGresult *shres;
3466 : PQExpBuffer seclabelQry;
3467 :
3468 154 : seclabelQry = createPQExpBuffer();
3469 :
3470 154 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3471 154 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3472 154 : resetPQExpBuffer(seclabelQry);
3473 154 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3474 154 : if (seclabelQry->len > 0)
3475 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3476 0 : ARCHIVE_OPTS(.tag = labelq->data,
3477 : .owner = dba,
3478 : .description = "SECURITY LABEL",
3479 : .section = SECTION_NONE,
3480 : .createStmt = seclabelQry->data,
3481 : .deps = &dbDumpId,
3482 : .nDeps = 1));
3483 154 : destroyPQExpBuffer(seclabelQry);
3484 154 : PQclear(shres);
3485 : }
3486 :
3487 : /*
3488 : * Dump ACL if any. Note that we do not support initial privileges
3489 : * (pg_init_privs) on databases.
3490 : */
3491 154 : dbdacl.privtype = 0;
3492 154 : dbdacl.initprivs = NULL;
3493 :
3494 154 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3495 : qdatname, NULL, NULL,
3496 : NULL, dba, &dbdacl);
3497 :
3498 : /*
3499 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3500 : * non-default database-level properties. (The reason this must be
3501 : * separate is that we cannot put any additional commands into the TOC
3502 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3503 : * in an implicit transaction block, and the backend won't allow CREATE
3504 : * DATABASE in that context.)
3505 : */
3506 154 : resetPQExpBuffer(creaQry);
3507 154 : resetPQExpBuffer(delQry);
3508 :
3509 154 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3510 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3511 : qdatname, datconnlimit);
3512 :
3513 154 : if (strcmp(datistemplate, "t") == 0)
3514 : {
3515 20 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3516 : qdatname);
3517 :
3518 : /*
3519 : * The backend won't accept DROP DATABASE on a template database. We
3520 : * can deal with that by removing the template marking before the DROP
3521 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3522 : * since no such command is currently supported, fake it with a direct
3523 : * UPDATE on pg_database.
3524 : */
3525 20 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3526 : "SET datistemplate = false WHERE datname = ");
3527 20 : appendStringLiteralAH(delQry, datname, fout);
3528 20 : appendPQExpBufferStr(delQry, ";\n");
3529 : }
3530 :
3531 : /*
3532 : * We do not restore pg_database.dathasloginevt because it is set
3533 : * automatically on login event trigger creation.
3534 : */
3535 :
3536 : /* Add database-specific SET options */
3537 154 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3538 :
3539 : /*
3540 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3541 : * entry, too, for lack of a better place.
3542 : */
3543 154 : if (dopt->binary_upgrade)
3544 : {
3545 60 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3546 60 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3547 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3548 : "WHERE datname = ",
3549 : frozenxid, minmxid);
3550 60 : appendStringLiteralAH(creaQry, datname, fout);
3551 60 : appendPQExpBufferStr(creaQry, ";\n");
3552 : }
3553 :
3554 154 : if (creaQry->len > 0)
3555 68 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3556 68 : ARCHIVE_OPTS(.tag = datname,
3557 : .owner = dba,
3558 : .description = "DATABASE PROPERTIES",
3559 : .section = SECTION_PRE_DATA,
3560 : .createStmt = creaQry->data,
3561 : .dropStmt = delQry->data,
3562 : .deps = &dbDumpId));
3563 :
3564 : /*
3565 : * pg_largeobject comes from the old system intact, so set its
3566 : * relfrozenxids, relminmxids and relfilenode.
3567 : */
3568 154 : if (dopt->binary_upgrade)
3569 : {
3570 : PGresult *lo_res;
3571 60 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3572 60 : PQExpBuffer loOutQry = createPQExpBuffer();
3573 60 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3574 : int ii_relfrozenxid,
3575 : ii_relfilenode,
3576 : ii_oid,
3577 : ii_relminmxid;
3578 :
3579 : /*
3580 : * pg_largeobject
3581 : */
3582 60 : if (fout->remoteVersion >= 90300)
3583 60 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3584 : "FROM pg_catalog.pg_class\n"
3585 : "WHERE oid IN (%u, %u);\n",
3586 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3587 : else
3588 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3589 : "FROM pg_catalog.pg_class\n"
3590 : "WHERE oid IN (%u, %u);\n",
3591 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3592 :
3593 60 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3594 :
3595 60 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3596 60 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3597 60 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3598 60 : ii_oid = PQfnumber(lo_res, "oid");
3599 :
3600 60 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3601 60 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3602 180 : for (int i = 0; i < PQntuples(lo_res); ++i)
3603 : {
3604 : Oid oid;
3605 : RelFileNumber relfilenumber;
3606 :
3607 120 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3608 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3609 : "WHERE oid = %u;\n",
3610 120 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3611 120 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3612 120 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3613 :
3614 120 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3615 120 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3616 :
3617 120 : if (oid == LargeObjectRelationId)
3618 60 : appendPQExpBuffer(loOutQry,
3619 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3620 : relfilenumber);
3621 60 : else if (oid == LargeObjectLOidPNIndexId)
3622 60 : appendPQExpBuffer(loOutQry,
3623 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3624 : relfilenumber);
3625 : }
3626 :
3627 60 : appendPQExpBufferStr(loOutQry,
3628 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3629 60 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3630 :
3631 60 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3632 60 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3633 : .description = "pg_largeobject",
3634 : .section = SECTION_PRE_DATA,
3635 : .createStmt = loOutQry->data));
3636 :
3637 60 : PQclear(lo_res);
3638 :
3639 60 : destroyPQExpBuffer(loFrozenQry);
3640 60 : destroyPQExpBuffer(loHorizonQry);
3641 60 : destroyPQExpBuffer(loOutQry);
3642 : }
3643 :
3644 154 : PQclear(res);
3645 :
3646 154 : free(qdatname);
3647 154 : destroyPQExpBuffer(dbQry);
3648 154 : destroyPQExpBuffer(delQry);
3649 154 : destroyPQExpBuffer(creaQry);
3650 154 : destroyPQExpBuffer(labelq);
3651 154 : }
3652 :
3653 : /*
3654 : * Collect any database-specific or role-and-database-specific SET options
3655 : * for this database, and append them to outbuf.
3656 : */
3657 : static void
3658 154 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3659 : const char *dbname, Oid dboid)
3660 : {
3661 154 : PGconn *conn = GetConnection(AH);
3662 154 : PQExpBuffer buf = createPQExpBuffer();
3663 : PGresult *res;
3664 :
3665 : /* First collect database-specific options */
3666 154 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3667 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3668 : dboid);
3669 :
3670 154 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3671 :
3672 214 : for (int i = 0; i < PQntuples(res); i++)
3673 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3674 : "DATABASE", dbname, NULL, NULL,
3675 : outbuf);
3676 :
3677 154 : PQclear(res);
3678 :
3679 : /* Now look for role-and-database-specific options */
3680 154 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3681 : "FROM pg_db_role_setting s, pg_roles r "
3682 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3683 : dboid);
3684 :
3685 154 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3686 :
3687 154 : for (int i = 0; i < PQntuples(res); i++)
3688 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3689 0 : "ROLE", PQgetvalue(res, i, 0),
3690 : "DATABASE", dbname,
3691 : outbuf);
3692 :
3693 154 : PQclear(res);
3694 :
3695 154 : destroyPQExpBuffer(buf);
3696 154 : }
3697 :
3698 : /*
3699 : * dumpEncoding: put the correct encoding into the archive
3700 : */
3701 : static void
3702 354 : dumpEncoding(Archive *AH)
3703 : {
3704 354 : const char *encname = pg_encoding_to_char(AH->encoding);
3705 354 : PQExpBuffer qry = createPQExpBuffer();
3706 :
3707 354 : pg_log_info("saving encoding = %s", encname);
3708 :
3709 354 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3710 354 : appendStringLiteralAH(qry, encname, AH);
3711 354 : appendPQExpBufferStr(qry, ";\n");
3712 :
3713 354 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3714 354 : ARCHIVE_OPTS(.tag = "ENCODING",
3715 : .description = "ENCODING",
3716 : .section = SECTION_PRE_DATA,
3717 : .createStmt = qry->data));
3718 :
3719 354 : destroyPQExpBuffer(qry);
3720 354 : }
3721 :
3722 :
3723 : /*
3724 : * dumpStdStrings: put the correct escape string behavior into the archive
3725 : */
3726 : static void
3727 354 : dumpStdStrings(Archive *AH)
3728 : {
3729 354 : const char *stdstrings = AH->std_strings ? "on" : "off";
3730 354 : PQExpBuffer qry = createPQExpBuffer();
3731 :
3732 354 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3733 : stdstrings);
3734 :
3735 354 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3736 : stdstrings);
3737 :
3738 354 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3739 354 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3740 : .description = "STDSTRINGS",
3741 : .section = SECTION_PRE_DATA,
3742 : .createStmt = qry->data));
3743 :
3744 354 : destroyPQExpBuffer(qry);
3745 354 : }
3746 :
3747 : /*
3748 : * dumpSearchPath: record the active search_path in the archive
3749 : */
3750 : static void
3751 354 : dumpSearchPath(Archive *AH)
3752 : {
3753 354 : PQExpBuffer qry = createPQExpBuffer();
3754 354 : PQExpBuffer path = createPQExpBuffer();
3755 : PGresult *res;
3756 354 : char **schemanames = NULL;
3757 354 : int nschemanames = 0;
3758 : int i;
3759 :
3760 : /*
3761 : * We use the result of current_schemas(), not the search_path GUC,
3762 : * because that might contain wildcards such as "$user", which won't
3763 : * necessarily have the same value during restore. Also, this way avoids
3764 : * listing schemas that may appear in search_path but not actually exist,
3765 : * which seems like a prudent exclusion.
3766 : */
3767 354 : res = ExecuteSqlQueryForSingleRow(AH,
3768 : "SELECT pg_catalog.current_schemas(false)");
3769 :
3770 354 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3771 0 : pg_fatal("could not parse result of current_schemas()");
3772 :
3773 : /*
3774 : * We use set_config(), not a simple "SET search_path" command, because
3775 : * the latter has less-clean behavior if the search path is empty. While
3776 : * that's likely to get fixed at some point, it seems like a good idea to
3777 : * be as backwards-compatible as possible in what we put into archives.
3778 : */
3779 354 : for (i = 0; i < nschemanames; i++)
3780 : {
3781 0 : if (i > 0)
3782 0 : appendPQExpBufferStr(path, ", ");
3783 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3784 : }
3785 :
3786 354 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3787 354 : appendStringLiteralAH(qry, path->data, AH);
3788 354 : appendPQExpBufferStr(qry, ", false);\n");
3789 :
3790 354 : pg_log_info("saving \"search_path = %s\"", path->data);
3791 :
3792 354 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3793 354 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3794 : .description = "SEARCHPATH",
3795 : .section = SECTION_PRE_DATA,
3796 : .createStmt = qry->data));
3797 :
3798 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3799 354 : AH->searchpath = pg_strdup(qry->data);
3800 :
3801 354 : free(schemanames);
3802 354 : PQclear(res);
3803 354 : destroyPQExpBuffer(qry);
3804 354 : destroyPQExpBuffer(path);
3805 354 : }
3806 :
3807 :
3808 : /*
3809 : * getLOs:
3810 : * Collect schema-level data about large objects
3811 : */
3812 : static void
3813 296 : getLOs(Archive *fout)
3814 : {
3815 296 : DumpOptions *dopt = fout->dopt;
3816 296 : PQExpBuffer loQry = createPQExpBuffer();
3817 : PGresult *res;
3818 : int ntups;
3819 : int i;
3820 : int n;
3821 : int i_oid;
3822 : int i_lomowner;
3823 : int i_lomacl;
3824 : int i_acldefault;
3825 :
3826 296 : pg_log_info("reading large objects");
3827 :
3828 : /*
3829 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3830 : * with the same owner/ACL appear together.
3831 : */
3832 296 : appendPQExpBufferStr(loQry,
3833 : "SELECT oid, lomowner, lomacl, "
3834 : "acldefault('L', lomowner) AS acldefault "
3835 : "FROM pg_largeobject_metadata "
3836 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3837 :
3838 296 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3839 :
3840 296 : i_oid = PQfnumber(res, "oid");
3841 296 : i_lomowner = PQfnumber(res, "lomowner");
3842 296 : i_lomacl = PQfnumber(res, "lomacl");
3843 296 : i_acldefault = PQfnumber(res, "acldefault");
3844 :
3845 296 : ntups = PQntuples(res);
3846 :
3847 : /*
3848 : * Group the blobs into suitably-sized groups that have the same owner and
3849 : * ACL setting, and build a metadata and a data DumpableObject for each
3850 : * group. (If we supported initprivs for blobs, we'd have to insist that
3851 : * groups also share initprivs settings, since the DumpableObject only has
3852 : * room for one.) i is the index of the first tuple in the current group,
3853 : * and n is the number of tuples we include in the group.
3854 : */
3855 454 : for (i = 0; i < ntups; i += n)
3856 : {
3857 158 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3858 158 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3859 158 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3860 : LoInfo *loinfo;
3861 : DumpableObject *lodata;
3862 : char namebuf[64];
3863 :
3864 : /* Scan to find first tuple not to be included in group */
3865 158 : n = 1;
3866 178 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3867 : {
3868 94 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3869 94 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3870 : break;
3871 20 : n++;
3872 : }
3873 :
3874 : /* Build the metadata DumpableObject */
3875 158 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3876 :
3877 158 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3878 158 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3879 158 : loinfo->dobj.catId.oid = thisoid;
3880 158 : AssignDumpId(&loinfo->dobj);
3881 :
3882 158 : if (n > 1)
3883 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3884 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3885 : else
3886 148 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3887 158 : loinfo->dobj.name = pg_strdup(namebuf);
3888 158 : loinfo->dacl.acl = pg_strdup(thisacl);
3889 158 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3890 158 : loinfo->dacl.privtype = 0;
3891 158 : loinfo->dacl.initprivs = NULL;
3892 158 : loinfo->rolname = getRoleName(thisowner);
3893 158 : loinfo->numlos = n;
3894 158 : loinfo->looids[0] = thisoid;
3895 : /* Collect OIDs of the remaining blobs in this group */
3896 178 : for (int k = 1; k < n; k++)
3897 : {
3898 : CatalogId extraID;
3899 :
3900 20 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3901 :
3902 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3903 20 : extraID.tableoid = LargeObjectRelationId;
3904 20 : extraID.oid = loinfo->looids[k];
3905 20 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3906 : }
3907 :
3908 : /* LOs have data */
3909 158 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3910 :
3911 : /* Mark whether LO group has a non-empty ACL */
3912 158 : if (!PQgetisnull(res, i, i_lomacl))
3913 74 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3914 :
3915 : /*
3916 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3917 : * as it will be copied by pg_upgrade, which simply copies the
3918 : * pg_largeobject table. We *do* however dump out anything but the
3919 : * data, as pg_upgrade copies just pg_largeobject, but not
3920 : * pg_largeobject_metadata, after the dump is restored.
3921 : */
3922 158 : if (dopt->binary_upgrade)
3923 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3924 :
3925 : /*
3926 : * Create a "BLOBS" data item for the group, too. This is just a
3927 : * placeholder for sorting; it carries no data now.
3928 : */
3929 158 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3930 158 : lodata->objType = DO_LARGE_OBJECT_DATA;
3931 158 : lodata->catId = nilCatalogId;
3932 158 : AssignDumpId(lodata);
3933 158 : lodata->name = pg_strdup(namebuf);
3934 158 : lodata->components |= DUMP_COMPONENT_DATA;
3935 : /* Set up explicit dependency from data to metadata */
3936 158 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3937 158 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3938 158 : lodata->nDeps = lodata->allocDeps = 1;
3939 : }
3940 :
3941 296 : PQclear(res);
3942 296 : destroyPQExpBuffer(loQry);
3943 296 : }
3944 :
3945 : /*
3946 : * dumpLO
3947 : *
3948 : * dump the definition (metadata) of the given large object group
3949 : */
3950 : static void
3951 158 : dumpLO(Archive *fout, const LoInfo *loinfo)
3952 : {
3953 158 : PQExpBuffer cquery = createPQExpBuffer();
3954 :
3955 : /*
3956 : * The "definition" is just a newline-separated list of OIDs. We need to
3957 : * put something into the dropStmt too, but it can just be a comment.
3958 : */
3959 336 : for (int i = 0; i < loinfo->numlos; i++)
3960 178 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3961 :
3962 158 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3963 158 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3964 158 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3965 : .owner = loinfo->rolname,
3966 : .description = "BLOB METADATA",
3967 : .section = SECTION_DATA,
3968 : .createStmt = cquery->data,
3969 : .dropStmt = "-- dummy"));
3970 :
3971 : /*
3972 : * Dump per-blob comments and seclabels if any. We assume these are rare
3973 : * enough that it's okay to generate retail TOC entries for them.
3974 : */
3975 158 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3976 : DUMP_COMPONENT_SECLABEL))
3977 : {
3978 188 : for (int i = 0; i < loinfo->numlos; i++)
3979 : {
3980 : CatalogId catId;
3981 : char namebuf[32];
3982 :
3983 : /* Build identifying info for this blob */
3984 104 : catId.tableoid = loinfo->dobj.catId.tableoid;
3985 104 : catId.oid = loinfo->looids[i];
3986 104 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3987 :
3988 104 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3989 104 : dumpComment(fout, "LARGE OBJECT", namebuf,
3990 : NULL, loinfo->rolname,
3991 : catId, 0, loinfo->dobj.dumpId);
3992 :
3993 104 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3994 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
3995 : NULL, loinfo->rolname,
3996 : catId, 0, loinfo->dobj.dumpId);
3997 : }
3998 : }
3999 :
4000 : /*
4001 : * Dump the ACLs if any (remember that all blobs in the group will have
4002 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4003 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4004 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4005 : * string to emit a mutated version for each blob.
4006 : */
4007 158 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4008 : {
4009 : char namebuf[32];
4010 :
4011 : /* Build identifying info for the first blob */
4012 74 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4013 :
4014 74 : if (loinfo->numlos > 1)
4015 : {
4016 : char tagbuf[64];
4017 :
4018 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4019 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4020 :
4021 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4022 : "LARGE OBJECT", namebuf, NULL, NULL,
4023 : tagbuf, loinfo->rolname, &loinfo->dacl);
4024 : }
4025 : else
4026 : {
4027 74 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4028 : "LARGE OBJECT", namebuf, NULL, NULL,
4029 : NULL, loinfo->rolname, &loinfo->dacl);
4030 : }
4031 : }
4032 :
4033 158 : destroyPQExpBuffer(cquery);
4034 158 : }
4035 :
4036 : /*
4037 : * dumpLOs:
4038 : * dump the data contents of the large objects in the given group
4039 : */
4040 : static int
4041 144 : dumpLOs(Archive *fout, const void *arg)
4042 : {
4043 144 : const LoInfo *loinfo = (const LoInfo *) arg;
4044 144 : PGconn *conn = GetConnection(fout);
4045 : char buf[LOBBUFSIZE];
4046 :
4047 144 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4048 :
4049 304 : for (int i = 0; i < loinfo->numlos; i++)
4050 : {
4051 160 : Oid loOid = loinfo->looids[i];
4052 : int loFd;
4053 : int cnt;
4054 :
4055 : /* Open the LO */
4056 160 : loFd = lo_open(conn, loOid, INV_READ);
4057 160 : if (loFd == -1)
4058 0 : pg_fatal("could not open large object %u: %s",
4059 : loOid, PQerrorMessage(conn));
4060 :
4061 160 : StartLO(fout, loOid);
4062 :
4063 : /* Now read it in chunks, sending data to archive */
4064 : do
4065 : {
4066 244 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4067 244 : if (cnt < 0)
4068 0 : pg_fatal("error reading large object %u: %s",
4069 : loOid, PQerrorMessage(conn));
4070 :
4071 244 : WriteData(fout, buf, cnt);
4072 244 : } while (cnt > 0);
4073 :
4074 160 : lo_close(conn, loFd);
4075 :
4076 160 : EndLO(fout, loOid);
4077 : }
4078 :
4079 144 : return 1;
4080 : }
4081 :
4082 : /*
4083 : * getPolicies
4084 : * get information about all RLS policies on dumpable tables.
4085 : */
4086 : void
4087 354 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4088 : {
4089 354 : DumpOptions *dopt = fout->dopt;
4090 : PQExpBuffer query;
4091 : PQExpBuffer tbloids;
4092 : PGresult *res;
4093 : PolicyInfo *polinfo;
4094 : int i_oid;
4095 : int i_tableoid;
4096 : int i_polrelid;
4097 : int i_polname;
4098 : int i_polcmd;
4099 : int i_polpermissive;
4100 : int i_polroles;
4101 : int i_polqual;
4102 : int i_polwithcheck;
4103 : int i,
4104 : j,
4105 : ntups;
4106 :
4107 : /* No policies before 9.5 */
4108 354 : if (fout->remoteVersion < 90500)
4109 0 : return;
4110 :
4111 : /* Skip if --no-policies was specified */
4112 354 : if (dopt->no_policies)
4113 2 : return;
4114 :
4115 352 : query = createPQExpBuffer();
4116 352 : tbloids = createPQExpBuffer();
4117 :
4118 : /*
4119 : * Identify tables of interest, and check which ones have RLS enabled.
4120 : */
4121 352 : appendPQExpBufferChar(tbloids, '{');
4122 92016 : for (i = 0; i < numTables; i++)
4123 : {
4124 91664 : TableInfo *tbinfo = &tblinfo[i];
4125 :
4126 : /* Ignore row security on tables not to be dumped */
4127 91664 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4128 78288 : continue;
4129 :
4130 : /* It can't have RLS or policies if it's not a table */
4131 13376 : if (tbinfo->relkind != RELKIND_RELATION &&
4132 3950 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4133 2846 : continue;
4134 :
4135 : /* Add it to the list of table OIDs to be probed below */
4136 10530 : if (tbloids->len > 1) /* do we have more than the '{'? */
4137 10304 : appendPQExpBufferChar(tbloids, ',');
4138 10530 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4139 :
4140 : /* Is RLS enabled? (That's separate from whether it has policies) */
4141 10530 : if (tbinfo->rowsec)
4142 : {
4143 114 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4144 :
4145 : /*
4146 : * We represent RLS being enabled on a table by creating a
4147 : * PolicyInfo object with null polname.
4148 : *
4149 : * Note: use tableoid 0 so that this object won't be mistaken for
4150 : * something that pg_depend entries apply to.
4151 : */
4152 114 : polinfo = pg_malloc(sizeof(PolicyInfo));
4153 114 : polinfo->dobj.objType = DO_POLICY;
4154 114 : polinfo->dobj.catId.tableoid = 0;
4155 114 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4156 114 : AssignDumpId(&polinfo->dobj);
4157 114 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4158 114 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4159 114 : polinfo->poltable = tbinfo;
4160 114 : polinfo->polname = NULL;
4161 114 : polinfo->polcmd = '\0';
4162 114 : polinfo->polpermissive = 0;
4163 114 : polinfo->polroles = NULL;
4164 114 : polinfo->polqual = NULL;
4165 114 : polinfo->polwithcheck = NULL;
4166 : }
4167 : }
4168 352 : appendPQExpBufferChar(tbloids, '}');
4169 :
4170 : /*
4171 : * Now, read all RLS policies belonging to the tables of interest, and
4172 : * create PolicyInfo objects for them. (Note that we must filter the
4173 : * results server-side not locally, because we dare not apply pg_get_expr
4174 : * to tables we don't have lock on.)
4175 : */
4176 352 : pg_log_info("reading row-level security policies");
4177 :
4178 352 : printfPQExpBuffer(query,
4179 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4180 352 : if (fout->remoteVersion >= 100000)
4181 352 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4182 : else
4183 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4184 352 : appendPQExpBuffer(query,
4185 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4186 : " 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, "
4187 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4188 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4189 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4190 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4191 : tbloids->data);
4192 :
4193 352 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4194 :
4195 352 : ntups = PQntuples(res);
4196 352 : if (ntups > 0)
4197 : {
4198 94 : i_oid = PQfnumber(res, "oid");
4199 94 : i_tableoid = PQfnumber(res, "tableoid");
4200 94 : i_polrelid = PQfnumber(res, "polrelid");
4201 94 : i_polname = PQfnumber(res, "polname");
4202 94 : i_polcmd = PQfnumber(res, "polcmd");
4203 94 : i_polpermissive = PQfnumber(res, "polpermissive");
4204 94 : i_polroles = PQfnumber(res, "polroles");
4205 94 : i_polqual = PQfnumber(res, "polqual");
4206 94 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4207 :
4208 94 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4209 :
4210 688 : for (j = 0; j < ntups; j++)
4211 : {
4212 594 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4213 594 : TableInfo *tbinfo = findTableByOid(polrelid);
4214 :
4215 594 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4216 :
4217 594 : polinfo[j].dobj.objType = DO_POLICY;
4218 594 : polinfo[j].dobj.catId.tableoid =
4219 594 : atooid(PQgetvalue(res, j, i_tableoid));
4220 594 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4221 594 : AssignDumpId(&polinfo[j].dobj);
4222 594 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4223 594 : polinfo[j].poltable = tbinfo;
4224 594 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4225 594 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4226 :
4227 594 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4228 594 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4229 :
4230 594 : if (PQgetisnull(res, j, i_polroles))
4231 258 : polinfo[j].polroles = NULL;
4232 : else
4233 336 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4234 :
4235 594 : if (PQgetisnull(res, j, i_polqual))
4236 84 : polinfo[j].polqual = NULL;
4237 : else
4238 510 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4239 :
4240 594 : if (PQgetisnull(res, j, i_polwithcheck))
4241 312 : polinfo[j].polwithcheck = NULL;
4242 : else
4243 282 : polinfo[j].polwithcheck
4244 282 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4245 : }
4246 : }
4247 :
4248 352 : PQclear(res);
4249 :
4250 352 : destroyPQExpBuffer(query);
4251 352 : destroyPQExpBuffer(tbloids);
4252 : }
4253 :
4254 : /*
4255 : * dumpPolicy
4256 : * dump the definition of the given policy
4257 : */
4258 : static void
4259 708 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4260 : {
4261 708 : DumpOptions *dopt = fout->dopt;
4262 708 : TableInfo *tbinfo = polinfo->poltable;
4263 : PQExpBuffer query;
4264 : PQExpBuffer delqry;
4265 : PQExpBuffer polprefix;
4266 : char *qtabname;
4267 : const char *cmd;
4268 : char *tag;
4269 :
4270 : /* Do nothing if not dumping schema */
4271 708 : if (!dopt->dumpSchema)
4272 98 : return;
4273 :
4274 : /*
4275 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4276 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4277 : * ROW LEVEL SECURITY.
4278 : */
4279 610 : if (polinfo->polname == NULL)
4280 : {
4281 100 : query = createPQExpBuffer();
4282 :
4283 100 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4284 100 : fmtQualifiedDumpable(tbinfo));
4285 :
4286 : /*
4287 : * We must emit the ROW SECURITY object's dependency on its table
4288 : * explicitly, because it will not match anything in pg_depend (unlike
4289 : * the case for other PolicyInfo objects).
4290 : */
4291 100 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4292 100 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4293 100 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4294 : .namespace = polinfo->dobj.namespace->dobj.name,
4295 : .owner = tbinfo->rolname,
4296 : .description = "ROW SECURITY",
4297 : .section = SECTION_POST_DATA,
4298 : .createStmt = query->data,
4299 : .deps = &(tbinfo->dobj.dumpId),
4300 : .nDeps = 1));
4301 :
4302 100 : destroyPQExpBuffer(query);
4303 100 : return;
4304 : }
4305 :
4306 510 : if (polinfo->polcmd == '*')
4307 170 : cmd = "";
4308 340 : else if (polinfo->polcmd == 'r')
4309 90 : cmd = " FOR SELECT";
4310 250 : else if (polinfo->polcmd == 'a')
4311 70 : cmd = " FOR INSERT";
4312 180 : else if (polinfo->polcmd == 'w')
4313 90 : cmd = " FOR UPDATE";
4314 90 : else if (polinfo->polcmd == 'd')
4315 90 : cmd = " FOR DELETE";
4316 : else
4317 0 : pg_fatal("unexpected policy command type: %c",
4318 : polinfo->polcmd);
4319 :
4320 510 : query = createPQExpBuffer();
4321 510 : delqry = createPQExpBuffer();
4322 510 : polprefix = createPQExpBuffer();
4323 :
4324 510 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4325 :
4326 510 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4327 :
4328 510 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4329 510 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4330 :
4331 510 : if (polinfo->polroles != NULL)
4332 280 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4333 :
4334 510 : if (polinfo->polqual != NULL)
4335 440 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4336 :
4337 510 : if (polinfo->polwithcheck != NULL)
4338 240 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4339 :
4340 510 : appendPQExpBufferStr(query, ";\n");
4341 :
4342 510 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4343 510 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4344 :
4345 510 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4346 510 : fmtId(polinfo->polname));
4347 :
4348 510 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4349 :
4350 510 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4351 510 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4352 510 : ARCHIVE_OPTS(.tag = tag,
4353 : .namespace = polinfo->dobj.namespace->dobj.name,
4354 : .owner = tbinfo->rolname,
4355 : .description = "POLICY",
4356 : .section = SECTION_POST_DATA,
4357 : .createStmt = query->data,
4358 : .dropStmt = delqry->data));
4359 :
4360 510 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4361 0 : dumpComment(fout, polprefix->data, qtabname,
4362 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4363 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4364 :
4365 510 : free(tag);
4366 510 : destroyPQExpBuffer(query);
4367 510 : destroyPQExpBuffer(delqry);
4368 510 : destroyPQExpBuffer(polprefix);
4369 510 : free(qtabname);
4370 : }
4371 :
4372 : /*
4373 : * getPublications
4374 : * get information about publications
4375 : */
4376 : void
4377 354 : getPublications(Archive *fout)
4378 : {
4379 354 : DumpOptions *dopt = fout->dopt;
4380 : PQExpBuffer query;
4381 : PGresult *res;
4382 : PublicationInfo *pubinfo;
4383 : int i_tableoid;
4384 : int i_oid;
4385 : int i_pubname;
4386 : int i_pubowner;
4387 : int i_puballtables;
4388 : int i_pubinsert;
4389 : int i_pubupdate;
4390 : int i_pubdelete;
4391 : int i_pubtruncate;
4392 : int i_pubviaroot;
4393 : int i_pubgencols;
4394 : int i,
4395 : ntups;
4396 :
4397 354 : if (dopt->no_publications || fout->remoteVersion < 100000)
4398 0 : return;
4399 :
4400 354 : query = createPQExpBuffer();
4401 :
4402 : /* Get the publications. */
4403 354 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4404 : "p.pubowner, p.puballtables, p.pubinsert, "
4405 : "p.pubupdate, p.pubdelete, ");
4406 :
4407 354 : if (fout->remoteVersion >= 110000)
4408 354 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4409 : else
4410 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4411 :
4412 354 : if (fout->remoteVersion >= 130000)
4413 354 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4414 : else
4415 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4416 :
4417 354 : if (fout->remoteVersion >= 180000)
4418 354 : appendPQExpBufferStr(query, "p.pubgencols ");
4419 : else
4420 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4421 :
4422 354 : appendPQExpBufferStr(query, "FROM pg_publication p");
4423 :
4424 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4425 :
4426 354 : ntups = PQntuples(res);
4427 :
4428 354 : if (ntups == 0)
4429 254 : goto cleanup;
4430 :
4431 100 : i_tableoid = PQfnumber(res, "tableoid");
4432 100 : i_oid = PQfnumber(res, "oid");
4433 100 : i_pubname = PQfnumber(res, "pubname");
4434 100 : i_pubowner = PQfnumber(res, "pubowner");
4435 100 : i_puballtables = PQfnumber(res, "puballtables");
4436 100 : i_pubinsert = PQfnumber(res, "pubinsert");
4437 100 : i_pubupdate = PQfnumber(res, "pubupdate");
4438 100 : i_pubdelete = PQfnumber(res, "pubdelete");
4439 100 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4440 100 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4441 100 : i_pubgencols = PQfnumber(res, "pubgencols");
4442 :
4443 100 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4444 :
4445 592 : for (i = 0; i < ntups; i++)
4446 : {
4447 492 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4448 492 : pubinfo[i].dobj.catId.tableoid =
4449 492 : atooid(PQgetvalue(res, i, i_tableoid));
4450 492 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4451 492 : AssignDumpId(&pubinfo[i].dobj);
4452 492 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4453 492 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4454 492 : pubinfo[i].puballtables =
4455 492 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4456 492 : pubinfo[i].pubinsert =
4457 492 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4458 492 : pubinfo[i].pubupdate =
4459 492 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4460 492 : pubinfo[i].pubdelete =
4461 492 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4462 492 : pubinfo[i].pubtruncate =
4463 492 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4464 492 : pubinfo[i].pubviaroot =
4465 492 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4466 492 : pubinfo[i].pubgencols_type =
4467 492 : *(PQgetvalue(res, i, i_pubgencols));
4468 :
4469 : /* Decide whether we want to dump it */
4470 492 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4471 : }
4472 :
4473 100 : cleanup:
4474 354 : PQclear(res);
4475 :
4476 354 : destroyPQExpBuffer(query);
4477 : }
4478 :
4479 : /*
4480 : * dumpPublication
4481 : * dump the definition of the given publication
4482 : */
4483 : static void
4484 412 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4485 : {
4486 412 : DumpOptions *dopt = fout->dopt;
4487 : PQExpBuffer delq;
4488 : PQExpBuffer query;
4489 : char *qpubname;
4490 412 : bool first = true;
4491 :
4492 : /* Do nothing if not dumping schema */
4493 412 : if (!dopt->dumpSchema)
4494 60 : return;
4495 :
4496 352 : delq = createPQExpBuffer();
4497 352 : query = createPQExpBuffer();
4498 :
4499 352 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4500 :
4501 352 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4502 : qpubname);
4503 :
4504 352 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4505 : qpubname);
4506 :
4507 352 : if (pubinfo->puballtables)
4508 72 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4509 :
4510 352 : appendPQExpBufferStr(query, " WITH (publish = '");
4511 352 : if (pubinfo->pubinsert)
4512 : {
4513 282 : appendPQExpBufferStr(query, "insert");
4514 282 : first = false;
4515 : }
4516 :
4517 352 : if (pubinfo->pubupdate)
4518 : {
4519 282 : if (!first)
4520 282 : appendPQExpBufferStr(query, ", ");
4521 :
4522 282 : appendPQExpBufferStr(query, "update");
4523 282 : first = false;
4524 : }
4525 :
4526 352 : if (pubinfo->pubdelete)
4527 : {
4528 282 : if (!first)
4529 282 : appendPQExpBufferStr(query, ", ");
4530 :
4531 282 : appendPQExpBufferStr(query, "delete");
4532 282 : first = false;
4533 : }
4534 :
4535 352 : if (pubinfo->pubtruncate)
4536 : {
4537 282 : if (!first)
4538 282 : appendPQExpBufferStr(query, ", ");
4539 :
4540 282 : appendPQExpBufferStr(query, "truncate");
4541 282 : first = false;
4542 : }
4543 :
4544 352 : appendPQExpBufferChar(query, '\'');
4545 :
4546 352 : if (pubinfo->pubviaroot)
4547 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4548 :
4549 352 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4550 70 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4551 :
4552 352 : appendPQExpBufferStr(query, ");\n");
4553 :
4554 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4555 352 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4556 352 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4557 : .owner = pubinfo->rolname,
4558 : .description = "PUBLICATION",
4559 : .section = SECTION_POST_DATA,
4560 : .createStmt = query->data,
4561 : .dropStmt = delq->data));
4562 :
4563 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4564 70 : dumpComment(fout, "PUBLICATION", qpubname,
4565 : NULL, pubinfo->rolname,
4566 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4567 :
4568 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4569 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4570 : NULL, pubinfo->rolname,
4571 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4572 :
4573 352 : destroyPQExpBuffer(delq);
4574 352 : destroyPQExpBuffer(query);
4575 352 : free(qpubname);
4576 : }
4577 :
4578 : /*
4579 : * getPublicationNamespaces
4580 : * get information about publication membership for dumpable schemas.
4581 : */
4582 : void
4583 354 : getPublicationNamespaces(Archive *fout)
4584 : {
4585 : PQExpBuffer query;
4586 : PGresult *res;
4587 : PublicationSchemaInfo *pubsinfo;
4588 354 : DumpOptions *dopt = fout->dopt;
4589 : int i_tableoid;
4590 : int i_oid;
4591 : int i_pnpubid;
4592 : int i_pnnspid;
4593 : int i,
4594 : j,
4595 : ntups;
4596 :
4597 354 : if (dopt->no_publications || fout->remoteVersion < 150000)
4598 0 : return;
4599 :
4600 354 : query = createPQExpBuffer();
4601 :
4602 : /* Collect all publication membership info. */
4603 354 : appendPQExpBufferStr(query,
4604 : "SELECT tableoid, oid, pnpubid, pnnspid "
4605 : "FROM pg_catalog.pg_publication_namespace");
4606 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4607 :
4608 354 : ntups = PQntuples(res);
4609 :
4610 354 : i_tableoid = PQfnumber(res, "tableoid");
4611 354 : i_oid = PQfnumber(res, "oid");
4612 354 : i_pnpubid = PQfnumber(res, "pnpubid");
4613 354 : i_pnnspid = PQfnumber(res, "pnnspid");
4614 :
4615 : /* this allocation may be more than we need */
4616 354 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4617 354 : j = 0;
4618 :
4619 550 : for (i = 0; i < ntups; i++)
4620 : {
4621 196 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4622 196 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4623 : PublicationInfo *pubinfo;
4624 : NamespaceInfo *nspinfo;
4625 :
4626 : /*
4627 : * Ignore any entries for which we aren't interested in either the
4628 : * publication or the rel.
4629 : */
4630 196 : pubinfo = findPublicationByOid(pnpubid);
4631 196 : if (pubinfo == NULL)
4632 0 : continue;
4633 196 : nspinfo = findNamespaceByOid(pnnspid);
4634 196 : if (nspinfo == NULL)
4635 0 : continue;
4636 :
4637 : /* OK, make a DumpableObject for this relationship */
4638 196 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4639 196 : pubsinfo[j].dobj.catId.tableoid =
4640 196 : atooid(PQgetvalue(res, i, i_tableoid));
4641 196 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4642 196 : AssignDumpId(&pubsinfo[j].dobj);
4643 196 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4644 196 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4645 196 : pubsinfo[j].publication = pubinfo;
4646 196 : pubsinfo[j].pubschema = nspinfo;
4647 :
4648 : /* Decide whether we want to dump it */
4649 196 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4650 :
4651 196 : j++;
4652 : }
4653 :
4654 354 : PQclear(res);
4655 354 : destroyPQExpBuffer(query);
4656 : }
4657 :
4658 : /*
4659 : * getPublicationTables
4660 : * get information about publication membership for dumpable tables.
4661 : */
4662 : void
4663 354 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4664 : {
4665 : PQExpBuffer query;
4666 : PGresult *res;
4667 : PublicationRelInfo *pubrinfo;
4668 354 : DumpOptions *dopt = fout->dopt;
4669 : int i_tableoid;
4670 : int i_oid;
4671 : int i_prpubid;
4672 : int i_prrelid;
4673 : int i_prrelqual;
4674 : int i_prattrs;
4675 : int i,
4676 : j,
4677 : ntups;
4678 :
4679 354 : if (dopt->no_publications || fout->remoteVersion < 100000)
4680 0 : return;
4681 :
4682 354 : query = createPQExpBuffer();
4683 :
4684 : /* Collect all publication membership info. */
4685 354 : if (fout->remoteVersion >= 150000)
4686 354 : appendPQExpBufferStr(query,
4687 : "SELECT tableoid, oid, prpubid, prrelid, "
4688 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4689 : "(CASE\n"
4690 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4691 : " (SELECT array_agg(attname)\n"
4692 : " FROM\n"
4693 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4694 : " pg_catalog.pg_attribute\n"
4695 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4696 : " ELSE NULL END) prattrs "
4697 : "FROM pg_catalog.pg_publication_rel pr");
4698 : else
4699 0 : appendPQExpBufferStr(query,
4700 : "SELECT tableoid, oid, prpubid, prrelid, "
4701 : "NULL AS prrelqual, NULL AS prattrs "
4702 : "FROM pg_catalog.pg_publication_rel");
4703 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4704 :
4705 354 : ntups = PQntuples(res);
4706 :
4707 354 : i_tableoid = PQfnumber(res, "tableoid");
4708 354 : i_oid = PQfnumber(res, "oid");
4709 354 : i_prpubid = PQfnumber(res, "prpubid");
4710 354 : i_prrelid = PQfnumber(res, "prrelid");
4711 354 : i_prrelqual = PQfnumber(res, "prrelqual");
4712 354 : i_prattrs = PQfnumber(res, "prattrs");
4713 :
4714 : /* this allocation may be more than we need */
4715 354 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4716 354 : j = 0;
4717 :
4718 1040 : for (i = 0; i < ntups; i++)
4719 : {
4720 686 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4721 686 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4722 : PublicationInfo *pubinfo;
4723 : TableInfo *tbinfo;
4724 :
4725 : /*
4726 : * Ignore any entries for which we aren't interested in either the
4727 : * publication or the rel.
4728 : */
4729 686 : pubinfo = findPublicationByOid(prpubid);
4730 686 : if (pubinfo == NULL)
4731 0 : continue;
4732 686 : tbinfo = findTableByOid(prrelid);
4733 686 : if (tbinfo == NULL)
4734 0 : continue;
4735 :
4736 : /* OK, make a DumpableObject for this relationship */
4737 686 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4738 686 : pubrinfo[j].dobj.catId.tableoid =
4739 686 : atooid(PQgetvalue(res, i, i_tableoid));
4740 686 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4741 686 : AssignDumpId(&pubrinfo[j].dobj);
4742 686 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4743 686 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4744 686 : pubrinfo[j].publication = pubinfo;
4745 686 : pubrinfo[j].pubtable = tbinfo;
4746 686 : if (PQgetisnull(res, i, i_prrelqual))
4747 392 : pubrinfo[j].pubrelqual = NULL;
4748 : else
4749 294 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4750 :
4751 686 : if (!PQgetisnull(res, i, i_prattrs))
4752 : {
4753 : char **attnames;
4754 : int nattnames;
4755 : PQExpBuffer attribs;
4756 :
4757 196 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4758 : &attnames, &nattnames))
4759 0 : pg_fatal("could not parse %s array", "prattrs");
4760 196 : attribs = createPQExpBuffer();
4761 588 : for (int k = 0; k < nattnames; k++)
4762 : {
4763 392 : if (k > 0)
4764 196 : appendPQExpBufferStr(attribs, ", ");
4765 :
4766 392 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4767 : }
4768 196 : pubrinfo[j].pubrattrs = attribs->data;
4769 196 : free(attribs); /* but not attribs->data */
4770 196 : free(attnames);
4771 : }
4772 : else
4773 490 : pubrinfo[j].pubrattrs = NULL;
4774 :
4775 : /* Decide whether we want to dump it */
4776 686 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4777 :
4778 686 : j++;
4779 : }
4780 :
4781 354 : PQclear(res);
4782 354 : destroyPQExpBuffer(query);
4783 : }
4784 :
4785 : /*
4786 : * dumpPublicationNamespace
4787 : * dump the definition of the given publication schema mapping.
4788 : */
4789 : static void
4790 164 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4791 : {
4792 164 : DumpOptions *dopt = fout->dopt;
4793 164 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4794 164 : PublicationInfo *pubinfo = pubsinfo->publication;
4795 : PQExpBuffer query;
4796 : char *tag;
4797 :
4798 : /* Do nothing if not dumping schema */
4799 164 : if (!dopt->dumpSchema)
4800 24 : return;
4801 :
4802 140 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4803 :
4804 140 : query = createPQExpBuffer();
4805 :
4806 140 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4807 140 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4808 :
4809 : /*
4810 : * There is no point in creating drop query as the drop is done by schema
4811 : * drop.
4812 : */
4813 140 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4814 140 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4815 140 : ARCHIVE_OPTS(.tag = tag,
4816 : .namespace = schemainfo->dobj.name,
4817 : .owner = pubinfo->rolname,
4818 : .description = "PUBLICATION TABLES IN SCHEMA",
4819 : .section = SECTION_POST_DATA,
4820 : .createStmt = query->data));
4821 :
4822 : /* These objects can't currently have comments or seclabels */
4823 :
4824 140 : free(tag);
4825 140 : destroyPQExpBuffer(query);
4826 : }
4827 :
4828 : /*
4829 : * dumpPublicationTable
4830 : * dump the definition of the given publication table mapping
4831 : */
4832 : static void
4833 574 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4834 : {
4835 574 : DumpOptions *dopt = fout->dopt;
4836 574 : PublicationInfo *pubinfo = pubrinfo->publication;
4837 574 : TableInfo *tbinfo = pubrinfo->pubtable;
4838 : PQExpBuffer query;
4839 : char *tag;
4840 :
4841 : /* Do nothing if not dumping schema */
4842 574 : if (!dopt->dumpSchema)
4843 84 : return;
4844 :
4845 490 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4846 :
4847 490 : query = createPQExpBuffer();
4848 :
4849 490 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4850 490 : fmtId(pubinfo->dobj.name));
4851 490 : appendPQExpBuffer(query, " %s",
4852 490 : fmtQualifiedDumpable(tbinfo));
4853 :
4854 490 : if (pubrinfo->pubrattrs)
4855 140 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4856 :
4857 490 : if (pubrinfo->pubrelqual)
4858 : {
4859 : /*
4860 : * It's necessary to add parentheses around the expression because
4861 : * pg_get_expr won't supply the parentheses for things like WHERE
4862 : * TRUE.
4863 : */
4864 210 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4865 : }
4866 490 : appendPQExpBufferStr(query, ";\n");
4867 :
4868 : /*
4869 : * There is no point in creating a drop query as the drop is done by table
4870 : * drop. (If you think to change this, see also _printTocEntry().)
4871 : * Although this object doesn't really have ownership as such, set the
4872 : * owner field anyway to ensure that the command is run by the correct
4873 : * role at restore time.
4874 : */
4875 490 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4876 490 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4877 490 : ARCHIVE_OPTS(.tag = tag,
4878 : .namespace = tbinfo->dobj.namespace->dobj.name,
4879 : .owner = pubinfo->rolname,
4880 : .description = "PUBLICATION TABLE",
4881 : .section = SECTION_POST_DATA,
4882 : .createStmt = query->data));
4883 :
4884 : /* These objects can't currently have comments or seclabels */
4885 :
4886 490 : free(tag);
4887 490 : destroyPQExpBuffer(query);
4888 : }
4889 :
4890 : /*
4891 : * Is the currently connected user a superuser?
4892 : */
4893 : static bool
4894 354 : is_superuser(Archive *fout)
4895 : {
4896 354 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4897 : const char *val;
4898 :
4899 354 : val = PQparameterStatus(AH->connection, "is_superuser");
4900 :
4901 354 : if (val && strcmp(val, "on") == 0)
4902 348 : return true;
4903 :
4904 6 : return false;
4905 : }
4906 :
4907 : /*
4908 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4909 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4910 : * the setting query is effective only where available.
4911 : */
4912 : static void
4913 422 : set_restrict_relation_kind(Archive *AH, const char *value)
4914 : {
4915 422 : PQExpBuffer query = createPQExpBuffer();
4916 : PGresult *res;
4917 :
4918 422 : appendPQExpBuffer(query,
4919 : "SELECT set_config(name, '%s', false) "
4920 : "FROM pg_settings "
4921 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4922 : value);
4923 422 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4924 :
4925 422 : PQclear(res);
4926 422 : destroyPQExpBuffer(query);
4927 422 : }
4928 :
4929 : /*
4930 : * getSubscriptions
4931 : * get information about subscriptions
4932 : */
4933 : void
4934 354 : getSubscriptions(Archive *fout)
4935 : {
4936 354 : DumpOptions *dopt = fout->dopt;
4937 : PQExpBuffer query;
4938 : PGresult *res;
4939 : SubscriptionInfo *subinfo;
4940 : int i_tableoid;
4941 : int i_oid;
4942 : int i_subname;
4943 : int i_subowner;
4944 : int i_subbinary;
4945 : int i_substream;
4946 : int i_subtwophasestate;
4947 : int i_subdisableonerr;
4948 : int i_subpasswordrequired;
4949 : int i_subrunasowner;
4950 : int i_subconninfo;
4951 : int i_subslotname;
4952 : int i_subsynccommit;
4953 : int i_subpublications;
4954 : int i_suborigin;
4955 : int i_suboriginremotelsn;
4956 : int i_subenabled;
4957 : int i_subfailover;
4958 : int i,
4959 : ntups;
4960 :
4961 354 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4962 0 : return;
4963 :
4964 354 : if (!is_superuser(fout))
4965 : {
4966 : int n;
4967 :
4968 6 : res = ExecuteSqlQuery(fout,
4969 : "SELECT count(*) FROM pg_subscription "
4970 : "WHERE subdbid = (SELECT oid FROM pg_database"
4971 : " WHERE datname = current_database())",
4972 : PGRES_TUPLES_OK);
4973 6 : n = atoi(PQgetvalue(res, 0, 0));
4974 6 : if (n > 0)
4975 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4976 6 : PQclear(res);
4977 6 : return;
4978 : }
4979 :
4980 348 : query = createPQExpBuffer();
4981 :
4982 : /* Get the subscriptions in current database. */
4983 348 : appendPQExpBufferStr(query,
4984 : "SELECT s.tableoid, s.oid, s.subname,\n"
4985 : " s.subowner,\n"
4986 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4987 : " s.subpublications,\n");
4988 :
4989 348 : if (fout->remoteVersion >= 140000)
4990 348 : appendPQExpBufferStr(query, " s.subbinary,\n");
4991 : else
4992 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4993 :
4994 348 : if (fout->remoteVersion >= 140000)
4995 348 : appendPQExpBufferStr(query, " s.substream,\n");
4996 : else
4997 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
4998 :
4999 348 : if (fout->remoteVersion >= 150000)
5000 348 : appendPQExpBufferStr(query,
5001 : " s.subtwophasestate,\n"
5002 : " s.subdisableonerr,\n");
5003 : else
5004 0 : appendPQExpBuffer(query,
5005 : " '%c' AS subtwophasestate,\n"
5006 : " false AS subdisableonerr,\n",
5007 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5008 :
5009 348 : if (fout->remoteVersion >= 160000)
5010 348 : appendPQExpBufferStr(query,
5011 : " s.subpasswordrequired,\n"
5012 : " s.subrunasowner,\n"
5013 : " s.suborigin,\n");
5014 : else
5015 0 : appendPQExpBuffer(query,
5016 : " 't' AS subpasswordrequired,\n"
5017 : " 't' AS subrunasowner,\n"
5018 : " '%s' AS suborigin,\n",
5019 : LOGICALREP_ORIGIN_ANY);
5020 :
5021 348 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5022 62 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5023 : " s.subenabled,\n");
5024 : else
5025 286 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5026 : " false AS subenabled,\n");
5027 :
5028 348 : if (fout->remoteVersion >= 170000)
5029 348 : appendPQExpBufferStr(query,
5030 : " s.subfailover\n");
5031 : else
5032 0 : appendPQExpBuffer(query,
5033 : " false AS subfailover\n");
5034 :
5035 348 : appendPQExpBufferStr(query,
5036 : "FROM pg_subscription s\n");
5037 :
5038 348 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5039 62 : appendPQExpBufferStr(query,
5040 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5041 : " ON o.external_id = 'pg_' || s.oid::text \n");
5042 :
5043 348 : appendPQExpBufferStr(query,
5044 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5045 : " WHERE datname = current_database())");
5046 :
5047 348 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5048 :
5049 348 : ntups = PQntuples(res);
5050 :
5051 : /*
5052 : * Get subscription fields. We don't include subskiplsn in the dump as
5053 : * after restoring the dump this value may no longer be relevant.
5054 : */
5055 348 : i_tableoid = PQfnumber(res, "tableoid");
5056 348 : i_oid = PQfnumber(res, "oid");
5057 348 : i_subname = PQfnumber(res, "subname");
5058 348 : i_subowner = PQfnumber(res, "subowner");
5059 348 : i_subenabled = PQfnumber(res, "subenabled");
5060 348 : i_subbinary = PQfnumber(res, "subbinary");
5061 348 : i_substream = PQfnumber(res, "substream");
5062 348 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5063 348 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5064 348 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5065 348 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5066 348 : i_subfailover = PQfnumber(res, "subfailover");
5067 348 : i_subconninfo = PQfnumber(res, "subconninfo");
5068 348 : i_subslotname = PQfnumber(res, "subslotname");
5069 348 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5070 348 : i_subpublications = PQfnumber(res, "subpublications");
5071 348 : i_suborigin = PQfnumber(res, "suborigin");
5072 348 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5073 :
5074 348 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5075 :
5076 634 : for (i = 0; i < ntups; i++)
5077 : {
5078 286 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5079 286 : subinfo[i].dobj.catId.tableoid =
5080 286 : atooid(PQgetvalue(res, i, i_tableoid));
5081 286 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5082 286 : AssignDumpId(&subinfo[i].dobj);
5083 286 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5084 286 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5085 :
5086 286 : subinfo[i].subenabled =
5087 286 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5088 286 : subinfo[i].subbinary =
5089 286 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5090 286 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5091 286 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5092 286 : subinfo[i].subdisableonerr =
5093 286 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5094 286 : subinfo[i].subpasswordrequired =
5095 286 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5096 286 : subinfo[i].subrunasowner =
5097 286 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5098 286 : subinfo[i].subfailover =
5099 286 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5100 572 : subinfo[i].subconninfo =
5101 286 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5102 286 : if (PQgetisnull(res, i, i_subslotname))
5103 0 : subinfo[i].subslotname = NULL;
5104 : else
5105 286 : subinfo[i].subslotname =
5106 286 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5107 572 : subinfo[i].subsynccommit =
5108 286 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5109 572 : subinfo[i].subpublications =
5110 286 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5111 286 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5112 286 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5113 284 : subinfo[i].suboriginremotelsn = NULL;
5114 : else
5115 2 : subinfo[i].suboriginremotelsn =
5116 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5117 :
5118 : /* Decide whether we want to dump it */
5119 286 : selectDumpableObject(&(subinfo[i].dobj), fout);
5120 : }
5121 348 : PQclear(res);
5122 :
5123 348 : destroyPQExpBuffer(query);
5124 : }
5125 :
5126 : /*
5127 : * getSubscriptionTables
5128 : * Get information about subscription membership for dumpable tables. This
5129 : * will be used only in binary-upgrade mode for PG17 or later versions.
5130 : */
5131 : void
5132 354 : getSubscriptionTables(Archive *fout)
5133 : {
5134 354 : DumpOptions *dopt = fout->dopt;
5135 354 : SubscriptionInfo *subinfo = NULL;
5136 : SubRelInfo *subrinfo;
5137 : PGresult *res;
5138 : int i_srsubid;
5139 : int i_srrelid;
5140 : int i_srsubstate;
5141 : int i_srsublsn;
5142 : int ntups;
5143 354 : Oid last_srsubid = InvalidOid;
5144 :
5145 354 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5146 62 : fout->remoteVersion < 170000)
5147 292 : return;
5148 :
5149 62 : res = ExecuteSqlQuery(fout,
5150 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5151 : "FROM pg_catalog.pg_subscription_rel "
5152 : "ORDER BY srsubid",
5153 : PGRES_TUPLES_OK);
5154 62 : ntups = PQntuples(res);
5155 62 : if (ntups == 0)
5156 60 : goto cleanup;
5157 :
5158 : /* Get pg_subscription_rel attributes */
5159 2 : i_srsubid = PQfnumber(res, "srsubid");
5160 2 : i_srrelid = PQfnumber(res, "srrelid");
5161 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5162 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5163 :
5164 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5165 6 : for (int i = 0; i < ntups; i++)
5166 : {
5167 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5168 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5169 : TableInfo *tblinfo;
5170 :
5171 : /*
5172 : * If we switched to a new subscription, check if the subscription
5173 : * exists.
5174 : */
5175 4 : if (cur_srsubid != last_srsubid)
5176 : {
5177 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5178 4 : if (subinfo == NULL)
5179 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5180 :
5181 4 : last_srsubid = cur_srsubid;
5182 : }
5183 :
5184 4 : tblinfo = findTableByOid(relid);
5185 4 : if (tblinfo == NULL)
5186 0 : pg_fatal("failed sanity check, table with OID %u not found",
5187 : relid);
5188 :
5189 : /* OK, make a DumpableObject for this relationship */
5190 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5191 4 : subrinfo[i].dobj.catId.tableoid = relid;
5192 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5193 4 : AssignDumpId(&subrinfo[i].dobj);
5194 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5195 4 : subrinfo[i].tblinfo = tblinfo;
5196 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5197 4 : if (PQgetisnull(res, i, i_srsublsn))
5198 2 : subrinfo[i].srsublsn = NULL;
5199 : else
5200 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5201 :
5202 4 : subrinfo[i].subinfo = subinfo;
5203 :
5204 : /* Decide whether we want to dump it */
5205 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5206 : }
5207 :
5208 2 : cleanup:
5209 62 : PQclear(res);
5210 : }
5211 :
5212 : /*
5213 : * dumpSubscriptionTable
5214 : * Dump the definition of the given subscription table mapping. This will be
5215 : * used only in binary-upgrade mode for PG17 or later versions.
5216 : */
5217 : static void
5218 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5219 : {
5220 4 : DumpOptions *dopt = fout->dopt;
5221 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5222 : PQExpBuffer query;
5223 : char *tag;
5224 :
5225 : /* Do nothing if not dumping schema */
5226 4 : if (!dopt->dumpSchema)
5227 0 : return;
5228 :
5229 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5230 :
5231 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5232 :
5233 4 : query = createPQExpBuffer();
5234 :
5235 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5236 : {
5237 : /*
5238 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5239 : * to pg_subscription_rel table. This will be used only in
5240 : * binary-upgrade mode.
5241 : */
5242 4 : appendPQExpBufferStr(query,
5243 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5244 4 : appendPQExpBufferStr(query,
5245 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5246 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5247 4 : appendPQExpBuffer(query,
5248 : ", %u, '%c'",
5249 4 : subrinfo->tblinfo->dobj.catId.oid,
5250 4 : subrinfo->srsubstate);
5251 :
5252 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5253 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5254 : else
5255 2 : appendPQExpBuffer(query, ", NULL");
5256 :
5257 4 : appendPQExpBufferStr(query, ");\n");
5258 : }
5259 :
5260 : /*
5261 : * There is no point in creating a drop query as the drop is done by table
5262 : * drop. (If you think to change this, see also _printTocEntry().)
5263 : * Although this object doesn't really have ownership as such, set the
5264 : * owner field anyway to ensure that the command is run by the correct
5265 : * role at restore time.
5266 : */
5267 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5268 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5269 4 : ARCHIVE_OPTS(.tag = tag,
5270 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5271 : .owner = subinfo->rolname,
5272 : .description = "SUBSCRIPTION TABLE",
5273 : .section = SECTION_POST_DATA,
5274 : .createStmt = query->data));
5275 :
5276 : /* These objects can't currently have comments or seclabels */
5277 :
5278 4 : free(tag);
5279 4 : destroyPQExpBuffer(query);
5280 : }
5281 :
5282 : /*
5283 : * dumpSubscription
5284 : * dump the definition of the given subscription
5285 : */
5286 : static void
5287 250 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5288 : {
5289 250 : DumpOptions *dopt = fout->dopt;
5290 : PQExpBuffer delq;
5291 : PQExpBuffer query;
5292 : PQExpBuffer publications;
5293 : char *qsubname;
5294 250 : char **pubnames = NULL;
5295 250 : int npubnames = 0;
5296 : int i;
5297 :
5298 : /* Do nothing if not dumping schema */
5299 250 : if (!dopt->dumpSchema)
5300 36 : return;
5301 :
5302 214 : delq = createPQExpBuffer();
5303 214 : query = createPQExpBuffer();
5304 :
5305 214 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5306 :
5307 214 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5308 : qsubname);
5309 :
5310 214 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5311 : qsubname);
5312 214 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5313 :
5314 : /* Build list of quoted publications and append them to query. */
5315 214 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5316 0 : pg_fatal("could not parse %s array", "subpublications");
5317 :
5318 214 : publications = createPQExpBuffer();
5319 428 : for (i = 0; i < npubnames; i++)
5320 : {
5321 214 : if (i > 0)
5322 0 : appendPQExpBufferStr(publications, ", ");
5323 :
5324 214 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5325 : }
5326 :
5327 214 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5328 214 : if (subinfo->subslotname)
5329 214 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5330 : else
5331 0 : appendPQExpBufferStr(query, "NONE");
5332 :
5333 214 : if (subinfo->subbinary)
5334 0 : appendPQExpBufferStr(query, ", binary = true");
5335 :
5336 214 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5337 70 : appendPQExpBufferStr(query, ", streaming = on");
5338 144 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5339 74 : appendPQExpBufferStr(query, ", streaming = parallel");
5340 : else
5341 70 : appendPQExpBufferStr(query, ", streaming = off");
5342 :
5343 214 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5344 0 : appendPQExpBufferStr(query, ", two_phase = on");
5345 :
5346 214 : if (subinfo->subdisableonerr)
5347 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5348 :
5349 214 : if (!subinfo->subpasswordrequired)
5350 0 : appendPQExpBuffer(query, ", password_required = false");
5351 :
5352 214 : if (subinfo->subrunasowner)
5353 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5354 :
5355 214 : if (subinfo->subfailover)
5356 2 : appendPQExpBufferStr(query, ", failover = true");
5357 :
5358 214 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5359 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5360 :
5361 214 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5362 70 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5363 :
5364 214 : appendPQExpBufferStr(query, ");\n");
5365 :
5366 : /*
5367 : * In binary-upgrade mode, we allow the replication to continue after the
5368 : * upgrade.
5369 : */
5370 214 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5371 : {
5372 10 : if (subinfo->suboriginremotelsn)
5373 : {
5374 : /*
5375 : * Preserve the remote_lsn for the subscriber's replication
5376 : * origin. This value is required to start the replication from
5377 : * the position before the upgrade. This value will be stale if
5378 : * the publisher gets upgraded before the subscriber node.
5379 : * However, this shouldn't be a problem as the upgrade of the
5380 : * publisher ensures that all the transactions were replicated
5381 : * before upgrading it.
5382 : */
5383 2 : appendPQExpBufferStr(query,
5384 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5385 2 : appendPQExpBufferStr(query,
5386 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5387 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5388 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5389 : }
5390 :
5391 10 : if (subinfo->subenabled)
5392 : {
5393 : /*
5394 : * Enable the subscription to allow the replication to continue
5395 : * after the upgrade.
5396 : */
5397 2 : appendPQExpBufferStr(query,
5398 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5399 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5400 : }
5401 : }
5402 :
5403 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5404 214 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5405 214 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5406 : .owner = subinfo->rolname,
5407 : .description = "SUBSCRIPTION",
5408 : .section = SECTION_POST_DATA,
5409 : .createStmt = query->data,
5410 : .dropStmt = delq->data));
5411 :
5412 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5413 70 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5414 : NULL, subinfo->rolname,
5415 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5416 :
5417 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5418 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5419 : NULL, subinfo->rolname,
5420 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5421 :
5422 214 : destroyPQExpBuffer(publications);
5423 214 : free(pubnames);
5424 :
5425 214 : destroyPQExpBuffer(delq);
5426 214 : destroyPQExpBuffer(query);
5427 214 : free(qsubname);
5428 : }
5429 :
5430 : /*
5431 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5432 : * the object needs.
5433 : */
5434 : static void
5435 10472 : append_depends_on_extension(Archive *fout,
5436 : PQExpBuffer create,
5437 : const DumpableObject *dobj,
5438 : const char *catalog,
5439 : const char *keyword,
5440 : const char *objname)
5441 : {
5442 10472 : if (dobj->depends_on_ext)
5443 : {
5444 : char *nm;
5445 : PGresult *res;
5446 : PQExpBuffer query;
5447 : int ntups;
5448 : int i_extname;
5449 : int i;
5450 :
5451 : /* dodge fmtId() non-reentrancy */
5452 84 : nm = pg_strdup(objname);
5453 :
5454 84 : query = createPQExpBuffer();
5455 84 : appendPQExpBuffer(query,
5456 : "SELECT e.extname "
5457 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5458 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5459 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5460 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5461 : catalog,
5462 : dobj->catId.oid);
5463 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5464 84 : ntups = PQntuples(res);
5465 84 : i_extname = PQfnumber(res, "extname");
5466 168 : for (i = 0; i < ntups; i++)
5467 : {
5468 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5469 : keyword, nm,
5470 84 : fmtId(PQgetvalue(res, i, i_extname)));
5471 : }
5472 :
5473 84 : PQclear(res);
5474 84 : destroyPQExpBuffer(query);
5475 84 : pg_free(nm);
5476 : }
5477 10472 : }
5478 :
5479 : static Oid
5480 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5481 : {
5482 : /*
5483 : * If the old version didn't assign an array type, but the new version
5484 : * does, we must select an unused type OID to assign. This currently only
5485 : * happens for domains, when upgrading pre-v11 to v11 and up.
5486 : *
5487 : * Note: local state here is kind of ugly, but we must have some, since we
5488 : * mustn't choose the same unused OID more than once.
5489 : */
5490 : static Oid next_possible_free_oid = FirstNormalObjectId;
5491 : PGresult *res;
5492 : bool is_dup;
5493 :
5494 : do
5495 : {
5496 0 : ++next_possible_free_oid;
5497 0 : printfPQExpBuffer(upgrade_query,
5498 : "SELECT EXISTS(SELECT 1 "
5499 : "FROM pg_catalog.pg_type "
5500 : "WHERE oid = '%u'::pg_catalog.oid);",
5501 : next_possible_free_oid);
5502 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5503 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5504 0 : PQclear(res);
5505 0 : } while (is_dup);
5506 :
5507 0 : return next_possible_free_oid;
5508 : }
5509 :
5510 : static void
5511 1788 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5512 : PQExpBuffer upgrade_buffer,
5513 : Oid pg_type_oid,
5514 : bool force_array_type,
5515 : bool include_multirange_type)
5516 : {
5517 1788 : PQExpBuffer upgrade_query = createPQExpBuffer();
5518 : PGresult *res;
5519 : Oid pg_type_array_oid;
5520 : Oid pg_type_multirange_oid;
5521 : Oid pg_type_multirange_array_oid;
5522 : TypeInfo *tinfo;
5523 :
5524 1788 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5525 1788 : appendPQExpBuffer(upgrade_buffer,
5526 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5527 : pg_type_oid);
5528 :
5529 1788 : tinfo = findTypeByOid(pg_type_oid);
5530 1788 : if (tinfo)
5531 1788 : pg_type_array_oid = tinfo->typarray;
5532 : else
5533 0 : pg_type_array_oid = InvalidOid;
5534 :
5535 1788 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5536 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5537 :
5538 1788 : if (OidIsValid(pg_type_array_oid))
5539 : {
5540 1784 : appendPQExpBufferStr(upgrade_buffer,
5541 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5542 1784 : appendPQExpBuffer(upgrade_buffer,
5543 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5544 : pg_type_array_oid);
5545 : }
5546 :
5547 : /*
5548 : * Pre-set the multirange type oid and its own array type oid.
5549 : */
5550 1788 : if (include_multirange_type)
5551 : {
5552 16 : if (fout->remoteVersion >= 140000)
5553 : {
5554 16 : printfPQExpBuffer(upgrade_query,
5555 : "SELECT t.oid, t.typarray "
5556 : "FROM pg_catalog.pg_type t "
5557 : "JOIN pg_catalog.pg_range r "
5558 : "ON t.oid = r.rngmultitypid "
5559 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5560 : pg_type_oid);
5561 :
5562 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5563 :
5564 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5565 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5566 :
5567 16 : PQclear(res);
5568 : }
5569 : else
5570 : {
5571 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5572 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5573 : }
5574 :
5575 16 : appendPQExpBufferStr(upgrade_buffer,
5576 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5577 16 : appendPQExpBuffer(upgrade_buffer,
5578 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5579 : pg_type_multirange_oid);
5580 16 : appendPQExpBufferStr(upgrade_buffer,
5581 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5582 16 : appendPQExpBuffer(upgrade_buffer,
5583 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5584 : pg_type_multirange_array_oid);
5585 : }
5586 :
5587 1788 : destroyPQExpBuffer(upgrade_query);
5588 1788 : }
5589 :
5590 : static void
5591 1646 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5592 : PQExpBuffer upgrade_buffer,
5593 : const TableInfo *tbinfo)
5594 : {
5595 1646 : Oid pg_type_oid = tbinfo->reltype;
5596 :
5597 1646 : if (OidIsValid(pg_type_oid))
5598 1646 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5599 : pg_type_oid, false, false);
5600 1646 : }
5601 :
5602 : /*
5603 : * bsearch() comparator for BinaryUpgradeClassOidItem
5604 : */
5605 : static int
5606 23762 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5607 : {
5608 23762 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5609 23762 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5610 :
5611 23762 : return pg_cmp_u32(v1.oid, v2.oid);
5612 : }
5613 :
5614 : /*
5615 : * collectBinaryUpgradeClassOids
5616 : *
5617 : * Construct a table of pg_class information required for
5618 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5619 : * lookup.
5620 : */
5621 : static void
5622 62 : collectBinaryUpgradeClassOids(Archive *fout)
5623 : {
5624 : PGresult *res;
5625 : const char *query;
5626 :
5627 62 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5628 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5629 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5630 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5631 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5632 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5633 : "ORDER BY c.oid;";
5634 :
5635 62 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5636 :
5637 62 : nbinaryUpgradeClassOids = PQntuples(res);
5638 62 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5639 62 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5640 :
5641 29306 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5642 : {
5643 29244 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5644 29244 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5645 29244 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5646 29244 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5647 29244 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5648 29244 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5649 29244 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5650 : }
5651 :
5652 62 : PQclear(res);
5653 62 : }
5654 :
5655 : static void
5656 2410 : binary_upgrade_set_pg_class_oids(Archive *fout,
5657 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5658 : {
5659 2410 : BinaryUpgradeClassOidItem key = {0};
5660 : BinaryUpgradeClassOidItem *entry;
5661 :
5662 : Assert(binaryUpgradeClassOids);
5663 :
5664 : /*
5665 : * Preserve the OID and relfilenumber of the table, table's index, table's
5666 : * toast table and toast table's index if any.
5667 : *
5668 : * One complexity is that the current table definition might not require
5669 : * the creation of a TOAST table, but the old database might have a TOAST
5670 : * table that was created earlier, before some wide columns were dropped.
5671 : * By setting the TOAST oid we force creation of the TOAST heap and index
5672 : * by the new backend, so we can copy the files during binary upgrade
5673 : * without worrying about this case.
5674 : */
5675 2410 : key.oid = pg_class_oid;
5676 2410 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5677 : sizeof(BinaryUpgradeClassOidItem),
5678 : BinaryUpgradeClassOidItemCmp);
5679 :
5680 2410 : appendPQExpBufferStr(upgrade_buffer,
5681 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5682 :
5683 2410 : if (entry->relkind != RELKIND_INDEX &&
5684 1864 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5685 : {
5686 1814 : appendPQExpBuffer(upgrade_buffer,
5687 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5688 : pg_class_oid);
5689 :
5690 : /*
5691 : * Not every relation has storage. Also, in a pre-v12 database,
5692 : * partitioned tables have a relfilenumber, which should not be
5693 : * preserved when upgrading.
5694 : */
5695 1814 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5696 1498 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5697 1498 : appendPQExpBuffer(upgrade_buffer,
5698 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5699 : entry->relfilenumber);
5700 :
5701 : /*
5702 : * In a pre-v12 database, partitioned tables might be marked as having
5703 : * toast tables, but we should ignore them if so.
5704 : */
5705 1814 : if (OidIsValid(entry->toast_oid) &&
5706 552 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5707 : {
5708 552 : appendPQExpBuffer(upgrade_buffer,
5709 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5710 : entry->toast_oid);
5711 552 : appendPQExpBuffer(upgrade_buffer,
5712 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5713 : entry->toast_relfilenumber);
5714 :
5715 : /* every toast table has an index */
5716 552 : appendPQExpBuffer(upgrade_buffer,
5717 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5718 : entry->toast_index_oid);
5719 552 : appendPQExpBuffer(upgrade_buffer,
5720 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5721 : entry->toast_index_relfilenumber);
5722 : }
5723 : }
5724 : else
5725 : {
5726 : /* Preserve the OID and relfilenumber of the index */
5727 596 : appendPQExpBuffer(upgrade_buffer,
5728 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5729 : pg_class_oid);
5730 596 : appendPQExpBuffer(upgrade_buffer,
5731 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5732 : entry->relfilenumber);
5733 : }
5734 :
5735 2410 : appendPQExpBufferChar(upgrade_buffer, '\n');
5736 2410 : }
5737 :
5738 : /*
5739 : * If the DumpableObject is a member of an extension, add a suitable
5740 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5741 : *
5742 : * For somewhat historical reasons, objname should already be quoted,
5743 : * but not objnamespace (if any).
5744 : */
5745 : static void
5746 2880 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5747 : const DumpableObject *dobj,
5748 : const char *objtype,
5749 : const char *objname,
5750 : const char *objnamespace)
5751 : {
5752 2880 : DumpableObject *extobj = NULL;
5753 : int i;
5754 :
5755 2880 : if (!dobj->ext_member)
5756 2848 : return;
5757 :
5758 : /*
5759 : * Find the parent extension. We could avoid this search if we wanted to
5760 : * add a link field to DumpableObject, but the space costs of that would
5761 : * be considerable. We assume that member objects could only have a
5762 : * direct dependency on their own extension, not any others.
5763 : */
5764 32 : for (i = 0; i < dobj->nDeps; i++)
5765 : {
5766 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5767 32 : if (extobj && extobj->objType == DO_EXTENSION)
5768 32 : break;
5769 0 : extobj = NULL;
5770 : }
5771 32 : if (extobj == NULL)
5772 0 : pg_fatal("could not find parent extension for %s %s",
5773 : objtype, objname);
5774 :
5775 32 : appendPQExpBufferStr(upgrade_buffer,
5776 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5777 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5778 32 : fmtId(extobj->name),
5779 : objtype);
5780 32 : if (objnamespace && *objnamespace)
5781 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5782 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5783 : }
5784 :
5785 : /*
5786 : * getNamespaces:
5787 : * get information about all namespaces in the system catalogs
5788 : */
5789 : void
5790 356 : getNamespaces(Archive *fout)
5791 : {
5792 : PGresult *res;
5793 : int ntups;
5794 : int i;
5795 : PQExpBuffer query;
5796 : NamespaceInfo *nsinfo;
5797 : int i_tableoid;
5798 : int i_oid;
5799 : int i_nspname;
5800 : int i_nspowner;
5801 : int i_nspacl;
5802 : int i_acldefault;
5803 :
5804 356 : query = createPQExpBuffer();
5805 :
5806 : /*
5807 : * we fetch all namespaces including system ones, so that every object we
5808 : * read in can be linked to a containing namespace.
5809 : */
5810 356 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5811 : "n.nspowner, "
5812 : "n.nspacl, "
5813 : "acldefault('n', n.nspowner) AS acldefault "
5814 : "FROM pg_namespace n");
5815 :
5816 356 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5817 :
5818 356 : ntups = PQntuples(res);
5819 :
5820 356 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5821 :
5822 356 : i_tableoid = PQfnumber(res, "tableoid");
5823 356 : i_oid = PQfnumber(res, "oid");
5824 356 : i_nspname = PQfnumber(res, "nspname");
5825 356 : i_nspowner = PQfnumber(res, "nspowner");
5826 356 : i_nspacl = PQfnumber(res, "nspacl");
5827 356 : i_acldefault = PQfnumber(res, "acldefault");
5828 :
5829 3072 : for (i = 0; i < ntups; i++)
5830 : {
5831 : const char *nspowner;
5832 :
5833 2716 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5834 2716 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5835 2716 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5836 2716 : AssignDumpId(&nsinfo[i].dobj);
5837 2716 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5838 2716 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5839 2716 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5840 2716 : nsinfo[i].dacl.privtype = 0;
5841 2716 : nsinfo[i].dacl.initprivs = NULL;
5842 2716 : nspowner = PQgetvalue(res, i, i_nspowner);
5843 2716 : nsinfo[i].nspowner = atooid(nspowner);
5844 2716 : nsinfo[i].rolname = getRoleName(nspowner);
5845 :
5846 : /* Decide whether to dump this namespace */
5847 2716 : selectDumpableNamespace(&nsinfo[i], fout);
5848 :
5849 : /* Mark whether namespace has an ACL */
5850 2716 : if (!PQgetisnull(res, i, i_nspacl))
5851 1200 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5852 :
5853 : /*
5854 : * We ignore any pg_init_privs.initprivs entry for the public schema
5855 : * and assume a predetermined default, for several reasons. First,
5856 : * dropping and recreating the schema removes its pg_init_privs entry,
5857 : * but an empty destination database starts with this ACL nonetheless.
5858 : * Second, we support dump/reload of public schema ownership changes.
5859 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5860 : * initprivs continues to reflect the initial owner. Hence,
5861 : * synthesize the value that nspacl will have after the restore's
5862 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5863 : * match the source's ACL, even if the latter was an initdb-default
5864 : * ACL, which changed in v15. An upgrade pulls in changes to most
5865 : * system object ACLs that the DBA had not customized. We've made the
5866 : * public schema depart from that, because changing its ACL so easily
5867 : * breaks applications.
5868 : */
5869 2716 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5870 : {
5871 348 : PQExpBuffer aclarray = createPQExpBuffer();
5872 348 : PQExpBuffer aclitem = createPQExpBuffer();
5873 :
5874 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5875 348 : appendPQExpBufferChar(aclarray, '{');
5876 348 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5877 348 : appendPQExpBufferStr(aclitem, "=UC/");
5878 348 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5879 348 : appendPGArray(aclarray, aclitem->data);
5880 348 : resetPQExpBuffer(aclitem);
5881 348 : appendPQExpBufferStr(aclitem, "=U/");
5882 348 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5883 348 : appendPGArray(aclarray, aclitem->data);
5884 348 : appendPQExpBufferChar(aclarray, '}');
5885 :
5886 348 : nsinfo[i].dacl.privtype = 'i';
5887 348 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5888 348 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5889 :
5890 348 : destroyPQExpBuffer(aclarray);
5891 348 : destroyPQExpBuffer(aclitem);
5892 : }
5893 : }
5894 :
5895 356 : PQclear(res);
5896 356 : destroyPQExpBuffer(query);
5897 356 : }
5898 :
5899 : /*
5900 : * findNamespace:
5901 : * given a namespace OID, look up the info read by getNamespaces
5902 : */
5903 : static NamespaceInfo *
5904 1105422 : findNamespace(Oid nsoid)
5905 : {
5906 : NamespaceInfo *nsinfo;
5907 :
5908 1105422 : nsinfo = findNamespaceByOid(nsoid);
5909 1105422 : if (nsinfo == NULL)
5910 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5911 1105422 : return nsinfo;
5912 : }
5913 :
5914 : /*
5915 : * getExtensions:
5916 : * read all extensions in the system catalogs and return them in the
5917 : * ExtensionInfo* structure
5918 : *
5919 : * numExtensions is set to the number of extensions read in
5920 : */
5921 : ExtensionInfo *
5922 356 : getExtensions(Archive *fout, int *numExtensions)
5923 : {
5924 356 : DumpOptions *dopt = fout->dopt;
5925 : PGresult *res;
5926 : int ntups;
5927 : int i;
5928 : PQExpBuffer query;
5929 356 : ExtensionInfo *extinfo = NULL;
5930 : int i_tableoid;
5931 : int i_oid;
5932 : int i_extname;
5933 : int i_nspname;
5934 : int i_extrelocatable;
5935 : int i_extversion;
5936 : int i_extconfig;
5937 : int i_extcondition;
5938 :
5939 356 : query = createPQExpBuffer();
5940 :
5941 356 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5942 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5943 : "FROM pg_extension x "
5944 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5945 :
5946 356 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5947 :
5948 356 : ntups = PQntuples(res);
5949 356 : if (ntups == 0)
5950 0 : goto cleanup;
5951 :
5952 356 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5953 :
5954 356 : i_tableoid = PQfnumber(res, "tableoid");
5955 356 : i_oid = PQfnumber(res, "oid");
5956 356 : i_extname = PQfnumber(res, "extname");
5957 356 : i_nspname = PQfnumber(res, "nspname");
5958 356 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5959 356 : i_extversion = PQfnumber(res, "extversion");
5960 356 : i_extconfig = PQfnumber(res, "extconfig");
5961 356 : i_extcondition = PQfnumber(res, "extcondition");
5962 :
5963 762 : for (i = 0; i < ntups; i++)
5964 : {
5965 406 : extinfo[i].dobj.objType = DO_EXTENSION;
5966 406 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5967 406 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5968 406 : AssignDumpId(&extinfo[i].dobj);
5969 406 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5970 406 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5971 406 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5972 406 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5973 406 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5974 406 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5975 :
5976 : /* Decide whether we want to dump it */
5977 406 : selectDumpableExtension(&(extinfo[i]), dopt);
5978 : }
5979 :
5980 356 : cleanup:
5981 356 : PQclear(res);
5982 356 : destroyPQExpBuffer(query);
5983 :
5984 356 : *numExtensions = ntups;
5985 :
5986 356 : return extinfo;
5987 : }
5988 :
5989 : /*
5990 : * getTypes:
5991 : * get information about all types in the system catalogs
5992 : *
5993 : * NB: this must run after getFuncs() because we assume we can do
5994 : * findFuncByOid().
5995 : */
5996 : void
5997 354 : getTypes(Archive *fout)
5998 : {
5999 : PGresult *res;
6000 : int ntups;
6001 : int i;
6002 354 : PQExpBuffer query = createPQExpBuffer();
6003 : TypeInfo *tyinfo;
6004 : ShellTypeInfo *stinfo;
6005 : int i_tableoid;
6006 : int i_oid;
6007 : int i_typname;
6008 : int i_typnamespace;
6009 : int i_typacl;
6010 : int i_acldefault;
6011 : int i_typowner;
6012 : int i_typelem;
6013 : int i_typrelid;
6014 : int i_typrelkind;
6015 : int i_typtype;
6016 : int i_typisdefined;
6017 : int i_isarray;
6018 : int i_typarray;
6019 :
6020 : /*
6021 : * we include even the built-in types because those may be used as array
6022 : * elements by user-defined types
6023 : *
6024 : * we filter out the built-in types when we dump out the types
6025 : *
6026 : * same approach for undefined (shell) types and array types
6027 : *
6028 : * Note: as of 8.3 we can reliably detect whether a type is an
6029 : * auto-generated array type by checking the element type's typarray.
6030 : * (Before that the test is capable of generating false positives.) We
6031 : * still check for name beginning with '_', though, so as to avoid the
6032 : * cost of the subselect probe for all standard types. This would have to
6033 : * be revisited if the backend ever allows renaming of array types.
6034 : */
6035 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6036 : "typnamespace, typacl, "
6037 : "acldefault('T', typowner) AS acldefault, "
6038 : "typowner, "
6039 : "typelem, typrelid, typarray, "
6040 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6041 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6042 : "typtype, typisdefined, "
6043 : "typname[0] = '_' AND typelem != 0 AND "
6044 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6045 : "FROM pg_type");
6046 :
6047 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6048 :
6049 354 : ntups = PQntuples(res);
6050 :
6051 354 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6052 :
6053 354 : i_tableoid = PQfnumber(res, "tableoid");
6054 354 : i_oid = PQfnumber(res, "oid");
6055 354 : i_typname = PQfnumber(res, "typname");
6056 354 : i_typnamespace = PQfnumber(res, "typnamespace");
6057 354 : i_typacl = PQfnumber(res, "typacl");
6058 354 : i_acldefault = PQfnumber(res, "acldefault");
6059 354 : i_typowner = PQfnumber(res, "typowner");
6060 354 : i_typelem = PQfnumber(res, "typelem");
6061 354 : i_typrelid = PQfnumber(res, "typrelid");
6062 354 : i_typrelkind = PQfnumber(res, "typrelkind");
6063 354 : i_typtype = PQfnumber(res, "typtype");
6064 354 : i_typisdefined = PQfnumber(res, "typisdefined");
6065 354 : i_isarray = PQfnumber(res, "isarray");
6066 354 : i_typarray = PQfnumber(res, "typarray");
6067 :
6068 253880 : for (i = 0; i < ntups; i++)
6069 : {
6070 253526 : tyinfo[i].dobj.objType = DO_TYPE;
6071 253526 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6072 253526 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6073 253526 : AssignDumpId(&tyinfo[i].dobj);
6074 253526 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6075 507052 : tyinfo[i].dobj.namespace =
6076 253526 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6077 253526 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6078 253526 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6079 253526 : tyinfo[i].dacl.privtype = 0;
6080 253526 : tyinfo[i].dacl.initprivs = NULL;
6081 253526 : tyinfo[i].ftypname = NULL; /* may get filled later */
6082 253526 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6083 253526 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6084 253526 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6085 253526 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6086 253526 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6087 253526 : tyinfo[i].shellType = NULL;
6088 :
6089 253526 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6090 253414 : tyinfo[i].isDefined = true;
6091 : else
6092 112 : tyinfo[i].isDefined = false;
6093 :
6094 253526 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6095 121574 : tyinfo[i].isArray = true;
6096 : else
6097 131952 : tyinfo[i].isArray = false;
6098 :
6099 253526 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6100 :
6101 253526 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6102 2404 : tyinfo[i].isMultirange = true;
6103 : else
6104 251122 : tyinfo[i].isMultirange = false;
6105 :
6106 : /* Decide whether we want to dump it */
6107 253526 : selectDumpableType(&tyinfo[i], fout);
6108 :
6109 : /* Mark whether type has an ACL */
6110 253526 : if (!PQgetisnull(res, i, i_typacl))
6111 442 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6112 :
6113 : /*
6114 : * If it's a domain, fetch info about its constraints, if any
6115 : */
6116 253526 : tyinfo[i].nDomChecks = 0;
6117 253526 : tyinfo[i].domChecks = NULL;
6118 253526 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6119 29066 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6120 294 : getDomainConstraints(fout, &(tyinfo[i]));
6121 :
6122 : /*
6123 : * If it's a base type, make a DumpableObject representing a shell
6124 : * definition of the type. We will need to dump that ahead of the I/O
6125 : * functions for the type. Similarly, range types need a shell
6126 : * definition in case they have a canonicalize function.
6127 : *
6128 : * Note: the shell type doesn't have a catId. You might think it
6129 : * should copy the base type's catId, but then it might capture the
6130 : * pg_depend entries for the type, which we don't want.
6131 : */
6132 253526 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6133 29066 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6134 14108 : tyinfo[i].typtype == TYPTYPE_RANGE))
6135 : {
6136 15222 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6137 15222 : stinfo->dobj.objType = DO_SHELL_TYPE;
6138 15222 : stinfo->dobj.catId = nilCatalogId;
6139 15222 : AssignDumpId(&stinfo->dobj);
6140 15222 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6141 15222 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6142 15222 : stinfo->baseType = &(tyinfo[i]);
6143 15222 : tyinfo[i].shellType = stinfo;
6144 :
6145 : /*
6146 : * Initially mark the shell type as not to be dumped. We'll only
6147 : * dump it if the I/O or canonicalize functions need to be dumped;
6148 : * this is taken care of while sorting dependencies.
6149 : */
6150 15222 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6151 : }
6152 : }
6153 :
6154 354 : PQclear(res);
6155 :
6156 354 : destroyPQExpBuffer(query);
6157 354 : }
6158 :
6159 : /*
6160 : * getOperators:
6161 : * get information about all operators in the system catalogs
6162 : */
6163 : void
6164 354 : getOperators(Archive *fout)
6165 : {
6166 : PGresult *res;
6167 : int ntups;
6168 : int i;
6169 354 : PQExpBuffer query = createPQExpBuffer();
6170 : OprInfo *oprinfo;
6171 : int i_tableoid;
6172 : int i_oid;
6173 : int i_oprname;
6174 : int i_oprnamespace;
6175 : int i_oprowner;
6176 : int i_oprkind;
6177 : int i_oprcode;
6178 :
6179 : /*
6180 : * find all operators, including builtin operators; we filter out
6181 : * system-defined operators at dump-out time.
6182 : */
6183 :
6184 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6185 : "oprnamespace, "
6186 : "oprowner, "
6187 : "oprkind, "
6188 : "oprcode::oid AS oprcode "
6189 : "FROM pg_operator");
6190 :
6191 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6192 :
6193 354 : ntups = PQntuples(res);
6194 :
6195 354 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6196 :
6197 354 : i_tableoid = PQfnumber(res, "tableoid");
6198 354 : i_oid = PQfnumber(res, "oid");
6199 354 : i_oprname = PQfnumber(res, "oprname");
6200 354 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6201 354 : i_oprowner = PQfnumber(res, "oprowner");
6202 354 : i_oprkind = PQfnumber(res, "oprkind");
6203 354 : i_oprcode = PQfnumber(res, "oprcode");
6204 :
6205 283492 : for (i = 0; i < ntups; i++)
6206 : {
6207 283138 : oprinfo[i].dobj.objType = DO_OPERATOR;
6208 283138 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6209 283138 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6210 283138 : AssignDumpId(&oprinfo[i].dobj);
6211 283138 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6212 566276 : oprinfo[i].dobj.namespace =
6213 283138 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6214 283138 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6215 283138 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6216 283138 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6217 :
6218 : /* Decide whether we want to dump it */
6219 283138 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6220 : }
6221 :
6222 354 : PQclear(res);
6223 :
6224 354 : destroyPQExpBuffer(query);
6225 354 : }
6226 :
6227 : /*
6228 : * getCollations:
6229 : * get information about all collations in the system catalogs
6230 : */
6231 : void
6232 354 : getCollations(Archive *fout)
6233 : {
6234 : PGresult *res;
6235 : int ntups;
6236 : int i;
6237 : PQExpBuffer query;
6238 : CollInfo *collinfo;
6239 : int i_tableoid;
6240 : int i_oid;
6241 : int i_collname;
6242 : int i_collnamespace;
6243 : int i_collowner;
6244 :
6245 354 : query = createPQExpBuffer();
6246 :
6247 : /*
6248 : * find all collations, including builtin collations; we filter out
6249 : * system-defined collations at dump-out time.
6250 : */
6251 :
6252 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6253 : "collnamespace, "
6254 : "collowner "
6255 : "FROM pg_collation");
6256 :
6257 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6258 :
6259 354 : ntups = PQntuples(res);
6260 :
6261 354 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6262 :
6263 354 : i_tableoid = PQfnumber(res, "tableoid");
6264 354 : i_oid = PQfnumber(res, "oid");
6265 354 : i_collname = PQfnumber(res, "collname");
6266 354 : i_collnamespace = PQfnumber(res, "collnamespace");
6267 354 : i_collowner = PQfnumber(res, "collowner");
6268 :
6269 281314 : for (i = 0; i < ntups; i++)
6270 : {
6271 280960 : collinfo[i].dobj.objType = DO_COLLATION;
6272 280960 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6273 280960 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6274 280960 : AssignDumpId(&collinfo[i].dobj);
6275 280960 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6276 561920 : collinfo[i].dobj.namespace =
6277 280960 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6278 280960 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6279 :
6280 : /* Decide whether we want to dump it */
6281 280960 : selectDumpableObject(&(collinfo[i].dobj), fout);
6282 : }
6283 :
6284 354 : PQclear(res);
6285 :
6286 354 : destroyPQExpBuffer(query);
6287 354 : }
6288 :
6289 : /*
6290 : * getConversions:
6291 : * get information about all conversions in the system catalogs
6292 : */
6293 : void
6294 354 : getConversions(Archive *fout)
6295 : {
6296 : PGresult *res;
6297 : int ntups;
6298 : int i;
6299 : PQExpBuffer query;
6300 : ConvInfo *convinfo;
6301 : int i_tableoid;
6302 : int i_oid;
6303 : int i_conname;
6304 : int i_connamespace;
6305 : int i_conowner;
6306 :
6307 354 : query = createPQExpBuffer();
6308 :
6309 : /*
6310 : * find all conversions, including builtin conversions; we filter out
6311 : * system-defined conversions at dump-out time.
6312 : */
6313 :
6314 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6315 : "connamespace, "
6316 : "conowner "
6317 : "FROM pg_conversion");
6318 :
6319 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6320 :
6321 354 : ntups = PQntuples(res);
6322 :
6323 354 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6324 :
6325 354 : i_tableoid = PQfnumber(res, "tableoid");
6326 354 : i_oid = PQfnumber(res, "oid");
6327 354 : i_conname = PQfnumber(res, "conname");
6328 354 : i_connamespace = PQfnumber(res, "connamespace");
6329 354 : i_conowner = PQfnumber(res, "conowner");
6330 :
6331 45764 : for (i = 0; i < ntups; i++)
6332 : {
6333 45410 : convinfo[i].dobj.objType = DO_CONVERSION;
6334 45410 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6335 45410 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6336 45410 : AssignDumpId(&convinfo[i].dobj);
6337 45410 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6338 90820 : convinfo[i].dobj.namespace =
6339 45410 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6340 45410 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6341 :
6342 : /* Decide whether we want to dump it */
6343 45410 : selectDumpableObject(&(convinfo[i].dobj), fout);
6344 : }
6345 :
6346 354 : PQclear(res);
6347 :
6348 354 : destroyPQExpBuffer(query);
6349 354 : }
6350 :
6351 : /*
6352 : * getAccessMethods:
6353 : * get information about all user-defined access methods
6354 : */
6355 : void
6356 354 : getAccessMethods(Archive *fout)
6357 : {
6358 : PGresult *res;
6359 : int ntups;
6360 : int i;
6361 : PQExpBuffer query;
6362 : AccessMethodInfo *aminfo;
6363 : int i_tableoid;
6364 : int i_oid;
6365 : int i_amname;
6366 : int i_amhandler;
6367 : int i_amtype;
6368 :
6369 : /* Before 9.6, there are no user-defined access methods */
6370 354 : if (fout->remoteVersion < 90600)
6371 0 : return;
6372 :
6373 354 : query = createPQExpBuffer();
6374 :
6375 : /* Select all access methods from pg_am table */
6376 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6377 : "amhandler::pg_catalog.regproc AS amhandler "
6378 : "FROM pg_am");
6379 :
6380 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6381 :
6382 354 : ntups = PQntuples(res);
6383 :
6384 354 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6385 :
6386 354 : i_tableoid = PQfnumber(res, "tableoid");
6387 354 : i_oid = PQfnumber(res, "oid");
6388 354 : i_amname = PQfnumber(res, "amname");
6389 354 : i_amhandler = PQfnumber(res, "amhandler");
6390 354 : i_amtype = PQfnumber(res, "amtype");
6391 :
6392 3092 : for (i = 0; i < ntups; i++)
6393 : {
6394 2738 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6395 2738 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6396 2738 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6397 2738 : AssignDumpId(&aminfo[i].dobj);
6398 2738 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6399 2738 : aminfo[i].dobj.namespace = NULL;
6400 2738 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6401 2738 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6402 :
6403 : /* Decide whether we want to dump it */
6404 2738 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6405 : }
6406 :
6407 354 : PQclear(res);
6408 :
6409 354 : destroyPQExpBuffer(query);
6410 : }
6411 :
6412 :
6413 : /*
6414 : * getOpclasses:
6415 : * get information about all opclasses in the system catalogs
6416 : */
6417 : void
6418 354 : getOpclasses(Archive *fout)
6419 : {
6420 : PGresult *res;
6421 : int ntups;
6422 : int i;
6423 354 : PQExpBuffer query = createPQExpBuffer();
6424 : OpclassInfo *opcinfo;
6425 : int i_tableoid;
6426 : int i_oid;
6427 : int i_opcname;
6428 : int i_opcnamespace;
6429 : int i_opcowner;
6430 :
6431 : /*
6432 : * find all opclasses, including builtin opclasses; we filter out
6433 : * system-defined opclasses at dump-out time.
6434 : */
6435 :
6436 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6437 : "opcnamespace, "
6438 : "opcowner "
6439 : "FROM pg_opclass");
6440 :
6441 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6442 :
6443 354 : ntups = PQntuples(res);
6444 :
6445 354 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6446 :
6447 354 : i_tableoid = PQfnumber(res, "tableoid");
6448 354 : i_oid = PQfnumber(res, "oid");
6449 354 : i_opcname = PQfnumber(res, "opcname");
6450 354 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6451 354 : i_opcowner = PQfnumber(res, "opcowner");
6452 :
6453 63348 : for (i = 0; i < ntups; i++)
6454 : {
6455 62994 : opcinfo[i].dobj.objType = DO_OPCLASS;
6456 62994 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6457 62994 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6458 62994 : AssignDumpId(&opcinfo[i].dobj);
6459 62994 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6460 125988 : opcinfo[i].dobj.namespace =
6461 62994 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6462 62994 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6463 :
6464 : /* Decide whether we want to dump it */
6465 62994 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6466 : }
6467 :
6468 354 : PQclear(res);
6469 :
6470 354 : destroyPQExpBuffer(query);
6471 354 : }
6472 :
6473 : /*
6474 : * getOpfamilies:
6475 : * get information about all opfamilies in the system catalogs
6476 : */
6477 : void
6478 354 : getOpfamilies(Archive *fout)
6479 : {
6480 : PGresult *res;
6481 : int ntups;
6482 : int i;
6483 : PQExpBuffer query;
6484 : OpfamilyInfo *opfinfo;
6485 : int i_tableoid;
6486 : int i_oid;
6487 : int i_opfname;
6488 : int i_opfnamespace;
6489 : int i_opfowner;
6490 :
6491 354 : query = createPQExpBuffer();
6492 :
6493 : /*
6494 : * find all opfamilies, including builtin opfamilies; we filter out
6495 : * system-defined opfamilies at dump-out time.
6496 : */
6497 :
6498 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6499 : "opfnamespace, "
6500 : "opfowner "
6501 : "FROM pg_opfamily");
6502 :
6503 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6504 :
6505 354 : ntups = PQntuples(res);
6506 :
6507 354 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6508 :
6509 354 : i_tableoid = PQfnumber(res, "tableoid");
6510 354 : i_oid = PQfnumber(res, "oid");
6511 354 : i_opfname = PQfnumber(res, "opfname");
6512 354 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6513 354 : i_opfowner = PQfnumber(res, "opfowner");
6514 :
6515 52332 : for (i = 0; i < ntups; i++)
6516 : {
6517 51978 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6518 51978 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6519 51978 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6520 51978 : AssignDumpId(&opfinfo[i].dobj);
6521 51978 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6522 103956 : opfinfo[i].dobj.namespace =
6523 51978 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6524 51978 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6525 :
6526 : /* Decide whether we want to dump it */
6527 51978 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6528 : }
6529 :
6530 354 : PQclear(res);
6531 :
6532 354 : destroyPQExpBuffer(query);
6533 354 : }
6534 :
6535 : /*
6536 : * getAggregates:
6537 : * get information about all user-defined aggregates in the system catalogs
6538 : */
6539 : void
6540 354 : getAggregates(Archive *fout)
6541 : {
6542 354 : DumpOptions *dopt = fout->dopt;
6543 : PGresult *res;
6544 : int ntups;
6545 : int i;
6546 354 : PQExpBuffer query = createPQExpBuffer();
6547 : AggInfo *agginfo;
6548 : int i_tableoid;
6549 : int i_oid;
6550 : int i_aggname;
6551 : int i_aggnamespace;
6552 : int i_pronargs;
6553 : int i_proargtypes;
6554 : int i_proowner;
6555 : int i_aggacl;
6556 : int i_acldefault;
6557 :
6558 : /*
6559 : * Find all interesting aggregates. See comment in getFuncs() for the
6560 : * rationale behind the filtering logic.
6561 : */
6562 354 : if (fout->remoteVersion >= 90600)
6563 : {
6564 : const char *agg_check;
6565 :
6566 708 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6567 354 : : "p.proisagg");
6568 :
6569 354 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6570 : "p.proname AS aggname, "
6571 : "p.pronamespace AS aggnamespace, "
6572 : "p.pronargs, p.proargtypes, "
6573 : "p.proowner, "
6574 : "p.proacl AS aggacl, "
6575 : "acldefault('f', p.proowner) AS acldefault "
6576 : "FROM pg_proc p "
6577 : "LEFT JOIN pg_init_privs pip ON "
6578 : "(p.oid = pip.objoid "
6579 : "AND pip.classoid = 'pg_proc'::regclass "
6580 : "AND pip.objsubid = 0) "
6581 : "WHERE %s AND ("
6582 : "p.pronamespace != "
6583 : "(SELECT oid FROM pg_namespace "
6584 : "WHERE nspname = 'pg_catalog') OR "
6585 : "p.proacl IS DISTINCT FROM pip.initprivs",
6586 : agg_check);
6587 354 : if (dopt->binary_upgrade)
6588 62 : appendPQExpBufferStr(query,
6589 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6590 : "classid = 'pg_proc'::regclass AND "
6591 : "objid = p.oid AND "
6592 : "refclassid = 'pg_extension'::regclass AND "
6593 : "deptype = 'e')");
6594 354 : appendPQExpBufferChar(query, ')');
6595 : }
6596 : else
6597 : {
6598 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6599 : "pronamespace AS aggnamespace, "
6600 : "pronargs, proargtypes, "
6601 : "proowner, "
6602 : "proacl AS aggacl, "
6603 : "acldefault('f', proowner) AS acldefault "
6604 : "FROM pg_proc p "
6605 : "WHERE proisagg AND ("
6606 : "pronamespace != "
6607 : "(SELECT oid FROM pg_namespace "
6608 : "WHERE nspname = 'pg_catalog')");
6609 0 : if (dopt->binary_upgrade)
6610 0 : appendPQExpBufferStr(query,
6611 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6612 : "classid = 'pg_proc'::regclass AND "
6613 : "objid = p.oid AND "
6614 : "refclassid = 'pg_extension'::regclass AND "
6615 : "deptype = 'e')");
6616 0 : appendPQExpBufferChar(query, ')');
6617 : }
6618 :
6619 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6620 :
6621 354 : ntups = PQntuples(res);
6622 :
6623 354 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6624 :
6625 354 : i_tableoid = PQfnumber(res, "tableoid");
6626 354 : i_oid = PQfnumber(res, "oid");
6627 354 : i_aggname = PQfnumber(res, "aggname");
6628 354 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6629 354 : i_pronargs = PQfnumber(res, "pronargs");
6630 354 : i_proargtypes = PQfnumber(res, "proargtypes");
6631 354 : i_proowner = PQfnumber(res, "proowner");
6632 354 : i_aggacl = PQfnumber(res, "aggacl");
6633 354 : i_acldefault = PQfnumber(res, "acldefault");
6634 :
6635 1160 : for (i = 0; i < ntups; i++)
6636 : {
6637 806 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6638 806 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6639 806 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6640 806 : AssignDumpId(&agginfo[i].aggfn.dobj);
6641 806 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6642 1612 : agginfo[i].aggfn.dobj.namespace =
6643 806 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6644 806 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6645 806 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6646 806 : agginfo[i].aggfn.dacl.privtype = 0;
6647 806 : agginfo[i].aggfn.dacl.initprivs = NULL;
6648 806 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6649 806 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6650 806 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6651 806 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6652 806 : if (agginfo[i].aggfn.nargs == 0)
6653 112 : agginfo[i].aggfn.argtypes = NULL;
6654 : else
6655 : {
6656 694 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6657 694 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6658 694 : agginfo[i].aggfn.argtypes,
6659 694 : agginfo[i].aggfn.nargs);
6660 : }
6661 806 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6662 :
6663 : /* Decide whether we want to dump it */
6664 806 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6665 :
6666 : /* Mark whether aggregate has an ACL */
6667 806 : if (!PQgetisnull(res, i, i_aggacl))
6668 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6669 : }
6670 :
6671 354 : PQclear(res);
6672 :
6673 354 : destroyPQExpBuffer(query);
6674 354 : }
6675 :
6676 : /*
6677 : * getFuncs:
6678 : * get information about all user-defined functions in the system catalogs
6679 : */
6680 : void
6681 354 : getFuncs(Archive *fout)
6682 : {
6683 354 : DumpOptions *dopt = fout->dopt;
6684 : PGresult *res;
6685 : int ntups;
6686 : int i;
6687 354 : PQExpBuffer query = createPQExpBuffer();
6688 : FuncInfo *finfo;
6689 : int i_tableoid;
6690 : int i_oid;
6691 : int i_proname;
6692 : int i_pronamespace;
6693 : int i_proowner;
6694 : int i_prolang;
6695 : int i_pronargs;
6696 : int i_proargtypes;
6697 : int i_prorettype;
6698 : int i_proacl;
6699 : int i_acldefault;
6700 :
6701 : /*
6702 : * Find all interesting functions. This is a bit complicated:
6703 : *
6704 : * 1. Always exclude aggregates; those are handled elsewhere.
6705 : *
6706 : * 2. Always exclude functions that are internally dependent on something
6707 : * else, since presumably those will be created as a result of creating
6708 : * the something else. This currently acts only to suppress constructor
6709 : * functions for range types. Note this is OK only because the
6710 : * constructors don't have any dependencies the range type doesn't have;
6711 : * otherwise we might not get creation ordering correct.
6712 : *
6713 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6714 : * they're members of extensions and we are in binary-upgrade mode then
6715 : * include them, since we want to dump extension members individually in
6716 : * that mode. Also, if they are used by casts or transforms then we need
6717 : * to gather the information about them, though they won't be dumped if
6718 : * they are built-in. Also, in 9.6 and up, include functions in
6719 : * pg_catalog if they have an ACL different from what's shown in
6720 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6721 : */
6722 354 : if (fout->remoteVersion >= 90600)
6723 : {
6724 : const char *not_agg_check;
6725 :
6726 708 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6727 354 : : "NOT p.proisagg");
6728 :
6729 354 : appendPQExpBuffer(query,
6730 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6731 : "p.pronargs, p.proargtypes, p.prorettype, "
6732 : "p.proacl, "
6733 : "acldefault('f', p.proowner) AS acldefault, "
6734 : "p.pronamespace, "
6735 : "p.proowner "
6736 : "FROM pg_proc p "
6737 : "LEFT JOIN pg_init_privs pip ON "
6738 : "(p.oid = pip.objoid "
6739 : "AND pip.classoid = 'pg_proc'::regclass "
6740 : "AND pip.objsubid = 0) "
6741 : "WHERE %s"
6742 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6743 : "WHERE classid = 'pg_proc'::regclass AND "
6744 : "objid = p.oid AND deptype = 'i')"
6745 : "\n AND ("
6746 : "\n pronamespace != "
6747 : "(SELECT oid FROM pg_namespace "
6748 : "WHERE nspname = 'pg_catalog')"
6749 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6750 : "\n WHERE pg_cast.oid > %u "
6751 : "\n AND p.oid = pg_cast.castfunc)"
6752 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6753 : "\n WHERE pg_transform.oid > %u AND "
6754 : "\n (p.oid = pg_transform.trffromsql"
6755 : "\n OR p.oid = pg_transform.trftosql))",
6756 : not_agg_check,
6757 : g_last_builtin_oid,
6758 : g_last_builtin_oid);
6759 354 : if (dopt->binary_upgrade)
6760 62 : appendPQExpBufferStr(query,
6761 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6762 : "classid = 'pg_proc'::regclass AND "
6763 : "objid = p.oid AND "
6764 : "refclassid = 'pg_extension'::regclass AND "
6765 : "deptype = 'e')");
6766 354 : appendPQExpBufferStr(query,
6767 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6768 354 : appendPQExpBufferChar(query, ')');
6769 : }
6770 : else
6771 : {
6772 0 : appendPQExpBuffer(query,
6773 : "SELECT tableoid, oid, proname, prolang, "
6774 : "pronargs, proargtypes, prorettype, proacl, "
6775 : "acldefault('f', proowner) AS acldefault, "
6776 : "pronamespace, "
6777 : "proowner "
6778 : "FROM pg_proc p "
6779 : "WHERE NOT proisagg"
6780 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6781 : "WHERE classid = 'pg_proc'::regclass AND "
6782 : "objid = p.oid AND deptype = 'i')"
6783 : "\n AND ("
6784 : "\n pronamespace != "
6785 : "(SELECT oid FROM pg_namespace "
6786 : "WHERE nspname = 'pg_catalog')"
6787 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6788 : "\n WHERE pg_cast.oid > '%u'::oid"
6789 : "\n AND p.oid = pg_cast.castfunc)",
6790 : g_last_builtin_oid);
6791 :
6792 0 : if (fout->remoteVersion >= 90500)
6793 0 : appendPQExpBuffer(query,
6794 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6795 : "\n WHERE pg_transform.oid > '%u'::oid"
6796 : "\n AND (p.oid = pg_transform.trffromsql"
6797 : "\n OR p.oid = pg_transform.trftosql))",
6798 : g_last_builtin_oid);
6799 :
6800 0 : if (dopt->binary_upgrade)
6801 0 : appendPQExpBufferStr(query,
6802 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6803 : "classid = 'pg_proc'::regclass AND "
6804 : "objid = p.oid AND "
6805 : "refclassid = 'pg_extension'::regclass AND "
6806 : "deptype = 'e')");
6807 0 : appendPQExpBufferChar(query, ')');
6808 : }
6809 :
6810 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6811 :
6812 354 : ntups = PQntuples(res);
6813 :
6814 354 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6815 :
6816 354 : i_tableoid = PQfnumber(res, "tableoid");
6817 354 : i_oid = PQfnumber(res, "oid");
6818 354 : i_proname = PQfnumber(res, "proname");
6819 354 : i_pronamespace = PQfnumber(res, "pronamespace");
6820 354 : i_proowner = PQfnumber(res, "proowner");
6821 354 : i_prolang = PQfnumber(res, "prolang");
6822 354 : i_pronargs = PQfnumber(res, "pronargs");
6823 354 : i_proargtypes = PQfnumber(res, "proargtypes");
6824 354 : i_prorettype = PQfnumber(res, "prorettype");
6825 354 : i_proacl = PQfnumber(res, "proacl");
6826 354 : i_acldefault = PQfnumber(res, "acldefault");
6827 :
6828 9772 : for (i = 0; i < ntups; i++)
6829 : {
6830 9418 : finfo[i].dobj.objType = DO_FUNC;
6831 9418 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6832 9418 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6833 9418 : AssignDumpId(&finfo[i].dobj);
6834 9418 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6835 18836 : finfo[i].dobj.namespace =
6836 9418 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6837 9418 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6838 9418 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6839 9418 : finfo[i].dacl.privtype = 0;
6840 9418 : finfo[i].dacl.initprivs = NULL;
6841 9418 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6842 9418 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6843 9418 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6844 9418 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6845 9418 : if (finfo[i].nargs == 0)
6846 2210 : finfo[i].argtypes = NULL;
6847 : else
6848 : {
6849 7208 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6850 7208 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6851 7208 : finfo[i].argtypes, finfo[i].nargs);
6852 : }
6853 9418 : finfo[i].postponed_def = false; /* might get set during sort */
6854 :
6855 : /* Decide whether we want to dump it */
6856 9418 : selectDumpableObject(&(finfo[i].dobj), fout);
6857 :
6858 : /* Mark whether function has an ACL */
6859 9418 : if (!PQgetisnull(res, i, i_proacl))
6860 296 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6861 : }
6862 :
6863 354 : PQclear(res);
6864 :
6865 354 : destroyPQExpBuffer(query);
6866 354 : }
6867 :
6868 : /*
6869 : * getRelationStatistics
6870 : * register the statistics object as a dependent of the relation.
6871 : *
6872 : * reltuples is passed as a string to avoid complexities in converting from/to
6873 : * floating point.
6874 : */
6875 : static RelStatsInfo *
6876 18844 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
6877 : char *reltuples, int32 relallvisible,
6878 : int32 relallfrozen, char relkind,
6879 : char **indAttNames, int nindAttNames)
6880 : {
6881 18844 : if (!fout->dopt->dumpStatistics)
6882 5084 : return NULL;
6883 :
6884 13760 : if ((relkind == RELKIND_RELATION) ||
6885 6258 : (relkind == RELKIND_PARTITIONED_TABLE) ||
6886 3212 : (relkind == RELKIND_INDEX) ||
6887 2292 : (relkind == RELKIND_PARTITIONED_INDEX) ||
6888 : (relkind == RELKIND_MATVIEW))
6889 : {
6890 12276 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
6891 12276 : DumpableObject *dobj = &info->dobj;
6892 :
6893 12276 : dobj->objType = DO_REL_STATS;
6894 12276 : dobj->catId.tableoid = 0;
6895 12276 : dobj->catId.oid = 0;
6896 12276 : AssignDumpId(dobj);
6897 12276 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
6898 12276 : dobj->dependencies[0] = rel->dumpId;
6899 12276 : dobj->nDeps = 1;
6900 12276 : dobj->allocDeps = 1;
6901 12276 : dobj->components |= DUMP_COMPONENT_STATISTICS;
6902 12276 : dobj->name = pg_strdup(rel->name);
6903 12276 : dobj->namespace = rel->namespace;
6904 12276 : info->relpages = relpages;
6905 12276 : info->reltuples = pstrdup(reltuples);
6906 12276 : info->relallvisible = relallvisible;
6907 12276 : info->relallfrozen = relallfrozen;
6908 12276 : info->relkind = relkind;
6909 12276 : info->indAttNames = indAttNames;
6910 12276 : info->nindAttNames = nindAttNames;
6911 :
6912 : /*
6913 : * Ordinarily, stats go in SECTION_DATA for tables and
6914 : * SECTION_POST_DATA for indexes.
6915 : *
6916 : * However, the section may be updated later for materialized view
6917 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
6918 : * the stats, so the stats must be restored after the data. Also, the
6919 : * materialized view definition may be postponed to SECTION_POST_DATA
6920 : * (see repairMatViewBoundaryMultiLoop()).
6921 : */
6922 12276 : switch (info->relkind)
6923 : {
6924 8310 : case RELKIND_RELATION:
6925 : case RELKIND_PARTITIONED_TABLE:
6926 : case RELKIND_MATVIEW:
6927 8310 : info->section = SECTION_DATA;
6928 8310 : break;
6929 3966 : case RELKIND_INDEX:
6930 : case RELKIND_PARTITIONED_INDEX:
6931 3966 : info->section = SECTION_POST_DATA;
6932 3966 : break;
6933 0 : default:
6934 0 : pg_fatal("cannot dump statistics for relation kind '%c'",
6935 : info->relkind);
6936 : }
6937 :
6938 12276 : return info;
6939 : }
6940 1484 : return NULL;
6941 : }
6942 :
6943 : /*
6944 : * getTables
6945 : * read all the tables (no indexes) in the system catalogs,
6946 : * and return them as an array of TableInfo structures
6947 : *
6948 : * *numTables is set to the number of tables read in
6949 : */
6950 : TableInfo *
6951 356 : getTables(Archive *fout, int *numTables)
6952 : {
6953 356 : DumpOptions *dopt = fout->dopt;
6954 : PGresult *res;
6955 : int ntups;
6956 : int i;
6957 356 : PQExpBuffer query = createPQExpBuffer();
6958 : TableInfo *tblinfo;
6959 : int i_reltableoid;
6960 : int i_reloid;
6961 : int i_relname;
6962 : int i_relnamespace;
6963 : int i_relkind;
6964 : int i_reltype;
6965 : int i_relowner;
6966 : int i_relchecks;
6967 : int i_relhasindex;
6968 : int i_relhasrules;
6969 : int i_relpages;
6970 : int i_reltuples;
6971 : int i_relallvisible;
6972 : int i_relallfrozen;
6973 : int i_toastpages;
6974 : int i_owning_tab;
6975 : int i_owning_col;
6976 : int i_reltablespace;
6977 : int i_relhasoids;
6978 : int i_relhastriggers;
6979 : int i_relpersistence;
6980 : int i_relispopulated;
6981 : int i_relreplident;
6982 : int i_relrowsec;
6983 : int i_relforcerowsec;
6984 : int i_relfrozenxid;
6985 : int i_toastfrozenxid;
6986 : int i_toastoid;
6987 : int i_relminmxid;
6988 : int i_toastminmxid;
6989 : int i_reloptions;
6990 : int i_checkoption;
6991 : int i_toastreloptions;
6992 : int i_reloftype;
6993 : int i_foreignserver;
6994 : int i_amname;
6995 : int i_is_identity_sequence;
6996 : int i_relacl;
6997 : int i_acldefault;
6998 : int i_ispartition;
6999 :
7000 : /*
7001 : * Find all the tables and table-like objects.
7002 : *
7003 : * We must fetch all tables in this phase because otherwise we cannot
7004 : * correctly identify inherited columns, owned sequences, etc.
7005 : *
7006 : * We include system catalogs, so that we can work if a user table is
7007 : * defined to inherit from a system catalog (pretty weird, but...)
7008 : *
7009 : * Note: in this phase we should collect only a minimal amount of
7010 : * information about each table, basically just enough to decide if it is
7011 : * interesting. In particular, since we do not yet have lock on any user
7012 : * table, we MUST NOT invoke any server-side data collection functions
7013 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7014 : * wrong answers if any concurrent DDL is happening.
7015 : */
7016 :
7017 356 : appendPQExpBufferStr(query,
7018 : "SELECT c.tableoid, c.oid, c.relname, "
7019 : "c.relnamespace, c.relkind, c.reltype, "
7020 : "c.relowner, "
7021 : "c.relchecks, "
7022 : "c.relhasindex, c.relhasrules, c.relpages, "
7023 : "c.reltuples, c.relallvisible, ");
7024 :
7025 356 : if (fout->remoteVersion >= 180000)
7026 356 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7027 : else
7028 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7029 :
7030 356 : appendPQExpBufferStr(query,
7031 : "c.relhastriggers, c.relpersistence, "
7032 : "c.reloftype, "
7033 : "c.relacl, "
7034 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7035 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7036 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7037 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7038 : "ELSE 0 END AS foreignserver, "
7039 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7040 : "tc.oid AS toid, "
7041 : "tc.relpages AS toastpages, "
7042 : "tc.reloptions AS toast_reloptions, "
7043 : "d.refobjid AS owning_tab, "
7044 : "d.refobjsubid AS owning_col, "
7045 : "tsp.spcname AS reltablespace, ");
7046 :
7047 356 : if (fout->remoteVersion >= 120000)
7048 356 : appendPQExpBufferStr(query,
7049 : "false AS relhasoids, ");
7050 : else
7051 0 : appendPQExpBufferStr(query,
7052 : "c.relhasoids, ");
7053 :
7054 356 : if (fout->remoteVersion >= 90300)
7055 356 : appendPQExpBufferStr(query,
7056 : "c.relispopulated, ");
7057 : else
7058 0 : appendPQExpBufferStr(query,
7059 : "'t' as relispopulated, ");
7060 :
7061 356 : if (fout->remoteVersion >= 90400)
7062 356 : appendPQExpBufferStr(query,
7063 : "c.relreplident, ");
7064 : else
7065 0 : appendPQExpBufferStr(query,
7066 : "'d' AS relreplident, ");
7067 :
7068 356 : if (fout->remoteVersion >= 90500)
7069 356 : appendPQExpBufferStr(query,
7070 : "c.relrowsecurity, c.relforcerowsecurity, ");
7071 : else
7072 0 : appendPQExpBufferStr(query,
7073 : "false AS relrowsecurity, "
7074 : "false AS relforcerowsecurity, ");
7075 :
7076 356 : if (fout->remoteVersion >= 90300)
7077 356 : appendPQExpBufferStr(query,
7078 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7079 : else
7080 0 : appendPQExpBufferStr(query,
7081 : "0 AS relminmxid, 0 AS tminmxid, ");
7082 :
7083 356 : if (fout->remoteVersion >= 90300)
7084 356 : appendPQExpBufferStr(query,
7085 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7086 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7087 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7088 : else
7089 0 : appendPQExpBufferStr(query,
7090 : "c.reloptions, NULL AS checkoption, ");
7091 :
7092 356 : if (fout->remoteVersion >= 90600)
7093 356 : appendPQExpBufferStr(query,
7094 : "am.amname, ");
7095 : else
7096 0 : appendPQExpBufferStr(query,
7097 : "NULL AS amname, ");
7098 :
7099 356 : if (fout->remoteVersion >= 90600)
7100 356 : appendPQExpBufferStr(query,
7101 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7102 : else
7103 0 : appendPQExpBufferStr(query,
7104 : "false AS is_identity_sequence, ");
7105 :
7106 356 : if (fout->remoteVersion >= 100000)
7107 356 : appendPQExpBufferStr(query,
7108 : "c.relispartition AS ispartition ");
7109 : else
7110 0 : appendPQExpBufferStr(query,
7111 : "false AS ispartition ");
7112 :
7113 : /*
7114 : * Left join to pg_depend to pick up dependency info linking sequences to
7115 : * their owning column, if any (note this dependency is AUTO except for
7116 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7117 : * collect the spcname.
7118 : */
7119 356 : appendPQExpBufferStr(query,
7120 : "\nFROM pg_class c\n"
7121 : "LEFT JOIN pg_depend d ON "
7122 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7123 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7124 : "d.objsubid = 0 AND "
7125 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7126 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7127 :
7128 : /*
7129 : * In 9.6 and up, left join to pg_am to pick up the amname.
7130 : */
7131 356 : if (fout->remoteVersion >= 90600)
7132 356 : appendPQExpBufferStr(query,
7133 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7134 :
7135 : /*
7136 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7137 : * that versions 10 and 11 have them, but later versions do not, so
7138 : * emitting them causes the upgrade to fail.
7139 : */
7140 356 : appendPQExpBufferStr(query,
7141 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7142 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7143 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7144 :
7145 : /*
7146 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7147 : * relkinds are possible in older servers, but it's not worth the trouble
7148 : * to emit a version-dependent list.
7149 : *
7150 : * Composite-type table entries won't be dumped as such, but we have to
7151 : * make a DumpableObject for them so that we can track dependencies of the
7152 : * composite type (pg_depend entries for columns of the composite type
7153 : * link to the pg_class entry not the pg_type entry).
7154 : */
7155 356 : appendPQExpBufferStr(query,
7156 : "WHERE c.relkind IN ("
7157 : CppAsString2(RELKIND_RELATION) ", "
7158 : CppAsString2(RELKIND_SEQUENCE) ", "
7159 : CppAsString2(RELKIND_VIEW) ", "
7160 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7161 : CppAsString2(RELKIND_MATVIEW) ", "
7162 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7163 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7164 : "ORDER BY c.oid");
7165 :
7166 356 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7167 :
7168 356 : ntups = PQntuples(res);
7169 :
7170 356 : *numTables = ntups;
7171 :
7172 : /*
7173 : * Extract data from result and lock dumpable tables. We do the locking
7174 : * before anything else, to minimize the window wherein a table could
7175 : * disappear under us.
7176 : *
7177 : * Note that we have to save info about all tables here, even when dumping
7178 : * only one, because we don't yet know which tables might be inheritance
7179 : * ancestors of the target table.
7180 : */
7181 356 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7182 :
7183 356 : i_reltableoid = PQfnumber(res, "tableoid");
7184 356 : i_reloid = PQfnumber(res, "oid");
7185 356 : i_relname = PQfnumber(res, "relname");
7186 356 : i_relnamespace = PQfnumber(res, "relnamespace");
7187 356 : i_relkind = PQfnumber(res, "relkind");
7188 356 : i_reltype = PQfnumber(res, "reltype");
7189 356 : i_relowner = PQfnumber(res, "relowner");
7190 356 : i_relchecks = PQfnumber(res, "relchecks");
7191 356 : i_relhasindex = PQfnumber(res, "relhasindex");
7192 356 : i_relhasrules = PQfnumber(res, "relhasrules");
7193 356 : i_relpages = PQfnumber(res, "relpages");
7194 356 : i_reltuples = PQfnumber(res, "reltuples");
7195 356 : i_relallvisible = PQfnumber(res, "relallvisible");
7196 356 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7197 356 : i_toastpages = PQfnumber(res, "toastpages");
7198 356 : i_owning_tab = PQfnumber(res, "owning_tab");
7199 356 : i_owning_col = PQfnumber(res, "owning_col");
7200 356 : i_reltablespace = PQfnumber(res, "reltablespace");
7201 356 : i_relhasoids = PQfnumber(res, "relhasoids");
7202 356 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7203 356 : i_relpersistence = PQfnumber(res, "relpersistence");
7204 356 : i_relispopulated = PQfnumber(res, "relispopulated");
7205 356 : i_relreplident = PQfnumber(res, "relreplident");
7206 356 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7207 356 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7208 356 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7209 356 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7210 356 : i_toastoid = PQfnumber(res, "toid");
7211 356 : i_relminmxid = PQfnumber(res, "relminmxid");
7212 356 : i_toastminmxid = PQfnumber(res, "tminmxid");
7213 356 : i_reloptions = PQfnumber(res, "reloptions");
7214 356 : i_checkoption = PQfnumber(res, "checkoption");
7215 356 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7216 356 : i_reloftype = PQfnumber(res, "reloftype");
7217 356 : i_foreignserver = PQfnumber(res, "foreignserver");
7218 356 : i_amname = PQfnumber(res, "amname");
7219 356 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7220 356 : i_relacl = PQfnumber(res, "relacl");
7221 356 : i_acldefault = PQfnumber(res, "acldefault");
7222 356 : i_ispartition = PQfnumber(res, "ispartition");
7223 :
7224 356 : if (dopt->lockWaitTimeout)
7225 : {
7226 : /*
7227 : * Arrange to fail instead of waiting forever for a table lock.
7228 : *
7229 : * NB: this coding assumes that the only queries issued within the
7230 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7231 : * applied to other things too.
7232 : */
7233 4 : resetPQExpBuffer(query);
7234 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7235 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7236 4 : ExecuteSqlStatement(fout, query->data);
7237 : }
7238 :
7239 356 : resetPQExpBuffer(query);
7240 :
7241 93064 : for (i = 0; i < ntups; i++)
7242 : {
7243 92708 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7244 92708 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7245 :
7246 92708 : tblinfo[i].dobj.objType = DO_TABLE;
7247 92708 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7248 92708 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7249 92708 : AssignDumpId(&tblinfo[i].dobj);
7250 92708 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7251 185416 : tblinfo[i].dobj.namespace =
7252 92708 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7253 92708 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7254 92708 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7255 92708 : tblinfo[i].dacl.privtype = 0;
7256 92708 : tblinfo[i].dacl.initprivs = NULL;
7257 92708 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7258 92708 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7259 92708 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7260 92708 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7261 92708 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7262 92708 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7263 92708 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7264 92708 : if (PQgetisnull(res, i, i_toastpages))
7265 73470 : tblinfo[i].toastpages = 0;
7266 : else
7267 19238 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7268 92708 : if (PQgetisnull(res, i, i_owning_tab))
7269 : {
7270 91854 : tblinfo[i].owning_tab = InvalidOid;
7271 91854 : tblinfo[i].owning_col = 0;
7272 : }
7273 : else
7274 : {
7275 854 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7276 854 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7277 : }
7278 92708 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7279 92708 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7280 92708 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7281 92708 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7282 92708 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7283 92708 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7284 92708 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7285 92708 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7286 92708 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7287 92708 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7288 92708 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7289 92708 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7290 92708 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7291 92708 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7292 92708 : if (PQgetisnull(res, i, i_checkoption))
7293 92608 : tblinfo[i].checkoption = NULL;
7294 : else
7295 100 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7296 92708 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7297 92708 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7298 92708 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7299 92708 : if (PQgetisnull(res, i, i_amname))
7300 54894 : tblinfo[i].amname = NULL;
7301 : else
7302 37814 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7303 92708 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7304 92708 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7305 :
7306 : /* other fields were zeroed above */
7307 :
7308 : /*
7309 : * Decide whether we want to dump this table.
7310 : */
7311 92708 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7312 374 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7313 : else
7314 92334 : selectDumpableTable(&tblinfo[i], fout);
7315 :
7316 : /*
7317 : * Now, consider the table "interesting" if we need to dump its
7318 : * definition, data or its statistics. Later on, we'll skip a lot of
7319 : * data collection for uninteresting tables.
7320 : *
7321 : * Note: the "interesting" flag will also be set by flagInhTables for
7322 : * parents of interesting tables, so that we collect necessary
7323 : * inheritance info even when the parents are not themselves being
7324 : * dumped. This is the main reason why we need an "interesting" flag
7325 : * that's separate from the components-to-dump bitmask.
7326 : */
7327 92708 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7328 : (DUMP_COMPONENT_DEFINITION |
7329 : DUMP_COMPONENT_DATA |
7330 92708 : DUMP_COMPONENT_STATISTICS)) != 0;
7331 :
7332 92708 : tblinfo[i].dummy_view = false; /* might get set during sort */
7333 92708 : tblinfo[i].postponed_def = false; /* might get set during sort */
7334 :
7335 : /* Tables have data */
7336 92708 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7337 :
7338 : /* Mark whether table has an ACL */
7339 92708 : if (!PQgetisnull(res, i, i_relacl))
7340 73808 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7341 92708 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7342 :
7343 : /* Add statistics */
7344 92708 : if (tblinfo[i].interesting)
7345 : {
7346 : RelStatsInfo *stats;
7347 :
7348 27132 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7349 13566 : tblinfo[i].relpages,
7350 : PQgetvalue(res, i, i_reltuples),
7351 : relallvisible, relallfrozen,
7352 13566 : tblinfo[i].relkind, NULL, 0);
7353 13566 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7354 940 : tblinfo[i].stats = stats;
7355 : }
7356 :
7357 : /*
7358 : * Read-lock target tables to make sure they aren't DROPPED or altered
7359 : * in schema before we get around to dumping them.
7360 : *
7361 : * Note that we don't explicitly lock parents of the target tables; we
7362 : * assume our lock on the child is enough to prevent schema
7363 : * alterations to parent tables.
7364 : *
7365 : * NOTE: it'd be kinda nice to lock other relations too, not only
7366 : * plain or partitioned tables, but the backend doesn't presently
7367 : * allow that.
7368 : *
7369 : * We only need to lock the table for certain components; see
7370 : * pg_dump.h
7371 : */
7372 92708 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7373 13566 : (tblinfo[i].relkind == RELKIND_RELATION ||
7374 4016 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7375 : {
7376 : /*
7377 : * Tables are locked in batches. When dumping from a remote
7378 : * server this can save a significant amount of time by reducing
7379 : * the number of round trips.
7380 : */
7381 10666 : if (query->len == 0)
7382 230 : appendPQExpBuffer(query, "LOCK TABLE %s",
7383 230 : fmtQualifiedDumpable(&tblinfo[i]));
7384 : else
7385 : {
7386 10436 : appendPQExpBuffer(query, ", %s",
7387 10436 : fmtQualifiedDumpable(&tblinfo[i]));
7388 :
7389 : /* Arbitrarily end a batch when query length reaches 100K. */
7390 10436 : if (query->len >= 100000)
7391 : {
7392 : /* Lock another batch of tables. */
7393 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7394 0 : ExecuteSqlStatement(fout, query->data);
7395 0 : resetPQExpBuffer(query);
7396 : }
7397 : }
7398 : }
7399 : }
7400 :
7401 356 : if (query->len != 0)
7402 : {
7403 : /* Lock the tables in the last batch. */
7404 230 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7405 230 : ExecuteSqlStatement(fout, query->data);
7406 : }
7407 :
7408 354 : if (dopt->lockWaitTimeout)
7409 : {
7410 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7411 : }
7412 :
7413 354 : PQclear(res);
7414 :
7415 354 : destroyPQExpBuffer(query);
7416 :
7417 354 : return tblinfo;
7418 : }
7419 :
7420 : /*
7421 : * getOwnedSeqs
7422 : * identify owned sequences and mark them as dumpable if owning table is
7423 : *
7424 : * We used to do this in getTables(), but it's better to do it after the
7425 : * index used by findTableByOid() has been set up.
7426 : */
7427 : void
7428 354 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7429 : {
7430 : int i;
7431 :
7432 : /*
7433 : * Force sequences that are "owned" by table columns to be dumped whenever
7434 : * their owning table is being dumped.
7435 : */
7436 92540 : for (i = 0; i < numTables; i++)
7437 : {
7438 92186 : TableInfo *seqinfo = &tblinfo[i];
7439 : TableInfo *owning_tab;
7440 :
7441 92186 : if (!OidIsValid(seqinfo->owning_tab))
7442 91338 : continue; /* not an owned sequence */
7443 :
7444 848 : owning_tab = findTableByOid(seqinfo->owning_tab);
7445 848 : if (owning_tab == NULL)
7446 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7447 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7448 :
7449 : /*
7450 : * For an identity sequence, dump exactly the same components for the
7451 : * sequence as for the owning table. This is important because we
7452 : * treat the identity sequence as an integral part of the table. For
7453 : * example, there is not any DDL command that allows creation of such
7454 : * a sequence independently of the table.
7455 : *
7456 : * For other owned sequences such as serial sequences, we need to dump
7457 : * the components that are being dumped for the table and any
7458 : * components that the sequence is explicitly marked with.
7459 : *
7460 : * We can't simply use the set of components which are being dumped
7461 : * for the table as the table might be in an extension (and only the
7462 : * non-extension components, eg: ACLs if changed, security labels, and
7463 : * policies, are being dumped) while the sequence is not (and
7464 : * therefore the definition and other components should also be
7465 : * dumped).
7466 : *
7467 : * If the sequence is part of the extension then it should be properly
7468 : * marked by checkExtensionMembership() and this will be a no-op as
7469 : * the table will be equivalently marked.
7470 : */
7471 848 : if (seqinfo->is_identity_sequence)
7472 406 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7473 : else
7474 442 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7475 :
7476 : /* Make sure that necessary data is available if we're dumping it */
7477 848 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7478 : {
7479 656 : seqinfo->interesting = true;
7480 656 : owning_tab->interesting = true;
7481 : }
7482 : }
7483 354 : }
7484 :
7485 : /*
7486 : * getInherits
7487 : * read all the inheritance information
7488 : * from the system catalogs return them in the InhInfo* structure
7489 : *
7490 : * numInherits is set to the number of pairs read in
7491 : */
7492 : InhInfo *
7493 354 : getInherits(Archive *fout, int *numInherits)
7494 : {
7495 : PGresult *res;
7496 : int ntups;
7497 : int i;
7498 354 : PQExpBuffer query = createPQExpBuffer();
7499 : InhInfo *inhinfo;
7500 :
7501 : int i_inhrelid;
7502 : int i_inhparent;
7503 :
7504 : /* find all the inheritance information */
7505 354 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7506 :
7507 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7508 :
7509 354 : ntups = PQntuples(res);
7510 :
7511 354 : *numInherits = ntups;
7512 :
7513 354 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7514 :
7515 354 : i_inhrelid = PQfnumber(res, "inhrelid");
7516 354 : i_inhparent = PQfnumber(res, "inhparent");
7517 :
7518 6526 : for (i = 0; i < ntups; i++)
7519 : {
7520 6172 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7521 6172 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7522 : }
7523 :
7524 354 : PQclear(res);
7525 :
7526 354 : destroyPQExpBuffer(query);
7527 :
7528 354 : return inhinfo;
7529 : }
7530 :
7531 : /*
7532 : * getPartitioningInfo
7533 : * get information about partitioning
7534 : *
7535 : * For the most part, we only collect partitioning info about tables we
7536 : * intend to dump. However, this function has to consider all partitioned
7537 : * tables in the database, because we need to know about parents of partitions
7538 : * we are going to dump even if the parents themselves won't be dumped.
7539 : *
7540 : * Specifically, what we need to know is whether each partitioned table
7541 : * has an "unsafe" partitioning scheme that requires us to force
7542 : * load-via-partition-root mode for its children. Currently the only case
7543 : * for which we force that is hash partitioning on enum columns, since the
7544 : * hash codes depend on enum value OIDs which won't be replicated across
7545 : * dump-and-reload. There are other cases in which load-via-partition-root
7546 : * might be necessary, but we expect users to cope with them.
7547 : */
7548 : void
7549 354 : getPartitioningInfo(Archive *fout)
7550 : {
7551 : PQExpBuffer query;
7552 : PGresult *res;
7553 : int ntups;
7554 :
7555 : /* hash partitioning didn't exist before v11 */
7556 354 : if (fout->remoteVersion < 110000)
7557 0 : return;
7558 : /* needn't bother if not dumping data */
7559 354 : if (!fout->dopt->dumpData)
7560 72 : return;
7561 :
7562 282 : query = createPQExpBuffer();
7563 :
7564 : /*
7565 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7566 : * appears among the partition opclasses. We needn't check partstrat.
7567 : *
7568 : * Note that this query may well retrieve info about tables we aren't
7569 : * going to dump and hence have no lock on. That's okay since we need not
7570 : * invoke any unsafe server-side functions.
7571 : */
7572 282 : appendPQExpBufferStr(query,
7573 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7574 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7575 : "ON c.opcmethod = a.oid\n"
7576 : "WHERE opcname = 'enum_ops' "
7577 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7578 : "AND amname = 'hash') = ANY(partclass)");
7579 :
7580 282 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7581 :
7582 282 : ntups = PQntuples(res);
7583 :
7584 286 : for (int i = 0; i < ntups; i++)
7585 : {
7586 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7587 : TableInfo *tbinfo;
7588 :
7589 4 : tbinfo = findTableByOid(tabrelid);
7590 4 : if (tbinfo == NULL)
7591 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7592 : tabrelid);
7593 4 : tbinfo->unsafe_partitions = true;
7594 : }
7595 :
7596 282 : PQclear(res);
7597 :
7598 282 : destroyPQExpBuffer(query);
7599 : }
7600 :
7601 : /*
7602 : * getIndexes
7603 : * get information about every index on a dumpable table
7604 : *
7605 : * Note: index data is not returned directly to the caller, but it
7606 : * does get entered into the DumpableObject tables.
7607 : */
7608 : void
7609 354 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7610 : {
7611 354 : PQExpBuffer query = createPQExpBuffer();
7612 354 : PQExpBuffer tbloids = createPQExpBuffer();
7613 : PGresult *res;
7614 : int ntups;
7615 : int curtblindx;
7616 : IndxInfo *indxinfo;
7617 : int i_tableoid,
7618 : i_oid,
7619 : i_indrelid,
7620 : i_indexname,
7621 : i_relpages,
7622 : i_reltuples,
7623 : i_relallvisible,
7624 : i_relallfrozen,
7625 : i_parentidx,
7626 : i_indexdef,
7627 : i_indnkeyatts,
7628 : i_indnatts,
7629 : i_indkey,
7630 : i_indisclustered,
7631 : i_indisreplident,
7632 : i_indnullsnotdistinct,
7633 : i_contype,
7634 : i_conname,
7635 : i_condeferrable,
7636 : i_condeferred,
7637 : i_conperiod,
7638 : i_contableoid,
7639 : i_conoid,
7640 : i_condef,
7641 : i_indattnames,
7642 : i_tablespace,
7643 : i_indreloptions,
7644 : i_indstatcols,
7645 : i_indstatvals;
7646 :
7647 : /*
7648 : * We want to perform just one query against pg_index. However, we
7649 : * mustn't try to select every row of the catalog and then sort it out on
7650 : * the client side, because some of the server-side functions we need
7651 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7652 : * build an array of the OIDs of tables we care about (and now have lock
7653 : * on!), and use a WHERE clause to constrain which rows are selected.
7654 : */
7655 354 : appendPQExpBufferChar(tbloids, '{');
7656 92540 : for (int i = 0; i < numTables; i++)
7657 : {
7658 92186 : TableInfo *tbinfo = &tblinfo[i];
7659 :
7660 92186 : if (!tbinfo->hasindex)
7661 64718 : continue;
7662 :
7663 : /*
7664 : * We can ignore indexes of uninteresting tables.
7665 : */
7666 27468 : if (!tbinfo->interesting)
7667 23414 : continue;
7668 :
7669 : /* OK, we need info for this table */
7670 4054 : if (tbloids->len > 1) /* do we have more than the '{'? */
7671 3890 : appendPQExpBufferChar(tbloids, ',');
7672 4054 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7673 : }
7674 354 : appendPQExpBufferChar(tbloids, '}');
7675 :
7676 354 : appendPQExpBufferStr(query,
7677 : "SELECT t.tableoid, t.oid, i.indrelid, "
7678 : "t.relname AS indexname, "
7679 : "t.relpages, t.reltuples, t.relallvisible, ");
7680 :
7681 354 : if (fout->remoteVersion >= 180000)
7682 354 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7683 : else
7684 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7685 :
7686 354 : appendPQExpBufferStr(query,
7687 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7688 : "i.indkey, i.indisclustered, "
7689 : "c.contype, c.conname, "
7690 : "c.condeferrable, c.condeferred, "
7691 : "c.tableoid AS contableoid, "
7692 : "c.oid AS conoid, "
7693 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7694 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7695 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7696 : " FROM pg_catalog.pg_attribute "
7697 : " WHERE attrelid = i.indexrelid) "
7698 : "ELSE NULL END AS indattnames, "
7699 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7700 : "t.reloptions AS indreloptions, ");
7701 :
7702 :
7703 354 : if (fout->remoteVersion >= 90400)
7704 354 : appendPQExpBufferStr(query,
7705 : "i.indisreplident, ");
7706 : else
7707 0 : appendPQExpBufferStr(query,
7708 : "false AS indisreplident, ");
7709 :
7710 354 : if (fout->remoteVersion >= 110000)
7711 354 : appendPQExpBufferStr(query,
7712 : "inh.inhparent AS parentidx, "
7713 : "i.indnkeyatts AS indnkeyatts, "
7714 : "i.indnatts AS indnatts, "
7715 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7716 : " FROM pg_catalog.pg_attribute "
7717 : " WHERE attrelid = i.indexrelid AND "
7718 : " attstattarget >= 0) AS indstatcols, "
7719 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7720 : " FROM pg_catalog.pg_attribute "
7721 : " WHERE attrelid = i.indexrelid AND "
7722 : " attstattarget >= 0) AS indstatvals, ");
7723 : else
7724 0 : appendPQExpBufferStr(query,
7725 : "0 AS parentidx, "
7726 : "i.indnatts AS indnkeyatts, "
7727 : "i.indnatts AS indnatts, "
7728 : "'' AS indstatcols, "
7729 : "'' AS indstatvals, ");
7730 :
7731 354 : if (fout->remoteVersion >= 150000)
7732 354 : appendPQExpBufferStr(query,
7733 : "i.indnullsnotdistinct, ");
7734 : else
7735 0 : appendPQExpBufferStr(query,
7736 : "false AS indnullsnotdistinct, ");
7737 :
7738 354 : if (fout->remoteVersion >= 180000)
7739 354 : appendPQExpBufferStr(query,
7740 : "c.conperiod ");
7741 : else
7742 0 : appendPQExpBufferStr(query,
7743 : "NULL AS conperiod ");
7744 :
7745 : /*
7746 : * The point of the messy-looking outer join is to find a constraint that
7747 : * is related by an internal dependency link to the index. If we find one,
7748 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7749 : * index won't have more than one internal dependency.
7750 : *
7751 : * Note: the check on conrelid is redundant, but useful because that
7752 : * column is indexed while conindid is not.
7753 : */
7754 354 : if (fout->remoteVersion >= 110000)
7755 : {
7756 354 : appendPQExpBuffer(query,
7757 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7758 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7759 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7760 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7761 : "LEFT JOIN pg_catalog.pg_constraint c "
7762 : "ON (i.indrelid = c.conrelid AND "
7763 : "i.indexrelid = c.conindid AND "
7764 : "c.contype IN ('p','u','x')) "
7765 : "LEFT JOIN pg_catalog.pg_inherits inh "
7766 : "ON (inh.inhrelid = indexrelid) "
7767 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7768 : "AND i.indisready "
7769 : "ORDER BY i.indrelid, indexname",
7770 : tbloids->data);
7771 : }
7772 : else
7773 : {
7774 : /*
7775 : * the test on indisready is necessary in 9.2, and harmless in
7776 : * earlier/later versions
7777 : */
7778 0 : appendPQExpBuffer(query,
7779 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7780 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7781 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7782 : "LEFT JOIN pg_catalog.pg_constraint c "
7783 : "ON (i.indrelid = c.conrelid AND "
7784 : "i.indexrelid = c.conindid AND "
7785 : "c.contype IN ('p','u','x')) "
7786 : "WHERE i.indisvalid AND i.indisready "
7787 : "ORDER BY i.indrelid, indexname",
7788 : tbloids->data);
7789 : }
7790 :
7791 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7792 :
7793 354 : ntups = PQntuples(res);
7794 :
7795 354 : i_tableoid = PQfnumber(res, "tableoid");
7796 354 : i_oid = PQfnumber(res, "oid");
7797 354 : i_indrelid = PQfnumber(res, "indrelid");
7798 354 : i_indexname = PQfnumber(res, "indexname");
7799 354 : i_relpages = PQfnumber(res, "relpages");
7800 354 : i_reltuples = PQfnumber(res, "reltuples");
7801 354 : i_relallvisible = PQfnumber(res, "relallvisible");
7802 354 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7803 354 : i_parentidx = PQfnumber(res, "parentidx");
7804 354 : i_indexdef = PQfnumber(res, "indexdef");
7805 354 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7806 354 : i_indnatts = PQfnumber(res, "indnatts");
7807 354 : i_indkey = PQfnumber(res, "indkey");
7808 354 : i_indisclustered = PQfnumber(res, "indisclustered");
7809 354 : i_indisreplident = PQfnumber(res, "indisreplident");
7810 354 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7811 354 : i_contype = PQfnumber(res, "contype");
7812 354 : i_conname = PQfnumber(res, "conname");
7813 354 : i_condeferrable = PQfnumber(res, "condeferrable");
7814 354 : i_condeferred = PQfnumber(res, "condeferred");
7815 354 : i_conperiod = PQfnumber(res, "conperiod");
7816 354 : i_contableoid = PQfnumber(res, "contableoid");
7817 354 : i_conoid = PQfnumber(res, "conoid");
7818 354 : i_condef = PQfnumber(res, "condef");
7819 354 : i_indattnames = PQfnumber(res, "indattnames");
7820 354 : i_tablespace = PQfnumber(res, "tablespace");
7821 354 : i_indreloptions = PQfnumber(res, "indreloptions");
7822 354 : i_indstatcols = PQfnumber(res, "indstatcols");
7823 354 : i_indstatvals = PQfnumber(res, "indstatvals");
7824 :
7825 354 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7826 :
7827 : /*
7828 : * Outer loop iterates once per table, not once per row. Incrementing of
7829 : * j is handled by the inner loop.
7830 : */
7831 354 : curtblindx = -1;
7832 4368 : for (int j = 0; j < ntups;)
7833 : {
7834 4014 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7835 4014 : TableInfo *tbinfo = NULL;
7836 4014 : char **indAttNames = NULL;
7837 4014 : int nindAttNames = 0;
7838 : int numinds;
7839 :
7840 : /* Count rows for this table */
7841 5278 : for (numinds = 1; numinds < ntups - j; numinds++)
7842 5114 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7843 3850 : break;
7844 :
7845 : /*
7846 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7847 : * order.
7848 : */
7849 46952 : while (++curtblindx < numTables)
7850 : {
7851 46952 : tbinfo = &tblinfo[curtblindx];
7852 46952 : if (tbinfo->dobj.catId.oid == indrelid)
7853 4014 : break;
7854 : }
7855 4014 : if (curtblindx >= numTables)
7856 0 : pg_fatal("unrecognized table OID %u", indrelid);
7857 : /* cross-check that we only got requested tables */
7858 4014 : if (!tbinfo->hasindex ||
7859 4014 : !tbinfo->interesting)
7860 0 : pg_fatal("unexpected index data for table \"%s\"",
7861 : tbinfo->dobj.name);
7862 :
7863 : /* Save data for this table */
7864 4014 : tbinfo->indexes = indxinfo + j;
7865 4014 : tbinfo->numIndexes = numinds;
7866 :
7867 9292 : for (int c = 0; c < numinds; c++, j++)
7868 : {
7869 : char contype;
7870 : char indexkind;
7871 : RelStatsInfo *relstats;
7872 5278 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
7873 5278 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
7874 5278 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
7875 :
7876 5278 : indxinfo[j].dobj.objType = DO_INDEX;
7877 5278 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7878 5278 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7879 5278 : AssignDumpId(&indxinfo[j].dobj);
7880 5278 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7881 5278 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7882 5278 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7883 5278 : indxinfo[j].indextable = tbinfo;
7884 5278 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7885 5278 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7886 5278 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7887 5278 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7888 5278 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7889 5278 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7890 5278 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7891 5278 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7892 5278 : parseOidArray(PQgetvalue(res, j, i_indkey),
7893 5278 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7894 5278 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7895 5278 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7896 5278 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7897 5278 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7898 5278 : indxinfo[j].partattaches = (SimplePtrList)
7899 : {
7900 : NULL, NULL
7901 : };
7902 :
7903 5278 : if (indxinfo[j].parentidx == 0)
7904 4086 : indexkind = RELKIND_INDEX;
7905 : else
7906 1192 : indexkind = RELKIND_PARTITIONED_INDEX;
7907 :
7908 5278 : if (!PQgetisnull(res, j, i_indattnames))
7909 : {
7910 308 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
7911 : &indAttNames, &nindAttNames))
7912 0 : pg_fatal("could not parse %s array", "indattnames");
7913 : }
7914 :
7915 5278 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
7916 : PQgetvalue(res, j, i_reltuples),
7917 : relallvisible, relallfrozen, indexkind,
7918 : indAttNames, nindAttNames);
7919 :
7920 5278 : contype = *(PQgetvalue(res, j, i_contype));
7921 5278 : if (contype == 'p' || contype == 'u' || contype == 'x')
7922 3068 : {
7923 : /*
7924 : * If we found a constraint matching the index, create an
7925 : * entry for it.
7926 : */
7927 : ConstraintInfo *constrinfo;
7928 :
7929 3068 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7930 3068 : constrinfo->dobj.objType = DO_CONSTRAINT;
7931 3068 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7932 3068 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7933 3068 : AssignDumpId(&constrinfo->dobj);
7934 3068 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7935 3068 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7936 3068 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7937 3068 : constrinfo->contable = tbinfo;
7938 3068 : constrinfo->condomain = NULL;
7939 3068 : constrinfo->contype = contype;
7940 3068 : if (contype == 'x')
7941 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7942 : else
7943 3048 : constrinfo->condef = NULL;
7944 3068 : constrinfo->confrelid = InvalidOid;
7945 3068 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7946 3068 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7947 3068 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7948 3068 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7949 3068 : constrinfo->conislocal = true;
7950 3068 : constrinfo->separate = true;
7951 :
7952 3068 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7953 3068 : if (relstats != NULL)
7954 2420 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
7955 : }
7956 : else
7957 : {
7958 : /* Plain secondary index */
7959 2210 : indxinfo[j].indexconstraint = 0;
7960 : }
7961 : }
7962 : }
7963 :
7964 354 : PQclear(res);
7965 :
7966 354 : destroyPQExpBuffer(query);
7967 354 : destroyPQExpBuffer(tbloids);
7968 354 : }
7969 :
7970 : /*
7971 : * getExtendedStatistics
7972 : * get information about extended-statistics objects.
7973 : *
7974 : * Note: extended statistics data is not returned directly to the caller, but
7975 : * it does get entered into the DumpableObject tables.
7976 : */
7977 : void
7978 354 : getExtendedStatistics(Archive *fout)
7979 : {
7980 : PQExpBuffer query;
7981 : PGresult *res;
7982 : StatsExtInfo *statsextinfo;
7983 : int ntups;
7984 : int i_tableoid;
7985 : int i_oid;
7986 : int i_stxname;
7987 : int i_stxnamespace;
7988 : int i_stxowner;
7989 : int i_stxrelid;
7990 : int i_stattarget;
7991 : int i;
7992 :
7993 : /* Extended statistics were new in v10 */
7994 354 : if (fout->remoteVersion < 100000)
7995 0 : return;
7996 :
7997 354 : query = createPQExpBuffer();
7998 :
7999 354 : if (fout->remoteVersion < 130000)
8000 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8001 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8002 : "FROM pg_catalog.pg_statistic_ext");
8003 : else
8004 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8005 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8006 : "FROM pg_catalog.pg_statistic_ext");
8007 :
8008 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8009 :
8010 354 : ntups = PQntuples(res);
8011 :
8012 354 : i_tableoid = PQfnumber(res, "tableoid");
8013 354 : i_oid = PQfnumber(res, "oid");
8014 354 : i_stxname = PQfnumber(res, "stxname");
8015 354 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8016 354 : i_stxowner = PQfnumber(res, "stxowner");
8017 354 : i_stxrelid = PQfnumber(res, "stxrelid");
8018 354 : i_stattarget = PQfnumber(res, "stxstattarget");
8019 :
8020 354 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8021 :
8022 704 : for (i = 0; i < ntups; i++)
8023 : {
8024 350 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8025 350 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8026 350 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8027 350 : AssignDumpId(&statsextinfo[i].dobj);
8028 350 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8029 700 : statsextinfo[i].dobj.namespace =
8030 350 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8031 350 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8032 700 : statsextinfo[i].stattable =
8033 350 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8034 350 : if (PQgetisnull(res, i, i_stattarget))
8035 252 : statsextinfo[i].stattarget = -1;
8036 : else
8037 98 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8038 :
8039 : /* Decide whether we want to dump it */
8040 350 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8041 : }
8042 :
8043 354 : PQclear(res);
8044 354 : destroyPQExpBuffer(query);
8045 : }
8046 :
8047 : /*
8048 : * getConstraints
8049 : *
8050 : * Get info about constraints on dumpable tables.
8051 : *
8052 : * Currently handles foreign keys only.
8053 : * Unique and primary key constraints are handled with indexes,
8054 : * while check constraints are processed in getTableAttrs().
8055 : */
8056 : void
8057 354 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8058 : {
8059 354 : PQExpBuffer query = createPQExpBuffer();
8060 354 : PQExpBuffer tbloids = createPQExpBuffer();
8061 : PGresult *res;
8062 : int ntups;
8063 : int curtblindx;
8064 354 : TableInfo *tbinfo = NULL;
8065 : ConstraintInfo *constrinfo;
8066 : int i_contableoid,
8067 : i_conoid,
8068 : i_conrelid,
8069 : i_conname,
8070 : i_confrelid,
8071 : i_conindid,
8072 : i_condef;
8073 :
8074 : /*
8075 : * We want to perform just one query against pg_constraint. However, we
8076 : * mustn't try to select every row of the catalog and then sort it out on
8077 : * the client side, because some of the server-side functions we need
8078 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8079 : * build an array of the OIDs of tables we care about (and now have lock
8080 : * on!), and use a WHERE clause to constrain which rows are selected.
8081 : */
8082 354 : appendPQExpBufferChar(tbloids, '{');
8083 92540 : for (int i = 0; i < numTables; i++)
8084 : {
8085 92186 : TableInfo *tinfo = &tblinfo[i];
8086 :
8087 : /*
8088 : * For partitioned tables, foreign keys have no triggers so they must
8089 : * be included anyway in case some foreign keys are defined.
8090 : */
8091 92186 : if ((!tinfo->hastriggers &&
8092 89886 : tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
8093 3292 : !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8094 89684 : continue;
8095 :
8096 : /* OK, we need info for this table */
8097 2502 : if (tbloids->len > 1) /* do we have more than the '{'? */
8098 2388 : appendPQExpBufferChar(tbloids, ',');
8099 2502 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8100 : }
8101 354 : appendPQExpBufferChar(tbloids, '}');
8102 :
8103 354 : appendPQExpBufferStr(query,
8104 : "SELECT c.tableoid, c.oid, "
8105 : "conrelid, conname, confrelid, ");
8106 354 : if (fout->remoteVersion >= 110000)
8107 354 : appendPQExpBufferStr(query, "conindid, ");
8108 : else
8109 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8110 354 : appendPQExpBuffer(query,
8111 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8112 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8113 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8114 : "WHERE contype = 'f' ",
8115 : tbloids->data);
8116 354 : if (fout->remoteVersion >= 110000)
8117 354 : appendPQExpBufferStr(query,
8118 : "AND conparentid = 0 ");
8119 354 : appendPQExpBufferStr(query,
8120 : "ORDER BY conrelid, conname");
8121 :
8122 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8123 :
8124 354 : ntups = PQntuples(res);
8125 :
8126 354 : i_contableoid = PQfnumber(res, "tableoid");
8127 354 : i_conoid = PQfnumber(res, "oid");
8128 354 : i_conrelid = PQfnumber(res, "conrelid");
8129 354 : i_conname = PQfnumber(res, "conname");
8130 354 : i_confrelid = PQfnumber(res, "confrelid");
8131 354 : i_conindid = PQfnumber(res, "conindid");
8132 354 : i_condef = PQfnumber(res, "condef");
8133 :
8134 354 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8135 :
8136 354 : curtblindx = -1;
8137 712 : for (int j = 0; j < ntups; j++)
8138 : {
8139 358 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8140 : TableInfo *reftable;
8141 :
8142 : /*
8143 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8144 : * order.
8145 : */
8146 358 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8147 : {
8148 28452 : while (++curtblindx < numTables)
8149 : {
8150 28452 : tbinfo = &tblinfo[curtblindx];
8151 28452 : if (tbinfo->dobj.catId.oid == conrelid)
8152 338 : break;
8153 : }
8154 338 : if (curtblindx >= numTables)
8155 0 : pg_fatal("unrecognized table OID %u", conrelid);
8156 : }
8157 :
8158 358 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8159 358 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8160 358 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8161 358 : AssignDumpId(&constrinfo[j].dobj);
8162 358 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8163 358 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8164 358 : constrinfo[j].contable = tbinfo;
8165 358 : constrinfo[j].condomain = NULL;
8166 358 : constrinfo[j].contype = 'f';
8167 358 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8168 358 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8169 358 : constrinfo[j].conindex = 0;
8170 358 : constrinfo[j].condeferrable = false;
8171 358 : constrinfo[j].condeferred = false;
8172 358 : constrinfo[j].conislocal = true;
8173 358 : constrinfo[j].separate = true;
8174 :
8175 : /*
8176 : * Restoring an FK that points to a partitioned table requires that
8177 : * all partition indexes have been attached beforehand. Ensure that
8178 : * happens by making the constraint depend on each index partition
8179 : * attach object.
8180 : */
8181 358 : reftable = findTableByOid(constrinfo[j].confrelid);
8182 358 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8183 : {
8184 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8185 :
8186 40 : if (indexOid != InvalidOid)
8187 : {
8188 40 : for (int k = 0; k < reftable->numIndexes; k++)
8189 : {
8190 : IndxInfo *refidx;
8191 :
8192 : /* not our index? */
8193 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8194 0 : continue;
8195 :
8196 40 : refidx = &reftable->indexes[k];
8197 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8198 40 : break;
8199 : }
8200 : }
8201 : }
8202 : }
8203 :
8204 354 : PQclear(res);
8205 :
8206 354 : destroyPQExpBuffer(query);
8207 354 : destroyPQExpBuffer(tbloids);
8208 354 : }
8209 :
8210 : /*
8211 : * addConstrChildIdxDeps
8212 : *
8213 : * Recursive subroutine for getConstraints
8214 : *
8215 : * Given an object representing a foreign key constraint and an index on the
8216 : * partitioned table it references, mark the constraint object as dependent
8217 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8218 : * drilling down to their partitions if any. This ensures that the FK is not
8219 : * restored until the index is fully marked valid.
8220 : */
8221 : static void
8222 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8223 : {
8224 : SimplePtrListCell *cell;
8225 :
8226 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8227 :
8228 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8229 : {
8230 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8231 :
8232 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8233 :
8234 220 : if (attach->partitionIdx->partattaches.head != NULL)
8235 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8236 : }
8237 90 : }
8238 :
8239 : /*
8240 : * getDomainConstraints
8241 : *
8242 : * Get info about constraints on a domain.
8243 : */
8244 : static void
8245 294 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8246 : {
8247 : int i;
8248 : ConstraintInfo *constrinfo;
8249 294 : PQExpBuffer query = createPQExpBuffer();
8250 : PGresult *res;
8251 : int i_tableoid,
8252 : i_oid,
8253 : i_conname,
8254 : i_consrc;
8255 : int ntups;
8256 :
8257 294 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8258 : {
8259 : /* Set up query for constraint-specific details */
8260 94 : appendPQExpBufferStr(query,
8261 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8262 : "SELECT tableoid, oid, conname, "
8263 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8264 : "convalidated "
8265 : "FROM pg_catalog.pg_constraint "
8266 : "WHERE contypid = $1 AND contype = 'c' "
8267 : "ORDER BY conname");
8268 :
8269 94 : ExecuteSqlStatement(fout, query->data);
8270 :
8271 94 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8272 : }
8273 :
8274 294 : printfPQExpBuffer(query,
8275 : "EXECUTE getDomainConstraints('%u')",
8276 : tyinfo->dobj.catId.oid);
8277 :
8278 294 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8279 :
8280 294 : ntups = PQntuples(res);
8281 :
8282 294 : i_tableoid = PQfnumber(res, "tableoid");
8283 294 : i_oid = PQfnumber(res, "oid");
8284 294 : i_conname = PQfnumber(res, "conname");
8285 294 : i_consrc = PQfnumber(res, "consrc");
8286 :
8287 294 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8288 :
8289 294 : tyinfo->nDomChecks = ntups;
8290 294 : tyinfo->domChecks = constrinfo;
8291 :
8292 498 : for (i = 0; i < ntups; i++)
8293 : {
8294 204 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
8295 :
8296 204 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
8297 204 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8298 204 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8299 204 : AssignDumpId(&constrinfo[i].dobj);
8300 204 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8301 204 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8302 204 : constrinfo[i].contable = NULL;
8303 204 : constrinfo[i].condomain = tyinfo;
8304 204 : constrinfo[i].contype = 'c';
8305 204 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8306 204 : constrinfo[i].confrelid = InvalidOid;
8307 204 : constrinfo[i].conindex = 0;
8308 204 : constrinfo[i].condeferrable = false;
8309 204 : constrinfo[i].condeferred = false;
8310 204 : constrinfo[i].conislocal = true;
8311 :
8312 204 : constrinfo[i].separate = !validated;
8313 :
8314 : /*
8315 : * Make the domain depend on the constraint, ensuring it won't be
8316 : * output till any constraint dependencies are OK. If the constraint
8317 : * has not been validated, it's going to be dumped after the domain
8318 : * anyway, so this doesn't matter.
8319 : */
8320 204 : if (validated)
8321 204 : addObjectDependency(&tyinfo->dobj,
8322 204 : constrinfo[i].dobj.dumpId);
8323 : }
8324 :
8325 294 : PQclear(res);
8326 :
8327 294 : destroyPQExpBuffer(query);
8328 294 : }
8329 :
8330 : /*
8331 : * getRules
8332 : * get basic information about every rule in the system
8333 : */
8334 : void
8335 354 : getRules(Archive *fout)
8336 : {
8337 : PGresult *res;
8338 : int ntups;
8339 : int i;
8340 354 : PQExpBuffer query = createPQExpBuffer();
8341 : RuleInfo *ruleinfo;
8342 : int i_tableoid;
8343 : int i_oid;
8344 : int i_rulename;
8345 : int i_ruletable;
8346 : int i_ev_type;
8347 : int i_is_instead;
8348 : int i_ev_enabled;
8349 :
8350 354 : appendPQExpBufferStr(query, "SELECT "
8351 : "tableoid, oid, rulename, "
8352 : "ev_class AS ruletable, ev_type, is_instead, "
8353 : "ev_enabled "
8354 : "FROM pg_rewrite "
8355 : "ORDER BY oid");
8356 :
8357 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8358 :
8359 354 : ntups = PQntuples(res);
8360 :
8361 354 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8362 :
8363 354 : i_tableoid = PQfnumber(res, "tableoid");
8364 354 : i_oid = PQfnumber(res, "oid");
8365 354 : i_rulename = PQfnumber(res, "rulename");
8366 354 : i_ruletable = PQfnumber(res, "ruletable");
8367 354 : i_ev_type = PQfnumber(res, "ev_type");
8368 354 : i_is_instead = PQfnumber(res, "is_instead");
8369 354 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8370 :
8371 54120 : for (i = 0; i < ntups; i++)
8372 : {
8373 : Oid ruletableoid;
8374 :
8375 53766 : ruleinfo[i].dobj.objType = DO_RULE;
8376 53766 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8377 53766 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8378 53766 : AssignDumpId(&ruleinfo[i].dobj);
8379 53766 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8380 53766 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8381 53766 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8382 53766 : if (ruleinfo[i].ruletable == NULL)
8383 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8384 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8385 53766 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8386 53766 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8387 53766 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8388 53766 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8389 53766 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8390 53766 : if (ruleinfo[i].ruletable)
8391 : {
8392 : /*
8393 : * If the table is a view or materialized view, force its ON
8394 : * SELECT rule to be sorted before the view itself --- this
8395 : * ensures that any dependencies for the rule affect the table's
8396 : * positioning. Other rules are forced to appear after their
8397 : * table.
8398 : */
8399 53766 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8400 1554 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8401 53304 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8402 : {
8403 52512 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8404 52512 : ruleinfo[i].dobj.dumpId);
8405 : /* We'll merge the rule into CREATE VIEW, if possible */
8406 52512 : ruleinfo[i].separate = false;
8407 : }
8408 : else
8409 : {
8410 1254 : addObjectDependency(&ruleinfo[i].dobj,
8411 1254 : ruleinfo[i].ruletable->dobj.dumpId);
8412 1254 : ruleinfo[i].separate = true;
8413 : }
8414 : }
8415 : else
8416 0 : ruleinfo[i].separate = true;
8417 : }
8418 :
8419 354 : PQclear(res);
8420 :
8421 354 : destroyPQExpBuffer(query);
8422 354 : }
8423 :
8424 : /*
8425 : * getTriggers
8426 : * get information about every trigger on a dumpable table
8427 : *
8428 : * Note: trigger data is not returned directly to the caller, but it
8429 : * does get entered into the DumpableObject tables.
8430 : */
8431 : void
8432 354 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8433 : {
8434 354 : PQExpBuffer query = createPQExpBuffer();
8435 354 : PQExpBuffer tbloids = createPQExpBuffer();
8436 : PGresult *res;
8437 : int ntups;
8438 : int curtblindx;
8439 : TriggerInfo *tginfo;
8440 : int i_tableoid,
8441 : i_oid,
8442 : i_tgrelid,
8443 : i_tgname,
8444 : i_tgenabled,
8445 : i_tgispartition,
8446 : i_tgdef;
8447 :
8448 : /*
8449 : * We want to perform just one query against pg_trigger. However, we
8450 : * mustn't try to select every row of the catalog and then sort it out on
8451 : * the client side, because some of the server-side functions we need
8452 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8453 : * build an array of the OIDs of tables we care about (and now have lock
8454 : * on!), and use a WHERE clause to constrain which rows are selected.
8455 : */
8456 354 : appendPQExpBufferChar(tbloids, '{');
8457 92540 : for (int i = 0; i < numTables; i++)
8458 : {
8459 92186 : TableInfo *tbinfo = &tblinfo[i];
8460 :
8461 92186 : if (!tbinfo->hastriggers ||
8462 2300 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8463 90410 : continue;
8464 :
8465 : /* OK, we need info for this table */
8466 1776 : if (tbloids->len > 1) /* do we have more than the '{'? */
8467 1666 : appendPQExpBufferChar(tbloids, ',');
8468 1776 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8469 : }
8470 354 : appendPQExpBufferChar(tbloids, '}');
8471 :
8472 354 : if (fout->remoteVersion >= 150000)
8473 : {
8474 : /*
8475 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8476 : * result in non-forward-compatible dumps of WHEN clauses due to
8477 : * under-parenthesization.
8478 : *
8479 : * NB: We need to see partition triggers in case the tgenabled flag
8480 : * has been changed from the parent.
8481 : */
8482 354 : appendPQExpBuffer(query,
8483 : "SELECT t.tgrelid, t.tgname, "
8484 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8485 : "t.tgenabled, t.tableoid, t.oid, "
8486 : "t.tgparentid <> 0 AS tgispartition\n"
8487 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8488 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8489 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8490 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8491 : "OR t.tgenabled != u.tgenabled) "
8492 : "ORDER BY t.tgrelid, t.tgname",
8493 : tbloids->data);
8494 : }
8495 0 : else if (fout->remoteVersion >= 130000)
8496 : {
8497 : /*
8498 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8499 : * result in non-forward-compatible dumps of WHEN clauses due to
8500 : * under-parenthesization.
8501 : *
8502 : * NB: We need to see tgisinternal triggers in partitions, in case the
8503 : * tgenabled flag has been changed from the parent.
8504 : */
8505 0 : appendPQExpBuffer(query,
8506 : "SELECT t.tgrelid, t.tgname, "
8507 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8508 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8509 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8510 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8511 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8512 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8513 : "ORDER BY t.tgrelid, t.tgname",
8514 : tbloids->data);
8515 : }
8516 0 : else if (fout->remoteVersion >= 110000)
8517 : {
8518 : /*
8519 : * NB: We need to see tgisinternal triggers in partitions, in case the
8520 : * tgenabled flag has been changed from the parent. No tgparentid in
8521 : * version 11-12, so we have to match them via pg_depend.
8522 : *
8523 : * See above about pretty=true in pg_get_triggerdef.
8524 : */
8525 0 : appendPQExpBuffer(query,
8526 : "SELECT t.tgrelid, t.tgname, "
8527 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8528 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8529 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8530 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8531 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8532 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8533 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8534 : " d.objid = t.oid "
8535 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8536 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8537 : "ORDER BY t.tgrelid, t.tgname",
8538 : tbloids->data);
8539 : }
8540 : else
8541 : {
8542 : /* See above about pretty=true in pg_get_triggerdef */
8543 0 : appendPQExpBuffer(query,
8544 : "SELECT t.tgrelid, t.tgname, "
8545 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8546 : "t.tgenabled, false as tgispartition, "
8547 : "t.tableoid, t.oid "
8548 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8549 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8550 : "WHERE NOT tgisinternal "
8551 : "ORDER BY t.tgrelid, t.tgname",
8552 : tbloids->data);
8553 : }
8554 :
8555 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8556 :
8557 354 : ntups = PQntuples(res);
8558 :
8559 354 : i_tableoid = PQfnumber(res, "tableoid");
8560 354 : i_oid = PQfnumber(res, "oid");
8561 354 : i_tgrelid = PQfnumber(res, "tgrelid");
8562 354 : i_tgname = PQfnumber(res, "tgname");
8563 354 : i_tgenabled = PQfnumber(res, "tgenabled");
8564 354 : i_tgispartition = PQfnumber(res, "tgispartition");
8565 354 : i_tgdef = PQfnumber(res, "tgdef");
8566 :
8567 354 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8568 :
8569 : /*
8570 : * Outer loop iterates once per table, not once per row. Incrementing of
8571 : * j is handled by the inner loop.
8572 : */
8573 354 : curtblindx = -1;
8574 1006 : for (int j = 0; j < ntups;)
8575 : {
8576 652 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8577 652 : TableInfo *tbinfo = NULL;
8578 : int numtrigs;
8579 :
8580 : /* Count rows for this table */
8581 1086 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8582 976 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8583 542 : break;
8584 :
8585 : /*
8586 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8587 : * order.
8588 : */
8589 33628 : while (++curtblindx < numTables)
8590 : {
8591 33628 : tbinfo = &tblinfo[curtblindx];
8592 33628 : if (tbinfo->dobj.catId.oid == tgrelid)
8593 652 : break;
8594 : }
8595 652 : if (curtblindx >= numTables)
8596 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8597 :
8598 : /* Save data for this table */
8599 652 : tbinfo->triggers = tginfo + j;
8600 652 : tbinfo->numTriggers = numtrigs;
8601 :
8602 1738 : for (int c = 0; c < numtrigs; c++, j++)
8603 : {
8604 1086 : tginfo[j].dobj.objType = DO_TRIGGER;
8605 1086 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8606 1086 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8607 1086 : AssignDumpId(&tginfo[j].dobj);
8608 1086 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8609 1086 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8610 1086 : tginfo[j].tgtable = tbinfo;
8611 1086 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8612 1086 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8613 1086 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8614 : }
8615 : }
8616 :
8617 354 : PQclear(res);
8618 :
8619 354 : destroyPQExpBuffer(query);
8620 354 : destroyPQExpBuffer(tbloids);
8621 354 : }
8622 :
8623 : /*
8624 : * getEventTriggers
8625 : * get information about event triggers
8626 : */
8627 : void
8628 354 : getEventTriggers(Archive *fout)
8629 : {
8630 : int i;
8631 : PQExpBuffer query;
8632 : PGresult *res;
8633 : EventTriggerInfo *evtinfo;
8634 : int i_tableoid,
8635 : i_oid,
8636 : i_evtname,
8637 : i_evtevent,
8638 : i_evtowner,
8639 : i_evttags,
8640 : i_evtfname,
8641 : i_evtenabled;
8642 : int ntups;
8643 :
8644 : /* Before 9.3, there are no event triggers */
8645 354 : if (fout->remoteVersion < 90300)
8646 0 : return;
8647 :
8648 354 : query = createPQExpBuffer();
8649 :
8650 354 : appendPQExpBufferStr(query,
8651 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8652 : "evtevent, evtowner, "
8653 : "array_to_string(array("
8654 : "select quote_literal(x) "
8655 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8656 : "e.evtfoid::regproc as evtfname "
8657 : "FROM pg_event_trigger e "
8658 : "ORDER BY e.oid");
8659 :
8660 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8661 :
8662 354 : ntups = PQntuples(res);
8663 :
8664 354 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8665 :
8666 354 : i_tableoid = PQfnumber(res, "tableoid");
8667 354 : i_oid = PQfnumber(res, "oid");
8668 354 : i_evtname = PQfnumber(res, "evtname");
8669 354 : i_evtevent = PQfnumber(res, "evtevent");
8670 354 : i_evtowner = PQfnumber(res, "evtowner");
8671 354 : i_evttags = PQfnumber(res, "evttags");
8672 354 : i_evtfname = PQfnumber(res, "evtfname");
8673 354 : i_evtenabled = PQfnumber(res, "evtenabled");
8674 :
8675 466 : for (i = 0; i < ntups; i++)
8676 : {
8677 112 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8678 112 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8679 112 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8680 112 : AssignDumpId(&evtinfo[i].dobj);
8681 112 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8682 112 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8683 112 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8684 112 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8685 112 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8686 112 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8687 112 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8688 :
8689 : /* Decide whether we want to dump it */
8690 112 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8691 : }
8692 :
8693 354 : PQclear(res);
8694 :
8695 354 : destroyPQExpBuffer(query);
8696 : }
8697 :
8698 : /*
8699 : * getProcLangs
8700 : * get basic information about every procedural language in the system
8701 : *
8702 : * NB: this must run after getFuncs() because we assume we can do
8703 : * findFuncByOid().
8704 : */
8705 : void
8706 354 : getProcLangs(Archive *fout)
8707 : {
8708 : PGresult *res;
8709 : int ntups;
8710 : int i;
8711 354 : PQExpBuffer query = createPQExpBuffer();
8712 : ProcLangInfo *planginfo;
8713 : int i_tableoid;
8714 : int i_oid;
8715 : int i_lanname;
8716 : int i_lanpltrusted;
8717 : int i_lanplcallfoid;
8718 : int i_laninline;
8719 : int i_lanvalidator;
8720 : int i_lanacl;
8721 : int i_acldefault;
8722 : int i_lanowner;
8723 :
8724 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8725 : "lanname, lanpltrusted, lanplcallfoid, "
8726 : "laninline, lanvalidator, "
8727 : "lanacl, "
8728 : "acldefault('l', lanowner) AS acldefault, "
8729 : "lanowner "
8730 : "FROM pg_language "
8731 : "WHERE lanispl "
8732 : "ORDER BY oid");
8733 :
8734 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8735 :
8736 354 : ntups = PQntuples(res);
8737 :
8738 354 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8739 :
8740 354 : i_tableoid = PQfnumber(res, "tableoid");
8741 354 : i_oid = PQfnumber(res, "oid");
8742 354 : i_lanname = PQfnumber(res, "lanname");
8743 354 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8744 354 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8745 354 : i_laninline = PQfnumber(res, "laninline");
8746 354 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8747 354 : i_lanacl = PQfnumber(res, "lanacl");
8748 354 : i_acldefault = PQfnumber(res, "acldefault");
8749 354 : i_lanowner = PQfnumber(res, "lanowner");
8750 :
8751 806 : for (i = 0; i < ntups; i++)
8752 : {
8753 452 : planginfo[i].dobj.objType = DO_PROCLANG;
8754 452 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8755 452 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8756 452 : AssignDumpId(&planginfo[i].dobj);
8757 :
8758 452 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8759 452 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8760 452 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8761 452 : planginfo[i].dacl.privtype = 0;
8762 452 : planginfo[i].dacl.initprivs = NULL;
8763 452 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8764 452 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8765 452 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8766 452 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8767 452 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8768 :
8769 : /* Decide whether we want to dump it */
8770 452 : selectDumpableProcLang(&(planginfo[i]), fout);
8771 :
8772 : /* Mark whether language has an ACL */
8773 452 : if (!PQgetisnull(res, i, i_lanacl))
8774 98 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8775 : }
8776 :
8777 354 : PQclear(res);
8778 :
8779 354 : destroyPQExpBuffer(query);
8780 354 : }
8781 :
8782 : /*
8783 : * getCasts
8784 : * get basic information about most casts in the system
8785 : *
8786 : * Skip casts from a range to its multirange, since we'll create those
8787 : * automatically.
8788 : */
8789 : void
8790 354 : getCasts(Archive *fout)
8791 : {
8792 : PGresult *res;
8793 : int ntups;
8794 : int i;
8795 354 : PQExpBuffer query = createPQExpBuffer();
8796 : CastInfo *castinfo;
8797 : int i_tableoid;
8798 : int i_oid;
8799 : int i_castsource;
8800 : int i_casttarget;
8801 : int i_castfunc;
8802 : int i_castcontext;
8803 : int i_castmethod;
8804 :
8805 354 : if (fout->remoteVersion >= 140000)
8806 : {
8807 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8808 : "castsource, casttarget, castfunc, castcontext, "
8809 : "castmethod "
8810 : "FROM pg_cast c "
8811 : "WHERE NOT EXISTS ( "
8812 : "SELECT 1 FROM pg_range r "
8813 : "WHERE c.castsource = r.rngtypid "
8814 : "AND c.casttarget = r.rngmultitypid "
8815 : ") "
8816 : "ORDER BY 3,4");
8817 : }
8818 : else
8819 : {
8820 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8821 : "castsource, casttarget, castfunc, castcontext, "
8822 : "castmethod "
8823 : "FROM pg_cast ORDER BY 3,4");
8824 : }
8825 :
8826 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8827 :
8828 354 : ntups = PQntuples(res);
8829 :
8830 354 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8831 :
8832 354 : i_tableoid = PQfnumber(res, "tableoid");
8833 354 : i_oid = PQfnumber(res, "oid");
8834 354 : i_castsource = PQfnumber(res, "castsource");
8835 354 : i_casttarget = PQfnumber(res, "casttarget");
8836 354 : i_castfunc = PQfnumber(res, "castfunc");
8837 354 : i_castcontext = PQfnumber(res, "castcontext");
8838 354 : i_castmethod = PQfnumber(res, "castmethod");
8839 :
8840 81602 : for (i = 0; i < ntups; i++)
8841 : {
8842 : PQExpBufferData namebuf;
8843 : TypeInfo *sTypeInfo;
8844 : TypeInfo *tTypeInfo;
8845 :
8846 81248 : castinfo[i].dobj.objType = DO_CAST;
8847 81248 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8848 81248 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8849 81248 : AssignDumpId(&castinfo[i].dobj);
8850 81248 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8851 81248 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8852 81248 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8853 81248 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8854 81248 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8855 :
8856 : /*
8857 : * Try to name cast as concatenation of typnames. This is only used
8858 : * for purposes of sorting. If we fail to find either type, the name
8859 : * will be an empty string.
8860 : */
8861 81248 : initPQExpBuffer(&namebuf);
8862 81248 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8863 81248 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8864 81248 : if (sTypeInfo && tTypeInfo)
8865 81248 : appendPQExpBuffer(&namebuf, "%s %s",
8866 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8867 81248 : castinfo[i].dobj.name = namebuf.data;
8868 :
8869 : /* Decide whether we want to dump it */
8870 81248 : selectDumpableCast(&(castinfo[i]), fout);
8871 : }
8872 :
8873 354 : PQclear(res);
8874 :
8875 354 : destroyPQExpBuffer(query);
8876 354 : }
8877 :
8878 : static char *
8879 192 : get_language_name(Archive *fout, Oid langid)
8880 : {
8881 : PQExpBuffer query;
8882 : PGresult *res;
8883 : char *lanname;
8884 :
8885 192 : query = createPQExpBuffer();
8886 192 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8887 192 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8888 192 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8889 192 : destroyPQExpBuffer(query);
8890 192 : PQclear(res);
8891 :
8892 192 : return lanname;
8893 : }
8894 :
8895 : /*
8896 : * getTransforms
8897 : * get basic information about every transform in the system
8898 : */
8899 : void
8900 354 : getTransforms(Archive *fout)
8901 : {
8902 : PGresult *res;
8903 : int ntups;
8904 : int i;
8905 : PQExpBuffer query;
8906 : TransformInfo *transforminfo;
8907 : int i_tableoid;
8908 : int i_oid;
8909 : int i_trftype;
8910 : int i_trflang;
8911 : int i_trffromsql;
8912 : int i_trftosql;
8913 :
8914 : /* Transforms didn't exist pre-9.5 */
8915 354 : if (fout->remoteVersion < 90500)
8916 0 : return;
8917 :
8918 354 : query = createPQExpBuffer();
8919 :
8920 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8921 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8922 : "FROM pg_transform "
8923 : "ORDER BY 3,4");
8924 :
8925 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8926 :
8927 354 : ntups = PQntuples(res);
8928 :
8929 354 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8930 :
8931 354 : i_tableoid = PQfnumber(res, "tableoid");
8932 354 : i_oid = PQfnumber(res, "oid");
8933 354 : i_trftype = PQfnumber(res, "trftype");
8934 354 : i_trflang = PQfnumber(res, "trflang");
8935 354 : i_trffromsql = PQfnumber(res, "trffromsql");
8936 354 : i_trftosql = PQfnumber(res, "trftosql");
8937 :
8938 466 : for (i = 0; i < ntups; i++)
8939 : {
8940 : PQExpBufferData namebuf;
8941 : TypeInfo *typeInfo;
8942 : char *lanname;
8943 :
8944 112 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8945 112 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8946 112 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8947 112 : AssignDumpId(&transforminfo[i].dobj);
8948 112 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8949 112 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8950 112 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8951 112 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8952 :
8953 : /*
8954 : * Try to name transform as concatenation of type and language name.
8955 : * This is only used for purposes of sorting. If we fail to find
8956 : * either, the name will be an empty string.
8957 : */
8958 112 : initPQExpBuffer(&namebuf);
8959 112 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8960 112 : lanname = get_language_name(fout, transforminfo[i].trflang);
8961 112 : if (typeInfo && lanname)
8962 112 : appendPQExpBuffer(&namebuf, "%s %s",
8963 : typeInfo->dobj.name, lanname);
8964 112 : transforminfo[i].dobj.name = namebuf.data;
8965 112 : free(lanname);
8966 :
8967 : /* Decide whether we want to dump it */
8968 112 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8969 : }
8970 :
8971 354 : PQclear(res);
8972 :
8973 354 : destroyPQExpBuffer(query);
8974 : }
8975 :
8976 : /*
8977 : * getTableAttrs -
8978 : * for each interesting table, read info about its attributes
8979 : * (names, types, default values, CHECK constraints, etc)
8980 : *
8981 : * modifies tblinfo
8982 : */
8983 : void
8984 354 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8985 : {
8986 354 : DumpOptions *dopt = fout->dopt;
8987 354 : PQExpBuffer q = createPQExpBuffer();
8988 354 : PQExpBuffer tbloids = createPQExpBuffer();
8989 354 : PQExpBuffer checkoids = createPQExpBuffer();
8990 : PGresult *res;
8991 : int ntups;
8992 : int curtblindx;
8993 : int i_attrelid;
8994 : int i_attnum;
8995 : int i_attname;
8996 : int i_atttypname;
8997 : int i_attstattarget;
8998 : int i_attstorage;
8999 : int i_typstorage;
9000 : int i_attidentity;
9001 : int i_attgenerated;
9002 : int i_attisdropped;
9003 : int i_attlen;
9004 : int i_attalign;
9005 : int i_attislocal;
9006 : int i_notnull_name;
9007 : int i_notnull_noinherit;
9008 : int i_notnull_islocal;
9009 : int i_attoptions;
9010 : int i_attcollation;
9011 : int i_attcompression;
9012 : int i_attfdwoptions;
9013 : int i_attmissingval;
9014 : int i_atthasdef;
9015 :
9016 : /*
9017 : * We want to perform just one query against pg_attribute, and then just
9018 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9019 : * (for CHECK constraints and for NOT NULL constraints). However, we
9020 : * mustn't try to select every row of those catalogs and then sort it out
9021 : * on the client side, because some of the server-side functions we need
9022 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9023 : * build an array of the OIDs of tables we care about (and now have lock
9024 : * on!), and use a WHERE clause to constrain which rows are selected.
9025 : */
9026 354 : appendPQExpBufferChar(tbloids, '{');
9027 354 : appendPQExpBufferChar(checkoids, '{');
9028 92540 : for (int i = 0; i < numTables; i++)
9029 : {
9030 92186 : TableInfo *tbinfo = &tblinfo[i];
9031 :
9032 : /* Don't bother to collect info for sequences */
9033 92186 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9034 1300 : continue;
9035 :
9036 : /* Don't bother with uninteresting tables, either */
9037 90886 : if (!tbinfo->interesting)
9038 78142 : continue;
9039 :
9040 : /* OK, we need info for this table */
9041 12744 : if (tbloids->len > 1) /* do we have more than the '{'? */
9042 12508 : appendPQExpBufferChar(tbloids, ',');
9043 12744 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9044 :
9045 12744 : if (tbinfo->ncheck > 0)
9046 : {
9047 : /* Also make a list of the ones with check constraints */
9048 1140 : if (checkoids->len > 1) /* do we have more than the '{'? */
9049 994 : appendPQExpBufferChar(checkoids, ',');
9050 1140 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9051 : }
9052 : }
9053 354 : appendPQExpBufferChar(tbloids, '}');
9054 354 : appendPQExpBufferChar(checkoids, '}');
9055 :
9056 : /*
9057 : * Find all the user attributes and their types.
9058 : *
9059 : * Since we only want to dump COLLATE clauses for attributes whose
9060 : * collation is different from their type's default, we use a CASE here to
9061 : * suppress uninteresting attcollations cheaply.
9062 : */
9063 354 : appendPQExpBufferStr(q,
9064 : "SELECT\n"
9065 : "a.attrelid,\n"
9066 : "a.attnum,\n"
9067 : "a.attname,\n"
9068 : "a.attstattarget,\n"
9069 : "a.attstorage,\n"
9070 : "t.typstorage,\n"
9071 : "a.atthasdef,\n"
9072 : "a.attisdropped,\n"
9073 : "a.attlen,\n"
9074 : "a.attalign,\n"
9075 : "a.attislocal,\n"
9076 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9077 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9078 : "CASE WHEN a.attcollation <> t.typcollation "
9079 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9080 : "pg_catalog.array_to_string(ARRAY("
9081 : "SELECT pg_catalog.quote_ident(option_name) || "
9082 : "' ' || pg_catalog.quote_literal(option_value) "
9083 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9084 : "ORDER BY option_name"
9085 : "), E',\n ') AS attfdwoptions,\n");
9086 :
9087 : /*
9088 : * Find out any NOT NULL markings for each column. In 18 and up we read
9089 : * pg_constraint to obtain the constraint name. notnull_noinherit is set
9090 : * according to the NO INHERIT property. For versions prior to 18, we
9091 : * store an empty string as the name when a constraint is marked as
9092 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9093 : * without a name); also, such cases are never NO INHERIT.
9094 : *
9095 : * We track in notnull_islocal whether the constraint was defined directly
9096 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9097 : * might modify this later for servers older than 18; it's also in charge
9098 : * of determining the correct inhcount.
9099 : */
9100 354 : if (fout->remoteVersion >= 180000)
9101 354 : appendPQExpBufferStr(q,
9102 : "co.conname AS notnull_name,\n"
9103 : "co.connoinherit AS notnull_noinherit,\n"
9104 : "co.conislocal AS notnull_islocal,\n");
9105 : else
9106 0 : appendPQExpBufferStr(q,
9107 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9108 : "false AS notnull_noinherit,\n"
9109 : "a.attislocal AS notnull_islocal,\n");
9110 :
9111 354 : if (fout->remoteVersion >= 140000)
9112 354 : appendPQExpBufferStr(q,
9113 : "a.attcompression AS attcompression,\n");
9114 : else
9115 0 : appendPQExpBufferStr(q,
9116 : "'' AS attcompression,\n");
9117 :
9118 354 : if (fout->remoteVersion >= 100000)
9119 354 : appendPQExpBufferStr(q,
9120 : "a.attidentity,\n");
9121 : else
9122 0 : appendPQExpBufferStr(q,
9123 : "'' AS attidentity,\n");
9124 :
9125 354 : if (fout->remoteVersion >= 110000)
9126 354 : appendPQExpBufferStr(q,
9127 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9128 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9129 : else
9130 0 : appendPQExpBufferStr(q,
9131 : "NULL AS attmissingval,\n");
9132 :
9133 354 : if (fout->remoteVersion >= 120000)
9134 354 : appendPQExpBufferStr(q,
9135 : "a.attgenerated\n");
9136 : else
9137 0 : appendPQExpBufferStr(q,
9138 : "'' AS attgenerated\n");
9139 :
9140 : /* need left join to pg_type to not fail on dropped columns ... */
9141 354 : appendPQExpBuffer(q,
9142 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9143 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9144 : "LEFT JOIN pg_catalog.pg_type t "
9145 : "ON (a.atttypid = t.oid)\n",
9146 : tbloids->data);
9147 :
9148 : /*
9149 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9150 : * entries. Also, we need to know if the NOT NULL for each column is
9151 : * backing a primary key.
9152 : */
9153 354 : if (fout->remoteVersion >= 180000)
9154 354 : appendPQExpBufferStr(q,
9155 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9156 : "(a.attrelid = co.conrelid\n"
9157 : " AND co.contype = 'n' AND "
9158 : "co.conkey = array[a.attnum])\n");
9159 :
9160 354 : appendPQExpBufferStr(q,
9161 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9162 : "ORDER BY a.attrelid, a.attnum");
9163 :
9164 354 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9165 :
9166 354 : ntups = PQntuples(res);
9167 :
9168 354 : i_attrelid = PQfnumber(res, "attrelid");
9169 354 : i_attnum = PQfnumber(res, "attnum");
9170 354 : i_attname = PQfnumber(res, "attname");
9171 354 : i_atttypname = PQfnumber(res, "atttypname");
9172 354 : i_attstattarget = PQfnumber(res, "attstattarget");
9173 354 : i_attstorage = PQfnumber(res, "attstorage");
9174 354 : i_typstorage = PQfnumber(res, "typstorage");
9175 354 : i_attidentity = PQfnumber(res, "attidentity");
9176 354 : i_attgenerated = PQfnumber(res, "attgenerated");
9177 354 : i_attisdropped = PQfnumber(res, "attisdropped");
9178 354 : i_attlen = PQfnumber(res, "attlen");
9179 354 : i_attalign = PQfnumber(res, "attalign");
9180 354 : i_attislocal = PQfnumber(res, "attislocal");
9181 354 : i_notnull_name = PQfnumber(res, "notnull_name");
9182 354 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9183 354 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9184 354 : i_attoptions = PQfnumber(res, "attoptions");
9185 354 : i_attcollation = PQfnumber(res, "attcollation");
9186 354 : i_attcompression = PQfnumber(res, "attcompression");
9187 354 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9188 354 : i_attmissingval = PQfnumber(res, "attmissingval");
9189 354 : i_atthasdef = PQfnumber(res, "atthasdef");
9190 :
9191 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9192 354 : resetPQExpBuffer(tbloids);
9193 354 : appendPQExpBufferChar(tbloids, '{');
9194 :
9195 : /*
9196 : * Outer loop iterates once per table, not once per row. Incrementing of
9197 : * r is handled by the inner loop.
9198 : */
9199 354 : curtblindx = -1;
9200 12798 : for (int r = 0; r < ntups;)
9201 : {
9202 12444 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9203 12444 : TableInfo *tbinfo = NULL;
9204 : int numatts;
9205 : bool hasdefaults;
9206 :
9207 : /* Count rows for this table */
9208 48128 : for (numatts = 1; numatts < ntups - r; numatts++)
9209 47898 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9210 12214 : break;
9211 :
9212 : /*
9213 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9214 : * order.
9215 : */
9216 61810 : while (++curtblindx < numTables)
9217 : {
9218 61810 : tbinfo = &tblinfo[curtblindx];
9219 61810 : if (tbinfo->dobj.catId.oid == attrelid)
9220 12444 : break;
9221 : }
9222 12444 : if (curtblindx >= numTables)
9223 0 : pg_fatal("unrecognized table OID %u", attrelid);
9224 : /* cross-check that we only got requested tables */
9225 12444 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9226 12444 : !tbinfo->interesting)
9227 0 : pg_fatal("unexpected column data for table \"%s\"",
9228 : tbinfo->dobj.name);
9229 :
9230 : /* Save data for this table */
9231 12444 : tbinfo->numatts = numatts;
9232 12444 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9233 12444 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9234 12444 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9235 12444 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9236 12444 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9237 12444 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9238 12444 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9239 12444 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9240 12444 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9241 12444 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9242 12444 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9243 12444 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9244 12444 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9245 12444 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9246 12444 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9247 12444 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9248 12444 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9249 12444 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9250 12444 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9251 12444 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9252 12444 : hasdefaults = false;
9253 :
9254 60572 : for (int j = 0; j < numatts; j++, r++)
9255 : {
9256 48128 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9257 0 : pg_fatal("invalid column numbering in table \"%s\"",
9258 : tbinfo->dobj.name);
9259 48128 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9260 48128 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9261 48128 : if (PQgetisnull(res, r, i_attstattarget))
9262 48040 : tbinfo->attstattarget[j] = -1;
9263 : else
9264 88 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9265 48128 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9266 48128 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9267 48128 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9268 48128 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9269 48128 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9270 48128 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9271 48128 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9272 48128 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9273 48128 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9274 :
9275 : /* Handle not-null constraint name and flags */
9276 48128 : determineNotNullFlags(fout, res, r,
9277 : tbinfo, j,
9278 : i_notnull_name, i_notnull_noinherit,
9279 : i_notnull_islocal);
9280 :
9281 48128 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9282 48128 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9283 48128 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9284 48128 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9285 48128 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9286 48128 : tbinfo->attrdefs[j] = NULL; /* fix below */
9287 48128 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9288 2644 : hasdefaults = true;
9289 : }
9290 :
9291 12444 : if (hasdefaults)
9292 : {
9293 : /* Collect OIDs of interesting tables that have defaults */
9294 1972 : if (tbloids->len > 1) /* do we have more than the '{'? */
9295 1828 : appendPQExpBufferChar(tbloids, ',');
9296 1972 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9297 : }
9298 : }
9299 :
9300 354 : PQclear(res);
9301 :
9302 : /*
9303 : * Now get info about column defaults. This is skipped for a data-only
9304 : * dump, as it is only needed for table schemas.
9305 : */
9306 354 : if (dopt->dumpSchema && tbloids->len > 1)
9307 : {
9308 : AttrDefInfo *attrdefs;
9309 : int numDefaults;
9310 128 : TableInfo *tbinfo = NULL;
9311 :
9312 128 : pg_log_info("finding table default expressions");
9313 :
9314 128 : appendPQExpBufferChar(tbloids, '}');
9315 :
9316 128 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9317 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9318 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9319 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9320 : "ORDER BY a.adrelid, a.adnum",
9321 : tbloids->data);
9322 :
9323 128 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9324 :
9325 128 : numDefaults = PQntuples(res);
9326 128 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9327 :
9328 128 : curtblindx = -1;
9329 2576 : for (int j = 0; j < numDefaults; j++)
9330 : {
9331 2448 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9332 2448 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9333 2448 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9334 2448 : int adnum = atoi(PQgetvalue(res, j, 3));
9335 2448 : char *adsrc = PQgetvalue(res, j, 4);
9336 :
9337 : /*
9338 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9339 : * OID order.
9340 : */
9341 2448 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9342 : {
9343 38844 : while (++curtblindx < numTables)
9344 : {
9345 38844 : tbinfo = &tblinfo[curtblindx];
9346 38844 : if (tbinfo->dobj.catId.oid == adrelid)
9347 1836 : break;
9348 : }
9349 1836 : if (curtblindx >= numTables)
9350 0 : pg_fatal("unrecognized table OID %u", adrelid);
9351 : }
9352 :
9353 2448 : if (adnum <= 0 || adnum > tbinfo->numatts)
9354 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9355 : adnum, tbinfo->dobj.name);
9356 :
9357 : /*
9358 : * dropped columns shouldn't have defaults, but just in case,
9359 : * ignore 'em
9360 : */
9361 2448 : if (tbinfo->attisdropped[adnum - 1])
9362 0 : continue;
9363 :
9364 2448 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9365 2448 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9366 2448 : attrdefs[j].dobj.catId.oid = adoid;
9367 2448 : AssignDumpId(&attrdefs[j].dobj);
9368 2448 : attrdefs[j].adtable = tbinfo;
9369 2448 : attrdefs[j].adnum = adnum;
9370 2448 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9371 :
9372 2448 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9373 2448 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9374 :
9375 2448 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9376 :
9377 : /*
9378 : * Figure out whether the default/generation expression should be
9379 : * dumped as part of the main CREATE TABLE (or similar) command or
9380 : * as a separate ALTER TABLE (or similar) command. The preference
9381 : * is to put it into the CREATE command, but in some cases that's
9382 : * not possible.
9383 : */
9384 2448 : if (tbinfo->attgenerated[adnum - 1])
9385 : {
9386 : /*
9387 : * Column generation expressions cannot be dumped separately,
9388 : * because there is no syntax for it. By setting separate to
9389 : * false here we prevent the "default" from being processed as
9390 : * its own dumpable object. Later, flagInhAttrs() will mark
9391 : * it as not to be dumped at all, if possible (that is, if it
9392 : * can be inherited from a parent).
9393 : */
9394 1356 : attrdefs[j].separate = false;
9395 : }
9396 1092 : else if (tbinfo->relkind == RELKIND_VIEW)
9397 : {
9398 : /*
9399 : * Defaults on a VIEW must always be dumped as separate ALTER
9400 : * TABLE commands.
9401 : */
9402 72 : attrdefs[j].separate = true;
9403 : }
9404 1020 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9405 : {
9406 : /* column will be suppressed, print default separately */
9407 8 : attrdefs[j].separate = true;
9408 : }
9409 : else
9410 : {
9411 1012 : attrdefs[j].separate = false;
9412 : }
9413 :
9414 2448 : if (!attrdefs[j].separate)
9415 : {
9416 : /*
9417 : * Mark the default as needing to appear before the table, so
9418 : * that any dependencies it has must be emitted before the
9419 : * CREATE TABLE. If this is not possible, we'll change to
9420 : * "separate" mode while sorting dependencies.
9421 : */
9422 2368 : addObjectDependency(&tbinfo->dobj,
9423 2368 : attrdefs[j].dobj.dumpId);
9424 : }
9425 :
9426 2448 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9427 : }
9428 :
9429 128 : PQclear(res);
9430 : }
9431 :
9432 : /*
9433 : * Get info about table CHECK constraints. This is skipped for a
9434 : * data-only dump, as it is only needed for table schemas.
9435 : */
9436 354 : if (dopt->dumpSchema && checkoids->len > 2)
9437 : {
9438 : ConstraintInfo *constrs;
9439 : int numConstrs;
9440 : int i_tableoid;
9441 : int i_oid;
9442 : int i_conrelid;
9443 : int i_conname;
9444 : int i_consrc;
9445 : int i_conislocal;
9446 : int i_convalidated;
9447 :
9448 130 : pg_log_info("finding table check constraints");
9449 :
9450 130 : resetPQExpBuffer(q);
9451 130 : appendPQExpBuffer(q,
9452 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9453 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9454 : "conislocal, convalidated "
9455 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9456 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9457 : "WHERE contype = 'c' "
9458 : "ORDER BY c.conrelid, c.conname",
9459 : checkoids->data);
9460 :
9461 130 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9462 :
9463 130 : numConstrs = PQntuples(res);
9464 130 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9465 :
9466 130 : i_tableoid = PQfnumber(res, "tableoid");
9467 130 : i_oid = PQfnumber(res, "oid");
9468 130 : i_conrelid = PQfnumber(res, "conrelid");
9469 130 : i_conname = PQfnumber(res, "conname");
9470 130 : i_consrc = PQfnumber(res, "consrc");
9471 130 : i_conislocal = PQfnumber(res, "conislocal");
9472 130 : i_convalidated = PQfnumber(res, "convalidated");
9473 :
9474 : /* As above, this loop iterates once per table, not once per row */
9475 130 : curtblindx = -1;
9476 1168 : for (int j = 0; j < numConstrs;)
9477 : {
9478 1038 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9479 1038 : TableInfo *tbinfo = NULL;
9480 : int numcons;
9481 :
9482 : /* Count rows for this table */
9483 1324 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9484 1194 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9485 908 : break;
9486 :
9487 : /*
9488 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9489 : * OID order.
9490 : */
9491 37528 : while (++curtblindx < numTables)
9492 : {
9493 37528 : tbinfo = &tblinfo[curtblindx];
9494 37528 : if (tbinfo->dobj.catId.oid == conrelid)
9495 1038 : break;
9496 : }
9497 1038 : if (curtblindx >= numTables)
9498 0 : pg_fatal("unrecognized table OID %u", conrelid);
9499 :
9500 1038 : if (numcons != tbinfo->ncheck)
9501 : {
9502 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9503 : "expected %d check constraints on table \"%s\" but found %d",
9504 : tbinfo->ncheck),
9505 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9506 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9507 0 : exit_nicely(1);
9508 : }
9509 :
9510 1038 : tbinfo->checkexprs = constrs + j;
9511 :
9512 2362 : for (int c = 0; c < numcons; c++, j++)
9513 : {
9514 1324 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9515 :
9516 1324 : constrs[j].dobj.objType = DO_CONSTRAINT;
9517 1324 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9518 1324 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9519 1324 : AssignDumpId(&constrs[j].dobj);
9520 1324 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9521 1324 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9522 1324 : constrs[j].contable = tbinfo;
9523 1324 : constrs[j].condomain = NULL;
9524 1324 : constrs[j].contype = 'c';
9525 1324 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9526 1324 : constrs[j].confrelid = InvalidOid;
9527 1324 : constrs[j].conindex = 0;
9528 1324 : constrs[j].condeferrable = false;
9529 1324 : constrs[j].condeferred = false;
9530 1324 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9531 :
9532 : /*
9533 : * An unvalidated constraint needs to be dumped separately, so
9534 : * that potentially-violating existing data is loaded before
9535 : * the constraint.
9536 : */
9537 1324 : constrs[j].separate = !validated;
9538 :
9539 1324 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9540 :
9541 : /*
9542 : * Mark the constraint as needing to appear before the table
9543 : * --- this is so that any other dependencies of the
9544 : * constraint will be emitted before we try to create the
9545 : * table. If the constraint is to be dumped separately, it
9546 : * will be dumped after data is loaded anyway, so don't do it.
9547 : * (There's an automatic dependency in the opposite direction
9548 : * anyway, so don't need to add one manually here.)
9549 : */
9550 1324 : if (!constrs[j].separate)
9551 1194 : addObjectDependency(&tbinfo->dobj,
9552 1194 : constrs[j].dobj.dumpId);
9553 :
9554 : /*
9555 : * We will detect later whether the constraint must be split
9556 : * out from the table definition.
9557 : */
9558 : }
9559 : }
9560 :
9561 130 : PQclear(res);
9562 : }
9563 :
9564 354 : destroyPQExpBuffer(q);
9565 354 : destroyPQExpBuffer(tbloids);
9566 354 : destroyPQExpBuffer(checkoids);
9567 354 : }
9568 :
9569 : /*
9570 : * Based on the getTableAttrs query's row corresponding to one column, set
9571 : * the name and flags to handle a not-null constraint for that column in
9572 : * the tbinfo struct.
9573 : *
9574 : * Result row 'r' is for tbinfo's attribute 'j'.
9575 : *
9576 : * There are three possibilities:
9577 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9578 : * (the constraint name) remains NULL.
9579 : * 2) The column has a constraint with no name (this is the case when
9580 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9581 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9582 : * 3) The column has a constraint with a known name; in that case
9583 : * notnull_constrs carries that name and dumpTableSchema will print
9584 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9585 : * (table_column_not_null), there's no need to print that name in the dump,
9586 : * so notnull_constrs is set to the empty string and it behaves as the case
9587 : * above.
9588 : *
9589 : * In a child table that inherits from a parent already containing NOT NULL
9590 : * constraints and the columns in the child don't have their own NOT NULL
9591 : * declarations, we suppress printing constraints in the child: the
9592 : * constraints are acquired at the point where the child is attached to the
9593 : * parent. This is tracked in ->notnull_islocal (which is set in flagInhAttrs
9594 : * for servers pre-18).
9595 : *
9596 : * Any of these constraints might have the NO INHERIT bit. If so we set
9597 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9598 : *
9599 : * In case 3 above, the name comparison is a bit of a hack; it actually fails
9600 : * to do the right thing in all but the trivial case. However, the downside
9601 : * of getting it wrong is simply that the name is printed rather than
9602 : * suppressed, so it's not a big deal.
9603 : */
9604 : static void
9605 48128 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9606 : TableInfo *tbinfo, int j,
9607 : int i_notnull_name, int i_notnull_noinherit,
9608 : int i_notnull_islocal)
9609 : {
9610 48128 : DumpOptions *dopt = fout->dopt;
9611 :
9612 : /*
9613 : * notnull_noinh is straight from the query result. notnull_islocal also,
9614 : * though flagInhAttrs may change that one later in versions < 18.
9615 : */
9616 48128 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9617 48128 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9618 :
9619 : /*
9620 : * Determine a constraint name to use. If the column is not marked not-
9621 : * null, we set NULL which cues ... to do nothing. An empty string says
9622 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9623 : * use.
9624 : */
9625 48128 : if (fout->remoteVersion < 180000)
9626 : {
9627 : /*
9628 : * < 18 doesn't have not-null names, so an unnamed constraint is
9629 : * sufficient.
9630 : */
9631 0 : if (PQgetisnull(res, r, i_notnull_name))
9632 0 : tbinfo->notnull_constrs[j] = NULL;
9633 : else
9634 0 : tbinfo->notnull_constrs[j] = "";
9635 : }
9636 : else
9637 : {
9638 48128 : if (PQgetisnull(res, r, i_notnull_name))
9639 43242 : tbinfo->notnull_constrs[j] = NULL;
9640 : else
9641 : {
9642 : /*
9643 : * In binary upgrade of inheritance child tables, must have a
9644 : * constraint name that we can UPDATE later.
9645 : */
9646 4886 : if (dopt->binary_upgrade &&
9647 550 : !tbinfo->ispartition &&
9648 400 : !tbinfo->notnull_islocal)
9649 : {
9650 0 : tbinfo->notnull_constrs[j] =
9651 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9652 : }
9653 : else
9654 : {
9655 : char *default_name;
9656 :
9657 : /* XXX should match ChooseConstraintName better */
9658 4886 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9659 4886 : tbinfo->attnames[j]);
9660 4886 : if (strcmp(default_name,
9661 4886 : PQgetvalue(res, r, i_notnull_name)) == 0)
9662 3290 : tbinfo->notnull_constrs[j] = "";
9663 : else
9664 : {
9665 1596 : tbinfo->notnull_constrs[j] =
9666 1596 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9667 : }
9668 4886 : free(default_name);
9669 : }
9670 : }
9671 : }
9672 48128 : }
9673 :
9674 : /*
9675 : * Test whether a column should be printed as part of table's CREATE TABLE.
9676 : * Column number is zero-based.
9677 : *
9678 : * Normally this is always true, but it's false for dropped columns, as well
9679 : * as those that were inherited without any local definition. (If we print
9680 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9681 : * For partitions, it's always true, because we want the partitions to be
9682 : * created independently and ATTACH PARTITION used afterwards.
9683 : *
9684 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9685 : * attisdropped state later, so as to keep control of the physical column
9686 : * order.
9687 : *
9688 : * This function exists because there are scattered nonobvious places that
9689 : * must be kept in sync with this decision.
9690 : */
9691 : bool
9692 79572 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9693 : {
9694 79572 : if (dopt->binary_upgrade)
9695 12184 : return true;
9696 67388 : if (tbinfo->attisdropped[colno])
9697 1468 : return false;
9698 65920 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9699 : }
9700 :
9701 :
9702 : /*
9703 : * getTSParsers:
9704 : * get information about all text search parsers in the system catalogs
9705 : */
9706 : void
9707 354 : getTSParsers(Archive *fout)
9708 : {
9709 : PGresult *res;
9710 : int ntups;
9711 : int i;
9712 : PQExpBuffer query;
9713 : TSParserInfo *prsinfo;
9714 : int i_tableoid;
9715 : int i_oid;
9716 : int i_prsname;
9717 : int i_prsnamespace;
9718 : int i_prsstart;
9719 : int i_prstoken;
9720 : int i_prsend;
9721 : int i_prsheadline;
9722 : int i_prslextype;
9723 :
9724 354 : query = createPQExpBuffer();
9725 :
9726 : /*
9727 : * find all text search objects, including builtin ones; we filter out
9728 : * system-defined objects at dump-out time.
9729 : */
9730 :
9731 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9732 : "prsstart::oid, prstoken::oid, "
9733 : "prsend::oid, prsheadline::oid, prslextype::oid "
9734 : "FROM pg_ts_parser");
9735 :
9736 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9737 :
9738 354 : ntups = PQntuples(res);
9739 :
9740 354 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9741 :
9742 354 : i_tableoid = PQfnumber(res, "tableoid");
9743 354 : i_oid = PQfnumber(res, "oid");
9744 354 : i_prsname = PQfnumber(res, "prsname");
9745 354 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9746 354 : i_prsstart = PQfnumber(res, "prsstart");
9747 354 : i_prstoken = PQfnumber(res, "prstoken");
9748 354 : i_prsend = PQfnumber(res, "prsend");
9749 354 : i_prsheadline = PQfnumber(res, "prsheadline");
9750 354 : i_prslextype = PQfnumber(res, "prslextype");
9751 :
9752 806 : for (i = 0; i < ntups; i++)
9753 : {
9754 452 : prsinfo[i].dobj.objType = DO_TSPARSER;
9755 452 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9756 452 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9757 452 : AssignDumpId(&prsinfo[i].dobj);
9758 452 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9759 904 : prsinfo[i].dobj.namespace =
9760 452 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9761 452 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9762 452 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9763 452 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9764 452 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9765 452 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9766 :
9767 : /* Decide whether we want to dump it */
9768 452 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9769 : }
9770 :
9771 354 : PQclear(res);
9772 :
9773 354 : destroyPQExpBuffer(query);
9774 354 : }
9775 :
9776 : /*
9777 : * getTSDictionaries:
9778 : * get information about all text search dictionaries in the system catalogs
9779 : */
9780 : void
9781 354 : getTSDictionaries(Archive *fout)
9782 : {
9783 : PGresult *res;
9784 : int ntups;
9785 : int i;
9786 : PQExpBuffer query;
9787 : TSDictInfo *dictinfo;
9788 : int i_tableoid;
9789 : int i_oid;
9790 : int i_dictname;
9791 : int i_dictnamespace;
9792 : int i_dictowner;
9793 : int i_dicttemplate;
9794 : int i_dictinitoption;
9795 :
9796 354 : query = createPQExpBuffer();
9797 :
9798 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9799 : "dictnamespace, dictowner, "
9800 : "dicttemplate, dictinitoption "
9801 : "FROM pg_ts_dict");
9802 :
9803 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9804 :
9805 354 : ntups = PQntuples(res);
9806 :
9807 354 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9808 :
9809 354 : i_tableoid = PQfnumber(res, "tableoid");
9810 354 : i_oid = PQfnumber(res, "oid");
9811 354 : i_dictname = PQfnumber(res, "dictname");
9812 354 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9813 354 : i_dictowner = PQfnumber(res, "dictowner");
9814 354 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9815 354 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9816 :
9817 11198 : for (i = 0; i < ntups; i++)
9818 : {
9819 10844 : dictinfo[i].dobj.objType = DO_TSDICT;
9820 10844 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9821 10844 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9822 10844 : AssignDumpId(&dictinfo[i].dobj);
9823 10844 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9824 21688 : dictinfo[i].dobj.namespace =
9825 10844 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9826 10844 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9827 10844 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9828 10844 : if (PQgetisnull(res, i, i_dictinitoption))
9829 452 : dictinfo[i].dictinitoption = NULL;
9830 : else
9831 10392 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9832 :
9833 : /* Decide whether we want to dump it */
9834 10844 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9835 : }
9836 :
9837 354 : PQclear(res);
9838 :
9839 354 : destroyPQExpBuffer(query);
9840 354 : }
9841 :
9842 : /*
9843 : * getTSTemplates:
9844 : * get information about all text search templates in the system catalogs
9845 : */
9846 : void
9847 354 : getTSTemplates(Archive *fout)
9848 : {
9849 : PGresult *res;
9850 : int ntups;
9851 : int i;
9852 : PQExpBuffer query;
9853 : TSTemplateInfo *tmplinfo;
9854 : int i_tableoid;
9855 : int i_oid;
9856 : int i_tmplname;
9857 : int i_tmplnamespace;
9858 : int i_tmplinit;
9859 : int i_tmpllexize;
9860 :
9861 354 : query = createPQExpBuffer();
9862 :
9863 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9864 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9865 : "FROM pg_ts_template");
9866 :
9867 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9868 :
9869 354 : ntups = PQntuples(res);
9870 :
9871 354 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9872 :
9873 354 : i_tableoid = PQfnumber(res, "tableoid");
9874 354 : i_oid = PQfnumber(res, "oid");
9875 354 : i_tmplname = PQfnumber(res, "tmplname");
9876 354 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9877 354 : i_tmplinit = PQfnumber(res, "tmplinit");
9878 354 : i_tmpllexize = PQfnumber(res, "tmpllexize");
9879 :
9880 2222 : for (i = 0; i < ntups; i++)
9881 : {
9882 1868 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9883 1868 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9884 1868 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9885 1868 : AssignDumpId(&tmplinfo[i].dobj);
9886 1868 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9887 3736 : tmplinfo[i].dobj.namespace =
9888 1868 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9889 1868 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9890 1868 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9891 :
9892 : /* Decide whether we want to dump it */
9893 1868 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
9894 : }
9895 :
9896 354 : PQclear(res);
9897 :
9898 354 : destroyPQExpBuffer(query);
9899 354 : }
9900 :
9901 : /*
9902 : * getTSConfigurations:
9903 : * get information about all text search configurations
9904 : */
9905 : void
9906 354 : getTSConfigurations(Archive *fout)
9907 : {
9908 : PGresult *res;
9909 : int ntups;
9910 : int i;
9911 : PQExpBuffer query;
9912 : TSConfigInfo *cfginfo;
9913 : int i_tableoid;
9914 : int i_oid;
9915 : int i_cfgname;
9916 : int i_cfgnamespace;
9917 : int i_cfgowner;
9918 : int i_cfgparser;
9919 :
9920 354 : query = createPQExpBuffer();
9921 :
9922 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9923 : "cfgnamespace, cfgowner, cfgparser "
9924 : "FROM pg_ts_config");
9925 :
9926 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9927 :
9928 354 : ntups = PQntuples(res);
9929 :
9930 354 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9931 :
9932 354 : i_tableoid = PQfnumber(res, "tableoid");
9933 354 : i_oid = PQfnumber(res, "oid");
9934 354 : i_cfgname = PQfnumber(res, "cfgname");
9935 354 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9936 354 : i_cfgowner = PQfnumber(res, "cfgowner");
9937 354 : i_cfgparser = PQfnumber(res, "cfgparser");
9938 :
9939 11128 : for (i = 0; i < ntups; i++)
9940 : {
9941 10774 : cfginfo[i].dobj.objType = DO_TSCONFIG;
9942 10774 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9943 10774 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9944 10774 : AssignDumpId(&cfginfo[i].dobj);
9945 10774 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9946 21548 : cfginfo[i].dobj.namespace =
9947 10774 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9948 10774 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9949 10774 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9950 :
9951 : /* Decide whether we want to dump it */
9952 10774 : selectDumpableObject(&(cfginfo[i].dobj), fout);
9953 : }
9954 :
9955 354 : PQclear(res);
9956 :
9957 354 : destroyPQExpBuffer(query);
9958 354 : }
9959 :
9960 : /*
9961 : * getForeignDataWrappers:
9962 : * get information about all foreign-data wrappers in the system catalogs
9963 : */
9964 : void
9965 354 : getForeignDataWrappers(Archive *fout)
9966 : {
9967 : PGresult *res;
9968 : int ntups;
9969 : int i;
9970 : PQExpBuffer query;
9971 : FdwInfo *fdwinfo;
9972 : int i_tableoid;
9973 : int i_oid;
9974 : int i_fdwname;
9975 : int i_fdwowner;
9976 : int i_fdwhandler;
9977 : int i_fdwvalidator;
9978 : int i_fdwacl;
9979 : int i_acldefault;
9980 : int i_fdwoptions;
9981 :
9982 354 : query = createPQExpBuffer();
9983 :
9984 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9985 : "fdwowner, "
9986 : "fdwhandler::pg_catalog.regproc, "
9987 : "fdwvalidator::pg_catalog.regproc, "
9988 : "fdwacl, "
9989 : "acldefault('F', fdwowner) AS acldefault, "
9990 : "array_to_string(ARRAY("
9991 : "SELECT quote_ident(option_name) || ' ' || "
9992 : "quote_literal(option_value) "
9993 : "FROM pg_options_to_table(fdwoptions) "
9994 : "ORDER BY option_name"
9995 : "), E',\n ') AS fdwoptions "
9996 : "FROM pg_foreign_data_wrapper");
9997 :
9998 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9999 :
10000 354 : ntups = PQntuples(res);
10001 :
10002 354 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10003 :
10004 354 : i_tableoid = PQfnumber(res, "tableoid");
10005 354 : i_oid = PQfnumber(res, "oid");
10006 354 : i_fdwname = PQfnumber(res, "fdwname");
10007 354 : i_fdwowner = PQfnumber(res, "fdwowner");
10008 354 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10009 354 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10010 354 : i_fdwacl = PQfnumber(res, "fdwacl");
10011 354 : i_acldefault = PQfnumber(res, "acldefault");
10012 354 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10013 :
10014 504 : for (i = 0; i < ntups; i++)
10015 : {
10016 150 : fdwinfo[i].dobj.objType = DO_FDW;
10017 150 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10018 150 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10019 150 : AssignDumpId(&fdwinfo[i].dobj);
10020 150 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10021 150 : fdwinfo[i].dobj.namespace = NULL;
10022 150 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10023 150 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10024 150 : fdwinfo[i].dacl.privtype = 0;
10025 150 : fdwinfo[i].dacl.initprivs = NULL;
10026 150 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10027 150 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10028 150 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10029 150 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10030 :
10031 : /* Decide whether we want to dump it */
10032 150 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10033 :
10034 : /* Mark whether FDW has an ACL */
10035 150 : if (!PQgetisnull(res, i, i_fdwacl))
10036 98 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10037 : }
10038 :
10039 354 : PQclear(res);
10040 :
10041 354 : destroyPQExpBuffer(query);
10042 354 : }
10043 :
10044 : /*
10045 : * getForeignServers:
10046 : * get information about all foreign servers in the system catalogs
10047 : */
10048 : void
10049 354 : getForeignServers(Archive *fout)
10050 : {
10051 : PGresult *res;
10052 : int ntups;
10053 : int i;
10054 : PQExpBuffer query;
10055 : ForeignServerInfo *srvinfo;
10056 : int i_tableoid;
10057 : int i_oid;
10058 : int i_srvname;
10059 : int i_srvowner;
10060 : int i_srvfdw;
10061 : int i_srvtype;
10062 : int i_srvversion;
10063 : int i_srvacl;
10064 : int i_acldefault;
10065 : int i_srvoptions;
10066 :
10067 354 : query = createPQExpBuffer();
10068 :
10069 354 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10070 : "srvowner, "
10071 : "srvfdw, srvtype, srvversion, srvacl, "
10072 : "acldefault('S', srvowner) AS acldefault, "
10073 : "array_to_string(ARRAY("
10074 : "SELECT quote_ident(option_name) || ' ' || "
10075 : "quote_literal(option_value) "
10076 : "FROM pg_options_to_table(srvoptions) "
10077 : "ORDER BY option_name"
10078 : "), E',\n ') AS srvoptions "
10079 : "FROM pg_foreign_server");
10080 :
10081 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10082 :
10083 354 : ntups = PQntuples(res);
10084 :
10085 354 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10086 :
10087 354 : i_tableoid = PQfnumber(res, "tableoid");
10088 354 : i_oid = PQfnumber(res, "oid");
10089 354 : i_srvname = PQfnumber(res, "srvname");
10090 354 : i_srvowner = PQfnumber(res, "srvowner");
10091 354 : i_srvfdw = PQfnumber(res, "srvfdw");
10092 354 : i_srvtype = PQfnumber(res, "srvtype");
10093 354 : i_srvversion = PQfnumber(res, "srvversion");
10094 354 : i_srvacl = PQfnumber(res, "srvacl");
10095 354 : i_acldefault = PQfnumber(res, "acldefault");
10096 354 : i_srvoptions = PQfnumber(res, "srvoptions");
10097 :
10098 512 : for (i = 0; i < ntups; i++)
10099 : {
10100 158 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10101 158 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10102 158 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10103 158 : AssignDumpId(&srvinfo[i].dobj);
10104 158 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10105 158 : srvinfo[i].dobj.namespace = NULL;
10106 158 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10107 158 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10108 158 : srvinfo[i].dacl.privtype = 0;
10109 158 : srvinfo[i].dacl.initprivs = NULL;
10110 158 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10111 158 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10112 158 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10113 158 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10114 158 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10115 :
10116 : /* Decide whether we want to dump it */
10117 158 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10118 :
10119 : /* Servers have user mappings */
10120 158 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10121 :
10122 : /* Mark whether server has an ACL */
10123 158 : if (!PQgetisnull(res, i, i_srvacl))
10124 98 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10125 : }
10126 :
10127 354 : PQclear(res);
10128 :
10129 354 : destroyPQExpBuffer(query);
10130 354 : }
10131 :
10132 : /*
10133 : * getDefaultACLs:
10134 : * get information about all default ACL information in the system catalogs
10135 : */
10136 : void
10137 354 : getDefaultACLs(Archive *fout)
10138 : {
10139 354 : DumpOptions *dopt = fout->dopt;
10140 : DefaultACLInfo *daclinfo;
10141 : PQExpBuffer query;
10142 : PGresult *res;
10143 : int i_oid;
10144 : int i_tableoid;
10145 : int i_defaclrole;
10146 : int i_defaclnamespace;
10147 : int i_defaclobjtype;
10148 : int i_defaclacl;
10149 : int i_acldefault;
10150 : int i,
10151 : ntups;
10152 :
10153 354 : query = createPQExpBuffer();
10154 :
10155 : /*
10156 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10157 : * ACL for their object type. We should dump them as deltas from the
10158 : * default ACL, since that will be used as a starting point for
10159 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10160 : * non-global entries can only add privileges not revoke them. We must
10161 : * dump those as-is (i.e., as deltas from an empty ACL).
10162 : *
10163 : * We can use defaclobjtype as the object type for acldefault(), except
10164 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10165 : * 's'.
10166 : */
10167 354 : appendPQExpBufferStr(query,
10168 : "SELECT oid, tableoid, "
10169 : "defaclrole, "
10170 : "defaclnamespace, "
10171 : "defaclobjtype, "
10172 : "defaclacl, "
10173 : "CASE WHEN defaclnamespace = 0 THEN "
10174 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10175 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10176 : "defaclrole) ELSE '{}' END AS acldefault "
10177 : "FROM pg_default_acl");
10178 :
10179 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10180 :
10181 354 : ntups = PQntuples(res);
10182 :
10183 354 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10184 :
10185 354 : i_oid = PQfnumber(res, "oid");
10186 354 : i_tableoid = PQfnumber(res, "tableoid");
10187 354 : i_defaclrole = PQfnumber(res, "defaclrole");
10188 354 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10189 354 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10190 354 : i_defaclacl = PQfnumber(res, "defaclacl");
10191 354 : i_acldefault = PQfnumber(res, "acldefault");
10192 :
10193 746 : for (i = 0; i < ntups; i++)
10194 : {
10195 392 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10196 :
10197 392 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10198 392 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10199 392 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10200 392 : AssignDumpId(&daclinfo[i].dobj);
10201 : /* cheesy ... is it worth coming up with a better object name? */
10202 392 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10203 :
10204 392 : if (nspid != InvalidOid)
10205 196 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10206 : else
10207 196 : daclinfo[i].dobj.namespace = NULL;
10208 :
10209 392 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10210 392 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10211 392 : daclinfo[i].dacl.privtype = 0;
10212 392 : daclinfo[i].dacl.initprivs = NULL;
10213 392 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10214 392 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10215 :
10216 : /* Default ACLs are ACLs, of course */
10217 392 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10218 :
10219 : /* Decide whether we want to dump it */
10220 392 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10221 : }
10222 :
10223 354 : PQclear(res);
10224 :
10225 354 : destroyPQExpBuffer(query);
10226 354 : }
10227 :
10228 : /*
10229 : * getRoleName -- look up the name of a role, given its OID
10230 : *
10231 : * In current usage, we don't expect failures, so error out for a bad OID.
10232 : */
10233 : static const char *
10234 1107976 : getRoleName(const char *roleoid_str)
10235 : {
10236 1107976 : Oid roleoid = atooid(roleoid_str);
10237 :
10238 : /*
10239 : * Do binary search to find the appropriate item.
10240 : */
10241 1107976 : if (nrolenames > 0)
10242 : {
10243 1107976 : RoleNameItem *low = &rolenames[0];
10244 1107976 : RoleNameItem *high = &rolenames[nrolenames - 1];
10245 :
10246 4432330 : while (low <= high)
10247 : {
10248 4432330 : RoleNameItem *middle = low + (high - low) / 2;
10249 :
10250 4432330 : if (roleoid < middle->roleoid)
10251 3321586 : high = middle - 1;
10252 1110744 : else if (roleoid > middle->roleoid)
10253 2768 : low = middle + 1;
10254 : else
10255 1107976 : return middle->rolename; /* found a match */
10256 : }
10257 : }
10258 :
10259 0 : pg_fatal("role with OID %u does not exist", roleoid);
10260 : return NULL; /* keep compiler quiet */
10261 : }
10262 :
10263 : /*
10264 : * collectRoleNames --
10265 : *
10266 : * Construct a table of all known roles.
10267 : * The table is sorted by OID for speed in lookup.
10268 : */
10269 : static void
10270 356 : collectRoleNames(Archive *fout)
10271 : {
10272 : PGresult *res;
10273 : const char *query;
10274 : int i;
10275 :
10276 356 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10277 :
10278 356 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10279 :
10280 356 : nrolenames = PQntuples(res);
10281 :
10282 356 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10283 :
10284 6832 : for (i = 0; i < nrolenames; i++)
10285 : {
10286 6476 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10287 6476 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10288 : }
10289 :
10290 356 : PQclear(res);
10291 356 : }
10292 :
10293 : /*
10294 : * getAdditionalACLs
10295 : *
10296 : * We have now created all the DumpableObjects, and collected the ACL data
10297 : * that appears in the directly-associated catalog entries. However, there's
10298 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10299 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10300 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10301 : * Also, in versions having the pg_init_privs catalog, read that and load the
10302 : * information into the relevant DumpableObjects.
10303 : */
10304 : static void
10305 350 : getAdditionalACLs(Archive *fout)
10306 : {
10307 350 : PQExpBuffer query = createPQExpBuffer();
10308 : PGresult *res;
10309 : int ntups,
10310 : i;
10311 :
10312 : /* Check for per-column ACLs */
10313 350 : appendPQExpBufferStr(query,
10314 : "SELECT DISTINCT attrelid FROM pg_attribute "
10315 : "WHERE attacl IS NOT NULL");
10316 :
10317 350 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10318 :
10319 350 : ntups = PQntuples(res);
10320 1064 : for (i = 0; i < ntups; i++)
10321 : {
10322 714 : Oid relid = atooid(PQgetvalue(res, i, 0));
10323 : TableInfo *tblinfo;
10324 :
10325 714 : tblinfo = findTableByOid(relid);
10326 : /* OK to ignore tables we haven't got a DumpableObject for */
10327 714 : if (tblinfo)
10328 : {
10329 714 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10330 714 : tblinfo->hascolumnACLs = true;
10331 : }
10332 : }
10333 350 : PQclear(res);
10334 :
10335 : /* Fetch initial-privileges data */
10336 350 : if (fout->remoteVersion >= 90600)
10337 : {
10338 350 : printfPQExpBuffer(query,
10339 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10340 : "FROM pg_init_privs");
10341 :
10342 350 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10343 :
10344 350 : ntups = PQntuples(res);
10345 79716 : for (i = 0; i < ntups; i++)
10346 : {
10347 79366 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10348 79366 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10349 79366 : int objsubid = atoi(PQgetvalue(res, i, 2));
10350 79366 : char privtype = *(PQgetvalue(res, i, 3));
10351 79366 : char *initprivs = PQgetvalue(res, i, 4);
10352 : CatalogId objId;
10353 : DumpableObject *dobj;
10354 :
10355 79366 : objId.tableoid = classoid;
10356 79366 : objId.oid = objoid;
10357 79366 : dobj = findObjectByCatalogId(objId);
10358 : /* OK to ignore entries we haven't got a DumpableObject for */
10359 79366 : if (dobj)
10360 : {
10361 : /* Cope with sub-object initprivs */
10362 57062 : if (objsubid != 0)
10363 : {
10364 5998 : if (dobj->objType == DO_TABLE)
10365 : {
10366 : /* For a column initprivs, set the table's ACL flags */
10367 5998 : dobj->components |= DUMP_COMPONENT_ACL;
10368 5998 : ((TableInfo *) dobj)->hascolumnACLs = true;
10369 : }
10370 : else
10371 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10372 : classoid, objoid, objsubid);
10373 6340 : continue;
10374 : }
10375 :
10376 : /*
10377 : * We ignore any pg_init_privs.initprivs entry for the public
10378 : * schema, as explained in getNamespaces().
10379 : */
10380 51064 : if (dobj->objType == DO_NAMESPACE &&
10381 692 : strcmp(dobj->name, "public") == 0)
10382 342 : continue;
10383 :
10384 : /* Else it had better be of a type we think has ACLs */
10385 50722 : if (dobj->objType == DO_NAMESPACE ||
10386 50372 : dobj->objType == DO_TYPE ||
10387 50324 : dobj->objType == DO_FUNC ||
10388 50132 : dobj->objType == DO_AGG ||
10389 50084 : dobj->objType == DO_TABLE ||
10390 0 : dobj->objType == DO_PROCLANG ||
10391 0 : dobj->objType == DO_FDW ||
10392 0 : dobj->objType == DO_FOREIGN_SERVER)
10393 50722 : {
10394 50722 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10395 :
10396 50722 : daobj->dacl.privtype = privtype;
10397 50722 : daobj->dacl.initprivs = pstrdup(initprivs);
10398 : }
10399 : else
10400 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10401 : classoid, objoid, objsubid);
10402 : }
10403 : }
10404 350 : PQclear(res);
10405 : }
10406 :
10407 350 : destroyPQExpBuffer(query);
10408 350 : }
10409 :
10410 : /*
10411 : * dumpCommentExtended --
10412 : *
10413 : * This routine is used to dump any comments associated with the
10414 : * object handed to this routine. The routine takes the object type
10415 : * and object name (ready to print, except for schema decoration), plus
10416 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10417 : * plus catalog ID and subid which are the lookup key for pg_description,
10418 : * plus the dump ID for the object (for setting a dependency).
10419 : * If a matching pg_description entry is found, it is dumped.
10420 : *
10421 : * Note: in some cases, such as comments for triggers and rules, the "type"
10422 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10423 : * but it doesn't seem worth complicating the API for all callers to make
10424 : * it cleaner.
10425 : *
10426 : * Note: although this routine takes a dumpId for dependency purposes,
10427 : * that purpose is just to mark the dependency in the emitted dump file
10428 : * for possible future use by pg_restore. We do NOT use it for determining
10429 : * ordering of the comment in the dump file, because this routine is called
10430 : * after dependency sorting occurs. This routine should be called just after
10431 : * calling ArchiveEntry() for the specified object.
10432 : */
10433 : static void
10434 12694 : dumpCommentExtended(Archive *fout, const char *type,
10435 : const char *name, const char *namespace,
10436 : const char *owner, CatalogId catalogId,
10437 : int subid, DumpId dumpId,
10438 : const char *initdb_comment)
10439 : {
10440 12694 : DumpOptions *dopt = fout->dopt;
10441 : CommentItem *comments;
10442 : int ncomments;
10443 :
10444 : /* do nothing, if --no-comments is supplied */
10445 12694 : if (dopt->no_comments)
10446 0 : return;
10447 :
10448 : /* Comments are schema not data ... except LO comments are data */
10449 12694 : if (strcmp(type, "LARGE OBJECT") != 0)
10450 : {
10451 12590 : if (!dopt->dumpSchema)
10452 0 : return;
10453 : }
10454 : else
10455 : {
10456 : /* We do dump LO comments in binary-upgrade mode */
10457 104 : if (!dopt->dumpData && !dopt->binary_upgrade)
10458 0 : return;
10459 : }
10460 :
10461 : /* Search for comments associated with catalogId, using table */
10462 12694 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10463 : &comments);
10464 :
10465 : /* Is there one matching the subid? */
10466 12694 : while (ncomments > 0)
10467 : {
10468 12604 : if (comments->objsubid == subid)
10469 12604 : break;
10470 0 : comments++;
10471 0 : ncomments--;
10472 : }
10473 :
10474 12694 : if (initdb_comment != NULL)
10475 : {
10476 : static CommentItem empty_comment = {.descr = ""};
10477 :
10478 : /*
10479 : * initdb creates this object with a comment. Skip dumping the
10480 : * initdb-provided comment, which would complicate matters for
10481 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10482 : * comment, replicate that.
10483 : */
10484 226 : if (ncomments == 0)
10485 : {
10486 8 : comments = &empty_comment;
10487 8 : ncomments = 1;
10488 : }
10489 218 : else if (strcmp(comments->descr, initdb_comment) == 0)
10490 218 : ncomments = 0;
10491 : }
10492 :
10493 : /* If a comment exists, build COMMENT ON statement */
10494 12694 : if (ncomments > 0)
10495 : {
10496 12394 : PQExpBuffer query = createPQExpBuffer();
10497 12394 : PQExpBuffer tag = createPQExpBuffer();
10498 :
10499 12394 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10500 12394 : if (namespace && *namespace)
10501 12048 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10502 12394 : appendPQExpBuffer(query, "%s IS ", name);
10503 12394 : appendStringLiteralAH(query, comments->descr, fout);
10504 12394 : appendPQExpBufferStr(query, ";\n");
10505 :
10506 12394 : appendPQExpBuffer(tag, "%s %s", type, name);
10507 :
10508 : /*
10509 : * We mark comments as SECTION_NONE because they really belong in the
10510 : * same section as their parent, whether that is pre-data or
10511 : * post-data.
10512 : */
10513 12394 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10514 12394 : ARCHIVE_OPTS(.tag = tag->data,
10515 : .namespace = namespace,
10516 : .owner = owner,
10517 : .description = "COMMENT",
10518 : .section = SECTION_NONE,
10519 : .createStmt = query->data,
10520 : .deps = &dumpId,
10521 : .nDeps = 1));
10522 :
10523 12394 : destroyPQExpBuffer(query);
10524 12394 : destroyPQExpBuffer(tag);
10525 : }
10526 : }
10527 :
10528 : /*
10529 : * dumpComment --
10530 : *
10531 : * Typical simplification of the above function.
10532 : */
10533 : static inline void
10534 12400 : dumpComment(Archive *fout, const char *type,
10535 : const char *name, const char *namespace,
10536 : const char *owner, CatalogId catalogId,
10537 : int subid, DumpId dumpId)
10538 : {
10539 12400 : dumpCommentExtended(fout, type, name, namespace, owner,
10540 : catalogId, subid, dumpId, NULL);
10541 12400 : }
10542 :
10543 : /*
10544 : * appendNamedArgument --
10545 : *
10546 : * Convenience routine for constructing parameters of the form:
10547 : * 'paraname', 'value'::type
10548 : */
10549 : static void
10550 41356 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10551 : const char *argtype, const char *argval)
10552 : {
10553 41356 : appendPQExpBufferStr(out, ",\n\t");
10554 :
10555 41356 : appendStringLiteralAH(out, argname, fout);
10556 41356 : appendPQExpBufferStr(out, ", ");
10557 :
10558 41356 : appendStringLiteralAH(out, argval, fout);
10559 41356 : appendPQExpBuffer(out, "::%s", argtype);
10560 41356 : }
10561 :
10562 : /*
10563 : * dumpRelationStats --
10564 : *
10565 : * Dump command to import stats into the relation on the new database.
10566 : */
10567 : static void
10568 12188 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
10569 : {
10570 12188 : const DumpableObject *dobj = &rsinfo->dobj;
10571 : PGresult *res;
10572 : PQExpBuffer query;
10573 : PQExpBuffer out;
10574 : int i_attname;
10575 : int i_inherited;
10576 : int i_null_frac;
10577 : int i_avg_width;
10578 : int i_n_distinct;
10579 : int i_most_common_vals;
10580 : int i_most_common_freqs;
10581 : int i_histogram_bounds;
10582 : int i_correlation;
10583 : int i_most_common_elems;
10584 : int i_most_common_elem_freqs;
10585 : int i_elem_count_histogram;
10586 : int i_range_length_histogram;
10587 : int i_range_empty_frac;
10588 : int i_range_bounds_histogram;
10589 :
10590 : /* nothing to do if we are not dumping statistics */
10591 12188 : if (!fout->dopt->dumpStatistics)
10592 0 : return;
10593 :
10594 12188 : query = createPQExpBuffer();
10595 12188 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
10596 : {
10597 206 : appendPQExpBufferStr(query,
10598 : "PREPARE getAttributeStats(pg_catalog.name, pg_catalog.name) AS\n"
10599 : "SELECT s.attname, s.inherited, "
10600 : "s.null_frac, s.avg_width, s.n_distinct, "
10601 : "s.most_common_vals, s.most_common_freqs, "
10602 : "s.histogram_bounds, s.correlation, "
10603 : "s.most_common_elems, s.most_common_elem_freqs, "
10604 : "s.elem_count_histogram, ");
10605 :
10606 206 : if (fout->remoteVersion >= 170000)
10607 206 : appendPQExpBufferStr(query,
10608 : "s.range_length_histogram, "
10609 : "s.range_empty_frac, "
10610 : "s.range_bounds_histogram ");
10611 : else
10612 0 : appendPQExpBufferStr(query,
10613 : "NULL AS range_length_histogram,"
10614 : "NULL AS range_empty_frac,"
10615 : "NULL AS range_bounds_histogram ");
10616 :
10617 206 : appendPQExpBufferStr(query,
10618 : "FROM pg_catalog.pg_stats s "
10619 : "WHERE s.schemaname = $1 "
10620 : "AND s.tablename = $2 "
10621 : "ORDER BY s.attname, s.inherited");
10622 :
10623 206 : ExecuteSqlStatement(fout, query->data);
10624 :
10625 206 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
10626 206 : resetPQExpBuffer(query);
10627 : }
10628 :
10629 12188 : out = createPQExpBuffer();
10630 :
10631 : /* restore relation stats */
10632 12188 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
10633 12188 : appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
10634 : fout->remoteVersion);
10635 12188 : appendPQExpBufferStr(out, "\t'schemaname', ");
10636 12188 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
10637 12188 : appendPQExpBufferStr(out, ",\n");
10638 12188 : appendPQExpBufferStr(out, "\t'relname', ");
10639 12188 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
10640 12188 : appendPQExpBufferStr(out, ",\n");
10641 12188 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
10642 12188 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
10643 12188 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
10644 : rsinfo->relallvisible);
10645 :
10646 12188 : if (fout->remoteVersion >= 180000)
10647 12188 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
10648 :
10649 12188 : appendPQExpBufferStr(out, "\n);\n");
10650 :
10651 :
10652 : /* fetch attribute stats */
10653 12188 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
10654 12188 : appendStringLiteralAH(query, dobj->namespace->dobj.name, fout);
10655 12188 : appendPQExpBufferStr(query, ", ");
10656 12188 : appendStringLiteralAH(query, dobj->name, fout);
10657 12188 : appendPQExpBufferStr(query, ");");
10658 :
10659 12188 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10660 :
10661 12188 : i_attname = PQfnumber(res, "attname");
10662 12188 : i_inherited = PQfnumber(res, "inherited");
10663 12188 : i_null_frac = PQfnumber(res, "null_frac");
10664 12188 : i_avg_width = PQfnumber(res, "avg_width");
10665 12188 : i_n_distinct = PQfnumber(res, "n_distinct");
10666 12188 : i_most_common_vals = PQfnumber(res, "most_common_vals");
10667 12188 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
10668 12188 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
10669 12188 : i_correlation = PQfnumber(res, "correlation");
10670 12188 : i_most_common_elems = PQfnumber(res, "most_common_elems");
10671 12188 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
10672 12188 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
10673 12188 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
10674 12188 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
10675 12188 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
10676 :
10677 : /* restore attribute stats */
10678 18456 : for (int rownum = 0; rownum < PQntuples(res); rownum++)
10679 : {
10680 : const char *attname;
10681 :
10682 6268 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
10683 6268 : appendPQExpBuffer(out, "\t'version', '%u'::integer,\n",
10684 : fout->remoteVersion);
10685 6268 : appendPQExpBufferStr(out, "\t'schemaname', ");
10686 6268 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
10687 6268 : appendPQExpBufferStr(out, ",\n\t'relname', ");
10688 6268 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
10689 :
10690 6268 : if (PQgetisnull(res, rownum, i_attname))
10691 0 : pg_fatal("attname cannot be NULL");
10692 6268 : attname = PQgetvalue(res, rownum, i_attname);
10693 :
10694 : /*
10695 : * Indexes look up attname in indAttNames to derive attnum, all others
10696 : * use attname directly. We must specify attnum for indexes, since
10697 : * their attnames are not necessarily stable across dump/reload.
10698 : */
10699 6268 : if (rsinfo->nindAttNames == 0)
10700 : {
10701 6170 : appendPQExpBuffer(out, ",\n\t'attname', ");
10702 6170 : appendStringLiteralAH(out, attname, fout);
10703 : }
10704 : else
10705 : {
10706 98 : bool found = false;
10707 :
10708 172 : for (int i = 0; i < rsinfo->nindAttNames; i++)
10709 : {
10710 172 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
10711 : {
10712 98 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
10713 : i + 1);
10714 98 : found = true;
10715 98 : break;
10716 : }
10717 : }
10718 :
10719 98 : if (!found)
10720 0 : pg_fatal("could not find index attname \"%s\"", attname);
10721 : }
10722 :
10723 6268 : if (!PQgetisnull(res, rownum, i_inherited))
10724 6268 : appendNamedArgument(out, fout, "inherited", "boolean",
10725 6268 : PQgetvalue(res, rownum, i_inherited));
10726 6268 : if (!PQgetisnull(res, rownum, i_null_frac))
10727 6268 : appendNamedArgument(out, fout, "null_frac", "real",
10728 6268 : PQgetvalue(res, rownum, i_null_frac));
10729 6268 : if (!PQgetisnull(res, rownum, i_avg_width))
10730 6268 : appendNamedArgument(out, fout, "avg_width", "integer",
10731 6268 : PQgetvalue(res, rownum, i_avg_width));
10732 6268 : if (!PQgetisnull(res, rownum, i_n_distinct))
10733 6268 : appendNamedArgument(out, fout, "n_distinct", "real",
10734 6268 : PQgetvalue(res, rownum, i_n_distinct));
10735 6268 : if (!PQgetisnull(res, rownum, i_most_common_vals))
10736 3698 : appendNamedArgument(out, fout, "most_common_vals", "text",
10737 3698 : PQgetvalue(res, rownum, i_most_common_vals));
10738 6268 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
10739 3698 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
10740 3698 : PQgetvalue(res, rownum, i_most_common_freqs));
10741 6268 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
10742 2944 : appendNamedArgument(out, fout, "histogram_bounds", "text",
10743 2944 : PQgetvalue(res, rownum, i_histogram_bounds));
10744 6268 : if (!PQgetisnull(res, rownum, i_correlation))
10745 5662 : appendNamedArgument(out, fout, "correlation", "real",
10746 5662 : PQgetvalue(res, rownum, i_correlation));
10747 6268 : if (!PQgetisnull(res, rownum, i_most_common_elems))
10748 78 : appendNamedArgument(out, fout, "most_common_elems", "text",
10749 78 : PQgetvalue(res, rownum, i_most_common_elems));
10750 6268 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
10751 78 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
10752 78 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
10753 6268 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
10754 72 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
10755 72 : PQgetvalue(res, rownum, i_elem_count_histogram));
10756 6268 : if (fout->remoteVersion >= 170000)
10757 : {
10758 6268 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
10759 18 : appendNamedArgument(out, fout, "range_length_histogram", "text",
10760 18 : PQgetvalue(res, rownum, i_range_length_histogram));
10761 6268 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
10762 18 : appendNamedArgument(out, fout, "range_empty_frac", "real",
10763 18 : PQgetvalue(res, rownum, i_range_empty_frac));
10764 6268 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
10765 18 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
10766 18 : PQgetvalue(res, rownum, i_range_bounds_histogram));
10767 : }
10768 6268 : appendPQExpBufferStr(out, "\n);\n");
10769 : }
10770 :
10771 12188 : PQclear(res);
10772 :
10773 12188 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10774 12188 : ARCHIVE_OPTS(.tag = dobj->name,
10775 : .namespace = dobj->namespace->dobj.name,
10776 : .description = "STATISTICS DATA",
10777 : .section = rsinfo->section,
10778 : .createStmt = out->data,
10779 : .deps = dobj->dependencies,
10780 : .nDeps = dobj->nDeps));
10781 :
10782 12188 : destroyPQExpBuffer(out);
10783 12188 : destroyPQExpBuffer(query);
10784 : }
10785 :
10786 : /*
10787 : * dumpTableComment --
10788 : *
10789 : * As above, but dump comments for both the specified table (or view)
10790 : * and its columns.
10791 : */
10792 : static void
10793 164 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10794 : const char *reltypename)
10795 : {
10796 164 : DumpOptions *dopt = fout->dopt;
10797 : CommentItem *comments;
10798 : int ncomments;
10799 : PQExpBuffer query;
10800 : PQExpBuffer tag;
10801 :
10802 : /* do nothing, if --no-comments is supplied */
10803 164 : if (dopt->no_comments)
10804 0 : return;
10805 :
10806 : /* Comments are SCHEMA not data */
10807 164 : if (!dopt->dumpSchema)
10808 0 : return;
10809 :
10810 : /* Search for comments associated with relation, using table */
10811 164 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
10812 : tbinfo->dobj.catId.oid,
10813 : &comments);
10814 :
10815 : /* If comments exist, build COMMENT ON statements */
10816 164 : if (ncomments <= 0)
10817 0 : return;
10818 :
10819 164 : query = createPQExpBuffer();
10820 164 : tag = createPQExpBuffer();
10821 :
10822 472 : while (ncomments > 0)
10823 : {
10824 308 : const char *descr = comments->descr;
10825 308 : int objsubid = comments->objsubid;
10826 :
10827 308 : if (objsubid == 0)
10828 : {
10829 72 : resetPQExpBuffer(tag);
10830 72 : appendPQExpBuffer(tag, "%s %s", reltypename,
10831 72 : fmtId(tbinfo->dobj.name));
10832 :
10833 72 : resetPQExpBuffer(query);
10834 72 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10835 72 : fmtQualifiedDumpable(tbinfo));
10836 72 : appendStringLiteralAH(query, descr, fout);
10837 72 : appendPQExpBufferStr(query, ";\n");
10838 :
10839 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10840 72 : ARCHIVE_OPTS(.tag = tag->data,
10841 : .namespace = tbinfo->dobj.namespace->dobj.name,
10842 : .owner = tbinfo->rolname,
10843 : .description = "COMMENT",
10844 : .section = SECTION_NONE,
10845 : .createStmt = query->data,
10846 : .deps = &(tbinfo->dobj.dumpId),
10847 : .nDeps = 1));
10848 : }
10849 236 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10850 : {
10851 236 : resetPQExpBuffer(tag);
10852 236 : appendPQExpBuffer(tag, "COLUMN %s.",
10853 236 : fmtId(tbinfo->dobj.name));
10854 236 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10855 :
10856 236 : resetPQExpBuffer(query);
10857 236 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10858 236 : fmtQualifiedDumpable(tbinfo));
10859 236 : appendPQExpBuffer(query, "%s IS ",
10860 236 : fmtId(tbinfo->attnames[objsubid - 1]));
10861 236 : appendStringLiteralAH(query, descr, fout);
10862 236 : appendPQExpBufferStr(query, ";\n");
10863 :
10864 236 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10865 236 : ARCHIVE_OPTS(.tag = tag->data,
10866 : .namespace = tbinfo->dobj.namespace->dobj.name,
10867 : .owner = tbinfo->rolname,
10868 : .description = "COMMENT",
10869 : .section = SECTION_NONE,
10870 : .createStmt = query->data,
10871 : .deps = &(tbinfo->dobj.dumpId),
10872 : .nDeps = 1));
10873 : }
10874 :
10875 308 : comments++;
10876 308 : ncomments--;
10877 : }
10878 :
10879 164 : destroyPQExpBuffer(query);
10880 164 : destroyPQExpBuffer(tag);
10881 : }
10882 :
10883 : /*
10884 : * findComments --
10885 : *
10886 : * Find the comment(s), if any, associated with the given object. All the
10887 : * objsubid values associated with the given classoid/objoid are found with
10888 : * one search.
10889 : */
10890 : static int
10891 12930 : findComments(Oid classoid, Oid objoid, CommentItem **items)
10892 : {
10893 12930 : CommentItem *middle = NULL;
10894 : CommentItem *low;
10895 : CommentItem *high;
10896 : int nmatch;
10897 :
10898 : /*
10899 : * Do binary search to find some item matching the object.
10900 : */
10901 12930 : low = &comments[0];
10902 12930 : high = &comments[ncomments - 1];
10903 128348 : while (low <= high)
10904 : {
10905 128258 : middle = low + (high - low) / 2;
10906 :
10907 128258 : if (classoid < middle->classoid)
10908 13692 : high = middle - 1;
10909 114566 : else if (classoid > middle->classoid)
10910 14368 : low = middle + 1;
10911 100198 : else if (objoid < middle->objoid)
10912 42212 : high = middle - 1;
10913 57986 : else if (objoid > middle->objoid)
10914 45146 : low = middle + 1;
10915 : else
10916 12840 : break; /* found a match */
10917 : }
10918 :
10919 12930 : if (low > high) /* no matches */
10920 : {
10921 90 : *items = NULL;
10922 90 : return 0;
10923 : }
10924 :
10925 : /*
10926 : * Now determine how many items match the object. The search loop
10927 : * invariant still holds: only items between low and high inclusive could
10928 : * match.
10929 : */
10930 12840 : nmatch = 1;
10931 12840 : while (middle > low)
10932 : {
10933 6144 : if (classoid != middle[-1].classoid ||
10934 5906 : objoid != middle[-1].objoid)
10935 : break;
10936 0 : middle--;
10937 0 : nmatch++;
10938 : }
10939 :
10940 12840 : *items = middle;
10941 :
10942 12840 : middle += nmatch;
10943 12984 : while (middle <= high)
10944 : {
10945 7058 : if (classoid != middle->classoid ||
10946 6282 : objoid != middle->objoid)
10947 : break;
10948 144 : middle++;
10949 144 : nmatch++;
10950 : }
10951 :
10952 12840 : return nmatch;
10953 : }
10954 :
10955 : /*
10956 : * collectComments --
10957 : *
10958 : * Construct a table of all comments available for database objects;
10959 : * also set the has-comment component flag for each relevant object.
10960 : *
10961 : * We used to do per-object queries for the comments, but it's much faster
10962 : * to pull them all over at once, and on most databases the memory cost
10963 : * isn't high.
10964 : *
10965 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
10966 : */
10967 : static void
10968 354 : collectComments(Archive *fout)
10969 : {
10970 : PGresult *res;
10971 : PQExpBuffer query;
10972 : int i_description;
10973 : int i_classoid;
10974 : int i_objoid;
10975 : int i_objsubid;
10976 : int ntups;
10977 : int i;
10978 : DumpableObject *dobj;
10979 :
10980 354 : query = createPQExpBuffer();
10981 :
10982 354 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10983 : "FROM pg_catalog.pg_description "
10984 : "ORDER BY classoid, objoid, objsubid");
10985 :
10986 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10987 :
10988 : /* Construct lookup table containing OIDs in numeric form */
10989 :
10990 354 : i_description = PQfnumber(res, "description");
10991 354 : i_classoid = PQfnumber(res, "classoid");
10992 354 : i_objoid = PQfnumber(res, "objoid");
10993 354 : i_objsubid = PQfnumber(res, "objsubid");
10994 :
10995 354 : ntups = PQntuples(res);
10996 :
10997 354 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10998 354 : ncomments = 0;
10999 354 : dobj = NULL;
11000 :
11001 1869890 : for (i = 0; i < ntups; i++)
11002 : {
11003 : CatalogId objId;
11004 : int subid;
11005 :
11006 1869536 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11007 1869536 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11008 1869536 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11009 :
11010 : /* We needn't remember comments that don't match any dumpable object */
11011 1869536 : if (dobj == NULL ||
11012 671654 : dobj->catId.tableoid != objId.tableoid ||
11013 667256 : dobj->catId.oid != objId.oid)
11014 1869340 : dobj = findObjectByCatalogId(objId);
11015 1869536 : if (dobj == NULL)
11016 1197540 : continue;
11017 :
11018 : /*
11019 : * Comments on columns of composite types are linked to the type's
11020 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11021 : * in the type's own DumpableObject.
11022 : */
11023 671996 : if (subid != 0 && dobj->objType == DO_TABLE &&
11024 420 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11025 98 : {
11026 : TypeInfo *cTypeInfo;
11027 :
11028 98 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11029 98 : if (cTypeInfo)
11030 98 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11031 : }
11032 : else
11033 671898 : dobj->components |= DUMP_COMPONENT_COMMENT;
11034 :
11035 671996 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11036 671996 : comments[ncomments].classoid = objId.tableoid;
11037 671996 : comments[ncomments].objoid = objId.oid;
11038 671996 : comments[ncomments].objsubid = subid;
11039 671996 : ncomments++;
11040 : }
11041 :
11042 354 : PQclear(res);
11043 354 : destroyPQExpBuffer(query);
11044 354 : }
11045 :
11046 : /*
11047 : * dumpDumpableObject
11048 : *
11049 : * This routine and its subsidiaries are responsible for creating
11050 : * ArchiveEntries (TOC objects) for each object to be dumped.
11051 : */
11052 : static void
11053 1304936 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11054 : {
11055 : /*
11056 : * Clear any dump-request bits for components that don't exist for this
11057 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11058 : * request for every kind of object.)
11059 : */
11060 1304936 : dobj->dump &= dobj->components;
11061 :
11062 : /* Now, short-circuit if there's nothing to be done here. */
11063 1304936 : if (dobj->dump == 0)
11064 1152506 : return;
11065 :
11066 152430 : switch (dobj->objType)
11067 : {
11068 910 : case DO_NAMESPACE:
11069 910 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11070 910 : break;
11071 38 : case DO_EXTENSION:
11072 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11073 38 : break;
11074 1786 : case DO_TYPE:
11075 1786 : dumpType(fout, (const TypeInfo *) dobj);
11076 1786 : break;
11077 154 : case DO_SHELL_TYPE:
11078 154 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11079 154 : break;
11080 3746 : case DO_FUNC:
11081 3746 : dumpFunc(fout, (const FuncInfo *) dobj);
11082 3746 : break;
11083 592 : case DO_AGG:
11084 592 : dumpAgg(fout, (const AggInfo *) dobj);
11085 592 : break;
11086 5016 : case DO_OPERATOR:
11087 5016 : dumpOpr(fout, (const OprInfo *) dobj);
11088 5016 : break;
11089 176 : case DO_ACCESS_METHOD:
11090 176 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11091 176 : break;
11092 1344 : case DO_OPCLASS:
11093 1344 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11094 1344 : break;
11095 1114 : case DO_OPFAMILY:
11096 1114 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11097 1114 : break;
11098 4952 : case DO_COLLATION:
11099 4952 : dumpCollation(fout, (const CollInfo *) dobj);
11100 4952 : break;
11101 852 : case DO_CONVERSION:
11102 852 : dumpConversion(fout, (const ConvInfo *) dobj);
11103 852 : break;
11104 57818 : case DO_TABLE:
11105 57818 : dumpTable(fout, (const TableInfo *) dobj);
11106 57818 : break;
11107 2630 : case DO_TABLE_ATTACH:
11108 2630 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11109 2630 : break;
11110 2140 : case DO_ATTRDEF:
11111 2140 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11112 2140 : break;
11113 5260 : case DO_INDEX:
11114 5260 : dumpIndex(fout, (const IndxInfo *) dobj);
11115 5260 : break;
11116 1192 : case DO_INDEX_ATTACH:
11117 1192 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11118 1192 : break;
11119 290 : case DO_STATSEXT:
11120 290 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11121 290 : break;
11122 804 : case DO_REFRESH_MATVIEW:
11123 804 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11124 804 : break;
11125 2374 : case DO_RULE:
11126 2374 : dumpRule(fout, (const RuleInfo *) dobj);
11127 2374 : break;
11128 1086 : case DO_TRIGGER:
11129 1086 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11130 1086 : break;
11131 92 : case DO_EVENT_TRIGGER:
11132 92 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11133 92 : break;
11134 4508 : case DO_CONSTRAINT:
11135 4508 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11136 4508 : break;
11137 358 : case DO_FK_CONSTRAINT:
11138 358 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11139 358 : break;
11140 180 : case DO_PROCLANG:
11141 180 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11142 180 : break;
11143 142 : case DO_CAST:
11144 142 : dumpCast(fout, (const CastInfo *) dobj);
11145 142 : break;
11146 92 : case DO_TRANSFORM:
11147 92 : dumpTransform(fout, (const TransformInfo *) dobj);
11148 92 : break;
11149 804 : case DO_SEQUENCE_SET:
11150 804 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11151 804 : break;
11152 7914 : case DO_TABLE_DATA:
11153 7914 : dumpTableData(fout, (const TableDataInfo *) dobj);
11154 7914 : break;
11155 27316 : case DO_DUMMY_TYPE:
11156 : /* table rowtypes and array types are never dumped separately */
11157 27316 : break;
11158 90 : case DO_TSPARSER:
11159 90 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11160 90 : break;
11161 354 : case DO_TSDICT:
11162 354 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11163 354 : break;
11164 114 : case DO_TSTEMPLATE:
11165 114 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11166 114 : break;
11167 304 : case DO_TSCONFIG:
11168 304 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11169 304 : break;
11170 112 : case DO_FDW:
11171 112 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11172 112 : break;
11173 120 : case DO_FOREIGN_SERVER:
11174 120 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11175 120 : break;
11176 332 : case DO_DEFAULT_ACL:
11177 332 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11178 332 : break;
11179 158 : case DO_LARGE_OBJECT:
11180 158 : dumpLO(fout, (const LoInfo *) dobj);
11181 158 : break;
11182 158 : case DO_LARGE_OBJECT_DATA:
11183 158 : if (dobj->dump & DUMP_COMPONENT_DATA)
11184 : {
11185 : LoInfo *loinfo;
11186 : TocEntry *te;
11187 :
11188 158 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11189 158 : if (loinfo == NULL)
11190 0 : pg_fatal("missing metadata for large objects \"%s\"",
11191 : dobj->name);
11192 :
11193 158 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11194 158 : ARCHIVE_OPTS(.tag = dobj->name,
11195 : .owner = loinfo->rolname,
11196 : .description = "BLOBS",
11197 : .section = SECTION_DATA,
11198 : .deps = dobj->dependencies,
11199 : .nDeps = dobj->nDeps,
11200 : .dumpFn = dumpLOs,
11201 : .dumpArg = loinfo));
11202 :
11203 : /*
11204 : * Set the TocEntry's dataLength in case we are doing a
11205 : * parallel dump and want to order dump jobs by table size.
11206 : * (We need some size estimate for every TocEntry with a
11207 : * DataDumper function.) We don't currently have any cheap
11208 : * way to estimate the size of LOs, but fortunately it doesn't
11209 : * matter too much as long as we get large batches of LOs
11210 : * processed reasonably early. Assume 8K per blob.
11211 : */
11212 158 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11213 : }
11214 158 : break;
11215 708 : case DO_POLICY:
11216 708 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11217 708 : break;
11218 412 : case DO_PUBLICATION:
11219 412 : dumpPublication(fout, (const PublicationInfo *) dobj);
11220 412 : break;
11221 574 : case DO_PUBLICATION_REL:
11222 574 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11223 574 : break;
11224 164 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11225 164 : dumpPublicationNamespace(fout,
11226 : (const PublicationSchemaInfo *) dobj);
11227 164 : break;
11228 250 : case DO_SUBSCRIPTION:
11229 250 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11230 250 : break;
11231 4 : case DO_SUBSCRIPTION_REL:
11232 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11233 4 : break;
11234 12188 : case DO_REL_STATS:
11235 12188 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11236 12188 : break;
11237 708 : case DO_PRE_DATA_BOUNDARY:
11238 : case DO_POST_DATA_BOUNDARY:
11239 : /* never dumped, nothing to do */
11240 708 : break;
11241 : }
11242 : }
11243 :
11244 : /*
11245 : * dumpNamespace
11246 : * writes out to fout the queries to recreate a user-defined namespace
11247 : */
11248 : static void
11249 910 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11250 : {
11251 910 : DumpOptions *dopt = fout->dopt;
11252 : PQExpBuffer q;
11253 : PQExpBuffer delq;
11254 : char *qnspname;
11255 :
11256 : /* Do nothing if not dumping schema */
11257 910 : if (!dopt->dumpSchema)
11258 56 : return;
11259 :
11260 854 : q = createPQExpBuffer();
11261 854 : delq = createPQExpBuffer();
11262 :
11263 854 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11264 :
11265 854 : if (nspinfo->create)
11266 : {
11267 570 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11268 570 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11269 : }
11270 : else
11271 : {
11272 : /* see selectDumpableNamespace() */
11273 284 : appendPQExpBufferStr(delq,
11274 : "-- *not* dropping schema, since initdb creates it\n");
11275 284 : appendPQExpBufferStr(q,
11276 : "-- *not* creating schema, since initdb creates it\n");
11277 : }
11278 :
11279 854 : if (dopt->binary_upgrade)
11280 152 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11281 : "SCHEMA", qnspname, NULL);
11282 :
11283 854 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11284 354 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11285 354 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11286 : .owner = nspinfo->rolname,
11287 : .description = "SCHEMA",
11288 : .section = SECTION_PRE_DATA,
11289 : .createStmt = q->data,
11290 : .dropStmt = delq->data));
11291 :
11292 : /* Dump Schema Comments and Security Labels */
11293 854 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11294 : {
11295 294 : const char *initdb_comment = NULL;
11296 :
11297 294 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11298 226 : initdb_comment = "standard public schema";
11299 294 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11300 : NULL, nspinfo->rolname,
11301 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11302 : initdb_comment);
11303 : }
11304 :
11305 854 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11306 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11307 : NULL, nspinfo->rolname,
11308 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11309 :
11310 854 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11311 682 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11312 : qnspname, NULL, NULL,
11313 : NULL, nspinfo->rolname, &nspinfo->dacl);
11314 :
11315 854 : free(qnspname);
11316 :
11317 854 : destroyPQExpBuffer(q);
11318 854 : destroyPQExpBuffer(delq);
11319 : }
11320 :
11321 : /*
11322 : * dumpExtension
11323 : * writes out to fout the queries to recreate an extension
11324 : */
11325 : static void
11326 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11327 : {
11328 38 : DumpOptions *dopt = fout->dopt;
11329 : PQExpBuffer q;
11330 : PQExpBuffer delq;
11331 : char *qextname;
11332 :
11333 : /* Do nothing if not dumping schema */
11334 38 : if (!dopt->dumpSchema)
11335 2 : return;
11336 :
11337 36 : q = createPQExpBuffer();
11338 36 : delq = createPQExpBuffer();
11339 :
11340 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11341 :
11342 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11343 :
11344 36 : if (!dopt->binary_upgrade)
11345 : {
11346 : /*
11347 : * In a regular dump, we simply create the extension, intentionally
11348 : * not specifying a version, so that the destination installation's
11349 : * default version is used.
11350 : *
11351 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11352 : * types; but there are various scenarios in which it's convenient to
11353 : * manually create the desired extension before restoring, so we
11354 : * prefer to allow it to exist already.
11355 : */
11356 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11357 34 : qextname, fmtId(extinfo->namespace));
11358 : }
11359 : else
11360 : {
11361 : /*
11362 : * In binary-upgrade mode, it's critical to reproduce the state of the
11363 : * database exactly, so our procedure is to create an empty extension,
11364 : * restore all the contained objects normally, and add them to the
11365 : * extension one by one. This function performs just the first of
11366 : * those steps. binary_upgrade_extension_member() takes care of
11367 : * adding member objects as they're created.
11368 : */
11369 : int i;
11370 : int n;
11371 :
11372 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11373 :
11374 : /*
11375 : * We unconditionally create the extension, so we must drop it if it
11376 : * exists. This could happen if the user deleted 'plpgsql' and then
11377 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11378 : */
11379 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11380 :
11381 2 : appendPQExpBufferStr(q,
11382 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11383 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11384 2 : appendPQExpBufferStr(q, ", ");
11385 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11386 2 : appendPQExpBufferStr(q, ", ");
11387 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11388 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11389 2 : appendPQExpBufferStr(q, ", ");
11390 :
11391 : /*
11392 : * Note that we're pushing extconfig (an OID array) back into
11393 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11394 : * preserved in binary upgrade.
11395 : */
11396 2 : if (strlen(extinfo->extconfig) > 2)
11397 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11398 : else
11399 0 : appendPQExpBufferStr(q, "NULL");
11400 2 : appendPQExpBufferStr(q, ", ");
11401 2 : if (strlen(extinfo->extcondition) > 2)
11402 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11403 : else
11404 0 : appendPQExpBufferStr(q, "NULL");
11405 2 : appendPQExpBufferStr(q, ", ");
11406 2 : appendPQExpBufferStr(q, "ARRAY[");
11407 2 : n = 0;
11408 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11409 : {
11410 : DumpableObject *extobj;
11411 :
11412 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11413 2 : if (extobj && extobj->objType == DO_EXTENSION)
11414 : {
11415 0 : if (n++ > 0)
11416 0 : appendPQExpBufferChar(q, ',');
11417 0 : appendStringLiteralAH(q, extobj->name, fout);
11418 : }
11419 : }
11420 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11421 2 : appendPQExpBufferStr(q, ");\n");
11422 : }
11423 :
11424 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11425 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11426 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11427 : .description = "EXTENSION",
11428 : .section = SECTION_PRE_DATA,
11429 : .createStmt = q->data,
11430 : .dropStmt = delq->data));
11431 :
11432 : /* Dump Extension Comments and Security Labels */
11433 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11434 36 : dumpComment(fout, "EXTENSION", qextname,
11435 : NULL, "",
11436 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11437 :
11438 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11439 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11440 : NULL, "",
11441 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11442 :
11443 36 : free(qextname);
11444 :
11445 36 : destroyPQExpBuffer(q);
11446 36 : destroyPQExpBuffer(delq);
11447 : }
11448 :
11449 : /*
11450 : * dumpType
11451 : * writes out to fout the queries to recreate a user-defined type
11452 : */
11453 : static void
11454 1786 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11455 : {
11456 1786 : DumpOptions *dopt = fout->dopt;
11457 :
11458 : /* Do nothing if not dumping schema */
11459 1786 : if (!dopt->dumpSchema)
11460 86 : return;
11461 :
11462 : /* Dump out in proper style */
11463 1700 : if (tyinfo->typtype == TYPTYPE_BASE)
11464 562 : dumpBaseType(fout, tyinfo);
11465 1138 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11466 282 : dumpDomain(fout, tyinfo);
11467 856 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11468 268 : dumpCompositeType(fout, tyinfo);
11469 588 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11470 116 : dumpEnumType(fout, tyinfo);
11471 472 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11472 240 : dumpRangeType(fout, tyinfo);
11473 232 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11474 82 : dumpUndefinedType(fout, tyinfo);
11475 : else
11476 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11477 : tyinfo->dobj.name);
11478 : }
11479 :
11480 : /*
11481 : * dumpEnumType
11482 : * writes out to fout the queries to recreate a user-defined enum type
11483 : */
11484 : static void
11485 116 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11486 : {
11487 116 : DumpOptions *dopt = fout->dopt;
11488 116 : PQExpBuffer q = createPQExpBuffer();
11489 116 : PQExpBuffer delq = createPQExpBuffer();
11490 116 : PQExpBuffer query = createPQExpBuffer();
11491 : PGresult *res;
11492 : int num,
11493 : i;
11494 : Oid enum_oid;
11495 : char *qtypname;
11496 : char *qualtypname;
11497 : char *label;
11498 : int i_enumlabel;
11499 : int i_oid;
11500 :
11501 116 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11502 : {
11503 : /* Set up query for enum-specific details */
11504 86 : appendPQExpBufferStr(query,
11505 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11506 : "SELECT oid, enumlabel "
11507 : "FROM pg_catalog.pg_enum "
11508 : "WHERE enumtypid = $1 "
11509 : "ORDER BY enumsortorder");
11510 :
11511 86 : ExecuteSqlStatement(fout, query->data);
11512 :
11513 86 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11514 : }
11515 :
11516 116 : printfPQExpBuffer(query,
11517 : "EXECUTE dumpEnumType('%u')",
11518 : tyinfo->dobj.catId.oid);
11519 :
11520 116 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11521 :
11522 116 : num = PQntuples(res);
11523 :
11524 116 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11525 116 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11526 :
11527 : /*
11528 : * CASCADE shouldn't be required here as for normal types since the I/O
11529 : * functions are generic and do not get dropped.
11530 : */
11531 116 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11532 :
11533 116 : if (dopt->binary_upgrade)
11534 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11535 : tyinfo->dobj.catId.oid,
11536 : false, false);
11537 :
11538 116 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11539 : qualtypname);
11540 :
11541 116 : if (!dopt->binary_upgrade)
11542 : {
11543 106 : i_enumlabel = PQfnumber(res, "enumlabel");
11544 :
11545 : /* Labels with server-assigned oids */
11546 756 : for (i = 0; i < num; i++)
11547 : {
11548 650 : label = PQgetvalue(res, i, i_enumlabel);
11549 650 : if (i > 0)
11550 544 : appendPQExpBufferChar(q, ',');
11551 650 : appendPQExpBufferStr(q, "\n ");
11552 650 : appendStringLiteralAH(q, label, fout);
11553 : }
11554 : }
11555 :
11556 116 : appendPQExpBufferStr(q, "\n);\n");
11557 :
11558 116 : if (dopt->binary_upgrade)
11559 : {
11560 10 : i_oid = PQfnumber(res, "oid");
11561 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11562 :
11563 : /* Labels with dump-assigned (preserved) oids */
11564 116 : for (i = 0; i < num; i++)
11565 : {
11566 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11567 106 : label = PQgetvalue(res, i, i_enumlabel);
11568 :
11569 106 : if (i == 0)
11570 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11571 106 : appendPQExpBuffer(q,
11572 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11573 : enum_oid);
11574 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11575 106 : appendStringLiteralAH(q, label, fout);
11576 106 : appendPQExpBufferStr(q, ";\n\n");
11577 : }
11578 : }
11579 :
11580 116 : if (dopt->binary_upgrade)
11581 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11582 : "TYPE", qtypname,
11583 10 : tyinfo->dobj.namespace->dobj.name);
11584 :
11585 116 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11586 116 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11587 116 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11588 : .namespace = tyinfo->dobj.namespace->dobj.name,
11589 : .owner = tyinfo->rolname,
11590 : .description = "TYPE",
11591 : .section = SECTION_PRE_DATA,
11592 : .createStmt = q->data,
11593 : .dropStmt = delq->data));
11594 :
11595 : /* Dump Type Comments and Security Labels */
11596 116 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11597 72 : dumpComment(fout, "TYPE", qtypname,
11598 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11599 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11600 :
11601 116 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11602 0 : dumpSecLabel(fout, "TYPE", qtypname,
11603 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11604 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11605 :
11606 116 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11607 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11608 : qtypname, NULL,
11609 72 : tyinfo->dobj.namespace->dobj.name,
11610 : NULL, tyinfo->rolname, &tyinfo->dacl);
11611 :
11612 116 : PQclear(res);
11613 116 : destroyPQExpBuffer(q);
11614 116 : destroyPQExpBuffer(delq);
11615 116 : destroyPQExpBuffer(query);
11616 116 : free(qtypname);
11617 116 : free(qualtypname);
11618 116 : }
11619 :
11620 : /*
11621 : * dumpRangeType
11622 : * writes out to fout the queries to recreate a user-defined range type
11623 : */
11624 : static void
11625 240 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11626 : {
11627 240 : DumpOptions *dopt = fout->dopt;
11628 240 : PQExpBuffer q = createPQExpBuffer();
11629 240 : PQExpBuffer delq = createPQExpBuffer();
11630 240 : PQExpBuffer query = createPQExpBuffer();
11631 : PGresult *res;
11632 : Oid collationOid;
11633 : char *qtypname;
11634 : char *qualtypname;
11635 : char *procname;
11636 :
11637 240 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11638 : {
11639 : /* Set up query for range-specific details */
11640 88 : appendPQExpBufferStr(query,
11641 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11642 :
11643 88 : appendPQExpBufferStr(query,
11644 : "SELECT ");
11645 :
11646 88 : if (fout->remoteVersion >= 140000)
11647 88 : appendPQExpBufferStr(query,
11648 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11649 : else
11650 0 : appendPQExpBufferStr(query,
11651 : "NULL AS rngmultitype, ");
11652 :
11653 88 : appendPQExpBufferStr(query,
11654 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11655 : "opc.opcname AS opcname, "
11656 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11657 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11658 : "opc.opcdefault, "
11659 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11660 : " ELSE rngcollation END AS collation, "
11661 : "rngcanonical, rngsubdiff "
11662 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11663 : " pg_catalog.pg_opclass opc "
11664 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11665 : "rngtypid = $1");
11666 :
11667 88 : ExecuteSqlStatement(fout, query->data);
11668 :
11669 88 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11670 : }
11671 :
11672 240 : printfPQExpBuffer(query,
11673 : "EXECUTE dumpRangeType('%u')",
11674 : tyinfo->dobj.catId.oid);
11675 :
11676 240 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11677 :
11678 240 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11679 240 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11680 :
11681 : /*
11682 : * CASCADE shouldn't be required here as for normal types since the I/O
11683 : * functions are generic and do not get dropped.
11684 : */
11685 240 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11686 :
11687 240 : if (dopt->binary_upgrade)
11688 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11689 : tyinfo->dobj.catId.oid,
11690 : false, true);
11691 :
11692 240 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11693 : qualtypname);
11694 :
11695 240 : appendPQExpBuffer(q, "\n subtype = %s",
11696 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11697 :
11698 240 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11699 240 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11700 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11701 :
11702 : /* print subtype_opclass only if not default for subtype */
11703 240 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11704 : {
11705 72 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11706 72 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11707 :
11708 72 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11709 : fmtId(nspname));
11710 72 : appendPQExpBufferStr(q, fmtId(opcname));
11711 : }
11712 :
11713 240 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11714 240 : if (OidIsValid(collationOid))
11715 : {
11716 82 : CollInfo *coll = findCollationByOid(collationOid);
11717 :
11718 82 : if (coll)
11719 82 : appendPQExpBuffer(q, ",\n collation = %s",
11720 82 : fmtQualifiedDumpable(coll));
11721 : }
11722 :
11723 240 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11724 240 : if (strcmp(procname, "-") != 0)
11725 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
11726 :
11727 240 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11728 240 : if (strcmp(procname, "-") != 0)
11729 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11730 :
11731 240 : appendPQExpBufferStr(q, "\n);\n");
11732 :
11733 240 : if (dopt->binary_upgrade)
11734 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11735 : "TYPE", qtypname,
11736 16 : tyinfo->dobj.namespace->dobj.name);
11737 :
11738 240 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11739 240 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11740 240 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11741 : .namespace = tyinfo->dobj.namespace->dobj.name,
11742 : .owner = tyinfo->rolname,
11743 : .description = "TYPE",
11744 : .section = SECTION_PRE_DATA,
11745 : .createStmt = q->data,
11746 : .dropStmt = delq->data));
11747 :
11748 : /* Dump Type Comments and Security Labels */
11749 240 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11750 108 : dumpComment(fout, "TYPE", qtypname,
11751 108 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11752 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11753 :
11754 240 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11755 0 : dumpSecLabel(fout, "TYPE", qtypname,
11756 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11757 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11758 :
11759 240 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11760 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11761 : qtypname, NULL,
11762 72 : tyinfo->dobj.namespace->dobj.name,
11763 : NULL, tyinfo->rolname, &tyinfo->dacl);
11764 :
11765 240 : PQclear(res);
11766 240 : destroyPQExpBuffer(q);
11767 240 : destroyPQExpBuffer(delq);
11768 240 : destroyPQExpBuffer(query);
11769 240 : free(qtypname);
11770 240 : free(qualtypname);
11771 240 : }
11772 :
11773 : /*
11774 : * dumpUndefinedType
11775 : * writes out to fout the queries to recreate a !typisdefined type
11776 : *
11777 : * This is a shell type, but we use different terminology to distinguish
11778 : * this case from where we have to emit a shell type definition to break
11779 : * circular dependencies. An undefined type shouldn't ever have anything
11780 : * depending on it.
11781 : */
11782 : static void
11783 82 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11784 : {
11785 82 : DumpOptions *dopt = fout->dopt;
11786 82 : PQExpBuffer q = createPQExpBuffer();
11787 82 : PQExpBuffer delq = createPQExpBuffer();
11788 : char *qtypname;
11789 : char *qualtypname;
11790 :
11791 82 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11792 82 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11793 :
11794 82 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11795 :
11796 82 : if (dopt->binary_upgrade)
11797 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11798 : tyinfo->dobj.catId.oid,
11799 : false, false);
11800 :
11801 82 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11802 : qualtypname);
11803 :
11804 82 : if (dopt->binary_upgrade)
11805 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11806 : "TYPE", qtypname,
11807 4 : tyinfo->dobj.namespace->dobj.name);
11808 :
11809 82 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11810 82 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11811 82 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11812 : .namespace = tyinfo->dobj.namespace->dobj.name,
11813 : .owner = tyinfo->rolname,
11814 : .description = "TYPE",
11815 : .section = SECTION_PRE_DATA,
11816 : .createStmt = q->data,
11817 : .dropStmt = delq->data));
11818 :
11819 : /* Dump Type Comments and Security Labels */
11820 82 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11821 72 : dumpComment(fout, "TYPE", qtypname,
11822 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11823 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11824 :
11825 82 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11826 0 : dumpSecLabel(fout, "TYPE", qtypname,
11827 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11828 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11829 :
11830 82 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11831 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11832 : qtypname, NULL,
11833 0 : tyinfo->dobj.namespace->dobj.name,
11834 : NULL, tyinfo->rolname, &tyinfo->dacl);
11835 :
11836 82 : destroyPQExpBuffer(q);
11837 82 : destroyPQExpBuffer(delq);
11838 82 : free(qtypname);
11839 82 : free(qualtypname);
11840 82 : }
11841 :
11842 : /*
11843 : * dumpBaseType
11844 : * writes out to fout the queries to recreate a user-defined base type
11845 : */
11846 : static void
11847 562 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11848 : {
11849 562 : DumpOptions *dopt = fout->dopt;
11850 562 : PQExpBuffer q = createPQExpBuffer();
11851 562 : PQExpBuffer delq = createPQExpBuffer();
11852 562 : PQExpBuffer query = createPQExpBuffer();
11853 : PGresult *res;
11854 : char *qtypname;
11855 : char *qualtypname;
11856 : char *typlen;
11857 : char *typinput;
11858 : char *typoutput;
11859 : char *typreceive;
11860 : char *typsend;
11861 : char *typmodin;
11862 : char *typmodout;
11863 : char *typanalyze;
11864 : char *typsubscript;
11865 : Oid typreceiveoid;
11866 : Oid typsendoid;
11867 : Oid typmodinoid;
11868 : Oid typmodoutoid;
11869 : Oid typanalyzeoid;
11870 : Oid typsubscriptoid;
11871 : char *typcategory;
11872 : char *typispreferred;
11873 : char *typdelim;
11874 : char *typbyval;
11875 : char *typalign;
11876 : char *typstorage;
11877 : char *typcollatable;
11878 : char *typdefault;
11879 562 : bool typdefault_is_literal = false;
11880 :
11881 562 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11882 : {
11883 : /* Set up query for type-specific details */
11884 88 : appendPQExpBufferStr(query,
11885 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11886 : "SELECT typlen, "
11887 : "typinput, typoutput, typreceive, typsend, "
11888 : "typreceive::pg_catalog.oid AS typreceiveoid, "
11889 : "typsend::pg_catalog.oid AS typsendoid, "
11890 : "typanalyze, "
11891 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11892 : "typdelim, typbyval, typalign, typstorage, "
11893 : "typmodin, typmodout, "
11894 : "typmodin::pg_catalog.oid AS typmodinoid, "
11895 : "typmodout::pg_catalog.oid AS typmodoutoid, "
11896 : "typcategory, typispreferred, "
11897 : "(typcollation <> 0) AS typcollatable, "
11898 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11899 :
11900 88 : if (fout->remoteVersion >= 140000)
11901 88 : appendPQExpBufferStr(query,
11902 : "typsubscript, "
11903 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11904 : else
11905 0 : appendPQExpBufferStr(query,
11906 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
11907 :
11908 88 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11909 : "WHERE oid = $1");
11910 :
11911 88 : ExecuteSqlStatement(fout, query->data);
11912 :
11913 88 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11914 : }
11915 :
11916 562 : printfPQExpBuffer(query,
11917 : "EXECUTE dumpBaseType('%u')",
11918 : tyinfo->dobj.catId.oid);
11919 :
11920 562 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11921 :
11922 562 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11923 562 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11924 562 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11925 562 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11926 562 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11927 562 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11928 562 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11929 562 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11930 562 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11931 562 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11932 562 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11933 562 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11934 562 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11935 562 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11936 562 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11937 562 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11938 562 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11939 562 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11940 562 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11941 562 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11942 562 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11943 562 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11944 562 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11945 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11946 562 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11947 : {
11948 92 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11949 92 : typdefault_is_literal = true; /* it needs quotes */
11950 : }
11951 : else
11952 470 : typdefault = NULL;
11953 :
11954 562 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11955 562 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11956 :
11957 : /*
11958 : * The reason we include CASCADE is that the circular dependency between
11959 : * the type and its I/O functions makes it impossible to drop the type any
11960 : * other way.
11961 : */
11962 562 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11963 :
11964 : /*
11965 : * We might already have a shell type, but setting pg_type_oid is
11966 : * harmless, and in any case we'd better set the array type OID.
11967 : */
11968 562 : if (dopt->binary_upgrade)
11969 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11970 : tyinfo->dobj.catId.oid,
11971 : false, false);
11972 :
11973 562 : appendPQExpBuffer(q,
11974 : "CREATE TYPE %s (\n"
11975 : " INTERNALLENGTH = %s",
11976 : qualtypname,
11977 562 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11978 :
11979 : /* regproc result is sufficiently quoted already */
11980 562 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11981 562 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11982 562 : if (OidIsValid(typreceiveoid))
11983 408 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11984 562 : if (OidIsValid(typsendoid))
11985 408 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11986 562 : if (OidIsValid(typmodinoid))
11987 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11988 562 : if (OidIsValid(typmodoutoid))
11989 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11990 562 : if (OidIsValid(typanalyzeoid))
11991 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11992 :
11993 562 : if (strcmp(typcollatable, "t") == 0)
11994 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11995 :
11996 562 : if (typdefault != NULL)
11997 : {
11998 92 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
11999 92 : if (typdefault_is_literal)
12000 92 : appendStringLiteralAH(q, typdefault, fout);
12001 : else
12002 0 : appendPQExpBufferStr(q, typdefault);
12003 : }
12004 :
12005 562 : if (OidIsValid(typsubscriptoid))
12006 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12007 :
12008 562 : if (OidIsValid(tyinfo->typelem))
12009 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12010 : getFormattedTypeName(fout, tyinfo->typelem,
12011 : zeroIsError));
12012 :
12013 562 : if (strcmp(typcategory, "U") != 0)
12014 : {
12015 310 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12016 310 : appendStringLiteralAH(q, typcategory, fout);
12017 : }
12018 :
12019 562 : if (strcmp(typispreferred, "t") == 0)
12020 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12021 :
12022 562 : if (typdelim && strcmp(typdelim, ",") != 0)
12023 : {
12024 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12025 6 : appendStringLiteralAH(q, typdelim, fout);
12026 : }
12027 :
12028 562 : if (*typalign == TYPALIGN_CHAR)
12029 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12030 538 : else if (*typalign == TYPALIGN_SHORT)
12031 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12032 526 : else if (*typalign == TYPALIGN_INT)
12033 376 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12034 150 : else if (*typalign == TYPALIGN_DOUBLE)
12035 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12036 :
12037 562 : if (*typstorage == TYPSTORAGE_PLAIN)
12038 412 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12039 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12040 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12041 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12042 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12043 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12044 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12045 :
12046 562 : if (strcmp(typbyval, "t") == 0)
12047 270 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12048 :
12049 562 : appendPQExpBufferStr(q, "\n);\n");
12050 :
12051 562 : if (dopt->binary_upgrade)
12052 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12053 : "TYPE", qtypname,
12054 16 : tyinfo->dobj.namespace->dobj.name);
12055 :
12056 562 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12057 562 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12058 562 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12059 : .namespace = tyinfo->dobj.namespace->dobj.name,
12060 : .owner = tyinfo->rolname,
12061 : .description = "TYPE",
12062 : .section = SECTION_PRE_DATA,
12063 : .createStmt = q->data,
12064 : .dropStmt = delq->data));
12065 :
12066 : /* Dump Type Comments and Security Labels */
12067 562 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12068 492 : dumpComment(fout, "TYPE", qtypname,
12069 492 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12070 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12071 :
12072 562 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12073 0 : dumpSecLabel(fout, "TYPE", qtypname,
12074 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12075 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12076 :
12077 562 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12078 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12079 : qtypname, NULL,
12080 72 : tyinfo->dobj.namespace->dobj.name,
12081 : NULL, tyinfo->rolname, &tyinfo->dacl);
12082 :
12083 562 : PQclear(res);
12084 562 : destroyPQExpBuffer(q);
12085 562 : destroyPQExpBuffer(delq);
12086 562 : destroyPQExpBuffer(query);
12087 562 : free(qtypname);
12088 562 : free(qualtypname);
12089 562 : }
12090 :
12091 : /*
12092 : * dumpDomain
12093 : * writes out to fout the queries to recreate a user-defined domain
12094 : */
12095 : static void
12096 282 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12097 : {
12098 282 : DumpOptions *dopt = fout->dopt;
12099 282 : PQExpBuffer q = createPQExpBuffer();
12100 282 : PQExpBuffer delq = createPQExpBuffer();
12101 282 : PQExpBuffer query = createPQExpBuffer();
12102 : PGresult *res;
12103 : int i;
12104 : char *qtypname;
12105 : char *qualtypname;
12106 : char *typnotnull;
12107 : char *typdefn;
12108 : char *typdefault;
12109 : Oid typcollation;
12110 282 : bool typdefault_is_literal = false;
12111 :
12112 282 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12113 : {
12114 : /* Set up query for domain-specific details */
12115 82 : appendPQExpBufferStr(query,
12116 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12117 :
12118 82 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12119 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12120 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12121 : "t.typdefault, "
12122 : "CASE WHEN t.typcollation <> u.typcollation "
12123 : "THEN t.typcollation ELSE 0 END AS typcollation "
12124 : "FROM pg_catalog.pg_type t "
12125 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12126 : "WHERE t.oid = $1");
12127 :
12128 82 : ExecuteSqlStatement(fout, query->data);
12129 :
12130 82 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12131 : }
12132 :
12133 282 : printfPQExpBuffer(query,
12134 : "EXECUTE dumpDomain('%u')",
12135 : tyinfo->dobj.catId.oid);
12136 :
12137 282 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12138 :
12139 282 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12140 282 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12141 282 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12142 82 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12143 200 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12144 : {
12145 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12146 0 : typdefault_is_literal = true; /* it needs quotes */
12147 : }
12148 : else
12149 200 : typdefault = NULL;
12150 282 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12151 :
12152 282 : if (dopt->binary_upgrade)
12153 44 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12154 : tyinfo->dobj.catId.oid,
12155 : true, /* force array type */
12156 : false); /* force multirange type */
12157 :
12158 282 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12159 282 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12160 :
12161 282 : appendPQExpBuffer(q,
12162 : "CREATE DOMAIN %s AS %s",
12163 : qualtypname,
12164 : typdefn);
12165 :
12166 : /* Print collation only if different from base type's collation */
12167 282 : if (OidIsValid(typcollation))
12168 : {
12169 : CollInfo *coll;
12170 :
12171 72 : coll = findCollationByOid(typcollation);
12172 72 : if (coll)
12173 72 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12174 : }
12175 :
12176 282 : if (typnotnull[0] == 't')
12177 30 : appendPQExpBufferStr(q, " NOT NULL");
12178 :
12179 282 : if (typdefault != NULL)
12180 : {
12181 82 : appendPQExpBufferStr(q, " DEFAULT ");
12182 82 : if (typdefault_is_literal)
12183 0 : appendStringLiteralAH(q, typdefault, fout);
12184 : else
12185 82 : appendPQExpBufferStr(q, typdefault);
12186 : }
12187 :
12188 282 : PQclear(res);
12189 :
12190 : /*
12191 : * Add any CHECK constraints for the domain
12192 : */
12193 474 : for (i = 0; i < tyinfo->nDomChecks; i++)
12194 : {
12195 192 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12196 :
12197 192 : if (!domcheck->separate)
12198 192 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12199 192 : fmtId(domcheck->dobj.name), domcheck->condef);
12200 : }
12201 :
12202 282 : appendPQExpBufferStr(q, ";\n");
12203 :
12204 282 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12205 :
12206 282 : if (dopt->binary_upgrade)
12207 44 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12208 : "DOMAIN", qtypname,
12209 44 : tyinfo->dobj.namespace->dobj.name);
12210 :
12211 282 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12212 282 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12213 282 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12214 : .namespace = tyinfo->dobj.namespace->dobj.name,
12215 : .owner = tyinfo->rolname,
12216 : .description = "DOMAIN",
12217 : .section = SECTION_PRE_DATA,
12218 : .createStmt = q->data,
12219 : .dropStmt = delq->data));
12220 :
12221 : /* Dump Domain Comments and Security Labels */
12222 282 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12223 0 : dumpComment(fout, "DOMAIN", qtypname,
12224 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12225 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12226 :
12227 282 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12228 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12229 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12230 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12231 :
12232 282 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12233 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12234 : qtypname, NULL,
12235 72 : tyinfo->dobj.namespace->dobj.name,
12236 : NULL, tyinfo->rolname, &tyinfo->dacl);
12237 :
12238 : /* Dump any per-constraint comments */
12239 474 : for (i = 0; i < tyinfo->nDomChecks; i++)
12240 : {
12241 192 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12242 192 : PQExpBuffer conprefix = createPQExpBuffer();
12243 :
12244 192 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12245 192 : fmtId(domcheck->dobj.name));
12246 :
12247 192 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12248 72 : dumpComment(fout, conprefix->data, qtypname,
12249 72 : tyinfo->dobj.namespace->dobj.name,
12250 : tyinfo->rolname,
12251 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12252 :
12253 192 : destroyPQExpBuffer(conprefix);
12254 : }
12255 :
12256 282 : destroyPQExpBuffer(q);
12257 282 : destroyPQExpBuffer(delq);
12258 282 : destroyPQExpBuffer(query);
12259 282 : free(qtypname);
12260 282 : free(qualtypname);
12261 282 : }
12262 :
12263 : /*
12264 : * dumpCompositeType
12265 : * writes out to fout the queries to recreate a user-defined stand-alone
12266 : * composite type
12267 : */
12268 : static void
12269 268 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12270 : {
12271 268 : DumpOptions *dopt = fout->dopt;
12272 268 : PQExpBuffer q = createPQExpBuffer();
12273 268 : PQExpBuffer dropped = createPQExpBuffer();
12274 268 : PQExpBuffer delq = createPQExpBuffer();
12275 268 : PQExpBuffer query = createPQExpBuffer();
12276 : PGresult *res;
12277 : char *qtypname;
12278 : char *qualtypname;
12279 : int ntups;
12280 : int i_attname;
12281 : int i_atttypdefn;
12282 : int i_attlen;
12283 : int i_attalign;
12284 : int i_attisdropped;
12285 : int i_attcollation;
12286 : int i;
12287 : int actual_atts;
12288 :
12289 268 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12290 : {
12291 : /*
12292 : * Set up query for type-specific details.
12293 : *
12294 : * Since we only want to dump COLLATE clauses for attributes whose
12295 : * collation is different from their type's default, we use a CASE
12296 : * here to suppress uninteresting attcollations cheaply. atttypid
12297 : * will be 0 for dropped columns; collation does not matter for those.
12298 : */
12299 118 : appendPQExpBufferStr(query,
12300 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12301 : "SELECT a.attname, a.attnum, "
12302 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12303 : "a.attlen, a.attalign, a.attisdropped, "
12304 : "CASE WHEN a.attcollation <> at.typcollation "
12305 : "THEN a.attcollation ELSE 0 END AS attcollation "
12306 : "FROM pg_catalog.pg_type ct "
12307 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12308 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12309 : "WHERE ct.oid = $1 "
12310 : "ORDER BY a.attnum");
12311 :
12312 118 : ExecuteSqlStatement(fout, query->data);
12313 :
12314 118 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12315 : }
12316 :
12317 268 : printfPQExpBuffer(query,
12318 : "EXECUTE dumpCompositeType('%u')",
12319 : tyinfo->dobj.catId.oid);
12320 :
12321 268 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12322 :
12323 268 : ntups = PQntuples(res);
12324 :
12325 268 : i_attname = PQfnumber(res, "attname");
12326 268 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12327 268 : i_attlen = PQfnumber(res, "attlen");
12328 268 : i_attalign = PQfnumber(res, "attalign");
12329 268 : i_attisdropped = PQfnumber(res, "attisdropped");
12330 268 : i_attcollation = PQfnumber(res, "attcollation");
12331 :
12332 268 : if (dopt->binary_upgrade)
12333 : {
12334 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12335 : tyinfo->dobj.catId.oid,
12336 : false, false);
12337 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12338 : }
12339 :
12340 268 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12341 268 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12342 :
12343 268 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12344 : qualtypname);
12345 :
12346 268 : actual_atts = 0;
12347 848 : for (i = 0; i < ntups; i++)
12348 : {
12349 : char *attname;
12350 : char *atttypdefn;
12351 : char *attlen;
12352 : char *attalign;
12353 : bool attisdropped;
12354 : Oid attcollation;
12355 :
12356 580 : attname = PQgetvalue(res, i, i_attname);
12357 580 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12358 580 : attlen = PQgetvalue(res, i, i_attlen);
12359 580 : attalign = PQgetvalue(res, i, i_attalign);
12360 580 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12361 580 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12362 :
12363 580 : if (attisdropped && !dopt->binary_upgrade)
12364 16 : continue;
12365 :
12366 : /* Format properly if not first attr */
12367 564 : if (actual_atts++ > 0)
12368 296 : appendPQExpBufferChar(q, ',');
12369 564 : appendPQExpBufferStr(q, "\n\t");
12370 :
12371 564 : if (!attisdropped)
12372 : {
12373 560 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12374 :
12375 : /* Add collation if not default for the column type */
12376 560 : if (OidIsValid(attcollation))
12377 : {
12378 : CollInfo *coll;
12379 :
12380 0 : coll = findCollationByOid(attcollation);
12381 0 : if (coll)
12382 0 : appendPQExpBuffer(q, " COLLATE %s",
12383 0 : fmtQualifiedDumpable(coll));
12384 : }
12385 : }
12386 : else
12387 : {
12388 : /*
12389 : * This is a dropped attribute and we're in binary_upgrade mode.
12390 : * Insert a placeholder for it in the CREATE TYPE command, and set
12391 : * length and alignment with direct UPDATE to the catalogs
12392 : * afterwards. See similar code in dumpTableSchema().
12393 : */
12394 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12395 :
12396 : /* stash separately for insertion after the CREATE TYPE */
12397 4 : appendPQExpBufferStr(dropped,
12398 : "\n-- For binary upgrade, recreate dropped column.\n");
12399 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12400 : "SET attlen = %s, "
12401 : "attalign = '%s', attbyval = false\n"
12402 : "WHERE attname = ", attlen, attalign);
12403 4 : appendStringLiteralAH(dropped, attname, fout);
12404 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12405 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12406 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12407 :
12408 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12409 : qualtypname);
12410 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12411 : fmtId(attname));
12412 : }
12413 : }
12414 268 : appendPQExpBufferStr(q, "\n);\n");
12415 268 : appendPQExpBufferStr(q, dropped->data);
12416 :
12417 268 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12418 :
12419 268 : if (dopt->binary_upgrade)
12420 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12421 : "TYPE", qtypname,
12422 36 : tyinfo->dobj.namespace->dobj.name);
12423 :
12424 268 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12425 234 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12426 234 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12427 : .namespace = tyinfo->dobj.namespace->dobj.name,
12428 : .owner = tyinfo->rolname,
12429 : .description = "TYPE",
12430 : .section = SECTION_PRE_DATA,
12431 : .createStmt = q->data,
12432 : .dropStmt = delq->data));
12433 :
12434 :
12435 : /* Dump Type Comments and Security Labels */
12436 268 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12437 72 : dumpComment(fout, "TYPE", qtypname,
12438 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12439 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12440 :
12441 268 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12442 0 : dumpSecLabel(fout, "TYPE", qtypname,
12443 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12444 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12445 :
12446 268 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12447 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12448 : qtypname, NULL,
12449 36 : tyinfo->dobj.namespace->dobj.name,
12450 : NULL, tyinfo->rolname, &tyinfo->dacl);
12451 :
12452 : /* Dump any per-column comments */
12453 268 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12454 72 : dumpCompositeTypeColComments(fout, tyinfo, res);
12455 :
12456 268 : PQclear(res);
12457 268 : destroyPQExpBuffer(q);
12458 268 : destroyPQExpBuffer(dropped);
12459 268 : destroyPQExpBuffer(delq);
12460 268 : destroyPQExpBuffer(query);
12461 268 : free(qtypname);
12462 268 : free(qualtypname);
12463 268 : }
12464 :
12465 : /*
12466 : * dumpCompositeTypeColComments
12467 : * writes out to fout the queries to recreate comments on the columns of
12468 : * a user-defined stand-alone composite type.
12469 : *
12470 : * The caller has already made a query to collect the names and attnums
12471 : * of the type's columns, so we just pass that result into here rather
12472 : * than reading them again.
12473 : */
12474 : static void
12475 72 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12476 : PGresult *res)
12477 : {
12478 : CommentItem *comments;
12479 : int ncomments;
12480 : PQExpBuffer query;
12481 : PQExpBuffer target;
12482 : int i;
12483 : int ntups;
12484 : int i_attname;
12485 : int i_attnum;
12486 : int i_attisdropped;
12487 :
12488 : /* do nothing, if --no-comments is supplied */
12489 72 : if (fout->dopt->no_comments)
12490 0 : return;
12491 :
12492 : /* Search for comments associated with type's pg_class OID */
12493 72 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12494 : &comments);
12495 :
12496 : /* If no comments exist, we're done */
12497 72 : if (ncomments <= 0)
12498 0 : return;
12499 :
12500 : /* Build COMMENT ON statements */
12501 72 : query = createPQExpBuffer();
12502 72 : target = createPQExpBuffer();
12503 :
12504 72 : ntups = PQntuples(res);
12505 72 : i_attnum = PQfnumber(res, "attnum");
12506 72 : i_attname = PQfnumber(res, "attname");
12507 72 : i_attisdropped = PQfnumber(res, "attisdropped");
12508 144 : while (ncomments > 0)
12509 : {
12510 : const char *attname;
12511 :
12512 72 : attname = NULL;
12513 72 : for (i = 0; i < ntups; i++)
12514 : {
12515 72 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12516 72 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12517 : {
12518 72 : attname = PQgetvalue(res, i, i_attname);
12519 72 : break;
12520 : }
12521 : }
12522 72 : if (attname) /* just in case we don't find it */
12523 : {
12524 72 : const char *descr = comments->descr;
12525 :
12526 72 : resetPQExpBuffer(target);
12527 72 : appendPQExpBuffer(target, "COLUMN %s.",
12528 72 : fmtId(tyinfo->dobj.name));
12529 72 : appendPQExpBufferStr(target, fmtId(attname));
12530 :
12531 72 : resetPQExpBuffer(query);
12532 72 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12533 72 : fmtQualifiedDumpable(tyinfo));
12534 72 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12535 72 : appendStringLiteralAH(query, descr, fout);
12536 72 : appendPQExpBufferStr(query, ";\n");
12537 :
12538 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12539 72 : ARCHIVE_OPTS(.tag = target->data,
12540 : .namespace = tyinfo->dobj.namespace->dobj.name,
12541 : .owner = tyinfo->rolname,
12542 : .description = "COMMENT",
12543 : .section = SECTION_NONE,
12544 : .createStmt = query->data,
12545 : .deps = &(tyinfo->dobj.dumpId),
12546 : .nDeps = 1));
12547 : }
12548 :
12549 72 : comments++;
12550 72 : ncomments--;
12551 : }
12552 :
12553 72 : destroyPQExpBuffer(query);
12554 72 : destroyPQExpBuffer(target);
12555 : }
12556 :
12557 : /*
12558 : * dumpShellType
12559 : * writes out to fout the queries to create a shell type
12560 : *
12561 : * We dump a shell definition in advance of the I/O functions for the type.
12562 : */
12563 : static void
12564 154 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12565 : {
12566 154 : DumpOptions *dopt = fout->dopt;
12567 : PQExpBuffer q;
12568 :
12569 : /* Do nothing if not dumping schema */
12570 154 : if (!dopt->dumpSchema)
12571 12 : return;
12572 :
12573 142 : q = createPQExpBuffer();
12574 :
12575 : /*
12576 : * Note the lack of a DROP command for the shell type; any required DROP
12577 : * is driven off the base type entry, instead. This interacts with
12578 : * _printTocEntry()'s use of the presence of a DROP command to decide
12579 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12580 : * the shell type's owner immediately on creation; that should happen only
12581 : * after it's filled in, otherwise the backend complains.
12582 : */
12583 :
12584 142 : if (dopt->binary_upgrade)
12585 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12586 16 : stinfo->baseType->dobj.catId.oid,
12587 : false, false);
12588 :
12589 142 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12590 142 : fmtQualifiedDumpable(stinfo));
12591 :
12592 142 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12593 142 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12594 142 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12595 : .namespace = stinfo->dobj.namespace->dobj.name,
12596 : .owner = stinfo->baseType->rolname,
12597 : .description = "SHELL TYPE",
12598 : .section = SECTION_PRE_DATA,
12599 : .createStmt = q->data));
12600 :
12601 142 : destroyPQExpBuffer(q);
12602 : }
12603 :
12604 : /*
12605 : * dumpProcLang
12606 : * writes out to fout the queries to recreate a user-defined
12607 : * procedural language
12608 : */
12609 : static void
12610 180 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12611 : {
12612 180 : DumpOptions *dopt = fout->dopt;
12613 : PQExpBuffer defqry;
12614 : PQExpBuffer delqry;
12615 : bool useParams;
12616 : char *qlanname;
12617 : FuncInfo *funcInfo;
12618 180 : FuncInfo *inlineInfo = NULL;
12619 180 : FuncInfo *validatorInfo = NULL;
12620 :
12621 : /* Do nothing if not dumping schema */
12622 180 : if (!dopt->dumpSchema)
12623 26 : return;
12624 :
12625 : /*
12626 : * Try to find the support function(s). It is not an error if we don't
12627 : * find them --- if the functions are in the pg_catalog schema, as is
12628 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12629 : * we will emit a parameterless CREATE LANGUAGE command, which will
12630 : * require PL template knowledge in the backend to reload.)
12631 : */
12632 :
12633 154 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12634 154 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12635 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12636 :
12637 154 : if (OidIsValid(plang->laninline))
12638 : {
12639 84 : inlineInfo = findFuncByOid(plang->laninline);
12640 84 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12641 2 : inlineInfo = NULL;
12642 : }
12643 :
12644 154 : if (OidIsValid(plang->lanvalidator))
12645 : {
12646 84 : validatorInfo = findFuncByOid(plang->lanvalidator);
12647 84 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12648 2 : validatorInfo = NULL;
12649 : }
12650 :
12651 : /*
12652 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12653 : * parameters. Otherwise, we'll write a parameterless command, which will
12654 : * be interpreted as CREATE EXTENSION.
12655 : */
12656 68 : useParams = (funcInfo != NULL &&
12657 290 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12658 68 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12659 :
12660 154 : defqry = createPQExpBuffer();
12661 154 : delqry = createPQExpBuffer();
12662 :
12663 154 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12664 :
12665 154 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12666 : qlanname);
12667 :
12668 154 : if (useParams)
12669 : {
12670 68 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12671 68 : plang->lanpltrusted ? "TRUSTED " : "",
12672 : qlanname);
12673 68 : appendPQExpBuffer(defqry, " HANDLER %s",
12674 68 : fmtQualifiedDumpable(funcInfo));
12675 68 : if (OidIsValid(plang->laninline))
12676 0 : appendPQExpBuffer(defqry, " INLINE %s",
12677 0 : fmtQualifiedDumpable(inlineInfo));
12678 68 : if (OidIsValid(plang->lanvalidator))
12679 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
12680 0 : fmtQualifiedDumpable(validatorInfo));
12681 : }
12682 : else
12683 : {
12684 : /*
12685 : * If not dumping parameters, then use CREATE OR REPLACE so that the
12686 : * command will not fail if the language is preinstalled in the target
12687 : * database.
12688 : *
12689 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
12690 : * EXISTS; perhaps we should emit that instead? But it might just add
12691 : * confusion.
12692 : */
12693 86 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12694 : qlanname);
12695 : }
12696 154 : appendPQExpBufferStr(defqry, ";\n");
12697 :
12698 154 : if (dopt->binary_upgrade)
12699 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
12700 : "LANGUAGE", qlanname, NULL);
12701 :
12702 154 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12703 70 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12704 70 : ARCHIVE_OPTS(.tag = plang->dobj.name,
12705 : .owner = plang->lanowner,
12706 : .description = "PROCEDURAL LANGUAGE",
12707 : .section = SECTION_PRE_DATA,
12708 : .createStmt = defqry->data,
12709 : .dropStmt = delqry->data,
12710 : ));
12711 :
12712 : /* Dump Proc Lang Comments and Security Labels */
12713 154 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12714 0 : dumpComment(fout, "LANGUAGE", qlanname,
12715 : NULL, plang->lanowner,
12716 : plang->dobj.catId, 0, plang->dobj.dumpId);
12717 :
12718 154 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12719 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
12720 : NULL, plang->lanowner,
12721 : plang->dobj.catId, 0, plang->dobj.dumpId);
12722 :
12723 154 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12724 84 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12725 : qlanname, NULL, NULL,
12726 : NULL, plang->lanowner, &plang->dacl);
12727 :
12728 154 : free(qlanname);
12729 :
12730 154 : destroyPQExpBuffer(defqry);
12731 154 : destroyPQExpBuffer(delqry);
12732 : }
12733 :
12734 : /*
12735 : * format_function_arguments: generate function name and argument list
12736 : *
12737 : * This is used when we can rely on pg_get_function_arguments to format
12738 : * the argument list. Note, however, that pg_get_function_arguments
12739 : * does not special-case zero-argument aggregates.
12740 : */
12741 : static char *
12742 8400 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12743 : {
12744 : PQExpBufferData fn;
12745 :
12746 8400 : initPQExpBuffer(&fn);
12747 8400 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12748 8400 : if (is_agg && finfo->nargs == 0)
12749 160 : appendPQExpBufferStr(&fn, "(*)");
12750 : else
12751 8240 : appendPQExpBuffer(&fn, "(%s)", funcargs);
12752 8400 : return fn.data;
12753 : }
12754 :
12755 : /*
12756 : * format_function_signature: generate function name and argument list
12757 : *
12758 : * Only a minimal list of input argument types is generated; this is
12759 : * sufficient to reference the function, but not to define it.
12760 : *
12761 : * If honor_quotes is false then the function name is never quoted.
12762 : * This is appropriate for use in TOC tags, but not in SQL commands.
12763 : */
12764 : static char *
12765 4440 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12766 : {
12767 : PQExpBufferData fn;
12768 : int j;
12769 :
12770 4440 : initPQExpBuffer(&fn);
12771 4440 : if (honor_quotes)
12772 818 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12773 : else
12774 3622 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12775 8096 : for (j = 0; j < finfo->nargs; j++)
12776 : {
12777 3656 : if (j > 0)
12778 844 : appendPQExpBufferStr(&fn, ", ");
12779 :
12780 3656 : appendPQExpBufferStr(&fn,
12781 3656 : getFormattedTypeName(fout, finfo->argtypes[j],
12782 : zeroIsError));
12783 : }
12784 4440 : appendPQExpBufferChar(&fn, ')');
12785 4440 : return fn.data;
12786 : }
12787 :
12788 :
12789 : /*
12790 : * dumpFunc:
12791 : * dump out one function
12792 : */
12793 : static void
12794 3746 : dumpFunc(Archive *fout, const FuncInfo *finfo)
12795 : {
12796 3746 : DumpOptions *dopt = fout->dopt;
12797 : PQExpBuffer query;
12798 : PQExpBuffer q;
12799 : PQExpBuffer delqry;
12800 : PQExpBuffer asPart;
12801 : PGresult *res;
12802 : char *funcsig; /* identity signature */
12803 3746 : char *funcfullsig = NULL; /* full signature */
12804 : char *funcsig_tag;
12805 : char *qual_funcsig;
12806 : char *proretset;
12807 : char *prosrc;
12808 : char *probin;
12809 : char *prosqlbody;
12810 : char *funcargs;
12811 : char *funciargs;
12812 : char *funcresult;
12813 : char *protrftypes;
12814 : char *prokind;
12815 : char *provolatile;
12816 : char *proisstrict;
12817 : char *prosecdef;
12818 : char *proleakproof;
12819 : char *proconfig;
12820 : char *procost;
12821 : char *prorows;
12822 : char *prosupport;
12823 : char *proparallel;
12824 : char *lanname;
12825 3746 : char **configitems = NULL;
12826 3746 : int nconfigitems = 0;
12827 : const char *keyword;
12828 :
12829 : /* Do nothing if not dumping schema */
12830 3746 : if (!dopt->dumpSchema)
12831 124 : return;
12832 :
12833 3622 : query = createPQExpBuffer();
12834 3622 : q = createPQExpBuffer();
12835 3622 : delqry = createPQExpBuffer();
12836 3622 : asPart = createPQExpBuffer();
12837 :
12838 3622 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12839 : {
12840 : /* Set up query for function-specific details */
12841 130 : appendPQExpBufferStr(query,
12842 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12843 :
12844 130 : appendPQExpBufferStr(query,
12845 : "SELECT\n"
12846 : "proretset,\n"
12847 : "prosrc,\n"
12848 : "probin,\n"
12849 : "provolatile,\n"
12850 : "proisstrict,\n"
12851 : "prosecdef,\n"
12852 : "lanname,\n"
12853 : "proconfig,\n"
12854 : "procost,\n"
12855 : "prorows,\n"
12856 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12857 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12858 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12859 : "proleakproof,\n");
12860 :
12861 130 : if (fout->remoteVersion >= 90500)
12862 130 : appendPQExpBufferStr(query,
12863 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12864 : else
12865 0 : appendPQExpBufferStr(query,
12866 : "NULL AS protrftypes,\n");
12867 :
12868 130 : if (fout->remoteVersion >= 90600)
12869 130 : appendPQExpBufferStr(query,
12870 : "proparallel,\n");
12871 : else
12872 0 : appendPQExpBufferStr(query,
12873 : "'u' AS proparallel,\n");
12874 :
12875 130 : if (fout->remoteVersion >= 110000)
12876 130 : appendPQExpBufferStr(query,
12877 : "prokind,\n");
12878 : else
12879 0 : appendPQExpBufferStr(query,
12880 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12881 :
12882 130 : if (fout->remoteVersion >= 120000)
12883 130 : appendPQExpBufferStr(query,
12884 : "prosupport,\n");
12885 : else
12886 0 : appendPQExpBufferStr(query,
12887 : "'-' AS prosupport,\n");
12888 :
12889 130 : if (fout->remoteVersion >= 140000)
12890 130 : appendPQExpBufferStr(query,
12891 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12892 : else
12893 0 : appendPQExpBufferStr(query,
12894 : "NULL AS prosqlbody\n");
12895 :
12896 130 : appendPQExpBufferStr(query,
12897 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12898 : "WHERE p.oid = $1 "
12899 : "AND l.oid = p.prolang");
12900 :
12901 130 : ExecuteSqlStatement(fout, query->data);
12902 :
12903 130 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12904 : }
12905 :
12906 3622 : printfPQExpBuffer(query,
12907 : "EXECUTE dumpFunc('%u')",
12908 : finfo->dobj.catId.oid);
12909 :
12910 3622 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12911 :
12912 3622 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12913 3622 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12914 : {
12915 3518 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12916 3518 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12917 3518 : prosqlbody = NULL;
12918 : }
12919 : else
12920 : {
12921 104 : prosrc = NULL;
12922 104 : probin = NULL;
12923 104 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12924 : }
12925 3622 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12926 3622 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12927 3622 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12928 3622 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12929 3622 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12930 3622 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12931 3622 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12932 3622 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12933 3622 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12934 3622 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12935 3622 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12936 3622 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12937 3622 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12938 3622 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12939 3622 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12940 :
12941 : /*
12942 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
12943 : * is used.
12944 : */
12945 3622 : if (prosqlbody)
12946 : {
12947 104 : appendPQExpBufferStr(asPart, prosqlbody);
12948 : }
12949 3518 : else if (probin[0] != '\0')
12950 : {
12951 312 : appendPQExpBufferStr(asPart, "AS ");
12952 312 : appendStringLiteralAH(asPart, probin, fout);
12953 312 : if (prosrc[0] != '\0')
12954 : {
12955 312 : appendPQExpBufferStr(asPart, ", ");
12956 :
12957 : /*
12958 : * where we have bin, use dollar quoting if allowed and src
12959 : * contains quote or backslash; else use regular quoting.
12960 : */
12961 312 : if (dopt->disable_dollar_quoting ||
12962 312 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12963 312 : appendStringLiteralAH(asPart, prosrc, fout);
12964 : else
12965 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
12966 : }
12967 : }
12968 : else
12969 : {
12970 3206 : appendPQExpBufferStr(asPart, "AS ");
12971 : /* with no bin, dollar quote src unconditionally if allowed */
12972 3206 : if (dopt->disable_dollar_quoting)
12973 0 : appendStringLiteralAH(asPart, prosrc, fout);
12974 : else
12975 3206 : appendStringLiteralDQ(asPart, prosrc, NULL);
12976 : }
12977 :
12978 3622 : if (*proconfig)
12979 : {
12980 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12981 0 : pg_fatal("could not parse %s array", "proconfig");
12982 : }
12983 : else
12984 : {
12985 3592 : configitems = NULL;
12986 3592 : nconfigitems = 0;
12987 : }
12988 :
12989 3622 : funcfullsig = format_function_arguments(finfo, funcargs, false);
12990 3622 : funcsig = format_function_arguments(finfo, funciargs, false);
12991 :
12992 3622 : funcsig_tag = format_function_signature(fout, finfo, false);
12993 :
12994 3622 : qual_funcsig = psprintf("%s.%s",
12995 3622 : fmtId(finfo->dobj.namespace->dobj.name),
12996 : funcsig);
12997 :
12998 3622 : if (prokind[0] == PROKIND_PROCEDURE)
12999 192 : keyword = "PROCEDURE";
13000 : else
13001 3430 : keyword = "FUNCTION"; /* works for window functions too */
13002 :
13003 3622 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13004 : keyword, qual_funcsig);
13005 :
13006 7244 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13007 : keyword,
13008 3622 : fmtId(finfo->dobj.namespace->dobj.name),
13009 : funcfullsig ? funcfullsig :
13010 : funcsig);
13011 :
13012 3622 : if (prokind[0] == PROKIND_PROCEDURE)
13013 : /* no result type to output */ ;
13014 3430 : else if (funcresult)
13015 3430 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13016 : else
13017 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13018 0 : (proretset[0] == 't') ? "SETOF " : "",
13019 : getFormattedTypeName(fout, finfo->prorettype,
13020 : zeroIsError));
13021 :
13022 3622 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13023 :
13024 3622 : if (*protrftypes)
13025 : {
13026 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13027 : int i;
13028 :
13029 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13030 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13031 0 : for (i = 0; typeids[i]; i++)
13032 : {
13033 0 : if (i != 0)
13034 0 : appendPQExpBufferStr(q, ", ");
13035 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13036 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13037 : }
13038 :
13039 0 : free(typeids);
13040 : }
13041 :
13042 3622 : if (prokind[0] == PROKIND_WINDOW)
13043 10 : appendPQExpBufferStr(q, " WINDOW");
13044 :
13045 3622 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13046 : {
13047 718 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13048 676 : appendPQExpBufferStr(q, " IMMUTABLE");
13049 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13050 42 : appendPQExpBufferStr(q, " STABLE");
13051 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13052 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13053 : finfo->dobj.name);
13054 : }
13055 :
13056 3622 : if (proisstrict[0] == 't')
13057 730 : appendPQExpBufferStr(q, " STRICT");
13058 :
13059 3622 : if (prosecdef[0] == 't')
13060 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13061 :
13062 3622 : if (proleakproof[0] == 't')
13063 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13064 :
13065 : /*
13066 : * COST and ROWS are emitted only if present and not default, so as not to
13067 : * break backwards-compatibility of the dump without need. Keep this code
13068 : * in sync with the defaults in functioncmds.c.
13069 : */
13070 3622 : if (strcmp(procost, "0") != 0)
13071 : {
13072 3622 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13073 : {
13074 : /* default cost is 1 */
13075 802 : if (strcmp(procost, "1") != 0)
13076 0 : appendPQExpBuffer(q, " COST %s", procost);
13077 : }
13078 : else
13079 : {
13080 : /* default cost is 100 */
13081 2820 : if (strcmp(procost, "100") != 0)
13082 12 : appendPQExpBuffer(q, " COST %s", procost);
13083 : }
13084 : }
13085 3622 : if (proretset[0] == 't' &&
13086 382 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13087 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13088 :
13089 3622 : if (strcmp(prosupport, "-") != 0)
13090 : {
13091 : /* We rely on regprocout to provide quoting and qualification */
13092 92 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13093 : }
13094 :
13095 3622 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13096 : {
13097 248 : if (proparallel[0] == PROPARALLEL_SAFE)
13098 238 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13099 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13100 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13101 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13102 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13103 : finfo->dobj.name);
13104 : }
13105 :
13106 3692 : for (int i = 0; i < nconfigitems; i++)
13107 : {
13108 : /* we feel free to scribble on configitems[] here */
13109 70 : char *configitem = configitems[i];
13110 : char *pos;
13111 :
13112 70 : pos = strchr(configitem, '=');
13113 70 : if (pos == NULL)
13114 0 : continue;
13115 70 : *pos++ = '\0';
13116 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13117 :
13118 : /*
13119 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13120 : * by flatten_set_variable_args() before they were put into the
13121 : * proconfig array. However, because the quoting rules used there
13122 : * aren't exactly like SQL's, we have to break the list value apart
13123 : * and then quote the elements as string literals. (The elements may
13124 : * be double-quoted as-is, but we can't just feed them to the SQL
13125 : * parser; it would do the wrong thing with elements that are
13126 : * zero-length or longer than NAMEDATALEN.)
13127 : *
13128 : * Variables that are not so marked should just be emitted as simple
13129 : * string literals. If the variable is not known to
13130 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13131 : * to use GUC_LIST_QUOTE for extension variables.
13132 : */
13133 70 : if (variable_is_guc_list_quote(configitem))
13134 : {
13135 : char **namelist;
13136 : char **nameptr;
13137 :
13138 : /* Parse string into list of identifiers */
13139 : /* this shouldn't fail really */
13140 20 : if (SplitGUCList(pos, ',', &namelist))
13141 : {
13142 70 : for (nameptr = namelist; *nameptr; nameptr++)
13143 : {
13144 50 : if (nameptr != namelist)
13145 30 : appendPQExpBufferStr(q, ", ");
13146 50 : appendStringLiteralAH(q, *nameptr, fout);
13147 : }
13148 : }
13149 20 : pg_free(namelist);
13150 : }
13151 : else
13152 50 : appendStringLiteralAH(q, pos, fout);
13153 : }
13154 :
13155 3622 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13156 :
13157 3622 : append_depends_on_extension(fout, q, &finfo->dobj,
13158 : "pg_catalog.pg_proc", keyword,
13159 : qual_funcsig);
13160 :
13161 3622 : if (dopt->binary_upgrade)
13162 584 : binary_upgrade_extension_member(q, &finfo->dobj,
13163 : keyword, funcsig,
13164 584 : finfo->dobj.namespace->dobj.name);
13165 :
13166 3622 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13167 3414 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13168 3414 : ARCHIVE_OPTS(.tag = funcsig_tag,
13169 : .namespace = finfo->dobj.namespace->dobj.name,
13170 : .owner = finfo->rolname,
13171 : .description = keyword,
13172 : .section = finfo->postponed_def ?
13173 : SECTION_POST_DATA : SECTION_PRE_DATA,
13174 : .createStmt = q->data,
13175 : .dropStmt = delqry->data));
13176 :
13177 : /* Dump Function Comments and Security Labels */
13178 3622 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13179 18 : dumpComment(fout, keyword, funcsig,
13180 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13181 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13182 :
13183 3622 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13184 0 : dumpSecLabel(fout, keyword, funcsig,
13185 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13186 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13187 :
13188 3622 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13189 216 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13190 : funcsig, NULL,
13191 216 : finfo->dobj.namespace->dobj.name,
13192 : NULL, finfo->rolname, &finfo->dacl);
13193 :
13194 3622 : PQclear(res);
13195 :
13196 3622 : destroyPQExpBuffer(query);
13197 3622 : destroyPQExpBuffer(q);
13198 3622 : destroyPQExpBuffer(delqry);
13199 3622 : destroyPQExpBuffer(asPart);
13200 3622 : free(funcsig);
13201 3622 : free(funcfullsig);
13202 3622 : free(funcsig_tag);
13203 3622 : free(qual_funcsig);
13204 3622 : free(configitems);
13205 : }
13206 :
13207 :
13208 : /*
13209 : * Dump a user-defined cast
13210 : */
13211 : static void
13212 142 : dumpCast(Archive *fout, const CastInfo *cast)
13213 : {
13214 142 : DumpOptions *dopt = fout->dopt;
13215 : PQExpBuffer defqry;
13216 : PQExpBuffer delqry;
13217 : PQExpBuffer labelq;
13218 : PQExpBuffer castargs;
13219 142 : FuncInfo *funcInfo = NULL;
13220 : const char *sourceType;
13221 : const char *targetType;
13222 :
13223 : /* Do nothing if not dumping schema */
13224 142 : if (!dopt->dumpSchema)
13225 12 : return;
13226 :
13227 : /* Cannot dump if we don't have the cast function's info */
13228 130 : if (OidIsValid(cast->castfunc))
13229 : {
13230 80 : funcInfo = findFuncByOid(cast->castfunc);
13231 80 : if (funcInfo == NULL)
13232 0 : pg_fatal("could not find function definition for function with OID %u",
13233 : cast->castfunc);
13234 : }
13235 :
13236 130 : defqry = createPQExpBuffer();
13237 130 : delqry = createPQExpBuffer();
13238 130 : labelq = createPQExpBuffer();
13239 130 : castargs = createPQExpBuffer();
13240 :
13241 130 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13242 130 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13243 130 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13244 : sourceType, targetType);
13245 :
13246 130 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13247 : sourceType, targetType);
13248 :
13249 130 : switch (cast->castmethod)
13250 : {
13251 50 : case COERCION_METHOD_BINARY:
13252 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13253 50 : break;
13254 0 : case COERCION_METHOD_INOUT:
13255 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13256 0 : break;
13257 80 : case COERCION_METHOD_FUNCTION:
13258 80 : if (funcInfo)
13259 : {
13260 80 : char *fsig = format_function_signature(fout, funcInfo, true);
13261 :
13262 : /*
13263 : * Always qualify the function name (format_function_signature
13264 : * won't qualify it).
13265 : */
13266 80 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13267 80 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13268 80 : free(fsig);
13269 : }
13270 : else
13271 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13272 80 : break;
13273 0 : default:
13274 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13275 : }
13276 :
13277 130 : if (cast->castcontext == 'a')
13278 70 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13279 60 : else if (cast->castcontext == 'i')
13280 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13281 130 : appendPQExpBufferStr(defqry, ";\n");
13282 :
13283 130 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13284 : sourceType, targetType);
13285 :
13286 130 : appendPQExpBuffer(castargs, "(%s AS %s)",
13287 : sourceType, targetType);
13288 :
13289 130 : if (dopt->binary_upgrade)
13290 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13291 14 : "CAST", castargs->data, NULL);
13292 :
13293 130 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13294 130 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13295 130 : ARCHIVE_OPTS(.tag = labelq->data,
13296 : .description = "CAST",
13297 : .section = SECTION_PRE_DATA,
13298 : .createStmt = defqry->data,
13299 : .dropStmt = delqry->data));
13300 :
13301 : /* Dump Cast Comments */
13302 130 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13303 0 : dumpComment(fout, "CAST", castargs->data,
13304 : NULL, "",
13305 : cast->dobj.catId, 0, cast->dobj.dumpId);
13306 :
13307 130 : destroyPQExpBuffer(defqry);
13308 130 : destroyPQExpBuffer(delqry);
13309 130 : destroyPQExpBuffer(labelq);
13310 130 : destroyPQExpBuffer(castargs);
13311 : }
13312 :
13313 : /*
13314 : * Dump a transform
13315 : */
13316 : static void
13317 92 : dumpTransform(Archive *fout, const TransformInfo *transform)
13318 : {
13319 92 : DumpOptions *dopt = fout->dopt;
13320 : PQExpBuffer defqry;
13321 : PQExpBuffer delqry;
13322 : PQExpBuffer labelq;
13323 : PQExpBuffer transformargs;
13324 92 : FuncInfo *fromsqlFuncInfo = NULL;
13325 92 : FuncInfo *tosqlFuncInfo = NULL;
13326 : char *lanname;
13327 : const char *transformType;
13328 :
13329 : /* Do nothing if not dumping schema */
13330 92 : if (!dopt->dumpSchema)
13331 12 : return;
13332 :
13333 : /* Cannot dump if we don't have the transform functions' info */
13334 80 : if (OidIsValid(transform->trffromsql))
13335 : {
13336 80 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13337 80 : if (fromsqlFuncInfo == NULL)
13338 0 : pg_fatal("could not find function definition for function with OID %u",
13339 : transform->trffromsql);
13340 : }
13341 80 : if (OidIsValid(transform->trftosql))
13342 : {
13343 80 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13344 80 : if (tosqlFuncInfo == NULL)
13345 0 : pg_fatal("could not find function definition for function with OID %u",
13346 : transform->trftosql);
13347 : }
13348 :
13349 80 : defqry = createPQExpBuffer();
13350 80 : delqry = createPQExpBuffer();
13351 80 : labelq = createPQExpBuffer();
13352 80 : transformargs = createPQExpBuffer();
13353 :
13354 80 : lanname = get_language_name(fout, transform->trflang);
13355 80 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13356 :
13357 80 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13358 : transformType, lanname);
13359 :
13360 80 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13361 : transformType, lanname);
13362 :
13363 80 : if (!transform->trffromsql && !transform->trftosql)
13364 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13365 :
13366 80 : if (transform->trffromsql)
13367 : {
13368 80 : if (fromsqlFuncInfo)
13369 : {
13370 80 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13371 :
13372 : /*
13373 : * Always qualify the function name (format_function_signature
13374 : * won't qualify it).
13375 : */
13376 80 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13377 80 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13378 80 : free(fsig);
13379 : }
13380 : else
13381 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13382 : }
13383 :
13384 80 : if (transform->trftosql)
13385 : {
13386 80 : if (transform->trffromsql)
13387 80 : appendPQExpBufferStr(defqry, ", ");
13388 :
13389 80 : if (tosqlFuncInfo)
13390 : {
13391 80 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13392 :
13393 : /*
13394 : * Always qualify the function name (format_function_signature
13395 : * won't qualify it).
13396 : */
13397 80 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13398 80 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13399 80 : free(fsig);
13400 : }
13401 : else
13402 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13403 : }
13404 :
13405 80 : appendPQExpBufferStr(defqry, ");\n");
13406 :
13407 80 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13408 : transformType, lanname);
13409 :
13410 80 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13411 : transformType, lanname);
13412 :
13413 80 : if (dopt->binary_upgrade)
13414 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13415 4 : "TRANSFORM", transformargs->data, NULL);
13416 :
13417 80 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13418 80 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13419 80 : ARCHIVE_OPTS(.tag = labelq->data,
13420 : .description = "TRANSFORM",
13421 : .section = SECTION_PRE_DATA,
13422 : .createStmt = defqry->data,
13423 : .dropStmt = delqry->data,
13424 : .deps = transform->dobj.dependencies,
13425 : .nDeps = transform->dobj.nDeps));
13426 :
13427 : /* Dump Transform Comments */
13428 80 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13429 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13430 : NULL, "",
13431 : transform->dobj.catId, 0, transform->dobj.dumpId);
13432 :
13433 80 : free(lanname);
13434 80 : destroyPQExpBuffer(defqry);
13435 80 : destroyPQExpBuffer(delqry);
13436 80 : destroyPQExpBuffer(labelq);
13437 80 : destroyPQExpBuffer(transformargs);
13438 : }
13439 :
13440 :
13441 : /*
13442 : * dumpOpr
13443 : * write out a single operator definition
13444 : */
13445 : static void
13446 5016 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
13447 : {
13448 5016 : DumpOptions *dopt = fout->dopt;
13449 : PQExpBuffer query;
13450 : PQExpBuffer q;
13451 : PQExpBuffer delq;
13452 : PQExpBuffer oprid;
13453 : PQExpBuffer details;
13454 : PGresult *res;
13455 : int i_oprkind;
13456 : int i_oprcode;
13457 : int i_oprleft;
13458 : int i_oprright;
13459 : int i_oprcom;
13460 : int i_oprnegate;
13461 : int i_oprrest;
13462 : int i_oprjoin;
13463 : int i_oprcanmerge;
13464 : int i_oprcanhash;
13465 : char *oprkind;
13466 : char *oprcode;
13467 : char *oprleft;
13468 : char *oprright;
13469 : char *oprcom;
13470 : char *oprnegate;
13471 : char *oprrest;
13472 : char *oprjoin;
13473 : char *oprcanmerge;
13474 : char *oprcanhash;
13475 : char *oprregproc;
13476 : char *oprref;
13477 :
13478 : /* Do nothing if not dumping schema */
13479 5016 : if (!dopt->dumpSchema)
13480 12 : return;
13481 :
13482 : /*
13483 : * some operators are invalid because they were the result of user
13484 : * defining operators before commutators exist
13485 : */
13486 5004 : if (!OidIsValid(oprinfo->oprcode))
13487 28 : return;
13488 :
13489 4976 : query = createPQExpBuffer();
13490 4976 : q = createPQExpBuffer();
13491 4976 : delq = createPQExpBuffer();
13492 4976 : oprid = createPQExpBuffer();
13493 4976 : details = createPQExpBuffer();
13494 :
13495 4976 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13496 : {
13497 : /* Set up query for operator-specific details */
13498 88 : appendPQExpBufferStr(query,
13499 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13500 : "SELECT oprkind, "
13501 : "oprcode::pg_catalog.regprocedure, "
13502 : "oprleft::pg_catalog.regtype, "
13503 : "oprright::pg_catalog.regtype, "
13504 : "oprcom, "
13505 : "oprnegate, "
13506 : "oprrest::pg_catalog.regprocedure, "
13507 : "oprjoin::pg_catalog.regprocedure, "
13508 : "oprcanmerge, oprcanhash "
13509 : "FROM pg_catalog.pg_operator "
13510 : "WHERE oid = $1");
13511 :
13512 88 : ExecuteSqlStatement(fout, query->data);
13513 :
13514 88 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13515 : }
13516 :
13517 4976 : printfPQExpBuffer(query,
13518 : "EXECUTE dumpOpr('%u')",
13519 : oprinfo->dobj.catId.oid);
13520 :
13521 4976 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13522 :
13523 4976 : i_oprkind = PQfnumber(res, "oprkind");
13524 4976 : i_oprcode = PQfnumber(res, "oprcode");
13525 4976 : i_oprleft = PQfnumber(res, "oprleft");
13526 4976 : i_oprright = PQfnumber(res, "oprright");
13527 4976 : i_oprcom = PQfnumber(res, "oprcom");
13528 4976 : i_oprnegate = PQfnumber(res, "oprnegate");
13529 4976 : i_oprrest = PQfnumber(res, "oprrest");
13530 4976 : i_oprjoin = PQfnumber(res, "oprjoin");
13531 4976 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13532 4976 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13533 :
13534 4976 : oprkind = PQgetvalue(res, 0, i_oprkind);
13535 4976 : oprcode = PQgetvalue(res, 0, i_oprcode);
13536 4976 : oprleft = PQgetvalue(res, 0, i_oprleft);
13537 4976 : oprright = PQgetvalue(res, 0, i_oprright);
13538 4976 : oprcom = PQgetvalue(res, 0, i_oprcom);
13539 4976 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13540 4976 : oprrest = PQgetvalue(res, 0, i_oprrest);
13541 4976 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13542 4976 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13543 4976 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13544 :
13545 : /* In PG14 upwards postfix operator support does not exist anymore. */
13546 4976 : if (strcmp(oprkind, "r") == 0)
13547 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13548 : oprcode);
13549 :
13550 4976 : oprregproc = convertRegProcReference(oprcode);
13551 4976 : if (oprregproc)
13552 : {
13553 4976 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13554 4976 : free(oprregproc);
13555 : }
13556 :
13557 4976 : appendPQExpBuffer(oprid, "%s (",
13558 : oprinfo->dobj.name);
13559 :
13560 : /*
13561 : * right unary means there's a left arg and left unary means there's a
13562 : * right arg. (Although the "r" case is dead code for PG14 and later,
13563 : * continue to support it in case we're dumping from an old server.)
13564 : */
13565 4976 : if (strcmp(oprkind, "r") == 0 ||
13566 4976 : strcmp(oprkind, "b") == 0)
13567 : {
13568 4690 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13569 4690 : appendPQExpBufferStr(oprid, oprleft);
13570 : }
13571 : else
13572 286 : appendPQExpBufferStr(oprid, "NONE");
13573 :
13574 4976 : if (strcmp(oprkind, "l") == 0 ||
13575 4690 : strcmp(oprkind, "b") == 0)
13576 : {
13577 4976 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13578 4976 : appendPQExpBuffer(oprid, ", %s)", oprright);
13579 : }
13580 : else
13581 0 : appendPQExpBufferStr(oprid, ", NONE)");
13582 :
13583 4976 : oprref = getFormattedOperatorName(oprcom);
13584 4976 : if (oprref)
13585 : {
13586 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13587 3322 : free(oprref);
13588 : }
13589 :
13590 4976 : oprref = getFormattedOperatorName(oprnegate);
13591 4976 : if (oprref)
13592 : {
13593 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13594 2326 : free(oprref);
13595 : }
13596 :
13597 4976 : if (strcmp(oprcanmerge, "t") == 0)
13598 370 : appendPQExpBufferStr(details, ",\n MERGES");
13599 :
13600 4976 : if (strcmp(oprcanhash, "t") == 0)
13601 276 : appendPQExpBufferStr(details, ",\n HASHES");
13602 :
13603 4976 : oprregproc = convertRegProcReference(oprrest);
13604 4976 : if (oprregproc)
13605 : {
13606 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13607 3028 : free(oprregproc);
13608 : }
13609 :
13610 4976 : oprregproc = convertRegProcReference(oprjoin);
13611 4976 : if (oprregproc)
13612 : {
13613 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13614 3028 : free(oprregproc);
13615 : }
13616 :
13617 4976 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13618 4976 : fmtId(oprinfo->dobj.namespace->dobj.name),
13619 : oprid->data);
13620 :
13621 4976 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13622 4976 : fmtId(oprinfo->dobj.namespace->dobj.name),
13623 : oprinfo->dobj.name, details->data);
13624 :
13625 4976 : if (dopt->binary_upgrade)
13626 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13627 24 : "OPERATOR", oprid->data,
13628 24 : oprinfo->dobj.namespace->dobj.name);
13629 :
13630 4976 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13631 4976 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13632 4976 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13633 : .namespace = oprinfo->dobj.namespace->dobj.name,
13634 : .owner = oprinfo->rolname,
13635 : .description = "OPERATOR",
13636 : .section = SECTION_PRE_DATA,
13637 : .createStmt = q->data,
13638 : .dropStmt = delq->data));
13639 :
13640 : /* Dump Operator Comments */
13641 4976 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13642 4794 : dumpComment(fout, "OPERATOR", oprid->data,
13643 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13644 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13645 :
13646 4976 : PQclear(res);
13647 :
13648 4976 : destroyPQExpBuffer(query);
13649 4976 : destroyPQExpBuffer(q);
13650 4976 : destroyPQExpBuffer(delq);
13651 4976 : destroyPQExpBuffer(oprid);
13652 4976 : destroyPQExpBuffer(details);
13653 : }
13654 :
13655 : /*
13656 : * Convert a function reference obtained from pg_operator
13657 : *
13658 : * Returns allocated string of what to print, or NULL if function references
13659 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13660 : *
13661 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13662 : * part.
13663 : */
13664 : static char *
13665 14928 : convertRegProcReference(const char *proc)
13666 : {
13667 : char *name;
13668 : char *paren;
13669 : bool inquote;
13670 :
13671 : /* In all cases "-" means a null reference */
13672 14928 : if (strcmp(proc, "-") == 0)
13673 3896 : return NULL;
13674 :
13675 11032 : name = pg_strdup(proc);
13676 : /* find non-double-quoted left paren */
13677 11032 : inquote = false;
13678 132920 : for (paren = name; *paren; paren++)
13679 : {
13680 132920 : if (*paren == '(' && !inquote)
13681 : {
13682 11032 : *paren = '\0';
13683 11032 : break;
13684 : }
13685 121888 : if (*paren == '"')
13686 100 : inquote = !inquote;
13687 : }
13688 11032 : return name;
13689 : }
13690 :
13691 : /*
13692 : * getFormattedOperatorName - retrieve the operator name for the
13693 : * given operator OID (presented in string form).
13694 : *
13695 : * Returns an allocated string, or NULL if the given OID is invalid.
13696 : * Caller is responsible for free'ing result string.
13697 : *
13698 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
13699 : * useful in commands where the operator's argument types can be inferred from
13700 : * context. We always schema-qualify the name, though. The predecessor to
13701 : * this code tried to skip the schema qualification if possible, but that led
13702 : * to wrong results in corner cases, such as if an operator and its negator
13703 : * are in different schemas.
13704 : */
13705 : static char *
13706 10530 : getFormattedOperatorName(const char *oproid)
13707 : {
13708 : OprInfo *oprInfo;
13709 :
13710 : /* In all cases "0" means a null reference */
13711 10530 : if (strcmp(oproid, "0") == 0)
13712 4882 : return NULL;
13713 :
13714 5648 : oprInfo = findOprByOid(atooid(oproid));
13715 5648 : if (oprInfo == NULL)
13716 : {
13717 0 : pg_log_warning("could not find operator with OID %s",
13718 : oproid);
13719 0 : return NULL;
13720 : }
13721 :
13722 5648 : return psprintf("OPERATOR(%s.%s)",
13723 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
13724 : oprInfo->dobj.name);
13725 : }
13726 :
13727 : /*
13728 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13729 : *
13730 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13731 : * argument lists of these functions are predetermined. Note that the
13732 : * caller should ensure we are in the proper schema, because the results
13733 : * are search path dependent!
13734 : */
13735 : static char *
13736 450 : convertTSFunction(Archive *fout, Oid funcOid)
13737 : {
13738 : char *result;
13739 : char query[128];
13740 : PGresult *res;
13741 :
13742 450 : snprintf(query, sizeof(query),
13743 : "SELECT '%u'::pg_catalog.regproc", funcOid);
13744 450 : res = ExecuteSqlQueryForSingleRow(fout, query);
13745 :
13746 450 : result = pg_strdup(PQgetvalue(res, 0, 0));
13747 :
13748 450 : PQclear(res);
13749 :
13750 450 : return result;
13751 : }
13752 :
13753 : /*
13754 : * dumpAccessMethod
13755 : * write out a single access method definition
13756 : */
13757 : static void
13758 176 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13759 : {
13760 176 : DumpOptions *dopt = fout->dopt;
13761 : PQExpBuffer q;
13762 : PQExpBuffer delq;
13763 : char *qamname;
13764 :
13765 : /* Do nothing if not dumping schema */
13766 176 : if (!dopt->dumpSchema)
13767 24 : return;
13768 :
13769 152 : q = createPQExpBuffer();
13770 152 : delq = createPQExpBuffer();
13771 :
13772 152 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
13773 :
13774 152 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13775 :
13776 152 : switch (aminfo->amtype)
13777 : {
13778 72 : case AMTYPE_INDEX:
13779 72 : appendPQExpBufferStr(q, "TYPE INDEX ");
13780 72 : break;
13781 80 : case AMTYPE_TABLE:
13782 80 : appendPQExpBufferStr(q, "TYPE TABLE ");
13783 80 : break;
13784 0 : default:
13785 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13786 : aminfo->amtype, qamname);
13787 0 : destroyPQExpBuffer(q);
13788 0 : destroyPQExpBuffer(delq);
13789 0 : free(qamname);
13790 0 : return;
13791 : }
13792 :
13793 152 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13794 :
13795 152 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13796 : qamname);
13797 :
13798 152 : if (dopt->binary_upgrade)
13799 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
13800 : "ACCESS METHOD", qamname, NULL);
13801 :
13802 152 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13803 152 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13804 152 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13805 : .description = "ACCESS METHOD",
13806 : .section = SECTION_PRE_DATA,
13807 : .createStmt = q->data,
13808 : .dropStmt = delq->data));
13809 :
13810 : /* Dump Access Method Comments */
13811 152 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13812 0 : dumpComment(fout, "ACCESS METHOD", qamname,
13813 : NULL, "",
13814 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13815 :
13816 152 : destroyPQExpBuffer(q);
13817 152 : destroyPQExpBuffer(delq);
13818 152 : free(qamname);
13819 : }
13820 :
13821 : /*
13822 : * dumpOpclass
13823 : * write out a single operator class definition
13824 : */
13825 : static void
13826 1344 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13827 : {
13828 1344 : DumpOptions *dopt = fout->dopt;
13829 : PQExpBuffer query;
13830 : PQExpBuffer q;
13831 : PQExpBuffer delq;
13832 : PQExpBuffer nameusing;
13833 : PGresult *res;
13834 : int ntups;
13835 : int i_opcintype;
13836 : int i_opckeytype;
13837 : int i_opcdefault;
13838 : int i_opcfamily;
13839 : int i_opcfamilyname;
13840 : int i_opcfamilynsp;
13841 : int i_amname;
13842 : int i_amopstrategy;
13843 : int i_amopopr;
13844 : int i_sortfamily;
13845 : int i_sortfamilynsp;
13846 : int i_amprocnum;
13847 : int i_amproc;
13848 : int i_amproclefttype;
13849 : int i_amprocrighttype;
13850 : char *opcintype;
13851 : char *opckeytype;
13852 : char *opcdefault;
13853 : char *opcfamily;
13854 : char *opcfamilyname;
13855 : char *opcfamilynsp;
13856 : char *amname;
13857 : char *amopstrategy;
13858 : char *amopopr;
13859 : char *sortfamily;
13860 : char *sortfamilynsp;
13861 : char *amprocnum;
13862 : char *amproc;
13863 : char *amproclefttype;
13864 : char *amprocrighttype;
13865 : bool needComma;
13866 : int i;
13867 :
13868 : /* Do nothing if not dumping schema */
13869 1344 : if (!dopt->dumpSchema)
13870 36 : return;
13871 :
13872 1308 : query = createPQExpBuffer();
13873 1308 : q = createPQExpBuffer();
13874 1308 : delq = createPQExpBuffer();
13875 1308 : nameusing = createPQExpBuffer();
13876 :
13877 : /* Get additional fields from the pg_opclass row */
13878 1308 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13879 : "opckeytype::pg_catalog.regtype, "
13880 : "opcdefault, opcfamily, "
13881 : "opfname AS opcfamilyname, "
13882 : "nspname AS opcfamilynsp, "
13883 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13884 : "FROM pg_catalog.pg_opclass c "
13885 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13886 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13887 : "WHERE c.oid = '%u'::pg_catalog.oid",
13888 : opcinfo->dobj.catId.oid);
13889 :
13890 1308 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13891 :
13892 1308 : i_opcintype = PQfnumber(res, "opcintype");
13893 1308 : i_opckeytype = PQfnumber(res, "opckeytype");
13894 1308 : i_opcdefault = PQfnumber(res, "opcdefault");
13895 1308 : i_opcfamily = PQfnumber(res, "opcfamily");
13896 1308 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13897 1308 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13898 1308 : i_amname = PQfnumber(res, "amname");
13899 :
13900 : /* opcintype may still be needed after we PQclear res */
13901 1308 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13902 1308 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
13903 1308 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
13904 : /* opcfamily will still be needed after we PQclear res */
13905 1308 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13906 1308 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13907 1308 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13908 : /* amname will still be needed after we PQclear res */
13909 1308 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13910 :
13911 1308 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13912 1308 : fmtQualifiedDumpable(opcinfo));
13913 1308 : appendPQExpBuffer(delq, " USING %s;\n",
13914 : fmtId(amname));
13915 :
13916 : /* Build the fixed portion of the CREATE command */
13917 1308 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13918 1308 : fmtQualifiedDumpable(opcinfo));
13919 1308 : if (strcmp(opcdefault, "t") == 0)
13920 714 : appendPQExpBufferStr(q, "DEFAULT ");
13921 1308 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13922 : opcintype,
13923 : fmtId(amname));
13924 1308 : if (strlen(opcfamilyname) > 0)
13925 : {
13926 1308 : appendPQExpBufferStr(q, " FAMILY ");
13927 1308 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13928 1308 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
13929 : }
13930 1308 : appendPQExpBufferStr(q, " AS\n ");
13931 :
13932 1308 : needComma = false;
13933 :
13934 1308 : if (strcmp(opckeytype, "-") != 0)
13935 : {
13936 504 : appendPQExpBuffer(q, "STORAGE %s",
13937 : opckeytype);
13938 504 : needComma = true;
13939 : }
13940 :
13941 1308 : PQclear(res);
13942 :
13943 : /*
13944 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13945 : *
13946 : * Print only those opfamily members that are tied to the opclass by
13947 : * pg_depend entries.
13948 : */
13949 1308 : resetPQExpBuffer(query);
13950 1308 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13951 : "amopopr::pg_catalog.regoperator, "
13952 : "opfname AS sortfamily, "
13953 : "nspname AS sortfamilynsp "
13954 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13955 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13956 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13957 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13958 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13959 : "AND refobjid = '%u'::pg_catalog.oid "
13960 : "AND amopfamily = '%s'::pg_catalog.oid "
13961 : "ORDER BY amopstrategy",
13962 : opcinfo->dobj.catId.oid,
13963 : opcfamily);
13964 :
13965 1308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13966 :
13967 1308 : ntups = PQntuples(res);
13968 :
13969 1308 : i_amopstrategy = PQfnumber(res, "amopstrategy");
13970 1308 : i_amopopr = PQfnumber(res, "amopopr");
13971 1308 : i_sortfamily = PQfnumber(res, "sortfamily");
13972 1308 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13973 :
13974 1760 : for (i = 0; i < ntups; i++)
13975 : {
13976 452 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13977 452 : amopopr = PQgetvalue(res, i, i_amopopr);
13978 452 : sortfamily = PQgetvalue(res, i, i_sortfamily);
13979 452 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13980 :
13981 452 : if (needComma)
13982 288 : appendPQExpBufferStr(q, " ,\n ");
13983 :
13984 452 : appendPQExpBuffer(q, "OPERATOR %s %s",
13985 : amopstrategy, amopopr);
13986 :
13987 452 : if (strlen(sortfamily) > 0)
13988 : {
13989 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13990 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13991 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13992 : }
13993 :
13994 452 : needComma = true;
13995 : }
13996 :
13997 1308 : PQclear(res);
13998 :
13999 : /*
14000 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14001 : *
14002 : * Print only those opfamily members that are tied to the opclass by
14003 : * pg_depend entries.
14004 : *
14005 : * We print the amproclefttype/amprocrighttype even though in most cases
14006 : * the backend could deduce the right values, because of the corner case
14007 : * of a btree sort support function for a cross-type comparison.
14008 : */
14009 1308 : resetPQExpBuffer(query);
14010 :
14011 1308 : appendPQExpBuffer(query, "SELECT amprocnum, "
14012 : "amproc::pg_catalog.regprocedure, "
14013 : "amproclefttype::pg_catalog.regtype, "
14014 : "amprocrighttype::pg_catalog.regtype "
14015 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14016 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14017 : "AND refobjid = '%u'::pg_catalog.oid "
14018 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14019 : "AND objid = ap.oid "
14020 : "ORDER BY amprocnum",
14021 : opcinfo->dobj.catId.oid);
14022 :
14023 1308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14024 :
14025 1308 : ntups = PQntuples(res);
14026 :
14027 1308 : i_amprocnum = PQfnumber(res, "amprocnum");
14028 1308 : i_amproc = PQfnumber(res, "amproc");
14029 1308 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14030 1308 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14031 :
14032 1380 : for (i = 0; i < ntups; i++)
14033 : {
14034 72 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14035 72 : amproc = PQgetvalue(res, i, i_amproc);
14036 72 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14037 72 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14038 :
14039 72 : if (needComma)
14040 72 : appendPQExpBufferStr(q, " ,\n ");
14041 :
14042 72 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14043 :
14044 72 : if (*amproclefttype && *amprocrighttype)
14045 72 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14046 :
14047 72 : appendPQExpBuffer(q, " %s", amproc);
14048 :
14049 72 : needComma = true;
14050 : }
14051 :
14052 1308 : PQclear(res);
14053 :
14054 : /*
14055 : * If needComma is still false it means we haven't added anything after
14056 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14057 : * clause with the same datatype. This isn't sanctioned by the
14058 : * documentation, but actually DefineOpClass will treat it as a no-op.
14059 : */
14060 1308 : if (!needComma)
14061 640 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14062 :
14063 1308 : appendPQExpBufferStr(q, ";\n");
14064 :
14065 1308 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14066 1308 : appendPQExpBuffer(nameusing, " USING %s",
14067 : fmtId(amname));
14068 :
14069 1308 : if (dopt->binary_upgrade)
14070 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14071 12 : "OPERATOR CLASS", nameusing->data,
14072 12 : opcinfo->dobj.namespace->dobj.name);
14073 :
14074 1308 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14075 1308 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14076 1308 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14077 : .namespace = opcinfo->dobj.namespace->dobj.name,
14078 : .owner = opcinfo->rolname,
14079 : .description = "OPERATOR CLASS",
14080 : .section = SECTION_PRE_DATA,
14081 : .createStmt = q->data,
14082 : .dropStmt = delq->data));
14083 :
14084 : /* Dump Operator Class Comments */
14085 1308 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14086 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14087 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14088 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14089 :
14090 1308 : free(opcintype);
14091 1308 : free(opcfamily);
14092 1308 : free(amname);
14093 1308 : destroyPQExpBuffer(query);
14094 1308 : destroyPQExpBuffer(q);
14095 1308 : destroyPQExpBuffer(delq);
14096 1308 : destroyPQExpBuffer(nameusing);
14097 : }
14098 :
14099 : /*
14100 : * dumpOpfamily
14101 : * write out a single operator family definition
14102 : *
14103 : * Note: this also dumps any "loose" operator members that aren't bound to a
14104 : * specific opclass within the opfamily.
14105 : */
14106 : static void
14107 1114 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14108 : {
14109 1114 : DumpOptions *dopt = fout->dopt;
14110 : PQExpBuffer query;
14111 : PQExpBuffer q;
14112 : PQExpBuffer delq;
14113 : PQExpBuffer nameusing;
14114 : PGresult *res;
14115 : PGresult *res_ops;
14116 : PGresult *res_procs;
14117 : int ntups;
14118 : int i_amname;
14119 : int i_amopstrategy;
14120 : int i_amopopr;
14121 : int i_sortfamily;
14122 : int i_sortfamilynsp;
14123 : int i_amprocnum;
14124 : int i_amproc;
14125 : int i_amproclefttype;
14126 : int i_amprocrighttype;
14127 : char *amname;
14128 : char *amopstrategy;
14129 : char *amopopr;
14130 : char *sortfamily;
14131 : char *sortfamilynsp;
14132 : char *amprocnum;
14133 : char *amproc;
14134 : char *amproclefttype;
14135 : char *amprocrighttype;
14136 : bool needComma;
14137 : int i;
14138 :
14139 : /* Do nothing if not dumping schema */
14140 1114 : if (!dopt->dumpSchema)
14141 24 : return;
14142 :
14143 1090 : query = createPQExpBuffer();
14144 1090 : q = createPQExpBuffer();
14145 1090 : delq = createPQExpBuffer();
14146 1090 : nameusing = createPQExpBuffer();
14147 :
14148 : /*
14149 : * Fetch only those opfamily members that are tied directly to the
14150 : * opfamily by pg_depend entries.
14151 : */
14152 1090 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14153 : "amopopr::pg_catalog.regoperator, "
14154 : "opfname AS sortfamily, "
14155 : "nspname AS sortfamilynsp "
14156 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14157 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14158 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14159 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14160 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14161 : "AND refobjid = '%u'::pg_catalog.oid "
14162 : "AND amopfamily = '%u'::pg_catalog.oid "
14163 : "ORDER BY amopstrategy",
14164 : opfinfo->dobj.catId.oid,
14165 : opfinfo->dobj.catId.oid);
14166 :
14167 1090 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14168 :
14169 1090 : resetPQExpBuffer(query);
14170 :
14171 1090 : appendPQExpBuffer(query, "SELECT amprocnum, "
14172 : "amproc::pg_catalog.regprocedure, "
14173 : "amproclefttype::pg_catalog.regtype, "
14174 : "amprocrighttype::pg_catalog.regtype "
14175 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14176 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14177 : "AND refobjid = '%u'::pg_catalog.oid "
14178 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14179 : "AND objid = ap.oid "
14180 : "ORDER BY amprocnum",
14181 : opfinfo->dobj.catId.oid);
14182 :
14183 1090 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14184 :
14185 : /* Get additional fields from the pg_opfamily row */
14186 1090 : resetPQExpBuffer(query);
14187 :
14188 1090 : appendPQExpBuffer(query, "SELECT "
14189 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14190 : "FROM pg_catalog.pg_opfamily "
14191 : "WHERE oid = '%u'::pg_catalog.oid",
14192 : opfinfo->dobj.catId.oid);
14193 :
14194 1090 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14195 :
14196 1090 : i_amname = PQfnumber(res, "amname");
14197 :
14198 : /* amname will still be needed after we PQclear res */
14199 1090 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14200 :
14201 1090 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14202 1090 : fmtQualifiedDumpable(opfinfo));
14203 1090 : appendPQExpBuffer(delq, " USING %s;\n",
14204 : fmtId(amname));
14205 :
14206 : /* Build the fixed portion of the CREATE command */
14207 1090 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14208 1090 : fmtQualifiedDumpable(opfinfo));
14209 1090 : appendPQExpBuffer(q, " USING %s;\n",
14210 : fmtId(amname));
14211 :
14212 1090 : PQclear(res);
14213 :
14214 : /* Do we need an ALTER to add loose members? */
14215 1090 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14216 : {
14217 102 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14218 102 : fmtQualifiedDumpable(opfinfo));
14219 102 : appendPQExpBuffer(q, " USING %s ADD\n ",
14220 : fmtId(amname));
14221 :
14222 102 : needComma = false;
14223 :
14224 : /*
14225 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14226 : */
14227 102 : ntups = PQntuples(res_ops);
14228 :
14229 102 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14230 102 : i_amopopr = PQfnumber(res_ops, "amopopr");
14231 102 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14232 102 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14233 :
14234 462 : for (i = 0; i < ntups; i++)
14235 : {
14236 360 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14237 360 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14238 360 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14239 360 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14240 :
14241 360 : if (needComma)
14242 288 : appendPQExpBufferStr(q, " ,\n ");
14243 :
14244 360 : appendPQExpBuffer(q, "OPERATOR %s %s",
14245 : amopstrategy, amopopr);
14246 :
14247 360 : if (strlen(sortfamily) > 0)
14248 : {
14249 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14250 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14251 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14252 : }
14253 :
14254 360 : needComma = true;
14255 : }
14256 :
14257 : /*
14258 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14259 : */
14260 102 : ntups = PQntuples(res_procs);
14261 :
14262 102 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14263 102 : i_amproc = PQfnumber(res_procs, "amproc");
14264 102 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14265 102 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14266 :
14267 492 : for (i = 0; i < ntups; i++)
14268 : {
14269 390 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14270 390 : amproc = PQgetvalue(res_procs, i, i_amproc);
14271 390 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14272 390 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14273 :
14274 390 : if (needComma)
14275 360 : appendPQExpBufferStr(q, " ,\n ");
14276 :
14277 390 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14278 : amprocnum, amproclefttype, amprocrighttype,
14279 : amproc);
14280 :
14281 390 : needComma = true;
14282 : }
14283 :
14284 102 : appendPQExpBufferStr(q, ";\n");
14285 : }
14286 :
14287 1090 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14288 1090 : appendPQExpBuffer(nameusing, " USING %s",
14289 : fmtId(amname));
14290 :
14291 1090 : if (dopt->binary_upgrade)
14292 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14293 18 : "OPERATOR FAMILY", nameusing->data,
14294 18 : opfinfo->dobj.namespace->dobj.name);
14295 :
14296 1090 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14297 1090 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14298 1090 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14299 : .namespace = opfinfo->dobj.namespace->dobj.name,
14300 : .owner = opfinfo->rolname,
14301 : .description = "OPERATOR FAMILY",
14302 : .section = SECTION_PRE_DATA,
14303 : .createStmt = q->data,
14304 : .dropStmt = delq->data));
14305 :
14306 : /* Dump Operator Family Comments */
14307 1090 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14308 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14309 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14310 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14311 :
14312 1090 : free(amname);
14313 1090 : PQclear(res_ops);
14314 1090 : PQclear(res_procs);
14315 1090 : destroyPQExpBuffer(query);
14316 1090 : destroyPQExpBuffer(q);
14317 1090 : destroyPQExpBuffer(delq);
14318 1090 : destroyPQExpBuffer(nameusing);
14319 : }
14320 :
14321 : /*
14322 : * dumpCollation
14323 : * write out a single collation definition
14324 : */
14325 : static void
14326 4952 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14327 : {
14328 4952 : DumpOptions *dopt = fout->dopt;
14329 : PQExpBuffer query;
14330 : PQExpBuffer q;
14331 : PQExpBuffer delq;
14332 : char *qcollname;
14333 : PGresult *res;
14334 : int i_collprovider;
14335 : int i_collisdeterministic;
14336 : int i_collcollate;
14337 : int i_collctype;
14338 : int i_colllocale;
14339 : int i_collicurules;
14340 : const char *collprovider;
14341 : const char *collcollate;
14342 : const char *collctype;
14343 : const char *colllocale;
14344 : const char *collicurules;
14345 :
14346 : /* Do nothing if not dumping schema */
14347 4952 : if (!dopt->dumpSchema)
14348 24 : return;
14349 :
14350 4928 : query = createPQExpBuffer();
14351 4928 : q = createPQExpBuffer();
14352 4928 : delq = createPQExpBuffer();
14353 :
14354 4928 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14355 :
14356 : /* Get collation-specific details */
14357 4928 : appendPQExpBufferStr(query, "SELECT ");
14358 :
14359 4928 : if (fout->remoteVersion >= 100000)
14360 4928 : appendPQExpBufferStr(query,
14361 : "collprovider, "
14362 : "collversion, ");
14363 : else
14364 0 : appendPQExpBufferStr(query,
14365 : "'c' AS collprovider, "
14366 : "NULL AS collversion, ");
14367 :
14368 4928 : if (fout->remoteVersion >= 120000)
14369 4928 : appendPQExpBufferStr(query,
14370 : "collisdeterministic, ");
14371 : else
14372 0 : appendPQExpBufferStr(query,
14373 : "true AS collisdeterministic, ");
14374 :
14375 4928 : if (fout->remoteVersion >= 170000)
14376 4928 : appendPQExpBufferStr(query,
14377 : "colllocale, ");
14378 0 : else if (fout->remoteVersion >= 150000)
14379 0 : appendPQExpBufferStr(query,
14380 : "colliculocale AS colllocale, ");
14381 : else
14382 0 : appendPQExpBufferStr(query,
14383 : "NULL AS colllocale, ");
14384 :
14385 4928 : if (fout->remoteVersion >= 160000)
14386 4928 : appendPQExpBufferStr(query,
14387 : "collicurules, ");
14388 : else
14389 0 : appendPQExpBufferStr(query,
14390 : "NULL AS collicurules, ");
14391 :
14392 4928 : appendPQExpBuffer(query,
14393 : "collcollate, "
14394 : "collctype "
14395 : "FROM pg_catalog.pg_collation c "
14396 : "WHERE c.oid = '%u'::pg_catalog.oid",
14397 : collinfo->dobj.catId.oid);
14398 :
14399 4928 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14400 :
14401 4928 : i_collprovider = PQfnumber(res, "collprovider");
14402 4928 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14403 4928 : i_collcollate = PQfnumber(res, "collcollate");
14404 4928 : i_collctype = PQfnumber(res, "collctype");
14405 4928 : i_colllocale = PQfnumber(res, "colllocale");
14406 4928 : i_collicurules = PQfnumber(res, "collicurules");
14407 :
14408 4928 : collprovider = PQgetvalue(res, 0, i_collprovider);
14409 :
14410 4928 : if (!PQgetisnull(res, 0, i_collcollate))
14411 100 : collcollate = PQgetvalue(res, 0, i_collcollate);
14412 : else
14413 4828 : collcollate = NULL;
14414 :
14415 4928 : if (!PQgetisnull(res, 0, i_collctype))
14416 100 : collctype = PQgetvalue(res, 0, i_collctype);
14417 : else
14418 4828 : collctype = NULL;
14419 :
14420 : /*
14421 : * Before version 15, collcollate and collctype were of type NAME and
14422 : * non-nullable. Treat empty strings as NULL for consistency.
14423 : */
14424 4928 : if (fout->remoteVersion < 150000)
14425 : {
14426 0 : if (collcollate[0] == '\0')
14427 0 : collcollate = NULL;
14428 0 : if (collctype[0] == '\0')
14429 0 : collctype = NULL;
14430 : }
14431 :
14432 4928 : if (!PQgetisnull(res, 0, i_colllocale))
14433 4822 : colllocale = PQgetvalue(res, 0, i_colllocale);
14434 : else
14435 106 : colllocale = NULL;
14436 :
14437 4928 : if (!PQgetisnull(res, 0, i_collicurules))
14438 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
14439 : else
14440 4928 : collicurules = NULL;
14441 :
14442 4928 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
14443 4928 : fmtQualifiedDumpable(collinfo));
14444 :
14445 4928 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
14446 4928 : fmtQualifiedDumpable(collinfo));
14447 :
14448 4928 : appendPQExpBufferStr(q, "provider = ");
14449 4928 : if (collprovider[0] == 'b')
14450 38 : appendPQExpBufferStr(q, "builtin");
14451 4890 : else if (collprovider[0] == 'c')
14452 100 : appendPQExpBufferStr(q, "libc");
14453 4790 : else if (collprovider[0] == 'i')
14454 4784 : appendPQExpBufferStr(q, "icu");
14455 6 : else if (collprovider[0] == 'd')
14456 : /* to allow dumping pg_catalog; not accepted on input */
14457 6 : appendPQExpBufferStr(q, "default");
14458 : else
14459 0 : pg_fatal("unrecognized collation provider: %s",
14460 : collprovider);
14461 :
14462 4928 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14463 0 : appendPQExpBufferStr(q, ", deterministic = false");
14464 :
14465 4928 : if (collprovider[0] == 'd')
14466 : {
14467 6 : if (collcollate || collctype || colllocale || collicurules)
14468 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14469 :
14470 : /* no locale -- the default collation cannot be reloaded anyway */
14471 : }
14472 4922 : else if (collprovider[0] == 'b')
14473 : {
14474 38 : if (collcollate || collctype || !colllocale || collicurules)
14475 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14476 :
14477 38 : appendPQExpBufferStr(q, ", locale = ");
14478 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14479 : fout);
14480 : }
14481 4884 : else if (collprovider[0] == 'i')
14482 : {
14483 4784 : if (fout->remoteVersion >= 150000)
14484 : {
14485 4784 : if (collcollate || collctype || !colllocale)
14486 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14487 :
14488 4784 : appendPQExpBufferStr(q, ", locale = ");
14489 4784 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14490 : fout);
14491 : }
14492 : else
14493 : {
14494 0 : if (!collcollate || !collctype || colllocale ||
14495 0 : strcmp(collcollate, collctype) != 0)
14496 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14497 :
14498 0 : appendPQExpBufferStr(q, ", locale = ");
14499 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14500 : }
14501 :
14502 4784 : if (collicurules)
14503 : {
14504 0 : appendPQExpBufferStr(q, ", rules = ");
14505 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14506 : }
14507 : }
14508 100 : else if (collprovider[0] == 'c')
14509 : {
14510 100 : if (colllocale || collicurules || !collcollate || !collctype)
14511 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14512 :
14513 100 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14514 : {
14515 100 : appendPQExpBufferStr(q, ", locale = ");
14516 100 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14517 : }
14518 : else
14519 : {
14520 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14521 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14522 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14523 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14524 : }
14525 : }
14526 : else
14527 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14528 :
14529 : /*
14530 : * For binary upgrade, carry over the collation version. For normal
14531 : * dump/restore, omit the version, so that it is computed upon restore.
14532 : */
14533 4928 : if (dopt->binary_upgrade)
14534 : {
14535 : int i_collversion;
14536 :
14537 10 : i_collversion = PQfnumber(res, "collversion");
14538 10 : if (!PQgetisnull(res, 0, i_collversion))
14539 : {
14540 8 : appendPQExpBufferStr(q, ", version = ");
14541 8 : appendStringLiteralAH(q,
14542 : PQgetvalue(res, 0, i_collversion),
14543 : fout);
14544 : }
14545 : }
14546 :
14547 4928 : appendPQExpBufferStr(q, ");\n");
14548 :
14549 4928 : if (dopt->binary_upgrade)
14550 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
14551 : "COLLATION", qcollname,
14552 10 : collinfo->dobj.namespace->dobj.name);
14553 :
14554 4928 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14555 4928 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14556 4928 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14557 : .namespace = collinfo->dobj.namespace->dobj.name,
14558 : .owner = collinfo->rolname,
14559 : .description = "COLLATION",
14560 : .section = SECTION_PRE_DATA,
14561 : .createStmt = q->data,
14562 : .dropStmt = delq->data));
14563 :
14564 : /* Dump Collation Comments */
14565 4928 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14566 4744 : dumpComment(fout, "COLLATION", qcollname,
14567 4744 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14568 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14569 :
14570 4928 : PQclear(res);
14571 :
14572 4928 : destroyPQExpBuffer(query);
14573 4928 : destroyPQExpBuffer(q);
14574 4928 : destroyPQExpBuffer(delq);
14575 4928 : free(qcollname);
14576 : }
14577 :
14578 : /*
14579 : * dumpConversion
14580 : * write out a single conversion definition
14581 : */
14582 : static void
14583 852 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14584 : {
14585 852 : DumpOptions *dopt = fout->dopt;
14586 : PQExpBuffer query;
14587 : PQExpBuffer q;
14588 : PQExpBuffer delq;
14589 : char *qconvname;
14590 : PGresult *res;
14591 : int i_conforencoding;
14592 : int i_contoencoding;
14593 : int i_conproc;
14594 : int i_condefault;
14595 : const char *conforencoding;
14596 : const char *contoencoding;
14597 : const char *conproc;
14598 : bool condefault;
14599 :
14600 : /* Do nothing if not dumping schema */
14601 852 : if (!dopt->dumpSchema)
14602 12 : return;
14603 :
14604 840 : query = createPQExpBuffer();
14605 840 : q = createPQExpBuffer();
14606 840 : delq = createPQExpBuffer();
14607 :
14608 840 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14609 :
14610 : /* Get conversion-specific details */
14611 840 : appendPQExpBuffer(query, "SELECT "
14612 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14613 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14614 : "conproc, condefault "
14615 : "FROM pg_catalog.pg_conversion c "
14616 : "WHERE c.oid = '%u'::pg_catalog.oid",
14617 : convinfo->dobj.catId.oid);
14618 :
14619 840 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14620 :
14621 840 : i_conforencoding = PQfnumber(res, "conforencoding");
14622 840 : i_contoencoding = PQfnumber(res, "contoencoding");
14623 840 : i_conproc = PQfnumber(res, "conproc");
14624 840 : i_condefault = PQfnumber(res, "condefault");
14625 :
14626 840 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14627 840 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14628 840 : conproc = PQgetvalue(res, 0, i_conproc);
14629 840 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14630 :
14631 840 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14632 840 : fmtQualifiedDumpable(convinfo));
14633 :
14634 840 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14635 : (condefault) ? "DEFAULT " : "",
14636 840 : fmtQualifiedDumpable(convinfo));
14637 840 : appendStringLiteralAH(q, conforencoding, fout);
14638 840 : appendPQExpBufferStr(q, " TO ");
14639 840 : appendStringLiteralAH(q, contoencoding, fout);
14640 : /* regproc output is already sufficiently quoted */
14641 840 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14642 :
14643 840 : if (dopt->binary_upgrade)
14644 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14645 : "CONVERSION", qconvname,
14646 2 : convinfo->dobj.namespace->dobj.name);
14647 :
14648 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14649 840 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14650 840 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14651 : .namespace = convinfo->dobj.namespace->dobj.name,
14652 : .owner = convinfo->rolname,
14653 : .description = "CONVERSION",
14654 : .section = SECTION_PRE_DATA,
14655 : .createStmt = q->data,
14656 : .dropStmt = delq->data));
14657 :
14658 : /* Dump Conversion Comments */
14659 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14660 840 : dumpComment(fout, "CONVERSION", qconvname,
14661 840 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14662 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14663 :
14664 840 : PQclear(res);
14665 :
14666 840 : destroyPQExpBuffer(query);
14667 840 : destroyPQExpBuffer(q);
14668 840 : destroyPQExpBuffer(delq);
14669 840 : free(qconvname);
14670 : }
14671 :
14672 : /*
14673 : * format_aggregate_signature: generate aggregate name and argument list
14674 : *
14675 : * The argument type names are qualified if needed. The aggregate name
14676 : * is never qualified.
14677 : */
14678 : static char *
14679 578 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14680 : {
14681 : PQExpBufferData buf;
14682 : int j;
14683 :
14684 578 : initPQExpBuffer(&buf);
14685 578 : if (honor_quotes)
14686 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14687 : else
14688 578 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14689 :
14690 578 : if (agginfo->aggfn.nargs == 0)
14691 80 : appendPQExpBufferStr(&buf, "(*)");
14692 : else
14693 : {
14694 498 : appendPQExpBufferChar(&buf, '(');
14695 1086 : for (j = 0; j < agginfo->aggfn.nargs; j++)
14696 588 : appendPQExpBuffer(&buf, "%s%s",
14697 : (j > 0) ? ", " : "",
14698 : getFormattedTypeName(fout,
14699 588 : agginfo->aggfn.argtypes[j],
14700 : zeroIsError));
14701 498 : appendPQExpBufferChar(&buf, ')');
14702 : }
14703 578 : return buf.data;
14704 : }
14705 :
14706 : /*
14707 : * dumpAgg
14708 : * write out a single aggregate definition
14709 : */
14710 : static void
14711 592 : dumpAgg(Archive *fout, const AggInfo *agginfo)
14712 : {
14713 592 : DumpOptions *dopt = fout->dopt;
14714 : PQExpBuffer query;
14715 : PQExpBuffer q;
14716 : PQExpBuffer delq;
14717 : PQExpBuffer details;
14718 : char *aggsig; /* identity signature */
14719 592 : char *aggfullsig = NULL; /* full signature */
14720 : char *aggsig_tag;
14721 : PGresult *res;
14722 : int i_agginitval;
14723 : int i_aggminitval;
14724 : const char *aggtransfn;
14725 : const char *aggfinalfn;
14726 : const char *aggcombinefn;
14727 : const char *aggserialfn;
14728 : const char *aggdeserialfn;
14729 : const char *aggmtransfn;
14730 : const char *aggminvtransfn;
14731 : const char *aggmfinalfn;
14732 : bool aggfinalextra;
14733 : bool aggmfinalextra;
14734 : char aggfinalmodify;
14735 : char aggmfinalmodify;
14736 : const char *aggsortop;
14737 : char *aggsortconvop;
14738 : char aggkind;
14739 : const char *aggtranstype;
14740 : const char *aggtransspace;
14741 : const char *aggmtranstype;
14742 : const char *aggmtransspace;
14743 : const char *agginitval;
14744 : const char *aggminitval;
14745 : const char *proparallel;
14746 : char defaultfinalmodify;
14747 :
14748 : /* Do nothing if not dumping schema */
14749 592 : if (!dopt->dumpSchema)
14750 14 : return;
14751 :
14752 578 : query = createPQExpBuffer();
14753 578 : q = createPQExpBuffer();
14754 578 : delq = createPQExpBuffer();
14755 578 : details = createPQExpBuffer();
14756 :
14757 578 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14758 : {
14759 : /* Set up query for aggregate-specific details */
14760 118 : appendPQExpBufferStr(query,
14761 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14762 :
14763 118 : appendPQExpBufferStr(query,
14764 : "SELECT "
14765 : "aggtransfn,\n"
14766 : "aggfinalfn,\n"
14767 : "aggtranstype::pg_catalog.regtype,\n"
14768 : "agginitval,\n"
14769 : "aggsortop,\n"
14770 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14771 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14772 :
14773 118 : if (fout->remoteVersion >= 90400)
14774 118 : appendPQExpBufferStr(query,
14775 : "aggkind,\n"
14776 : "aggmtransfn,\n"
14777 : "aggminvtransfn,\n"
14778 : "aggmfinalfn,\n"
14779 : "aggmtranstype::pg_catalog.regtype,\n"
14780 : "aggfinalextra,\n"
14781 : "aggmfinalextra,\n"
14782 : "aggtransspace,\n"
14783 : "aggmtransspace,\n"
14784 : "aggminitval,\n");
14785 : else
14786 0 : appendPQExpBufferStr(query,
14787 : "'n' AS aggkind,\n"
14788 : "'-' AS aggmtransfn,\n"
14789 : "'-' AS aggminvtransfn,\n"
14790 : "'-' AS aggmfinalfn,\n"
14791 : "0 AS aggmtranstype,\n"
14792 : "false AS aggfinalextra,\n"
14793 : "false AS aggmfinalextra,\n"
14794 : "0 AS aggtransspace,\n"
14795 : "0 AS aggmtransspace,\n"
14796 : "NULL AS aggminitval,\n");
14797 :
14798 118 : if (fout->remoteVersion >= 90600)
14799 118 : appendPQExpBufferStr(query,
14800 : "aggcombinefn,\n"
14801 : "aggserialfn,\n"
14802 : "aggdeserialfn,\n"
14803 : "proparallel,\n");
14804 : else
14805 0 : appendPQExpBufferStr(query,
14806 : "'-' AS aggcombinefn,\n"
14807 : "'-' AS aggserialfn,\n"
14808 : "'-' AS aggdeserialfn,\n"
14809 : "'u' AS proparallel,\n");
14810 :
14811 118 : if (fout->remoteVersion >= 110000)
14812 118 : appendPQExpBufferStr(query,
14813 : "aggfinalmodify,\n"
14814 : "aggmfinalmodify\n");
14815 : else
14816 0 : appendPQExpBufferStr(query,
14817 : "'0' AS aggfinalmodify,\n"
14818 : "'0' AS aggmfinalmodify\n");
14819 :
14820 118 : appendPQExpBufferStr(query,
14821 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14822 : "WHERE a.aggfnoid = p.oid "
14823 : "AND p.oid = $1");
14824 :
14825 118 : ExecuteSqlStatement(fout, query->data);
14826 :
14827 118 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14828 : }
14829 :
14830 578 : printfPQExpBuffer(query,
14831 : "EXECUTE dumpAgg('%u')",
14832 : agginfo->aggfn.dobj.catId.oid);
14833 :
14834 578 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14835 :
14836 578 : i_agginitval = PQfnumber(res, "agginitval");
14837 578 : i_aggminitval = PQfnumber(res, "aggminitval");
14838 :
14839 578 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14840 578 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14841 578 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14842 578 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14843 578 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14844 578 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14845 578 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14846 578 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14847 578 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14848 578 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14849 578 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14850 578 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14851 578 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14852 578 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14853 578 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14854 578 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14855 578 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14856 578 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14857 578 : agginitval = PQgetvalue(res, 0, i_agginitval);
14858 578 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
14859 578 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14860 :
14861 : {
14862 : char *funcargs;
14863 : char *funciargs;
14864 :
14865 578 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14866 578 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14867 578 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14868 578 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14869 : }
14870 :
14871 578 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14872 :
14873 : /* identify default modify flag for aggkind (must match DefineAggregate) */
14874 578 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14875 : /* replace omitted flags for old versions */
14876 578 : if (aggfinalmodify == '0')
14877 0 : aggfinalmodify = defaultfinalmodify;
14878 578 : if (aggmfinalmodify == '0')
14879 0 : aggmfinalmodify = defaultfinalmodify;
14880 :
14881 : /* regproc and regtype output is already sufficiently quoted */
14882 578 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14883 : aggtransfn, aggtranstype);
14884 :
14885 578 : if (strcmp(aggtransspace, "0") != 0)
14886 : {
14887 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
14888 : aggtransspace);
14889 : }
14890 :
14891 578 : if (!PQgetisnull(res, 0, i_agginitval))
14892 : {
14893 422 : appendPQExpBufferStr(details, ",\n INITCOND = ");
14894 422 : appendStringLiteralAH(details, agginitval, fout);
14895 : }
14896 :
14897 578 : if (strcmp(aggfinalfn, "-") != 0)
14898 : {
14899 272 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14900 : aggfinalfn);
14901 272 : if (aggfinalextra)
14902 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14903 272 : if (aggfinalmodify != defaultfinalmodify)
14904 : {
14905 72 : switch (aggfinalmodify)
14906 : {
14907 0 : case AGGMODIFY_READ_ONLY:
14908 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14909 0 : break;
14910 72 : case AGGMODIFY_SHAREABLE:
14911 72 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14912 72 : break;
14913 0 : case AGGMODIFY_READ_WRITE:
14914 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14915 0 : break;
14916 0 : default:
14917 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14918 : agginfo->aggfn.dobj.name);
14919 : break;
14920 : }
14921 506 : }
14922 : }
14923 :
14924 578 : if (strcmp(aggcombinefn, "-") != 0)
14925 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14926 :
14927 578 : if (strcmp(aggserialfn, "-") != 0)
14928 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14929 :
14930 578 : if (strcmp(aggdeserialfn, "-") != 0)
14931 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14932 :
14933 578 : if (strcmp(aggmtransfn, "-") != 0)
14934 : {
14935 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14936 : aggmtransfn,
14937 : aggminvtransfn,
14938 : aggmtranstype);
14939 : }
14940 :
14941 578 : if (strcmp(aggmtransspace, "0") != 0)
14942 : {
14943 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
14944 : aggmtransspace);
14945 : }
14946 :
14947 578 : if (!PQgetisnull(res, 0, i_aggminitval))
14948 : {
14949 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
14950 20 : appendStringLiteralAH(details, aggminitval, fout);
14951 : }
14952 :
14953 578 : if (strcmp(aggmfinalfn, "-") != 0)
14954 : {
14955 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14956 : aggmfinalfn);
14957 0 : if (aggmfinalextra)
14958 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14959 0 : if (aggmfinalmodify != defaultfinalmodify)
14960 : {
14961 0 : switch (aggmfinalmodify)
14962 : {
14963 0 : case AGGMODIFY_READ_ONLY:
14964 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14965 0 : break;
14966 0 : case AGGMODIFY_SHAREABLE:
14967 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14968 0 : break;
14969 0 : case AGGMODIFY_READ_WRITE:
14970 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14971 0 : break;
14972 0 : default:
14973 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14974 : agginfo->aggfn.dobj.name);
14975 : break;
14976 : }
14977 578 : }
14978 : }
14979 :
14980 578 : aggsortconvop = getFormattedOperatorName(aggsortop);
14981 578 : if (aggsortconvop)
14982 : {
14983 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
14984 : aggsortconvop);
14985 0 : free(aggsortconvop);
14986 : }
14987 :
14988 578 : if (aggkind == AGGKIND_HYPOTHETICAL)
14989 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14990 :
14991 578 : if (proparallel[0] != PROPARALLEL_UNSAFE)
14992 : {
14993 10 : if (proparallel[0] == PROPARALLEL_SAFE)
14994 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14995 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14996 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14997 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
14998 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
14999 : agginfo->aggfn.dobj.name);
15000 : }
15001 :
15002 578 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15003 578 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15004 : aggsig);
15005 :
15006 1156 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15007 578 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15008 : aggfullsig ? aggfullsig : aggsig, details->data);
15009 :
15010 578 : if (dopt->binary_upgrade)
15011 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15012 : "AGGREGATE", aggsig,
15013 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15014 :
15015 578 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15016 544 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15017 : agginfo->aggfn.dobj.dumpId,
15018 544 : ARCHIVE_OPTS(.tag = aggsig_tag,
15019 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15020 : .owner = agginfo->aggfn.rolname,
15021 : .description = "AGGREGATE",
15022 : .section = SECTION_PRE_DATA,
15023 : .createStmt = q->data,
15024 : .dropStmt = delq->data));
15025 :
15026 : /* Dump Aggregate Comments */
15027 578 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15028 20 : dumpComment(fout, "AGGREGATE", aggsig,
15029 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15030 : agginfo->aggfn.rolname,
15031 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15032 :
15033 578 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15034 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15035 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15036 : agginfo->aggfn.rolname,
15037 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15038 :
15039 : /*
15040 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15041 : * command look like a function's GRANT; in particular this affects the
15042 : * syntax for zero-argument aggregates and ordered-set aggregates.
15043 : */
15044 578 : free(aggsig);
15045 :
15046 578 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15047 :
15048 578 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15049 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15050 : "FUNCTION", aggsig, NULL,
15051 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15052 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15053 :
15054 578 : free(aggsig);
15055 578 : free(aggfullsig);
15056 578 : free(aggsig_tag);
15057 :
15058 578 : PQclear(res);
15059 :
15060 578 : destroyPQExpBuffer(query);
15061 578 : destroyPQExpBuffer(q);
15062 578 : destroyPQExpBuffer(delq);
15063 578 : destroyPQExpBuffer(details);
15064 : }
15065 :
15066 : /*
15067 : * dumpTSParser
15068 : * write out a single text search parser
15069 : */
15070 : static void
15071 90 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15072 : {
15073 90 : DumpOptions *dopt = fout->dopt;
15074 : PQExpBuffer q;
15075 : PQExpBuffer delq;
15076 : char *qprsname;
15077 :
15078 : /* Do nothing if not dumping schema */
15079 90 : if (!dopt->dumpSchema)
15080 12 : return;
15081 :
15082 78 : q = createPQExpBuffer();
15083 78 : delq = createPQExpBuffer();
15084 :
15085 78 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15086 :
15087 78 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15088 78 : fmtQualifiedDumpable(prsinfo));
15089 :
15090 78 : appendPQExpBuffer(q, " START = %s,\n",
15091 : convertTSFunction(fout, prsinfo->prsstart));
15092 78 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15093 : convertTSFunction(fout, prsinfo->prstoken));
15094 78 : appendPQExpBuffer(q, " END = %s,\n",
15095 : convertTSFunction(fout, prsinfo->prsend));
15096 78 : if (prsinfo->prsheadline != InvalidOid)
15097 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15098 : convertTSFunction(fout, prsinfo->prsheadline));
15099 78 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15100 : convertTSFunction(fout, prsinfo->prslextype));
15101 :
15102 78 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15103 78 : fmtQualifiedDumpable(prsinfo));
15104 :
15105 78 : if (dopt->binary_upgrade)
15106 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15107 : "TEXT SEARCH PARSER", qprsname,
15108 2 : prsinfo->dobj.namespace->dobj.name);
15109 :
15110 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15111 78 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15112 78 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15113 : .namespace = prsinfo->dobj.namespace->dobj.name,
15114 : .description = "TEXT SEARCH PARSER",
15115 : .section = SECTION_PRE_DATA,
15116 : .createStmt = q->data,
15117 : .dropStmt = delq->data));
15118 :
15119 : /* Dump Parser Comments */
15120 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15121 78 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15122 78 : prsinfo->dobj.namespace->dobj.name, "",
15123 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15124 :
15125 78 : destroyPQExpBuffer(q);
15126 78 : destroyPQExpBuffer(delq);
15127 78 : free(qprsname);
15128 : }
15129 :
15130 : /*
15131 : * dumpTSDictionary
15132 : * write out a single text search dictionary
15133 : */
15134 : static void
15135 354 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15136 : {
15137 354 : DumpOptions *dopt = fout->dopt;
15138 : PQExpBuffer q;
15139 : PQExpBuffer delq;
15140 : PQExpBuffer query;
15141 : char *qdictname;
15142 : PGresult *res;
15143 : char *nspname;
15144 : char *tmplname;
15145 :
15146 : /* Do nothing if not dumping schema */
15147 354 : if (!dopt->dumpSchema)
15148 12 : return;
15149 :
15150 342 : q = createPQExpBuffer();
15151 342 : delq = createPQExpBuffer();
15152 342 : query = createPQExpBuffer();
15153 :
15154 342 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15155 :
15156 : /* Fetch name and namespace of the dictionary's template */
15157 342 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15158 : "FROM pg_ts_template p, pg_namespace n "
15159 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15160 : dictinfo->dicttemplate);
15161 342 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15162 342 : nspname = PQgetvalue(res, 0, 0);
15163 342 : tmplname = PQgetvalue(res, 0, 1);
15164 :
15165 342 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15166 342 : fmtQualifiedDumpable(dictinfo));
15167 :
15168 342 : appendPQExpBufferStr(q, " TEMPLATE = ");
15169 342 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15170 342 : appendPQExpBufferStr(q, fmtId(tmplname));
15171 :
15172 342 : PQclear(res);
15173 :
15174 : /* the dictinitoption can be dumped straight into the command */
15175 342 : if (dictinfo->dictinitoption)
15176 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15177 :
15178 342 : appendPQExpBufferStr(q, " );\n");
15179 :
15180 342 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15181 342 : fmtQualifiedDumpable(dictinfo));
15182 :
15183 342 : if (dopt->binary_upgrade)
15184 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15185 : "TEXT SEARCH DICTIONARY", qdictname,
15186 20 : dictinfo->dobj.namespace->dobj.name);
15187 :
15188 342 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15189 342 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15190 342 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15191 : .namespace = dictinfo->dobj.namespace->dobj.name,
15192 : .owner = dictinfo->rolname,
15193 : .description = "TEXT SEARCH DICTIONARY",
15194 : .section = SECTION_PRE_DATA,
15195 : .createStmt = q->data,
15196 : .dropStmt = delq->data));
15197 :
15198 : /* Dump Dictionary Comments */
15199 342 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15200 252 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15201 252 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15202 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15203 :
15204 342 : destroyPQExpBuffer(q);
15205 342 : destroyPQExpBuffer(delq);
15206 342 : destroyPQExpBuffer(query);
15207 342 : free(qdictname);
15208 : }
15209 :
15210 : /*
15211 : * dumpTSTemplate
15212 : * write out a single text search template
15213 : */
15214 : static void
15215 114 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15216 : {
15217 114 : DumpOptions *dopt = fout->dopt;
15218 : PQExpBuffer q;
15219 : PQExpBuffer delq;
15220 : char *qtmplname;
15221 :
15222 : /* Do nothing if not dumping schema */
15223 114 : if (!dopt->dumpSchema)
15224 12 : return;
15225 :
15226 102 : q = createPQExpBuffer();
15227 102 : delq = createPQExpBuffer();
15228 :
15229 102 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15230 :
15231 102 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15232 102 : fmtQualifiedDumpable(tmplinfo));
15233 :
15234 102 : if (tmplinfo->tmplinit != InvalidOid)
15235 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15236 : convertTSFunction(fout, tmplinfo->tmplinit));
15237 102 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15238 : convertTSFunction(fout, tmplinfo->tmpllexize));
15239 :
15240 102 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15241 102 : fmtQualifiedDumpable(tmplinfo));
15242 :
15243 102 : if (dopt->binary_upgrade)
15244 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15245 : "TEXT SEARCH TEMPLATE", qtmplname,
15246 2 : tmplinfo->dobj.namespace->dobj.name);
15247 :
15248 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15249 102 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15250 102 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15251 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15252 : .description = "TEXT SEARCH TEMPLATE",
15253 : .section = SECTION_PRE_DATA,
15254 : .createStmt = q->data,
15255 : .dropStmt = delq->data));
15256 :
15257 : /* Dump Template Comments */
15258 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15259 102 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15260 102 : tmplinfo->dobj.namespace->dobj.name, "",
15261 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15262 :
15263 102 : destroyPQExpBuffer(q);
15264 102 : destroyPQExpBuffer(delq);
15265 102 : free(qtmplname);
15266 : }
15267 :
15268 : /*
15269 : * dumpTSConfig
15270 : * write out a single text search configuration
15271 : */
15272 : static void
15273 304 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15274 : {
15275 304 : DumpOptions *dopt = fout->dopt;
15276 : PQExpBuffer q;
15277 : PQExpBuffer delq;
15278 : PQExpBuffer query;
15279 : char *qcfgname;
15280 : PGresult *res;
15281 : char *nspname;
15282 : char *prsname;
15283 : int ntups,
15284 : i;
15285 : int i_tokenname;
15286 : int i_dictname;
15287 :
15288 : /* Do nothing if not dumping schema */
15289 304 : if (!dopt->dumpSchema)
15290 12 : return;
15291 :
15292 292 : q = createPQExpBuffer();
15293 292 : delq = createPQExpBuffer();
15294 292 : query = createPQExpBuffer();
15295 :
15296 292 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15297 :
15298 : /* Fetch name and namespace of the config's parser */
15299 292 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15300 : "FROM pg_ts_parser p, pg_namespace n "
15301 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15302 : cfginfo->cfgparser);
15303 292 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15304 292 : nspname = PQgetvalue(res, 0, 0);
15305 292 : prsname = PQgetvalue(res, 0, 1);
15306 :
15307 292 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15308 292 : fmtQualifiedDumpable(cfginfo));
15309 :
15310 292 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15311 292 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15312 :
15313 292 : PQclear(res);
15314 :
15315 292 : resetPQExpBuffer(query);
15316 292 : appendPQExpBuffer(query,
15317 : "SELECT\n"
15318 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15319 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15320 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15321 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15322 : "WHERE m.mapcfg = '%u'\n"
15323 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15324 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15325 :
15326 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15327 292 : ntups = PQntuples(res);
15328 :
15329 292 : i_tokenname = PQfnumber(res, "tokenname");
15330 292 : i_dictname = PQfnumber(res, "dictname");
15331 :
15332 6110 : for (i = 0; i < ntups; i++)
15333 : {
15334 5818 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15335 5818 : char *dictname = PQgetvalue(res, i, i_dictname);
15336 :
15337 5818 : if (i == 0 ||
15338 5526 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15339 : {
15340 : /* starting a new token type, so start a new command */
15341 5548 : if (i > 0)
15342 5256 : appendPQExpBufferStr(q, ";\n");
15343 5548 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15344 5548 : fmtQualifiedDumpable(cfginfo));
15345 : /* tokenname needs quoting, dictname does NOT */
15346 5548 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15347 : fmtId(tokenname), dictname);
15348 : }
15349 : else
15350 270 : appendPQExpBuffer(q, ", %s", dictname);
15351 : }
15352 :
15353 292 : if (ntups > 0)
15354 292 : appendPQExpBufferStr(q, ";\n");
15355 :
15356 292 : PQclear(res);
15357 :
15358 292 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15359 292 : fmtQualifiedDumpable(cfginfo));
15360 :
15361 292 : if (dopt->binary_upgrade)
15362 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15363 : "TEXT SEARCH CONFIGURATION", qcfgname,
15364 10 : cfginfo->dobj.namespace->dobj.name);
15365 :
15366 292 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15367 292 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15368 292 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15369 : .namespace = cfginfo->dobj.namespace->dobj.name,
15370 : .owner = cfginfo->rolname,
15371 : .description = "TEXT SEARCH CONFIGURATION",
15372 : .section = SECTION_PRE_DATA,
15373 : .createStmt = q->data,
15374 : .dropStmt = delq->data));
15375 :
15376 : /* Dump Configuration Comments */
15377 292 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15378 252 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15379 252 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15380 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15381 :
15382 292 : destroyPQExpBuffer(q);
15383 292 : destroyPQExpBuffer(delq);
15384 292 : destroyPQExpBuffer(query);
15385 292 : free(qcfgname);
15386 : }
15387 :
15388 : /*
15389 : * dumpForeignDataWrapper
15390 : * write out a single foreign-data wrapper definition
15391 : */
15392 : static void
15393 112 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15394 : {
15395 112 : DumpOptions *dopt = fout->dopt;
15396 : PQExpBuffer q;
15397 : PQExpBuffer delq;
15398 : char *qfdwname;
15399 :
15400 : /* Do nothing if not dumping schema */
15401 112 : if (!dopt->dumpSchema)
15402 14 : return;
15403 :
15404 98 : q = createPQExpBuffer();
15405 98 : delq = createPQExpBuffer();
15406 :
15407 98 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15408 :
15409 98 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15410 : qfdwname);
15411 :
15412 98 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15413 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15414 :
15415 98 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15416 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15417 :
15418 98 : if (strlen(fdwinfo->fdwoptions) > 0)
15419 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15420 :
15421 98 : appendPQExpBufferStr(q, ";\n");
15422 :
15423 98 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15424 : qfdwname);
15425 :
15426 98 : if (dopt->binary_upgrade)
15427 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15428 : "FOREIGN DATA WRAPPER", qfdwname,
15429 : NULL);
15430 :
15431 98 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15432 98 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15433 98 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15434 : .owner = fdwinfo->rolname,
15435 : .description = "FOREIGN DATA WRAPPER",
15436 : .section = SECTION_PRE_DATA,
15437 : .createStmt = q->data,
15438 : .dropStmt = delq->data));
15439 :
15440 : /* Dump Foreign Data Wrapper Comments */
15441 98 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15442 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
15443 : NULL, fdwinfo->rolname,
15444 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
15445 :
15446 : /* Handle the ACL */
15447 98 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
15448 70 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15449 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15450 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15451 :
15452 98 : free(qfdwname);
15453 :
15454 98 : destroyPQExpBuffer(q);
15455 98 : destroyPQExpBuffer(delq);
15456 : }
15457 :
15458 : /*
15459 : * dumpForeignServer
15460 : * write out a foreign server definition
15461 : */
15462 : static void
15463 120 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15464 : {
15465 120 : DumpOptions *dopt = fout->dopt;
15466 : PQExpBuffer q;
15467 : PQExpBuffer delq;
15468 : PQExpBuffer query;
15469 : PGresult *res;
15470 : char *qsrvname;
15471 : char *fdwname;
15472 :
15473 : /* Do nothing if not dumping schema */
15474 120 : if (!dopt->dumpSchema)
15475 18 : return;
15476 :
15477 102 : q = createPQExpBuffer();
15478 102 : delq = createPQExpBuffer();
15479 102 : query = createPQExpBuffer();
15480 :
15481 102 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15482 :
15483 : /* look up the foreign-data wrapper */
15484 102 : appendPQExpBuffer(query, "SELECT fdwname "
15485 : "FROM pg_foreign_data_wrapper w "
15486 : "WHERE w.oid = '%u'",
15487 : srvinfo->srvfdw);
15488 102 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15489 102 : fdwname = PQgetvalue(res, 0, 0);
15490 :
15491 102 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15492 102 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15493 : {
15494 0 : appendPQExpBufferStr(q, " TYPE ");
15495 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15496 : }
15497 102 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15498 : {
15499 0 : appendPQExpBufferStr(q, " VERSION ");
15500 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15501 : }
15502 :
15503 102 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15504 102 : appendPQExpBufferStr(q, fmtId(fdwname));
15505 :
15506 102 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15507 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15508 :
15509 102 : appendPQExpBufferStr(q, ";\n");
15510 :
15511 102 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15512 : qsrvname);
15513 :
15514 102 : if (dopt->binary_upgrade)
15515 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15516 : "SERVER", qsrvname, NULL);
15517 :
15518 102 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15519 102 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15520 102 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15521 : .owner = srvinfo->rolname,
15522 : .description = "SERVER",
15523 : .section = SECTION_PRE_DATA,
15524 : .createStmt = q->data,
15525 : .dropStmt = delq->data));
15526 :
15527 : /* Dump Foreign Server Comments */
15528 102 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15529 0 : dumpComment(fout, "SERVER", qsrvname,
15530 : NULL, srvinfo->rolname,
15531 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15532 :
15533 : /* Handle the ACL */
15534 102 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15535 70 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15536 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15537 : NULL, srvinfo->rolname, &srvinfo->dacl);
15538 :
15539 : /* Dump user mappings */
15540 102 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15541 102 : dumpUserMappings(fout,
15542 102 : srvinfo->dobj.name, NULL,
15543 : srvinfo->rolname,
15544 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15545 :
15546 102 : PQclear(res);
15547 :
15548 102 : free(qsrvname);
15549 :
15550 102 : destroyPQExpBuffer(q);
15551 102 : destroyPQExpBuffer(delq);
15552 102 : destroyPQExpBuffer(query);
15553 : }
15554 :
15555 : /*
15556 : * dumpUserMappings
15557 : *
15558 : * This routine is used to dump any user mappings associated with the
15559 : * server handed to this routine. Should be called after ArchiveEntry()
15560 : * for the server.
15561 : */
15562 : static void
15563 102 : dumpUserMappings(Archive *fout,
15564 : const char *servername, const char *namespace,
15565 : const char *owner,
15566 : CatalogId catalogId, DumpId dumpId)
15567 : {
15568 : PQExpBuffer q;
15569 : PQExpBuffer delq;
15570 : PQExpBuffer query;
15571 : PQExpBuffer tag;
15572 : PGresult *res;
15573 : int ntups;
15574 : int i_usename;
15575 : int i_umoptions;
15576 : int i;
15577 :
15578 102 : q = createPQExpBuffer();
15579 102 : tag = createPQExpBuffer();
15580 102 : delq = createPQExpBuffer();
15581 102 : query = createPQExpBuffer();
15582 :
15583 : /*
15584 : * We read from the publicly accessible view pg_user_mappings, so as not
15585 : * to fail if run by a non-superuser. Note that the view will show
15586 : * umoptions as null if the user hasn't got privileges for the associated
15587 : * server; this means that pg_dump will dump such a mapping, but with no
15588 : * OPTIONS clause. A possible alternative is to skip such mappings
15589 : * altogether, but it's not clear that that's an improvement.
15590 : */
15591 102 : appendPQExpBuffer(query,
15592 : "SELECT usename, "
15593 : "array_to_string(ARRAY("
15594 : "SELECT quote_ident(option_name) || ' ' || "
15595 : "quote_literal(option_value) "
15596 : "FROM pg_options_to_table(umoptions) "
15597 : "ORDER BY option_name"
15598 : "), E',\n ') AS umoptions "
15599 : "FROM pg_user_mappings "
15600 : "WHERE srvid = '%u' "
15601 : "ORDER BY usename",
15602 : catalogId.oid);
15603 :
15604 102 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15605 :
15606 102 : ntups = PQntuples(res);
15607 102 : i_usename = PQfnumber(res, "usename");
15608 102 : i_umoptions = PQfnumber(res, "umoptions");
15609 :
15610 172 : for (i = 0; i < ntups; i++)
15611 : {
15612 : char *usename;
15613 : char *umoptions;
15614 :
15615 70 : usename = PQgetvalue(res, i, i_usename);
15616 70 : umoptions = PQgetvalue(res, i, i_umoptions);
15617 :
15618 70 : resetPQExpBuffer(q);
15619 70 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15620 70 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15621 :
15622 70 : if (umoptions && strlen(umoptions) > 0)
15623 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15624 :
15625 70 : appendPQExpBufferStr(q, ";\n");
15626 :
15627 70 : resetPQExpBuffer(delq);
15628 70 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15629 70 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15630 :
15631 70 : resetPQExpBuffer(tag);
15632 70 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15633 : usename, servername);
15634 :
15635 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15636 70 : ARCHIVE_OPTS(.tag = tag->data,
15637 : .namespace = namespace,
15638 : .owner = owner,
15639 : .description = "USER MAPPING",
15640 : .section = SECTION_PRE_DATA,
15641 : .createStmt = q->data,
15642 : .dropStmt = delq->data));
15643 : }
15644 :
15645 102 : PQclear(res);
15646 :
15647 102 : destroyPQExpBuffer(query);
15648 102 : destroyPQExpBuffer(delq);
15649 102 : destroyPQExpBuffer(tag);
15650 102 : destroyPQExpBuffer(q);
15651 102 : }
15652 :
15653 : /*
15654 : * Write out default privileges information
15655 : */
15656 : static void
15657 332 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15658 : {
15659 332 : DumpOptions *dopt = fout->dopt;
15660 : PQExpBuffer q;
15661 : PQExpBuffer tag;
15662 : const char *type;
15663 :
15664 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
15665 332 : if (!dopt->dumpSchema || dopt->aclsSkip)
15666 56 : return;
15667 :
15668 276 : q = createPQExpBuffer();
15669 276 : tag = createPQExpBuffer();
15670 :
15671 276 : switch (daclinfo->defaclobjtype)
15672 : {
15673 138 : case DEFACLOBJ_RELATION:
15674 138 : type = "TABLES";
15675 138 : break;
15676 0 : case DEFACLOBJ_SEQUENCE:
15677 0 : type = "SEQUENCES";
15678 0 : break;
15679 138 : case DEFACLOBJ_FUNCTION:
15680 138 : type = "FUNCTIONS";
15681 138 : break;
15682 0 : case DEFACLOBJ_TYPE:
15683 0 : type = "TYPES";
15684 0 : break;
15685 0 : case DEFACLOBJ_NAMESPACE:
15686 0 : type = "SCHEMAS";
15687 0 : break;
15688 0 : default:
15689 : /* shouldn't get here */
15690 0 : pg_fatal("unrecognized object type in default privileges: %d",
15691 : (int) daclinfo->defaclobjtype);
15692 : type = ""; /* keep compiler quiet */
15693 : }
15694 :
15695 276 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15696 :
15697 : /* build the actual command(s) for this tuple */
15698 276 : if (!buildDefaultACLCommands(type,
15699 276 : daclinfo->dobj.namespace != NULL ?
15700 140 : daclinfo->dobj.namespace->dobj.name : NULL,
15701 276 : daclinfo->dacl.acl,
15702 276 : daclinfo->dacl.acldefault,
15703 : daclinfo->defaclrole,
15704 : fout->remoteVersion,
15705 : q))
15706 0 : pg_fatal("could not parse default ACL list (%s)",
15707 : daclinfo->dacl.acl);
15708 :
15709 276 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15710 276 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15711 276 : ARCHIVE_OPTS(.tag = tag->data,
15712 : .namespace = daclinfo->dobj.namespace ?
15713 : daclinfo->dobj.namespace->dobj.name : NULL,
15714 : .owner = daclinfo->defaclrole,
15715 : .description = "DEFAULT ACL",
15716 : .section = SECTION_POST_DATA,
15717 : .createStmt = q->data));
15718 :
15719 276 : destroyPQExpBuffer(tag);
15720 276 : destroyPQExpBuffer(q);
15721 : }
15722 :
15723 : /*----------
15724 : * Write out grant/revoke information
15725 : *
15726 : * 'objDumpId' is the dump ID of the underlying object.
15727 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15728 : * or InvalidDumpId if there is no need for a second dependency.
15729 : * 'type' must be one of
15730 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15731 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15732 : * 'name' is the formatted name of the object. Must be quoted etc. already.
15733 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15734 : * (Currently we assume that subname is only provided for table columns.)
15735 : * 'nspname' is the namespace the object is in (NULL if none).
15736 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
15737 : * to use the default for the object type.
15738 : * 'owner' is the owner, NULL if there is no owner (for languages).
15739 : * 'dacl' is the DumpableAcl struct for the object.
15740 : *
15741 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15742 : * no ACL entry was created.
15743 : *----------
15744 : */
15745 : static DumpId
15746 52620 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15747 : const char *type, const char *name, const char *subname,
15748 : const char *nspname, const char *tag, const char *owner,
15749 : const DumpableAcl *dacl)
15750 : {
15751 52620 : DumpId aclDumpId = InvalidDumpId;
15752 52620 : DumpOptions *dopt = fout->dopt;
15753 52620 : const char *acls = dacl->acl;
15754 52620 : const char *acldefault = dacl->acldefault;
15755 52620 : char privtype = dacl->privtype;
15756 52620 : const char *initprivs = dacl->initprivs;
15757 : const char *baseacls;
15758 : PQExpBuffer sql;
15759 :
15760 : /* Do nothing if ACL dump is not enabled */
15761 52620 : if (dopt->aclsSkip)
15762 636 : return InvalidDumpId;
15763 :
15764 : /* --data-only skips ACLs *except* large object ACLs */
15765 51984 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
15766 0 : return InvalidDumpId;
15767 :
15768 51984 : sql = createPQExpBuffer();
15769 :
15770 : /*
15771 : * In binary upgrade mode, we don't run an extension's script but instead
15772 : * dump out the objects independently and then recreate them. To preserve
15773 : * any initial privileges which were set on extension objects, we need to
15774 : * compute the set of GRANT and REVOKE commands necessary to get from the
15775 : * default privileges of an object to its initial privileges as recorded
15776 : * in pg_init_privs.
15777 : *
15778 : * At restore time, we apply these commands after having called
15779 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
15780 : * copy the results into pg_init_privs. This is how we preserve the
15781 : * contents of that catalog across binary upgrades.
15782 : */
15783 51984 : if (dopt->binary_upgrade && privtype == 'e' &&
15784 26 : initprivs && *initprivs != '\0')
15785 : {
15786 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15787 26 : if (!buildACLCommands(name, subname, nspname, type,
15788 : initprivs, acldefault, owner,
15789 : "", fout->remoteVersion, sql))
15790 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15791 : initprivs, acldefault, name, type);
15792 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15793 : }
15794 :
15795 : /*
15796 : * Now figure the GRANT and REVOKE commands needed to get to the object's
15797 : * actual current ACL, starting from the initprivs if given, else from the
15798 : * object-type-specific default. Also, while buildACLCommands will assume
15799 : * that a NULL/empty acls string means it needn't do anything, what that
15800 : * actually represents is the object-type-specific default; so we need to
15801 : * substitute the acldefault string to get the right results in that case.
15802 : */
15803 51984 : if (initprivs && *initprivs != '\0')
15804 : {
15805 48156 : baseacls = initprivs;
15806 48156 : if (acls == NULL || *acls == '\0')
15807 34 : acls = acldefault;
15808 : }
15809 : else
15810 3828 : baseacls = acldefault;
15811 :
15812 51984 : if (!buildACLCommands(name, subname, nspname, type,
15813 : acls, baseacls, owner,
15814 : "", fout->remoteVersion, sql))
15815 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15816 : acls, baseacls, name, type);
15817 :
15818 51984 : if (sql->len > 0)
15819 : {
15820 3976 : PQExpBuffer tagbuf = createPQExpBuffer();
15821 : DumpId aclDeps[2];
15822 3976 : int nDeps = 0;
15823 :
15824 3976 : if (tag)
15825 0 : appendPQExpBufferStr(tagbuf, tag);
15826 3976 : else if (subname)
15827 2350 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
15828 : else
15829 1626 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
15830 :
15831 3976 : aclDeps[nDeps++] = objDumpId;
15832 3976 : if (altDumpId != InvalidDumpId)
15833 2174 : aclDeps[nDeps++] = altDumpId;
15834 :
15835 3976 : aclDumpId = createDumpId();
15836 :
15837 3976 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
15838 3976 : ARCHIVE_OPTS(.tag = tagbuf->data,
15839 : .namespace = nspname,
15840 : .owner = owner,
15841 : .description = "ACL",
15842 : .section = SECTION_NONE,
15843 : .createStmt = sql->data,
15844 : .deps = aclDeps,
15845 : .nDeps = nDeps));
15846 :
15847 3976 : destroyPQExpBuffer(tagbuf);
15848 : }
15849 :
15850 51984 : destroyPQExpBuffer(sql);
15851 :
15852 51984 : return aclDumpId;
15853 : }
15854 :
15855 : /*
15856 : * dumpSecLabel
15857 : *
15858 : * This routine is used to dump any security labels associated with the
15859 : * object handed to this routine. The routine takes the object type
15860 : * and object name (ready to print, except for schema decoration), plus
15861 : * the namespace and owner of the object (for labeling the ArchiveEntry),
15862 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
15863 : * plus the dump ID for the object (for setting a dependency).
15864 : * If a matching pg_seclabel entry is found, it is dumped.
15865 : *
15866 : * Note: although this routine takes a dumpId for dependency purposes,
15867 : * that purpose is just to mark the dependency in the emitted dump file
15868 : * for possible future use by pg_restore. We do NOT use it for determining
15869 : * ordering of the label in the dump file, because this routine is called
15870 : * after dependency sorting occurs. This routine should be called just after
15871 : * calling ArchiveEntry() for the specified object.
15872 : */
15873 : static void
15874 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
15875 : const char *namespace, const char *owner,
15876 : CatalogId catalogId, int subid, DumpId dumpId)
15877 : {
15878 0 : DumpOptions *dopt = fout->dopt;
15879 : SecLabelItem *labels;
15880 : int nlabels;
15881 : int i;
15882 : PQExpBuffer query;
15883 :
15884 : /* do nothing, if --no-security-labels is supplied */
15885 0 : if (dopt->no_security_labels)
15886 0 : return;
15887 :
15888 : /*
15889 : * Security labels are schema not data ... except large object labels are
15890 : * data
15891 : */
15892 0 : if (strcmp(type, "LARGE OBJECT") != 0)
15893 : {
15894 0 : if (!dopt->dumpSchema)
15895 0 : return;
15896 : }
15897 : else
15898 : {
15899 : /* We do dump large object security labels in binary-upgrade mode */
15900 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
15901 0 : return;
15902 : }
15903 :
15904 : /* Search for security labels associated with catalogId, using table */
15905 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15906 :
15907 0 : query = createPQExpBuffer();
15908 :
15909 0 : for (i = 0; i < nlabels; i++)
15910 : {
15911 : /*
15912 : * Ignore label entries for which the subid doesn't match.
15913 : */
15914 0 : if (labels[i].objsubid != subid)
15915 0 : continue;
15916 :
15917 0 : appendPQExpBuffer(query,
15918 : "SECURITY LABEL FOR %s ON %s ",
15919 0 : fmtId(labels[i].provider), type);
15920 0 : if (namespace && *namespace)
15921 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
15922 0 : appendPQExpBuffer(query, "%s IS ", name);
15923 0 : appendStringLiteralAH(query, labels[i].label, fout);
15924 0 : appendPQExpBufferStr(query, ";\n");
15925 : }
15926 :
15927 0 : if (query->len > 0)
15928 : {
15929 0 : PQExpBuffer tag = createPQExpBuffer();
15930 :
15931 0 : appendPQExpBuffer(tag, "%s %s", type, name);
15932 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15933 0 : ARCHIVE_OPTS(.tag = tag->data,
15934 : .namespace = namespace,
15935 : .owner = owner,
15936 : .description = "SECURITY LABEL",
15937 : .section = SECTION_NONE,
15938 : .createStmt = query->data,
15939 : .deps = &dumpId,
15940 : .nDeps = 1));
15941 0 : destroyPQExpBuffer(tag);
15942 : }
15943 :
15944 0 : destroyPQExpBuffer(query);
15945 : }
15946 :
15947 : /*
15948 : * dumpTableSecLabel
15949 : *
15950 : * As above, but dump security label for both the specified table (or view)
15951 : * and its columns.
15952 : */
15953 : static void
15954 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15955 : {
15956 0 : DumpOptions *dopt = fout->dopt;
15957 : SecLabelItem *labels;
15958 : int nlabels;
15959 : int i;
15960 : PQExpBuffer query;
15961 : PQExpBuffer target;
15962 :
15963 : /* do nothing, if --no-security-labels is supplied */
15964 0 : if (dopt->no_security_labels)
15965 0 : return;
15966 :
15967 : /* SecLabel are SCHEMA not data */
15968 0 : if (!dopt->dumpSchema)
15969 0 : return;
15970 :
15971 : /* Search for comments associated with relation, using table */
15972 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15973 : tbinfo->dobj.catId.oid,
15974 : &labels);
15975 :
15976 : /* If security labels exist, build SECURITY LABEL statements */
15977 0 : if (nlabels <= 0)
15978 0 : return;
15979 :
15980 0 : query = createPQExpBuffer();
15981 0 : target = createPQExpBuffer();
15982 :
15983 0 : for (i = 0; i < nlabels; i++)
15984 : {
15985 : const char *colname;
15986 0 : const char *provider = labels[i].provider;
15987 0 : const char *label = labels[i].label;
15988 0 : int objsubid = labels[i].objsubid;
15989 :
15990 0 : resetPQExpBuffer(target);
15991 0 : if (objsubid == 0)
15992 : {
15993 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15994 0 : fmtQualifiedDumpable(tbinfo));
15995 : }
15996 : else
15997 : {
15998 0 : colname = getAttrName(objsubid, tbinfo);
15999 : /* first fmtXXX result must be consumed before calling again */
16000 0 : appendPQExpBuffer(target, "COLUMN %s",
16001 0 : fmtQualifiedDumpable(tbinfo));
16002 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16003 : }
16004 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16005 : fmtId(provider), target->data);
16006 0 : appendStringLiteralAH(query, label, fout);
16007 0 : appendPQExpBufferStr(query, ";\n");
16008 : }
16009 0 : if (query->len > 0)
16010 : {
16011 0 : resetPQExpBuffer(target);
16012 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16013 0 : fmtId(tbinfo->dobj.name));
16014 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16015 0 : ARCHIVE_OPTS(.tag = target->data,
16016 : .namespace = tbinfo->dobj.namespace->dobj.name,
16017 : .owner = tbinfo->rolname,
16018 : .description = "SECURITY LABEL",
16019 : .section = SECTION_NONE,
16020 : .createStmt = query->data,
16021 : .deps = &(tbinfo->dobj.dumpId),
16022 : .nDeps = 1));
16023 : }
16024 0 : destroyPQExpBuffer(query);
16025 0 : destroyPQExpBuffer(target);
16026 : }
16027 :
16028 : /*
16029 : * findSecLabels
16030 : *
16031 : * Find the security label(s), if any, associated with the given object.
16032 : * All the objsubid values associated with the given classoid/objoid are
16033 : * found with one search.
16034 : */
16035 : static int
16036 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16037 : {
16038 0 : SecLabelItem *middle = NULL;
16039 : SecLabelItem *low;
16040 : SecLabelItem *high;
16041 : int nmatch;
16042 :
16043 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16044 : {
16045 0 : *items = NULL;
16046 0 : return 0;
16047 : }
16048 :
16049 : /*
16050 : * Do binary search to find some item matching the object.
16051 : */
16052 0 : low = &seclabels[0];
16053 0 : high = &seclabels[nseclabels - 1];
16054 0 : while (low <= high)
16055 : {
16056 0 : middle = low + (high - low) / 2;
16057 :
16058 0 : if (classoid < middle->classoid)
16059 0 : high = middle - 1;
16060 0 : else if (classoid > middle->classoid)
16061 0 : low = middle + 1;
16062 0 : else if (objoid < middle->objoid)
16063 0 : high = middle - 1;
16064 0 : else if (objoid > middle->objoid)
16065 0 : low = middle + 1;
16066 : else
16067 0 : break; /* found a match */
16068 : }
16069 :
16070 0 : if (low > high) /* no matches */
16071 : {
16072 0 : *items = NULL;
16073 0 : return 0;
16074 : }
16075 :
16076 : /*
16077 : * Now determine how many items match the object. The search loop
16078 : * invariant still holds: only items between low and high inclusive could
16079 : * match.
16080 : */
16081 0 : nmatch = 1;
16082 0 : while (middle > low)
16083 : {
16084 0 : if (classoid != middle[-1].classoid ||
16085 0 : objoid != middle[-1].objoid)
16086 : break;
16087 0 : middle--;
16088 0 : nmatch++;
16089 : }
16090 :
16091 0 : *items = middle;
16092 :
16093 0 : middle += nmatch;
16094 0 : while (middle <= high)
16095 : {
16096 0 : if (classoid != middle->classoid ||
16097 0 : objoid != middle->objoid)
16098 : break;
16099 0 : middle++;
16100 0 : nmatch++;
16101 : }
16102 :
16103 0 : return nmatch;
16104 : }
16105 :
16106 : /*
16107 : * collectSecLabels
16108 : *
16109 : * Construct a table of all security labels available for database objects;
16110 : * also set the has-seclabel component flag for each relevant object.
16111 : *
16112 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16113 : */
16114 : static void
16115 354 : collectSecLabels(Archive *fout)
16116 : {
16117 : PGresult *res;
16118 : PQExpBuffer query;
16119 : int i_label;
16120 : int i_provider;
16121 : int i_classoid;
16122 : int i_objoid;
16123 : int i_objsubid;
16124 : int ntups;
16125 : int i;
16126 : DumpableObject *dobj;
16127 :
16128 354 : query = createPQExpBuffer();
16129 :
16130 354 : appendPQExpBufferStr(query,
16131 : "SELECT label, provider, classoid, objoid, objsubid "
16132 : "FROM pg_catalog.pg_seclabel "
16133 : "ORDER BY classoid, objoid, objsubid");
16134 :
16135 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16136 :
16137 : /* Construct lookup table containing OIDs in numeric form */
16138 354 : i_label = PQfnumber(res, "label");
16139 354 : i_provider = PQfnumber(res, "provider");
16140 354 : i_classoid = PQfnumber(res, "classoid");
16141 354 : i_objoid = PQfnumber(res, "objoid");
16142 354 : i_objsubid = PQfnumber(res, "objsubid");
16143 :
16144 354 : ntups = PQntuples(res);
16145 :
16146 354 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16147 354 : nseclabels = 0;
16148 354 : dobj = NULL;
16149 :
16150 354 : for (i = 0; i < ntups; i++)
16151 : {
16152 : CatalogId objId;
16153 : int subid;
16154 :
16155 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16156 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16157 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16158 :
16159 : /* We needn't remember labels that don't match any dumpable object */
16160 0 : if (dobj == NULL ||
16161 0 : dobj->catId.tableoid != objId.tableoid ||
16162 0 : dobj->catId.oid != objId.oid)
16163 0 : dobj = findObjectByCatalogId(objId);
16164 0 : if (dobj == NULL)
16165 0 : continue;
16166 :
16167 : /*
16168 : * Labels on columns of composite types are linked to the type's
16169 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16170 : * in the type's own DumpableObject.
16171 : */
16172 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16173 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16174 0 : {
16175 : TypeInfo *cTypeInfo;
16176 :
16177 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16178 0 : if (cTypeInfo)
16179 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16180 : }
16181 : else
16182 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16183 :
16184 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16185 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16186 0 : seclabels[nseclabels].classoid = objId.tableoid;
16187 0 : seclabels[nseclabels].objoid = objId.oid;
16188 0 : seclabels[nseclabels].objsubid = subid;
16189 0 : nseclabels++;
16190 : }
16191 :
16192 354 : PQclear(res);
16193 354 : destroyPQExpBuffer(query);
16194 354 : }
16195 :
16196 : /*
16197 : * dumpTable
16198 : * write out to fout the declarations (not data) of a user-defined table
16199 : */
16200 : static void
16201 57818 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16202 : {
16203 57818 : DumpOptions *dopt = fout->dopt;
16204 57818 : DumpId tableAclDumpId = InvalidDumpId;
16205 : char *namecopy;
16206 :
16207 : /* Do nothing if not dumping schema */
16208 57818 : if (!dopt->dumpSchema)
16209 2892 : return;
16210 :
16211 54926 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16212 : {
16213 12872 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16214 774 : dumpSequence(fout, tbinfo);
16215 : else
16216 12098 : dumpTableSchema(fout, tbinfo);
16217 : }
16218 :
16219 : /* Handle the ACL here */
16220 54926 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16221 54926 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16222 : {
16223 43528 : const char *objtype =
16224 43528 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16225 :
16226 : tableAclDumpId =
16227 43528 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16228 : objtype, namecopy, NULL,
16229 43528 : tbinfo->dobj.namespace->dobj.name,
16230 : NULL, tbinfo->rolname, &tbinfo->dacl);
16231 : }
16232 :
16233 : /*
16234 : * Handle column ACLs, if any. Note: we pull these with a separate query
16235 : * rather than trying to fetch them during getTableAttrs, so that we won't
16236 : * miss ACLs on system columns. Doing it this way also allows us to dump
16237 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16238 : */
16239 54926 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16240 : {
16241 568 : PQExpBuffer query = createPQExpBuffer();
16242 : PGresult *res;
16243 : int i;
16244 :
16245 568 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16246 : {
16247 : /* Set up query for column ACLs */
16248 302 : appendPQExpBufferStr(query,
16249 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16250 :
16251 302 : if (fout->remoteVersion >= 90600)
16252 : {
16253 : /*
16254 : * In principle we should call acldefault('c', relowner) to
16255 : * get the default ACL for a column. However, we don't
16256 : * currently store the numeric OID of the relowner in
16257 : * TableInfo. We could convert the owner name using regrole,
16258 : * but that creates a risk of failure due to concurrent role
16259 : * renames. Given that the default ACL for columns is empty
16260 : * and is likely to stay that way, it's not worth extra cycles
16261 : * and risk to avoid hard-wiring that knowledge here.
16262 : */
16263 302 : appendPQExpBufferStr(query,
16264 : "SELECT at.attname, "
16265 : "at.attacl, "
16266 : "'{}' AS acldefault, "
16267 : "pip.privtype, pip.initprivs "
16268 : "FROM pg_catalog.pg_attribute at "
16269 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16270 : "(at.attrelid = pip.objoid "
16271 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16272 : "AND at.attnum = pip.objsubid) "
16273 : "WHERE at.attrelid = $1 AND "
16274 : "NOT at.attisdropped "
16275 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16276 : "ORDER BY at.attnum");
16277 : }
16278 : else
16279 : {
16280 0 : appendPQExpBufferStr(query,
16281 : "SELECT attname, attacl, '{}' AS acldefault, "
16282 : "NULL AS privtype, NULL AS initprivs "
16283 : "FROM pg_catalog.pg_attribute "
16284 : "WHERE attrelid = $1 AND NOT attisdropped "
16285 : "AND attacl IS NOT NULL "
16286 : "ORDER BY attnum");
16287 : }
16288 :
16289 302 : ExecuteSqlStatement(fout, query->data);
16290 :
16291 302 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16292 : }
16293 :
16294 568 : printfPQExpBuffer(query,
16295 : "EXECUTE getColumnACLs('%u')",
16296 : tbinfo->dobj.catId.oid);
16297 :
16298 568 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16299 :
16300 7950 : for (i = 0; i < PQntuples(res); i++)
16301 : {
16302 7382 : char *attname = PQgetvalue(res, i, 0);
16303 7382 : char *attacl = PQgetvalue(res, i, 1);
16304 7382 : char *acldefault = PQgetvalue(res, i, 2);
16305 7382 : char privtype = *(PQgetvalue(res, i, 3));
16306 7382 : char *initprivs = PQgetvalue(res, i, 4);
16307 : DumpableAcl coldacl;
16308 : char *attnamecopy;
16309 :
16310 7382 : coldacl.acl = attacl;
16311 7382 : coldacl.acldefault = acldefault;
16312 7382 : coldacl.privtype = privtype;
16313 7382 : coldacl.initprivs = initprivs;
16314 7382 : attnamecopy = pg_strdup(fmtId(attname));
16315 :
16316 : /*
16317 : * Column's GRANT type is always TABLE. Each column ACL depends
16318 : * on the table-level ACL, since we can restore column ACLs in
16319 : * parallel but the table-level ACL has to be done first.
16320 : */
16321 7382 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16322 : "TABLE", namecopy, attnamecopy,
16323 7382 : tbinfo->dobj.namespace->dobj.name,
16324 : NULL, tbinfo->rolname, &coldacl);
16325 7382 : free(attnamecopy);
16326 : }
16327 568 : PQclear(res);
16328 568 : destroyPQExpBuffer(query);
16329 : }
16330 :
16331 54926 : free(namecopy);
16332 : }
16333 :
16334 : /*
16335 : * Create the AS clause for a view or materialized view. The semicolon is
16336 : * stripped because a materialized view must add a WITH NO DATA clause.
16337 : *
16338 : * This returns a new buffer which must be freed by the caller.
16339 : */
16340 : static PQExpBuffer
16341 1840 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16342 : {
16343 1840 : PQExpBuffer query = createPQExpBuffer();
16344 1840 : PQExpBuffer result = createPQExpBuffer();
16345 : PGresult *res;
16346 : int len;
16347 :
16348 : /* Fetch the view definition */
16349 1840 : appendPQExpBuffer(query,
16350 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16351 : tbinfo->dobj.catId.oid);
16352 :
16353 1840 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16354 :
16355 1840 : if (PQntuples(res) != 1)
16356 : {
16357 0 : if (PQntuples(res) < 1)
16358 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16359 : tbinfo->dobj.name);
16360 : else
16361 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16362 : tbinfo->dobj.name);
16363 : }
16364 :
16365 1840 : len = PQgetlength(res, 0, 0);
16366 :
16367 1840 : if (len == 0)
16368 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16369 : tbinfo->dobj.name);
16370 :
16371 : /* Strip off the trailing semicolon so that other things may follow. */
16372 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16373 1840 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16374 :
16375 1840 : PQclear(res);
16376 1840 : destroyPQExpBuffer(query);
16377 :
16378 1840 : return result;
16379 : }
16380 :
16381 : /*
16382 : * Create a dummy AS clause for a view. This is used when the real view
16383 : * definition has to be postponed because of circular dependencies.
16384 : * We must duplicate the view's external properties -- column names and types
16385 : * (including collation) -- so that it works for subsequent references.
16386 : *
16387 : * This returns a new buffer which must be freed by the caller.
16388 : */
16389 : static PQExpBuffer
16390 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16391 : {
16392 40 : PQExpBuffer result = createPQExpBuffer();
16393 : int j;
16394 :
16395 40 : appendPQExpBufferStr(result, "SELECT");
16396 :
16397 80 : for (j = 0; j < tbinfo->numatts; j++)
16398 : {
16399 40 : if (j > 0)
16400 20 : appendPQExpBufferChar(result, ',');
16401 40 : appendPQExpBufferStr(result, "\n ");
16402 :
16403 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16404 :
16405 : /*
16406 : * Must add collation if not default for the type, because CREATE OR
16407 : * REPLACE VIEW won't change it
16408 : */
16409 40 : if (OidIsValid(tbinfo->attcollation[j]))
16410 : {
16411 : CollInfo *coll;
16412 :
16413 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16414 0 : if (coll)
16415 0 : appendPQExpBuffer(result, " COLLATE %s",
16416 0 : fmtQualifiedDumpable(coll));
16417 : }
16418 :
16419 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16420 : }
16421 :
16422 40 : return result;
16423 : }
16424 :
16425 : /*
16426 : * dumpTableSchema
16427 : * write the declaration (not data) of one user-defined table or view
16428 : */
16429 : static void
16430 12098 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16431 : {
16432 12098 : DumpOptions *dopt = fout->dopt;
16433 12098 : PQExpBuffer q = createPQExpBuffer();
16434 12098 : PQExpBuffer delq = createPQExpBuffer();
16435 12098 : PQExpBuffer extra = createPQExpBuffer();
16436 : char *qrelname;
16437 : char *qualrelname;
16438 : int numParents;
16439 : TableInfo **parents;
16440 : int actual_atts; /* number of attrs in this CREATE statement */
16441 : const char *reltypename;
16442 : char *storage;
16443 : int j,
16444 : k;
16445 :
16446 : /* We had better have loaded per-column details about this table */
16447 : Assert(tbinfo->interesting);
16448 :
16449 12098 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16450 12098 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16451 :
16452 12098 : if (tbinfo->hasoids)
16453 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16454 : qrelname);
16455 :
16456 12098 : if (dopt->binary_upgrade)
16457 1646 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16458 :
16459 : /* Is it a table or a view? */
16460 12098 : if (tbinfo->relkind == RELKIND_VIEW)
16461 : {
16462 : PQExpBuffer result;
16463 :
16464 : /*
16465 : * Note: keep this code in sync with the is_view case in dumpRule()
16466 : */
16467 :
16468 1040 : reltypename = "VIEW";
16469 :
16470 1040 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16471 :
16472 1040 : if (dopt->binary_upgrade)
16473 102 : binary_upgrade_set_pg_class_oids(fout, q,
16474 : tbinfo->dobj.catId.oid);
16475 :
16476 1040 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16477 :
16478 1040 : if (tbinfo->dummy_view)
16479 20 : result = createDummyViewAsClause(fout, tbinfo);
16480 : else
16481 : {
16482 1020 : if (nonemptyReloptions(tbinfo->reloptions))
16483 : {
16484 130 : appendPQExpBufferStr(q, " WITH (");
16485 130 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16486 130 : appendPQExpBufferChar(q, ')');
16487 : }
16488 1020 : result = createViewAsClause(fout, tbinfo);
16489 : }
16490 1040 : appendPQExpBuffer(q, " AS\n%s", result->data);
16491 1040 : destroyPQExpBuffer(result);
16492 :
16493 1040 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16494 72 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16495 1040 : appendPQExpBufferStr(q, ";\n");
16496 : }
16497 : else
16498 : {
16499 11058 : char *partkeydef = NULL;
16500 11058 : char *ftoptions = NULL;
16501 11058 : char *srvname = NULL;
16502 11058 : const char *foreign = "";
16503 :
16504 : /*
16505 : * Set reltypename, and collect any relkind-specific data that we
16506 : * didn't fetch during getTables().
16507 : */
16508 11058 : switch (tbinfo->relkind)
16509 : {
16510 1074 : case RELKIND_PARTITIONED_TABLE:
16511 : {
16512 1074 : PQExpBuffer query = createPQExpBuffer();
16513 : PGresult *res;
16514 :
16515 1074 : reltypename = "TABLE";
16516 :
16517 : /* retrieve partition key definition */
16518 1074 : appendPQExpBuffer(query,
16519 : "SELECT pg_get_partkeydef('%u')",
16520 : tbinfo->dobj.catId.oid);
16521 1074 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16522 1074 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16523 1074 : PQclear(res);
16524 1074 : destroyPQExpBuffer(query);
16525 1074 : break;
16526 : }
16527 76 : case RELKIND_FOREIGN_TABLE:
16528 : {
16529 76 : PQExpBuffer query = createPQExpBuffer();
16530 : PGresult *res;
16531 : int i_srvname;
16532 : int i_ftoptions;
16533 :
16534 76 : reltypename = "FOREIGN TABLE";
16535 :
16536 : /* retrieve name of foreign server and generic options */
16537 76 : appendPQExpBuffer(query,
16538 : "SELECT fs.srvname, "
16539 : "pg_catalog.array_to_string(ARRAY("
16540 : "SELECT pg_catalog.quote_ident(option_name) || "
16541 : "' ' || pg_catalog.quote_literal(option_value) "
16542 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16543 : "ORDER BY option_name"
16544 : "), E',\n ') AS ftoptions "
16545 : "FROM pg_catalog.pg_foreign_table ft "
16546 : "JOIN pg_catalog.pg_foreign_server fs "
16547 : "ON (fs.oid = ft.ftserver) "
16548 : "WHERE ft.ftrelid = '%u'",
16549 : tbinfo->dobj.catId.oid);
16550 76 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16551 76 : i_srvname = PQfnumber(res, "srvname");
16552 76 : i_ftoptions = PQfnumber(res, "ftoptions");
16553 76 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16554 76 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16555 76 : PQclear(res);
16556 76 : destroyPQExpBuffer(query);
16557 :
16558 76 : foreign = "FOREIGN ";
16559 76 : break;
16560 : }
16561 800 : case RELKIND_MATVIEW:
16562 800 : reltypename = "MATERIALIZED VIEW";
16563 800 : break;
16564 9108 : default:
16565 9108 : reltypename = "TABLE";
16566 9108 : break;
16567 : }
16568 :
16569 11058 : numParents = tbinfo->numParents;
16570 11058 : parents = tbinfo->parents;
16571 :
16572 11058 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16573 :
16574 11058 : if (dopt->binary_upgrade)
16575 1544 : binary_upgrade_set_pg_class_oids(fout, q,
16576 : tbinfo->dobj.catId.oid);
16577 :
16578 : /*
16579 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16580 : * ignore it when dumping if it was set in this case.
16581 : */
16582 11058 : appendPQExpBuffer(q, "CREATE %s%s %s",
16583 11058 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16584 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16585 : "UNLOGGED " : "",
16586 : reltypename,
16587 : qualrelname);
16588 :
16589 : /*
16590 : * Attach to type, if reloftype; except in case of a binary upgrade,
16591 : * we dump the table normally and attach it to the type afterward.
16592 : */
16593 11058 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16594 48 : appendPQExpBuffer(q, " OF %s",
16595 : getFormattedTypeName(fout, tbinfo->reloftype,
16596 : zeroIsError));
16597 :
16598 11058 : if (tbinfo->relkind != RELKIND_MATVIEW)
16599 : {
16600 : /* Dump the attributes */
16601 10258 : actual_atts = 0;
16602 49602 : for (j = 0; j < tbinfo->numatts; j++)
16603 : {
16604 : /*
16605 : * Normally, dump if it's locally defined in this table, and
16606 : * not dropped. But for binary upgrade, we'll dump all the
16607 : * columns, and then fix up the dropped and nonlocal cases
16608 : * below.
16609 : */
16610 39344 : if (shouldPrintColumn(dopt, tbinfo, j))
16611 : {
16612 : bool print_default;
16613 : bool print_notnull;
16614 :
16615 : /*
16616 : * Default value --- suppress if to be printed separately
16617 : * or not at all.
16618 : */
16619 77150 : print_default = (tbinfo->attrdefs[j] != NULL &&
16620 39554 : tbinfo->attrdefs[j]->dobj.dump &&
16621 2060 : !tbinfo->attrdefs[j]->separate);
16622 :
16623 : /*
16624 : * Not Null constraint --- print it if it is locally
16625 : * defined, or if binary upgrade. (In the latter case, we
16626 : * reset conislocal below.)
16627 : */
16628 41864 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16629 4370 : (tbinfo->notnull_islocal[j] ||
16630 1206 : dopt->binary_upgrade ||
16631 1042 : tbinfo->ispartition));
16632 :
16633 : /*
16634 : * Skip column if fully defined by reloftype, except in
16635 : * binary upgrade
16636 : */
16637 37494 : if (OidIsValid(tbinfo->reloftype) &&
16638 100 : !print_default && !print_notnull &&
16639 60 : !dopt->binary_upgrade)
16640 48 : continue;
16641 :
16642 : /* Format properly if not first attr */
16643 37446 : if (actual_atts == 0)
16644 9738 : appendPQExpBufferStr(q, " (");
16645 : else
16646 27708 : appendPQExpBufferChar(q, ',');
16647 37446 : appendPQExpBufferStr(q, "\n ");
16648 37446 : actual_atts++;
16649 :
16650 : /* Attribute name */
16651 37446 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16652 :
16653 37446 : if (tbinfo->attisdropped[j])
16654 : {
16655 : /*
16656 : * ALTER TABLE DROP COLUMN clears
16657 : * pg_attribute.atttypid, so we will not have gotten a
16658 : * valid type name; insert INTEGER as a stopgap. We'll
16659 : * clean things up later.
16660 : */
16661 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16662 : /* and skip to the next column */
16663 168 : continue;
16664 : }
16665 :
16666 : /*
16667 : * Attribute type; print it except when creating a typed
16668 : * table ('OF type_name'), but in binary-upgrade mode,
16669 : * print it in that case too.
16670 : */
16671 37278 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16672 : {
16673 37246 : appendPQExpBuffer(q, " %s",
16674 37246 : tbinfo->atttypnames[j]);
16675 : }
16676 :
16677 37278 : if (print_default)
16678 : {
16679 1784 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16680 568 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16681 568 : tbinfo->attrdefs[j]->adef_expr);
16682 1216 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
16683 444 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
16684 444 : tbinfo->attrdefs[j]->adef_expr);
16685 : else
16686 772 : appendPQExpBuffer(q, " DEFAULT %s",
16687 772 : tbinfo->attrdefs[j]->adef_expr);
16688 : }
16689 :
16690 41648 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16691 4370 : (tbinfo->notnull_islocal[j] ||
16692 1206 : dopt->binary_upgrade ||
16693 1042 : tbinfo->ispartition));
16694 :
16695 37278 : if (print_notnull)
16696 : {
16697 4300 : if (tbinfo->notnull_constrs[j][0] == '\0')
16698 3092 : appendPQExpBufferStr(q, " NOT NULL");
16699 : else
16700 1208 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16701 1208 : fmtId(tbinfo->notnull_constrs[j]));
16702 :
16703 4300 : if (tbinfo->notnull_noinh[j])
16704 0 : appendPQExpBufferStr(q, " NO INHERIT");
16705 : }
16706 :
16707 : /* Add collation if not default for the type */
16708 37278 : if (OidIsValid(tbinfo->attcollation[j]))
16709 : {
16710 : CollInfo *coll;
16711 :
16712 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
16713 394 : if (coll)
16714 394 : appendPQExpBuffer(q, " COLLATE %s",
16715 394 : fmtQualifiedDumpable(coll));
16716 : }
16717 : }
16718 :
16719 : /*
16720 : * On the other hand, if we choose not to print a column
16721 : * (likely because it is created by inheritance), but the
16722 : * column has a locally-defined not-null constraint, we need
16723 : * to dump the constraint as a standalone object.
16724 : *
16725 : * This syntax isn't SQL-conforming, but if you wanted
16726 : * standard output you wouldn't be creating non-standard
16727 : * objects to begin with.
16728 : */
16729 39128 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
16730 1850 : !tbinfo->attisdropped[j] &&
16731 1116 : tbinfo->notnull_constrs[j] != NULL &&
16732 206 : tbinfo->notnull_islocal[j])
16733 : {
16734 : /* Format properly if not first attr */
16735 32 : if (actual_atts == 0)
16736 24 : appendPQExpBufferStr(q, " (");
16737 : else
16738 8 : appendPQExpBufferChar(q, ',');
16739 32 : appendPQExpBufferStr(q, "\n ");
16740 32 : actual_atts++;
16741 :
16742 32 : if (tbinfo->notnull_constrs[j][0] == '\0')
16743 8 : appendPQExpBuffer(q, "NOT NULL %s",
16744 8 : fmtId(tbinfo->attnames[j]));
16745 : else
16746 48 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
16747 24 : tbinfo->notnull_constrs[j],
16748 24 : fmtId(tbinfo->attnames[j]));
16749 : }
16750 : }
16751 :
16752 : /*
16753 : * Add non-inherited CHECK constraints, if any.
16754 : *
16755 : * For partitions, we need to include check constraints even if
16756 : * they're not defined locally, because the ALTER TABLE ATTACH
16757 : * PARTITION that we'll emit later expects the constraint to be
16758 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
16759 : */
16760 11504 : for (j = 0; j < tbinfo->ncheck; j++)
16761 : {
16762 1246 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16763 :
16764 1246 : if (constr->separate ||
16765 1106 : (!constr->conislocal && !tbinfo->ispartition))
16766 222 : continue;
16767 :
16768 1024 : if (actual_atts == 0)
16769 32 : appendPQExpBufferStr(q, " (\n ");
16770 : else
16771 992 : appendPQExpBufferStr(q, ",\n ");
16772 :
16773 1024 : appendPQExpBuffer(q, "CONSTRAINT %s ",
16774 1024 : fmtId(constr->dobj.name));
16775 1024 : appendPQExpBufferStr(q, constr->condef);
16776 :
16777 1024 : actual_atts++;
16778 : }
16779 :
16780 10258 : if (actual_atts)
16781 9794 : appendPQExpBufferStr(q, "\n)");
16782 464 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16783 : {
16784 : /*
16785 : * No attributes? we must have a parenthesized attribute list,
16786 : * even though empty, when not using the OF TYPE syntax.
16787 : */
16788 440 : appendPQExpBufferStr(q, " (\n)");
16789 : }
16790 :
16791 : /*
16792 : * Emit the INHERITS clause (not for partitions), except in
16793 : * binary-upgrade mode.
16794 : */
16795 10258 : if (numParents > 0 && !tbinfo->ispartition &&
16796 750 : !dopt->binary_upgrade)
16797 : {
16798 640 : appendPQExpBufferStr(q, "\nINHERITS (");
16799 1344 : for (k = 0; k < numParents; k++)
16800 : {
16801 704 : TableInfo *parentRel = parents[k];
16802 :
16803 704 : if (k > 0)
16804 64 : appendPQExpBufferStr(q, ", ");
16805 704 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16806 : }
16807 640 : appendPQExpBufferChar(q, ')');
16808 : }
16809 :
16810 10258 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16811 1074 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16812 :
16813 10258 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16814 76 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16815 : }
16816 :
16817 21824 : if (nonemptyReloptions(tbinfo->reloptions) ||
16818 10766 : nonemptyReloptions(tbinfo->toast_reloptions))
16819 : {
16820 292 : bool addcomma = false;
16821 :
16822 292 : appendPQExpBufferStr(q, "\nWITH (");
16823 292 : if (nonemptyReloptions(tbinfo->reloptions))
16824 : {
16825 292 : addcomma = true;
16826 292 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16827 : }
16828 292 : if (nonemptyReloptions(tbinfo->toast_reloptions))
16829 : {
16830 10 : if (addcomma)
16831 10 : appendPQExpBufferStr(q, ", ");
16832 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16833 : fout);
16834 : }
16835 292 : appendPQExpBufferChar(q, ')');
16836 : }
16837 :
16838 : /* Dump generic options if any */
16839 11058 : if (ftoptions && ftoptions[0])
16840 72 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16841 :
16842 : /*
16843 : * For materialized views, create the AS clause just like a view. At
16844 : * this point, we always mark the view as not populated.
16845 : */
16846 11058 : if (tbinfo->relkind == RELKIND_MATVIEW)
16847 : {
16848 : PQExpBuffer result;
16849 :
16850 800 : result = createViewAsClause(fout, tbinfo);
16851 800 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16852 : result->data);
16853 800 : destroyPQExpBuffer(result);
16854 : }
16855 : else
16856 10258 : appendPQExpBufferStr(q, ";\n");
16857 :
16858 : /* Materialized views can depend on extensions */
16859 11058 : if (tbinfo->relkind == RELKIND_MATVIEW)
16860 800 : append_depends_on_extension(fout, q, &tbinfo->dobj,
16861 : "pg_catalog.pg_class",
16862 : "MATERIALIZED VIEW",
16863 : qualrelname);
16864 :
16865 : /*
16866 : * in binary upgrade mode, update the catalog with any missing values
16867 : * that might be present.
16868 : */
16869 11058 : if (dopt->binary_upgrade)
16870 : {
16871 7702 : for (j = 0; j < tbinfo->numatts; j++)
16872 : {
16873 6158 : if (tbinfo->attmissingval[j][0] != '\0')
16874 : {
16875 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
16876 4 : appendPQExpBufferStr(q,
16877 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16878 4 : appendStringLiteralAH(q, qualrelname, fout);
16879 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16880 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16881 4 : appendPQExpBufferChar(q, ',');
16882 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16883 4 : appendPQExpBufferStr(q, ");\n\n");
16884 : }
16885 : }
16886 : }
16887 :
16888 : /*
16889 : * To create binary-compatible heap files, we have to ensure the same
16890 : * physical column order, including dropped columns, as in the
16891 : * original. Therefore, we create dropped columns above and drop them
16892 : * here, also updating their attlen/attalign values so that the
16893 : * dropped column can be skipped properly. (We do not bother with
16894 : * restoring the original attbyval setting.) Also, inheritance
16895 : * relationships are set up by doing ALTER TABLE INHERIT rather than
16896 : * using an INHERITS clause --- the latter would possibly mess up the
16897 : * column order. That also means we have to take care about setting
16898 : * attislocal correctly, plus fix up any inherited CHECK constraints.
16899 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
16900 : *
16901 : * We process foreign and partitioned tables here, even though they
16902 : * lack heap storage, because they can participate in inheritance
16903 : * relationships and we want this stuff to be consistent across the
16904 : * inheritance tree. We can exclude indexes, toast tables, sequences
16905 : * and matviews, even though they have storage, because we don't
16906 : * support altering or dropping columns in them, nor can they be part
16907 : * of inheritance trees.
16908 : */
16909 11058 : if (dopt->binary_upgrade &&
16910 1544 : (tbinfo->relkind == RELKIND_RELATION ||
16911 214 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16912 212 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16913 : {
16914 : bool firstitem;
16915 : bool firstitem_extra;
16916 :
16917 : /*
16918 : * Drop any dropped columns. Merge the pg_attribute manipulations
16919 : * into a single SQL command, so that we don't cause repeated
16920 : * relcache flushes on the target table. Otherwise we risk O(N^2)
16921 : * relcache bloat while dropping N columns.
16922 : */
16923 1508 : resetPQExpBuffer(extra);
16924 1508 : firstitem = true;
16925 7622 : for (j = 0; j < tbinfo->numatts; j++)
16926 : {
16927 6114 : if (tbinfo->attisdropped[j])
16928 : {
16929 168 : if (firstitem)
16930 : {
16931 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
16932 : "UPDATE pg_catalog.pg_attribute\n"
16933 : "SET attlen = v.dlen, "
16934 : "attalign = v.dalign, "
16935 : "attbyval = false\n"
16936 : "FROM (VALUES ");
16937 76 : firstitem = false;
16938 : }
16939 : else
16940 92 : appendPQExpBufferStr(q, ",\n ");
16941 168 : appendPQExpBufferChar(q, '(');
16942 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16943 168 : appendPQExpBuffer(q, ", %d, '%c')",
16944 168 : tbinfo->attlen[j],
16945 168 : tbinfo->attalign[j]);
16946 : /* The ALTER ... DROP COLUMN commands must come after */
16947 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
16948 : foreign, qualrelname);
16949 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
16950 168 : fmtId(tbinfo->attnames[j]));
16951 : }
16952 : }
16953 1508 : if (!firstitem)
16954 : {
16955 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
16956 : "WHERE attrelid = ");
16957 76 : appendStringLiteralAH(q, qualrelname, fout);
16958 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16959 : " AND attname = v.dname;\n");
16960 : /* Now we can issue the actual DROP COLUMN commands */
16961 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16962 : }
16963 :
16964 : /*
16965 : * Fix up inherited columns. As above, do the pg_attribute
16966 : * manipulations in a single SQL command.
16967 : */
16968 1508 : firstitem = true;
16969 7622 : for (j = 0; j < tbinfo->numatts; j++)
16970 : {
16971 6114 : if (!tbinfo->attisdropped[j] &&
16972 5946 : !tbinfo->attislocal[j])
16973 : {
16974 1174 : if (firstitem)
16975 : {
16976 508 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
16977 508 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16978 : "SET attislocal = false\n"
16979 : "WHERE attrelid = ");
16980 508 : appendStringLiteralAH(q, qualrelname, fout);
16981 508 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16982 : " AND attname IN (");
16983 508 : firstitem = false;
16984 : }
16985 : else
16986 666 : appendPQExpBufferStr(q, ", ");
16987 1174 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16988 : }
16989 : }
16990 1508 : if (!firstitem)
16991 508 : appendPQExpBufferStr(q, ");\n");
16992 :
16993 : /*
16994 : * Fix up not-null constraints that come from inheritance. As
16995 : * above, do the pg_constraint manipulations in a single SQL
16996 : * command. (Actually, two in special cases, if we're doing an
16997 : * upgrade from < 18).
16998 : */
16999 1508 : firstitem = true;
17000 1508 : firstitem_extra = true;
17001 1508 : resetPQExpBuffer(extra);
17002 7622 : for (j = 0; j < tbinfo->numatts; j++)
17003 : {
17004 : /*
17005 : * If a not-null constraint comes from inheritance, reset
17006 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17007 : * below. Special hack: in versions < 18, columns with no
17008 : * local definition need their constraint to be matched by
17009 : * column number in conkeys instead of by constraint name,
17010 : * because the latter is not available. (We distinguish the
17011 : * case because the constraint name is the empty string.)
17012 : */
17013 6114 : if (tbinfo->notnull_constrs[j] != NULL &&
17014 550 : !tbinfo->notnull_islocal[j])
17015 : {
17016 164 : if (tbinfo->notnull_constrs[j][0] != '\0')
17017 : {
17018 138 : if (firstitem)
17019 : {
17020 118 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17021 : "SET conislocal = false\n"
17022 : "WHERE contype = 'n' AND conrelid = ");
17023 118 : appendStringLiteralAH(q, qualrelname, fout);
17024 118 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17025 : "conname IN (");
17026 118 : firstitem = false;
17027 : }
17028 : else
17029 20 : appendPQExpBufferStr(q, ", ");
17030 138 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17031 : }
17032 : else
17033 : {
17034 26 : if (firstitem_extra)
17035 : {
17036 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17037 : "SET conislocal = false\n"
17038 : "WHERE contype = 'n' AND conrelid = ");
17039 26 : appendStringLiteralAH(extra, qualrelname, fout);
17040 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17041 : "conkey IN (");
17042 26 : firstitem_extra = false;
17043 : }
17044 : else
17045 0 : appendPQExpBufferStr(extra, ", ");
17046 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17047 : }
17048 : }
17049 : }
17050 1508 : if (!firstitem)
17051 118 : appendPQExpBufferStr(q, ");\n");
17052 1508 : if (!firstitem_extra)
17053 26 : appendPQExpBufferStr(extra, ");\n");
17054 :
17055 1508 : if (extra->len > 0)
17056 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17057 :
17058 : /*
17059 : * Add inherited CHECK constraints, if any.
17060 : *
17061 : * For partitions, they were already dumped, and conislocal
17062 : * doesn't need fixing.
17063 : *
17064 : * As above, issue only one direct manipulation of pg_constraint.
17065 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17066 : * commands into one as well, refrain for now due to concern about
17067 : * possible backend memory bloat if there are many such
17068 : * constraints.
17069 : */
17070 1508 : resetPQExpBuffer(extra);
17071 1508 : firstitem = true;
17072 1636 : for (k = 0; k < tbinfo->ncheck; k++)
17073 : {
17074 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17075 :
17076 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17077 124 : continue;
17078 :
17079 4 : if (firstitem)
17080 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17081 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17082 : foreign, qualrelname,
17083 4 : fmtId(constr->dobj.name),
17084 : constr->condef);
17085 : /* Update pg_constraint after all the ALTER TABLEs */
17086 4 : if (firstitem)
17087 : {
17088 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17089 : "SET conislocal = false\n"
17090 : "WHERE contype = 'c' AND conrelid = ");
17091 4 : appendStringLiteralAH(extra, qualrelname, fout);
17092 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17093 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17094 4 : firstitem = false;
17095 : }
17096 : else
17097 0 : appendPQExpBufferStr(extra, ", ");
17098 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17099 : }
17100 1508 : if (!firstitem)
17101 : {
17102 4 : appendPQExpBufferStr(extra, ");\n");
17103 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17104 : }
17105 :
17106 1508 : if (numParents > 0 && !tbinfo->ispartition)
17107 : {
17108 110 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17109 236 : for (k = 0; k < numParents; k++)
17110 : {
17111 126 : TableInfo *parentRel = parents[k];
17112 :
17113 126 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17114 : qualrelname,
17115 126 : fmtQualifiedDumpable(parentRel));
17116 : }
17117 : }
17118 :
17119 1508 : if (OidIsValid(tbinfo->reloftype))
17120 : {
17121 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17122 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17123 : qualrelname,
17124 : getFormattedTypeName(fout, tbinfo->reloftype,
17125 : zeroIsError));
17126 : }
17127 : }
17128 :
17129 : /*
17130 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17131 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17132 : * TOAST tables semi-independently, here we see them only as children
17133 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17134 : * child toast table is handled below.)
17135 : */
17136 11058 : if (dopt->binary_upgrade &&
17137 1544 : (tbinfo->relkind == RELKIND_RELATION ||
17138 214 : tbinfo->relkind == RELKIND_MATVIEW))
17139 : {
17140 1366 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17141 1366 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17142 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17143 : "WHERE oid = ",
17144 : tbinfo->frozenxid, tbinfo->minmxid);
17145 1366 : appendStringLiteralAH(q, qualrelname, fout);
17146 1366 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17147 :
17148 1366 : if (tbinfo->toast_oid)
17149 : {
17150 : /*
17151 : * The toast table will have the same OID at restore, so we
17152 : * can safely target it by OID.
17153 : */
17154 552 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17155 552 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17156 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17157 : "WHERE oid = '%u';\n",
17158 : tbinfo->toast_frozenxid,
17159 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17160 : }
17161 : }
17162 :
17163 : /*
17164 : * In binary_upgrade mode, restore matviews' populated status by
17165 : * poking pg_class directly. This is pretty ugly, but we can't use
17166 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17167 : * matview is not populated even though this matview is; in any case,
17168 : * we want to transfer the matview's heap storage, not run REFRESH.
17169 : */
17170 11058 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17171 36 : tbinfo->relispopulated)
17172 : {
17173 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17174 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17175 : "SET relispopulated = 't'\n"
17176 : "WHERE oid = ");
17177 32 : appendStringLiteralAH(q, qualrelname, fout);
17178 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17179 : }
17180 :
17181 : /*
17182 : * Dump additional per-column properties that we can't handle in the
17183 : * main CREATE TABLE command.
17184 : */
17185 51366 : for (j = 0; j < tbinfo->numatts; j++)
17186 : {
17187 : /* None of this applies to dropped columns */
17188 40308 : if (tbinfo->attisdropped[j])
17189 902 : continue;
17190 :
17191 : /*
17192 : * Dump per-column statistics information. We only issue an ALTER
17193 : * TABLE statement if the attstattarget entry for this column is
17194 : * not the default value.
17195 : */
17196 39406 : if (tbinfo->attstattarget[j] >= 0)
17197 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17198 : foreign, qualrelname,
17199 72 : fmtId(tbinfo->attnames[j]),
17200 72 : tbinfo->attstattarget[j]);
17201 :
17202 : /*
17203 : * Dump per-column storage information. The statement is only
17204 : * dumped if the storage has been changed from the type's default.
17205 : */
17206 39406 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17207 : {
17208 174 : switch (tbinfo->attstorage[j])
17209 : {
17210 20 : case TYPSTORAGE_PLAIN:
17211 20 : storage = "PLAIN";
17212 20 : break;
17213 82 : case TYPSTORAGE_EXTERNAL:
17214 82 : storage = "EXTERNAL";
17215 82 : break;
17216 0 : case TYPSTORAGE_EXTENDED:
17217 0 : storage = "EXTENDED";
17218 0 : break;
17219 72 : case TYPSTORAGE_MAIN:
17220 72 : storage = "MAIN";
17221 72 : break;
17222 0 : default:
17223 0 : storage = NULL;
17224 : }
17225 :
17226 : /*
17227 : * Only dump the statement if it's a storage type we recognize
17228 : */
17229 174 : if (storage != NULL)
17230 174 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17231 : foreign, qualrelname,
17232 174 : fmtId(tbinfo->attnames[j]),
17233 : storage);
17234 : }
17235 :
17236 : /*
17237 : * Dump per-column compression, if it's been set.
17238 : */
17239 39406 : if (!dopt->no_toast_compression)
17240 : {
17241 : const char *cmname;
17242 :
17243 39226 : switch (tbinfo->attcompression[j])
17244 : {
17245 120 : case 'p':
17246 120 : cmname = "pglz";
17247 120 : break;
17248 200 : case 'l':
17249 200 : cmname = "lz4";
17250 200 : break;
17251 38906 : default:
17252 38906 : cmname = NULL;
17253 38906 : break;
17254 : }
17255 :
17256 39226 : if (cmname != NULL)
17257 320 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17258 : foreign, qualrelname,
17259 320 : fmtId(tbinfo->attnames[j]),
17260 : cmname);
17261 : }
17262 :
17263 : /*
17264 : * Dump per-column attributes.
17265 : */
17266 39406 : if (tbinfo->attoptions[j][0] != '\0')
17267 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17268 : foreign, qualrelname,
17269 72 : fmtId(tbinfo->attnames[j]),
17270 72 : tbinfo->attoptions[j]);
17271 :
17272 : /*
17273 : * Dump per-column fdw options.
17274 : */
17275 39406 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17276 76 : tbinfo->attfdwoptions[j][0] != '\0')
17277 72 : appendPQExpBuffer(q,
17278 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17279 : " %s\n"
17280 : ");\n",
17281 : qualrelname,
17282 72 : fmtId(tbinfo->attnames[j]),
17283 72 : tbinfo->attfdwoptions[j]);
17284 : } /* end loop over columns */
17285 :
17286 11058 : free(partkeydef);
17287 11058 : free(ftoptions);
17288 11058 : free(srvname);
17289 : }
17290 :
17291 : /*
17292 : * dump properties we only have ALTER TABLE syntax for
17293 : */
17294 12098 : if ((tbinfo->relkind == RELKIND_RELATION ||
17295 2990 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17296 1916 : tbinfo->relkind == RELKIND_MATVIEW) &&
17297 10982 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17298 : {
17299 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17300 : {
17301 : /* nothing to do, will be set when the index is dumped */
17302 : }
17303 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17304 : {
17305 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17306 : qualrelname);
17307 : }
17308 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17309 : {
17310 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17311 : qualrelname);
17312 : }
17313 : }
17314 :
17315 12098 : if (tbinfo->forcerowsec)
17316 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17317 : qualrelname);
17318 :
17319 12098 : if (dopt->binary_upgrade)
17320 1646 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17321 : reltypename, qrelname,
17322 1646 : tbinfo->dobj.namespace->dobj.name);
17323 :
17324 12098 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17325 : {
17326 12098 : char *tablespace = NULL;
17327 12098 : char *tableam = NULL;
17328 :
17329 : /*
17330 : * _selectTablespace() relies on tablespace-enabled objects in the
17331 : * default tablespace to have a tablespace of "" (empty string) versus
17332 : * non-tablespace-enabled objects to have a tablespace of NULL.
17333 : * getTables() sets tbinfo->reltablespace to "" for the default
17334 : * tablespace (not NULL).
17335 : */
17336 12098 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17337 10982 : tablespace = tbinfo->reltablespace;
17338 :
17339 12098 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17340 2190 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17341 10982 : tableam = tbinfo->amname;
17342 :
17343 12098 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17344 12098 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17345 : .namespace = tbinfo->dobj.namespace->dobj.name,
17346 : .tablespace = tablespace,
17347 : .tableam = tableam,
17348 : .relkind = tbinfo->relkind,
17349 : .owner = tbinfo->rolname,
17350 : .description = reltypename,
17351 : .section = tbinfo->postponed_def ?
17352 : SECTION_POST_DATA : SECTION_PRE_DATA,
17353 : .createStmt = q->data,
17354 : .dropStmt = delq->data));
17355 : }
17356 :
17357 : /* Dump Table Comments */
17358 12098 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17359 164 : dumpTableComment(fout, tbinfo, reltypename);
17360 :
17361 : /* Dump Table Security Labels */
17362 12098 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17363 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17364 :
17365 : /* Dump comments on inlined table constraints */
17366 13344 : for (j = 0; j < tbinfo->ncheck; j++)
17367 : {
17368 1246 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17369 :
17370 1246 : if (constr->separate || !constr->conislocal)
17371 528 : continue;
17372 :
17373 718 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17374 82 : dumpTableConstraintComment(fout, constr);
17375 : }
17376 :
17377 12098 : destroyPQExpBuffer(q);
17378 12098 : destroyPQExpBuffer(delq);
17379 12098 : destroyPQExpBuffer(extra);
17380 12098 : free(qrelname);
17381 12098 : free(qualrelname);
17382 12098 : }
17383 :
17384 : /*
17385 : * dumpTableAttach
17386 : * write to fout the commands to attach a child partition
17387 : *
17388 : * Child partitions are always made by creating them separately
17389 : * and then using ATTACH PARTITION, rather than using
17390 : * CREATE TABLE ... PARTITION OF. This is important for preserving
17391 : * any possible discrepancy in column layout, to allow assigning the
17392 : * correct tablespace if different, and so that it's possible to restore
17393 : * a partition without restoring its parent. (You'll get an error from
17394 : * the ATTACH PARTITION command, but that can be ignored, or skipped
17395 : * using "pg_restore -L" if you prefer.) The last point motivates
17396 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
17397 : * rather than emitting it within the child partition's ArchiveEntry.
17398 : */
17399 : static void
17400 2630 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
17401 : {
17402 2630 : DumpOptions *dopt = fout->dopt;
17403 : PQExpBuffer q;
17404 : PGresult *res;
17405 : char *partbound;
17406 :
17407 : /* Do nothing if not dumping schema */
17408 2630 : if (!dopt->dumpSchema)
17409 84 : return;
17410 :
17411 2546 : q = createPQExpBuffer();
17412 :
17413 2546 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
17414 : {
17415 : /* Set up query for partbound details */
17416 94 : appendPQExpBufferStr(q,
17417 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
17418 :
17419 94 : appendPQExpBufferStr(q,
17420 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
17421 : "FROM pg_class c "
17422 : "WHERE c.oid = $1");
17423 :
17424 94 : ExecuteSqlStatement(fout, q->data);
17425 :
17426 94 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
17427 : }
17428 :
17429 2546 : printfPQExpBuffer(q,
17430 : "EXECUTE dumpTableAttach('%u')",
17431 2546 : attachinfo->partitionTbl->dobj.catId.oid);
17432 :
17433 2546 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
17434 2546 : partbound = PQgetvalue(res, 0, 0);
17435 :
17436 : /* Perform ALTER TABLE on the parent */
17437 2546 : printfPQExpBuffer(q,
17438 : "ALTER TABLE ONLY %s ",
17439 2546 : fmtQualifiedDumpable(attachinfo->parentTbl));
17440 2546 : appendPQExpBuffer(q,
17441 : "ATTACH PARTITION %s %s;\n",
17442 2546 : fmtQualifiedDumpable(attachinfo->partitionTbl),
17443 : partbound);
17444 :
17445 : /*
17446 : * There is no point in creating a drop query as the drop is done by table
17447 : * drop. (If you think to change this, see also _printTocEntry().)
17448 : * Although this object doesn't really have ownership as such, set the
17449 : * owner field anyway to ensure that the command is run by the correct
17450 : * role at restore time.
17451 : */
17452 2546 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17453 2546 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17454 : .namespace = attachinfo->dobj.namespace->dobj.name,
17455 : .owner = attachinfo->partitionTbl->rolname,
17456 : .description = "TABLE ATTACH",
17457 : .section = SECTION_PRE_DATA,
17458 : .createStmt = q->data));
17459 :
17460 2546 : PQclear(res);
17461 2546 : destroyPQExpBuffer(q);
17462 : }
17463 :
17464 : /*
17465 : * dumpAttrDef --- dump an attribute's default-value declaration
17466 : */
17467 : static void
17468 2140 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
17469 : {
17470 2140 : DumpOptions *dopt = fout->dopt;
17471 2140 : TableInfo *tbinfo = adinfo->adtable;
17472 2140 : int adnum = adinfo->adnum;
17473 : PQExpBuffer q;
17474 : PQExpBuffer delq;
17475 : char *qualrelname;
17476 : char *tag;
17477 : char *foreign;
17478 :
17479 : /* Do nothing if not dumping schema */
17480 2140 : if (!dopt->dumpSchema)
17481 0 : return;
17482 :
17483 : /* Skip if not "separate"; it was dumped in the table's definition */
17484 2140 : if (!adinfo->separate)
17485 1784 : return;
17486 :
17487 356 : q = createPQExpBuffer();
17488 356 : delq = createPQExpBuffer();
17489 :
17490 356 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17491 :
17492 356 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17493 :
17494 356 : appendPQExpBuffer(q,
17495 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17496 356 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17497 : adinfo->adef_expr);
17498 :
17499 356 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17500 : foreign, qualrelname,
17501 356 : fmtId(tbinfo->attnames[adnum - 1]));
17502 :
17503 356 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17504 :
17505 356 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17506 356 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17507 356 : ARCHIVE_OPTS(.tag = tag,
17508 : .namespace = tbinfo->dobj.namespace->dobj.name,
17509 : .owner = tbinfo->rolname,
17510 : .description = "DEFAULT",
17511 : .section = SECTION_PRE_DATA,
17512 : .createStmt = q->data,
17513 : .dropStmt = delq->data));
17514 :
17515 356 : free(tag);
17516 356 : destroyPQExpBuffer(q);
17517 356 : destroyPQExpBuffer(delq);
17518 356 : free(qualrelname);
17519 : }
17520 :
17521 : /*
17522 : * getAttrName: extract the correct name for an attribute
17523 : *
17524 : * The array tblInfo->attnames[] only provides names of user attributes;
17525 : * if a system attribute number is supplied, we have to fake it.
17526 : * We also do a little bit of bounds checking for safety's sake.
17527 : */
17528 : static const char *
17529 4200 : getAttrName(int attrnum, const TableInfo *tblInfo)
17530 : {
17531 4200 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
17532 4200 : return tblInfo->attnames[attrnum - 1];
17533 0 : switch (attrnum)
17534 : {
17535 0 : case SelfItemPointerAttributeNumber:
17536 0 : return "ctid";
17537 0 : case MinTransactionIdAttributeNumber:
17538 0 : return "xmin";
17539 0 : case MinCommandIdAttributeNumber:
17540 0 : return "cmin";
17541 0 : case MaxTransactionIdAttributeNumber:
17542 0 : return "xmax";
17543 0 : case MaxCommandIdAttributeNumber:
17544 0 : return "cmax";
17545 0 : case TableOidAttributeNumber:
17546 0 : return "tableoid";
17547 : }
17548 0 : pg_fatal("invalid column number %d for table \"%s\"",
17549 : attrnum, tblInfo->dobj.name);
17550 : return NULL; /* keep compiler quiet */
17551 : }
17552 :
17553 : /*
17554 : * dumpIndex
17555 : * write out to fout a user-defined index
17556 : */
17557 : static void
17558 5260 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17559 : {
17560 5260 : DumpOptions *dopt = fout->dopt;
17561 5260 : TableInfo *tbinfo = indxinfo->indextable;
17562 5260 : bool is_constraint = (indxinfo->indexconstraint != 0);
17563 : PQExpBuffer q;
17564 : PQExpBuffer delq;
17565 : char *qindxname;
17566 : char *qqindxname;
17567 :
17568 : /* Do nothing if not dumping schema */
17569 5260 : if (!dopt->dumpSchema)
17570 234 : return;
17571 :
17572 5026 : q = createPQExpBuffer();
17573 5026 : delq = createPQExpBuffer();
17574 :
17575 5026 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17576 5026 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17577 :
17578 : /*
17579 : * If there's an associated constraint, don't dump the index per se, but
17580 : * do dump any comment for it. (This is safe because dependency ordering
17581 : * will have ensured the constraint is emitted first.) Note that the
17582 : * emitted comment has to be shown as depending on the constraint, not the
17583 : * index, in such cases.
17584 : */
17585 5026 : if (!is_constraint)
17586 : {
17587 2116 : char *indstatcols = indxinfo->indstatcols;
17588 2116 : char *indstatvals = indxinfo->indstatvals;
17589 2116 : char **indstatcolsarray = NULL;
17590 2116 : char **indstatvalsarray = NULL;
17591 2116 : int nstatcols = 0;
17592 2116 : int nstatvals = 0;
17593 :
17594 2116 : if (dopt->binary_upgrade)
17595 310 : binary_upgrade_set_pg_class_oids(fout, q,
17596 : indxinfo->dobj.catId.oid);
17597 :
17598 : /* Plain secondary index */
17599 2116 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17600 :
17601 : /*
17602 : * Append ALTER TABLE commands as needed to set properties that we
17603 : * only have ALTER TABLE syntax for. Keep this in sync with the
17604 : * similar code in dumpConstraint!
17605 : */
17606 :
17607 : /* If the index is clustered, we need to record that. */
17608 2116 : if (indxinfo->indisclustered)
17609 : {
17610 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17611 0 : fmtQualifiedDumpable(tbinfo));
17612 : /* index name is not qualified in this syntax */
17613 0 : appendPQExpBuffer(q, " ON %s;\n",
17614 : qindxname);
17615 : }
17616 :
17617 : /*
17618 : * If the index has any statistics on some of its columns, generate
17619 : * the associated ALTER INDEX queries.
17620 : */
17621 2116 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17622 : {
17623 : int j;
17624 :
17625 72 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17626 0 : pg_fatal("could not parse index statistic columns");
17627 72 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17628 0 : pg_fatal("could not parse index statistic values");
17629 72 : if (nstatcols != nstatvals)
17630 0 : pg_fatal("mismatched number of columns and values for index statistics");
17631 :
17632 216 : for (j = 0; j < nstatcols; j++)
17633 : {
17634 144 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17635 :
17636 : /*
17637 : * Note that this is a column number, so no quotes should be
17638 : * used.
17639 : */
17640 144 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
17641 144 : indstatcolsarray[j]);
17642 144 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17643 144 : indstatvalsarray[j]);
17644 : }
17645 : }
17646 :
17647 : /* Indexes can depend on extensions */
17648 2116 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17649 : "pg_catalog.pg_class",
17650 : "INDEX", qqindxname);
17651 :
17652 : /* If the index defines identity, we need to record that. */
17653 2116 : if (indxinfo->indisreplident)
17654 : {
17655 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17656 0 : fmtQualifiedDumpable(tbinfo));
17657 : /* index name is not qualified in this syntax */
17658 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17659 : qindxname);
17660 : }
17661 :
17662 2116 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17663 :
17664 2116 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17665 2116 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17666 2116 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17667 : .namespace = tbinfo->dobj.namespace->dobj.name,
17668 : .tablespace = indxinfo->tablespace,
17669 : .owner = tbinfo->rolname,
17670 : .description = "INDEX",
17671 : .section = SECTION_POST_DATA,
17672 : .createStmt = q->data,
17673 : .dropStmt = delq->data));
17674 :
17675 2116 : free(indstatcolsarray);
17676 2116 : free(indstatvalsarray);
17677 : }
17678 :
17679 : /* Dump Index Comments */
17680 5026 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17681 30 : dumpComment(fout, "INDEX", qindxname,
17682 30 : tbinfo->dobj.namespace->dobj.name,
17683 : tbinfo->rolname,
17684 : indxinfo->dobj.catId, 0,
17685 : is_constraint ? indxinfo->indexconstraint :
17686 : indxinfo->dobj.dumpId);
17687 :
17688 5026 : destroyPQExpBuffer(q);
17689 5026 : destroyPQExpBuffer(delq);
17690 5026 : free(qindxname);
17691 5026 : free(qqindxname);
17692 : }
17693 :
17694 : /*
17695 : * dumpIndexAttach
17696 : * write out to fout a partitioned-index attachment clause
17697 : */
17698 : static void
17699 1192 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17700 : {
17701 : /* Do nothing if not dumping schema */
17702 1192 : if (!fout->dopt->dumpSchema)
17703 96 : return;
17704 :
17705 1096 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17706 : {
17707 1096 : PQExpBuffer q = createPQExpBuffer();
17708 :
17709 1096 : appendPQExpBuffer(q, "ALTER INDEX %s ",
17710 1096 : fmtQualifiedDumpable(attachinfo->parentIdx));
17711 1096 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17712 1096 : fmtQualifiedDumpable(attachinfo->partitionIdx));
17713 :
17714 : /*
17715 : * There is no point in creating a drop query as the drop is done by
17716 : * index drop. (If you think to change this, see also
17717 : * _printTocEntry().) Although this object doesn't really have
17718 : * ownership as such, set the owner field anyway to ensure that the
17719 : * command is run by the correct role at restore time.
17720 : */
17721 1096 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17722 1096 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17723 : .namespace = attachinfo->dobj.namespace->dobj.name,
17724 : .owner = attachinfo->parentIdx->indextable->rolname,
17725 : .description = "INDEX ATTACH",
17726 : .section = SECTION_POST_DATA,
17727 : .createStmt = q->data));
17728 :
17729 1096 : destroyPQExpBuffer(q);
17730 : }
17731 : }
17732 :
17733 : /*
17734 : * dumpStatisticsExt
17735 : * write out to fout an extended statistics object
17736 : */
17737 : static void
17738 290 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17739 : {
17740 290 : DumpOptions *dopt = fout->dopt;
17741 : PQExpBuffer q;
17742 : PQExpBuffer delq;
17743 : PQExpBuffer query;
17744 : char *qstatsextname;
17745 : PGresult *res;
17746 : char *stxdef;
17747 :
17748 : /* Do nothing if not dumping schema */
17749 290 : if (!dopt->dumpSchema)
17750 36 : return;
17751 :
17752 254 : q = createPQExpBuffer();
17753 254 : delq = createPQExpBuffer();
17754 254 : query = createPQExpBuffer();
17755 :
17756 254 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17757 :
17758 254 : appendPQExpBuffer(query, "SELECT "
17759 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17760 : statsextinfo->dobj.catId.oid);
17761 :
17762 254 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17763 :
17764 254 : stxdef = PQgetvalue(res, 0, 0);
17765 :
17766 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17767 254 : appendPQExpBuffer(q, "%s;\n", stxdef);
17768 :
17769 : /*
17770 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17771 : * for this statistics object is not the default value.
17772 : */
17773 254 : if (statsextinfo->stattarget >= 0)
17774 : {
17775 72 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17776 72 : fmtQualifiedDumpable(statsextinfo));
17777 72 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17778 : statsextinfo->stattarget);
17779 : }
17780 :
17781 254 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17782 254 : fmtQualifiedDumpable(statsextinfo));
17783 :
17784 254 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17785 254 : ArchiveEntry(fout, statsextinfo->dobj.catId,
17786 : statsextinfo->dobj.dumpId,
17787 254 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17788 : .namespace = statsextinfo->dobj.namespace->dobj.name,
17789 : .owner = statsextinfo->rolname,
17790 : .description = "STATISTICS",
17791 : .section = SECTION_POST_DATA,
17792 : .createStmt = q->data,
17793 : .dropStmt = delq->data));
17794 :
17795 : /* Dump Statistics Comments */
17796 254 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17797 0 : dumpComment(fout, "STATISTICS", qstatsextname,
17798 0 : statsextinfo->dobj.namespace->dobj.name,
17799 : statsextinfo->rolname,
17800 : statsextinfo->dobj.catId, 0,
17801 : statsextinfo->dobj.dumpId);
17802 :
17803 254 : PQclear(res);
17804 254 : destroyPQExpBuffer(q);
17805 254 : destroyPQExpBuffer(delq);
17806 254 : destroyPQExpBuffer(query);
17807 254 : free(qstatsextname);
17808 : }
17809 :
17810 : /*
17811 : * dumpConstraint
17812 : * write out to fout a user-defined constraint
17813 : */
17814 : static void
17815 4866 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17816 : {
17817 4866 : DumpOptions *dopt = fout->dopt;
17818 4866 : TableInfo *tbinfo = coninfo->contable;
17819 : PQExpBuffer q;
17820 : PQExpBuffer delq;
17821 4866 : char *tag = NULL;
17822 : char *foreign;
17823 :
17824 : /* Do nothing if not dumping schema */
17825 4866 : if (!dopt->dumpSchema)
17826 184 : return;
17827 :
17828 4682 : q = createPQExpBuffer();
17829 4682 : delq = createPQExpBuffer();
17830 :
17831 9172 : foreign = tbinfo &&
17832 4682 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17833 :
17834 4682 : if (coninfo->contype == 'p' ||
17835 2258 : coninfo->contype == 'u' ||
17836 1792 : coninfo->contype == 'x')
17837 2910 : {
17838 : /* Index-related constraint */
17839 : IndxInfo *indxinfo;
17840 : int k;
17841 :
17842 2910 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17843 :
17844 2910 : if (indxinfo == NULL)
17845 0 : pg_fatal("missing index for constraint \"%s\"",
17846 : coninfo->dobj.name);
17847 :
17848 2910 : if (dopt->binary_upgrade)
17849 286 : binary_upgrade_set_pg_class_oids(fout, q,
17850 : indxinfo->dobj.catId.oid);
17851 :
17852 2910 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17853 2910 : fmtQualifiedDumpable(tbinfo));
17854 2910 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17855 2910 : fmtId(coninfo->dobj.name));
17856 :
17857 2910 : if (coninfo->condef)
17858 : {
17859 : /* pg_get_constraintdef should have provided everything */
17860 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17861 : }
17862 : else
17863 : {
17864 2890 : appendPQExpBufferStr(q,
17865 2890 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17866 :
17867 : /*
17868 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17869 : * indexes. Being able to create this was fixed, but we need to
17870 : * make the index distinct in order to be able to restore the
17871 : * dump.
17872 : */
17873 2890 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17874 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17875 2890 : appendPQExpBufferStr(q, " (");
17876 7010 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
17877 : {
17878 4120 : int indkey = (int) indxinfo->indkeys[k];
17879 : const char *attname;
17880 :
17881 4120 : if (indkey == InvalidAttrNumber)
17882 0 : break;
17883 4120 : attname = getAttrName(indkey, tbinfo);
17884 :
17885 4120 : appendPQExpBuffer(q, "%s%s",
17886 : (k == 0) ? "" : ", ",
17887 : fmtId(attname));
17888 : }
17889 2890 : if (coninfo->conperiod)
17890 224 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17891 :
17892 2890 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17893 40 : appendPQExpBufferStr(q, ") INCLUDE (");
17894 :
17895 2970 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17896 : {
17897 80 : int indkey = (int) indxinfo->indkeys[k];
17898 : const char *attname;
17899 :
17900 80 : if (indkey == InvalidAttrNumber)
17901 0 : break;
17902 80 : attname = getAttrName(indkey, tbinfo);
17903 :
17904 160 : appendPQExpBuffer(q, "%s%s",
17905 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
17906 : fmtId(attname));
17907 : }
17908 :
17909 2890 : appendPQExpBufferChar(q, ')');
17910 :
17911 2890 : if (nonemptyReloptions(indxinfo->indreloptions))
17912 : {
17913 0 : appendPQExpBufferStr(q, " WITH (");
17914 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17915 0 : appendPQExpBufferChar(q, ')');
17916 : }
17917 :
17918 2890 : if (coninfo->condeferrable)
17919 : {
17920 50 : appendPQExpBufferStr(q, " DEFERRABLE");
17921 50 : if (coninfo->condeferred)
17922 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17923 : }
17924 :
17925 2890 : appendPQExpBufferStr(q, ";\n");
17926 : }
17927 :
17928 : /*
17929 : * Append ALTER TABLE commands as needed to set properties that we
17930 : * only have ALTER TABLE syntax for. Keep this in sync with the
17931 : * similar code in dumpIndex!
17932 : */
17933 :
17934 : /* If the index is clustered, we need to record that. */
17935 2910 : if (indxinfo->indisclustered)
17936 : {
17937 72 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17938 72 : fmtQualifiedDumpable(tbinfo));
17939 : /* index name is not qualified in this syntax */
17940 72 : appendPQExpBuffer(q, " ON %s;\n",
17941 72 : fmtId(indxinfo->dobj.name));
17942 : }
17943 :
17944 : /* If the index defines identity, we need to record that. */
17945 2910 : if (indxinfo->indisreplident)
17946 : {
17947 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17948 0 : fmtQualifiedDumpable(tbinfo));
17949 : /* index name is not qualified in this syntax */
17950 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17951 0 : fmtId(indxinfo->dobj.name));
17952 : }
17953 :
17954 : /* Indexes can depend on extensions */
17955 2910 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17956 : "pg_catalog.pg_class", "INDEX",
17957 2910 : fmtQualifiedDumpable(indxinfo));
17958 :
17959 2910 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17960 2910 : fmtQualifiedDumpable(tbinfo));
17961 2910 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17962 2910 : fmtId(coninfo->dobj.name));
17963 :
17964 2910 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17965 :
17966 2910 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17967 2910 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17968 2910 : ARCHIVE_OPTS(.tag = tag,
17969 : .namespace = tbinfo->dobj.namespace->dobj.name,
17970 : .tablespace = indxinfo->tablespace,
17971 : .owner = tbinfo->rolname,
17972 : .description = "CONSTRAINT",
17973 : .section = SECTION_POST_DATA,
17974 : .createStmt = q->data,
17975 : .dropStmt = delq->data));
17976 : }
17977 1772 : else if (coninfo->contype == 'f')
17978 : {
17979 : char *only;
17980 :
17981 : /*
17982 : * Foreign keys on partitioned tables are always declared as
17983 : * inheriting to partitions; for all other cases, emit them as
17984 : * applying ONLY directly to the named table, because that's how they
17985 : * work for regular inherited tables.
17986 : */
17987 334 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17988 :
17989 : /*
17990 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17991 : * current table data is not processed
17992 : */
17993 334 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17994 334 : only, fmtQualifiedDumpable(tbinfo));
17995 334 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17996 334 : fmtId(coninfo->dobj.name),
17997 : coninfo->condef);
17998 :
17999 334 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18000 334 : only, fmtQualifiedDumpable(tbinfo));
18001 334 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18002 334 : fmtId(coninfo->dobj.name));
18003 :
18004 334 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18005 :
18006 334 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18007 334 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18008 334 : ARCHIVE_OPTS(.tag = tag,
18009 : .namespace = tbinfo->dobj.namespace->dobj.name,
18010 : .owner = tbinfo->rolname,
18011 : .description = "FK CONSTRAINT",
18012 : .section = SECTION_POST_DATA,
18013 : .createStmt = q->data,
18014 : .dropStmt = delq->data));
18015 : }
18016 1438 : else if (coninfo->contype == 'c' && tbinfo)
18017 : {
18018 : /* CHECK constraint on a table */
18019 :
18020 : /* Ignore if not to be dumped separately, or if it was inherited */
18021 1246 : if (coninfo->separate && coninfo->conislocal)
18022 : {
18023 : /* not ONLY since we want it to propagate to children */
18024 90 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18025 90 : fmtQualifiedDumpable(tbinfo));
18026 90 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18027 90 : fmtId(coninfo->dobj.name),
18028 : coninfo->condef);
18029 :
18030 90 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18031 90 : fmtQualifiedDumpable(tbinfo));
18032 90 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18033 90 : fmtId(coninfo->dobj.name));
18034 :
18035 90 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18036 :
18037 90 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18038 90 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18039 90 : ARCHIVE_OPTS(.tag = tag,
18040 : .namespace = tbinfo->dobj.namespace->dobj.name,
18041 : .owner = tbinfo->rolname,
18042 : .description = "CHECK CONSTRAINT",
18043 : .section = SECTION_POST_DATA,
18044 : .createStmt = q->data,
18045 : .dropStmt = delq->data));
18046 : }
18047 : }
18048 192 : else if (coninfo->contype == 'c' && tbinfo == NULL)
18049 192 : {
18050 : /* CHECK constraint on a domain */
18051 192 : TypeInfo *tyinfo = coninfo->condomain;
18052 :
18053 : /* Ignore if not to be dumped separately */
18054 192 : if (coninfo->separate)
18055 : {
18056 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18057 0 : fmtQualifiedDumpable(tyinfo));
18058 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18059 0 : fmtId(coninfo->dobj.name),
18060 : coninfo->condef);
18061 :
18062 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18063 0 : fmtQualifiedDumpable(tyinfo));
18064 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18065 0 : fmtId(coninfo->dobj.name));
18066 :
18067 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18068 :
18069 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18070 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18071 0 : ARCHIVE_OPTS(.tag = tag,
18072 : .namespace = tyinfo->dobj.namespace->dobj.name,
18073 : .owner = tyinfo->rolname,
18074 : .description = "CHECK CONSTRAINT",
18075 : .section = SECTION_POST_DATA,
18076 : .createStmt = q->data,
18077 : .dropStmt = delq->data));
18078 : }
18079 : }
18080 : else
18081 : {
18082 0 : pg_fatal("unrecognized constraint type: %c",
18083 : coninfo->contype);
18084 : }
18085 :
18086 : /* Dump Constraint Comments --- only works for table constraints */
18087 4682 : if (tbinfo && coninfo->separate &&
18088 3384 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18089 20 : dumpTableConstraintComment(fout, coninfo);
18090 :
18091 4682 : free(tag);
18092 4682 : destroyPQExpBuffer(q);
18093 4682 : destroyPQExpBuffer(delq);
18094 : }
18095 :
18096 : /*
18097 : * dumpTableConstraintComment --- dump a constraint's comment if any
18098 : *
18099 : * This is split out because we need the function in two different places
18100 : * depending on whether the constraint is dumped as part of CREATE TABLE
18101 : * or as a separate ALTER command.
18102 : */
18103 : static void
18104 102 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18105 : {
18106 102 : TableInfo *tbinfo = coninfo->contable;
18107 102 : PQExpBuffer conprefix = createPQExpBuffer();
18108 : char *qtabname;
18109 :
18110 102 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18111 :
18112 102 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18113 102 : fmtId(coninfo->dobj.name));
18114 :
18115 102 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18116 102 : dumpComment(fout, conprefix->data, qtabname,
18117 102 : tbinfo->dobj.namespace->dobj.name,
18118 : tbinfo->rolname,
18119 : coninfo->dobj.catId, 0,
18120 102 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18121 :
18122 102 : destroyPQExpBuffer(conprefix);
18123 102 : free(qtabname);
18124 102 : }
18125 :
18126 : static inline SeqType
18127 1300 : parse_sequence_type(const char *name)
18128 : {
18129 2898 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18130 : {
18131 2898 : if (strcmp(SeqTypeNames[i], name) == 0)
18132 1300 : return (SeqType) i;
18133 : }
18134 :
18135 0 : pg_fatal("unrecognized sequence type: %s", name);
18136 : return (SeqType) 0; /* keep compiler quiet */
18137 : }
18138 :
18139 : /*
18140 : * bsearch() comparator for SequenceItem
18141 : */
18142 : static int
18143 5984 : SequenceItemCmp(const void *p1, const void *p2)
18144 : {
18145 5984 : SequenceItem v1 = *((const SequenceItem *) p1);
18146 5984 : SequenceItem v2 = *((const SequenceItem *) p2);
18147 :
18148 5984 : return pg_cmp_u32(v1.oid, v2.oid);
18149 : }
18150 :
18151 : /*
18152 : * collectSequences
18153 : *
18154 : * Construct a table of sequence information. This table is sorted by OID for
18155 : * speed in lookup.
18156 : */
18157 : static void
18158 354 : collectSequences(Archive *fout)
18159 : {
18160 : PGresult *res;
18161 : const char *query;
18162 :
18163 : /*
18164 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18165 : * some extra effort, we might be able to use the sorted table for those
18166 : * versions, but for now it seems unlikely to be worth it.
18167 : *
18168 : * Since version 18, we can gather the sequence data in this query with
18169 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18170 : */
18171 354 : if (fout->remoteVersion < 100000)
18172 0 : return;
18173 354 : else if (fout->remoteVersion < 180000 ||
18174 354 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18175 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18176 : "seqstart, seqincrement, "
18177 : "seqmax, seqmin, "
18178 : "seqcache, seqcycle, "
18179 : "NULL, 'f' "
18180 : "FROM pg_catalog.pg_sequence "
18181 : "ORDER BY seqrelid";
18182 : else
18183 338 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18184 : "seqstart, seqincrement, "
18185 : "seqmax, seqmin, "
18186 : "seqcache, seqcycle, "
18187 : "last_value, is_called "
18188 : "FROM pg_catalog.pg_sequence, "
18189 : "pg_get_sequence_data(seqrelid) "
18190 : "ORDER BY seqrelid;";
18191 :
18192 354 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18193 :
18194 354 : nsequences = PQntuples(res);
18195 354 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18196 :
18197 1654 : for (int i = 0; i < nsequences; i++)
18198 : {
18199 1300 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18200 1300 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18201 1300 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18202 1300 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18203 1300 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18204 1300 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18205 1300 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18206 1300 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18207 1300 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18208 1300 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18209 : }
18210 :
18211 354 : PQclear(res);
18212 : }
18213 :
18214 : /*
18215 : * dumpSequence
18216 : * write the declaration (not data) of one user-defined sequence
18217 : */
18218 : static void
18219 774 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18220 : {
18221 774 : DumpOptions *dopt = fout->dopt;
18222 : SequenceItem *seq;
18223 : bool is_ascending;
18224 : int64 default_minv,
18225 : default_maxv;
18226 774 : PQExpBuffer query = createPQExpBuffer();
18227 774 : PQExpBuffer delqry = createPQExpBuffer();
18228 : char *qseqname;
18229 774 : TableInfo *owning_tab = NULL;
18230 :
18231 774 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18232 :
18233 : /*
18234 : * For versions >= 10, the sequence information is gathered in a sorted
18235 : * table before any calls to dumpSequence(). See collectSequences() for
18236 : * more information.
18237 : */
18238 774 : if (fout->remoteVersion >= 100000)
18239 : {
18240 774 : SequenceItem key = {0};
18241 :
18242 : Assert(sequences);
18243 :
18244 774 : key.oid = tbinfo->dobj.catId.oid;
18245 774 : seq = bsearch(&key, sequences, nsequences,
18246 : sizeof(SequenceItem), SequenceItemCmp);
18247 : }
18248 : else
18249 : {
18250 : PGresult *res;
18251 :
18252 : /*
18253 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18254 : *
18255 : * Note: it might seem that 'bigint' potentially needs to be
18256 : * schema-qualified, but actually that's a keyword.
18257 : */
18258 0 : appendPQExpBuffer(query,
18259 : "SELECT 'bigint' AS sequence_type, "
18260 : "start_value, increment_by, max_value, min_value, "
18261 : "cache_value, is_cycled FROM %s",
18262 0 : fmtQualifiedDumpable(tbinfo));
18263 :
18264 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18265 :
18266 0 : if (PQntuples(res) != 1)
18267 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18268 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18269 : PQntuples(res)),
18270 : tbinfo->dobj.name, PQntuples(res));
18271 :
18272 0 : seq = pg_malloc0(sizeof(SequenceItem));
18273 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18274 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18275 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18276 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18277 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18278 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18279 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18280 :
18281 0 : PQclear(res);
18282 : }
18283 :
18284 : /* Calculate default limits for a sequence of this type */
18285 774 : is_ascending = (seq->incby >= 0);
18286 774 : if (seq->seqtype == SEQTYPE_SMALLINT)
18287 : {
18288 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18289 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18290 : }
18291 724 : else if (seq->seqtype == SEQTYPE_INTEGER)
18292 : {
18293 592 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18294 592 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18295 : }
18296 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
18297 : {
18298 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18299 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18300 : }
18301 : else
18302 : {
18303 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18304 : default_minv = default_maxv = 0; /* keep compiler quiet */
18305 : }
18306 :
18307 : /*
18308 : * Identity sequences are not to be dropped separately.
18309 : */
18310 774 : if (!tbinfo->is_identity_sequence)
18311 : {
18312 482 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18313 482 : fmtQualifiedDumpable(tbinfo));
18314 : }
18315 :
18316 774 : resetPQExpBuffer(query);
18317 :
18318 774 : if (dopt->binary_upgrade)
18319 : {
18320 132 : binary_upgrade_set_pg_class_oids(fout, query,
18321 : tbinfo->dobj.catId.oid);
18322 :
18323 : /*
18324 : * In older PG versions a sequence will have a pg_type entry, but v14
18325 : * and up don't use that, so don't attempt to preserve the type OID.
18326 : */
18327 : }
18328 :
18329 774 : if (tbinfo->is_identity_sequence)
18330 : {
18331 292 : owning_tab = findTableByOid(tbinfo->owning_tab);
18332 :
18333 292 : appendPQExpBuffer(query,
18334 : "ALTER TABLE %s ",
18335 292 : fmtQualifiedDumpable(owning_tab));
18336 292 : appendPQExpBuffer(query,
18337 : "ALTER COLUMN %s ADD GENERATED ",
18338 292 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18339 292 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18340 212 : appendPQExpBufferStr(query, "ALWAYS");
18341 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18342 80 : appendPQExpBufferStr(query, "BY DEFAULT");
18343 292 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
18344 292 : fmtQualifiedDumpable(tbinfo));
18345 :
18346 : /*
18347 : * Emit persistence option only if it's different from the owning
18348 : * table's. This avoids using this new syntax unnecessarily.
18349 : */
18350 292 : if (tbinfo->relpersistence != owning_tab->relpersistence)
18351 20 : appendPQExpBuffer(query, " %s\n",
18352 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18353 : "UNLOGGED" : "LOGGED");
18354 : }
18355 : else
18356 : {
18357 482 : appendPQExpBuffer(query,
18358 : "CREATE %sSEQUENCE %s\n",
18359 482 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18360 : "UNLOGGED " : "",
18361 482 : fmtQualifiedDumpable(tbinfo));
18362 :
18363 482 : if (seq->seqtype != SEQTYPE_BIGINT)
18364 380 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
18365 : }
18366 :
18367 774 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
18368 :
18369 774 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
18370 :
18371 774 : if (seq->minv != default_minv)
18372 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
18373 : else
18374 744 : appendPQExpBufferStr(query, " NO MINVALUE\n");
18375 :
18376 774 : if (seq->maxv != default_maxv)
18377 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
18378 : else
18379 744 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
18380 :
18381 774 : appendPQExpBuffer(query,
18382 : " CACHE " INT64_FORMAT "%s",
18383 774 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
18384 :
18385 774 : if (tbinfo->is_identity_sequence)
18386 292 : appendPQExpBufferStr(query, "\n);\n");
18387 : else
18388 482 : appendPQExpBufferStr(query, ";\n");
18389 :
18390 : /* binary_upgrade: no need to clear TOAST table oid */
18391 :
18392 774 : if (dopt->binary_upgrade)
18393 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
18394 : "SEQUENCE", qseqname,
18395 132 : tbinfo->dobj.namespace->dobj.name);
18396 :
18397 774 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18398 774 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18399 774 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18400 : .namespace = tbinfo->dobj.namespace->dobj.name,
18401 : .owner = tbinfo->rolname,
18402 : .description = "SEQUENCE",
18403 : .section = SECTION_PRE_DATA,
18404 : .createStmt = query->data,
18405 : .dropStmt = delqry->data));
18406 :
18407 : /*
18408 : * If the sequence is owned by a table column, emit the ALTER for it as a
18409 : * separate TOC entry immediately following the sequence's own entry. It's
18410 : * OK to do this rather than using full sorting logic, because the
18411 : * dependency that tells us it's owned will have forced the table to be
18412 : * created first. We can't just include the ALTER in the TOC entry
18413 : * because it will fail if we haven't reassigned the sequence owner to
18414 : * match the table's owner.
18415 : *
18416 : * We need not schema-qualify the table reference because both sequence
18417 : * and table must be in the same schema.
18418 : */
18419 774 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
18420 : {
18421 290 : owning_tab = findTableByOid(tbinfo->owning_tab);
18422 :
18423 290 : if (owning_tab == NULL)
18424 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
18425 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
18426 :
18427 290 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
18428 : {
18429 286 : resetPQExpBuffer(query);
18430 286 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
18431 286 : fmtQualifiedDumpable(tbinfo));
18432 286 : appendPQExpBuffer(query, " OWNED BY %s",
18433 286 : fmtQualifiedDumpable(owning_tab));
18434 286 : appendPQExpBuffer(query, ".%s;\n",
18435 286 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18436 :
18437 286 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18438 286 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18439 286 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18440 : .namespace = tbinfo->dobj.namespace->dobj.name,
18441 : .owner = tbinfo->rolname,
18442 : .description = "SEQUENCE OWNED BY",
18443 : .section = SECTION_PRE_DATA,
18444 : .createStmt = query->data,
18445 : .deps = &(tbinfo->dobj.dumpId),
18446 : .nDeps = 1));
18447 : }
18448 : }
18449 :
18450 : /* Dump Sequence Comments and Security Labels */
18451 774 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18452 0 : dumpComment(fout, "SEQUENCE", qseqname,
18453 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18454 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18455 :
18456 774 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18457 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
18458 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18459 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18460 :
18461 774 : if (fout->remoteVersion < 100000)
18462 0 : pg_free(seq);
18463 774 : destroyPQExpBuffer(query);
18464 774 : destroyPQExpBuffer(delqry);
18465 774 : free(qseqname);
18466 774 : }
18467 :
18468 : /*
18469 : * dumpSequenceData
18470 : * write the data of one user-defined sequence
18471 : */
18472 : static void
18473 804 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
18474 : {
18475 804 : TableInfo *tbinfo = tdinfo->tdtable;
18476 : int64 last;
18477 : bool called;
18478 804 : PQExpBuffer query = createPQExpBuffer();
18479 :
18480 : /*
18481 : * For versions >= 18, the sequence information is gathered in the sorted
18482 : * array before any calls to dumpSequenceData(). See collectSequences()
18483 : * for more information.
18484 : *
18485 : * For older versions, we have to query the sequence relations
18486 : * individually.
18487 : */
18488 804 : if (fout->remoteVersion < 180000)
18489 : {
18490 : PGresult *res;
18491 :
18492 0 : appendPQExpBuffer(query,
18493 : "SELECT last_value, is_called FROM %s",
18494 0 : fmtQualifiedDumpable(tbinfo));
18495 :
18496 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18497 :
18498 0 : if (PQntuples(res) != 1)
18499 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18500 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18501 : PQntuples(res)),
18502 : tbinfo->dobj.name, PQntuples(res));
18503 :
18504 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18505 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18506 :
18507 0 : PQclear(res);
18508 : }
18509 : else
18510 : {
18511 804 : SequenceItem key = {0};
18512 : SequenceItem *entry;
18513 :
18514 : Assert(sequences);
18515 : Assert(tbinfo->dobj.catId.oid);
18516 :
18517 804 : key.oid = tbinfo->dobj.catId.oid;
18518 804 : entry = bsearch(&key, sequences, nsequences,
18519 : sizeof(SequenceItem), SequenceItemCmp);
18520 :
18521 804 : last = entry->last_value;
18522 804 : called = entry->is_called;
18523 : }
18524 :
18525 804 : resetPQExpBuffer(query);
18526 804 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18527 804 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18528 804 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18529 : last, (called ? "true" : "false"));
18530 :
18531 804 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18532 804 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18533 804 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18534 : .namespace = tbinfo->dobj.namespace->dobj.name,
18535 : .owner = tbinfo->rolname,
18536 : .description = "SEQUENCE SET",
18537 : .section = SECTION_DATA,
18538 : .createStmt = query->data,
18539 : .deps = &(tbinfo->dobj.dumpId),
18540 : .nDeps = 1));
18541 :
18542 804 : destroyPQExpBuffer(query);
18543 804 : }
18544 :
18545 : /*
18546 : * dumpTrigger
18547 : * write the declaration of one user-defined table trigger
18548 : */
18549 : static void
18550 1086 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18551 : {
18552 1086 : DumpOptions *dopt = fout->dopt;
18553 1086 : TableInfo *tbinfo = tginfo->tgtable;
18554 : PQExpBuffer query;
18555 : PQExpBuffer delqry;
18556 : PQExpBuffer trigprefix;
18557 : PQExpBuffer trigidentity;
18558 : char *qtabname;
18559 : char *tag;
18560 :
18561 : /* Do nothing if not dumping schema */
18562 1086 : if (!dopt->dumpSchema)
18563 62 : return;
18564 :
18565 1024 : query = createPQExpBuffer();
18566 1024 : delqry = createPQExpBuffer();
18567 1024 : trigprefix = createPQExpBuffer();
18568 1024 : trigidentity = createPQExpBuffer();
18569 :
18570 1024 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18571 :
18572 1024 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18573 1024 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18574 :
18575 1024 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18576 1024 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18577 :
18578 : /* Triggers can depend on extensions */
18579 1024 : append_depends_on_extension(fout, query, &tginfo->dobj,
18580 : "pg_catalog.pg_trigger", "TRIGGER",
18581 1024 : trigidentity->data);
18582 :
18583 1024 : if (tginfo->tgispartition)
18584 : {
18585 : Assert(tbinfo->ispartition);
18586 :
18587 : /*
18588 : * Partition triggers only appear here because their 'tgenabled' flag
18589 : * differs from its parent's. The trigger is created already, so
18590 : * remove the CREATE and replace it with an ALTER. (Clear out the
18591 : * DROP query too, so that pg_dump --create does not cause errors.)
18592 : */
18593 242 : resetPQExpBuffer(query);
18594 242 : resetPQExpBuffer(delqry);
18595 242 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18596 242 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18597 242 : fmtQualifiedDumpable(tbinfo));
18598 242 : switch (tginfo->tgenabled)
18599 : {
18600 84 : case 'f':
18601 : case 'D':
18602 84 : appendPQExpBufferStr(query, "DISABLE");
18603 84 : break;
18604 0 : case 't':
18605 : case 'O':
18606 0 : appendPQExpBufferStr(query, "ENABLE");
18607 0 : break;
18608 74 : case 'R':
18609 74 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18610 74 : break;
18611 84 : case 'A':
18612 84 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18613 84 : break;
18614 : }
18615 242 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18616 242 : fmtId(tginfo->dobj.name));
18617 : }
18618 782 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
18619 : {
18620 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18621 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18622 0 : fmtQualifiedDumpable(tbinfo));
18623 0 : switch (tginfo->tgenabled)
18624 : {
18625 0 : case 'D':
18626 : case 'f':
18627 0 : appendPQExpBufferStr(query, "DISABLE");
18628 0 : break;
18629 0 : case 'A':
18630 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18631 0 : break;
18632 0 : case 'R':
18633 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18634 0 : break;
18635 0 : default:
18636 0 : appendPQExpBufferStr(query, "ENABLE");
18637 0 : break;
18638 : }
18639 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18640 0 : fmtId(tginfo->dobj.name));
18641 : }
18642 :
18643 1024 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
18644 1024 : fmtId(tginfo->dobj.name));
18645 :
18646 1024 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
18647 :
18648 1024 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18649 1024 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
18650 1024 : ARCHIVE_OPTS(.tag = tag,
18651 : .namespace = tbinfo->dobj.namespace->dobj.name,
18652 : .owner = tbinfo->rolname,
18653 : .description = "TRIGGER",
18654 : .section = SECTION_POST_DATA,
18655 : .createStmt = query->data,
18656 : .dropStmt = delqry->data));
18657 :
18658 1024 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18659 0 : dumpComment(fout, trigprefix->data, qtabname,
18660 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18661 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
18662 :
18663 1024 : free(tag);
18664 1024 : destroyPQExpBuffer(query);
18665 1024 : destroyPQExpBuffer(delqry);
18666 1024 : destroyPQExpBuffer(trigprefix);
18667 1024 : destroyPQExpBuffer(trigidentity);
18668 1024 : free(qtabname);
18669 : }
18670 :
18671 : /*
18672 : * dumpEventTrigger
18673 : * write the declaration of one user-defined event trigger
18674 : */
18675 : static void
18676 92 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
18677 : {
18678 92 : DumpOptions *dopt = fout->dopt;
18679 : PQExpBuffer query;
18680 : PQExpBuffer delqry;
18681 : char *qevtname;
18682 :
18683 : /* Do nothing if not dumping schema */
18684 92 : if (!dopt->dumpSchema)
18685 12 : return;
18686 :
18687 80 : query = createPQExpBuffer();
18688 80 : delqry = createPQExpBuffer();
18689 :
18690 80 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
18691 :
18692 80 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
18693 80 : appendPQExpBufferStr(query, qevtname);
18694 80 : appendPQExpBufferStr(query, " ON ");
18695 80 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
18696 :
18697 80 : if (strcmp("", evtinfo->evttags) != 0)
18698 : {
18699 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
18700 10 : appendPQExpBufferStr(query, evtinfo->evttags);
18701 10 : appendPQExpBufferChar(query, ')');
18702 : }
18703 :
18704 80 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
18705 80 : appendPQExpBufferStr(query, evtinfo->evtfname);
18706 80 : appendPQExpBufferStr(query, "();\n");
18707 :
18708 80 : if (evtinfo->evtenabled != 'O')
18709 : {
18710 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
18711 : qevtname);
18712 0 : switch (evtinfo->evtenabled)
18713 : {
18714 0 : case 'D':
18715 0 : appendPQExpBufferStr(query, "DISABLE");
18716 0 : break;
18717 0 : case 'A':
18718 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18719 0 : break;
18720 0 : case 'R':
18721 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18722 0 : break;
18723 0 : default:
18724 0 : appendPQExpBufferStr(query, "ENABLE");
18725 0 : break;
18726 : }
18727 0 : appendPQExpBufferStr(query, ";\n");
18728 : }
18729 :
18730 80 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
18731 : qevtname);
18732 :
18733 80 : if (dopt->binary_upgrade)
18734 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
18735 : "EVENT TRIGGER", qevtname, NULL);
18736 :
18737 80 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18738 80 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
18739 80 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
18740 : .owner = evtinfo->evtowner,
18741 : .description = "EVENT TRIGGER",
18742 : .section = SECTION_POST_DATA,
18743 : .createStmt = query->data,
18744 : .dropStmt = delqry->data));
18745 :
18746 80 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18747 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
18748 : NULL, evtinfo->evtowner,
18749 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
18750 :
18751 80 : destroyPQExpBuffer(query);
18752 80 : destroyPQExpBuffer(delqry);
18753 80 : free(qevtname);
18754 : }
18755 :
18756 : /*
18757 : * dumpRule
18758 : * Dump a rule
18759 : */
18760 : static void
18761 2374 : dumpRule(Archive *fout, const RuleInfo *rinfo)
18762 : {
18763 2374 : DumpOptions *dopt = fout->dopt;
18764 2374 : TableInfo *tbinfo = rinfo->ruletable;
18765 : bool is_view;
18766 : PQExpBuffer query;
18767 : PQExpBuffer cmd;
18768 : PQExpBuffer delcmd;
18769 : PQExpBuffer ruleprefix;
18770 : char *qtabname;
18771 : PGresult *res;
18772 : char *tag;
18773 :
18774 : /* Do nothing if not dumping schema */
18775 2374 : if (!dopt->dumpSchema)
18776 132 : return;
18777 :
18778 : /*
18779 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18780 : * we do not want to dump it as a separate object.
18781 : */
18782 2242 : if (!rinfo->separate)
18783 1820 : return;
18784 :
18785 : /*
18786 : * If it's an ON SELECT rule, we want to print it as a view definition,
18787 : * instead of a rule.
18788 : */
18789 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18790 :
18791 422 : query = createPQExpBuffer();
18792 422 : cmd = createPQExpBuffer();
18793 422 : delcmd = createPQExpBuffer();
18794 422 : ruleprefix = createPQExpBuffer();
18795 :
18796 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18797 :
18798 422 : if (is_view)
18799 : {
18800 : PQExpBuffer result;
18801 :
18802 : /*
18803 : * We need OR REPLACE here because we'll be replacing a dummy view.
18804 : * Otherwise this should look largely like the regular view dump code.
18805 : */
18806 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18807 20 : fmtQualifiedDumpable(tbinfo));
18808 20 : if (nonemptyReloptions(tbinfo->reloptions))
18809 : {
18810 0 : appendPQExpBufferStr(cmd, " WITH (");
18811 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18812 0 : appendPQExpBufferChar(cmd, ')');
18813 : }
18814 20 : result = createViewAsClause(fout, tbinfo);
18815 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
18816 20 : destroyPQExpBuffer(result);
18817 20 : if (tbinfo->checkoption != NULL)
18818 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18819 : tbinfo->checkoption);
18820 20 : appendPQExpBufferStr(cmd, ";\n");
18821 : }
18822 : else
18823 : {
18824 : /* In the rule case, just print pg_get_ruledef's result verbatim */
18825 402 : appendPQExpBuffer(query,
18826 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18827 : rinfo->dobj.catId.oid);
18828 :
18829 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18830 :
18831 402 : if (PQntuples(res) != 1)
18832 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18833 : rinfo->dobj.name, tbinfo->dobj.name);
18834 :
18835 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18836 :
18837 402 : PQclear(res);
18838 : }
18839 :
18840 : /*
18841 : * Add the command to alter the rules replication firing semantics if it
18842 : * differs from the default.
18843 : */
18844 422 : if (rinfo->ev_enabled != 'O')
18845 : {
18846 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18847 30 : switch (rinfo->ev_enabled)
18848 : {
18849 0 : case 'A':
18850 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18851 0 : fmtId(rinfo->dobj.name));
18852 0 : break;
18853 0 : case 'R':
18854 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18855 0 : fmtId(rinfo->dobj.name));
18856 0 : break;
18857 30 : case 'D':
18858 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18859 30 : fmtId(rinfo->dobj.name));
18860 30 : break;
18861 : }
18862 392 : }
18863 :
18864 422 : if (is_view)
18865 : {
18866 : /*
18867 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18868 : * REPLACE VIEW to replace the rule with something with minimal
18869 : * dependencies.
18870 : */
18871 : PQExpBuffer result;
18872 :
18873 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18874 20 : fmtQualifiedDumpable(tbinfo));
18875 20 : result = createDummyViewAsClause(fout, tbinfo);
18876 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18877 20 : destroyPQExpBuffer(result);
18878 : }
18879 : else
18880 : {
18881 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
18882 402 : fmtId(rinfo->dobj.name));
18883 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
18884 402 : fmtQualifiedDumpable(tbinfo));
18885 : }
18886 :
18887 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
18888 422 : fmtId(rinfo->dobj.name));
18889 :
18890 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18891 :
18892 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18893 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18894 422 : ARCHIVE_OPTS(.tag = tag,
18895 : .namespace = tbinfo->dobj.namespace->dobj.name,
18896 : .owner = tbinfo->rolname,
18897 : .description = "RULE",
18898 : .section = SECTION_POST_DATA,
18899 : .createStmt = cmd->data,
18900 : .dropStmt = delcmd->data));
18901 :
18902 : /* Dump rule comments */
18903 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18904 0 : dumpComment(fout, ruleprefix->data, qtabname,
18905 0 : tbinfo->dobj.namespace->dobj.name,
18906 : tbinfo->rolname,
18907 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18908 :
18909 422 : free(tag);
18910 422 : destroyPQExpBuffer(query);
18911 422 : destroyPQExpBuffer(cmd);
18912 422 : destroyPQExpBuffer(delcmd);
18913 422 : destroyPQExpBuffer(ruleprefix);
18914 422 : free(qtabname);
18915 : }
18916 :
18917 : /*
18918 : * getExtensionMembership --- obtain extension membership data
18919 : *
18920 : * We need to identify objects that are extension members as soon as they're
18921 : * loaded, so that we can correctly determine whether they need to be dumped.
18922 : * Generally speaking, extension member objects will get marked as *not* to
18923 : * be dumped, as they will be recreated by the single CREATE EXTENSION
18924 : * command. However, in binary upgrade mode we still need to dump the members
18925 : * individually.
18926 : */
18927 : void
18928 356 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18929 : int numExtensions)
18930 : {
18931 : PQExpBuffer query;
18932 : PGresult *res;
18933 : int ntups,
18934 : i;
18935 : int i_classid,
18936 : i_objid,
18937 : i_refobjid;
18938 : ExtensionInfo *ext;
18939 :
18940 : /* Nothing to do if no extensions */
18941 356 : if (numExtensions == 0)
18942 0 : return;
18943 :
18944 356 : query = createPQExpBuffer();
18945 :
18946 : /* refclassid constraint is redundant but may speed the search */
18947 356 : appendPQExpBufferStr(query, "SELECT "
18948 : "classid, objid, refobjid "
18949 : "FROM pg_depend "
18950 : "WHERE refclassid = 'pg_extension'::regclass "
18951 : "AND deptype = 'e' "
18952 : "ORDER BY 3");
18953 :
18954 356 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18955 :
18956 356 : ntups = PQntuples(res);
18957 :
18958 356 : i_classid = PQfnumber(res, "classid");
18959 356 : i_objid = PQfnumber(res, "objid");
18960 356 : i_refobjid = PQfnumber(res, "refobjid");
18961 :
18962 : /*
18963 : * Since we ordered the SELECT by referenced ID, we can expect that
18964 : * multiple entries for the same extension will appear together; this
18965 : * saves on searches.
18966 : */
18967 356 : ext = NULL;
18968 :
18969 2980 : for (i = 0; i < ntups; i++)
18970 : {
18971 : CatalogId objId;
18972 : Oid extId;
18973 :
18974 2624 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18975 2624 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18976 2624 : extId = atooid(PQgetvalue(res, i, i_refobjid));
18977 :
18978 2624 : if (ext == NULL ||
18979 2268 : ext->dobj.catId.oid != extId)
18980 406 : ext = findExtensionByOid(extId);
18981 :
18982 2624 : if (ext == NULL)
18983 : {
18984 : /* shouldn't happen */
18985 0 : pg_log_warning("could not find referenced extension %u", extId);
18986 0 : continue;
18987 : }
18988 :
18989 2624 : recordExtensionMembership(objId, ext);
18990 : }
18991 :
18992 356 : PQclear(res);
18993 :
18994 356 : destroyPQExpBuffer(query);
18995 : }
18996 :
18997 : /*
18998 : * processExtensionTables --- deal with extension configuration tables
18999 : *
19000 : * There are two parts to this process:
19001 : *
19002 : * 1. Identify and create dump records for extension configuration tables.
19003 : *
19004 : * Extensions can mark tables as "configuration", which means that the user
19005 : * is able and expected to modify those tables after the extension has been
19006 : * loaded. For these tables, we dump out only the data- the structure is
19007 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19008 : * foreign keys, which brings us to-
19009 : *
19010 : * 2. Record FK dependencies between configuration tables.
19011 : *
19012 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19013 : * the data is loaded, we have to work out what the best order for reloading
19014 : * the data is, to avoid FK violations when the tables are restored. This is
19015 : * not perfect- we can't handle circular dependencies and if any exist they
19016 : * will cause an invalid dump to be produced (though at least all of the data
19017 : * is included for a user to manually restore). This is currently documented
19018 : * but perhaps we can provide a better solution in the future.
19019 : */
19020 : void
19021 354 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19022 : int numExtensions)
19023 : {
19024 354 : DumpOptions *dopt = fout->dopt;
19025 : PQExpBuffer query;
19026 : PGresult *res;
19027 : int ntups,
19028 : i;
19029 : int i_conrelid,
19030 : i_confrelid;
19031 :
19032 : /* Nothing to do if no extensions */
19033 354 : if (numExtensions == 0)
19034 0 : return;
19035 :
19036 : /*
19037 : * Identify extension configuration tables and create TableDataInfo
19038 : * objects for them, ensuring their data will be dumped even though the
19039 : * tables themselves won't be.
19040 : *
19041 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19042 : * user data in a configuration table is treated like schema data. This
19043 : * seems appropriate since system data in a config table would get
19044 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19045 : * list of extensions to be included, none of its data is dumped.
19046 : */
19047 758 : for (i = 0; i < numExtensions; i++)
19048 : {
19049 404 : ExtensionInfo *curext = &(extinfo[i]);
19050 404 : char *extconfig = curext->extconfig;
19051 404 : char *extcondition = curext->extcondition;
19052 404 : char **extconfigarray = NULL;
19053 404 : char **extconditionarray = NULL;
19054 404 : int nconfigitems = 0;
19055 404 : int nconditionitems = 0;
19056 :
19057 : /*
19058 : * Check if this extension is listed as to include in the dump. If
19059 : * not, any table data associated with it is discarded.
19060 : */
19061 404 : if (extension_include_oids.head != NULL &&
19062 16 : !simple_oid_list_member(&extension_include_oids,
19063 : curext->dobj.catId.oid))
19064 12 : continue;
19065 :
19066 : /*
19067 : * Check if this extension is listed as to exclude in the dump. If
19068 : * yes, any table data associated with it is discarded.
19069 : */
19070 404 : if (extension_exclude_oids.head != NULL &&
19071 8 : simple_oid_list_member(&extension_exclude_oids,
19072 : curext->dobj.catId.oid))
19073 4 : continue;
19074 :
19075 392 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19076 : {
19077 : int j;
19078 :
19079 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19080 0 : pg_fatal("could not parse %s array", "extconfig");
19081 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19082 0 : pg_fatal("could not parse %s array", "extcondition");
19083 40 : if (nconfigitems != nconditionitems)
19084 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19085 :
19086 120 : for (j = 0; j < nconfigitems; j++)
19087 : {
19088 : TableInfo *configtbl;
19089 80 : Oid configtbloid = atooid(extconfigarray[j]);
19090 80 : bool dumpobj =
19091 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19092 :
19093 80 : configtbl = findTableByOid(configtbloid);
19094 80 : if (configtbl == NULL)
19095 0 : continue;
19096 :
19097 : /*
19098 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19099 : * unless the table or its schema is explicitly included
19100 : */
19101 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19102 : {
19103 : /* check table explicitly requested */
19104 4 : if (table_include_oids.head != NULL &&
19105 0 : simple_oid_list_member(&table_include_oids,
19106 : configtbloid))
19107 0 : dumpobj = true;
19108 :
19109 : /* check table's schema explicitly requested */
19110 4 : if (configtbl->dobj.namespace->dobj.dump &
19111 : DUMP_COMPONENT_DATA)
19112 4 : dumpobj = true;
19113 : }
19114 :
19115 : /* check table excluded by an exclusion switch */
19116 88 : if (table_exclude_oids.head != NULL &&
19117 8 : simple_oid_list_member(&table_exclude_oids,
19118 : configtbloid))
19119 2 : dumpobj = false;
19120 :
19121 : /* check schema excluded by an exclusion switch */
19122 80 : if (simple_oid_list_member(&schema_exclude_oids,
19123 80 : configtbl->dobj.namespace->dobj.catId.oid))
19124 0 : dumpobj = false;
19125 :
19126 80 : if (dumpobj)
19127 : {
19128 78 : makeTableDataInfo(dopt, configtbl);
19129 78 : if (configtbl->dataObj != NULL)
19130 : {
19131 78 : if (strlen(extconditionarray[j]) > 0)
19132 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19133 : }
19134 : }
19135 : }
19136 : }
19137 392 : if (extconfigarray)
19138 40 : free(extconfigarray);
19139 392 : if (extconditionarray)
19140 40 : free(extconditionarray);
19141 : }
19142 :
19143 : /*
19144 : * Now that all the TableDataInfo objects have been created for all the
19145 : * extensions, check their FK dependencies and register them to try and
19146 : * dump the data out in an order that they can be restored in.
19147 : *
19148 : * Note that this is not a problem for user tables as their FKs are
19149 : * recreated after the data has been loaded.
19150 : */
19151 :
19152 354 : query = createPQExpBuffer();
19153 :
19154 354 : printfPQExpBuffer(query,
19155 : "SELECT conrelid, confrelid "
19156 : "FROM pg_constraint "
19157 : "JOIN pg_depend ON (objid = confrelid) "
19158 : "WHERE contype = 'f' "
19159 : "AND refclassid = 'pg_extension'::regclass "
19160 : "AND classid = 'pg_class'::regclass;");
19161 :
19162 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19163 354 : ntups = PQntuples(res);
19164 :
19165 354 : i_conrelid = PQfnumber(res, "conrelid");
19166 354 : i_confrelid = PQfnumber(res, "confrelid");
19167 :
19168 : /* Now get the dependencies and register them */
19169 354 : for (i = 0; i < ntups; i++)
19170 : {
19171 : Oid conrelid,
19172 : confrelid;
19173 : TableInfo *reftable,
19174 : *contable;
19175 :
19176 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19177 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19178 0 : contable = findTableByOid(conrelid);
19179 0 : reftable = findTableByOid(confrelid);
19180 :
19181 0 : if (reftable == NULL ||
19182 0 : reftable->dataObj == NULL ||
19183 0 : contable == NULL ||
19184 0 : contable->dataObj == NULL)
19185 0 : continue;
19186 :
19187 : /*
19188 : * Make referencing TABLE_DATA object depend on the referenced table's
19189 : * TABLE_DATA object.
19190 : */
19191 0 : addObjectDependency(&contable->dataObj->dobj,
19192 0 : reftable->dataObj->dobj.dumpId);
19193 : }
19194 354 : PQclear(res);
19195 354 : destroyPQExpBuffer(query);
19196 : }
19197 :
19198 : /*
19199 : * getDependencies --- obtain available dependency data
19200 : */
19201 : static void
19202 354 : getDependencies(Archive *fout)
19203 : {
19204 : PQExpBuffer query;
19205 : PGresult *res;
19206 : int ntups,
19207 : i;
19208 : int i_classid,
19209 : i_objid,
19210 : i_refclassid,
19211 : i_refobjid,
19212 : i_deptype;
19213 : DumpableObject *dobj,
19214 : *refdobj;
19215 :
19216 354 : pg_log_info("reading dependency data");
19217 :
19218 354 : query = createPQExpBuffer();
19219 :
19220 : /*
19221 : * Messy query to collect the dependency data we need. Note that we
19222 : * ignore the sub-object column, so that dependencies of or on a column
19223 : * look the same as dependencies of or on a whole table.
19224 : *
19225 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19226 : * already processed by getExtensionMembership.
19227 : */
19228 354 : appendPQExpBufferStr(query, "SELECT "
19229 : "classid, objid, refclassid, refobjid, deptype "
19230 : "FROM pg_depend "
19231 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19232 :
19233 : /*
19234 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19235 : * have to translate their dependencies into dependencies of their parent
19236 : * opfamily. Ignore internal dependencies though, as those will point to
19237 : * their parent opclass, which we needn't consider here (and if we did,
19238 : * it'd just result in circular dependencies). Also, "loose" opfamily
19239 : * entries will have dependencies on their parent opfamily, which we
19240 : * should drop since they'd likewise become useless self-dependencies.
19241 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19242 : */
19243 354 : appendPQExpBufferStr(query, "UNION ALL\n"
19244 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19245 : "FROM pg_depend d, pg_amop o "
19246 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19247 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19248 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19249 :
19250 : /* Likewise for pg_amproc entries */
19251 354 : appendPQExpBufferStr(query, "UNION ALL\n"
19252 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19253 : "FROM pg_depend d, pg_amproc p "
19254 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19255 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19256 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19257 :
19258 : /* Sort the output for efficiency below */
19259 354 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19260 :
19261 354 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19262 :
19263 354 : ntups = PQntuples(res);
19264 :
19265 354 : i_classid = PQfnumber(res, "classid");
19266 354 : i_objid = PQfnumber(res, "objid");
19267 354 : i_refclassid = PQfnumber(res, "refclassid");
19268 354 : i_refobjid = PQfnumber(res, "refobjid");
19269 354 : i_deptype = PQfnumber(res, "deptype");
19270 :
19271 : /*
19272 : * Since we ordered the SELECT by referencing ID, we can expect that
19273 : * multiple entries for the same object will appear together; this saves
19274 : * on searches.
19275 : */
19276 354 : dobj = NULL;
19277 :
19278 765112 : for (i = 0; i < ntups; i++)
19279 : {
19280 : CatalogId objId;
19281 : CatalogId refobjId;
19282 : char deptype;
19283 :
19284 764758 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19285 764758 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19286 764758 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19287 764758 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19288 764758 : deptype = *(PQgetvalue(res, i, i_deptype));
19289 :
19290 764758 : if (dobj == NULL ||
19291 715440 : dobj->catId.tableoid != objId.tableoid ||
19292 711262 : dobj->catId.oid != objId.oid)
19293 334862 : dobj = findObjectByCatalogId(objId);
19294 :
19295 : /*
19296 : * Failure to find objects mentioned in pg_depend is not unexpected,
19297 : * since for example we don't collect info about TOAST tables.
19298 : */
19299 764758 : if (dobj == NULL)
19300 : {
19301 : #ifdef NOT_USED
19302 : pg_log_warning("no referencing object %u %u",
19303 : objId.tableoid, objId.oid);
19304 : #endif
19305 50612 : continue;
19306 : }
19307 :
19308 715794 : refdobj = findObjectByCatalogId(refobjId);
19309 :
19310 715794 : if (refdobj == NULL)
19311 : {
19312 : #ifdef NOT_USED
19313 : pg_log_warning("no referenced object %u %u",
19314 : refobjId.tableoid, refobjId.oid);
19315 : #endif
19316 1648 : continue;
19317 : }
19318 :
19319 : /*
19320 : * For 'x' dependencies, mark the object for later; we still add the
19321 : * normal dependency, for possible ordering purposes. Currently
19322 : * pg_dump_sort.c knows to put extensions ahead of all object types
19323 : * that could possibly depend on them, but this is safer.
19324 : */
19325 714146 : if (deptype == 'x')
19326 88 : dobj->depends_on_ext = true;
19327 :
19328 : /*
19329 : * Ordinarily, table rowtypes have implicit dependencies on their
19330 : * tables. However, for a composite type the implicit dependency goes
19331 : * the other way in pg_depend; which is the right thing for DROP but
19332 : * it doesn't produce the dependency ordering we need. So in that one
19333 : * case, we reverse the direction of the dependency.
19334 : */
19335 714146 : if (deptype == 'i' &&
19336 198130 : dobj->objType == DO_TABLE &&
19337 2410 : refdobj->objType == DO_TYPE)
19338 372 : addObjectDependency(refdobj, dobj->dumpId);
19339 : else
19340 : /* normal case */
19341 713774 : addObjectDependency(dobj, refdobj->dumpId);
19342 : }
19343 :
19344 354 : PQclear(res);
19345 :
19346 354 : destroyPQExpBuffer(query);
19347 354 : }
19348 :
19349 :
19350 : /*
19351 : * createBoundaryObjects - create dummy DumpableObjects to represent
19352 : * dump section boundaries.
19353 : */
19354 : static DumpableObject *
19355 354 : createBoundaryObjects(void)
19356 : {
19357 : DumpableObject *dobjs;
19358 :
19359 354 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
19360 :
19361 354 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
19362 354 : dobjs[0].catId = nilCatalogId;
19363 354 : AssignDumpId(dobjs + 0);
19364 354 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
19365 :
19366 354 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
19367 354 : dobjs[1].catId = nilCatalogId;
19368 354 : AssignDumpId(dobjs + 1);
19369 354 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
19370 :
19371 354 : return dobjs;
19372 : }
19373 :
19374 : /*
19375 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
19376 : * section boundaries.
19377 : */
19378 : static void
19379 354 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
19380 : DumpableObject *boundaryObjs)
19381 : {
19382 354 : DumpableObject *preDataBound = boundaryObjs + 0;
19383 354 : DumpableObject *postDataBound = boundaryObjs + 1;
19384 : int i;
19385 :
19386 1305290 : for (i = 0; i < numObjs; i++)
19387 : {
19388 1304936 : DumpableObject *dobj = dobjs[i];
19389 :
19390 : /*
19391 : * The classification of object types here must match the SECTION_xxx
19392 : * values assigned during subsequent ArchiveEntry calls!
19393 : */
19394 1304936 : switch (dobj->objType)
19395 : {
19396 1212700 : case DO_NAMESPACE:
19397 : case DO_EXTENSION:
19398 : case DO_TYPE:
19399 : case DO_SHELL_TYPE:
19400 : case DO_FUNC:
19401 : case DO_AGG:
19402 : case DO_OPERATOR:
19403 : case DO_ACCESS_METHOD:
19404 : case DO_OPCLASS:
19405 : case DO_OPFAMILY:
19406 : case DO_COLLATION:
19407 : case DO_CONVERSION:
19408 : case DO_TABLE:
19409 : case DO_TABLE_ATTACH:
19410 : case DO_ATTRDEF:
19411 : case DO_PROCLANG:
19412 : case DO_CAST:
19413 : case DO_DUMMY_TYPE:
19414 : case DO_TSPARSER:
19415 : case DO_TSDICT:
19416 : case DO_TSTEMPLATE:
19417 : case DO_TSCONFIG:
19418 : case DO_FDW:
19419 : case DO_FOREIGN_SERVER:
19420 : case DO_TRANSFORM:
19421 : /* Pre-data objects: must come before the pre-data boundary */
19422 1212700 : addObjectDependency(preDataBound, dobj->dumpId);
19423 1212700 : break;
19424 9034 : case DO_TABLE_DATA:
19425 : case DO_SEQUENCE_SET:
19426 : case DO_LARGE_OBJECT:
19427 : case DO_LARGE_OBJECT_DATA:
19428 : /* Data objects: must come between the boundaries */
19429 9034 : addObjectDependency(dobj, preDataBound->dumpId);
19430 9034 : addObjectDependency(postDataBound, dobj->dumpId);
19431 9034 : break;
19432 11586 : case DO_INDEX:
19433 : case DO_INDEX_ATTACH:
19434 : case DO_STATSEXT:
19435 : case DO_REFRESH_MATVIEW:
19436 : case DO_TRIGGER:
19437 : case DO_EVENT_TRIGGER:
19438 : case DO_DEFAULT_ACL:
19439 : case DO_POLICY:
19440 : case DO_PUBLICATION:
19441 : case DO_PUBLICATION_REL:
19442 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
19443 : case DO_SUBSCRIPTION:
19444 : case DO_SUBSCRIPTION_REL:
19445 : /* Post-data objects: must come after the post-data boundary */
19446 11586 : addObjectDependency(dobj, postDataBound->dumpId);
19447 11586 : break;
19448 53766 : case DO_RULE:
19449 : /* Rules are post-data, but only if dumped separately */
19450 53766 : if (((RuleInfo *) dobj)->separate)
19451 1254 : addObjectDependency(dobj, postDataBound->dumpId);
19452 53766 : break;
19453 4954 : case DO_CONSTRAINT:
19454 : case DO_FK_CONSTRAINT:
19455 : /* Constraints are post-data, but only if dumped separately */
19456 4954 : if (((ConstraintInfo *) dobj)->separate)
19457 3556 : addObjectDependency(dobj, postDataBound->dumpId);
19458 4954 : break;
19459 354 : case DO_PRE_DATA_BOUNDARY:
19460 : /* nothing to do */
19461 354 : break;
19462 354 : case DO_POST_DATA_BOUNDARY:
19463 : /* must come after the pre-data boundary */
19464 354 : addObjectDependency(dobj, preDataBound->dumpId);
19465 354 : break;
19466 12188 : case DO_REL_STATS:
19467 : /* stats section varies by parent object type, DATA or POST */
19468 12188 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
19469 : {
19470 7530 : addObjectDependency(dobj, preDataBound->dumpId);
19471 7530 : addObjectDependency(postDataBound, dobj->dumpId);
19472 : }
19473 : else
19474 4658 : addObjectDependency(dobj, postDataBound->dumpId);
19475 12188 : break;
19476 : }
19477 1304936 : }
19478 354 : }
19479 :
19480 :
19481 : /*
19482 : * BuildArchiveDependencies - create dependency data for archive TOC entries
19483 : *
19484 : * The raw dependency data obtained by getDependencies() is not terribly
19485 : * useful in an archive dump, because in many cases there are dependency
19486 : * chains linking through objects that don't appear explicitly in the dump.
19487 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
19488 : * will depend on other objects --- but the rule will not appear as a separate
19489 : * object in the dump. We need to adjust the view's dependencies to include
19490 : * whatever the rule depends on that is included in the dump.
19491 : *
19492 : * Just to make things more complicated, there are also "special" dependencies
19493 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
19494 : * not rearrange because pg_restore knows that TABLE DATA only depends on
19495 : * its table. In these cases we must leave the dependencies strictly as-is
19496 : * even if they refer to not-to-be-dumped objects.
19497 : *
19498 : * To handle this, the convention is that "special" dependencies are created
19499 : * during ArchiveEntry calls, and an archive TOC item that has any such
19500 : * entries will not be touched here. Otherwise, we recursively search the
19501 : * DumpableObject data structures to build the correct dependencies for each
19502 : * archive TOC item.
19503 : */
19504 : static void
19505 96 : BuildArchiveDependencies(Archive *fout)
19506 : {
19507 96 : ArchiveHandle *AH = (ArchiveHandle *) fout;
19508 : TocEntry *te;
19509 :
19510 : /* Scan all TOC entries in the archive */
19511 13666 : for (te = AH->toc->next; te != AH->toc; te = te->next)
19512 : {
19513 : DumpableObject *dobj;
19514 : DumpId *dependencies;
19515 : int nDeps;
19516 : int allocDeps;
19517 :
19518 : /* No need to process entries that will not be dumped */
19519 13570 : if (te->reqs == 0)
19520 6584 : continue;
19521 : /* Ignore entries that already have "special" dependencies */
19522 13564 : if (te->nDeps > 0)
19523 5844 : continue;
19524 : /* Otherwise, look up the item's original DumpableObject, if any */
19525 7720 : dobj = findObjectByDumpId(te->dumpId);
19526 7720 : if (dobj == NULL)
19527 522 : continue;
19528 : /* No work if it has no dependencies */
19529 7198 : if (dobj->nDeps <= 0)
19530 212 : continue;
19531 : /* Set up work array */
19532 6986 : allocDeps = 64;
19533 6986 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19534 6986 : nDeps = 0;
19535 : /* Recursively find all dumpable dependencies */
19536 6986 : findDumpableDependencies(AH, dobj,
19537 : &dependencies, &nDeps, &allocDeps);
19538 : /* And save 'em ... */
19539 6986 : if (nDeps > 0)
19540 : {
19541 5330 : dependencies = (DumpId *) pg_realloc(dependencies,
19542 : nDeps * sizeof(DumpId));
19543 5330 : te->dependencies = dependencies;
19544 5330 : te->nDeps = nDeps;
19545 : }
19546 : else
19547 1656 : free(dependencies);
19548 : }
19549 96 : }
19550 :
19551 : /* Recursive search subroutine for BuildArchiveDependencies */
19552 : static void
19553 17104 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19554 : DumpId **dependencies, int *nDeps, int *allocDeps)
19555 : {
19556 : int i;
19557 :
19558 : /*
19559 : * Ignore section boundary objects: if we search through them, we'll
19560 : * report lots of bogus dependencies.
19561 : */
19562 17104 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19563 17066 : dobj->objType == DO_POST_DATA_BOUNDARY)
19564 2992 : return;
19565 :
19566 35574 : for (i = 0; i < dobj->nDeps; i++)
19567 : {
19568 21462 : DumpId depid = dobj->dependencies[i];
19569 :
19570 21462 : if (TocIDRequired(AH, depid) != 0)
19571 : {
19572 : /* Object will be dumped, so just reference it as a dependency */
19573 11344 : if (*nDeps >= *allocDeps)
19574 : {
19575 0 : *allocDeps *= 2;
19576 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
19577 0 : *allocDeps * sizeof(DumpId));
19578 : }
19579 11344 : (*dependencies)[*nDeps] = depid;
19580 11344 : (*nDeps)++;
19581 : }
19582 : else
19583 : {
19584 : /*
19585 : * Object will not be dumped, so recursively consider its deps. We
19586 : * rely on the assumption that sortDumpableObjects already broke
19587 : * any dependency loops, else we might recurse infinitely.
19588 : */
19589 10118 : DumpableObject *otherdobj = findObjectByDumpId(depid);
19590 :
19591 10118 : if (otherdobj)
19592 10118 : findDumpableDependencies(AH, otherdobj,
19593 : dependencies, nDeps, allocDeps);
19594 : }
19595 : }
19596 : }
19597 :
19598 :
19599 : /*
19600 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
19601 : * given type OID.
19602 : *
19603 : * This does not guarantee to schema-qualify the output, so it should not
19604 : * be used to create the target object name for CREATE or ALTER commands.
19605 : *
19606 : * Note that the result is cached and must not be freed by the caller.
19607 : */
19608 : static const char *
19609 4696 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
19610 : {
19611 : TypeInfo *typeInfo;
19612 : char *result;
19613 : PQExpBuffer query;
19614 : PGresult *res;
19615 :
19616 4696 : if (oid == 0)
19617 : {
19618 0 : if ((opts & zeroAsStar) != 0)
19619 0 : return "*";
19620 0 : else if ((opts & zeroAsNone) != 0)
19621 0 : return "NONE";
19622 : }
19623 :
19624 : /* see if we have the result cached in the type's TypeInfo record */
19625 4696 : typeInfo = findTypeByOid(oid);
19626 4696 : if (typeInfo && typeInfo->ftypname)
19627 3706 : return typeInfo->ftypname;
19628 :
19629 990 : query = createPQExpBuffer();
19630 990 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
19631 : oid);
19632 :
19633 990 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
19634 :
19635 : /* result of format_type is already quoted */
19636 990 : result = pg_strdup(PQgetvalue(res, 0, 0));
19637 :
19638 990 : PQclear(res);
19639 990 : destroyPQExpBuffer(query);
19640 :
19641 : /*
19642 : * Cache the result for re-use in later requests, if possible. If we
19643 : * don't have a TypeInfo for the type, the string will be leaked once the
19644 : * caller is done with it ... but that case really should not happen, so
19645 : * leaking if it does seems acceptable.
19646 : */
19647 990 : if (typeInfo)
19648 990 : typeInfo->ftypname = result;
19649 :
19650 990 : return result;
19651 : }
19652 :
19653 : /*
19654 : * Return a column list clause for the given relation.
19655 : *
19656 : * Special case: if there are no undropped columns in the relation, return
19657 : * "", not an invalid "()" column list.
19658 : */
19659 : static const char *
19660 15404 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
19661 : {
19662 15404 : int numatts = ti->numatts;
19663 15404 : char **attnames = ti->attnames;
19664 15404 : bool *attisdropped = ti->attisdropped;
19665 15404 : char *attgenerated = ti->attgenerated;
19666 : bool needComma;
19667 : int i;
19668 :
19669 15404 : appendPQExpBufferChar(buffer, '(');
19670 15404 : needComma = false;
19671 77124 : for (i = 0; i < numatts; i++)
19672 : {
19673 61720 : if (attisdropped[i])
19674 1208 : continue;
19675 60512 : if (attgenerated[i])
19676 2272 : continue;
19677 58240 : if (needComma)
19678 43320 : appendPQExpBufferStr(buffer, ", ");
19679 58240 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
19680 58240 : needComma = true;
19681 : }
19682 :
19683 15404 : if (!needComma)
19684 484 : return ""; /* no undropped columns */
19685 :
19686 14920 : appendPQExpBufferChar(buffer, ')');
19687 14920 : return buffer->data;
19688 : }
19689 :
19690 : /*
19691 : * Check if a reloptions array is nonempty.
19692 : */
19693 : static bool
19694 26338 : nonemptyReloptions(const char *reloptions)
19695 : {
19696 : /* Don't want to print it if it's just "{}" */
19697 26338 : return (reloptions != NULL && strlen(reloptions) > 2);
19698 : }
19699 :
19700 : /*
19701 : * Format a reloptions array and append it to the given buffer.
19702 : *
19703 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
19704 : */
19705 : static void
19706 432 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
19707 : const char *prefix, Archive *fout)
19708 : {
19709 : bool res;
19710 :
19711 432 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
19712 432 : fout->std_strings);
19713 432 : if (!res)
19714 0 : pg_log_warning("could not parse %s array", "reloptions");
19715 432 : }
19716 :
19717 : /*
19718 : * read_dump_filters - retrieve object identifier patterns from file
19719 : *
19720 : * Parse the specified filter file for include and exclude patterns, and add
19721 : * them to the relevant lists. If the filename is "-" then filters will be
19722 : * read from STDIN rather than a file.
19723 : */
19724 : static void
19725 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
19726 : {
19727 : FilterStateData fstate;
19728 : char *objname;
19729 : FilterCommandType comtype;
19730 : FilterObjectType objtype;
19731 :
19732 52 : filter_init(&fstate, filename, exit_nicely);
19733 :
19734 116 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
19735 : {
19736 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
19737 : {
19738 34 : switch (objtype)
19739 : {
19740 0 : case FILTER_OBJECT_TYPE_NONE:
19741 0 : break;
19742 0 : case FILTER_OBJECT_TYPE_DATABASE:
19743 : case FILTER_OBJECT_TYPE_FUNCTION:
19744 : case FILTER_OBJECT_TYPE_INDEX:
19745 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19746 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19747 : case FILTER_OBJECT_TYPE_TRIGGER:
19748 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19749 : "include",
19750 : filter_object_type_name(objtype));
19751 0 : exit_nicely(1);
19752 : break; /* unreachable */
19753 :
19754 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19755 2 : simple_string_list_append(&extension_include_patterns, objname);
19756 2 : break;
19757 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19758 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
19759 2 : break;
19760 2 : case FILTER_OBJECT_TYPE_SCHEMA:
19761 2 : simple_string_list_append(&schema_include_patterns, objname);
19762 2 : dopt->include_everything = false;
19763 2 : break;
19764 26 : case FILTER_OBJECT_TYPE_TABLE:
19765 26 : simple_string_list_append(&table_include_patterns, objname);
19766 26 : dopt->include_everything = false;
19767 26 : break;
19768 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19769 2 : simple_string_list_append(&table_include_patterns_and_children,
19770 : objname);
19771 2 : dopt->include_everything = false;
19772 2 : break;
19773 : }
19774 34 : }
19775 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
19776 : {
19777 18 : switch (objtype)
19778 : {
19779 0 : case FILTER_OBJECT_TYPE_NONE:
19780 0 : break;
19781 2 : case FILTER_OBJECT_TYPE_DATABASE:
19782 : case FILTER_OBJECT_TYPE_FUNCTION:
19783 : case FILTER_OBJECT_TYPE_INDEX:
19784 : case FILTER_OBJECT_TYPE_TRIGGER:
19785 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19786 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19787 : "exclude",
19788 : filter_object_type_name(objtype));
19789 2 : exit_nicely(1);
19790 : break;
19791 :
19792 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19793 2 : simple_string_list_append(&extension_exclude_patterns, objname);
19794 2 : break;
19795 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19796 2 : simple_string_list_append(&tabledata_exclude_patterns,
19797 : objname);
19798 2 : break;
19799 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19800 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
19801 : objname);
19802 2 : break;
19803 4 : case FILTER_OBJECT_TYPE_SCHEMA:
19804 4 : simple_string_list_append(&schema_exclude_patterns, objname);
19805 4 : break;
19806 4 : case FILTER_OBJECT_TYPE_TABLE:
19807 4 : simple_string_list_append(&table_exclude_patterns, objname);
19808 4 : break;
19809 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19810 2 : simple_string_list_append(&table_exclude_patterns_and_children,
19811 : objname);
19812 2 : break;
19813 : }
19814 16 : }
19815 : else
19816 : {
19817 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19818 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19819 : }
19820 :
19821 64 : if (objname)
19822 50 : free(objname);
19823 : }
19824 :
19825 44 : filter_free(&fstate);
19826 44 : }
|