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 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
213 : #define MAX_ATTR_STATS_RELS 64
214 :
215 : /*
216 : * The default number of rows per INSERT when
217 : * --inserts is specified without --rows-per-insert
218 : */
219 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
220 :
221 : /*
222 : * Maximum number of large objects to group into a single ArchiveEntry.
223 : * At some point we might want to make this user-controllable, but for now
224 : * a hard-wired setting will suffice.
225 : */
226 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
227 :
228 : /*
229 : * Macro for producing quoted, schema-qualified name of a dumpable object.
230 : */
231 : #define fmtQualifiedDumpable(obj) \
232 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
233 : (obj)->dobj.name)
234 :
235 : static void help(const char *progname);
236 : static void setup_connection(Archive *AH,
237 : const char *dumpencoding, const char *dumpsnapshot,
238 : char *use_role);
239 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
240 : static void expand_schema_name_patterns(Archive *fout,
241 : SimpleStringList *patterns,
242 : SimpleOidList *oids,
243 : bool strict_names);
244 : static void expand_extension_name_patterns(Archive *fout,
245 : SimpleStringList *patterns,
246 : SimpleOidList *oids,
247 : bool strict_names);
248 : static void expand_foreign_server_name_patterns(Archive *fout,
249 : SimpleStringList *patterns,
250 : SimpleOidList *oids);
251 : static void expand_table_name_patterns(Archive *fout,
252 : SimpleStringList *patterns,
253 : SimpleOidList *oids,
254 : bool strict_names,
255 : bool with_child_tables);
256 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
257 : const char *pattern);
258 :
259 : static NamespaceInfo *findNamespace(Oid nsoid);
260 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
261 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
262 : static const char *getRoleName(const char *roleoid_str);
263 : static void collectRoleNames(Archive *fout);
264 : static void getAdditionalACLs(Archive *fout);
265 : static void dumpCommentExtended(Archive *fout, const char *type,
266 : const char *name, const char *namespace,
267 : const char *owner, CatalogId catalogId,
268 : int subid, DumpId dumpId,
269 : const char *initdb_comment);
270 : static inline void dumpComment(Archive *fout, const char *type,
271 : const char *name, const char *namespace,
272 : const char *owner, CatalogId catalogId,
273 : int subid, DumpId dumpId);
274 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
275 : static void collectComments(Archive *fout);
276 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
277 : const char *namespace, const char *owner,
278 : CatalogId catalogId, int subid, DumpId dumpId);
279 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
280 : static void collectSecLabels(Archive *fout);
281 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
282 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
283 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
284 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
285 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
286 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
287 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
288 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
289 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
290 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
291 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
292 : PGresult *res);
293 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
294 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
295 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
296 : static void dumpCast(Archive *fout, const CastInfo *cast);
297 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
298 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
299 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
300 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
301 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
302 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
303 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
304 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
305 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
306 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
307 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
308 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
309 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
310 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
311 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
312 : static void collectSequences(Archive *fout);
313 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
314 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
315 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
316 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
317 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
318 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
319 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
320 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
321 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
322 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
323 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
324 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
325 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
326 : static void dumpUserMappings(Archive *fout,
327 : const char *servername, const char *namespace,
328 : const char *owner, CatalogId catalogId, DumpId dumpId);
329 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
330 :
331 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
332 : const char *type, const char *name, const char *subname,
333 : const char *nspname, const char *tag, const char *owner,
334 : const DumpableAcl *dacl);
335 :
336 : static void getDependencies(Archive *fout);
337 : static void BuildArchiveDependencies(Archive *fout);
338 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
339 : DumpId **dependencies, int *nDeps, int *allocDeps);
340 :
341 : static DumpableObject *createBoundaryObjects(void);
342 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
343 : DumpableObject *boundaryObjs);
344 :
345 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
346 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
347 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
348 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
349 : static void buildMatViewRefreshDependencies(Archive *fout);
350 : static void getTableDataFKConstraints(void);
351 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
352 : TableInfo *tbinfo, int j,
353 : int i_notnull_name,
354 : int i_notnull_comment,
355 : int i_notnull_invalidoid,
356 : int i_notnull_noinherit,
357 : int i_notnull_islocal,
358 : PQExpBuffer *invalidnotnulloids);
359 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
360 : bool is_agg);
361 : static char *format_function_signature(Archive *fout,
362 : const FuncInfo *finfo, bool honor_quotes);
363 : static char *convertRegProcReference(const char *proc);
364 : static char *getFormattedOperatorName(const char *oproid);
365 : static char *convertTSFunction(Archive *fout, Oid funcOid);
366 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
367 : static void getLOs(Archive *fout);
368 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
369 : static int dumpLOs(Archive *fout, const void *arg);
370 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
371 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
372 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
373 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
374 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
375 : static void dumpDatabase(Archive *fout);
376 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
377 : const char *dbname, Oid dboid);
378 : static void dumpEncoding(Archive *AH);
379 : static void dumpStdStrings(Archive *AH);
380 : static void dumpSearchPath(Archive *AH);
381 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
382 : PQExpBuffer upgrade_buffer,
383 : Oid pg_type_oid,
384 : bool force_array_type,
385 : bool include_multirange_type);
386 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
387 : PQExpBuffer upgrade_buffer,
388 : const TableInfo *tbinfo);
389 : static void collectBinaryUpgradeClassOids(Archive *fout);
390 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
391 : PQExpBuffer upgrade_buffer,
392 : Oid pg_class_oid);
393 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
394 : const DumpableObject *dobj,
395 : const char *objtype,
396 : const char *objname,
397 : const char *objnamespace);
398 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
399 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
400 : static bool nonemptyReloptions(const char *reloptions);
401 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
402 : const char *prefix, Archive *fout);
403 : static char *get_synchronized_snapshot(Archive *fout);
404 : static void set_restrict_relation_kind(Archive *AH, const char *value);
405 : static void setupDumpWorker(Archive *AH);
406 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
407 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
408 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
409 :
410 :
411 : int
412 696 : main(int argc, char **argv)
413 : {
414 : int c;
415 696 : const char *filename = NULL;
416 696 : const char *format = "p";
417 : TableInfo *tblinfo;
418 : int numTables;
419 : DumpableObject **dobjs;
420 : int numObjs;
421 : DumpableObject *boundaryObjs;
422 : int i;
423 : int optindex;
424 : RestoreOptions *ropt;
425 : Archive *fout; /* the script file */
426 696 : bool g_verbose = false;
427 696 : const char *dumpencoding = NULL;
428 696 : const char *dumpsnapshot = NULL;
429 696 : char *use_role = NULL;
430 696 : int numWorkers = 1;
431 696 : int plainText = 0;
432 696 : ArchiveFormat archiveFormat = archUnknown;
433 : ArchiveMode archiveMode;
434 696 : pg_compress_specification compression_spec = {0};
435 696 : char *compression_detail = NULL;
436 696 : char *compression_algorithm_str = "none";
437 696 : char *error_detail = NULL;
438 696 : bool user_compression_defined = false;
439 696 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
440 696 : bool data_only = false;
441 696 : bool schema_only = false;
442 696 : bool statistics_only = false;
443 696 : bool with_data = false;
444 696 : bool with_schema = false;
445 696 : bool with_statistics = false;
446 696 : bool no_data = false;
447 696 : bool no_schema = false;
448 696 : bool no_statistics = false;
449 :
450 : static DumpOptions dopt;
451 :
452 : static struct option long_options[] = {
453 : {"data-only", no_argument, NULL, 'a'},
454 : {"blobs", no_argument, NULL, 'b'},
455 : {"large-objects", no_argument, NULL, 'b'},
456 : {"no-blobs", no_argument, NULL, 'B'},
457 : {"no-large-objects", no_argument, NULL, 'B'},
458 : {"clean", no_argument, NULL, 'c'},
459 : {"create", no_argument, NULL, 'C'},
460 : {"dbname", required_argument, NULL, 'd'},
461 : {"extension", required_argument, NULL, 'e'},
462 : {"file", required_argument, NULL, 'f'},
463 : {"format", required_argument, NULL, 'F'},
464 : {"host", required_argument, NULL, 'h'},
465 : {"jobs", 1, NULL, 'j'},
466 : {"no-reconnect", no_argument, NULL, 'R'},
467 : {"no-owner", no_argument, NULL, 'O'},
468 : {"port", required_argument, NULL, 'p'},
469 : {"schema", required_argument, NULL, 'n'},
470 : {"exclude-schema", required_argument, NULL, 'N'},
471 : {"schema-only", no_argument, NULL, 's'},
472 : {"superuser", required_argument, NULL, 'S'},
473 : {"table", required_argument, NULL, 't'},
474 : {"exclude-table", required_argument, NULL, 'T'},
475 : {"no-password", no_argument, NULL, 'w'},
476 : {"password", no_argument, NULL, 'W'},
477 : {"username", required_argument, NULL, 'U'},
478 : {"verbose", no_argument, NULL, 'v'},
479 : {"no-privileges", no_argument, NULL, 'x'},
480 : {"no-acl", no_argument, NULL, 'x'},
481 : {"compress", required_argument, NULL, 'Z'},
482 : {"encoding", required_argument, NULL, 'E'},
483 : {"help", no_argument, NULL, '?'},
484 : {"version", no_argument, NULL, 'V'},
485 :
486 : /*
487 : * the following options don't have an equivalent short option letter
488 : */
489 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
490 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
491 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
492 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
493 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
494 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
495 : {"exclude-table-data", required_argument, NULL, 4},
496 : {"extra-float-digits", required_argument, NULL, 8},
497 : {"if-exists", no_argument, &dopt.if_exists, 1},
498 : {"inserts", no_argument, NULL, 9},
499 : {"lock-wait-timeout", required_argument, NULL, 2},
500 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
501 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
502 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
503 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
504 : {"role", required_argument, NULL, 3},
505 : {"section", required_argument, NULL, 5},
506 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
507 : {"snapshot", required_argument, NULL, 6},
508 : {"statistics-only", no_argument, NULL, 18},
509 : {"strict-names", no_argument, &strict_names, 1},
510 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
511 : {"no-comments", no_argument, &dopt.no_comments, 1},
512 : {"no-data", no_argument, NULL, 19},
513 : {"no-policies", no_argument, &dopt.no_policies, 1},
514 : {"no-publications", no_argument, &dopt.no_publications, 1},
515 : {"no-schema", no_argument, NULL, 20},
516 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
517 : {"no-statistics", no_argument, NULL, 21},
518 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
519 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
520 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
521 : {"no-sync", no_argument, NULL, 7},
522 : {"with-data", no_argument, NULL, 22},
523 : {"with-schema", no_argument, NULL, 23},
524 : {"with-statistics", no_argument, NULL, 24},
525 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
526 : {"rows-per-insert", required_argument, NULL, 10},
527 : {"include-foreign-data", required_argument, NULL, 11},
528 : {"table-and-children", required_argument, NULL, 12},
529 : {"exclude-table-and-children", required_argument, NULL, 13},
530 : {"exclude-table-data-and-children", required_argument, NULL, 14},
531 : {"sync-method", required_argument, NULL, 15},
532 : {"filter", required_argument, NULL, 16},
533 : {"exclude-extension", required_argument, NULL, 17},
534 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
535 :
536 : {NULL, 0, NULL, 0}
537 : };
538 :
539 696 : pg_logging_init(argv[0]);
540 696 : pg_logging_set_level(PG_LOG_WARNING);
541 696 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
542 :
543 : /*
544 : * Initialize what we need for parallel execution, especially for thread
545 : * support on Windows.
546 : */
547 696 : init_parallel_dump_utils();
548 :
549 696 : progname = get_progname(argv[0]);
550 :
551 696 : if (argc > 1)
552 : {
553 696 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
554 : {
555 2 : help(progname);
556 2 : exit_nicely(0);
557 : }
558 694 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
559 : {
560 138 : puts("pg_dump (PostgreSQL) " PG_VERSION);
561 138 : exit_nicely(0);
562 : }
563 : }
564 :
565 556 : InitDumpOptions(&dopt);
566 :
567 2780 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
568 2780 : long_options, &optindex)) != -1)
569 : {
570 2240 : switch (c)
571 : {
572 18 : case 'a': /* Dump data only */
573 18 : data_only = true;
574 18 : break;
575 :
576 2 : case 'b': /* Dump LOs */
577 2 : dopt.outputLOs = true;
578 2 : break;
579 :
580 4 : case 'B': /* Don't dump LOs */
581 4 : dopt.dontOutputLOs = true;
582 4 : break;
583 :
584 12 : case 'c': /* clean (i.e., drop) schema prior to create */
585 12 : dopt.outputClean = 1;
586 12 : break;
587 :
588 140 : case 'C': /* Create DB */
589 140 : dopt.outputCreateDB = 1;
590 140 : break;
591 :
592 16 : case 'd': /* database name */
593 16 : dopt.cparams.dbname = pg_strdup(optarg);
594 16 : break;
595 :
596 8 : case 'e': /* include extension(s) */
597 8 : simple_string_list_append(&extension_include_patterns, optarg);
598 8 : dopt.include_everything = false;
599 8 : break;
600 :
601 4 : case 'E': /* Dump encoding */
602 4 : dumpencoding = pg_strdup(optarg);
603 4 : break;
604 :
605 466 : case 'f':
606 466 : filename = pg_strdup(optarg);
607 466 : break;
608 :
609 314 : case 'F':
610 314 : format = pg_strdup(optarg);
611 314 : break;
612 :
613 58 : case 'h': /* server host */
614 58 : dopt.cparams.pghost = pg_strdup(optarg);
615 58 : break;
616 :
617 24 : case 'j': /* number of dump jobs */
618 24 : if (!option_parse_int(optarg, "-j/--jobs", 1,
619 : PG_MAX_JOBS,
620 : &numWorkers))
621 2 : exit_nicely(1);
622 22 : break;
623 :
624 34 : case 'n': /* include schema(s) */
625 34 : simple_string_list_append(&schema_include_patterns, optarg);
626 34 : dopt.include_everything = false;
627 34 : break;
628 :
629 2 : case 'N': /* exclude schema(s) */
630 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
631 2 : break;
632 :
633 4 : case 'O': /* Don't reconnect to match owner */
634 4 : dopt.outputNoOwner = 1;
635 4 : break;
636 :
637 134 : case 'p': /* server port */
638 134 : dopt.cparams.pgport = pg_strdup(optarg);
639 134 : break;
640 :
641 4 : case 'R':
642 : /* no-op, still accepted for backwards compatibility */
643 4 : break;
644 :
645 14 : case 's': /* dump schema only */
646 14 : schema_only = true;
647 14 : break;
648 :
649 2 : case 'S': /* Username for superuser in plain text output */
650 2 : dopt.outputSuperuser = pg_strdup(optarg);
651 2 : break;
652 :
653 16 : case 't': /* include table(s) */
654 16 : simple_string_list_append(&table_include_patterns, optarg);
655 16 : dopt.include_everything = false;
656 16 : break;
657 :
658 8 : case 'T': /* exclude table(s) */
659 8 : simple_string_list_append(&table_exclude_patterns, optarg);
660 8 : break;
661 :
662 62 : case 'U':
663 62 : dopt.cparams.username = pg_strdup(optarg);
664 62 : break;
665 :
666 12 : case 'v': /* verbose */
667 12 : g_verbose = true;
668 12 : pg_logging_increase_verbosity();
669 12 : break;
670 :
671 2 : case 'w':
672 2 : dopt.cparams.promptPassword = TRI_NO;
673 2 : break;
674 :
675 0 : case 'W':
676 0 : dopt.cparams.promptPassword = TRI_YES;
677 0 : break;
678 :
679 4 : case 'x': /* skip ACL dump */
680 4 : dopt.aclsSkip = true;
681 4 : break;
682 :
683 24 : case 'Z': /* Compression */
684 24 : parse_compress_options(optarg, &compression_algorithm_str,
685 : &compression_detail);
686 24 : user_compression_defined = true;
687 24 : break;
688 :
689 226 : case 0:
690 : /* This covers the long options. */
691 226 : break;
692 :
693 4 : case 2: /* lock-wait-timeout */
694 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
695 4 : break;
696 :
697 6 : case 3: /* SET ROLE */
698 6 : use_role = pg_strdup(optarg);
699 6 : break;
700 :
701 2 : case 4: /* exclude table(s) data */
702 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
703 2 : break;
704 :
705 12 : case 5: /* section */
706 12 : set_dump_section(optarg, &dopt.dumpSections);
707 12 : break;
708 :
709 0 : case 6: /* snapshot */
710 0 : dumpsnapshot = pg_strdup(optarg);
711 0 : break;
712 :
713 274 : case 7: /* no-sync */
714 274 : dosync = false;
715 274 : break;
716 :
717 2 : case 8:
718 2 : have_extra_float_digits = true;
719 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
720 : &extra_float_digits))
721 2 : exit_nicely(1);
722 0 : break;
723 :
724 4 : case 9: /* inserts */
725 :
726 : /*
727 : * dump_inserts also stores --rows-per-insert, careful not to
728 : * overwrite that.
729 : */
730 4 : if (dopt.dump_inserts == 0)
731 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
732 4 : break;
733 :
734 4 : case 10: /* rows per insert */
735 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
736 : &dopt.dump_inserts))
737 2 : exit_nicely(1);
738 2 : break;
739 :
740 8 : case 11: /* include foreign data */
741 8 : simple_string_list_append(&foreign_servers_include_patterns,
742 : optarg);
743 8 : break;
744 :
745 2 : case 12: /* include table(s) and their children */
746 2 : simple_string_list_append(&table_include_patterns_and_children,
747 : optarg);
748 2 : dopt.include_everything = false;
749 2 : break;
750 :
751 2 : case 13: /* exclude table(s) and their children */
752 2 : simple_string_list_append(&table_exclude_patterns_and_children,
753 : optarg);
754 2 : break;
755 :
756 2 : case 14: /* exclude data of table(s) and children */
757 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
758 : optarg);
759 2 : break;
760 :
761 0 : case 15:
762 0 : if (!parse_sync_method(optarg, &sync_method))
763 0 : exit_nicely(1);
764 0 : break;
765 :
766 52 : case 16: /* read object filters from file */
767 52 : read_dump_filters(optarg, &dopt);
768 44 : break;
769 :
770 2 : case 17: /* exclude extension(s) */
771 2 : simple_string_list_append(&extension_exclude_patterns,
772 : optarg);
773 2 : break;
774 :
775 8 : case 18:
776 8 : statistics_only = true;
777 8 : break;
778 :
779 62 : case 19:
780 62 : no_data = true;
781 62 : break;
782 :
783 4 : case 20:
784 4 : no_schema = true;
785 4 : break;
786 :
787 16 : case 21:
788 16 : no_statistics = true;
789 16 : break;
790 :
791 0 : case 22:
792 0 : with_data = true;
793 0 : break;
794 :
795 0 : case 23:
796 0 : with_schema = true;
797 0 : break;
798 :
799 158 : case 24:
800 158 : with_statistics = true;
801 158 : break;
802 :
803 2 : default:
804 : /* getopt_long already emitted a complaint */
805 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
806 2 : exit_nicely(1);
807 : }
808 : }
809 :
810 : /*
811 : * Non-option argument specifies database name as long as it wasn't
812 : * already specified with -d / --dbname
813 : */
814 540 : if (optind < argc && dopt.cparams.dbname == NULL)
815 464 : dopt.cparams.dbname = argv[optind++];
816 :
817 : /* Complain if any arguments remain */
818 540 : if (optind < argc)
819 : {
820 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
821 : argv[optind]);
822 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
823 2 : exit_nicely(1);
824 : }
825 :
826 : /* --column-inserts implies --inserts */
827 538 : if (dopt.column_inserts && dopt.dump_inserts == 0)
828 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
829 :
830 : /* reject conflicting "-only" options */
831 538 : if (data_only && schema_only)
832 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
833 536 : if (schema_only && statistics_only)
834 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
835 534 : if (data_only && statistics_only)
836 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
837 :
838 : /* reject conflicting "-only" and "no-" options */
839 532 : if (data_only && no_data)
840 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
841 532 : if (schema_only && no_schema)
842 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
843 532 : if (statistics_only && no_statistics)
844 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
845 :
846 : /* reject conflicting "with-" and "no-" options */
847 530 : if (with_data && no_data)
848 0 : pg_fatal("options --with-data and --no-data cannot be used together");
849 530 : if (with_schema && no_schema)
850 0 : pg_fatal("options --with-schema and --no-schema cannot be used together");
851 530 : if (with_statistics && no_statistics)
852 0 : pg_fatal("options --with-statistics and --no-statistics cannot be used together");
853 :
854 530 : if (schema_only && foreign_servers_include_patterns.head != NULL)
855 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
856 :
857 528 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
858 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
859 :
860 526 : if (data_only && dopt.outputClean)
861 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
862 :
863 524 : if (dopt.if_exists && !dopt.outputClean)
864 2 : pg_fatal("option --if-exists requires option -c/--clean");
865 :
866 : /*
867 : * Set derivative flags. An "-only" option may be overridden by an
868 : * explicit "with-" option; e.g. "--schema-only --with-statistics" will
869 : * include schema and statistics. Other ambiguous or nonsensical
870 : * combinations, e.g. "--schema-only --no-schema", will have already
871 : * caused an error in one of the checks above.
872 : */
873 522 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
874 1044 : (data_only || with_data)) && !no_data;
875 522 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
876 1044 : (schema_only || with_schema)) && !no_schema;
877 522 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
878 1044 : (statistics_only || with_statistics)) && !no_statistics;
879 :
880 :
881 : /*
882 : * --inserts are already implied above if --column-inserts or
883 : * --rows-per-insert were specified.
884 : */
885 522 : if (dopt.do_nothing && dopt.dump_inserts == 0)
886 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
887 :
888 : /* Identify archive format to emit */
889 520 : archiveFormat = parseArchiveFormat(format, &archiveMode);
890 :
891 : /* archiveFormat specific setup */
892 518 : if (archiveFormat == archNull)
893 310 : plainText = 1;
894 :
895 : /*
896 : * Custom and directory formats are compressed by default with gzip when
897 : * available, not the others. If gzip is not available, no compression is
898 : * done by default.
899 : */
900 518 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
901 186 : !user_compression_defined)
902 : {
903 : #ifdef HAVE_LIBZ
904 176 : compression_algorithm_str = "gzip";
905 : #else
906 : compression_algorithm_str = "none";
907 : #endif
908 : }
909 :
910 : /*
911 : * Compression options
912 : */
913 518 : if (!parse_compress_algorithm(compression_algorithm_str,
914 : &compression_algorithm))
915 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
916 : compression_algorithm_str);
917 :
918 516 : parse_compress_specification(compression_algorithm, compression_detail,
919 : &compression_spec);
920 516 : error_detail = validate_compress_specification(&compression_spec);
921 516 : if (error_detail != NULL)
922 6 : pg_fatal("invalid compression specification: %s",
923 : error_detail);
924 :
925 510 : error_detail = supports_compression(compression_spec);
926 510 : if (error_detail != NULL)
927 0 : pg_fatal("%s", error_detail);
928 :
929 : /*
930 : * Disable support for zstd workers for now - these are based on
931 : * threading, and it's unclear how it interacts with parallel dumps on
932 : * platforms where that relies on threads too (e.g. Windows).
933 : */
934 510 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
935 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
936 : "workers");
937 :
938 : /*
939 : * If emitting an archive format, we always want to emit a DATABASE item,
940 : * in case --create is specified at pg_restore time.
941 : */
942 510 : if (!plainText)
943 208 : dopt.outputCreateDB = 1;
944 :
945 : /* Parallel backup only in the directory archive format so far */
946 510 : if (archiveFormat != archDirectory && numWorkers > 1)
947 2 : pg_fatal("parallel backup only supported by the directory format");
948 :
949 : /* Open the output file */
950 508 : fout = CreateArchive(filename, archiveFormat, compression_spec,
951 : dosync, archiveMode, setupDumpWorker, sync_method);
952 :
953 : /* Make dump options accessible right away */
954 506 : SetArchiveOptions(fout, &dopt, NULL);
955 :
956 : /* Register the cleanup hook */
957 506 : on_exit_close_archive(fout);
958 :
959 : /* Let the archiver know how noisy to be */
960 506 : fout->verbose = g_verbose;
961 :
962 :
963 : /*
964 : * We allow the server to be back to 9.2, and up to any minor release of
965 : * our own major version. (See also version check in pg_dumpall.c.)
966 : */
967 506 : fout->minRemoteVersion = 90200;
968 506 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
969 :
970 506 : fout->numWorkers = numWorkers;
971 :
972 : /*
973 : * Open the database using the Archiver, so it knows about it. Errors mean
974 : * death.
975 : */
976 506 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
977 502 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
978 :
979 : /*
980 : * On hot standbys, never try to dump unlogged table data, since it will
981 : * just throw an error.
982 : */
983 502 : if (fout->isStandby)
984 8 : dopt.no_unlogged_table_data = true;
985 :
986 : /*
987 : * Find the last built-in OID, if needed (prior to 8.1)
988 : *
989 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
990 : */
991 502 : g_last_builtin_oid = FirstNormalObjectId - 1;
992 :
993 502 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
994 :
995 : /* Expand schema selection patterns into OID lists */
996 502 : if (schema_include_patterns.head != NULL)
997 : {
998 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
999 : &schema_include_oids,
1000 : strict_names);
1001 24 : if (schema_include_oids.head == NULL)
1002 2 : pg_fatal("no matching schemas were found");
1003 : }
1004 488 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1005 : &schema_exclude_oids,
1006 : false);
1007 : /* non-matching exclusion patterns aren't an error */
1008 :
1009 : /* Expand table selection patterns into OID lists */
1010 488 : expand_table_name_patterns(fout, &table_include_patterns,
1011 : &table_include_oids,
1012 : strict_names, false);
1013 478 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1014 : &table_include_oids,
1015 : strict_names, true);
1016 478 : if ((table_include_patterns.head != NULL ||
1017 456 : table_include_patterns_and_children.head != NULL) &&
1018 26 : table_include_oids.head == NULL)
1019 4 : pg_fatal("no matching tables were found");
1020 :
1021 474 : expand_table_name_patterns(fout, &table_exclude_patterns,
1022 : &table_exclude_oids,
1023 : false, false);
1024 474 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1025 : &table_exclude_oids,
1026 : false, true);
1027 :
1028 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1029 : &tabledata_exclude_oids,
1030 : false, false);
1031 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1032 : &tabledata_exclude_oids,
1033 : false, true);
1034 :
1035 474 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1036 : &foreign_servers_include_oids);
1037 :
1038 : /* non-matching exclusion patterns aren't an error */
1039 :
1040 : /* Expand extension selection patterns into OID lists */
1041 472 : if (extension_include_patterns.head != NULL)
1042 : {
1043 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1044 : &extension_include_oids,
1045 : strict_names);
1046 10 : if (extension_include_oids.head == NULL)
1047 2 : pg_fatal("no matching extensions were found");
1048 : }
1049 470 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1050 : &extension_exclude_oids,
1051 : false);
1052 : /* non-matching exclusion patterns aren't an error */
1053 :
1054 : /*
1055 : * Dumping LOs is the default for dumps where an inclusion switch is not
1056 : * used (an "include everything" dump). -B can be used to exclude LOs
1057 : * from those dumps. -b can be used to include LOs even when an inclusion
1058 : * switch is used.
1059 : *
1060 : * -s means "schema only" and LOs are data, not schema, so we never
1061 : * include LOs when -s is used.
1062 : */
1063 470 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1064 348 : dopt.outputLOs = true;
1065 :
1066 : /*
1067 : * Collect role names so we can map object owner OIDs to names.
1068 : */
1069 470 : collectRoleNames(fout);
1070 :
1071 : /*
1072 : * Now scan the database and create DumpableObject structs for all the
1073 : * objects we intend to dump.
1074 : */
1075 470 : tblinfo = getSchemaData(fout, &numTables);
1076 :
1077 468 : if (dopt.dumpData)
1078 : {
1079 396 : getTableData(&dopt, tblinfo, numTables, 0);
1080 396 : buildMatViewRefreshDependencies(fout);
1081 396 : if (!dopt.dumpSchema)
1082 14 : getTableDataFKConstraints();
1083 : }
1084 :
1085 468 : if (!dopt.dumpData && dopt.sequence_data)
1086 56 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1087 :
1088 : /*
1089 : * In binary-upgrade mode, we do not have to worry about the actual LO
1090 : * data or the associated metadata that resides in the pg_largeobject and
1091 : * pg_largeobject_metadata tables, respectively.
1092 : *
1093 : * However, we do need to collect LO information as there may be comments
1094 : * or other information on LOs that we do need to dump out.
1095 : */
1096 468 : if (dopt.outputLOs || dopt.binary_upgrade)
1097 410 : getLOs(fout);
1098 :
1099 : /*
1100 : * Collect dependency data to assist in ordering the objects.
1101 : */
1102 468 : getDependencies(fout);
1103 :
1104 : /*
1105 : * Collect ACLs, comments, and security labels, if wanted.
1106 : */
1107 468 : if (!dopt.aclsSkip)
1108 464 : getAdditionalACLs(fout);
1109 468 : if (!dopt.no_comments)
1110 468 : collectComments(fout);
1111 468 : if (!dopt.no_security_labels)
1112 468 : collectSecLabels(fout);
1113 :
1114 : /* For binary upgrade mode, collect required pg_class information. */
1115 468 : if (dopt.binary_upgrade)
1116 62 : collectBinaryUpgradeClassOids(fout);
1117 :
1118 : /* Collect sequence information. */
1119 468 : collectSequences(fout);
1120 :
1121 : /* Lastly, create dummy objects to represent the section boundaries */
1122 468 : boundaryObjs = createBoundaryObjects();
1123 :
1124 : /* Get pointers to all the known DumpableObjects */
1125 468 : getDumpableObjects(&dobjs, &numObjs);
1126 :
1127 : /*
1128 : * Add dummy dependencies to enforce the dump section ordering.
1129 : */
1130 468 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1131 :
1132 : /*
1133 : * Sort the objects into a safe dump order (no forward references).
1134 : *
1135 : * We rely on dependency information to help us determine a safe order, so
1136 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1137 : * ensure that logically identical schemas will dump identically.
1138 : */
1139 468 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1140 :
1141 468 : sortDumpableObjects(dobjs, numObjs,
1142 468 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1143 :
1144 : /*
1145 : * Create archive TOC entries for all the objects to be dumped, in a safe
1146 : * order.
1147 : */
1148 :
1149 : /*
1150 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1151 : */
1152 468 : dumpEncoding(fout);
1153 468 : dumpStdStrings(fout);
1154 468 : dumpSearchPath(fout);
1155 :
1156 : /* The database items are always next, unless we don't want them at all */
1157 468 : if (dopt.outputCreateDB)
1158 264 : dumpDatabase(fout);
1159 :
1160 : /* Now the rearrangeable objects. */
1161 1737994 : for (i = 0; i < numObjs; i++)
1162 1737526 : dumpDumpableObject(fout, dobjs[i]);
1163 :
1164 : /*
1165 : * Set up options info to ensure we dump what we want.
1166 : */
1167 468 : ropt = NewRestoreOptions();
1168 468 : ropt->filename = filename;
1169 :
1170 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1171 468 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1172 468 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1173 468 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1174 468 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1175 468 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1176 468 : ropt->dropSchema = dopt.outputClean;
1177 468 : ropt->dumpData = dopt.dumpData;
1178 468 : ropt->dumpSchema = dopt.dumpSchema;
1179 468 : ropt->dumpStatistics = dopt.dumpStatistics;
1180 468 : ropt->if_exists = dopt.if_exists;
1181 468 : ropt->column_inserts = dopt.column_inserts;
1182 468 : ropt->dumpSections = dopt.dumpSections;
1183 468 : ropt->aclsSkip = dopt.aclsSkip;
1184 468 : ropt->superuser = dopt.outputSuperuser;
1185 468 : ropt->createDB = dopt.outputCreateDB;
1186 468 : ropt->noOwner = dopt.outputNoOwner;
1187 468 : ropt->noTableAm = dopt.outputNoTableAm;
1188 468 : ropt->noTablespace = dopt.outputNoTablespaces;
1189 468 : ropt->disable_triggers = dopt.disable_triggers;
1190 468 : ropt->use_setsessauth = dopt.use_setsessauth;
1191 468 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1192 468 : ropt->dump_inserts = dopt.dump_inserts;
1193 468 : ropt->no_comments = dopt.no_comments;
1194 468 : ropt->no_policies = dopt.no_policies;
1195 468 : ropt->no_publications = dopt.no_publications;
1196 468 : ropt->no_security_labels = dopt.no_security_labels;
1197 468 : ropt->no_subscriptions = dopt.no_subscriptions;
1198 468 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1199 468 : ropt->include_everything = dopt.include_everything;
1200 468 : ropt->enable_row_security = dopt.enable_row_security;
1201 468 : ropt->sequence_data = dopt.sequence_data;
1202 468 : ropt->binary_upgrade = dopt.binary_upgrade;
1203 :
1204 468 : ropt->compression_spec = compression_spec;
1205 :
1206 468 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1207 :
1208 468 : SetArchiveOptions(fout, &dopt, ropt);
1209 :
1210 : /* Mark which entries should be output */
1211 468 : ProcessArchiveRestoreOptions(fout);
1212 :
1213 : /*
1214 : * The archive's TOC entries are now marked as to which ones will actually
1215 : * be output, so we can set up their dependency lists properly. This isn't
1216 : * necessary for plain-text output, though.
1217 : */
1218 468 : if (!plainText)
1219 206 : BuildArchiveDependencies(fout);
1220 :
1221 : /*
1222 : * And finally we can do the actual output.
1223 : *
1224 : * Note: for non-plain-text output formats, the output file is written
1225 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1226 : * right now.
1227 : */
1228 468 : if (plainText)
1229 262 : RestoreArchive(fout, false);
1230 :
1231 466 : CloseArchive(fout);
1232 :
1233 466 : exit_nicely(0);
1234 : }
1235 :
1236 :
1237 : static void
1238 2 : help(const char *progname)
1239 : {
1240 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1241 2 : printf(_("Usage:\n"));
1242 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1243 :
1244 2 : printf(_("\nGeneral options:\n"));
1245 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1246 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1247 : " plain text (default))\n"));
1248 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1249 2 : printf(_(" -v, --verbose verbose mode\n"));
1250 2 : printf(_(" -V, --version output version information, then exit\n"));
1251 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1252 : " compress as specified\n"));
1253 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1254 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1255 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1256 2 : printf(_(" -?, --help show this help, then exit\n"));
1257 :
1258 2 : printf(_("\nOptions controlling the output content:\n"));
1259 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1260 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1261 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1262 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1263 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1264 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1265 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1266 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1267 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1268 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1269 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1270 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1271 : " plain-text format\n"));
1272 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1273 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1274 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1275 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1276 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1277 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1278 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1279 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1280 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1281 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1282 : " access to)\n"));
1283 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1284 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1285 : " do NOT dump the specified table(s), including\n"
1286 : " child and partition tables\n"));
1287 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1288 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1289 : " do NOT dump data for the specified table(s),\n"
1290 : " including child and partition tables\n"));
1291 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1292 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1293 : " based on expressions in FILENAME\n"));
1294 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1295 2 : printf(_(" --include-foreign-data=PATTERN\n"
1296 : " include data of foreign tables on foreign\n"
1297 : " servers matching PATTERN\n"));
1298 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1299 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1300 2 : printf(_(" --no-comments do not dump comment commands\n"));
1301 2 : printf(_(" --no-data do not dump data\n"));
1302 2 : printf(_(" --no-policies do not dump row security policies\n"));
1303 2 : printf(_(" --no-publications do not dump publications\n"));
1304 2 : printf(_(" --no-schema do not dump schema\n"));
1305 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1306 2 : printf(_(" --no-statistics do not dump statistics\n"));
1307 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1308 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1309 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1310 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1311 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1312 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1313 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1314 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1315 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1316 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1317 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1318 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1319 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1320 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1321 : " match at least one entity each\n"));
1322 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1323 : " child and partition tables\n"));
1324 2 : printf(_(" --use-set-session-authorization\n"
1325 : " use SET SESSION AUTHORIZATION commands instead of\n"
1326 : " ALTER OWNER commands to set ownership\n"));
1327 2 : printf(_(" --with-data dump the data\n"));
1328 2 : printf(_(" --with-schema dump the schema\n"));
1329 2 : printf(_(" --with-statistics dump the statistics\n"));
1330 :
1331 2 : printf(_("\nConnection options:\n"));
1332 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1333 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1334 2 : printf(_(" -p, --port=PORT database server port number\n"));
1335 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1336 2 : printf(_(" -w, --no-password never prompt for password\n"));
1337 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1338 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1339 :
1340 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1341 : "variable value is used.\n\n"));
1342 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1343 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1344 2 : }
1345 :
1346 : static void
1347 538 : setup_connection(Archive *AH, const char *dumpencoding,
1348 : const char *dumpsnapshot, char *use_role)
1349 : {
1350 538 : DumpOptions *dopt = AH->dopt;
1351 538 : PGconn *conn = GetConnection(AH);
1352 : const char *std_strings;
1353 :
1354 538 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1355 :
1356 : /*
1357 : * Set the client encoding if requested.
1358 : */
1359 538 : if (dumpencoding)
1360 : {
1361 40 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1362 0 : pg_fatal("invalid client encoding \"%s\" specified",
1363 : dumpencoding);
1364 : }
1365 :
1366 : /*
1367 : * Get the active encoding and the standard_conforming_strings setting, so
1368 : * we know how to escape strings.
1369 : */
1370 538 : AH->encoding = PQclientEncoding(conn);
1371 538 : setFmtEncoding(AH->encoding);
1372 :
1373 538 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1374 538 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1375 :
1376 : /*
1377 : * Set the role if requested. In a parallel dump worker, we'll be passed
1378 : * use_role == NULL, but AH->use_role is already set (if user specified it
1379 : * originally) and we should use that.
1380 : */
1381 538 : if (!use_role && AH->use_role)
1382 4 : use_role = AH->use_role;
1383 :
1384 : /* Set the role if requested */
1385 538 : if (use_role)
1386 : {
1387 10 : PQExpBuffer query = createPQExpBuffer();
1388 :
1389 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1390 10 : ExecuteSqlStatement(AH, query->data);
1391 10 : destroyPQExpBuffer(query);
1392 :
1393 : /* save it for possible later use by parallel workers */
1394 10 : if (!AH->use_role)
1395 6 : AH->use_role = pg_strdup(use_role);
1396 : }
1397 :
1398 : /* Set the datestyle to ISO to ensure the dump's portability */
1399 538 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1400 :
1401 : /* Likewise, avoid using sql_standard intervalstyle */
1402 538 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1403 :
1404 : /*
1405 : * Use an explicitly specified extra_float_digits if it has been provided.
1406 : * Otherwise, set extra_float_digits so that we can dump float data
1407 : * exactly (given correctly implemented float I/O code, anyway).
1408 : */
1409 538 : if (have_extra_float_digits)
1410 : {
1411 0 : PQExpBuffer q = createPQExpBuffer();
1412 :
1413 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1414 : extra_float_digits);
1415 0 : ExecuteSqlStatement(AH, q->data);
1416 0 : destroyPQExpBuffer(q);
1417 : }
1418 : else
1419 538 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1420 :
1421 : /*
1422 : * Disable synchronized scanning, to prevent unpredictable changes in row
1423 : * ordering across a dump and reload.
1424 : */
1425 538 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1426 :
1427 : /*
1428 : * Disable timeouts if supported.
1429 : */
1430 538 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1431 538 : if (AH->remoteVersion >= 90300)
1432 538 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1433 538 : if (AH->remoteVersion >= 90600)
1434 538 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1435 538 : if (AH->remoteVersion >= 170000)
1436 538 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1437 :
1438 : /*
1439 : * Quote all identifiers, if requested.
1440 : */
1441 538 : if (quote_all_identifiers)
1442 58 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1443 :
1444 : /*
1445 : * Adjust row-security mode, if supported.
1446 : */
1447 538 : if (AH->remoteVersion >= 90500)
1448 : {
1449 538 : if (dopt->enable_row_security)
1450 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1451 : else
1452 538 : ExecuteSqlStatement(AH, "SET row_security = off");
1453 : }
1454 :
1455 : /*
1456 : * For security reasons, we restrict the expansion of non-system views and
1457 : * access to foreign tables during the pg_dump process. This restriction
1458 : * is adjusted when dumping foreign table data.
1459 : */
1460 538 : set_restrict_relation_kind(AH, "view, foreign-table");
1461 :
1462 : /*
1463 : * Initialize prepared-query state to "nothing prepared". We do this here
1464 : * so that a parallel dump worker will have its own state.
1465 : */
1466 538 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1467 :
1468 : /*
1469 : * Start transaction-snapshot mode transaction to dump consistent data.
1470 : */
1471 538 : ExecuteSqlStatement(AH, "BEGIN");
1472 :
1473 : /*
1474 : * To support the combination of serializable_deferrable with the jobs
1475 : * option we use REPEATABLE READ for the worker connections that are
1476 : * passed a snapshot. As long as the snapshot is acquired in a
1477 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1478 : * REPEATABLE READ transaction provides the appropriate integrity
1479 : * guarantees. This is a kluge, but safe for back-patching.
1480 : */
1481 538 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1482 0 : ExecuteSqlStatement(AH,
1483 : "SET TRANSACTION ISOLATION LEVEL "
1484 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1485 : else
1486 538 : ExecuteSqlStatement(AH,
1487 : "SET TRANSACTION ISOLATION LEVEL "
1488 : "REPEATABLE READ, READ ONLY");
1489 :
1490 : /*
1491 : * If user specified a snapshot to use, select that. In a parallel dump
1492 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1493 : * is already set (if the server can handle it) and we should use that.
1494 : */
1495 538 : if (dumpsnapshot)
1496 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1497 :
1498 538 : if (AH->sync_snapshot_id)
1499 : {
1500 36 : PQExpBuffer query = createPQExpBuffer();
1501 :
1502 36 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1503 36 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1504 36 : ExecuteSqlStatement(AH, query->data);
1505 36 : destroyPQExpBuffer(query);
1506 : }
1507 502 : else if (AH->numWorkers > 1)
1508 : {
1509 18 : if (AH->isStandby && AH->remoteVersion < 100000)
1510 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1511 18 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1512 : }
1513 538 : }
1514 :
1515 : /* Set up connection for a parallel worker process */
1516 : static void
1517 36 : setupDumpWorker(Archive *AH)
1518 : {
1519 : /*
1520 : * We want to re-select all the same values the leader connection is
1521 : * using. We'll have inherited directly-usable values in
1522 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1523 : * inherited encoding value back to a string to pass to setup_connection.
1524 : */
1525 36 : setup_connection(AH,
1526 : pg_encoding_to_char(AH->encoding),
1527 : NULL,
1528 : NULL);
1529 36 : }
1530 :
1531 : static char *
1532 18 : get_synchronized_snapshot(Archive *fout)
1533 : {
1534 18 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1535 : char *result;
1536 : PGresult *res;
1537 :
1538 18 : res = ExecuteSqlQueryForSingleRow(fout, query);
1539 18 : result = pg_strdup(PQgetvalue(res, 0, 0));
1540 18 : PQclear(res);
1541 :
1542 18 : return result;
1543 : }
1544 :
1545 : static ArchiveFormat
1546 520 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1547 : {
1548 : ArchiveFormat archiveFormat;
1549 :
1550 520 : *mode = archModeWrite;
1551 :
1552 520 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1553 : {
1554 : /* This is used by pg_dumpall, and is not documented */
1555 86 : archiveFormat = archNull;
1556 86 : *mode = archModeAppend;
1557 : }
1558 434 : else if (pg_strcasecmp(format, "c") == 0)
1559 0 : archiveFormat = archCustom;
1560 434 : else if (pg_strcasecmp(format, "custom") == 0)
1561 88 : archiveFormat = archCustom;
1562 346 : else if (pg_strcasecmp(format, "d") == 0)
1563 2 : archiveFormat = archDirectory;
1564 344 : else if (pg_strcasecmp(format, "directory") == 0)
1565 96 : archiveFormat = archDirectory;
1566 248 : else if (pg_strcasecmp(format, "p") == 0)
1567 218 : archiveFormat = archNull;
1568 30 : else if (pg_strcasecmp(format, "plain") == 0)
1569 6 : archiveFormat = archNull;
1570 24 : else if (pg_strcasecmp(format, "t") == 0)
1571 0 : archiveFormat = archTar;
1572 24 : else if (pg_strcasecmp(format, "tar") == 0)
1573 22 : archiveFormat = archTar;
1574 : else
1575 2 : pg_fatal("invalid output format \"%s\" specified", format);
1576 518 : return archiveFormat;
1577 : }
1578 :
1579 : /*
1580 : * Find the OIDs of all schemas matching the given list of patterns,
1581 : * and append them to the given OID list.
1582 : */
1583 : static void
1584 524 : expand_schema_name_patterns(Archive *fout,
1585 : SimpleStringList *patterns,
1586 : SimpleOidList *oids,
1587 : bool strict_names)
1588 : {
1589 : PQExpBuffer query;
1590 : PGresult *res;
1591 : SimpleStringListCell *cell;
1592 : int i;
1593 :
1594 524 : if (patterns->head == NULL)
1595 482 : return; /* nothing to do */
1596 :
1597 42 : query = createPQExpBuffer();
1598 :
1599 : /*
1600 : * The loop below runs multiple SELECTs might sometimes result in
1601 : * duplicate entries in the OID list, but we don't care.
1602 : */
1603 :
1604 72 : for (cell = patterns->head; cell; cell = cell->next)
1605 : {
1606 : PQExpBufferData dbbuf;
1607 : int dotcnt;
1608 :
1609 42 : appendPQExpBufferStr(query,
1610 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1611 42 : initPQExpBuffer(&dbbuf);
1612 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1613 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1614 : &dotcnt);
1615 42 : if (dotcnt > 1)
1616 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1617 : cell->val);
1618 38 : else if (dotcnt == 1)
1619 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1620 32 : termPQExpBuffer(&dbbuf);
1621 :
1622 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1623 32 : if (strict_names && PQntuples(res) == 0)
1624 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1625 :
1626 58 : for (i = 0; i < PQntuples(res); i++)
1627 : {
1628 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1629 : }
1630 :
1631 30 : PQclear(res);
1632 30 : resetPQExpBuffer(query);
1633 : }
1634 :
1635 30 : destroyPQExpBuffer(query);
1636 : }
1637 :
1638 : /*
1639 : * Find the OIDs of all extensions matching the given list of patterns,
1640 : * and append them to the given OID list.
1641 : */
1642 : static void
1643 480 : expand_extension_name_patterns(Archive *fout,
1644 : SimpleStringList *patterns,
1645 : SimpleOidList *oids,
1646 : bool strict_names)
1647 : {
1648 : PQExpBuffer query;
1649 : PGresult *res;
1650 : SimpleStringListCell *cell;
1651 : int i;
1652 :
1653 480 : if (patterns->head == NULL)
1654 466 : return; /* nothing to do */
1655 :
1656 14 : query = createPQExpBuffer();
1657 :
1658 : /*
1659 : * The loop below runs multiple SELECTs might sometimes result in
1660 : * duplicate entries in the OID list, but we don't care.
1661 : */
1662 28 : for (cell = patterns->head; cell; cell = cell->next)
1663 : {
1664 : int dotcnt;
1665 :
1666 14 : appendPQExpBufferStr(query,
1667 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1668 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1669 : false, NULL, "e.extname", NULL, NULL, NULL,
1670 : &dotcnt);
1671 14 : if (dotcnt > 0)
1672 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1673 : cell->val);
1674 :
1675 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1676 14 : if (strict_names && PQntuples(res) == 0)
1677 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1678 :
1679 26 : for (i = 0; i < PQntuples(res); i++)
1680 : {
1681 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1682 : }
1683 :
1684 14 : PQclear(res);
1685 14 : resetPQExpBuffer(query);
1686 : }
1687 :
1688 14 : destroyPQExpBuffer(query);
1689 : }
1690 :
1691 : /*
1692 : * Find the OIDs of all foreign servers matching the given list of patterns,
1693 : * and append them to the given OID list.
1694 : */
1695 : static void
1696 474 : expand_foreign_server_name_patterns(Archive *fout,
1697 : SimpleStringList *patterns,
1698 : SimpleOidList *oids)
1699 : {
1700 : PQExpBuffer query;
1701 : PGresult *res;
1702 : SimpleStringListCell *cell;
1703 : int i;
1704 :
1705 474 : if (patterns->head == NULL)
1706 468 : return; /* nothing to do */
1707 :
1708 6 : query = createPQExpBuffer();
1709 :
1710 : /*
1711 : * The loop below runs multiple SELECTs might sometimes result in
1712 : * duplicate entries in the OID list, but we don't care.
1713 : */
1714 :
1715 10 : for (cell = patterns->head; cell; cell = cell->next)
1716 : {
1717 : int dotcnt;
1718 :
1719 6 : appendPQExpBufferStr(query,
1720 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1721 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1722 : false, NULL, "s.srvname", NULL, NULL, NULL,
1723 : &dotcnt);
1724 6 : if (dotcnt > 0)
1725 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1726 : cell->val);
1727 :
1728 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1729 6 : if (PQntuples(res) == 0)
1730 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1731 :
1732 8 : for (i = 0; i < PQntuples(res); i++)
1733 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1734 :
1735 4 : PQclear(res);
1736 4 : resetPQExpBuffer(query);
1737 : }
1738 :
1739 4 : destroyPQExpBuffer(query);
1740 : }
1741 :
1742 : /*
1743 : * Find the OIDs of all tables matching the given list of patterns,
1744 : * and append them to the given OID list. See also expand_dbname_patterns()
1745 : * in pg_dumpall.c
1746 : */
1747 : static void
1748 2862 : expand_table_name_patterns(Archive *fout,
1749 : SimpleStringList *patterns, SimpleOidList *oids,
1750 : bool strict_names, bool with_child_tables)
1751 : {
1752 : PQExpBuffer query;
1753 : PGresult *res;
1754 : SimpleStringListCell *cell;
1755 : int i;
1756 :
1757 2862 : if (patterns->head == NULL)
1758 2804 : return; /* nothing to do */
1759 :
1760 58 : query = createPQExpBuffer();
1761 :
1762 : /*
1763 : * this might sometimes result in duplicate entries in the OID list, but
1764 : * we don't care.
1765 : */
1766 :
1767 118 : for (cell = patterns->head; cell; cell = cell->next)
1768 : {
1769 : PQExpBufferData dbbuf;
1770 : int dotcnt;
1771 :
1772 : /*
1773 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1774 : * would be unnecessary given a pg_table_is_visible() variant taking a
1775 : * search_path argument.
1776 : *
1777 : * For with_child_tables, we start with the basic query's results and
1778 : * recursively search the inheritance tree to add child tables.
1779 : */
1780 70 : if (with_child_tables)
1781 : {
1782 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1783 : }
1784 :
1785 70 : appendPQExpBuffer(query,
1786 : "SELECT c.oid"
1787 : "\nFROM pg_catalog.pg_class c"
1788 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1789 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1790 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1791 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1792 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1793 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1794 : RELKIND_PARTITIONED_TABLE);
1795 70 : initPQExpBuffer(&dbbuf);
1796 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1797 : false, "n.nspname", "c.relname", NULL,
1798 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1799 : &dotcnt);
1800 70 : if (dotcnt > 2)
1801 2 : pg_fatal("improper relation name (too many dotted names): %s",
1802 : cell->val);
1803 68 : else if (dotcnt == 2)
1804 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1805 64 : termPQExpBuffer(&dbbuf);
1806 :
1807 64 : if (with_child_tables)
1808 : {
1809 12 : appendPQExpBufferStr(query, "UNION"
1810 : "\nSELECT i.inhrelid"
1811 : "\nFROM partition_tree p"
1812 : "\n JOIN pg_catalog.pg_inherits i"
1813 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1814 : "\n)"
1815 : "\nSELECT relid FROM partition_tree");
1816 : }
1817 :
1818 64 : ExecuteSqlStatement(fout, "RESET search_path");
1819 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1820 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1821 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1822 64 : if (strict_names && PQntuples(res) == 0)
1823 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1824 :
1825 148 : for (i = 0; i < PQntuples(res); i++)
1826 : {
1827 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1828 : }
1829 :
1830 60 : PQclear(res);
1831 60 : resetPQExpBuffer(query);
1832 : }
1833 :
1834 48 : destroyPQExpBuffer(query);
1835 : }
1836 :
1837 : /*
1838 : * Verifies that the connected database name matches the given database name,
1839 : * and if not, dies with an error about the given pattern.
1840 : *
1841 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1842 : */
1843 : static void
1844 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1845 : {
1846 : const char *db;
1847 :
1848 10 : db = PQdb(conn);
1849 10 : if (db == NULL)
1850 0 : pg_fatal("You are currently not connected to a database.");
1851 :
1852 10 : if (strcmp(db, dbname) != 0)
1853 10 : pg_fatal("cross-database references are not implemented: %s",
1854 : pattern);
1855 0 : }
1856 :
1857 : /*
1858 : * checkExtensionMembership
1859 : * Determine whether object is an extension member, and if so,
1860 : * record an appropriate dependency and set the object's dump flag.
1861 : *
1862 : * It's important to call this for each object that could be an extension
1863 : * member. Generally, we integrate this with determining the object's
1864 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1865 : *
1866 : * Returns true if object is an extension member, else false.
1867 : */
1868 : static bool
1869 1475328 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1870 : {
1871 1475328 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1872 :
1873 1475328 : if (ext == NULL)
1874 1473674 : return false;
1875 :
1876 1654 : dobj->ext_member = true;
1877 :
1878 : /* Record dependency so that getDependencies needn't deal with that */
1879 1654 : addObjectDependency(dobj, ext->dobj.dumpId);
1880 :
1881 : /*
1882 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1883 : * dumped. (Any initial ACLs will be removed later, using data from
1884 : * pg_init_privs, so that we'll dump only the delta from the extension's
1885 : * initial setup.)
1886 : *
1887 : * Prior to 9.6, we do not include any extension member components.
1888 : *
1889 : * In binary upgrades, we still dump all components of the members
1890 : * individually, since the idea is to exactly reproduce the database
1891 : * contents rather than replace the extension contents with something
1892 : * different.
1893 : *
1894 : * Note: it might be interesting someday to implement storage and delta
1895 : * dumping of extension members' RLS policies and/or security labels.
1896 : * However there is a pitfall for RLS policies: trying to dump them
1897 : * requires getting a lock on their tables, and the calling user might not
1898 : * have privileges for that. We need no lock to examine a table's ACLs,
1899 : * so the current feature doesn't have a problem of that sort.
1900 : */
1901 1654 : if (fout->dopt->binary_upgrade)
1902 288 : dobj->dump = ext->dobj.dump;
1903 : else
1904 : {
1905 1366 : if (fout->remoteVersion < 90600)
1906 0 : dobj->dump = DUMP_COMPONENT_NONE;
1907 : else
1908 1366 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1909 : }
1910 :
1911 1654 : return true;
1912 : }
1913 :
1914 : /*
1915 : * selectDumpableNamespace: policy-setting subroutine
1916 : * Mark a namespace as to be dumped or not
1917 : */
1918 : static void
1919 3804 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1920 : {
1921 : /*
1922 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1923 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1924 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1925 : */
1926 3804 : nsinfo->create = true;
1927 :
1928 : /*
1929 : * If specific tables are being dumped, do not dump any complete
1930 : * namespaces. If specific namespaces are being dumped, dump just those
1931 : * namespaces. Otherwise, dump all non-system namespaces.
1932 : */
1933 3804 : if (table_include_oids.head != NULL)
1934 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1935 3704 : else if (schema_include_oids.head != NULL)
1936 358 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1937 358 : simple_oid_list_member(&schema_include_oids,
1938 : nsinfo->dobj.catId.oid) ?
1939 358 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1940 3346 : else if (fout->remoteVersion >= 90600 &&
1941 3346 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1942 : {
1943 : /*
1944 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1945 : * they are interesting (and not the original ACLs which were set at
1946 : * initdb time, see pg_init_privs).
1947 : */
1948 426 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1949 : }
1950 2920 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1951 1254 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1952 : {
1953 : /* Other system schemas don't get dumped */
1954 2092 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1955 : }
1956 828 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1957 : {
1958 : /*
1959 : * The public schema is a strange beast that sits in a sort of
1960 : * no-mans-land between being a system object and a user object.
1961 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1962 : * a comment and an indication of ownership. If the owner is the
1963 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1964 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1965 : */
1966 418 : nsinfo->create = false;
1967 418 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1968 418 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1969 326 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1970 418 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1971 :
1972 : /*
1973 : * Also, make like it has a comment even if it doesn't; this is so
1974 : * that we'll emit a command to drop the comment, if appropriate.
1975 : * (Without this, we'd not call dumpCommentExtended for it.)
1976 : */
1977 418 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1978 : }
1979 : else
1980 410 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1981 :
1982 : /*
1983 : * In any case, a namespace can be excluded by an exclusion switch
1984 : */
1985 5080 : if (nsinfo->dobj.dump_contains &&
1986 1276 : simple_oid_list_member(&schema_exclude_oids,
1987 : nsinfo->dobj.catId.oid))
1988 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1989 :
1990 : /*
1991 : * If the schema belongs to an extension, allow extension membership to
1992 : * override the dump decision for the schema itself. However, this does
1993 : * not change dump_contains, so this won't change what we do with objects
1994 : * within the schema. (If they belong to the extension, they'll get
1995 : * suppressed by it, otherwise not.)
1996 : */
1997 3804 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1998 3804 : }
1999 :
2000 : /*
2001 : * selectDumpableTable: policy-setting subroutine
2002 : * Mark a table as to be dumped or not
2003 : */
2004 : static void
2005 123310 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2006 : {
2007 123310 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2008 450 : return; /* extension membership overrides all else */
2009 :
2010 : /*
2011 : * If specific tables are being dumped, dump just those tables; else, dump
2012 : * according to the parent namespace's dump flag.
2013 : */
2014 122860 : if (table_include_oids.head != NULL)
2015 10276 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2016 : tbinfo->dobj.catId.oid) ?
2017 5138 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2018 : else
2019 117722 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2020 :
2021 : /*
2022 : * In any case, a table can be excluded by an exclusion switch
2023 : */
2024 203620 : if (tbinfo->dobj.dump &&
2025 80760 : simple_oid_list_member(&table_exclude_oids,
2026 : tbinfo->dobj.catId.oid))
2027 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2028 : }
2029 :
2030 : /*
2031 : * selectDumpableType: policy-setting subroutine
2032 : * Mark a type as to be dumped or not
2033 : *
2034 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2035 : * special type code to facilitate sorting into the desired order. (We don't
2036 : * want to consider those to be ordinary types because that would bring tables
2037 : * up into the datatype part of the dump order.) We still set the object's
2038 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2039 : * need it so that casts involving such types will be dumped correctly -- see
2040 : * dumpCast. This means the flag should be set the same as for the underlying
2041 : * object (the table or base type).
2042 : */
2043 : static void
2044 338758 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2045 : {
2046 : /* skip complex types, except for standalone composite types */
2047 338758 : if (OidIsValid(tyinfo->typrelid) &&
2048 121612 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2049 : {
2050 121144 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2051 :
2052 121144 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2053 121144 : if (tytable != NULL)
2054 121144 : tyinfo->dobj.dump = tytable->dobj.dump;
2055 : else
2056 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2057 121144 : return;
2058 : }
2059 :
2060 : /* skip auto-generated array and multirange types */
2061 217614 : if (tyinfo->isArray || tyinfo->isMultirange)
2062 : {
2063 165658 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2064 :
2065 : /*
2066 : * Fall through to set the dump flag; we assume that the subsequent
2067 : * rules will do the same thing as they would for the array's base
2068 : * type or multirange's range type. (We cannot reliably look up the
2069 : * base type here, since getTypes may not have processed it yet.)
2070 : */
2071 : }
2072 :
2073 217614 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2074 300 : return; /* extension membership overrides all else */
2075 :
2076 : /* Dump based on if the contents of the namespace are being dumped */
2077 217314 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2078 : }
2079 :
2080 : /*
2081 : * selectDumpableDefaultACL: policy-setting subroutine
2082 : * Mark a default ACL as to be dumped or not
2083 : *
2084 : * For per-schema default ACLs, dump if the schema is to be dumped.
2085 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2086 : * and aclsSkip are checked separately.
2087 : */
2088 : static void
2089 392 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2090 : {
2091 : /* Default ACLs can't be extension members */
2092 :
2093 392 : if (dinfo->dobj.namespace)
2094 : /* default ACLs are considered part of the namespace */
2095 196 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2096 : else
2097 196 : dinfo->dobj.dump = dopt->include_everything ?
2098 196 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2099 392 : }
2100 :
2101 : /*
2102 : * selectDumpableCast: policy-setting subroutine
2103 : * Mark a cast as to be dumped or not
2104 : *
2105 : * Casts do not belong to any particular namespace (since they haven't got
2106 : * names), nor do they have identifiable owners. To distinguish user-defined
2107 : * casts from built-in ones, we must resort to checking whether the cast's
2108 : * OID is in the range reserved for initdb.
2109 : */
2110 : static void
2111 110666 : selectDumpableCast(CastInfo *cast, Archive *fout)
2112 : {
2113 110666 : if (checkExtensionMembership(&cast->dobj, fout))
2114 0 : return; /* extension membership overrides all else */
2115 :
2116 : /*
2117 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2118 : * support ACLs currently.
2119 : */
2120 110666 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2121 110448 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2122 : else
2123 218 : cast->dobj.dump = fout->dopt->include_everything ?
2124 218 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2125 : }
2126 :
2127 : /*
2128 : * selectDumpableProcLang: policy-setting subroutine
2129 : * Mark a procedural language as to be dumped or not
2130 : *
2131 : * Procedural languages do not belong to any particular namespace. To
2132 : * identify built-in languages, we must resort to checking whether the
2133 : * language's OID is in the range reserved for initdb.
2134 : */
2135 : static void
2136 566 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2137 : {
2138 566 : if (checkExtensionMembership(&plang->dobj, fout))
2139 468 : return; /* extension membership overrides all else */
2140 :
2141 : /*
2142 : * Only include procedural languages when we are dumping everything.
2143 : *
2144 : * For from-initdb procedural languages, only include ACLs, as we do for
2145 : * the pg_catalog namespace. We need this because procedural languages do
2146 : * not live in any namespace.
2147 : */
2148 98 : if (!fout->dopt->include_everything)
2149 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2150 : else
2151 : {
2152 82 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2153 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2154 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2155 : else
2156 82 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2157 : }
2158 : }
2159 :
2160 : /*
2161 : * selectDumpableAccessMethod: policy-setting subroutine
2162 : * Mark an access method as to be dumped or not
2163 : *
2164 : * Access methods do not belong to any particular namespace. To identify
2165 : * built-in access methods, we must resort to checking whether the
2166 : * method's OID is in the range reserved for initdb.
2167 : */
2168 : static void
2169 3542 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2170 : {
2171 3542 : if (checkExtensionMembership(&method->dobj, fout))
2172 50 : return; /* extension membership overrides all else */
2173 :
2174 : /*
2175 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2176 : * they do not support ACLs currently.
2177 : */
2178 3492 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2179 3276 : method->dobj.dump = DUMP_COMPONENT_NONE;
2180 : else
2181 216 : method->dobj.dump = fout->dopt->include_everything ?
2182 216 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2183 : }
2184 :
2185 : /*
2186 : * selectDumpableExtension: policy-setting subroutine
2187 : * Mark an extension as to be dumped or not
2188 : *
2189 : * Built-in extensions should be skipped except for checking ACLs, since we
2190 : * assume those will already be installed in the target database. We identify
2191 : * such extensions by their having OIDs in the range reserved for initdb.
2192 : * We dump all user-added extensions by default. No extensions are dumped
2193 : * if include_everything is false (i.e., a --schema or --table switch was
2194 : * given), except if --extension specifies a list of extensions to dump.
2195 : */
2196 : static void
2197 520 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2198 : {
2199 : /*
2200 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2201 : * change permissions on their member objects, if they wish to, and have
2202 : * those changes preserved.
2203 : */
2204 520 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2205 470 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2206 : else
2207 : {
2208 : /* check if there is a list of extensions to dump */
2209 50 : if (extension_include_oids.head != NULL)
2210 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2211 8 : simple_oid_list_member(&extension_include_oids,
2212 : extinfo->dobj.catId.oid) ?
2213 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2214 : else
2215 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2216 42 : dopt->include_everything ?
2217 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2218 :
2219 : /* check that the extension is not explicitly excluded */
2220 92 : if (extinfo->dobj.dump &&
2221 42 : simple_oid_list_member(&extension_exclude_oids,
2222 : extinfo->dobj.catId.oid))
2223 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2224 : }
2225 520 : }
2226 :
2227 : /*
2228 : * selectDumpablePublicationObject: policy-setting subroutine
2229 : * Mark a publication object as to be dumped or not
2230 : *
2231 : * A publication can have schemas and tables which have schemas, but those are
2232 : * ignored in decision making, because publications are only dumped when we are
2233 : * dumping everything.
2234 : */
2235 : static void
2236 882 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2237 : {
2238 882 : if (checkExtensionMembership(dobj, fout))
2239 0 : return; /* extension membership overrides all else */
2240 :
2241 882 : dobj->dump = fout->dopt->include_everything ?
2242 882 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2243 : }
2244 :
2245 : /*
2246 : * selectDumpableStatisticsObject: policy-setting subroutine
2247 : * Mark an extended statistics object as to be dumped or not
2248 : *
2249 : * We dump an extended statistics object if the schema it's in and the table
2250 : * it's for are being dumped. (This'll need more thought if statistics
2251 : * objects ever support cross-table stats.)
2252 : */
2253 : static void
2254 374 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2255 : {
2256 374 : if (checkExtensionMembership(&sobj->dobj, fout))
2257 0 : return; /* extension membership overrides all else */
2258 :
2259 374 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2260 374 : if (sobj->stattable == NULL ||
2261 374 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2262 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2263 : }
2264 :
2265 : /*
2266 : * selectDumpableObject: policy-setting subroutine
2267 : * Mark a generic dumpable object as to be dumped or not
2268 : *
2269 : * Use this only for object types without a special-case routine above.
2270 : */
2271 : static void
2272 1014570 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2273 : {
2274 1014570 : if (checkExtensionMembership(dobj, fout))
2275 336 : return; /* extension membership overrides all else */
2276 :
2277 : /*
2278 : * Default policy is to dump if parent namespace is dumpable, or for
2279 : * non-namespace-associated items, dump if we're dumping "everything".
2280 : */
2281 1014234 : if (dobj->namespace)
2282 1012896 : dobj->dump = dobj->namespace->dobj.dump_contains;
2283 : else
2284 1338 : dobj->dump = fout->dopt->include_everything ?
2285 1338 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2286 : }
2287 :
2288 : /*
2289 : * Dump a table's contents for loading using the COPY command
2290 : * - this routine is called by the Archiver when it wants the table
2291 : * to be dumped.
2292 : */
2293 : static int
2294 12092 : dumpTableData_copy(Archive *fout, const void *dcontext)
2295 : {
2296 12092 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2297 12092 : TableInfo *tbinfo = tdinfo->tdtable;
2298 12092 : const char *classname = tbinfo->dobj.name;
2299 12092 : PQExpBuffer q = createPQExpBuffer();
2300 :
2301 : /*
2302 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2303 : * which uses it already.
2304 : */
2305 12092 : PQExpBuffer clistBuf = createPQExpBuffer();
2306 12092 : PGconn *conn = GetConnection(fout);
2307 : PGresult *res;
2308 : int ret;
2309 : char *copybuf;
2310 : const char *column_list;
2311 :
2312 12092 : pg_log_info("dumping contents of table \"%s.%s\"",
2313 : tbinfo->dobj.namespace->dobj.name, classname);
2314 :
2315 : /*
2316 : * Specify the column list explicitly so that we have no possibility of
2317 : * retrieving data in the wrong column order. (The default column
2318 : * ordering of COPY will not be what we want in certain corner cases
2319 : * involving ADD COLUMN and inheritance.)
2320 : */
2321 12092 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2322 :
2323 : /*
2324 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2325 : * a filter condition was specified. For other cases a simple COPY
2326 : * suffices.
2327 : */
2328 12092 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2329 : {
2330 : /* Temporary allows to access to foreign tables to dump data */
2331 2 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2332 2 : set_restrict_relation_kind(fout, "view");
2333 :
2334 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2335 : /* klugery to get rid of parens in column list */
2336 2 : if (strlen(column_list) > 2)
2337 : {
2338 2 : appendPQExpBufferStr(q, column_list + 1);
2339 2 : q->data[q->len - 1] = ' ';
2340 : }
2341 : else
2342 0 : appendPQExpBufferStr(q, "* ");
2343 :
2344 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2345 2 : fmtQualifiedDumpable(tbinfo),
2346 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2347 : }
2348 : else
2349 : {
2350 12090 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2351 12090 : fmtQualifiedDumpable(tbinfo),
2352 : column_list);
2353 : }
2354 12092 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2355 12090 : PQclear(res);
2356 12090 : destroyPQExpBuffer(clistBuf);
2357 :
2358 : for (;;)
2359 : {
2360 6135562 : ret = PQgetCopyData(conn, ©buf, 0);
2361 :
2362 6135562 : if (ret < 0)
2363 12090 : break; /* done or error */
2364 :
2365 6123472 : if (copybuf)
2366 : {
2367 6123472 : WriteData(fout, copybuf, ret);
2368 6123472 : PQfreemem(copybuf);
2369 : }
2370 :
2371 : /* ----------
2372 : * THROTTLE:
2373 : *
2374 : * There was considerable discussion in late July, 2000 regarding
2375 : * slowing down pg_dump when backing up large tables. Users with both
2376 : * slow & fast (multi-processor) machines experienced performance
2377 : * degradation when doing a backup.
2378 : *
2379 : * Initial attempts based on sleeping for a number of ms for each ms
2380 : * of work were deemed too complex, then a simple 'sleep in each loop'
2381 : * implementation was suggested. The latter failed because the loop
2382 : * was too tight. Finally, the following was implemented:
2383 : *
2384 : * If throttle is non-zero, then
2385 : * See how long since the last sleep.
2386 : * Work out how long to sleep (based on ratio).
2387 : * If sleep is more than 100ms, then
2388 : * sleep
2389 : * reset timer
2390 : * EndIf
2391 : * EndIf
2392 : *
2393 : * where the throttle value was the number of ms to sleep per ms of
2394 : * work. The calculation was done in each loop.
2395 : *
2396 : * Most of the hard work is done in the backend, and this solution
2397 : * still did not work particularly well: on slow machines, the ratio
2398 : * was 50:1, and on medium paced machines, 1:1, and on fast
2399 : * multi-processor machines, it had little or no effect, for reasons
2400 : * that were unclear.
2401 : *
2402 : * Further discussion ensued, and the proposal was dropped.
2403 : *
2404 : * For those people who want this feature, it can be implemented using
2405 : * gettimeofday in each loop, calculating the time since last sleep,
2406 : * multiplying that by the sleep ratio, then if the result is more
2407 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2408 : * function to sleep for a subsecond period ie.
2409 : *
2410 : * select(0, NULL, NULL, NULL, &tvi);
2411 : *
2412 : * This will return after the interval specified in the structure tvi.
2413 : * Finally, call gettimeofday again to save the 'last sleep time'.
2414 : * ----------
2415 : */
2416 : }
2417 12090 : archprintf(fout, "\\.\n\n\n");
2418 :
2419 12090 : if (ret == -2)
2420 : {
2421 : /* copy data transfer failed */
2422 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2423 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2424 0 : pg_log_error_detail("Command was: %s", q->data);
2425 0 : exit_nicely(1);
2426 : }
2427 :
2428 : /* Check command status and return to normal libpq state */
2429 12090 : res = PQgetResult(conn);
2430 12090 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2431 : {
2432 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2433 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2434 0 : pg_log_error_detail("Command was: %s", q->data);
2435 0 : exit_nicely(1);
2436 : }
2437 12090 : PQclear(res);
2438 :
2439 : /* Do this to ensure we've pumped libpq back to idle state */
2440 12090 : if (PQgetResult(conn) != NULL)
2441 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2442 : classname);
2443 :
2444 12090 : destroyPQExpBuffer(q);
2445 :
2446 : /* Revert back the setting */
2447 12090 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2448 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2449 :
2450 12090 : return 1;
2451 : }
2452 :
2453 : /*
2454 : * Dump table data using INSERT commands.
2455 : *
2456 : * Caution: when we restore from an archive file direct to database, the
2457 : * INSERT commands emitted by this function have to be parsed by
2458 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2459 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2460 : */
2461 : static int
2462 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2463 : {
2464 162 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2465 162 : TableInfo *tbinfo = tdinfo->tdtable;
2466 162 : DumpOptions *dopt = fout->dopt;
2467 162 : PQExpBuffer q = createPQExpBuffer();
2468 162 : PQExpBuffer insertStmt = NULL;
2469 : char *attgenerated;
2470 : PGresult *res;
2471 : int nfields,
2472 : i;
2473 162 : int rows_per_statement = dopt->dump_inserts;
2474 162 : int rows_this_statement = 0;
2475 :
2476 : /* Temporary allows to access to foreign tables to dump data */
2477 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2478 0 : set_restrict_relation_kind(fout, "view");
2479 :
2480 : /*
2481 : * If we're going to emit INSERTs with column names, the most efficient
2482 : * way to deal with generated columns is to exclude them entirely. For
2483 : * INSERTs without column names, we have to emit DEFAULT rather than the
2484 : * actual column value --- but we can save a few cycles by fetching nulls
2485 : * rather than the uninteresting-to-us value.
2486 : */
2487 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2488 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2489 162 : nfields = 0;
2490 506 : for (i = 0; i < tbinfo->numatts; i++)
2491 : {
2492 344 : if (tbinfo->attisdropped[i])
2493 4 : continue;
2494 340 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2495 16 : continue;
2496 324 : if (nfields > 0)
2497 176 : appendPQExpBufferStr(q, ", ");
2498 324 : if (tbinfo->attgenerated[i])
2499 16 : appendPQExpBufferStr(q, "NULL");
2500 : else
2501 308 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2502 324 : attgenerated[nfields] = tbinfo->attgenerated[i];
2503 324 : nfields++;
2504 : }
2505 : /* Servers before 9.4 will complain about zero-column SELECT */
2506 162 : if (nfields == 0)
2507 14 : appendPQExpBufferStr(q, "NULL");
2508 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2509 162 : fmtQualifiedDumpable(tbinfo));
2510 162 : if (tdinfo->filtercond)
2511 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2512 :
2513 162 : ExecuteSqlStatement(fout, q->data);
2514 :
2515 : while (1)
2516 : {
2517 266 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2518 : PGRES_TUPLES_OK);
2519 :
2520 : /* cross-check field count, allowing for dummy NULL if any */
2521 266 : if (nfields != PQnfields(res) &&
2522 20 : !(nfields == 0 && PQnfields(res) == 1))
2523 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2524 : tbinfo->dobj.name);
2525 :
2526 : /*
2527 : * First time through, we build as much of the INSERT statement as
2528 : * possible in "insertStmt", which we can then just print for each
2529 : * statement. If the table happens to have zero dumpable columns then
2530 : * this will be a complete statement, otherwise it will end in
2531 : * "VALUES" and be ready to have the row's column values printed.
2532 : */
2533 266 : if (insertStmt == NULL)
2534 : {
2535 : TableInfo *targettab;
2536 :
2537 162 : insertStmt = createPQExpBuffer();
2538 :
2539 : /*
2540 : * When load-via-partition-root is set or forced, get the root
2541 : * table name for the partition table, so that we can reload data
2542 : * through the root table.
2543 : */
2544 162 : if (tbinfo->ispartition &&
2545 80 : (dopt->load_via_partition_root ||
2546 40 : forcePartitionRootLoad(tbinfo)))
2547 6 : targettab = getRootTableInfo(tbinfo);
2548 : else
2549 156 : targettab = tbinfo;
2550 :
2551 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2552 162 : fmtQualifiedDumpable(targettab));
2553 :
2554 : /* corner case for zero-column table */
2555 162 : if (nfields == 0)
2556 : {
2557 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2558 : }
2559 : else
2560 : {
2561 : /* append the list of column names if required */
2562 148 : if (dopt->column_inserts)
2563 : {
2564 66 : appendPQExpBufferChar(insertStmt, '(');
2565 202 : for (int field = 0; field < nfields; field++)
2566 : {
2567 136 : if (field > 0)
2568 70 : appendPQExpBufferStr(insertStmt, ", ");
2569 136 : appendPQExpBufferStr(insertStmt,
2570 136 : fmtId(PQfname(res, field)));
2571 : }
2572 66 : appendPQExpBufferStr(insertStmt, ") ");
2573 : }
2574 :
2575 148 : if (tbinfo->needs_override)
2576 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2577 :
2578 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2579 : }
2580 : }
2581 :
2582 6808 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2583 : {
2584 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2585 6542 : if (rows_this_statement == 0)
2586 6530 : archputs(insertStmt->data, fout);
2587 :
2588 : /*
2589 : * If it is zero-column table then we've already written the
2590 : * complete statement, which will mean we've disobeyed
2591 : * --rows-per-insert when it's set greater than 1. We do support
2592 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2593 : * UNION ALL ... but that's non-standard so we should avoid it
2594 : * given that using INSERTs is mostly only ever needed for
2595 : * cross-database exports.
2596 : */
2597 6542 : if (nfields == 0)
2598 12 : continue;
2599 :
2600 : /* Emit a row heading */
2601 6530 : if (rows_per_statement == 1)
2602 6512 : archputs(" (", fout);
2603 18 : else if (rows_this_statement > 0)
2604 12 : archputs(",\n\t(", fout);
2605 : else
2606 6 : archputs("\n\t(", fout);
2607 :
2608 19698 : for (int field = 0; field < nfields; field++)
2609 : {
2610 13168 : if (field > 0)
2611 6638 : archputs(", ", fout);
2612 13168 : if (attgenerated[field])
2613 : {
2614 4 : archputs("DEFAULT", fout);
2615 4 : continue;
2616 : }
2617 13164 : if (PQgetisnull(res, tuple, field))
2618 : {
2619 166 : archputs("NULL", fout);
2620 166 : continue;
2621 : }
2622 :
2623 : /* XXX This code is partially duplicated in ruleutils.c */
2624 12998 : switch (PQftype(res, field))
2625 : {
2626 8938 : case INT2OID:
2627 : case INT4OID:
2628 : case INT8OID:
2629 : case OIDOID:
2630 : case FLOAT4OID:
2631 : case FLOAT8OID:
2632 : case NUMERICOID:
2633 : {
2634 : /*
2635 : * These types are printed without quotes unless
2636 : * they contain values that aren't accepted by the
2637 : * scanner unquoted (e.g., 'NaN'). Note that
2638 : * strtod() and friends might accept NaN, so we
2639 : * can't use that to test.
2640 : *
2641 : * In reality we only need to defend against
2642 : * infinity and NaN, so we need not get too crazy
2643 : * about pattern matching here.
2644 : */
2645 8938 : const char *s = PQgetvalue(res, tuple, field);
2646 :
2647 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2648 8934 : archputs(s, fout);
2649 : else
2650 4 : archprintf(fout, "'%s'", s);
2651 : }
2652 8938 : break;
2653 :
2654 4 : case BITOID:
2655 : case VARBITOID:
2656 4 : archprintf(fout, "B'%s'",
2657 : PQgetvalue(res, tuple, field));
2658 4 : break;
2659 :
2660 8 : case BOOLOID:
2661 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2662 4 : archputs("true", fout);
2663 : else
2664 4 : archputs("false", fout);
2665 8 : break;
2666 :
2667 4048 : default:
2668 : /* All other types are printed as string literals. */
2669 4048 : resetPQExpBuffer(q);
2670 4048 : appendStringLiteralAH(q,
2671 : PQgetvalue(res, tuple, field),
2672 : fout);
2673 4048 : archputs(q->data, fout);
2674 4048 : break;
2675 : }
2676 : }
2677 :
2678 : /* Terminate the row ... */
2679 6530 : archputs(")", fout);
2680 :
2681 : /* ... and the statement, if the target no. of rows is reached */
2682 6530 : if (++rows_this_statement >= rows_per_statement)
2683 : {
2684 6516 : if (dopt->do_nothing)
2685 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2686 : else
2687 6516 : archputs(";\n", fout);
2688 : /* Reset the row counter */
2689 6516 : rows_this_statement = 0;
2690 : }
2691 : }
2692 :
2693 266 : if (PQntuples(res) <= 0)
2694 : {
2695 162 : PQclear(res);
2696 162 : break;
2697 : }
2698 104 : PQclear(res);
2699 : }
2700 :
2701 : /* Terminate any statements that didn't make the row count. */
2702 162 : if (rows_this_statement > 0)
2703 : {
2704 2 : if (dopt->do_nothing)
2705 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2706 : else
2707 2 : archputs(";\n", fout);
2708 : }
2709 :
2710 162 : archputs("\n\n", fout);
2711 :
2712 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2713 :
2714 162 : destroyPQExpBuffer(q);
2715 162 : if (insertStmt != NULL)
2716 162 : destroyPQExpBuffer(insertStmt);
2717 162 : free(attgenerated);
2718 :
2719 : /* Revert back the setting */
2720 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2721 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2722 :
2723 162 : return 1;
2724 : }
2725 :
2726 : /*
2727 : * getRootTableInfo:
2728 : * get the root TableInfo for the given partition table.
2729 : */
2730 : static TableInfo *
2731 18 : getRootTableInfo(const TableInfo *tbinfo)
2732 : {
2733 : TableInfo *parentTbinfo;
2734 :
2735 : Assert(tbinfo->ispartition);
2736 : Assert(tbinfo->numParents == 1);
2737 :
2738 18 : parentTbinfo = tbinfo->parents[0];
2739 18 : while (parentTbinfo->ispartition)
2740 : {
2741 : Assert(parentTbinfo->numParents == 1);
2742 0 : parentTbinfo = parentTbinfo->parents[0];
2743 : }
2744 :
2745 18 : return parentTbinfo;
2746 : }
2747 :
2748 : /*
2749 : * forcePartitionRootLoad
2750 : * Check if we must force load_via_partition_root for this partition.
2751 : *
2752 : * This is required if any level of ancestral partitioned table has an
2753 : * unsafe partitioning scheme.
2754 : */
2755 : static bool
2756 3060 : forcePartitionRootLoad(const TableInfo *tbinfo)
2757 : {
2758 : TableInfo *parentTbinfo;
2759 :
2760 : Assert(tbinfo->ispartition);
2761 : Assert(tbinfo->numParents == 1);
2762 :
2763 3060 : parentTbinfo = tbinfo->parents[0];
2764 3060 : if (parentTbinfo->unsafe_partitions)
2765 18 : return true;
2766 3798 : while (parentTbinfo->ispartition)
2767 : {
2768 : Assert(parentTbinfo->numParents == 1);
2769 756 : parentTbinfo = parentTbinfo->parents[0];
2770 756 : if (parentTbinfo->unsafe_partitions)
2771 0 : return true;
2772 : }
2773 :
2774 3042 : return false;
2775 : }
2776 :
2777 : /*
2778 : * dumpTableData -
2779 : * dump the contents of a single table
2780 : *
2781 : * Actually, this just makes an ArchiveEntry for the table contents.
2782 : */
2783 : static void
2784 12414 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2785 : {
2786 12414 : DumpOptions *dopt = fout->dopt;
2787 12414 : TableInfo *tbinfo = tdinfo->tdtable;
2788 12414 : PQExpBuffer copyBuf = createPQExpBuffer();
2789 12414 : PQExpBuffer clistBuf = createPQExpBuffer();
2790 : DataDumperPtr dumpFn;
2791 12414 : char *tdDefn = NULL;
2792 : char *copyStmt;
2793 : const char *copyFrom;
2794 :
2795 : /* We had better have loaded per-column details about this table */
2796 : Assert(tbinfo->interesting);
2797 :
2798 : /*
2799 : * When load-via-partition-root is set or forced, get the root table name
2800 : * for the partition table, so that we can reload data through the root
2801 : * table. Then construct a comment to be inserted into the TOC entry's
2802 : * defn field, so that such cases can be identified reliably.
2803 : */
2804 12414 : if (tbinfo->ispartition &&
2805 6040 : (dopt->load_via_partition_root ||
2806 3020 : forcePartitionRootLoad(tbinfo)))
2807 12 : {
2808 : TableInfo *parentTbinfo;
2809 :
2810 12 : parentTbinfo = getRootTableInfo(tbinfo);
2811 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2812 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2813 : copyFrom);
2814 12 : tdDefn = pg_strdup(copyBuf->data);
2815 : }
2816 : else
2817 12402 : copyFrom = fmtQualifiedDumpable(tbinfo);
2818 :
2819 12414 : if (dopt->dump_inserts == 0)
2820 : {
2821 : /* Dump/restore using COPY */
2822 12252 : dumpFn = dumpTableData_copy;
2823 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2824 12252 : printfPQExpBuffer(copyBuf, "COPY %s ",
2825 : copyFrom);
2826 12252 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2827 : fmtCopyColumnList(tbinfo, clistBuf));
2828 12252 : copyStmt = copyBuf->data;
2829 : }
2830 : else
2831 : {
2832 : /* Restore using INSERT */
2833 162 : dumpFn = dumpTableData_insert;
2834 162 : copyStmt = NULL;
2835 : }
2836 :
2837 : /*
2838 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2839 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2840 : * See comments for BuildArchiveDependencies.
2841 : */
2842 12414 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2843 : {
2844 : TocEntry *te;
2845 :
2846 12414 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2847 12414 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2848 : .namespace = tbinfo->dobj.namespace->dobj.name,
2849 : .owner = tbinfo->rolname,
2850 : .description = "TABLE DATA",
2851 : .section = SECTION_DATA,
2852 : .createStmt = tdDefn,
2853 : .copyStmt = copyStmt,
2854 : .deps = &(tbinfo->dobj.dumpId),
2855 : .nDeps = 1,
2856 : .dumpFn = dumpFn,
2857 : .dumpArg = tdinfo));
2858 :
2859 : /*
2860 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2861 : * and want to order dump jobs by table size. We choose to measure
2862 : * dataLength in table pages (including TOAST pages) during dump, so
2863 : * no scaling is needed.
2864 : *
2865 : * However, relpages is declared as "integer" in pg_class, and hence
2866 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2867 : * Cast so that we get the right interpretation of table sizes
2868 : * exceeding INT_MAX pages.
2869 : */
2870 12414 : te->dataLength = (BlockNumber) tbinfo->relpages;
2871 12414 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2872 :
2873 : /*
2874 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2875 : * and instead we'd better worry about integer overflow. Clamp to
2876 : * INT_MAX if the correct result exceeds that.
2877 : */
2878 : if (sizeof(te->dataLength) == 4 &&
2879 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2880 : te->dataLength < 0))
2881 : te->dataLength = INT_MAX;
2882 : }
2883 :
2884 12414 : destroyPQExpBuffer(copyBuf);
2885 12414 : destroyPQExpBuffer(clistBuf);
2886 12414 : }
2887 :
2888 : /*
2889 : * refreshMatViewData -
2890 : * load or refresh the contents of a single materialized view
2891 : *
2892 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2893 : * statement.
2894 : */
2895 : static void
2896 852 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2897 : {
2898 852 : TableInfo *tbinfo = tdinfo->tdtable;
2899 : PQExpBuffer q;
2900 :
2901 : /* If the materialized view is not flagged as populated, skip this. */
2902 852 : if (!tbinfo->relispopulated)
2903 148 : return;
2904 :
2905 704 : q = createPQExpBuffer();
2906 :
2907 704 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2908 704 : fmtQualifiedDumpable(tbinfo));
2909 :
2910 704 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2911 704 : ArchiveEntry(fout,
2912 : tdinfo->dobj.catId, /* catalog ID */
2913 704 : tdinfo->dobj.dumpId, /* dump ID */
2914 704 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2915 : .namespace = tbinfo->dobj.namespace->dobj.name,
2916 : .owner = tbinfo->rolname,
2917 : .description = "MATERIALIZED VIEW DATA",
2918 : .section = SECTION_POST_DATA,
2919 : .createStmt = q->data,
2920 : .deps = tdinfo->dobj.dependencies,
2921 : .nDeps = tdinfo->dobj.nDeps));
2922 :
2923 704 : destroyPQExpBuffer(q);
2924 : }
2925 :
2926 : /*
2927 : * getTableData -
2928 : * set up dumpable objects representing the contents of tables
2929 : */
2930 : static void
2931 452 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2932 : {
2933 : int i;
2934 :
2935 119818 : for (i = 0; i < numTables; i++)
2936 : {
2937 119366 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2938 1822 : (!relkind || tblinfo[i].relkind == relkind))
2939 17174 : makeTableDataInfo(dopt, &(tblinfo[i]));
2940 : }
2941 452 : }
2942 :
2943 : /*
2944 : * Make a dumpable object for the data of this specific table
2945 : *
2946 : * Note: we make a TableDataInfo if and only if we are going to dump the
2947 : * table data; the "dump" field in such objects isn't very interesting.
2948 : */
2949 : static void
2950 17252 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2951 : {
2952 : TableDataInfo *tdinfo;
2953 :
2954 : /*
2955 : * Nothing to do if we already decided to dump the table. This will
2956 : * happen for "config" tables.
2957 : */
2958 17252 : if (tbinfo->dataObj != NULL)
2959 2 : return;
2960 :
2961 : /* Skip VIEWs (no data to dump) */
2962 17250 : if (tbinfo->relkind == RELKIND_VIEW)
2963 1268 : return;
2964 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2965 15982 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2966 82 : (foreign_servers_include_oids.head == NULL ||
2967 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2968 : tbinfo->foreign_server)))
2969 80 : return;
2970 : /* Skip partitioned tables (data in partitions) */
2971 15902 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2972 1448 : return;
2973 :
2974 : /* Don't dump data in unlogged tables, if so requested */
2975 14454 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2976 136 : dopt->no_unlogged_table_data)
2977 36 : return;
2978 :
2979 : /* Check that the data is not explicitly excluded */
2980 14418 : if (simple_oid_list_member(&tabledata_exclude_oids,
2981 : tbinfo->dobj.catId.oid))
2982 16 : return;
2983 :
2984 : /* OK, let's dump it */
2985 14402 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2986 :
2987 14402 : if (tbinfo->relkind == RELKIND_MATVIEW)
2988 852 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2989 13550 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2990 1136 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2991 : else
2992 12414 : tdinfo->dobj.objType = DO_TABLE_DATA;
2993 :
2994 : /*
2995 : * Note: use tableoid 0 so that this object won't be mistaken for
2996 : * something that pg_depend entries apply to.
2997 : */
2998 14402 : tdinfo->dobj.catId.tableoid = 0;
2999 14402 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3000 14402 : AssignDumpId(&tdinfo->dobj);
3001 14402 : tdinfo->dobj.name = tbinfo->dobj.name;
3002 14402 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3003 14402 : tdinfo->tdtable = tbinfo;
3004 14402 : tdinfo->filtercond = NULL; /* might get set later */
3005 14402 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3006 :
3007 : /* A TableDataInfo contains data, of course */
3008 14402 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3009 :
3010 14402 : tbinfo->dataObj = tdinfo;
3011 :
3012 : /*
3013 : * Materialized view statistics must be restored after the data, because
3014 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3015 : *
3016 : * The dependency is added here because the statistics objects are created
3017 : * first.
3018 : */
3019 14402 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3020 : {
3021 640 : tbinfo->stats->section = SECTION_POST_DATA;
3022 640 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3023 : }
3024 :
3025 : /* Make sure that we'll collect per-column info for this table. */
3026 14402 : tbinfo->interesting = true;
3027 : }
3028 :
3029 : /*
3030 : * The refresh for a materialized view must be dependent on the refresh for
3031 : * any materialized view that this one is dependent on.
3032 : *
3033 : * This must be called after all the objects are created, but before they are
3034 : * sorted.
3035 : */
3036 : static void
3037 396 : buildMatViewRefreshDependencies(Archive *fout)
3038 : {
3039 : PQExpBuffer query;
3040 : PGresult *res;
3041 : int ntups,
3042 : i;
3043 : int i_classid,
3044 : i_objid,
3045 : i_refobjid;
3046 :
3047 : /* No Mat Views before 9.3. */
3048 396 : if (fout->remoteVersion < 90300)
3049 0 : return;
3050 :
3051 396 : query = createPQExpBuffer();
3052 :
3053 396 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3054 : "( "
3055 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3056 : "FROM pg_depend d1 "
3057 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3058 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3059 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3060 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3061 : "AND d2.objid = r1.oid "
3062 : "AND d2.refobjid <> d1.objid "
3063 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3064 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3065 : CppAsString2(RELKIND_VIEW) ") "
3066 : "WHERE d1.classid = 'pg_class'::regclass "
3067 : "UNION "
3068 : "SELECT w.objid, d3.refobjid, c3.relkind "
3069 : "FROM w "
3070 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3071 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3072 : "AND d3.objid = r3.oid "
3073 : "AND d3.refobjid <> w.refobjid "
3074 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3075 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3076 : CppAsString2(RELKIND_VIEW) ") "
3077 : ") "
3078 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3079 : "FROM w "
3080 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3081 :
3082 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3083 :
3084 396 : ntups = PQntuples(res);
3085 :
3086 396 : i_classid = PQfnumber(res, "classid");
3087 396 : i_objid = PQfnumber(res, "objid");
3088 396 : i_refobjid = PQfnumber(res, "refobjid");
3089 :
3090 978 : for (i = 0; i < ntups; i++)
3091 : {
3092 : CatalogId objId;
3093 : CatalogId refobjId;
3094 : DumpableObject *dobj;
3095 : DumpableObject *refdobj;
3096 : TableInfo *tbinfo;
3097 : TableInfo *reftbinfo;
3098 :
3099 582 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3100 582 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3101 582 : refobjId.tableoid = objId.tableoid;
3102 582 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3103 :
3104 582 : dobj = findObjectByCatalogId(objId);
3105 582 : if (dobj == NULL)
3106 96 : continue;
3107 :
3108 : Assert(dobj->objType == DO_TABLE);
3109 582 : tbinfo = (TableInfo *) dobj;
3110 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3111 582 : dobj = (DumpableObject *) tbinfo->dataObj;
3112 582 : if (dobj == NULL)
3113 96 : continue;
3114 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3115 :
3116 486 : refdobj = findObjectByCatalogId(refobjId);
3117 486 : if (refdobj == NULL)
3118 0 : continue;
3119 :
3120 : Assert(refdobj->objType == DO_TABLE);
3121 486 : reftbinfo = (TableInfo *) refdobj;
3122 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3123 486 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3124 486 : if (refdobj == NULL)
3125 0 : continue;
3126 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3127 :
3128 486 : addObjectDependency(dobj, refdobj->dumpId);
3129 :
3130 486 : if (!reftbinfo->relispopulated)
3131 74 : tbinfo->relispopulated = false;
3132 : }
3133 :
3134 396 : PQclear(res);
3135 :
3136 396 : destroyPQExpBuffer(query);
3137 : }
3138 :
3139 : /*
3140 : * getTableDataFKConstraints -
3141 : * add dump-order dependencies reflecting foreign key constraints
3142 : *
3143 : * This code is executed only in a data-only dump --- in schema+data dumps
3144 : * we handle foreign key issues by not creating the FK constraints until
3145 : * after the data is loaded. In a data-only dump, however, we want to
3146 : * order the table data objects in such a way that a table's referenced
3147 : * tables are restored first. (In the presence of circular references or
3148 : * self-references this may be impossible; we'll detect and complain about
3149 : * that during the dependency sorting step.)
3150 : */
3151 : static void
3152 14 : getTableDataFKConstraints(void)
3153 : {
3154 : DumpableObject **dobjs;
3155 : int numObjs;
3156 : int i;
3157 :
3158 : /* Search through all the dumpable objects for FK constraints */
3159 14 : getDumpableObjects(&dobjs, &numObjs);
3160 51370 : for (i = 0; i < numObjs; i++)
3161 : {
3162 51356 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3163 : {
3164 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3165 : TableInfo *ftable;
3166 :
3167 : /* Not interesting unless both tables are to be dumped */
3168 16 : if (cinfo->contable == NULL ||
3169 16 : cinfo->contable->dataObj == NULL)
3170 8 : continue;
3171 8 : ftable = findTableByOid(cinfo->confrelid);
3172 8 : if (ftable == NULL ||
3173 8 : ftable->dataObj == NULL)
3174 0 : continue;
3175 :
3176 : /*
3177 : * Okay, make referencing table's TABLE_DATA object depend on the
3178 : * referenced table's TABLE_DATA object.
3179 : */
3180 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3181 8 : ftable->dataObj->dobj.dumpId);
3182 : }
3183 : }
3184 14 : free(dobjs);
3185 14 : }
3186 :
3187 :
3188 : /*
3189 : * dumpDatabase:
3190 : * dump the database definition
3191 : */
3192 : static void
3193 264 : dumpDatabase(Archive *fout)
3194 : {
3195 264 : DumpOptions *dopt = fout->dopt;
3196 264 : PQExpBuffer dbQry = createPQExpBuffer();
3197 264 : PQExpBuffer delQry = createPQExpBuffer();
3198 264 : PQExpBuffer creaQry = createPQExpBuffer();
3199 264 : PQExpBuffer labelq = createPQExpBuffer();
3200 264 : PGconn *conn = GetConnection(fout);
3201 : PGresult *res;
3202 : int i_tableoid,
3203 : i_oid,
3204 : i_datname,
3205 : i_datdba,
3206 : i_encoding,
3207 : i_datlocprovider,
3208 : i_collate,
3209 : i_ctype,
3210 : i_datlocale,
3211 : i_daticurules,
3212 : i_frozenxid,
3213 : i_minmxid,
3214 : i_datacl,
3215 : i_acldefault,
3216 : i_datistemplate,
3217 : i_datconnlimit,
3218 : i_datcollversion,
3219 : i_tablespace;
3220 : CatalogId dbCatId;
3221 : DumpId dbDumpId;
3222 : DumpableAcl dbdacl;
3223 : const char *datname,
3224 : *dba,
3225 : *encoding,
3226 : *datlocprovider,
3227 : *collate,
3228 : *ctype,
3229 : *locale,
3230 : *icurules,
3231 : *datistemplate,
3232 : *datconnlimit,
3233 : *tablespace;
3234 : uint32 frozenxid,
3235 : minmxid;
3236 : char *qdatname;
3237 :
3238 264 : pg_log_info("saving database definition");
3239 :
3240 : /*
3241 : * Fetch the database-level properties for this database.
3242 : */
3243 264 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3244 : "datdba, "
3245 : "pg_encoding_to_char(encoding) AS encoding, "
3246 : "datcollate, datctype, datfrozenxid, "
3247 : "datacl, acldefault('d', datdba) AS acldefault, "
3248 : "datistemplate, datconnlimit, ");
3249 264 : if (fout->remoteVersion >= 90300)
3250 264 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3251 : else
3252 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3253 264 : if (fout->remoteVersion >= 170000)
3254 264 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3255 0 : else if (fout->remoteVersion >= 150000)
3256 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3257 : else
3258 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3259 264 : if (fout->remoteVersion >= 160000)
3260 264 : appendPQExpBufferStr(dbQry, "daticurules, ");
3261 : else
3262 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3263 264 : appendPQExpBufferStr(dbQry,
3264 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3265 : "shobj_description(oid, 'pg_database') AS description "
3266 : "FROM pg_database "
3267 : "WHERE datname = current_database()");
3268 :
3269 264 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3270 :
3271 264 : i_tableoid = PQfnumber(res, "tableoid");
3272 264 : i_oid = PQfnumber(res, "oid");
3273 264 : i_datname = PQfnumber(res, "datname");
3274 264 : i_datdba = PQfnumber(res, "datdba");
3275 264 : i_encoding = PQfnumber(res, "encoding");
3276 264 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3277 264 : i_collate = PQfnumber(res, "datcollate");
3278 264 : i_ctype = PQfnumber(res, "datctype");
3279 264 : i_datlocale = PQfnumber(res, "datlocale");
3280 264 : i_daticurules = PQfnumber(res, "daticurules");
3281 264 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3282 264 : i_minmxid = PQfnumber(res, "datminmxid");
3283 264 : i_datacl = PQfnumber(res, "datacl");
3284 264 : i_acldefault = PQfnumber(res, "acldefault");
3285 264 : i_datistemplate = PQfnumber(res, "datistemplate");
3286 264 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3287 264 : i_datcollversion = PQfnumber(res, "datcollversion");
3288 264 : i_tablespace = PQfnumber(res, "tablespace");
3289 :
3290 264 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3291 264 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3292 264 : datname = PQgetvalue(res, 0, i_datname);
3293 264 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3294 264 : encoding = PQgetvalue(res, 0, i_encoding);
3295 264 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3296 264 : collate = PQgetvalue(res, 0, i_collate);
3297 264 : ctype = PQgetvalue(res, 0, i_ctype);
3298 264 : if (!PQgetisnull(res, 0, i_datlocale))
3299 30 : locale = PQgetvalue(res, 0, i_datlocale);
3300 : else
3301 234 : locale = NULL;
3302 264 : if (!PQgetisnull(res, 0, i_daticurules))
3303 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3304 : else
3305 264 : icurules = NULL;
3306 264 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3307 264 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3308 264 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3309 264 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3310 264 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3311 264 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3312 264 : tablespace = PQgetvalue(res, 0, i_tablespace);
3313 :
3314 264 : qdatname = pg_strdup(fmtId(datname));
3315 :
3316 : /*
3317 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3318 : * to preserve that), as well as the encoding, locale, and tablespace
3319 : * since those can't be altered later. Other DB properties are left to
3320 : * the DATABASE PROPERTIES entry, so that they can be applied after
3321 : * reconnecting to the target DB.
3322 : *
3323 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3324 : * shown it to be faster. When the server is in binary upgrade mode, it
3325 : * will also skip the checkpoints this strategy ordinarily performs.
3326 : */
3327 264 : if (dopt->binary_upgrade)
3328 : {
3329 60 : appendPQExpBuffer(creaQry,
3330 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3331 : "OID = %u STRATEGY = FILE_COPY",
3332 : qdatname, dbCatId.oid);
3333 : }
3334 : else
3335 : {
3336 204 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3337 : qdatname);
3338 : }
3339 264 : if (strlen(encoding) > 0)
3340 : {
3341 264 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3342 264 : appendStringLiteralAH(creaQry, encoding, fout);
3343 : }
3344 :
3345 264 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3346 264 : if (datlocprovider[0] == 'b')
3347 30 : appendPQExpBufferStr(creaQry, "builtin");
3348 234 : else if (datlocprovider[0] == 'c')
3349 234 : appendPQExpBufferStr(creaQry, "libc");
3350 0 : else if (datlocprovider[0] == 'i')
3351 0 : appendPQExpBufferStr(creaQry, "icu");
3352 : else
3353 0 : pg_fatal("unrecognized locale provider: %s",
3354 : datlocprovider);
3355 :
3356 264 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3357 : {
3358 264 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3359 264 : appendStringLiteralAH(creaQry, collate, fout);
3360 : }
3361 : else
3362 : {
3363 0 : if (strlen(collate) > 0)
3364 : {
3365 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3366 0 : appendStringLiteralAH(creaQry, collate, fout);
3367 : }
3368 0 : if (strlen(ctype) > 0)
3369 : {
3370 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3371 0 : appendStringLiteralAH(creaQry, ctype, fout);
3372 : }
3373 : }
3374 264 : if (locale)
3375 : {
3376 30 : if (datlocprovider[0] == 'b')
3377 30 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3378 : else
3379 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3380 :
3381 30 : appendStringLiteralAH(creaQry, locale, fout);
3382 : }
3383 :
3384 264 : if (icurules)
3385 : {
3386 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3387 0 : appendStringLiteralAH(creaQry, icurules, fout);
3388 : }
3389 :
3390 : /*
3391 : * For binary upgrade, carry over the collation version. For normal
3392 : * dump/restore, omit the version, so that it is computed upon restore.
3393 : */
3394 264 : if (dopt->binary_upgrade)
3395 : {
3396 60 : if (!PQgetisnull(res, 0, i_datcollversion))
3397 : {
3398 60 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3399 60 : appendStringLiteralAH(creaQry,
3400 : PQgetvalue(res, 0, i_datcollversion),
3401 : fout);
3402 : }
3403 : }
3404 :
3405 : /*
3406 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3407 : * thing; the decision whether to specify a tablespace should be left till
3408 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3409 : * label the DATABASE entry with the tablespace and let the normal
3410 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3411 : * attention to default_tablespace, so that won't work.
3412 : */
3413 264 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3414 0 : !dopt->outputNoTablespaces)
3415 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3416 : fmtId(tablespace));
3417 264 : appendPQExpBufferStr(creaQry, ";\n");
3418 :
3419 264 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3420 : qdatname);
3421 :
3422 264 : dbDumpId = createDumpId();
3423 :
3424 264 : ArchiveEntry(fout,
3425 : dbCatId, /* catalog ID */
3426 : dbDumpId, /* dump ID */
3427 264 : ARCHIVE_OPTS(.tag = datname,
3428 : .owner = dba,
3429 : .description = "DATABASE",
3430 : .section = SECTION_PRE_DATA,
3431 : .createStmt = creaQry->data,
3432 : .dropStmt = delQry->data));
3433 :
3434 : /* Compute correct tag for archive entry */
3435 264 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3436 :
3437 : /* Dump DB comment if any */
3438 : {
3439 : /*
3440 : * 8.2 and up keep comments on shared objects in a shared table, so we
3441 : * cannot use the dumpComment() code used for other database objects.
3442 : * Be careful that the ArchiveEntry parameters match that function.
3443 : */
3444 264 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3445 :
3446 264 : if (comment && *comment && !dopt->no_comments)
3447 : {
3448 102 : resetPQExpBuffer(dbQry);
3449 :
3450 : /*
3451 : * Generates warning when loaded into a differently-named
3452 : * database.
3453 : */
3454 102 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3455 102 : appendStringLiteralAH(dbQry, comment, fout);
3456 102 : appendPQExpBufferStr(dbQry, ";\n");
3457 :
3458 102 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3459 102 : ARCHIVE_OPTS(.tag = labelq->data,
3460 : .owner = dba,
3461 : .description = "COMMENT",
3462 : .section = SECTION_NONE,
3463 : .createStmt = dbQry->data,
3464 : .deps = &dbDumpId,
3465 : .nDeps = 1));
3466 : }
3467 : }
3468 :
3469 : /* Dump DB security label, if enabled */
3470 264 : if (!dopt->no_security_labels)
3471 : {
3472 : PGresult *shres;
3473 : PQExpBuffer seclabelQry;
3474 :
3475 264 : seclabelQry = createPQExpBuffer();
3476 :
3477 264 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3478 264 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3479 264 : resetPQExpBuffer(seclabelQry);
3480 264 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3481 264 : if (seclabelQry->len > 0)
3482 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3483 0 : ARCHIVE_OPTS(.tag = labelq->data,
3484 : .owner = dba,
3485 : .description = "SECURITY LABEL",
3486 : .section = SECTION_NONE,
3487 : .createStmt = seclabelQry->data,
3488 : .deps = &dbDumpId,
3489 : .nDeps = 1));
3490 264 : destroyPQExpBuffer(seclabelQry);
3491 264 : PQclear(shres);
3492 : }
3493 :
3494 : /*
3495 : * Dump ACL if any. Note that we do not support initial privileges
3496 : * (pg_init_privs) on databases.
3497 : */
3498 264 : dbdacl.privtype = 0;
3499 264 : dbdacl.initprivs = NULL;
3500 :
3501 264 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3502 : qdatname, NULL, NULL,
3503 : NULL, dba, &dbdacl);
3504 :
3505 : /*
3506 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3507 : * non-default database-level properties. (The reason this must be
3508 : * separate is that we cannot put any additional commands into the TOC
3509 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3510 : * in an implicit transaction block, and the backend won't allow CREATE
3511 : * DATABASE in that context.)
3512 : */
3513 264 : resetPQExpBuffer(creaQry);
3514 264 : resetPQExpBuffer(delQry);
3515 :
3516 264 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3517 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3518 : qdatname, datconnlimit);
3519 :
3520 264 : if (strcmp(datistemplate, "t") == 0)
3521 : {
3522 34 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3523 : qdatname);
3524 :
3525 : /*
3526 : * The backend won't accept DROP DATABASE on a template database. We
3527 : * can deal with that by removing the template marking before the DROP
3528 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3529 : * since no such command is currently supported, fake it with a direct
3530 : * UPDATE on pg_database.
3531 : */
3532 34 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3533 : "SET datistemplate = false WHERE datname = ");
3534 34 : appendStringLiteralAH(delQry, datname, fout);
3535 34 : appendPQExpBufferStr(delQry, ";\n");
3536 : }
3537 :
3538 : /*
3539 : * We do not restore pg_database.dathasloginevt because it is set
3540 : * automatically on login event trigger creation.
3541 : */
3542 :
3543 : /* Add database-specific SET options */
3544 264 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3545 :
3546 : /*
3547 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3548 : * entry, too, for lack of a better place.
3549 : */
3550 264 : if (dopt->binary_upgrade)
3551 : {
3552 60 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3553 60 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3554 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3555 : "WHERE datname = ",
3556 : frozenxid, minmxid);
3557 60 : appendStringLiteralAH(creaQry, datname, fout);
3558 60 : appendPQExpBufferStr(creaQry, ";\n");
3559 : }
3560 :
3561 264 : if (creaQry->len > 0)
3562 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3563 84 : ARCHIVE_OPTS(.tag = datname,
3564 : .owner = dba,
3565 : .description = "DATABASE PROPERTIES",
3566 : .section = SECTION_PRE_DATA,
3567 : .createStmt = creaQry->data,
3568 : .dropStmt = delQry->data,
3569 : .deps = &dbDumpId));
3570 :
3571 : /*
3572 : * pg_largeobject comes from the old system intact, so set its
3573 : * relfrozenxids, relminmxids and relfilenode.
3574 : */
3575 264 : if (dopt->binary_upgrade)
3576 : {
3577 : PGresult *lo_res;
3578 60 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3579 60 : PQExpBuffer loOutQry = createPQExpBuffer();
3580 60 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3581 : int ii_relfrozenxid,
3582 : ii_relfilenode,
3583 : ii_oid,
3584 : ii_relminmxid;
3585 :
3586 : /*
3587 : * pg_largeobject
3588 : */
3589 60 : if (fout->remoteVersion >= 90300)
3590 60 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3591 : "FROM pg_catalog.pg_class\n"
3592 : "WHERE oid IN (%u, %u);\n",
3593 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3594 : else
3595 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3596 : "FROM pg_catalog.pg_class\n"
3597 : "WHERE oid IN (%u, %u);\n",
3598 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3599 :
3600 60 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3601 :
3602 60 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3603 60 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3604 60 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3605 60 : ii_oid = PQfnumber(lo_res, "oid");
3606 :
3607 60 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3608 60 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3609 180 : for (int i = 0; i < PQntuples(lo_res); ++i)
3610 : {
3611 : Oid oid;
3612 : RelFileNumber relfilenumber;
3613 :
3614 120 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3615 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3616 : "WHERE oid = %u;\n",
3617 120 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3618 120 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3619 120 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3620 :
3621 120 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3622 120 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3623 :
3624 120 : if (oid == LargeObjectRelationId)
3625 60 : appendPQExpBuffer(loOutQry,
3626 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3627 : relfilenumber);
3628 60 : else if (oid == LargeObjectLOidPNIndexId)
3629 60 : appendPQExpBuffer(loOutQry,
3630 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3631 : relfilenumber);
3632 : }
3633 :
3634 60 : appendPQExpBufferStr(loOutQry,
3635 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3636 60 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3637 :
3638 60 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3639 60 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3640 : .description = "pg_largeobject",
3641 : .section = SECTION_PRE_DATA,
3642 : .createStmt = loOutQry->data));
3643 :
3644 60 : PQclear(lo_res);
3645 :
3646 60 : destroyPQExpBuffer(loFrozenQry);
3647 60 : destroyPQExpBuffer(loHorizonQry);
3648 60 : destroyPQExpBuffer(loOutQry);
3649 : }
3650 :
3651 264 : PQclear(res);
3652 :
3653 264 : free(qdatname);
3654 264 : destroyPQExpBuffer(dbQry);
3655 264 : destroyPQExpBuffer(delQry);
3656 264 : destroyPQExpBuffer(creaQry);
3657 264 : destroyPQExpBuffer(labelq);
3658 264 : }
3659 :
3660 : /*
3661 : * Collect any database-specific or role-and-database-specific SET options
3662 : * for this database, and append them to outbuf.
3663 : */
3664 : static void
3665 264 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3666 : const char *dbname, Oid dboid)
3667 : {
3668 264 : PGconn *conn = GetConnection(AH);
3669 264 : PQExpBuffer buf = createPQExpBuffer();
3670 : PGresult *res;
3671 :
3672 : /* First collect database-specific options */
3673 264 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3674 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3675 : dboid);
3676 :
3677 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3678 :
3679 336 : for (int i = 0; i < PQntuples(res); i++)
3680 72 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3681 : "DATABASE", dbname, NULL, NULL,
3682 : outbuf);
3683 :
3684 264 : PQclear(res);
3685 :
3686 : /* Now look for role-and-database-specific options */
3687 264 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3688 : "FROM pg_db_role_setting s, pg_roles r "
3689 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3690 : dboid);
3691 :
3692 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3693 :
3694 264 : for (int i = 0; i < PQntuples(res); i++)
3695 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3696 0 : "ROLE", PQgetvalue(res, i, 0),
3697 : "DATABASE", dbname,
3698 : outbuf);
3699 :
3700 264 : PQclear(res);
3701 :
3702 264 : destroyPQExpBuffer(buf);
3703 264 : }
3704 :
3705 : /*
3706 : * dumpEncoding: put the correct encoding into the archive
3707 : */
3708 : static void
3709 468 : dumpEncoding(Archive *AH)
3710 : {
3711 468 : const char *encname = pg_encoding_to_char(AH->encoding);
3712 468 : PQExpBuffer qry = createPQExpBuffer();
3713 :
3714 468 : pg_log_info("saving encoding = %s", encname);
3715 :
3716 468 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3717 468 : appendStringLiteralAH(qry, encname, AH);
3718 468 : appendPQExpBufferStr(qry, ";\n");
3719 :
3720 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3721 468 : ARCHIVE_OPTS(.tag = "ENCODING",
3722 : .description = "ENCODING",
3723 : .section = SECTION_PRE_DATA,
3724 : .createStmt = qry->data));
3725 :
3726 468 : destroyPQExpBuffer(qry);
3727 468 : }
3728 :
3729 :
3730 : /*
3731 : * dumpStdStrings: put the correct escape string behavior into the archive
3732 : */
3733 : static void
3734 468 : dumpStdStrings(Archive *AH)
3735 : {
3736 468 : const char *stdstrings = AH->std_strings ? "on" : "off";
3737 468 : PQExpBuffer qry = createPQExpBuffer();
3738 :
3739 468 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3740 : stdstrings);
3741 :
3742 468 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3743 : stdstrings);
3744 :
3745 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3746 468 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3747 : .description = "STDSTRINGS",
3748 : .section = SECTION_PRE_DATA,
3749 : .createStmt = qry->data));
3750 :
3751 468 : destroyPQExpBuffer(qry);
3752 468 : }
3753 :
3754 : /*
3755 : * dumpSearchPath: record the active search_path in the archive
3756 : */
3757 : static void
3758 468 : dumpSearchPath(Archive *AH)
3759 : {
3760 468 : PQExpBuffer qry = createPQExpBuffer();
3761 468 : PQExpBuffer path = createPQExpBuffer();
3762 : PGresult *res;
3763 468 : char **schemanames = NULL;
3764 468 : int nschemanames = 0;
3765 : int i;
3766 :
3767 : /*
3768 : * We use the result of current_schemas(), not the search_path GUC,
3769 : * because that might contain wildcards such as "$user", which won't
3770 : * necessarily have the same value during restore. Also, this way avoids
3771 : * listing schemas that may appear in search_path but not actually exist,
3772 : * which seems like a prudent exclusion.
3773 : */
3774 468 : res = ExecuteSqlQueryForSingleRow(AH,
3775 : "SELECT pg_catalog.current_schemas(false)");
3776 :
3777 468 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3778 0 : pg_fatal("could not parse result of current_schemas()");
3779 :
3780 : /*
3781 : * We use set_config(), not a simple "SET search_path" command, because
3782 : * the latter has less-clean behavior if the search path is empty. While
3783 : * that's likely to get fixed at some point, it seems like a good idea to
3784 : * be as backwards-compatible as possible in what we put into archives.
3785 : */
3786 468 : for (i = 0; i < nschemanames; i++)
3787 : {
3788 0 : if (i > 0)
3789 0 : appendPQExpBufferStr(path, ", ");
3790 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3791 : }
3792 :
3793 468 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3794 468 : appendStringLiteralAH(qry, path->data, AH);
3795 468 : appendPQExpBufferStr(qry, ", false);\n");
3796 :
3797 468 : pg_log_info("saving \"search_path = %s\"", path->data);
3798 :
3799 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3800 468 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3801 : .description = "SEARCHPATH",
3802 : .section = SECTION_PRE_DATA,
3803 : .createStmt = qry->data));
3804 :
3805 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3806 468 : AH->searchpath = pg_strdup(qry->data);
3807 :
3808 468 : free(schemanames);
3809 468 : PQclear(res);
3810 468 : destroyPQExpBuffer(qry);
3811 468 : destroyPQExpBuffer(path);
3812 468 : }
3813 :
3814 :
3815 : /*
3816 : * getLOs:
3817 : * Collect schema-level data about large objects
3818 : */
3819 : static void
3820 410 : getLOs(Archive *fout)
3821 : {
3822 410 : DumpOptions *dopt = fout->dopt;
3823 410 : PQExpBuffer loQry = createPQExpBuffer();
3824 : PGresult *res;
3825 : int ntups;
3826 : int i;
3827 : int n;
3828 : int i_oid;
3829 : int i_lomowner;
3830 : int i_lomacl;
3831 : int i_acldefault;
3832 :
3833 410 : pg_log_info("reading large objects");
3834 :
3835 : /*
3836 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3837 : * with the same owner/ACL appear together.
3838 : */
3839 410 : appendPQExpBufferStr(loQry,
3840 : "SELECT oid, lomowner, lomacl, "
3841 : "acldefault('L', lomowner) AS acldefault "
3842 : "FROM pg_largeobject_metadata "
3843 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3844 :
3845 410 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3846 :
3847 410 : i_oid = PQfnumber(res, "oid");
3848 410 : i_lomowner = PQfnumber(res, "lomowner");
3849 410 : i_lomacl = PQfnumber(res, "lomacl");
3850 410 : i_acldefault = PQfnumber(res, "acldefault");
3851 :
3852 410 : ntups = PQntuples(res);
3853 :
3854 : /*
3855 : * Group the blobs into suitably-sized groups that have the same owner and
3856 : * ACL setting, and build a metadata and a data DumpableObject for each
3857 : * group. (If we supported initprivs for blobs, we'd have to insist that
3858 : * groups also share initprivs settings, since the DumpableObject only has
3859 : * room for one.) i is the index of the first tuple in the current group,
3860 : * and n is the number of tuples we include in the group.
3861 : */
3862 574 : for (i = 0; i < ntups; i += n)
3863 : {
3864 164 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3865 164 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3866 164 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3867 : LoInfo *loinfo;
3868 : DumpableObject *lodata;
3869 : char namebuf[64];
3870 :
3871 : /* Scan to find first tuple not to be included in group */
3872 164 : n = 1;
3873 196 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3874 : {
3875 106 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3876 106 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3877 : break;
3878 32 : n++;
3879 : }
3880 :
3881 : /* Build the metadata DumpableObject */
3882 164 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3883 :
3884 164 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3885 164 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3886 164 : loinfo->dobj.catId.oid = thisoid;
3887 164 : AssignDumpId(&loinfo->dobj);
3888 :
3889 164 : if (n > 1)
3890 16 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3891 16 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3892 : else
3893 148 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3894 164 : loinfo->dobj.name = pg_strdup(namebuf);
3895 164 : loinfo->dacl.acl = pg_strdup(thisacl);
3896 164 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3897 164 : loinfo->dacl.privtype = 0;
3898 164 : loinfo->dacl.initprivs = NULL;
3899 164 : loinfo->rolname = getRoleName(thisowner);
3900 164 : loinfo->numlos = n;
3901 164 : loinfo->looids[0] = thisoid;
3902 : /* Collect OIDs of the remaining blobs in this group */
3903 196 : for (int k = 1; k < n; k++)
3904 : {
3905 : CatalogId extraID;
3906 :
3907 32 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3908 :
3909 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3910 32 : extraID.tableoid = LargeObjectRelationId;
3911 32 : extraID.oid = loinfo->looids[k];
3912 32 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3913 : }
3914 :
3915 : /* LOs have data */
3916 164 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3917 :
3918 : /* Mark whether LO group has a non-empty ACL */
3919 164 : if (!PQgetisnull(res, i, i_lomacl))
3920 74 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3921 :
3922 : /*
3923 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3924 : * as it will be copied by pg_upgrade, which simply copies the
3925 : * pg_largeobject table. We *do* however dump out anything but the
3926 : * data, as pg_upgrade copies just pg_largeobject, but not
3927 : * pg_largeobject_metadata, after the dump is restored.
3928 : */
3929 164 : if (dopt->binary_upgrade)
3930 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3931 :
3932 : /*
3933 : * Create a "BLOBS" data item for the group, too. This is just a
3934 : * placeholder for sorting; it carries no data now.
3935 : */
3936 164 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3937 164 : lodata->objType = DO_LARGE_OBJECT_DATA;
3938 164 : lodata->catId = nilCatalogId;
3939 164 : AssignDumpId(lodata);
3940 164 : lodata->name = pg_strdup(namebuf);
3941 164 : lodata->components |= DUMP_COMPONENT_DATA;
3942 : /* Set up explicit dependency from data to metadata */
3943 164 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3944 164 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3945 164 : lodata->nDeps = lodata->allocDeps = 1;
3946 : }
3947 :
3948 410 : PQclear(res);
3949 410 : destroyPQExpBuffer(loQry);
3950 410 : }
3951 :
3952 : /*
3953 : * dumpLO
3954 : *
3955 : * dump the definition (metadata) of the given large object group
3956 : */
3957 : static void
3958 164 : dumpLO(Archive *fout, const LoInfo *loinfo)
3959 : {
3960 164 : PQExpBuffer cquery = createPQExpBuffer();
3961 :
3962 : /*
3963 : * The "definition" is just a newline-separated list of OIDs. We need to
3964 : * put something into the dropStmt too, but it can just be a comment.
3965 : */
3966 360 : for (int i = 0; i < loinfo->numlos; i++)
3967 196 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3968 :
3969 164 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3970 164 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3971 164 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3972 : .owner = loinfo->rolname,
3973 : .description = "BLOB METADATA",
3974 : .section = SECTION_DATA,
3975 : .createStmt = cquery->data,
3976 : .dropStmt = "-- dummy"));
3977 :
3978 : /*
3979 : * Dump per-blob comments and seclabels if any. We assume these are rare
3980 : * enough that it's okay to generate retail TOC entries for them.
3981 : */
3982 164 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3983 : DUMP_COMPONENT_SECLABEL))
3984 : {
3985 212 : for (int i = 0; i < loinfo->numlos; i++)
3986 : {
3987 : CatalogId catId;
3988 : char namebuf[32];
3989 :
3990 : /* Build identifying info for this blob */
3991 122 : catId.tableoid = loinfo->dobj.catId.tableoid;
3992 122 : catId.oid = loinfo->looids[i];
3993 122 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3994 :
3995 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3996 122 : dumpComment(fout, "LARGE OBJECT", namebuf,
3997 122 : NULL, loinfo->rolname,
3998 122 : catId, 0, loinfo->dobj.dumpId);
3999 :
4000 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4001 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4002 0 : NULL, loinfo->rolname,
4003 0 : catId, 0, loinfo->dobj.dumpId);
4004 : }
4005 : }
4006 :
4007 : /*
4008 : * Dump the ACLs if any (remember that all blobs in the group will have
4009 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4010 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4011 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4012 : * string to emit a mutated version for each blob.
4013 : */
4014 164 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4015 : {
4016 : char namebuf[32];
4017 :
4018 : /* Build identifying info for the first blob */
4019 74 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4020 :
4021 74 : if (loinfo->numlos > 1)
4022 : {
4023 : char tagbuf[64];
4024 :
4025 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4026 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4027 :
4028 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4029 : "LARGE OBJECT", namebuf, NULL, NULL,
4030 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4031 : }
4032 : else
4033 : {
4034 74 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4035 : "LARGE OBJECT", namebuf, NULL, NULL,
4036 74 : NULL, loinfo->rolname, &loinfo->dacl);
4037 : }
4038 : }
4039 :
4040 164 : destroyPQExpBuffer(cquery);
4041 164 : }
4042 :
4043 : /*
4044 : * dumpLOs:
4045 : * dump the data contents of the large objects in the given group
4046 : */
4047 : static int
4048 150 : dumpLOs(Archive *fout, const void *arg)
4049 : {
4050 150 : const LoInfo *loinfo = (const LoInfo *) arg;
4051 150 : PGconn *conn = GetConnection(fout);
4052 : char buf[LOBBUFSIZE];
4053 :
4054 150 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4055 :
4056 328 : for (int i = 0; i < loinfo->numlos; i++)
4057 : {
4058 178 : Oid loOid = loinfo->looids[i];
4059 : int loFd;
4060 : int cnt;
4061 :
4062 : /* Open the LO */
4063 178 : loFd = lo_open(conn, loOid, INV_READ);
4064 178 : if (loFd == -1)
4065 0 : pg_fatal("could not open large object %u: %s",
4066 : loOid, PQerrorMessage(conn));
4067 :
4068 178 : StartLO(fout, loOid);
4069 :
4070 : /* Now read it in chunks, sending data to archive */
4071 : do
4072 : {
4073 274 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4074 274 : if (cnt < 0)
4075 0 : pg_fatal("error reading large object %u: %s",
4076 : loOid, PQerrorMessage(conn));
4077 :
4078 274 : WriteData(fout, buf, cnt);
4079 274 : } while (cnt > 0);
4080 :
4081 178 : lo_close(conn, loFd);
4082 :
4083 178 : EndLO(fout, loOid);
4084 : }
4085 :
4086 150 : return 1;
4087 : }
4088 :
4089 : /*
4090 : * getPolicies
4091 : * get information about all RLS policies on dumpable tables.
4092 : */
4093 : void
4094 468 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4095 : {
4096 468 : DumpOptions *dopt = fout->dopt;
4097 : PQExpBuffer query;
4098 : PQExpBuffer tbloids;
4099 : PGresult *res;
4100 : PolicyInfo *polinfo;
4101 : int i_oid;
4102 : int i_tableoid;
4103 : int i_polrelid;
4104 : int i_polname;
4105 : int i_polcmd;
4106 : int i_polpermissive;
4107 : int i_polroles;
4108 : int i_polqual;
4109 : int i_polwithcheck;
4110 : int i,
4111 : j,
4112 : ntups;
4113 :
4114 : /* No policies before 9.5 */
4115 468 : if (fout->remoteVersion < 90500)
4116 0 : return;
4117 :
4118 : /* Skip if --no-policies was specified */
4119 468 : if (dopt->no_policies)
4120 2 : return;
4121 :
4122 466 : query = createPQExpBuffer();
4123 466 : tbloids = createPQExpBuffer();
4124 :
4125 : /*
4126 : * Identify tables of interest, and check which ones have RLS enabled.
4127 : */
4128 466 : appendPQExpBufferChar(tbloids, '{');
4129 123174 : for (i = 0; i < numTables; i++)
4130 : {
4131 122708 : TableInfo *tbinfo = &tblinfo[i];
4132 :
4133 : /* Ignore row security on tables not to be dumped */
4134 122708 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4135 103508 : continue;
4136 :
4137 : /* It can't have RLS or policies if it's not a table */
4138 19200 : if (tbinfo->relkind != RELKIND_RELATION &&
4139 5200 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4140 3554 : continue;
4141 :
4142 : /* Add it to the list of table OIDs to be probed below */
4143 15646 : if (tbloids->len > 1) /* do we have more than the '{'? */
4144 15334 : appendPQExpBufferChar(tbloids, ',');
4145 15646 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4146 :
4147 : /* Is RLS enabled? (That's separate from whether it has policies) */
4148 15646 : if (tbinfo->rowsec)
4149 : {
4150 132 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4151 :
4152 : /*
4153 : * We represent RLS being enabled on a table by creating a
4154 : * PolicyInfo object with null polname.
4155 : *
4156 : * Note: use tableoid 0 so that this object won't be mistaken for
4157 : * something that pg_depend entries apply to.
4158 : */
4159 132 : polinfo = pg_malloc(sizeof(PolicyInfo));
4160 132 : polinfo->dobj.objType = DO_POLICY;
4161 132 : polinfo->dobj.catId.tableoid = 0;
4162 132 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4163 132 : AssignDumpId(&polinfo->dobj);
4164 132 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4165 132 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4166 132 : polinfo->poltable = tbinfo;
4167 132 : polinfo->polname = NULL;
4168 132 : polinfo->polcmd = '\0';
4169 132 : polinfo->polpermissive = 0;
4170 132 : polinfo->polroles = NULL;
4171 132 : polinfo->polqual = NULL;
4172 132 : polinfo->polwithcheck = NULL;
4173 : }
4174 : }
4175 466 : appendPQExpBufferChar(tbloids, '}');
4176 :
4177 : /*
4178 : * Now, read all RLS policies belonging to the tables of interest, and
4179 : * create PolicyInfo objects for them. (Note that we must filter the
4180 : * results server-side not locally, because we dare not apply pg_get_expr
4181 : * to tables we don't have lock on.)
4182 : */
4183 466 : pg_log_info("reading row-level security policies");
4184 :
4185 466 : printfPQExpBuffer(query,
4186 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4187 466 : if (fout->remoteVersion >= 100000)
4188 466 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4189 : else
4190 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4191 466 : appendPQExpBuffer(query,
4192 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4193 : " 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, "
4194 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4195 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4196 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4197 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4198 : tbloids->data);
4199 :
4200 466 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4201 :
4202 466 : ntups = PQntuples(res);
4203 466 : if (ntups > 0)
4204 : {
4205 100 : i_oid = PQfnumber(res, "oid");
4206 100 : i_tableoid = PQfnumber(res, "tableoid");
4207 100 : i_polrelid = PQfnumber(res, "polrelid");
4208 100 : i_polname = PQfnumber(res, "polname");
4209 100 : i_polcmd = PQfnumber(res, "polcmd");
4210 100 : i_polpermissive = PQfnumber(res, "polpermissive");
4211 100 : i_polroles = PQfnumber(res, "polroles");
4212 100 : i_polqual = PQfnumber(res, "polqual");
4213 100 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4214 :
4215 100 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4216 :
4217 748 : for (j = 0; j < ntups; j++)
4218 : {
4219 648 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4220 648 : TableInfo *tbinfo = findTableByOid(polrelid);
4221 :
4222 648 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4223 :
4224 648 : polinfo[j].dobj.objType = DO_POLICY;
4225 648 : polinfo[j].dobj.catId.tableoid =
4226 648 : atooid(PQgetvalue(res, j, i_tableoid));
4227 648 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4228 648 : AssignDumpId(&polinfo[j].dobj);
4229 648 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4230 648 : polinfo[j].poltable = tbinfo;
4231 648 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4232 648 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4233 :
4234 648 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4235 648 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4236 :
4237 648 : if (PQgetisnull(res, j, i_polroles))
4238 312 : polinfo[j].polroles = NULL;
4239 : else
4240 336 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4241 :
4242 648 : if (PQgetisnull(res, j, i_polqual))
4243 84 : polinfo[j].polqual = NULL;
4244 : else
4245 564 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4246 :
4247 648 : if (PQgetisnull(res, j, i_polwithcheck))
4248 348 : polinfo[j].polwithcheck = NULL;
4249 : else
4250 300 : polinfo[j].polwithcheck
4251 300 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4252 : }
4253 : }
4254 :
4255 466 : PQclear(res);
4256 :
4257 466 : destroyPQExpBuffer(query);
4258 466 : destroyPQExpBuffer(tbloids);
4259 : }
4260 :
4261 : /*
4262 : * dumpPolicy
4263 : * dump the definition of the given policy
4264 : */
4265 : static void
4266 780 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4267 : {
4268 780 : DumpOptions *dopt = fout->dopt;
4269 780 : TableInfo *tbinfo = polinfo->poltable;
4270 : PQExpBuffer query;
4271 : PQExpBuffer delqry;
4272 : PQExpBuffer polprefix;
4273 : char *qtabname;
4274 : const char *cmd;
4275 : char *tag;
4276 :
4277 : /* Do nothing if not dumping schema */
4278 780 : if (!dopt->dumpSchema)
4279 98 : return;
4280 :
4281 : /*
4282 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4283 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4284 : * ROW LEVEL SECURITY.
4285 : */
4286 682 : if (polinfo->polname == NULL)
4287 : {
4288 118 : query = createPQExpBuffer();
4289 :
4290 118 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4291 118 : fmtQualifiedDumpable(tbinfo));
4292 :
4293 : /*
4294 : * We must emit the ROW SECURITY object's dependency on its table
4295 : * explicitly, because it will not match anything in pg_depend (unlike
4296 : * the case for other PolicyInfo objects).
4297 : */
4298 118 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4299 118 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4300 118 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4301 : .namespace = polinfo->dobj.namespace->dobj.name,
4302 : .owner = tbinfo->rolname,
4303 : .description = "ROW SECURITY",
4304 : .section = SECTION_POST_DATA,
4305 : .createStmt = query->data,
4306 : .deps = &(tbinfo->dobj.dumpId),
4307 : .nDeps = 1));
4308 :
4309 118 : destroyPQExpBuffer(query);
4310 118 : return;
4311 : }
4312 :
4313 564 : if (polinfo->polcmd == '*')
4314 188 : cmd = "";
4315 376 : else if (polinfo->polcmd == 'r')
4316 102 : cmd = " FOR SELECT";
4317 274 : else if (polinfo->polcmd == 'a')
4318 70 : cmd = " FOR INSERT";
4319 204 : else if (polinfo->polcmd == 'w')
4320 102 : cmd = " FOR UPDATE";
4321 102 : else if (polinfo->polcmd == 'd')
4322 102 : cmd = " FOR DELETE";
4323 : else
4324 0 : pg_fatal("unexpected policy command type: %c",
4325 : polinfo->polcmd);
4326 :
4327 564 : query = createPQExpBuffer();
4328 564 : delqry = createPQExpBuffer();
4329 564 : polprefix = createPQExpBuffer();
4330 :
4331 564 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4332 :
4333 564 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4334 :
4335 564 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4336 564 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4337 :
4338 564 : if (polinfo->polroles != NULL)
4339 280 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4340 :
4341 564 : if (polinfo->polqual != NULL)
4342 494 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4343 :
4344 564 : if (polinfo->polwithcheck != NULL)
4345 258 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4346 :
4347 564 : appendPQExpBufferStr(query, ";\n");
4348 :
4349 564 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4350 564 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4351 :
4352 564 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4353 564 : fmtId(polinfo->polname));
4354 :
4355 564 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4356 :
4357 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4358 564 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4359 564 : ARCHIVE_OPTS(.tag = tag,
4360 : .namespace = polinfo->dobj.namespace->dobj.name,
4361 : .owner = tbinfo->rolname,
4362 : .description = "POLICY",
4363 : .section = SECTION_POST_DATA,
4364 : .createStmt = query->data,
4365 : .dropStmt = delqry->data));
4366 :
4367 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4368 0 : dumpComment(fout, polprefix->data, qtabname,
4369 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4370 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4371 :
4372 564 : free(tag);
4373 564 : destroyPQExpBuffer(query);
4374 564 : destroyPQExpBuffer(delqry);
4375 564 : destroyPQExpBuffer(polprefix);
4376 564 : free(qtabname);
4377 : }
4378 :
4379 : /*
4380 : * getPublications
4381 : * get information about publications
4382 : */
4383 : void
4384 468 : getPublications(Archive *fout)
4385 : {
4386 468 : DumpOptions *dopt = fout->dopt;
4387 : PQExpBuffer query;
4388 : PGresult *res;
4389 : PublicationInfo *pubinfo;
4390 : int i_tableoid;
4391 : int i_oid;
4392 : int i_pubname;
4393 : int i_pubowner;
4394 : int i_puballtables;
4395 : int i_pubinsert;
4396 : int i_pubupdate;
4397 : int i_pubdelete;
4398 : int i_pubtruncate;
4399 : int i_pubviaroot;
4400 : int i_pubgencols;
4401 : int i,
4402 : ntups;
4403 :
4404 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4405 0 : return;
4406 :
4407 468 : query = createPQExpBuffer();
4408 :
4409 : /* Get the publications. */
4410 468 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4411 : "p.pubowner, p.puballtables, p.pubinsert, "
4412 : "p.pubupdate, p.pubdelete, ");
4413 :
4414 468 : if (fout->remoteVersion >= 110000)
4415 468 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4416 : else
4417 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4418 :
4419 468 : if (fout->remoteVersion >= 130000)
4420 468 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4421 : else
4422 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4423 :
4424 468 : if (fout->remoteVersion >= 180000)
4425 468 : appendPQExpBufferStr(query, "p.pubgencols ");
4426 : else
4427 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4428 :
4429 468 : appendPQExpBufferStr(query, "FROM pg_publication p");
4430 :
4431 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4432 :
4433 468 : ntups = PQntuples(res);
4434 :
4435 468 : if (ntups == 0)
4436 368 : goto cleanup;
4437 :
4438 100 : i_tableoid = PQfnumber(res, "tableoid");
4439 100 : i_oid = PQfnumber(res, "oid");
4440 100 : i_pubname = PQfnumber(res, "pubname");
4441 100 : i_pubowner = PQfnumber(res, "pubowner");
4442 100 : i_puballtables = PQfnumber(res, "puballtables");
4443 100 : i_pubinsert = PQfnumber(res, "pubinsert");
4444 100 : i_pubupdate = PQfnumber(res, "pubupdate");
4445 100 : i_pubdelete = PQfnumber(res, "pubdelete");
4446 100 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4447 100 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4448 100 : i_pubgencols = PQfnumber(res, "pubgencols");
4449 :
4450 100 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4451 :
4452 592 : for (i = 0; i < ntups; i++)
4453 : {
4454 492 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4455 492 : pubinfo[i].dobj.catId.tableoid =
4456 492 : atooid(PQgetvalue(res, i, i_tableoid));
4457 492 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4458 492 : AssignDumpId(&pubinfo[i].dobj);
4459 492 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4460 492 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4461 492 : pubinfo[i].puballtables =
4462 492 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4463 492 : pubinfo[i].pubinsert =
4464 492 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4465 492 : pubinfo[i].pubupdate =
4466 492 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4467 492 : pubinfo[i].pubdelete =
4468 492 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4469 492 : pubinfo[i].pubtruncate =
4470 492 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4471 492 : pubinfo[i].pubviaroot =
4472 492 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4473 492 : pubinfo[i].pubgencols_type =
4474 492 : *(PQgetvalue(res, i, i_pubgencols));
4475 :
4476 : /* Decide whether we want to dump it */
4477 492 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4478 : }
4479 :
4480 100 : cleanup:
4481 468 : PQclear(res);
4482 :
4483 468 : destroyPQExpBuffer(query);
4484 : }
4485 :
4486 : /*
4487 : * dumpPublication
4488 : * dump the definition of the given publication
4489 : */
4490 : static void
4491 412 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4492 : {
4493 412 : DumpOptions *dopt = fout->dopt;
4494 : PQExpBuffer delq;
4495 : PQExpBuffer query;
4496 : char *qpubname;
4497 412 : bool first = true;
4498 :
4499 : /* Do nothing if not dumping schema */
4500 412 : if (!dopt->dumpSchema)
4501 60 : return;
4502 :
4503 352 : delq = createPQExpBuffer();
4504 352 : query = createPQExpBuffer();
4505 :
4506 352 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4507 :
4508 352 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4509 : qpubname);
4510 :
4511 352 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4512 : qpubname);
4513 :
4514 352 : if (pubinfo->puballtables)
4515 72 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4516 :
4517 352 : appendPQExpBufferStr(query, " WITH (publish = '");
4518 352 : if (pubinfo->pubinsert)
4519 : {
4520 282 : appendPQExpBufferStr(query, "insert");
4521 282 : first = false;
4522 : }
4523 :
4524 352 : if (pubinfo->pubupdate)
4525 : {
4526 282 : if (!first)
4527 282 : appendPQExpBufferStr(query, ", ");
4528 :
4529 282 : appendPQExpBufferStr(query, "update");
4530 282 : first = false;
4531 : }
4532 :
4533 352 : if (pubinfo->pubdelete)
4534 : {
4535 282 : if (!first)
4536 282 : appendPQExpBufferStr(query, ", ");
4537 :
4538 282 : appendPQExpBufferStr(query, "delete");
4539 282 : first = false;
4540 : }
4541 :
4542 352 : if (pubinfo->pubtruncate)
4543 : {
4544 282 : if (!first)
4545 282 : appendPQExpBufferStr(query, ", ");
4546 :
4547 282 : appendPQExpBufferStr(query, "truncate");
4548 282 : first = false;
4549 : }
4550 :
4551 352 : appendPQExpBufferChar(query, '\'');
4552 :
4553 352 : if (pubinfo->pubviaroot)
4554 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4555 :
4556 352 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4557 70 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4558 :
4559 352 : appendPQExpBufferStr(query, ");\n");
4560 :
4561 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4562 352 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4563 352 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4564 : .owner = pubinfo->rolname,
4565 : .description = "PUBLICATION",
4566 : .section = SECTION_POST_DATA,
4567 : .createStmt = query->data,
4568 : .dropStmt = delq->data));
4569 :
4570 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4571 70 : dumpComment(fout, "PUBLICATION", qpubname,
4572 70 : NULL, pubinfo->rolname,
4573 70 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4574 :
4575 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4576 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4577 0 : NULL, pubinfo->rolname,
4578 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4579 :
4580 352 : destroyPQExpBuffer(delq);
4581 352 : destroyPQExpBuffer(query);
4582 352 : free(qpubname);
4583 : }
4584 :
4585 : /*
4586 : * getPublicationNamespaces
4587 : * get information about publication membership for dumpable schemas.
4588 : */
4589 : void
4590 468 : getPublicationNamespaces(Archive *fout)
4591 : {
4592 : PQExpBuffer query;
4593 : PGresult *res;
4594 : PublicationSchemaInfo *pubsinfo;
4595 468 : DumpOptions *dopt = fout->dopt;
4596 : int i_tableoid;
4597 : int i_oid;
4598 : int i_pnpubid;
4599 : int i_pnnspid;
4600 : int i,
4601 : j,
4602 : ntups;
4603 :
4604 468 : if (dopt->no_publications || fout->remoteVersion < 150000)
4605 0 : return;
4606 :
4607 468 : query = createPQExpBuffer();
4608 :
4609 : /* Collect all publication membership info. */
4610 468 : appendPQExpBufferStr(query,
4611 : "SELECT tableoid, oid, pnpubid, pnnspid "
4612 : "FROM pg_catalog.pg_publication_namespace");
4613 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4614 :
4615 468 : ntups = PQntuples(res);
4616 :
4617 468 : i_tableoid = PQfnumber(res, "tableoid");
4618 468 : i_oid = PQfnumber(res, "oid");
4619 468 : i_pnpubid = PQfnumber(res, "pnpubid");
4620 468 : i_pnnspid = PQfnumber(res, "pnnspid");
4621 :
4622 : /* this allocation may be more than we need */
4623 468 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4624 468 : j = 0;
4625 :
4626 664 : for (i = 0; i < ntups; i++)
4627 : {
4628 196 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4629 196 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4630 : PublicationInfo *pubinfo;
4631 : NamespaceInfo *nspinfo;
4632 :
4633 : /*
4634 : * Ignore any entries for which we aren't interested in either the
4635 : * publication or the rel.
4636 : */
4637 196 : pubinfo = findPublicationByOid(pnpubid);
4638 196 : if (pubinfo == NULL)
4639 0 : continue;
4640 196 : nspinfo = findNamespaceByOid(pnnspid);
4641 196 : if (nspinfo == NULL)
4642 0 : continue;
4643 :
4644 : /* OK, make a DumpableObject for this relationship */
4645 196 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4646 196 : pubsinfo[j].dobj.catId.tableoid =
4647 196 : atooid(PQgetvalue(res, i, i_tableoid));
4648 196 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4649 196 : AssignDumpId(&pubsinfo[j].dobj);
4650 196 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4651 196 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4652 196 : pubsinfo[j].publication = pubinfo;
4653 196 : pubsinfo[j].pubschema = nspinfo;
4654 :
4655 : /* Decide whether we want to dump it */
4656 196 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4657 :
4658 196 : j++;
4659 : }
4660 :
4661 468 : PQclear(res);
4662 468 : destroyPQExpBuffer(query);
4663 : }
4664 :
4665 : /*
4666 : * getPublicationTables
4667 : * get information about publication membership for dumpable tables.
4668 : */
4669 : void
4670 468 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4671 : {
4672 : PQExpBuffer query;
4673 : PGresult *res;
4674 : PublicationRelInfo *pubrinfo;
4675 468 : DumpOptions *dopt = fout->dopt;
4676 : int i_tableoid;
4677 : int i_oid;
4678 : int i_prpubid;
4679 : int i_prrelid;
4680 : int i_prrelqual;
4681 : int i_prattrs;
4682 : int i,
4683 : j,
4684 : ntups;
4685 :
4686 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4687 0 : return;
4688 :
4689 468 : query = createPQExpBuffer();
4690 :
4691 : /* Collect all publication membership info. */
4692 468 : if (fout->remoteVersion >= 150000)
4693 468 : appendPQExpBufferStr(query,
4694 : "SELECT tableoid, oid, prpubid, prrelid, "
4695 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4696 : "(CASE\n"
4697 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4698 : " (SELECT array_agg(attname)\n"
4699 : " FROM\n"
4700 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4701 : " pg_catalog.pg_attribute\n"
4702 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4703 : " ELSE NULL END) prattrs "
4704 : "FROM pg_catalog.pg_publication_rel pr");
4705 : else
4706 0 : appendPQExpBufferStr(query,
4707 : "SELECT tableoid, oid, prpubid, prrelid, "
4708 : "NULL AS prrelqual, NULL AS prattrs "
4709 : "FROM pg_catalog.pg_publication_rel");
4710 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4711 :
4712 468 : ntups = PQntuples(res);
4713 :
4714 468 : i_tableoid = PQfnumber(res, "tableoid");
4715 468 : i_oid = PQfnumber(res, "oid");
4716 468 : i_prpubid = PQfnumber(res, "prpubid");
4717 468 : i_prrelid = PQfnumber(res, "prrelid");
4718 468 : i_prrelqual = PQfnumber(res, "prrelqual");
4719 468 : i_prattrs = PQfnumber(res, "prattrs");
4720 :
4721 : /* this allocation may be more than we need */
4722 468 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4723 468 : j = 0;
4724 :
4725 1154 : for (i = 0; i < ntups; i++)
4726 : {
4727 686 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4728 686 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4729 : PublicationInfo *pubinfo;
4730 : TableInfo *tbinfo;
4731 :
4732 : /*
4733 : * Ignore any entries for which we aren't interested in either the
4734 : * publication or the rel.
4735 : */
4736 686 : pubinfo = findPublicationByOid(prpubid);
4737 686 : if (pubinfo == NULL)
4738 0 : continue;
4739 686 : tbinfo = findTableByOid(prrelid);
4740 686 : if (tbinfo == NULL)
4741 0 : continue;
4742 :
4743 : /* OK, make a DumpableObject for this relationship */
4744 686 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4745 686 : pubrinfo[j].dobj.catId.tableoid =
4746 686 : atooid(PQgetvalue(res, i, i_tableoid));
4747 686 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4748 686 : AssignDumpId(&pubrinfo[j].dobj);
4749 686 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4750 686 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4751 686 : pubrinfo[j].publication = pubinfo;
4752 686 : pubrinfo[j].pubtable = tbinfo;
4753 686 : if (PQgetisnull(res, i, i_prrelqual))
4754 392 : pubrinfo[j].pubrelqual = NULL;
4755 : else
4756 294 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4757 :
4758 686 : if (!PQgetisnull(res, i, i_prattrs))
4759 : {
4760 : char **attnames;
4761 : int nattnames;
4762 : PQExpBuffer attribs;
4763 :
4764 196 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4765 : &attnames, &nattnames))
4766 0 : pg_fatal("could not parse %s array", "prattrs");
4767 196 : attribs = createPQExpBuffer();
4768 588 : for (int k = 0; k < nattnames; k++)
4769 : {
4770 392 : if (k > 0)
4771 196 : appendPQExpBufferStr(attribs, ", ");
4772 :
4773 392 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4774 : }
4775 196 : pubrinfo[j].pubrattrs = attribs->data;
4776 196 : free(attribs); /* but not attribs->data */
4777 196 : free(attnames);
4778 : }
4779 : else
4780 490 : pubrinfo[j].pubrattrs = NULL;
4781 :
4782 : /* Decide whether we want to dump it */
4783 686 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4784 :
4785 686 : j++;
4786 : }
4787 :
4788 468 : PQclear(res);
4789 468 : destroyPQExpBuffer(query);
4790 : }
4791 :
4792 : /*
4793 : * dumpPublicationNamespace
4794 : * dump the definition of the given publication schema mapping.
4795 : */
4796 : static void
4797 164 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4798 : {
4799 164 : DumpOptions *dopt = fout->dopt;
4800 164 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4801 164 : PublicationInfo *pubinfo = pubsinfo->publication;
4802 : PQExpBuffer query;
4803 : char *tag;
4804 :
4805 : /* Do nothing if not dumping schema */
4806 164 : if (!dopt->dumpSchema)
4807 24 : return;
4808 :
4809 140 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4810 :
4811 140 : query = createPQExpBuffer();
4812 :
4813 140 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4814 140 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4815 :
4816 : /*
4817 : * There is no point in creating drop query as the drop is done by schema
4818 : * drop.
4819 : */
4820 140 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4821 140 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4822 140 : ARCHIVE_OPTS(.tag = tag,
4823 : .namespace = schemainfo->dobj.name,
4824 : .owner = pubinfo->rolname,
4825 : .description = "PUBLICATION TABLES IN SCHEMA",
4826 : .section = SECTION_POST_DATA,
4827 : .createStmt = query->data));
4828 :
4829 : /* These objects can't currently have comments or seclabels */
4830 :
4831 140 : free(tag);
4832 140 : destroyPQExpBuffer(query);
4833 : }
4834 :
4835 : /*
4836 : * dumpPublicationTable
4837 : * dump the definition of the given publication table mapping
4838 : */
4839 : static void
4840 574 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4841 : {
4842 574 : DumpOptions *dopt = fout->dopt;
4843 574 : PublicationInfo *pubinfo = pubrinfo->publication;
4844 574 : TableInfo *tbinfo = pubrinfo->pubtable;
4845 : PQExpBuffer query;
4846 : char *tag;
4847 :
4848 : /* Do nothing if not dumping schema */
4849 574 : if (!dopt->dumpSchema)
4850 84 : return;
4851 :
4852 490 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4853 :
4854 490 : query = createPQExpBuffer();
4855 :
4856 490 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4857 490 : fmtId(pubinfo->dobj.name));
4858 490 : appendPQExpBuffer(query, " %s",
4859 490 : fmtQualifiedDumpable(tbinfo));
4860 :
4861 490 : if (pubrinfo->pubrattrs)
4862 140 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4863 :
4864 490 : if (pubrinfo->pubrelqual)
4865 : {
4866 : /*
4867 : * It's necessary to add parentheses around the expression because
4868 : * pg_get_expr won't supply the parentheses for things like WHERE
4869 : * TRUE.
4870 : */
4871 210 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4872 : }
4873 490 : appendPQExpBufferStr(query, ";\n");
4874 :
4875 : /*
4876 : * There is no point in creating a drop query as the drop is done by table
4877 : * drop. (If you think to change this, see also _printTocEntry().)
4878 : * Although this object doesn't really have ownership as such, set the
4879 : * owner field anyway to ensure that the command is run by the correct
4880 : * role at restore time.
4881 : */
4882 490 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4883 490 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4884 490 : ARCHIVE_OPTS(.tag = tag,
4885 : .namespace = tbinfo->dobj.namespace->dobj.name,
4886 : .owner = pubinfo->rolname,
4887 : .description = "PUBLICATION TABLE",
4888 : .section = SECTION_POST_DATA,
4889 : .createStmt = query->data));
4890 :
4891 : /* These objects can't currently have comments or seclabels */
4892 :
4893 490 : free(tag);
4894 490 : destroyPQExpBuffer(query);
4895 : }
4896 :
4897 : /*
4898 : * Is the currently connected user a superuser?
4899 : */
4900 : static bool
4901 468 : is_superuser(Archive *fout)
4902 : {
4903 468 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4904 : const char *val;
4905 :
4906 468 : val = PQparameterStatus(AH->connection, "is_superuser");
4907 :
4908 468 : if (val && strcmp(val, "on") == 0)
4909 462 : return true;
4910 :
4911 6 : return false;
4912 : }
4913 :
4914 : /*
4915 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4916 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4917 : * the setting query is effective only where available.
4918 : */
4919 : static void
4920 540 : set_restrict_relation_kind(Archive *AH, const char *value)
4921 : {
4922 540 : PQExpBuffer query = createPQExpBuffer();
4923 : PGresult *res;
4924 :
4925 540 : appendPQExpBuffer(query,
4926 : "SELECT set_config(name, '%s', false) "
4927 : "FROM pg_settings "
4928 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4929 : value);
4930 540 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4931 :
4932 540 : PQclear(res);
4933 540 : destroyPQExpBuffer(query);
4934 540 : }
4935 :
4936 : /*
4937 : * getSubscriptions
4938 : * get information about subscriptions
4939 : */
4940 : void
4941 468 : getSubscriptions(Archive *fout)
4942 : {
4943 468 : DumpOptions *dopt = fout->dopt;
4944 : PQExpBuffer query;
4945 : PGresult *res;
4946 : SubscriptionInfo *subinfo;
4947 : int i_tableoid;
4948 : int i_oid;
4949 : int i_subname;
4950 : int i_subowner;
4951 : int i_subbinary;
4952 : int i_substream;
4953 : int i_subtwophasestate;
4954 : int i_subdisableonerr;
4955 : int i_subpasswordrequired;
4956 : int i_subrunasowner;
4957 : int i_subconninfo;
4958 : int i_subslotname;
4959 : int i_subsynccommit;
4960 : int i_subpublications;
4961 : int i_suborigin;
4962 : int i_suboriginremotelsn;
4963 : int i_subenabled;
4964 : int i_subfailover;
4965 : int i,
4966 : ntups;
4967 :
4968 468 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4969 0 : return;
4970 :
4971 468 : if (!is_superuser(fout))
4972 : {
4973 : int n;
4974 :
4975 6 : res = ExecuteSqlQuery(fout,
4976 : "SELECT count(*) FROM pg_subscription "
4977 : "WHERE subdbid = (SELECT oid FROM pg_database"
4978 : " WHERE datname = current_database())",
4979 : PGRES_TUPLES_OK);
4980 6 : n = atoi(PQgetvalue(res, 0, 0));
4981 6 : if (n > 0)
4982 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4983 6 : PQclear(res);
4984 6 : return;
4985 : }
4986 :
4987 462 : query = createPQExpBuffer();
4988 :
4989 : /* Get the subscriptions in current database. */
4990 462 : appendPQExpBufferStr(query,
4991 : "SELECT s.tableoid, s.oid, s.subname,\n"
4992 : " s.subowner,\n"
4993 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4994 : " s.subpublications,\n");
4995 :
4996 462 : if (fout->remoteVersion >= 140000)
4997 462 : appendPQExpBufferStr(query, " s.subbinary,\n");
4998 : else
4999 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5000 :
5001 462 : if (fout->remoteVersion >= 140000)
5002 462 : appendPQExpBufferStr(query, " s.substream,\n");
5003 : else
5004 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5005 :
5006 462 : if (fout->remoteVersion >= 150000)
5007 462 : appendPQExpBufferStr(query,
5008 : " s.subtwophasestate,\n"
5009 : " s.subdisableonerr,\n");
5010 : else
5011 0 : appendPQExpBuffer(query,
5012 : " '%c' AS subtwophasestate,\n"
5013 : " false AS subdisableonerr,\n",
5014 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5015 :
5016 462 : if (fout->remoteVersion >= 160000)
5017 462 : appendPQExpBufferStr(query,
5018 : " s.subpasswordrequired,\n"
5019 : " s.subrunasowner,\n"
5020 : " s.suborigin,\n");
5021 : else
5022 0 : appendPQExpBuffer(query,
5023 : " 't' AS subpasswordrequired,\n"
5024 : " 't' AS subrunasowner,\n"
5025 : " '%s' AS suborigin,\n",
5026 : LOGICALREP_ORIGIN_ANY);
5027 :
5028 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5029 62 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5030 : " s.subenabled,\n");
5031 : else
5032 400 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5033 : " false AS subenabled,\n");
5034 :
5035 462 : if (fout->remoteVersion >= 170000)
5036 462 : appendPQExpBufferStr(query,
5037 : " s.subfailover\n");
5038 : else
5039 0 : appendPQExpBufferStr(query,
5040 : " false AS subfailover\n");
5041 :
5042 462 : appendPQExpBufferStr(query,
5043 : "FROM pg_subscription s\n");
5044 :
5045 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5046 62 : appendPQExpBufferStr(query,
5047 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5048 : " ON o.external_id = 'pg_' || s.oid::text \n");
5049 :
5050 462 : appendPQExpBufferStr(query,
5051 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5052 : " WHERE datname = current_database())");
5053 :
5054 462 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5055 :
5056 462 : ntups = PQntuples(res);
5057 :
5058 : /*
5059 : * Get subscription fields. We don't include subskiplsn in the dump as
5060 : * after restoring the dump this value may no longer be relevant.
5061 : */
5062 462 : i_tableoid = PQfnumber(res, "tableoid");
5063 462 : i_oid = PQfnumber(res, "oid");
5064 462 : i_subname = PQfnumber(res, "subname");
5065 462 : i_subowner = PQfnumber(res, "subowner");
5066 462 : i_subenabled = PQfnumber(res, "subenabled");
5067 462 : i_subbinary = PQfnumber(res, "subbinary");
5068 462 : i_substream = PQfnumber(res, "substream");
5069 462 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5070 462 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5071 462 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5072 462 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5073 462 : i_subfailover = PQfnumber(res, "subfailover");
5074 462 : i_subconninfo = PQfnumber(res, "subconninfo");
5075 462 : i_subslotname = PQfnumber(res, "subslotname");
5076 462 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5077 462 : i_subpublications = PQfnumber(res, "subpublications");
5078 462 : i_suborigin = PQfnumber(res, "suborigin");
5079 462 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5080 :
5081 462 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5082 :
5083 748 : for (i = 0; i < ntups; i++)
5084 : {
5085 286 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5086 286 : subinfo[i].dobj.catId.tableoid =
5087 286 : atooid(PQgetvalue(res, i, i_tableoid));
5088 286 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5089 286 : AssignDumpId(&subinfo[i].dobj);
5090 286 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5091 286 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5092 :
5093 286 : subinfo[i].subenabled =
5094 286 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5095 286 : subinfo[i].subbinary =
5096 286 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5097 286 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5098 286 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5099 286 : subinfo[i].subdisableonerr =
5100 286 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5101 286 : subinfo[i].subpasswordrequired =
5102 286 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5103 286 : subinfo[i].subrunasowner =
5104 286 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5105 286 : subinfo[i].subfailover =
5106 286 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5107 572 : subinfo[i].subconninfo =
5108 286 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5109 286 : if (PQgetisnull(res, i, i_subslotname))
5110 0 : subinfo[i].subslotname = NULL;
5111 : else
5112 286 : subinfo[i].subslotname =
5113 286 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5114 572 : subinfo[i].subsynccommit =
5115 286 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5116 572 : subinfo[i].subpublications =
5117 286 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5118 286 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5119 286 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5120 284 : subinfo[i].suboriginremotelsn = NULL;
5121 : else
5122 2 : subinfo[i].suboriginremotelsn =
5123 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5124 :
5125 : /* Decide whether we want to dump it */
5126 286 : selectDumpableObject(&(subinfo[i].dobj), fout);
5127 : }
5128 462 : PQclear(res);
5129 :
5130 462 : destroyPQExpBuffer(query);
5131 : }
5132 :
5133 : /*
5134 : * getSubscriptionTables
5135 : * Get information about subscription membership for dumpable tables. This
5136 : * will be used only in binary-upgrade mode for PG17 or later versions.
5137 : */
5138 : void
5139 468 : getSubscriptionTables(Archive *fout)
5140 : {
5141 468 : DumpOptions *dopt = fout->dopt;
5142 468 : SubscriptionInfo *subinfo = NULL;
5143 : SubRelInfo *subrinfo;
5144 : PGresult *res;
5145 : int i_srsubid;
5146 : int i_srrelid;
5147 : int i_srsubstate;
5148 : int i_srsublsn;
5149 : int ntups;
5150 468 : Oid last_srsubid = InvalidOid;
5151 :
5152 468 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5153 62 : fout->remoteVersion < 170000)
5154 406 : return;
5155 :
5156 62 : res = ExecuteSqlQuery(fout,
5157 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5158 : "FROM pg_catalog.pg_subscription_rel "
5159 : "ORDER BY srsubid",
5160 : PGRES_TUPLES_OK);
5161 62 : ntups = PQntuples(res);
5162 62 : if (ntups == 0)
5163 60 : goto cleanup;
5164 :
5165 : /* Get pg_subscription_rel attributes */
5166 2 : i_srsubid = PQfnumber(res, "srsubid");
5167 2 : i_srrelid = PQfnumber(res, "srrelid");
5168 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5169 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5170 :
5171 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5172 6 : for (int i = 0; i < ntups; i++)
5173 : {
5174 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5175 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5176 : TableInfo *tblinfo;
5177 :
5178 : /*
5179 : * If we switched to a new subscription, check if the subscription
5180 : * exists.
5181 : */
5182 4 : if (cur_srsubid != last_srsubid)
5183 : {
5184 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5185 4 : if (subinfo == NULL)
5186 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5187 :
5188 4 : last_srsubid = cur_srsubid;
5189 : }
5190 :
5191 4 : tblinfo = findTableByOid(relid);
5192 4 : if (tblinfo == NULL)
5193 0 : pg_fatal("failed sanity check, table with OID %u not found",
5194 : relid);
5195 :
5196 : /* OK, make a DumpableObject for this relationship */
5197 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5198 4 : subrinfo[i].dobj.catId.tableoid = relid;
5199 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5200 4 : AssignDumpId(&subrinfo[i].dobj);
5201 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5202 4 : subrinfo[i].tblinfo = tblinfo;
5203 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5204 4 : if (PQgetisnull(res, i, i_srsublsn))
5205 2 : subrinfo[i].srsublsn = NULL;
5206 : else
5207 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5208 :
5209 4 : subrinfo[i].subinfo = subinfo;
5210 :
5211 : /* Decide whether we want to dump it */
5212 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5213 : }
5214 :
5215 2 : cleanup:
5216 62 : PQclear(res);
5217 : }
5218 :
5219 : /*
5220 : * dumpSubscriptionTable
5221 : * Dump the definition of the given subscription table mapping. This will be
5222 : * used only in binary-upgrade mode for PG17 or later versions.
5223 : */
5224 : static void
5225 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5226 : {
5227 4 : DumpOptions *dopt = fout->dopt;
5228 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5229 : PQExpBuffer query;
5230 : char *tag;
5231 :
5232 : /* Do nothing if not dumping schema */
5233 4 : if (!dopt->dumpSchema)
5234 0 : return;
5235 :
5236 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5237 :
5238 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5239 :
5240 4 : query = createPQExpBuffer();
5241 :
5242 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5243 : {
5244 : /*
5245 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5246 : * to pg_subscription_rel table. This will be used only in
5247 : * binary-upgrade mode.
5248 : */
5249 4 : appendPQExpBufferStr(query,
5250 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5251 4 : appendPQExpBufferStr(query,
5252 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5253 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5254 4 : appendPQExpBuffer(query,
5255 : ", %u, '%c'",
5256 4 : subrinfo->tblinfo->dobj.catId.oid,
5257 4 : subrinfo->srsubstate);
5258 :
5259 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5260 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5261 : else
5262 2 : appendPQExpBufferStr(query, ", NULL");
5263 :
5264 4 : appendPQExpBufferStr(query, ");\n");
5265 : }
5266 :
5267 : /*
5268 : * There is no point in creating a drop query as the drop is done by table
5269 : * drop. (If you think to change this, see also _printTocEntry().)
5270 : * Although this object doesn't really have ownership as such, set the
5271 : * owner field anyway to ensure that the command is run by the correct
5272 : * role at restore time.
5273 : */
5274 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5275 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5276 4 : ARCHIVE_OPTS(.tag = tag,
5277 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5278 : .owner = subinfo->rolname,
5279 : .description = "SUBSCRIPTION TABLE",
5280 : .section = SECTION_POST_DATA,
5281 : .createStmt = query->data));
5282 :
5283 : /* These objects can't currently have comments or seclabels */
5284 :
5285 4 : free(tag);
5286 4 : destroyPQExpBuffer(query);
5287 : }
5288 :
5289 : /*
5290 : * dumpSubscription
5291 : * dump the definition of the given subscription
5292 : */
5293 : static void
5294 250 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5295 : {
5296 250 : DumpOptions *dopt = fout->dopt;
5297 : PQExpBuffer delq;
5298 : PQExpBuffer query;
5299 : PQExpBuffer publications;
5300 : char *qsubname;
5301 250 : char **pubnames = NULL;
5302 250 : int npubnames = 0;
5303 : int i;
5304 :
5305 : /* Do nothing if not dumping schema */
5306 250 : if (!dopt->dumpSchema)
5307 36 : return;
5308 :
5309 214 : delq = createPQExpBuffer();
5310 214 : query = createPQExpBuffer();
5311 :
5312 214 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5313 :
5314 214 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5315 : qsubname);
5316 :
5317 214 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5318 : qsubname);
5319 214 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5320 :
5321 : /* Build list of quoted publications and append them to query. */
5322 214 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5323 0 : pg_fatal("could not parse %s array", "subpublications");
5324 :
5325 214 : publications = createPQExpBuffer();
5326 428 : for (i = 0; i < npubnames; i++)
5327 : {
5328 214 : if (i > 0)
5329 0 : appendPQExpBufferStr(publications, ", ");
5330 :
5331 214 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5332 : }
5333 :
5334 214 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5335 214 : if (subinfo->subslotname)
5336 214 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5337 : else
5338 0 : appendPQExpBufferStr(query, "NONE");
5339 :
5340 214 : if (subinfo->subbinary)
5341 0 : appendPQExpBufferStr(query, ", binary = true");
5342 :
5343 214 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5344 70 : appendPQExpBufferStr(query, ", streaming = on");
5345 144 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5346 74 : appendPQExpBufferStr(query, ", streaming = parallel");
5347 : else
5348 70 : appendPQExpBufferStr(query, ", streaming = off");
5349 :
5350 214 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5351 0 : appendPQExpBufferStr(query, ", two_phase = on");
5352 :
5353 214 : if (subinfo->subdisableonerr)
5354 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5355 :
5356 214 : if (!subinfo->subpasswordrequired)
5357 0 : appendPQExpBufferStr(query, ", password_required = false");
5358 :
5359 214 : if (subinfo->subrunasowner)
5360 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5361 :
5362 214 : if (subinfo->subfailover)
5363 2 : appendPQExpBufferStr(query, ", failover = true");
5364 :
5365 214 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5366 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5367 :
5368 214 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5369 70 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5370 :
5371 214 : appendPQExpBufferStr(query, ");\n");
5372 :
5373 : /*
5374 : * In binary-upgrade mode, we allow the replication to continue after the
5375 : * upgrade.
5376 : */
5377 214 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5378 : {
5379 10 : if (subinfo->suboriginremotelsn)
5380 : {
5381 : /*
5382 : * Preserve the remote_lsn for the subscriber's replication
5383 : * origin. This value is required to start the replication from
5384 : * the position before the upgrade. This value will be stale if
5385 : * the publisher gets upgraded before the subscriber node.
5386 : * However, this shouldn't be a problem as the upgrade of the
5387 : * publisher ensures that all the transactions were replicated
5388 : * before upgrading it.
5389 : */
5390 2 : appendPQExpBufferStr(query,
5391 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5392 2 : appendPQExpBufferStr(query,
5393 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5394 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5395 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5396 : }
5397 :
5398 10 : if (subinfo->subenabled)
5399 : {
5400 : /*
5401 : * Enable the subscription to allow the replication to continue
5402 : * after the upgrade.
5403 : */
5404 2 : appendPQExpBufferStr(query,
5405 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5406 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5407 : }
5408 : }
5409 :
5410 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5411 214 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5412 214 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5413 : .owner = subinfo->rolname,
5414 : .description = "SUBSCRIPTION",
5415 : .section = SECTION_POST_DATA,
5416 : .createStmt = query->data,
5417 : .dropStmt = delq->data));
5418 :
5419 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5420 70 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5421 70 : NULL, subinfo->rolname,
5422 70 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5423 :
5424 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5425 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5426 0 : NULL, subinfo->rolname,
5427 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5428 :
5429 214 : destroyPQExpBuffer(publications);
5430 214 : free(pubnames);
5431 :
5432 214 : destroyPQExpBuffer(delq);
5433 214 : destroyPQExpBuffer(query);
5434 214 : free(qsubname);
5435 : }
5436 :
5437 : /*
5438 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5439 : * the object needs.
5440 : */
5441 : static void
5442 14250 : append_depends_on_extension(Archive *fout,
5443 : PQExpBuffer create,
5444 : const DumpableObject *dobj,
5445 : const char *catalog,
5446 : const char *keyword,
5447 : const char *objname)
5448 : {
5449 14250 : if (dobj->depends_on_ext)
5450 : {
5451 : char *nm;
5452 : PGresult *res;
5453 : PQExpBuffer query;
5454 : int ntups;
5455 : int i_extname;
5456 : int i;
5457 :
5458 : /* dodge fmtId() non-reentrancy */
5459 84 : nm = pg_strdup(objname);
5460 :
5461 84 : query = createPQExpBuffer();
5462 84 : appendPQExpBuffer(query,
5463 : "SELECT e.extname "
5464 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5465 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5466 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5467 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5468 : catalog,
5469 84 : dobj->catId.oid);
5470 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5471 84 : ntups = PQntuples(res);
5472 84 : i_extname = PQfnumber(res, "extname");
5473 168 : for (i = 0; i < ntups; i++)
5474 : {
5475 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5476 : keyword, nm,
5477 84 : fmtId(PQgetvalue(res, i, i_extname)));
5478 : }
5479 :
5480 84 : PQclear(res);
5481 84 : destroyPQExpBuffer(query);
5482 84 : pg_free(nm);
5483 : }
5484 14250 : }
5485 :
5486 : static Oid
5487 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5488 : {
5489 : /*
5490 : * If the old version didn't assign an array type, but the new version
5491 : * does, we must select an unused type OID to assign. This currently only
5492 : * happens for domains, when upgrading pre-v11 to v11 and up.
5493 : *
5494 : * Note: local state here is kind of ugly, but we must have some, since we
5495 : * mustn't choose the same unused OID more than once.
5496 : */
5497 : static Oid next_possible_free_oid = FirstNormalObjectId;
5498 : PGresult *res;
5499 : bool is_dup;
5500 :
5501 : do
5502 : {
5503 0 : ++next_possible_free_oid;
5504 0 : printfPQExpBuffer(upgrade_query,
5505 : "SELECT EXISTS(SELECT 1 "
5506 : "FROM pg_catalog.pg_type "
5507 : "WHERE oid = '%u'::pg_catalog.oid);",
5508 : next_possible_free_oid);
5509 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5510 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5511 0 : PQclear(res);
5512 0 : } while (is_dup);
5513 :
5514 0 : return next_possible_free_oid;
5515 : }
5516 :
5517 : static void
5518 1842 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5519 : PQExpBuffer upgrade_buffer,
5520 : Oid pg_type_oid,
5521 : bool force_array_type,
5522 : bool include_multirange_type)
5523 : {
5524 1842 : PQExpBuffer upgrade_query = createPQExpBuffer();
5525 : PGresult *res;
5526 : Oid pg_type_array_oid;
5527 : Oid pg_type_multirange_oid;
5528 : Oid pg_type_multirange_array_oid;
5529 : TypeInfo *tinfo;
5530 :
5531 1842 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5532 1842 : appendPQExpBuffer(upgrade_buffer,
5533 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5534 : pg_type_oid);
5535 :
5536 1842 : tinfo = findTypeByOid(pg_type_oid);
5537 1842 : if (tinfo)
5538 1842 : pg_type_array_oid = tinfo->typarray;
5539 : else
5540 0 : pg_type_array_oid = InvalidOid;
5541 :
5542 1842 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5543 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5544 :
5545 1842 : if (OidIsValid(pg_type_array_oid))
5546 : {
5547 1838 : appendPQExpBufferStr(upgrade_buffer,
5548 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5549 1838 : appendPQExpBuffer(upgrade_buffer,
5550 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5551 : pg_type_array_oid);
5552 : }
5553 :
5554 : /*
5555 : * Pre-set the multirange type oid and its own array type oid.
5556 : */
5557 1842 : if (include_multirange_type)
5558 : {
5559 16 : if (fout->remoteVersion >= 140000)
5560 : {
5561 16 : printfPQExpBuffer(upgrade_query,
5562 : "SELECT t.oid, t.typarray "
5563 : "FROM pg_catalog.pg_type t "
5564 : "JOIN pg_catalog.pg_range r "
5565 : "ON t.oid = r.rngmultitypid "
5566 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5567 : pg_type_oid);
5568 :
5569 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5570 :
5571 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5572 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5573 :
5574 16 : PQclear(res);
5575 : }
5576 : else
5577 : {
5578 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5579 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5580 : }
5581 :
5582 16 : appendPQExpBufferStr(upgrade_buffer,
5583 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5584 16 : appendPQExpBuffer(upgrade_buffer,
5585 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5586 : pg_type_multirange_oid);
5587 16 : appendPQExpBufferStr(upgrade_buffer,
5588 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5589 16 : appendPQExpBuffer(upgrade_buffer,
5590 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5591 : pg_type_multirange_array_oid);
5592 : }
5593 :
5594 1842 : destroyPQExpBuffer(upgrade_query);
5595 1842 : }
5596 :
5597 : static void
5598 1696 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5599 : PQExpBuffer upgrade_buffer,
5600 : const TableInfo *tbinfo)
5601 : {
5602 1696 : Oid pg_type_oid = tbinfo->reltype;
5603 :
5604 1696 : if (OidIsValid(pg_type_oid))
5605 1696 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5606 : pg_type_oid, false, false);
5607 1696 : }
5608 :
5609 : /*
5610 : * bsearch() comparator for BinaryUpgradeClassOidItem
5611 : */
5612 : static int
5613 24252 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5614 : {
5615 24252 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5616 24252 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5617 :
5618 24252 : return pg_cmp_u32(v1.oid, v2.oid);
5619 : }
5620 :
5621 : /*
5622 : * collectBinaryUpgradeClassOids
5623 : *
5624 : * Construct a table of pg_class information required for
5625 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5626 : * lookup.
5627 : */
5628 : static void
5629 62 : collectBinaryUpgradeClassOids(Archive *fout)
5630 : {
5631 : PGresult *res;
5632 : const char *query;
5633 :
5634 62 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5635 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5636 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5637 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5638 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5639 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5640 : "ORDER BY c.oid;";
5641 :
5642 62 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5643 :
5644 62 : nbinaryUpgradeClassOids = PQntuples(res);
5645 62 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5646 62 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5647 :
5648 29362 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5649 : {
5650 29300 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5651 29300 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5652 29300 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5653 29300 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5654 29300 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5655 29300 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5656 29300 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5657 : }
5658 :
5659 62 : PQclear(res);
5660 62 : }
5661 :
5662 : static void
5663 2466 : binary_upgrade_set_pg_class_oids(Archive *fout,
5664 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5665 : {
5666 2466 : BinaryUpgradeClassOidItem key = {0};
5667 : BinaryUpgradeClassOidItem *entry;
5668 :
5669 : Assert(binaryUpgradeClassOids);
5670 :
5671 : /*
5672 : * Preserve the OID and relfilenumber of the table, table's index, table's
5673 : * toast table and toast table's index if any.
5674 : *
5675 : * One complexity is that the current table definition might not require
5676 : * the creation of a TOAST table, but the old database might have a TOAST
5677 : * table that was created earlier, before some wide columns were dropped.
5678 : * By setting the TOAST oid we force creation of the TOAST heap and index
5679 : * by the new backend, so we can copy the files during binary upgrade
5680 : * without worrying about this case.
5681 : */
5682 2466 : key.oid = pg_class_oid;
5683 2466 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5684 : sizeof(BinaryUpgradeClassOidItem),
5685 : BinaryUpgradeClassOidItemCmp);
5686 :
5687 2466 : appendPQExpBufferStr(upgrade_buffer,
5688 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5689 :
5690 2466 : if (entry->relkind != RELKIND_INDEX &&
5691 1914 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5692 : {
5693 1864 : appendPQExpBuffer(upgrade_buffer,
5694 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5695 : pg_class_oid);
5696 :
5697 : /*
5698 : * Not every relation has storage. Also, in a pre-v12 database,
5699 : * partitioned tables have a relfilenumber, which should not be
5700 : * preserved when upgrading.
5701 : */
5702 1864 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5703 1542 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5704 1542 : appendPQExpBuffer(upgrade_buffer,
5705 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5706 : entry->relfilenumber);
5707 :
5708 : /*
5709 : * In a pre-v12 database, partitioned tables might be marked as having
5710 : * toast tables, but we should ignore them if so.
5711 : */
5712 1864 : if (OidIsValid(entry->toast_oid) &&
5713 552 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5714 : {
5715 552 : appendPQExpBuffer(upgrade_buffer,
5716 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5717 : entry->toast_oid);
5718 552 : appendPQExpBuffer(upgrade_buffer,
5719 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5720 : entry->toast_relfilenumber);
5721 :
5722 : /* every toast table has an index */
5723 552 : appendPQExpBuffer(upgrade_buffer,
5724 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5725 : entry->toast_index_oid);
5726 552 : appendPQExpBuffer(upgrade_buffer,
5727 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5728 : entry->toast_index_relfilenumber);
5729 : }
5730 : }
5731 : else
5732 : {
5733 : /* Preserve the OID and relfilenumber of the index */
5734 602 : appendPQExpBuffer(upgrade_buffer,
5735 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5736 : pg_class_oid);
5737 602 : appendPQExpBuffer(upgrade_buffer,
5738 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5739 : entry->relfilenumber);
5740 : }
5741 :
5742 2466 : appendPQExpBufferChar(upgrade_buffer, '\n');
5743 2466 : }
5744 :
5745 : /*
5746 : * If the DumpableObject is a member of an extension, add a suitable
5747 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5748 : *
5749 : * For somewhat historical reasons, objname should already be quoted,
5750 : * but not objnamespace (if any).
5751 : */
5752 : static void
5753 2926 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5754 : const DumpableObject *dobj,
5755 : const char *objtype,
5756 : const char *objname,
5757 : const char *objnamespace)
5758 : {
5759 2926 : DumpableObject *extobj = NULL;
5760 : int i;
5761 :
5762 2926 : if (!dobj->ext_member)
5763 2894 : return;
5764 :
5765 : /*
5766 : * Find the parent extension. We could avoid this search if we wanted to
5767 : * add a link field to DumpableObject, but the space costs of that would
5768 : * be considerable. We assume that member objects could only have a
5769 : * direct dependency on their own extension, not any others.
5770 : */
5771 32 : for (i = 0; i < dobj->nDeps; i++)
5772 : {
5773 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5774 32 : if (extobj && extobj->objType == DO_EXTENSION)
5775 32 : break;
5776 0 : extobj = NULL;
5777 : }
5778 32 : if (extobj == NULL)
5779 0 : pg_fatal("could not find parent extension for %s %s",
5780 : objtype, objname);
5781 :
5782 32 : appendPQExpBufferStr(upgrade_buffer,
5783 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5784 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5785 32 : fmtId(extobj->name),
5786 : objtype);
5787 32 : if (objnamespace && *objnamespace)
5788 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5789 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5790 : }
5791 :
5792 : /*
5793 : * getNamespaces:
5794 : * get information about all namespaces in the system catalogs
5795 : */
5796 : void
5797 470 : getNamespaces(Archive *fout)
5798 : {
5799 : PGresult *res;
5800 : int ntups;
5801 : int i;
5802 : PQExpBuffer query;
5803 : NamespaceInfo *nsinfo;
5804 : int i_tableoid;
5805 : int i_oid;
5806 : int i_nspname;
5807 : int i_nspowner;
5808 : int i_nspacl;
5809 : int i_acldefault;
5810 :
5811 470 : query = createPQExpBuffer();
5812 :
5813 : /*
5814 : * we fetch all namespaces including system ones, so that every object we
5815 : * read in can be linked to a containing namespace.
5816 : */
5817 470 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5818 : "n.nspowner, "
5819 : "n.nspacl, "
5820 : "acldefault('n', n.nspowner) AS acldefault "
5821 : "FROM pg_namespace n");
5822 :
5823 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5824 :
5825 470 : ntups = PQntuples(res);
5826 :
5827 470 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5828 :
5829 470 : i_tableoid = PQfnumber(res, "tableoid");
5830 470 : i_oid = PQfnumber(res, "oid");
5831 470 : i_nspname = PQfnumber(res, "nspname");
5832 470 : i_nspowner = PQfnumber(res, "nspowner");
5833 470 : i_nspacl = PQfnumber(res, "nspacl");
5834 470 : i_acldefault = PQfnumber(res, "acldefault");
5835 :
5836 4274 : for (i = 0; i < ntups; i++)
5837 : {
5838 : const char *nspowner;
5839 :
5840 3804 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5841 3804 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5842 3804 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5843 3804 : AssignDumpId(&nsinfo[i].dobj);
5844 3804 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5845 3804 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5846 3804 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5847 3804 : nsinfo[i].dacl.privtype = 0;
5848 3804 : nsinfo[i].dacl.initprivs = NULL;
5849 3804 : nspowner = PQgetvalue(res, i, i_nspowner);
5850 3804 : nsinfo[i].nspowner = atooid(nspowner);
5851 3804 : nsinfo[i].rolname = getRoleName(nspowner);
5852 :
5853 : /* Decide whether to dump this namespace */
5854 3804 : selectDumpableNamespace(&nsinfo[i], fout);
5855 :
5856 : /* Mark whether namespace has an ACL */
5857 3804 : if (!PQgetisnull(res, i, i_nspacl))
5858 1572 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5859 :
5860 : /*
5861 : * We ignore any pg_init_privs.initprivs entry for the public schema
5862 : * and assume a predetermined default, for several reasons. First,
5863 : * dropping and recreating the schema removes its pg_init_privs entry,
5864 : * but an empty destination database starts with this ACL nonetheless.
5865 : * Second, we support dump/reload of public schema ownership changes.
5866 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5867 : * initprivs continues to reflect the initial owner. Hence,
5868 : * synthesize the value that nspacl will have after the restore's
5869 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5870 : * match the source's ACL, even if the latter was an initdb-default
5871 : * ACL, which changed in v15. An upgrade pulls in changes to most
5872 : * system object ACLs that the DBA had not customized. We've made the
5873 : * public schema depart from that, because changing its ACL so easily
5874 : * breaks applications.
5875 : */
5876 3804 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5877 : {
5878 462 : PQExpBuffer aclarray = createPQExpBuffer();
5879 462 : PQExpBuffer aclitem = createPQExpBuffer();
5880 :
5881 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5882 462 : appendPQExpBufferChar(aclarray, '{');
5883 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5884 462 : appendPQExpBufferStr(aclitem, "=UC/");
5885 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5886 462 : appendPGArray(aclarray, aclitem->data);
5887 462 : resetPQExpBuffer(aclitem);
5888 462 : appendPQExpBufferStr(aclitem, "=U/");
5889 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5890 462 : appendPGArray(aclarray, aclitem->data);
5891 462 : appendPQExpBufferChar(aclarray, '}');
5892 :
5893 462 : nsinfo[i].dacl.privtype = 'i';
5894 462 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5895 462 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5896 :
5897 462 : destroyPQExpBuffer(aclarray);
5898 462 : destroyPQExpBuffer(aclitem);
5899 : }
5900 : }
5901 :
5902 470 : PQclear(res);
5903 470 : destroyPQExpBuffer(query);
5904 470 : }
5905 :
5906 : /*
5907 : * findNamespace:
5908 : * given a namespace OID, look up the info read by getNamespaces
5909 : */
5910 : static NamespaceInfo *
5911 1476340 : findNamespace(Oid nsoid)
5912 : {
5913 : NamespaceInfo *nsinfo;
5914 :
5915 1476340 : nsinfo = findNamespaceByOid(nsoid);
5916 1476340 : if (nsinfo == NULL)
5917 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5918 1476340 : return nsinfo;
5919 : }
5920 :
5921 : /*
5922 : * getExtensions:
5923 : * read all extensions in the system catalogs and return them in the
5924 : * ExtensionInfo* structure
5925 : *
5926 : * numExtensions is set to the number of extensions read in
5927 : */
5928 : ExtensionInfo *
5929 470 : getExtensions(Archive *fout, int *numExtensions)
5930 : {
5931 470 : DumpOptions *dopt = fout->dopt;
5932 : PGresult *res;
5933 : int ntups;
5934 : int i;
5935 : PQExpBuffer query;
5936 470 : ExtensionInfo *extinfo = NULL;
5937 : int i_tableoid;
5938 : int i_oid;
5939 : int i_extname;
5940 : int i_nspname;
5941 : int i_extrelocatable;
5942 : int i_extversion;
5943 : int i_extconfig;
5944 : int i_extcondition;
5945 :
5946 470 : query = createPQExpBuffer();
5947 :
5948 470 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5949 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5950 : "FROM pg_extension x "
5951 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5952 :
5953 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5954 :
5955 470 : ntups = PQntuples(res);
5956 470 : if (ntups == 0)
5957 0 : goto cleanup;
5958 :
5959 470 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5960 :
5961 470 : i_tableoid = PQfnumber(res, "tableoid");
5962 470 : i_oid = PQfnumber(res, "oid");
5963 470 : i_extname = PQfnumber(res, "extname");
5964 470 : i_nspname = PQfnumber(res, "nspname");
5965 470 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5966 470 : i_extversion = PQfnumber(res, "extversion");
5967 470 : i_extconfig = PQfnumber(res, "extconfig");
5968 470 : i_extcondition = PQfnumber(res, "extcondition");
5969 :
5970 990 : for (i = 0; i < ntups; i++)
5971 : {
5972 520 : extinfo[i].dobj.objType = DO_EXTENSION;
5973 520 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5974 520 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5975 520 : AssignDumpId(&extinfo[i].dobj);
5976 520 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5977 520 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5978 520 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5979 520 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5980 520 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5981 520 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5982 :
5983 : /* Decide whether we want to dump it */
5984 520 : selectDumpableExtension(&(extinfo[i]), dopt);
5985 : }
5986 :
5987 470 : cleanup:
5988 470 : PQclear(res);
5989 470 : destroyPQExpBuffer(query);
5990 :
5991 470 : *numExtensions = ntups;
5992 :
5993 470 : return extinfo;
5994 : }
5995 :
5996 : /*
5997 : * getTypes:
5998 : * get information about all types in the system catalogs
5999 : *
6000 : * NB: this must run after getFuncs() because we assume we can do
6001 : * findFuncByOid().
6002 : */
6003 : void
6004 468 : getTypes(Archive *fout)
6005 : {
6006 : PGresult *res;
6007 : int ntups;
6008 : int i;
6009 468 : PQExpBuffer query = createPQExpBuffer();
6010 : TypeInfo *tyinfo;
6011 : ShellTypeInfo *stinfo;
6012 : int i_tableoid;
6013 : int i_oid;
6014 : int i_typname;
6015 : int i_typnamespace;
6016 : int i_typacl;
6017 : int i_acldefault;
6018 : int i_typowner;
6019 : int i_typelem;
6020 : int i_typrelid;
6021 : int i_typrelkind;
6022 : int i_typtype;
6023 : int i_typisdefined;
6024 : int i_isarray;
6025 : int i_typarray;
6026 :
6027 : /*
6028 : * we include even the built-in types because those may be used as array
6029 : * elements by user-defined types
6030 : *
6031 : * we filter out the built-in types when we dump out the types
6032 : *
6033 : * same approach for undefined (shell) types and array types
6034 : *
6035 : * Note: as of 8.3 we can reliably detect whether a type is an
6036 : * auto-generated array type by checking the element type's typarray.
6037 : * (Before that the test is capable of generating false positives.) We
6038 : * still check for name beginning with '_', though, so as to avoid the
6039 : * cost of the subselect probe for all standard types. This would have to
6040 : * be revisited if the backend ever allows renaming of array types.
6041 : */
6042 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6043 : "typnamespace, typacl, "
6044 : "acldefault('T', typowner) AS acldefault, "
6045 : "typowner, "
6046 : "typelem, typrelid, typarray, "
6047 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6048 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6049 : "typtype, typisdefined, "
6050 : "typname[0] = '_' AND typelem != 0 AND "
6051 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6052 : "FROM pg_type");
6053 :
6054 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6055 :
6056 468 : ntups = PQntuples(res);
6057 :
6058 468 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6059 :
6060 468 : i_tableoid = PQfnumber(res, "tableoid");
6061 468 : i_oid = PQfnumber(res, "oid");
6062 468 : i_typname = PQfnumber(res, "typname");
6063 468 : i_typnamespace = PQfnumber(res, "typnamespace");
6064 468 : i_typacl = PQfnumber(res, "typacl");
6065 468 : i_acldefault = PQfnumber(res, "acldefault");
6066 468 : i_typowner = PQfnumber(res, "typowner");
6067 468 : i_typelem = PQfnumber(res, "typelem");
6068 468 : i_typrelid = PQfnumber(res, "typrelid");
6069 468 : i_typrelkind = PQfnumber(res, "typrelkind");
6070 468 : i_typtype = PQfnumber(res, "typtype");
6071 468 : i_typisdefined = PQfnumber(res, "typisdefined");
6072 468 : i_isarray = PQfnumber(res, "isarray");
6073 468 : i_typarray = PQfnumber(res, "typarray");
6074 :
6075 339226 : for (i = 0; i < ntups; i++)
6076 : {
6077 338758 : tyinfo[i].dobj.objType = DO_TYPE;
6078 338758 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6079 338758 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6080 338758 : AssignDumpId(&tyinfo[i].dobj);
6081 338758 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6082 677516 : tyinfo[i].dobj.namespace =
6083 338758 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6084 338758 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6085 338758 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6086 338758 : tyinfo[i].dacl.privtype = 0;
6087 338758 : tyinfo[i].dacl.initprivs = NULL;
6088 338758 : tyinfo[i].ftypname = NULL; /* may get filled later */
6089 338758 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6090 338758 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6091 338758 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6092 338758 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6093 338758 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6094 338758 : tyinfo[i].shellType = NULL;
6095 :
6096 338758 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6097 338640 : tyinfo[i].isDefined = true;
6098 : else
6099 118 : tyinfo[i].isDefined = false;
6100 :
6101 338758 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6102 162534 : tyinfo[i].isArray = true;
6103 : else
6104 176224 : tyinfo[i].isArray = false;
6105 :
6106 338758 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6107 :
6108 338758 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6109 3124 : tyinfo[i].isMultirange = true;
6110 : else
6111 335634 : tyinfo[i].isMultirange = false;
6112 :
6113 : /* Decide whether we want to dump it */
6114 338758 : selectDumpableType(&tyinfo[i], fout);
6115 :
6116 : /* Mark whether type has an ACL */
6117 338758 : if (!PQgetisnull(res, i, i_typacl))
6118 442 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6119 :
6120 : /*
6121 : * If it's a domain, fetch info about its constraints, if any
6122 : */
6123 338758 : tyinfo[i].nDomChecks = 0;
6124 338758 : tyinfo[i].domChecks = NULL;
6125 338758 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6126 40872 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6127 452 : getDomainConstraints(fout, &(tyinfo[i]));
6128 :
6129 : /*
6130 : * If it's a base type, make a DumpableObject representing a shell
6131 : * definition of the type. We will need to dump that ahead of the I/O
6132 : * functions for the type. Similarly, range types need a shell
6133 : * definition in case they have a canonicalize function.
6134 : *
6135 : * Note: the shell type doesn't have a catId. You might think it
6136 : * should copy the base type's catId, but then it might capture the
6137 : * pg_depend entries for the type, which we don't want.
6138 : */
6139 338758 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6140 40872 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6141 19966 : tyinfo[i].typtype == TYPTYPE_RANGE))
6142 : {
6143 21206 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6144 21206 : stinfo->dobj.objType = DO_SHELL_TYPE;
6145 21206 : stinfo->dobj.catId = nilCatalogId;
6146 21206 : AssignDumpId(&stinfo->dobj);
6147 21206 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6148 21206 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6149 21206 : stinfo->baseType = &(tyinfo[i]);
6150 21206 : tyinfo[i].shellType = stinfo;
6151 :
6152 : /*
6153 : * Initially mark the shell type as not to be dumped. We'll only
6154 : * dump it if the I/O or canonicalize functions need to be dumped;
6155 : * this is taken care of while sorting dependencies.
6156 : */
6157 21206 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6158 : }
6159 : }
6160 :
6161 468 : PQclear(res);
6162 :
6163 468 : destroyPQExpBuffer(query);
6164 468 : }
6165 :
6166 : /*
6167 : * getOperators:
6168 : * get information about all operators in the system catalogs
6169 : */
6170 : void
6171 468 : getOperators(Archive *fout)
6172 : {
6173 : PGresult *res;
6174 : int ntups;
6175 : int i;
6176 468 : PQExpBuffer query = createPQExpBuffer();
6177 : OprInfo *oprinfo;
6178 : int i_tableoid;
6179 : int i_oid;
6180 : int i_oprname;
6181 : int i_oprnamespace;
6182 : int i_oprowner;
6183 : int i_oprkind;
6184 : int i_oprcode;
6185 :
6186 : /*
6187 : * find all operators, including builtin operators; we filter out
6188 : * system-defined operators at dump-out time.
6189 : */
6190 :
6191 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6192 : "oprnamespace, "
6193 : "oprowner, "
6194 : "oprkind, "
6195 : "oprcode::oid AS oprcode "
6196 : "FROM pg_operator");
6197 :
6198 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6199 :
6200 468 : ntups = PQntuples(res);
6201 :
6202 468 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6203 :
6204 468 : i_tableoid = PQfnumber(res, "tableoid");
6205 468 : i_oid = PQfnumber(res, "oid");
6206 468 : i_oprname = PQfnumber(res, "oprname");
6207 468 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6208 468 : i_oprowner = PQfnumber(res, "oprowner");
6209 468 : i_oprkind = PQfnumber(res, "oprkind");
6210 468 : i_oprcode = PQfnumber(res, "oprcode");
6211 :
6212 374774 : for (i = 0; i < ntups; i++)
6213 : {
6214 374306 : oprinfo[i].dobj.objType = DO_OPERATOR;
6215 374306 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6216 374306 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6217 374306 : AssignDumpId(&oprinfo[i].dobj);
6218 374306 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6219 748612 : oprinfo[i].dobj.namespace =
6220 374306 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6221 374306 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6222 374306 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6223 374306 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6224 :
6225 : /* Decide whether we want to dump it */
6226 374306 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6227 : }
6228 :
6229 468 : PQclear(res);
6230 :
6231 468 : destroyPQExpBuffer(query);
6232 468 : }
6233 :
6234 : /*
6235 : * getCollations:
6236 : * get information about all collations in the system catalogs
6237 : */
6238 : void
6239 468 : getCollations(Archive *fout)
6240 : {
6241 : PGresult *res;
6242 : int ntups;
6243 : int i;
6244 : PQExpBuffer query;
6245 : CollInfo *collinfo;
6246 : int i_tableoid;
6247 : int i_oid;
6248 : int i_collname;
6249 : int i_collnamespace;
6250 : int i_collowner;
6251 :
6252 468 : query = createPQExpBuffer();
6253 :
6254 : /*
6255 : * find all collations, including builtin collations; we filter out
6256 : * system-defined collations at dump-out time.
6257 : */
6258 :
6259 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6260 : "collnamespace, "
6261 : "collowner "
6262 : "FROM pg_collation");
6263 :
6264 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6265 :
6266 468 : ntups = PQntuples(res);
6267 :
6268 468 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6269 :
6270 468 : i_tableoid = PQfnumber(res, "tableoid");
6271 468 : i_oid = PQfnumber(res, "oid");
6272 468 : i_collname = PQfnumber(res, "collname");
6273 468 : i_collnamespace = PQfnumber(res, "collnamespace");
6274 468 : i_collowner = PQfnumber(res, "collowner");
6275 :
6276 382612 : for (i = 0; i < ntups; i++)
6277 : {
6278 382144 : collinfo[i].dobj.objType = DO_COLLATION;
6279 382144 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6280 382144 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6281 382144 : AssignDumpId(&collinfo[i].dobj);
6282 382144 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6283 764288 : collinfo[i].dobj.namespace =
6284 382144 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6285 382144 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6286 :
6287 : /* Decide whether we want to dump it */
6288 382144 : selectDumpableObject(&(collinfo[i].dobj), fout);
6289 : }
6290 :
6291 468 : PQclear(res);
6292 :
6293 468 : destroyPQExpBuffer(query);
6294 468 : }
6295 :
6296 : /*
6297 : * getConversions:
6298 : * get information about all conversions in the system catalogs
6299 : */
6300 : void
6301 468 : getConversions(Archive *fout)
6302 : {
6303 : PGresult *res;
6304 : int ntups;
6305 : int i;
6306 : PQExpBuffer query;
6307 : ConvInfo *convinfo;
6308 : int i_tableoid;
6309 : int i_oid;
6310 : int i_conname;
6311 : int i_connamespace;
6312 : int i_conowner;
6313 :
6314 468 : query = createPQExpBuffer();
6315 :
6316 : /*
6317 : * find all conversions, including builtin conversions; we filter out
6318 : * system-defined conversions at dump-out time.
6319 : */
6320 :
6321 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6322 : "connamespace, "
6323 : "conowner "
6324 : "FROM pg_conversion");
6325 :
6326 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6327 :
6328 468 : ntups = PQntuples(res);
6329 :
6330 468 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6331 :
6332 468 : i_tableoid = PQfnumber(res, "tableoid");
6333 468 : i_oid = PQfnumber(res, "oid");
6334 468 : i_conname = PQfnumber(res, "conname");
6335 468 : i_connamespace = PQfnumber(res, "connamespace");
6336 468 : i_conowner = PQfnumber(res, "conowner");
6337 :
6338 60470 : for (i = 0; i < ntups; i++)
6339 : {
6340 60002 : convinfo[i].dobj.objType = DO_CONVERSION;
6341 60002 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6342 60002 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6343 60002 : AssignDumpId(&convinfo[i].dobj);
6344 60002 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6345 120004 : convinfo[i].dobj.namespace =
6346 60002 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6347 60002 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6348 :
6349 : /* Decide whether we want to dump it */
6350 60002 : selectDumpableObject(&(convinfo[i].dobj), fout);
6351 : }
6352 :
6353 468 : PQclear(res);
6354 :
6355 468 : destroyPQExpBuffer(query);
6356 468 : }
6357 :
6358 : /*
6359 : * getAccessMethods:
6360 : * get information about all user-defined access methods
6361 : */
6362 : void
6363 468 : getAccessMethods(Archive *fout)
6364 : {
6365 : PGresult *res;
6366 : int ntups;
6367 : int i;
6368 : PQExpBuffer query;
6369 : AccessMethodInfo *aminfo;
6370 : int i_tableoid;
6371 : int i_oid;
6372 : int i_amname;
6373 : int i_amhandler;
6374 : int i_amtype;
6375 :
6376 : /* Before 9.6, there are no user-defined access methods */
6377 468 : if (fout->remoteVersion < 90600)
6378 0 : return;
6379 :
6380 468 : query = createPQExpBuffer();
6381 :
6382 : /* Select all access methods from pg_am table */
6383 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6384 : "amhandler::pg_catalog.regproc AS amhandler "
6385 : "FROM pg_am");
6386 :
6387 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6388 :
6389 468 : ntups = PQntuples(res);
6390 :
6391 468 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6392 :
6393 468 : i_tableoid = PQfnumber(res, "tableoid");
6394 468 : i_oid = PQfnumber(res, "oid");
6395 468 : i_amname = PQfnumber(res, "amname");
6396 468 : i_amhandler = PQfnumber(res, "amhandler");
6397 468 : i_amtype = PQfnumber(res, "amtype");
6398 :
6399 4010 : for (i = 0; i < ntups; i++)
6400 : {
6401 3542 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6402 3542 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6403 3542 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6404 3542 : AssignDumpId(&aminfo[i].dobj);
6405 3542 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6406 3542 : aminfo[i].dobj.namespace = NULL;
6407 3542 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6408 3542 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6409 :
6410 : /* Decide whether we want to dump it */
6411 3542 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6412 : }
6413 :
6414 468 : PQclear(res);
6415 :
6416 468 : destroyPQExpBuffer(query);
6417 : }
6418 :
6419 :
6420 : /*
6421 : * getOpclasses:
6422 : * get information about all opclasses in the system catalogs
6423 : */
6424 : void
6425 468 : getOpclasses(Archive *fout)
6426 : {
6427 : PGresult *res;
6428 : int ntups;
6429 : int i;
6430 468 : PQExpBuffer query = createPQExpBuffer();
6431 : OpclassInfo *opcinfo;
6432 : int i_tableoid;
6433 : int i_oid;
6434 : int i_opcname;
6435 : int i_opcnamespace;
6436 : int i_opcowner;
6437 :
6438 : /*
6439 : * find all opclasses, including builtin opclasses; we filter out
6440 : * system-defined opclasses at dump-out time.
6441 : */
6442 :
6443 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6444 : "opcnamespace, "
6445 : "opcowner "
6446 : "FROM pg_opclass");
6447 :
6448 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6449 :
6450 468 : ntups = PQntuples(res);
6451 :
6452 468 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6453 :
6454 468 : i_tableoid = PQfnumber(res, "tableoid");
6455 468 : i_oid = PQfnumber(res, "oid");
6456 468 : i_opcname = PQfnumber(res, "opcname");
6457 468 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6458 468 : i_opcowner = PQfnumber(res, "opcowner");
6459 :
6460 83658 : for (i = 0; i < ntups; i++)
6461 : {
6462 83190 : opcinfo[i].dobj.objType = DO_OPCLASS;
6463 83190 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6464 83190 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6465 83190 : AssignDumpId(&opcinfo[i].dobj);
6466 83190 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6467 166380 : opcinfo[i].dobj.namespace =
6468 83190 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6469 83190 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6470 :
6471 : /* Decide whether we want to dump it */
6472 83190 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6473 : }
6474 :
6475 468 : PQclear(res);
6476 :
6477 468 : destroyPQExpBuffer(query);
6478 468 : }
6479 :
6480 : /*
6481 : * getOpfamilies:
6482 : * get information about all opfamilies in the system catalogs
6483 : */
6484 : void
6485 468 : getOpfamilies(Archive *fout)
6486 : {
6487 : PGresult *res;
6488 : int ntups;
6489 : int i;
6490 : PQExpBuffer query;
6491 : OpfamilyInfo *opfinfo;
6492 : int i_tableoid;
6493 : int i_oid;
6494 : int i_opfname;
6495 : int i_opfnamespace;
6496 : int i_opfowner;
6497 :
6498 468 : query = createPQExpBuffer();
6499 :
6500 : /*
6501 : * find all opfamilies, including builtin opfamilies; we filter out
6502 : * system-defined opfamilies at dump-out time.
6503 : */
6504 :
6505 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6506 : "opfnamespace, "
6507 : "opfowner "
6508 : "FROM pg_opfamily");
6509 :
6510 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6511 :
6512 468 : ntups = PQntuples(res);
6513 :
6514 468 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6515 :
6516 468 : i_tableoid = PQfnumber(res, "tableoid");
6517 468 : i_oid = PQfnumber(res, "oid");
6518 468 : i_opfname = PQfnumber(res, "opfname");
6519 468 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6520 468 : i_opfowner = PQfnumber(res, "opfowner");
6521 :
6522 69132 : for (i = 0; i < ntups; i++)
6523 : {
6524 68664 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6525 68664 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6526 68664 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6527 68664 : AssignDumpId(&opfinfo[i].dobj);
6528 68664 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6529 137328 : opfinfo[i].dobj.namespace =
6530 68664 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6531 68664 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6532 :
6533 : /* Decide whether we want to dump it */
6534 68664 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6535 : }
6536 :
6537 468 : PQclear(res);
6538 :
6539 468 : destroyPQExpBuffer(query);
6540 468 : }
6541 :
6542 : /*
6543 : * getAggregates:
6544 : * get information about all user-defined aggregates in the system catalogs
6545 : */
6546 : void
6547 468 : getAggregates(Archive *fout)
6548 : {
6549 468 : DumpOptions *dopt = fout->dopt;
6550 : PGresult *res;
6551 : int ntups;
6552 : int i;
6553 468 : PQExpBuffer query = createPQExpBuffer();
6554 : AggInfo *agginfo;
6555 : int i_tableoid;
6556 : int i_oid;
6557 : int i_aggname;
6558 : int i_aggnamespace;
6559 : int i_pronargs;
6560 : int i_proargtypes;
6561 : int i_proowner;
6562 : int i_aggacl;
6563 : int i_acldefault;
6564 :
6565 : /*
6566 : * Find all interesting aggregates. See comment in getFuncs() for the
6567 : * rationale behind the filtering logic.
6568 : */
6569 468 : if (fout->remoteVersion >= 90600)
6570 : {
6571 : const char *agg_check;
6572 :
6573 936 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6574 468 : : "p.proisagg");
6575 :
6576 468 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6577 : "p.proname AS aggname, "
6578 : "p.pronamespace AS aggnamespace, "
6579 : "p.pronargs, p.proargtypes, "
6580 : "p.proowner, "
6581 : "p.proacl AS aggacl, "
6582 : "acldefault('f', p.proowner) AS acldefault "
6583 : "FROM pg_proc p "
6584 : "LEFT JOIN pg_init_privs pip ON "
6585 : "(p.oid = pip.objoid "
6586 : "AND pip.classoid = 'pg_proc'::regclass "
6587 : "AND pip.objsubid = 0) "
6588 : "WHERE %s AND ("
6589 : "p.pronamespace != "
6590 : "(SELECT oid FROM pg_namespace "
6591 : "WHERE nspname = 'pg_catalog') OR "
6592 : "p.proacl IS DISTINCT FROM pip.initprivs",
6593 : agg_check);
6594 468 : if (dopt->binary_upgrade)
6595 62 : appendPQExpBufferStr(query,
6596 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6597 : "classid = 'pg_proc'::regclass AND "
6598 : "objid = p.oid AND "
6599 : "refclassid = 'pg_extension'::regclass AND "
6600 : "deptype = 'e')");
6601 468 : appendPQExpBufferChar(query, ')');
6602 : }
6603 : else
6604 : {
6605 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6606 : "pronamespace AS aggnamespace, "
6607 : "pronargs, proargtypes, "
6608 : "proowner, "
6609 : "proacl AS aggacl, "
6610 : "acldefault('f', proowner) AS acldefault "
6611 : "FROM pg_proc p "
6612 : "WHERE proisagg AND ("
6613 : "pronamespace != "
6614 : "(SELECT oid FROM pg_namespace "
6615 : "WHERE nspname = 'pg_catalog')");
6616 0 : if (dopt->binary_upgrade)
6617 0 : appendPQExpBufferStr(query,
6618 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6619 : "classid = 'pg_proc'::regclass AND "
6620 : "objid = p.oid AND "
6621 : "refclassid = 'pg_extension'::regclass AND "
6622 : "deptype = 'e')");
6623 0 : appendPQExpBufferChar(query, ')');
6624 : }
6625 :
6626 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6627 :
6628 468 : ntups = PQntuples(res);
6629 :
6630 468 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6631 :
6632 468 : i_tableoid = PQfnumber(res, "tableoid");
6633 468 : i_oid = PQfnumber(res, "oid");
6634 468 : i_aggname = PQfnumber(res, "aggname");
6635 468 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6636 468 : i_pronargs = PQfnumber(res, "pronargs");
6637 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6638 468 : i_proowner = PQfnumber(res, "proowner");
6639 468 : i_aggacl = PQfnumber(res, "aggacl");
6640 468 : i_acldefault = PQfnumber(res, "acldefault");
6641 :
6642 1556 : for (i = 0; i < ntups; i++)
6643 : {
6644 1088 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6645 1088 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6646 1088 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6647 1088 : AssignDumpId(&agginfo[i].aggfn.dobj);
6648 1088 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6649 2176 : agginfo[i].aggfn.dobj.namespace =
6650 1088 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6651 1088 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6652 1088 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6653 1088 : agginfo[i].aggfn.dacl.privtype = 0;
6654 1088 : agginfo[i].aggfn.dacl.initprivs = NULL;
6655 1088 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6656 1088 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6657 1088 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6658 1088 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6659 1088 : if (agginfo[i].aggfn.nargs == 0)
6660 160 : agginfo[i].aggfn.argtypes = NULL;
6661 : else
6662 : {
6663 928 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6664 928 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6665 928 : agginfo[i].aggfn.argtypes,
6666 928 : agginfo[i].aggfn.nargs);
6667 : }
6668 1088 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6669 :
6670 : /* Decide whether we want to dump it */
6671 1088 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6672 :
6673 : /* Mark whether aggregate has an ACL */
6674 1088 : if (!PQgetisnull(res, i, i_aggacl))
6675 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6676 : }
6677 :
6678 468 : PQclear(res);
6679 :
6680 468 : destroyPQExpBuffer(query);
6681 468 : }
6682 :
6683 : /*
6684 : * getFuncs:
6685 : * get information about all user-defined functions in the system catalogs
6686 : */
6687 : void
6688 468 : getFuncs(Archive *fout)
6689 : {
6690 468 : DumpOptions *dopt = fout->dopt;
6691 : PGresult *res;
6692 : int ntups;
6693 : int i;
6694 468 : PQExpBuffer query = createPQExpBuffer();
6695 : FuncInfo *finfo;
6696 : int i_tableoid;
6697 : int i_oid;
6698 : int i_proname;
6699 : int i_pronamespace;
6700 : int i_proowner;
6701 : int i_prolang;
6702 : int i_pronargs;
6703 : int i_proargtypes;
6704 : int i_prorettype;
6705 : int i_proacl;
6706 : int i_acldefault;
6707 :
6708 : /*
6709 : * Find all interesting functions. This is a bit complicated:
6710 : *
6711 : * 1. Always exclude aggregates; those are handled elsewhere.
6712 : *
6713 : * 2. Always exclude functions that are internally dependent on something
6714 : * else, since presumably those will be created as a result of creating
6715 : * the something else. This currently acts only to suppress constructor
6716 : * functions for range types. Note this is OK only because the
6717 : * constructors don't have any dependencies the range type doesn't have;
6718 : * otherwise we might not get creation ordering correct.
6719 : *
6720 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6721 : * they're members of extensions and we are in binary-upgrade mode then
6722 : * include them, since we want to dump extension members individually in
6723 : * that mode. Also, if they are used by casts or transforms then we need
6724 : * to gather the information about them, though they won't be dumped if
6725 : * they are built-in. Also, in 9.6 and up, include functions in
6726 : * pg_catalog if they have an ACL different from what's shown in
6727 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6728 : */
6729 468 : if (fout->remoteVersion >= 90600)
6730 : {
6731 : const char *not_agg_check;
6732 :
6733 936 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6734 468 : : "NOT p.proisagg");
6735 :
6736 468 : appendPQExpBuffer(query,
6737 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6738 : "p.pronargs, p.proargtypes, p.prorettype, "
6739 : "p.proacl, "
6740 : "acldefault('f', p.proowner) AS acldefault, "
6741 : "p.pronamespace, "
6742 : "p.proowner "
6743 : "FROM pg_proc p "
6744 : "LEFT JOIN pg_init_privs pip ON "
6745 : "(p.oid = pip.objoid "
6746 : "AND pip.classoid = 'pg_proc'::regclass "
6747 : "AND pip.objsubid = 0) "
6748 : "WHERE %s"
6749 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6750 : "WHERE classid = 'pg_proc'::regclass AND "
6751 : "objid = p.oid AND deptype = 'i')"
6752 : "\n AND ("
6753 : "\n pronamespace != "
6754 : "(SELECT oid FROM pg_namespace "
6755 : "WHERE nspname = 'pg_catalog')"
6756 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6757 : "\n WHERE pg_cast.oid > %u "
6758 : "\n AND p.oid = pg_cast.castfunc)"
6759 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6760 : "\n WHERE pg_transform.oid > %u AND "
6761 : "\n (p.oid = pg_transform.trffromsql"
6762 : "\n OR p.oid = pg_transform.trftosql))",
6763 : not_agg_check,
6764 : g_last_builtin_oid,
6765 : g_last_builtin_oid);
6766 468 : if (dopt->binary_upgrade)
6767 62 : appendPQExpBufferStr(query,
6768 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6769 : "classid = 'pg_proc'::regclass AND "
6770 : "objid = p.oid AND "
6771 : "refclassid = 'pg_extension'::regclass AND "
6772 : "deptype = 'e')");
6773 468 : appendPQExpBufferStr(query,
6774 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6775 468 : appendPQExpBufferChar(query, ')');
6776 : }
6777 : else
6778 : {
6779 0 : appendPQExpBuffer(query,
6780 : "SELECT tableoid, oid, proname, prolang, "
6781 : "pronargs, proargtypes, prorettype, proacl, "
6782 : "acldefault('f', proowner) AS acldefault, "
6783 : "pronamespace, "
6784 : "proowner "
6785 : "FROM pg_proc p "
6786 : "WHERE NOT proisagg"
6787 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6788 : "WHERE classid = 'pg_proc'::regclass AND "
6789 : "objid = p.oid AND deptype = 'i')"
6790 : "\n AND ("
6791 : "\n pronamespace != "
6792 : "(SELECT oid FROM pg_namespace "
6793 : "WHERE nspname = 'pg_catalog')"
6794 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6795 : "\n WHERE pg_cast.oid > '%u'::oid"
6796 : "\n AND p.oid = pg_cast.castfunc)",
6797 : g_last_builtin_oid);
6798 :
6799 0 : if (fout->remoteVersion >= 90500)
6800 0 : appendPQExpBuffer(query,
6801 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6802 : "\n WHERE pg_transform.oid > '%u'::oid"
6803 : "\n AND (p.oid = pg_transform.trffromsql"
6804 : "\n OR p.oid = pg_transform.trftosql))",
6805 : g_last_builtin_oid);
6806 :
6807 0 : if (dopt->binary_upgrade)
6808 0 : appendPQExpBufferStr(query,
6809 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6810 : "classid = 'pg_proc'::regclass AND "
6811 : "objid = p.oid AND "
6812 : "refclassid = 'pg_extension'::regclass AND "
6813 : "deptype = 'e')");
6814 0 : appendPQExpBufferChar(query, ')');
6815 : }
6816 :
6817 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6818 :
6819 468 : ntups = PQntuples(res);
6820 :
6821 468 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6822 :
6823 468 : i_tableoid = PQfnumber(res, "tableoid");
6824 468 : i_oid = PQfnumber(res, "oid");
6825 468 : i_proname = PQfnumber(res, "proname");
6826 468 : i_pronamespace = PQfnumber(res, "pronamespace");
6827 468 : i_proowner = PQfnumber(res, "proowner");
6828 468 : i_prolang = PQfnumber(res, "prolang");
6829 468 : i_pronargs = PQfnumber(res, "pronargs");
6830 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6831 468 : i_prorettype = PQfnumber(res, "prorettype");
6832 468 : i_proacl = PQfnumber(res, "proacl");
6833 468 : i_acldefault = PQfnumber(res, "acldefault");
6834 :
6835 12766 : for (i = 0; i < ntups; i++)
6836 : {
6837 12298 : finfo[i].dobj.objType = DO_FUNC;
6838 12298 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6839 12298 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6840 12298 : AssignDumpId(&finfo[i].dobj);
6841 12298 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6842 24596 : finfo[i].dobj.namespace =
6843 12298 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6844 12298 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6845 12298 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6846 12298 : finfo[i].dacl.privtype = 0;
6847 12298 : finfo[i].dacl.initprivs = NULL;
6848 12298 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6849 12298 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6850 12298 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6851 12298 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6852 12298 : if (finfo[i].nargs == 0)
6853 2768 : finfo[i].argtypes = NULL;
6854 : else
6855 : {
6856 9530 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6857 9530 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6858 9530 : finfo[i].argtypes, finfo[i].nargs);
6859 : }
6860 12298 : finfo[i].postponed_def = false; /* might get set during sort */
6861 :
6862 : /* Decide whether we want to dump it */
6863 12298 : selectDumpableObject(&(finfo[i].dobj), fout);
6864 :
6865 : /* Mark whether function has an ACL */
6866 12298 : if (!PQgetisnull(res, i, i_proacl))
6867 310 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6868 : }
6869 :
6870 468 : PQclear(res);
6871 :
6872 468 : destroyPQExpBuffer(query);
6873 468 : }
6874 :
6875 : /*
6876 : * getRelationStatistics
6877 : * register the statistics object as a dependent of the relation.
6878 : *
6879 : * reltuples is passed as a string to avoid complexities in converting from/to
6880 : * floating point.
6881 : */
6882 : static RelStatsInfo *
6883 26398 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
6884 : char *reltuples, int32 relallvisible,
6885 : int32 relallfrozen, char relkind,
6886 : char **indAttNames, int nindAttNames)
6887 : {
6888 26398 : if (!fout->dopt->dumpStatistics)
6889 18842 : return NULL;
6890 :
6891 7556 : if ((relkind == RELKIND_RELATION) ||
6892 3276 : (relkind == RELKIND_PARTITIONED_TABLE) ||
6893 2002 : (relkind == RELKIND_INDEX) ||
6894 1314 : (relkind == RELKIND_PARTITIONED_INDEX) ||
6895 578 : (relkind == RELKIND_MATVIEW ||
6896 : relkind == RELKIND_FOREIGN_TABLE))
6897 : {
6898 7050 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
6899 7050 : DumpableObject *dobj = &info->dobj;
6900 :
6901 7050 : dobj->objType = DO_REL_STATS;
6902 7050 : dobj->catId.tableoid = 0;
6903 7050 : dobj->catId.oid = 0;
6904 7050 : AssignDumpId(dobj);
6905 7050 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
6906 7050 : dobj->dependencies[0] = rel->dumpId;
6907 7050 : dobj->nDeps = 1;
6908 7050 : dobj->allocDeps = 1;
6909 7050 : dobj->components |= DUMP_COMPONENT_STATISTICS;
6910 7050 : dobj->name = pg_strdup(rel->name);
6911 7050 : dobj->namespace = rel->namespace;
6912 7050 : info->relpages = relpages;
6913 7050 : info->reltuples = pstrdup(reltuples);
6914 7050 : info->relallvisible = relallvisible;
6915 7050 : info->relallfrozen = relallfrozen;
6916 7050 : info->relkind = relkind;
6917 7050 : info->indAttNames = indAttNames;
6918 7050 : info->nindAttNames = nindAttNames;
6919 :
6920 : /*
6921 : * Ordinarily, stats go in SECTION_DATA for tables and
6922 : * SECTION_POST_DATA for indexes.
6923 : *
6924 : * However, the section may be updated later for materialized view
6925 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
6926 : * the stats, so the stats must be restored after the data. Also, the
6927 : * materialized view definition may be postponed to SECTION_POST_DATA
6928 : * (see repairMatViewBoundaryMultiLoop()).
6929 : */
6930 7050 : switch (info->relkind)
6931 : {
6932 5088 : case RELKIND_RELATION:
6933 : case RELKIND_PARTITIONED_TABLE:
6934 : case RELKIND_MATVIEW:
6935 : case RELKIND_FOREIGN_TABLE:
6936 5088 : info->section = SECTION_DATA;
6937 5088 : break;
6938 1962 : case RELKIND_INDEX:
6939 : case RELKIND_PARTITIONED_INDEX:
6940 1962 : info->section = SECTION_POST_DATA;
6941 1962 : break;
6942 0 : default:
6943 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
6944 : info->relkind);
6945 : }
6946 :
6947 7050 : return info;
6948 : }
6949 506 : return NULL;
6950 : }
6951 :
6952 : /*
6953 : * getTables
6954 : * read all the tables (no indexes) in the system catalogs,
6955 : * and return them as an array of TableInfo structures
6956 : *
6957 : * *numTables is set to the number of tables read in
6958 : */
6959 : TableInfo *
6960 470 : getTables(Archive *fout, int *numTables)
6961 : {
6962 470 : DumpOptions *dopt = fout->dopt;
6963 : PGresult *res;
6964 : int ntups;
6965 : int i;
6966 470 : PQExpBuffer query = createPQExpBuffer();
6967 : TableInfo *tblinfo;
6968 : int i_reltableoid;
6969 : int i_reloid;
6970 : int i_relname;
6971 : int i_relnamespace;
6972 : int i_relkind;
6973 : int i_reltype;
6974 : int i_relowner;
6975 : int i_relchecks;
6976 : int i_relhasindex;
6977 : int i_relhasrules;
6978 : int i_relpages;
6979 : int i_reltuples;
6980 : int i_relallvisible;
6981 : int i_relallfrozen;
6982 : int i_toastpages;
6983 : int i_owning_tab;
6984 : int i_owning_col;
6985 : int i_reltablespace;
6986 : int i_relhasoids;
6987 : int i_relhastriggers;
6988 : int i_relpersistence;
6989 : int i_relispopulated;
6990 : int i_relreplident;
6991 : int i_relrowsec;
6992 : int i_relforcerowsec;
6993 : int i_relfrozenxid;
6994 : int i_toastfrozenxid;
6995 : int i_toastoid;
6996 : int i_relminmxid;
6997 : int i_toastminmxid;
6998 : int i_reloptions;
6999 : int i_checkoption;
7000 : int i_toastreloptions;
7001 : int i_reloftype;
7002 : int i_foreignserver;
7003 : int i_amname;
7004 : int i_is_identity_sequence;
7005 : int i_relacl;
7006 : int i_acldefault;
7007 : int i_ispartition;
7008 :
7009 : /*
7010 : * Find all the tables and table-like objects.
7011 : *
7012 : * We must fetch all tables in this phase because otherwise we cannot
7013 : * correctly identify inherited columns, owned sequences, etc.
7014 : *
7015 : * We include system catalogs, so that we can work if a user table is
7016 : * defined to inherit from a system catalog (pretty weird, but...)
7017 : *
7018 : * Note: in this phase we should collect only a minimal amount of
7019 : * information about each table, basically just enough to decide if it is
7020 : * interesting. In particular, since we do not yet have lock on any user
7021 : * table, we MUST NOT invoke any server-side data collection functions
7022 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7023 : * wrong answers if any concurrent DDL is happening.
7024 : */
7025 :
7026 470 : appendPQExpBufferStr(query,
7027 : "SELECT c.tableoid, c.oid, c.relname, "
7028 : "c.relnamespace, c.relkind, c.reltype, "
7029 : "c.relowner, "
7030 : "c.relchecks, "
7031 : "c.relhasindex, c.relhasrules, c.relpages, "
7032 : "c.reltuples, c.relallvisible, ");
7033 :
7034 470 : if (fout->remoteVersion >= 180000)
7035 470 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7036 : else
7037 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7038 :
7039 470 : appendPQExpBufferStr(query,
7040 : "c.relhastriggers, c.relpersistence, "
7041 : "c.reloftype, "
7042 : "c.relacl, "
7043 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7044 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7045 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7046 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7047 : "ELSE 0 END AS foreignserver, "
7048 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7049 : "tc.oid AS toid, "
7050 : "tc.relpages AS toastpages, "
7051 : "tc.reloptions AS toast_reloptions, "
7052 : "d.refobjid AS owning_tab, "
7053 : "d.refobjsubid AS owning_col, "
7054 : "tsp.spcname AS reltablespace, ");
7055 :
7056 470 : if (fout->remoteVersion >= 120000)
7057 470 : appendPQExpBufferStr(query,
7058 : "false AS relhasoids, ");
7059 : else
7060 0 : appendPQExpBufferStr(query,
7061 : "c.relhasoids, ");
7062 :
7063 470 : if (fout->remoteVersion >= 90300)
7064 470 : appendPQExpBufferStr(query,
7065 : "c.relispopulated, ");
7066 : else
7067 0 : appendPQExpBufferStr(query,
7068 : "'t' as relispopulated, ");
7069 :
7070 470 : if (fout->remoteVersion >= 90400)
7071 470 : appendPQExpBufferStr(query,
7072 : "c.relreplident, ");
7073 : else
7074 0 : appendPQExpBufferStr(query,
7075 : "'d' AS relreplident, ");
7076 :
7077 470 : if (fout->remoteVersion >= 90500)
7078 470 : appendPQExpBufferStr(query,
7079 : "c.relrowsecurity, c.relforcerowsecurity, ");
7080 : else
7081 0 : appendPQExpBufferStr(query,
7082 : "false AS relrowsecurity, "
7083 : "false AS relforcerowsecurity, ");
7084 :
7085 470 : if (fout->remoteVersion >= 90300)
7086 470 : appendPQExpBufferStr(query,
7087 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7088 : else
7089 0 : appendPQExpBufferStr(query,
7090 : "0 AS relminmxid, 0 AS tminmxid, ");
7091 :
7092 470 : if (fout->remoteVersion >= 90300)
7093 470 : appendPQExpBufferStr(query,
7094 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7095 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7096 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7097 : else
7098 0 : appendPQExpBufferStr(query,
7099 : "c.reloptions, NULL AS checkoption, ");
7100 :
7101 470 : if (fout->remoteVersion >= 90600)
7102 470 : appendPQExpBufferStr(query,
7103 : "am.amname, ");
7104 : else
7105 0 : appendPQExpBufferStr(query,
7106 : "NULL AS amname, ");
7107 :
7108 470 : if (fout->remoteVersion >= 90600)
7109 470 : appendPQExpBufferStr(query,
7110 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7111 : else
7112 0 : appendPQExpBufferStr(query,
7113 : "false AS is_identity_sequence, ");
7114 :
7115 470 : if (fout->remoteVersion >= 100000)
7116 470 : appendPQExpBufferStr(query,
7117 : "c.relispartition AS ispartition ");
7118 : else
7119 0 : appendPQExpBufferStr(query,
7120 : "false AS ispartition ");
7121 :
7122 : /*
7123 : * Left join to pg_depend to pick up dependency info linking sequences to
7124 : * their owning column, if any (note this dependency is AUTO except for
7125 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7126 : * collect the spcname.
7127 : */
7128 470 : appendPQExpBufferStr(query,
7129 : "\nFROM pg_class c\n"
7130 : "LEFT JOIN pg_depend d ON "
7131 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7132 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7133 : "d.objsubid = 0 AND "
7134 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7135 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7136 :
7137 : /*
7138 : * In 9.6 and up, left join to pg_am to pick up the amname.
7139 : */
7140 470 : if (fout->remoteVersion >= 90600)
7141 470 : appendPQExpBufferStr(query,
7142 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7143 :
7144 : /*
7145 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7146 : * that versions 10 and 11 have them, but later versions do not, so
7147 : * emitting them causes the upgrade to fail.
7148 : */
7149 470 : appendPQExpBufferStr(query,
7150 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7151 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7152 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7153 :
7154 : /*
7155 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7156 : * relkinds are possible in older servers, but it's not worth the trouble
7157 : * to emit a version-dependent list.
7158 : *
7159 : * Composite-type table entries won't be dumped as such, but we have to
7160 : * make a DumpableObject for them so that we can track dependencies of the
7161 : * composite type (pg_depend entries for columns of the composite type
7162 : * link to the pg_class entry not the pg_type entry).
7163 : */
7164 470 : appendPQExpBufferStr(query,
7165 : "WHERE c.relkind IN ("
7166 : CppAsString2(RELKIND_RELATION) ", "
7167 : CppAsString2(RELKIND_SEQUENCE) ", "
7168 : CppAsString2(RELKIND_VIEW) ", "
7169 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7170 : CppAsString2(RELKIND_MATVIEW) ", "
7171 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7172 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7173 : "ORDER BY c.oid");
7174 :
7175 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7176 :
7177 470 : ntups = PQntuples(res);
7178 :
7179 470 : *numTables = ntups;
7180 :
7181 : /*
7182 : * Extract data from result and lock dumpable tables. We do the locking
7183 : * before anything else, to minimize the window wherein a table could
7184 : * disappear under us.
7185 : *
7186 : * Note that we have to save info about all tables here, even when dumping
7187 : * only one, because we don't yet know which tables might be inheritance
7188 : * ancestors of the target table.
7189 : */
7190 470 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7191 :
7192 470 : i_reltableoid = PQfnumber(res, "tableoid");
7193 470 : i_reloid = PQfnumber(res, "oid");
7194 470 : i_relname = PQfnumber(res, "relname");
7195 470 : i_relnamespace = PQfnumber(res, "relnamespace");
7196 470 : i_relkind = PQfnumber(res, "relkind");
7197 470 : i_reltype = PQfnumber(res, "reltype");
7198 470 : i_relowner = PQfnumber(res, "relowner");
7199 470 : i_relchecks = PQfnumber(res, "relchecks");
7200 470 : i_relhasindex = PQfnumber(res, "relhasindex");
7201 470 : i_relhasrules = PQfnumber(res, "relhasrules");
7202 470 : i_relpages = PQfnumber(res, "relpages");
7203 470 : i_reltuples = PQfnumber(res, "reltuples");
7204 470 : i_relallvisible = PQfnumber(res, "relallvisible");
7205 470 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7206 470 : i_toastpages = PQfnumber(res, "toastpages");
7207 470 : i_owning_tab = PQfnumber(res, "owning_tab");
7208 470 : i_owning_col = PQfnumber(res, "owning_col");
7209 470 : i_reltablespace = PQfnumber(res, "reltablespace");
7210 470 : i_relhasoids = PQfnumber(res, "relhasoids");
7211 470 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7212 470 : i_relpersistence = PQfnumber(res, "relpersistence");
7213 470 : i_relispopulated = PQfnumber(res, "relispopulated");
7214 470 : i_relreplident = PQfnumber(res, "relreplident");
7215 470 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7216 470 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7217 470 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7218 470 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7219 470 : i_toastoid = PQfnumber(res, "toid");
7220 470 : i_relminmxid = PQfnumber(res, "relminmxid");
7221 470 : i_toastminmxid = PQfnumber(res, "tminmxid");
7222 470 : i_reloptions = PQfnumber(res, "reloptions");
7223 470 : i_checkoption = PQfnumber(res, "checkoption");
7224 470 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7225 470 : i_reloftype = PQfnumber(res, "reloftype");
7226 470 : i_foreignserver = PQfnumber(res, "foreignserver");
7227 470 : i_amname = PQfnumber(res, "amname");
7228 470 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7229 470 : i_relacl = PQfnumber(res, "relacl");
7230 470 : i_acldefault = PQfnumber(res, "acldefault");
7231 470 : i_ispartition = PQfnumber(res, "ispartition");
7232 :
7233 470 : if (dopt->lockWaitTimeout)
7234 : {
7235 : /*
7236 : * Arrange to fail instead of waiting forever for a table lock.
7237 : *
7238 : * NB: this coding assumes that the only queries issued within the
7239 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7240 : * applied to other things too.
7241 : */
7242 4 : resetPQExpBuffer(query);
7243 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7244 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7245 4 : ExecuteSqlStatement(fout, query->data);
7246 : }
7247 :
7248 470 : resetPQExpBuffer(query);
7249 :
7250 124250 : for (i = 0; i < ntups; i++)
7251 : {
7252 123780 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7253 123780 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7254 :
7255 123780 : tblinfo[i].dobj.objType = DO_TABLE;
7256 123780 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7257 123780 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7258 123780 : AssignDumpId(&tblinfo[i].dobj);
7259 123780 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7260 247560 : tblinfo[i].dobj.namespace =
7261 123780 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7262 123780 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7263 123780 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7264 123780 : tblinfo[i].dacl.privtype = 0;
7265 123780 : tblinfo[i].dacl.initprivs = NULL;
7266 123780 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7267 123780 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7268 123780 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7269 123780 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7270 123780 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7271 123780 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7272 123780 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7273 123780 : if (PQgetisnull(res, i, i_toastpages))
7274 98802 : tblinfo[i].toastpages = 0;
7275 : else
7276 24978 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7277 123780 : if (PQgetisnull(res, i, i_owning_tab))
7278 : {
7279 122710 : tblinfo[i].owning_tab = InvalidOid;
7280 122710 : tblinfo[i].owning_col = 0;
7281 : }
7282 : else
7283 : {
7284 1070 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7285 1070 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7286 : }
7287 123780 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7288 123780 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7289 123780 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7290 123780 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7291 123780 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7292 123780 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7293 123780 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7294 123780 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7295 123780 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7296 123780 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7297 123780 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7298 123780 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7299 123780 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7300 123780 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7301 123780 : if (PQgetisnull(res, i, i_checkoption))
7302 123680 : tblinfo[i].checkoption = NULL;
7303 : else
7304 100 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7305 123780 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7306 123780 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7307 123780 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7308 123780 : if (PQgetisnull(res, i, i_amname))
7309 73434 : tblinfo[i].amname = NULL;
7310 : else
7311 50346 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7312 123780 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7313 123780 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7314 :
7315 : /* other fields were zeroed above */
7316 :
7317 : /*
7318 : * Decide whether we want to dump this table.
7319 : */
7320 123780 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7321 470 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7322 : else
7323 123310 : selectDumpableTable(&tblinfo[i], fout);
7324 :
7325 : /*
7326 : * Now, consider the table "interesting" if we need to dump its
7327 : * definition, data or its statistics. Later on, we'll skip a lot of
7328 : * data collection for uninteresting tables.
7329 : *
7330 : * Note: the "interesting" flag will also be set by flagInhTables for
7331 : * parents of interesting tables, so that we collect necessary
7332 : * inheritance info even when the parents are not themselves being
7333 : * dumped. This is the main reason why we need an "interesting" flag
7334 : * that's separate from the components-to-dump bitmask.
7335 : */
7336 123780 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7337 : (DUMP_COMPONENT_DEFINITION |
7338 : DUMP_COMPONENT_DATA |
7339 123780 : DUMP_COMPONENT_STATISTICS)) != 0;
7340 :
7341 123780 : tblinfo[i].dummy_view = false; /* might get set during sort */
7342 123780 : tblinfo[i].postponed_def = false; /* might get set during sort */
7343 :
7344 : /* Tables have data */
7345 123780 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7346 :
7347 : /* Mark whether table has an ACL */
7348 123780 : if (!PQgetisnull(res, i, i_relacl))
7349 98086 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7350 123780 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7351 :
7352 : /* Add statistics */
7353 123780 : if (tblinfo[i].interesting)
7354 : {
7355 : RelStatsInfo *stats;
7356 :
7357 38820 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7358 19410 : tblinfo[i].relpages,
7359 : PQgetvalue(res, i, i_reltuples),
7360 : relallvisible, relallfrozen,
7361 19410 : tblinfo[i].relkind, NULL, 0);
7362 19410 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7363 988 : tblinfo[i].stats = stats;
7364 : }
7365 :
7366 : /*
7367 : * Read-lock target tables to make sure they aren't DROPPED or altered
7368 : * in schema before we get around to dumping them.
7369 : *
7370 : * Note that we don't explicitly lock parents of the target tables; we
7371 : * assume our lock on the child is enough to prevent schema
7372 : * alterations to parent tables.
7373 : *
7374 : * NOTE: it'd be kinda nice to lock other relations too, not only
7375 : * plain or partitioned tables, but the backend doesn't presently
7376 : * allow that.
7377 : *
7378 : * We only need to lock the table for certain components; see
7379 : * pg_dump.h
7380 : */
7381 123780 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7382 19410 : (tblinfo[i].relkind == RELKIND_RELATION ||
7383 5266 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7384 : {
7385 : /*
7386 : * Tables are locked in batches. When dumping from a remote
7387 : * server this can save a significant amount of time by reducing
7388 : * the number of round trips.
7389 : */
7390 15802 : if (query->len == 0)
7391 316 : appendPQExpBuffer(query, "LOCK TABLE %s",
7392 316 : fmtQualifiedDumpable(&tblinfo[i]));
7393 : else
7394 : {
7395 15486 : appendPQExpBuffer(query, ", %s",
7396 15486 : fmtQualifiedDumpable(&tblinfo[i]));
7397 :
7398 : /* Arbitrarily end a batch when query length reaches 100K. */
7399 15486 : if (query->len >= 100000)
7400 : {
7401 : /* Lock another batch of tables. */
7402 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7403 0 : ExecuteSqlStatement(fout, query->data);
7404 0 : resetPQExpBuffer(query);
7405 : }
7406 : }
7407 : }
7408 : }
7409 :
7410 470 : if (query->len != 0)
7411 : {
7412 : /* Lock the tables in the last batch. */
7413 316 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7414 316 : ExecuteSqlStatement(fout, query->data);
7415 : }
7416 :
7417 468 : if (dopt->lockWaitTimeout)
7418 : {
7419 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7420 : }
7421 :
7422 468 : PQclear(res);
7423 :
7424 468 : destroyPQExpBuffer(query);
7425 :
7426 468 : return tblinfo;
7427 : }
7428 :
7429 : /*
7430 : * getOwnedSeqs
7431 : * identify owned sequences and mark them as dumpable if owning table is
7432 : *
7433 : * We used to do this in getTables(), but it's better to do it after the
7434 : * index used by findTableByOid() has been set up.
7435 : */
7436 : void
7437 468 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7438 : {
7439 : int i;
7440 :
7441 : /*
7442 : * Force sequences that are "owned" by table columns to be dumped whenever
7443 : * their owning table is being dumped.
7444 : */
7445 123712 : for (i = 0; i < numTables; i++)
7446 : {
7447 123244 : TableInfo *seqinfo = &tblinfo[i];
7448 : TableInfo *owning_tab;
7449 :
7450 123244 : if (!OidIsValid(seqinfo->owning_tab))
7451 122180 : continue; /* not an owned sequence */
7452 :
7453 1064 : owning_tab = findTableByOid(seqinfo->owning_tab);
7454 1064 : if (owning_tab == NULL)
7455 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7456 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7457 :
7458 : /*
7459 : * For an identity sequence, dump exactly the same components for the
7460 : * sequence as for the owning table. This is important because we
7461 : * treat the identity sequence as an integral part of the table. For
7462 : * example, there is not any DDL command that allows creation of such
7463 : * a sequence independently of the table.
7464 : *
7465 : * For other owned sequences such as serial sequences, we need to dump
7466 : * the components that are being dumped for the table and any
7467 : * components that the sequence is explicitly marked with.
7468 : *
7469 : * We can't simply use the set of components which are being dumped
7470 : * for the table as the table might be in an extension (and only the
7471 : * non-extension components, eg: ACLs if changed, security labels, and
7472 : * policies, are being dumped) while the sequence is not (and
7473 : * therefore the definition and other components should also be
7474 : * dumped).
7475 : *
7476 : * If the sequence is part of the extension then it should be properly
7477 : * marked by checkExtensionMembership() and this will be a no-op as
7478 : * the table will be equivalently marked.
7479 : */
7480 1064 : if (seqinfo->is_identity_sequence)
7481 538 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7482 : else
7483 526 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7484 :
7485 : /* Make sure that necessary data is available if we're dumping it */
7486 1064 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7487 : {
7488 872 : seqinfo->interesting = true;
7489 872 : owning_tab->interesting = true;
7490 : }
7491 : }
7492 468 : }
7493 :
7494 : /*
7495 : * getInherits
7496 : * read all the inheritance information
7497 : * from the system catalogs return them in the InhInfo* structure
7498 : *
7499 : * numInherits is set to the number of pairs read in
7500 : */
7501 : InhInfo *
7502 468 : getInherits(Archive *fout, int *numInherits)
7503 : {
7504 : PGresult *res;
7505 : int ntups;
7506 : int i;
7507 468 : PQExpBuffer query = createPQExpBuffer();
7508 : InhInfo *inhinfo;
7509 :
7510 : int i_inhrelid;
7511 : int i_inhparent;
7512 :
7513 : /* find all the inheritance information */
7514 468 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7515 :
7516 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7517 :
7518 468 : ntups = PQntuples(res);
7519 :
7520 468 : *numInherits = ntups;
7521 :
7522 468 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7523 :
7524 468 : i_inhrelid = PQfnumber(res, "inhrelid");
7525 468 : i_inhparent = PQfnumber(res, "inhparent");
7526 :
7527 9066 : for (i = 0; i < ntups; i++)
7528 : {
7529 8598 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7530 8598 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7531 : }
7532 :
7533 468 : PQclear(res);
7534 :
7535 468 : destroyPQExpBuffer(query);
7536 :
7537 468 : return inhinfo;
7538 : }
7539 :
7540 : /*
7541 : * getPartitioningInfo
7542 : * get information about partitioning
7543 : *
7544 : * For the most part, we only collect partitioning info about tables we
7545 : * intend to dump. However, this function has to consider all partitioned
7546 : * tables in the database, because we need to know about parents of partitions
7547 : * we are going to dump even if the parents themselves won't be dumped.
7548 : *
7549 : * Specifically, what we need to know is whether each partitioned table
7550 : * has an "unsafe" partitioning scheme that requires us to force
7551 : * load-via-partition-root mode for its children. Currently the only case
7552 : * for which we force that is hash partitioning on enum columns, since the
7553 : * hash codes depend on enum value OIDs which won't be replicated across
7554 : * dump-and-reload. There are other cases in which load-via-partition-root
7555 : * might be necessary, but we expect users to cope with them.
7556 : */
7557 : void
7558 468 : getPartitioningInfo(Archive *fout)
7559 : {
7560 : PQExpBuffer query;
7561 : PGresult *res;
7562 : int ntups;
7563 :
7564 : /* hash partitioning didn't exist before v11 */
7565 468 : if (fout->remoteVersion < 110000)
7566 0 : return;
7567 : /* needn't bother if not dumping data */
7568 468 : if (!fout->dopt->dumpData)
7569 72 : return;
7570 :
7571 396 : query = createPQExpBuffer();
7572 :
7573 : /*
7574 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7575 : * appears among the partition opclasses. We needn't check partstrat.
7576 : *
7577 : * Note that this query may well retrieve info about tables we aren't
7578 : * going to dump and hence have no lock on. That's okay since we need not
7579 : * invoke any unsafe server-side functions.
7580 : */
7581 396 : appendPQExpBufferStr(query,
7582 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7583 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7584 : "ON c.opcmethod = a.oid\n"
7585 : "WHERE opcname = 'enum_ops' "
7586 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7587 : "AND amname = 'hash') = ANY(partclass)");
7588 :
7589 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7590 :
7591 396 : ntups = PQntuples(res);
7592 :
7593 400 : for (int i = 0; i < ntups; i++)
7594 : {
7595 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7596 : TableInfo *tbinfo;
7597 :
7598 4 : tbinfo = findTableByOid(tabrelid);
7599 4 : if (tbinfo == NULL)
7600 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7601 : tabrelid);
7602 4 : tbinfo->unsafe_partitions = true;
7603 : }
7604 :
7605 396 : PQclear(res);
7606 :
7607 396 : destroyPQExpBuffer(query);
7608 : }
7609 :
7610 : /*
7611 : * getIndexes
7612 : * get information about every index on a dumpable table
7613 : *
7614 : * Note: index data is not returned directly to the caller, but it
7615 : * does get entered into the DumpableObject tables.
7616 : */
7617 : void
7618 468 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7619 : {
7620 468 : PQExpBuffer query = createPQExpBuffer();
7621 468 : PQExpBuffer tbloids = createPQExpBuffer();
7622 : PGresult *res;
7623 : int ntups;
7624 : int curtblindx;
7625 : IndxInfo *indxinfo;
7626 : int i_tableoid,
7627 : i_oid,
7628 : i_indrelid,
7629 : i_indexname,
7630 : i_relpages,
7631 : i_reltuples,
7632 : i_relallvisible,
7633 : i_relallfrozen,
7634 : i_parentidx,
7635 : i_indexdef,
7636 : i_indnkeyatts,
7637 : i_indnatts,
7638 : i_indkey,
7639 : i_indisclustered,
7640 : i_indisreplident,
7641 : i_indnullsnotdistinct,
7642 : i_contype,
7643 : i_conname,
7644 : i_condeferrable,
7645 : i_condeferred,
7646 : i_conperiod,
7647 : i_contableoid,
7648 : i_conoid,
7649 : i_condef,
7650 : i_indattnames,
7651 : i_tablespace,
7652 : i_indreloptions,
7653 : i_indstatcols,
7654 : i_indstatvals;
7655 :
7656 : /*
7657 : * We want to perform just one query against pg_index. However, we
7658 : * mustn't try to select every row of the catalog and then sort it out on
7659 : * the client side, because some of the server-side functions we need
7660 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7661 : * build an array of the OIDs of tables we care about (and now have lock
7662 : * on!), and use a WHERE clause to constrain which rows are selected.
7663 : */
7664 468 : appendPQExpBufferChar(tbloids, '{');
7665 123712 : for (int i = 0; i < numTables; i++)
7666 : {
7667 123244 : TableInfo *tbinfo = &tblinfo[i];
7668 :
7669 123244 : if (!tbinfo->hasindex)
7670 86990 : continue;
7671 :
7672 : /*
7673 : * We can ignore indexes of uninteresting tables.
7674 : */
7675 36254 : if (!tbinfo->interesting)
7676 30722 : continue;
7677 :
7678 : /* OK, we need info for this table */
7679 5532 : if (tbloids->len > 1) /* do we have more than the '{'? */
7680 5362 : appendPQExpBufferChar(tbloids, ',');
7681 5532 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7682 : }
7683 468 : appendPQExpBufferChar(tbloids, '}');
7684 :
7685 468 : appendPQExpBufferStr(query,
7686 : "SELECT t.tableoid, t.oid, i.indrelid, "
7687 : "t.relname AS indexname, "
7688 : "t.relpages, t.reltuples, t.relallvisible, ");
7689 :
7690 468 : if (fout->remoteVersion >= 180000)
7691 468 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7692 : else
7693 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7694 :
7695 468 : appendPQExpBufferStr(query,
7696 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7697 : "i.indkey, i.indisclustered, "
7698 : "c.contype, c.conname, "
7699 : "c.condeferrable, c.condeferred, "
7700 : "c.tableoid AS contableoid, "
7701 : "c.oid AS conoid, "
7702 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7703 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7704 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7705 : " FROM pg_catalog.pg_attribute "
7706 : " WHERE attrelid = i.indexrelid) "
7707 : "ELSE NULL END AS indattnames, "
7708 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7709 : "t.reloptions AS indreloptions, ");
7710 :
7711 :
7712 468 : if (fout->remoteVersion >= 90400)
7713 468 : appendPQExpBufferStr(query,
7714 : "i.indisreplident, ");
7715 : else
7716 0 : appendPQExpBufferStr(query,
7717 : "false AS indisreplident, ");
7718 :
7719 468 : if (fout->remoteVersion >= 110000)
7720 468 : appendPQExpBufferStr(query,
7721 : "inh.inhparent AS parentidx, "
7722 : "i.indnkeyatts AS indnkeyatts, "
7723 : "i.indnatts AS indnatts, "
7724 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7725 : " FROM pg_catalog.pg_attribute "
7726 : " WHERE attrelid = i.indexrelid AND "
7727 : " attstattarget >= 0) AS indstatcols, "
7728 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7729 : " FROM pg_catalog.pg_attribute "
7730 : " WHERE attrelid = i.indexrelid AND "
7731 : " attstattarget >= 0) AS indstatvals, ");
7732 : else
7733 0 : appendPQExpBufferStr(query,
7734 : "0 AS parentidx, "
7735 : "i.indnatts AS indnkeyatts, "
7736 : "i.indnatts AS indnatts, "
7737 : "'' AS indstatcols, "
7738 : "'' AS indstatvals, ");
7739 :
7740 468 : if (fout->remoteVersion >= 150000)
7741 468 : appendPQExpBufferStr(query,
7742 : "i.indnullsnotdistinct, ");
7743 : else
7744 0 : appendPQExpBufferStr(query,
7745 : "false AS indnullsnotdistinct, ");
7746 :
7747 468 : if (fout->remoteVersion >= 180000)
7748 468 : appendPQExpBufferStr(query,
7749 : "c.conperiod ");
7750 : else
7751 0 : appendPQExpBufferStr(query,
7752 : "NULL AS conperiod ");
7753 :
7754 : /*
7755 : * The point of the messy-looking outer join is to find a constraint that
7756 : * is related by an internal dependency link to the index. If we find one,
7757 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7758 : * index won't have more than one internal dependency.
7759 : *
7760 : * Note: the check on conrelid is redundant, but useful because that
7761 : * column is indexed while conindid is not.
7762 : */
7763 468 : if (fout->remoteVersion >= 110000)
7764 : {
7765 468 : appendPQExpBuffer(query,
7766 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7767 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7768 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7769 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7770 : "LEFT JOIN pg_catalog.pg_constraint c "
7771 : "ON (i.indrelid = c.conrelid AND "
7772 : "i.indexrelid = c.conindid AND "
7773 : "c.contype IN ('p','u','x')) "
7774 : "LEFT JOIN pg_catalog.pg_inherits inh "
7775 : "ON (inh.inhrelid = indexrelid) "
7776 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7777 : "AND i.indisready "
7778 : "ORDER BY i.indrelid, indexname",
7779 : tbloids->data);
7780 : }
7781 : else
7782 : {
7783 : /*
7784 : * the test on indisready is necessary in 9.2, and harmless in
7785 : * earlier/later versions
7786 : */
7787 0 : appendPQExpBuffer(query,
7788 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7789 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7790 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7791 : "LEFT JOIN pg_catalog.pg_constraint c "
7792 : "ON (i.indrelid = c.conrelid AND "
7793 : "i.indexrelid = c.conindid AND "
7794 : "c.contype IN ('p','u','x')) "
7795 : "WHERE i.indisvalid AND i.indisready "
7796 : "ORDER BY i.indrelid, indexname",
7797 : tbloids->data);
7798 : }
7799 :
7800 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7801 :
7802 468 : ntups = PQntuples(res);
7803 :
7804 468 : i_tableoid = PQfnumber(res, "tableoid");
7805 468 : i_oid = PQfnumber(res, "oid");
7806 468 : i_indrelid = PQfnumber(res, "indrelid");
7807 468 : i_indexname = PQfnumber(res, "indexname");
7808 468 : i_relpages = PQfnumber(res, "relpages");
7809 468 : i_reltuples = PQfnumber(res, "reltuples");
7810 468 : i_relallvisible = PQfnumber(res, "relallvisible");
7811 468 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7812 468 : i_parentidx = PQfnumber(res, "parentidx");
7813 468 : i_indexdef = PQfnumber(res, "indexdef");
7814 468 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7815 468 : i_indnatts = PQfnumber(res, "indnatts");
7816 468 : i_indkey = PQfnumber(res, "indkey");
7817 468 : i_indisclustered = PQfnumber(res, "indisclustered");
7818 468 : i_indisreplident = PQfnumber(res, "indisreplident");
7819 468 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7820 468 : i_contype = PQfnumber(res, "contype");
7821 468 : i_conname = PQfnumber(res, "conname");
7822 468 : i_condeferrable = PQfnumber(res, "condeferrable");
7823 468 : i_condeferred = PQfnumber(res, "condeferred");
7824 468 : i_conperiod = PQfnumber(res, "conperiod");
7825 468 : i_contableoid = PQfnumber(res, "contableoid");
7826 468 : i_conoid = PQfnumber(res, "conoid");
7827 468 : i_condef = PQfnumber(res, "condef");
7828 468 : i_indattnames = PQfnumber(res, "indattnames");
7829 468 : i_tablespace = PQfnumber(res, "tablespace");
7830 468 : i_indreloptions = PQfnumber(res, "indreloptions");
7831 468 : i_indstatcols = PQfnumber(res, "indstatcols");
7832 468 : i_indstatvals = PQfnumber(res, "indstatvals");
7833 :
7834 468 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7835 :
7836 : /*
7837 : * Outer loop iterates once per table, not once per row. Incrementing of
7838 : * j is handled by the inner loop.
7839 : */
7840 468 : curtblindx = -1;
7841 5940 : for (int j = 0; j < ntups;)
7842 : {
7843 5472 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7844 5472 : TableInfo *tbinfo = NULL;
7845 5472 : char **indAttNames = NULL;
7846 5472 : int nindAttNames = 0;
7847 : int numinds;
7848 :
7849 : /* Count rows for this table */
7850 6988 : for (numinds = 1; numinds < ntups - j; numinds++)
7851 6818 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7852 5302 : break;
7853 :
7854 : /*
7855 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7856 : * order.
7857 : */
7858 54250 : while (++curtblindx < numTables)
7859 : {
7860 54250 : tbinfo = &tblinfo[curtblindx];
7861 54250 : if (tbinfo->dobj.catId.oid == indrelid)
7862 5472 : break;
7863 : }
7864 5472 : if (curtblindx >= numTables)
7865 0 : pg_fatal("unrecognized table OID %u", indrelid);
7866 : /* cross-check that we only got requested tables */
7867 5472 : if (!tbinfo->hasindex ||
7868 5472 : !tbinfo->interesting)
7869 0 : pg_fatal("unexpected index data for table \"%s\"",
7870 : tbinfo->dobj.name);
7871 :
7872 : /* Save data for this table */
7873 5472 : tbinfo->indexes = indxinfo + j;
7874 5472 : tbinfo->numIndexes = numinds;
7875 :
7876 12460 : for (int c = 0; c < numinds; c++, j++)
7877 : {
7878 : char contype;
7879 : char indexkind;
7880 : RelStatsInfo *relstats;
7881 6988 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
7882 6988 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
7883 6988 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
7884 :
7885 6988 : indxinfo[j].dobj.objType = DO_INDEX;
7886 6988 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7887 6988 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7888 6988 : AssignDumpId(&indxinfo[j].dobj);
7889 6988 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7890 6988 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7891 6988 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7892 6988 : indxinfo[j].indextable = tbinfo;
7893 6988 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7894 6988 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7895 6988 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7896 6988 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7897 6988 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7898 6988 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7899 6988 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7900 6988 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7901 6988 : parseOidArray(PQgetvalue(res, j, i_indkey),
7902 6988 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7903 6988 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7904 6988 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7905 6988 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7906 6988 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7907 6988 : indxinfo[j].partattaches = (SimplePtrList)
7908 : {
7909 : NULL, NULL
7910 : };
7911 :
7912 6988 : if (indxinfo[j].parentidx == 0)
7913 5508 : indexkind = RELKIND_INDEX;
7914 : else
7915 1480 : indexkind = RELKIND_PARTITIONED_INDEX;
7916 :
7917 6988 : if (!PQgetisnull(res, j, i_indattnames))
7918 : {
7919 392 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
7920 : &indAttNames, &nindAttNames))
7921 0 : pg_fatal("could not parse %s array", "indattnames");
7922 : }
7923 :
7924 6988 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
7925 : PQgetvalue(res, j, i_reltuples),
7926 : relallvisible, relallfrozen, indexkind,
7927 : indAttNames, nindAttNames);
7928 :
7929 6988 : contype = *(PQgetvalue(res, j, i_contype));
7930 6988 : if (contype == 'p' || contype == 'u' || contype == 'x')
7931 3896 : {
7932 : /*
7933 : * If we found a constraint matching the index, create an
7934 : * entry for it.
7935 : */
7936 : ConstraintInfo *constrinfo;
7937 :
7938 3896 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7939 3896 : constrinfo->dobj.objType = DO_CONSTRAINT;
7940 3896 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7941 3896 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7942 3896 : AssignDumpId(&constrinfo->dobj);
7943 3896 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7944 3896 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7945 3896 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7946 3896 : constrinfo->contable = tbinfo;
7947 3896 : constrinfo->condomain = NULL;
7948 3896 : constrinfo->contype = contype;
7949 3896 : if (contype == 'x')
7950 32 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7951 : else
7952 3864 : constrinfo->condef = NULL;
7953 3896 : constrinfo->confrelid = InvalidOid;
7954 3896 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7955 3896 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7956 3896 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7957 3896 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7958 3896 : constrinfo->conislocal = true;
7959 3896 : constrinfo->separate = true;
7960 :
7961 3896 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7962 3896 : if (relstats != NULL)
7963 1148 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
7964 : }
7965 : else
7966 : {
7967 : /* Plain secondary index */
7968 3092 : indxinfo[j].indexconstraint = 0;
7969 : }
7970 : }
7971 : }
7972 :
7973 468 : PQclear(res);
7974 :
7975 468 : destroyPQExpBuffer(query);
7976 468 : destroyPQExpBuffer(tbloids);
7977 468 : }
7978 :
7979 : /*
7980 : * getExtendedStatistics
7981 : * get information about extended-statistics objects.
7982 : *
7983 : * Note: extended statistics data is not returned directly to the caller, but
7984 : * it does get entered into the DumpableObject tables.
7985 : */
7986 : void
7987 468 : getExtendedStatistics(Archive *fout)
7988 : {
7989 : PQExpBuffer query;
7990 : PGresult *res;
7991 : StatsExtInfo *statsextinfo;
7992 : int ntups;
7993 : int i_tableoid;
7994 : int i_oid;
7995 : int i_stxname;
7996 : int i_stxnamespace;
7997 : int i_stxowner;
7998 : int i_stxrelid;
7999 : int i_stattarget;
8000 : int i;
8001 :
8002 : /* Extended statistics were new in v10 */
8003 468 : if (fout->remoteVersion < 100000)
8004 0 : return;
8005 :
8006 468 : query = createPQExpBuffer();
8007 :
8008 468 : if (fout->remoteVersion < 130000)
8009 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8010 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8011 : "FROM pg_catalog.pg_statistic_ext");
8012 : else
8013 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8014 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8015 : "FROM pg_catalog.pg_statistic_ext");
8016 :
8017 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8018 :
8019 468 : ntups = PQntuples(res);
8020 :
8021 468 : i_tableoid = PQfnumber(res, "tableoid");
8022 468 : i_oid = PQfnumber(res, "oid");
8023 468 : i_stxname = PQfnumber(res, "stxname");
8024 468 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8025 468 : i_stxowner = PQfnumber(res, "stxowner");
8026 468 : i_stxrelid = PQfnumber(res, "stxrelid");
8027 468 : i_stattarget = PQfnumber(res, "stxstattarget");
8028 :
8029 468 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8030 :
8031 842 : for (i = 0; i < ntups; i++)
8032 : {
8033 374 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8034 374 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8035 374 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8036 374 : AssignDumpId(&statsextinfo[i].dobj);
8037 374 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8038 748 : statsextinfo[i].dobj.namespace =
8039 374 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8040 374 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8041 748 : statsextinfo[i].stattable =
8042 374 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8043 374 : if (PQgetisnull(res, i, i_stattarget))
8044 276 : statsextinfo[i].stattarget = -1;
8045 : else
8046 98 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8047 :
8048 : /* Decide whether we want to dump it */
8049 374 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8050 : }
8051 :
8052 468 : PQclear(res);
8053 468 : destroyPQExpBuffer(query);
8054 : }
8055 :
8056 : /*
8057 : * getConstraints
8058 : *
8059 : * Get info about constraints on dumpable tables.
8060 : *
8061 : * Currently handles foreign keys only.
8062 : * Unique and primary key constraints are handled with indexes,
8063 : * while check constraints are processed in getTableAttrs().
8064 : */
8065 : void
8066 468 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8067 : {
8068 468 : PQExpBuffer query = createPQExpBuffer();
8069 468 : PQExpBuffer tbloids = createPQExpBuffer();
8070 : PGresult *res;
8071 : int ntups;
8072 : int curtblindx;
8073 468 : TableInfo *tbinfo = NULL;
8074 : ConstraintInfo *constrinfo;
8075 : int i_contableoid,
8076 : i_conoid,
8077 : i_conrelid,
8078 : i_conname,
8079 : i_confrelid,
8080 : i_conindid,
8081 : i_condef;
8082 :
8083 : /*
8084 : * We want to perform just one query against pg_constraint. However, we
8085 : * mustn't try to select every row of the catalog and then sort it out on
8086 : * the client side, because some of the server-side functions we need
8087 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8088 : * build an array of the OIDs of tables we care about (and now have lock
8089 : * on!), and use a WHERE clause to constrain which rows are selected.
8090 : */
8091 468 : appendPQExpBufferChar(tbloids, '{');
8092 123712 : for (int i = 0; i < numTables; i++)
8093 : {
8094 123244 : TableInfo *tinfo = &tblinfo[i];
8095 :
8096 123244 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8097 103936 : continue;
8098 :
8099 : /* OK, we need info for this table */
8100 19308 : if (tbloids->len > 1) /* do we have more than the '{'? */
8101 18990 : appendPQExpBufferChar(tbloids, ',');
8102 19308 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8103 : }
8104 468 : appendPQExpBufferChar(tbloids, '}');
8105 :
8106 468 : appendPQExpBufferStr(query,
8107 : "SELECT c.tableoid, c.oid, "
8108 : "conrelid, conname, confrelid, ");
8109 468 : if (fout->remoteVersion >= 110000)
8110 468 : appendPQExpBufferStr(query, "conindid, ");
8111 : else
8112 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8113 468 : appendPQExpBuffer(query,
8114 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8115 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8116 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8117 : "WHERE contype = 'f' ",
8118 : tbloids->data);
8119 468 : if (fout->remoteVersion >= 110000)
8120 468 : appendPQExpBufferStr(query,
8121 : "AND conparentid = 0 ");
8122 468 : appendPQExpBufferStr(query,
8123 : "ORDER BY conrelid, conname");
8124 :
8125 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8126 :
8127 468 : ntups = PQntuples(res);
8128 :
8129 468 : i_contableoid = PQfnumber(res, "tableoid");
8130 468 : i_conoid = PQfnumber(res, "oid");
8131 468 : i_conrelid = PQfnumber(res, "conrelid");
8132 468 : i_conname = PQfnumber(res, "conname");
8133 468 : i_confrelid = PQfnumber(res, "confrelid");
8134 468 : i_conindid = PQfnumber(res, "conindid");
8135 468 : i_condef = PQfnumber(res, "condef");
8136 :
8137 468 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8138 :
8139 468 : curtblindx = -1;
8140 940 : for (int j = 0; j < ntups; j++)
8141 : {
8142 472 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8143 : TableInfo *reftable;
8144 :
8145 : /*
8146 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8147 : * order.
8148 : */
8149 472 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8150 : {
8151 34242 : while (++curtblindx < numTables)
8152 : {
8153 34242 : tbinfo = &tblinfo[curtblindx];
8154 34242 : if (tbinfo->dobj.catId.oid == conrelid)
8155 440 : break;
8156 : }
8157 440 : if (curtblindx >= numTables)
8158 0 : pg_fatal("unrecognized table OID %u", conrelid);
8159 : }
8160 :
8161 472 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8162 472 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8163 472 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8164 472 : AssignDumpId(&constrinfo[j].dobj);
8165 472 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8166 472 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8167 472 : constrinfo[j].contable = tbinfo;
8168 472 : constrinfo[j].condomain = NULL;
8169 472 : constrinfo[j].contype = 'f';
8170 472 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8171 472 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8172 472 : constrinfo[j].conindex = 0;
8173 472 : constrinfo[j].condeferrable = false;
8174 472 : constrinfo[j].condeferred = false;
8175 472 : constrinfo[j].conislocal = true;
8176 472 : constrinfo[j].separate = true;
8177 :
8178 : /*
8179 : * Restoring an FK that points to a partitioned table requires that
8180 : * all partition indexes have been attached beforehand. Ensure that
8181 : * happens by making the constraint depend on each index partition
8182 : * attach object.
8183 : */
8184 472 : reftable = findTableByOid(constrinfo[j].confrelid);
8185 472 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8186 : {
8187 64 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8188 :
8189 64 : if (indexOid != InvalidOid)
8190 : {
8191 64 : for (int k = 0; k < reftable->numIndexes; k++)
8192 : {
8193 : IndxInfo *refidx;
8194 :
8195 : /* not our index? */
8196 64 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8197 0 : continue;
8198 :
8199 64 : refidx = &reftable->indexes[k];
8200 64 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8201 64 : break;
8202 : }
8203 : }
8204 : }
8205 : }
8206 :
8207 468 : PQclear(res);
8208 :
8209 468 : destroyPQExpBuffer(query);
8210 468 : destroyPQExpBuffer(tbloids);
8211 468 : }
8212 :
8213 : /*
8214 : * addConstrChildIdxDeps
8215 : *
8216 : * Recursive subroutine for getConstraints
8217 : *
8218 : * Given an object representing a foreign key constraint and an index on the
8219 : * partitioned table it references, mark the constraint object as dependent
8220 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8221 : * drilling down to their partitions if any. This ensures that the FK is not
8222 : * restored until the index is fully marked valid.
8223 : */
8224 : static void
8225 144 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8226 : {
8227 : SimplePtrListCell *cell;
8228 :
8229 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8230 :
8231 496 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8232 : {
8233 352 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8234 :
8235 352 : addObjectDependency(dobj, attach->dobj.dumpId);
8236 :
8237 352 : if (attach->partitionIdx->partattaches.head != NULL)
8238 80 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8239 : }
8240 144 : }
8241 :
8242 : /*
8243 : * getDomainConstraints
8244 : *
8245 : * Get info about constraints on a domain.
8246 : */
8247 : static void
8248 452 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8249 : {
8250 : int i;
8251 : ConstraintInfo *constrinfo;
8252 452 : PQExpBuffer query = createPQExpBuffer();
8253 : PGresult *res;
8254 : int i_tableoid,
8255 : i_oid,
8256 : i_conname,
8257 : i_consrc;
8258 : int ntups;
8259 :
8260 452 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8261 : {
8262 : /* Set up query for constraint-specific details */
8263 100 : appendPQExpBufferStr(query,
8264 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8265 : "SELECT tableoid, oid, conname, "
8266 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8267 : "convalidated "
8268 : "FROM pg_catalog.pg_constraint "
8269 : "WHERE contypid = $1 AND contype = 'c' "
8270 : "ORDER BY conname");
8271 :
8272 100 : ExecuteSqlStatement(fout, query->data);
8273 :
8274 100 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8275 : }
8276 :
8277 452 : printfPQExpBuffer(query,
8278 : "EXECUTE getDomainConstraints('%u')",
8279 : tyinfo->dobj.catId.oid);
8280 :
8281 452 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8282 :
8283 452 : ntups = PQntuples(res);
8284 :
8285 452 : i_tableoid = PQfnumber(res, "tableoid");
8286 452 : i_oid = PQfnumber(res, "oid");
8287 452 : i_conname = PQfnumber(res, "conname");
8288 452 : i_consrc = PQfnumber(res, "consrc");
8289 :
8290 452 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8291 :
8292 452 : tyinfo->nDomChecks = ntups;
8293 452 : tyinfo->domChecks = constrinfo;
8294 :
8295 760 : for (i = 0; i < ntups; i++)
8296 : {
8297 308 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
8298 :
8299 308 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
8300 308 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8301 308 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8302 308 : AssignDumpId(&constrinfo[i].dobj);
8303 308 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8304 308 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8305 308 : constrinfo[i].contable = NULL;
8306 308 : constrinfo[i].condomain = tyinfo;
8307 308 : constrinfo[i].contype = 'c';
8308 308 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8309 308 : constrinfo[i].confrelid = InvalidOid;
8310 308 : constrinfo[i].conindex = 0;
8311 308 : constrinfo[i].condeferrable = false;
8312 308 : constrinfo[i].condeferred = false;
8313 308 : constrinfo[i].conislocal = true;
8314 :
8315 308 : constrinfo[i].separate = !validated;
8316 :
8317 : /*
8318 : * Make the domain depend on the constraint, ensuring it won't be
8319 : * output till any constraint dependencies are OK. If the constraint
8320 : * has not been validated, it's going to be dumped after the domain
8321 : * anyway, so this doesn't matter.
8322 : */
8323 308 : if (validated)
8324 308 : addObjectDependency(&tyinfo->dobj,
8325 308 : constrinfo[i].dobj.dumpId);
8326 : }
8327 :
8328 452 : PQclear(res);
8329 :
8330 452 : destroyPQExpBuffer(query);
8331 452 : }
8332 :
8333 : /*
8334 : * getRules
8335 : * get basic information about every rule in the system
8336 : */
8337 : void
8338 468 : getRules(Archive *fout)
8339 : {
8340 : PGresult *res;
8341 : int ntups;
8342 : int i;
8343 468 : PQExpBuffer query = createPQExpBuffer();
8344 : RuleInfo *ruleinfo;
8345 : int i_tableoid;
8346 : int i_oid;
8347 : int i_rulename;
8348 : int i_ruletable;
8349 : int i_ev_type;
8350 : int i_is_instead;
8351 : int i_ev_enabled;
8352 :
8353 468 : appendPQExpBufferStr(query, "SELECT "
8354 : "tableoid, oid, rulename, "
8355 : "ev_class AS ruletable, ev_type, is_instead, "
8356 : "ev_enabled "
8357 : "FROM pg_rewrite "
8358 : "ORDER BY oid");
8359 :
8360 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8361 :
8362 468 : ntups = PQntuples(res);
8363 :
8364 468 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8365 :
8366 468 : i_tableoid = PQfnumber(res, "tableoid");
8367 468 : i_oid = PQfnumber(res, "oid");
8368 468 : i_rulename = PQfnumber(res, "rulename");
8369 468 : i_ruletable = PQfnumber(res, "ruletable");
8370 468 : i_ev_type = PQfnumber(res, "ev_type");
8371 468 : i_is_instead = PQfnumber(res, "is_instead");
8372 468 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8373 :
8374 72302 : for (i = 0; i < ntups; i++)
8375 : {
8376 : Oid ruletableoid;
8377 :
8378 71834 : ruleinfo[i].dobj.objType = DO_RULE;
8379 71834 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8380 71834 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8381 71834 : AssignDumpId(&ruleinfo[i].dobj);
8382 71834 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8383 71834 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8384 71834 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8385 71834 : if (ruleinfo[i].ruletable == NULL)
8386 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8387 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8388 71834 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8389 71834 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8390 71834 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8391 71834 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8392 71834 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8393 71834 : if (ruleinfo[i].ruletable)
8394 : {
8395 : /*
8396 : * If the table is a view or materialized view, force its ON
8397 : * SELECT rule to be sorted before the view itself --- this
8398 : * ensures that any dependencies for the rule affect the table's
8399 : * positioning. Other rules are forced to appear after their
8400 : * table.
8401 : */
8402 71834 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8403 1800 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8404 71174 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8405 : {
8406 70118 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8407 70118 : ruleinfo[i].dobj.dumpId);
8408 : /* We'll merge the rule into CREATE VIEW, if possible */
8409 70118 : ruleinfo[i].separate = false;
8410 : }
8411 : else
8412 : {
8413 1716 : addObjectDependency(&ruleinfo[i].dobj,
8414 1716 : ruleinfo[i].ruletable->dobj.dumpId);
8415 1716 : ruleinfo[i].separate = true;
8416 : }
8417 : }
8418 : else
8419 0 : ruleinfo[i].separate = true;
8420 : }
8421 :
8422 468 : PQclear(res);
8423 :
8424 468 : destroyPQExpBuffer(query);
8425 468 : }
8426 :
8427 : /*
8428 : * getTriggers
8429 : * get information about every trigger on a dumpable table
8430 : *
8431 : * Note: trigger data is not returned directly to the caller, but it
8432 : * does get entered into the DumpableObject tables.
8433 : */
8434 : void
8435 468 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8436 : {
8437 468 : PQExpBuffer query = createPQExpBuffer();
8438 468 : PQExpBuffer tbloids = createPQExpBuffer();
8439 : PGresult *res;
8440 : int ntups;
8441 : int curtblindx;
8442 : TriggerInfo *tginfo;
8443 : int i_tableoid,
8444 : i_oid,
8445 : i_tgrelid,
8446 : i_tgname,
8447 : i_tgenabled,
8448 : i_tgispartition,
8449 : i_tgdef;
8450 :
8451 : /*
8452 : * We want to perform just one query against pg_trigger. However, we
8453 : * mustn't try to select every row of the catalog and then sort it out on
8454 : * the client side, because some of the server-side functions we need
8455 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8456 : * build an array of the OIDs of tables we care about (and now have lock
8457 : * on!), and use a WHERE clause to constrain which rows are selected.
8458 : */
8459 468 : appendPQExpBufferChar(tbloids, '{');
8460 123712 : for (int i = 0; i < numTables; i++)
8461 : {
8462 123244 : TableInfo *tbinfo = &tblinfo[i];
8463 :
8464 123244 : if (!tbinfo->hastriggers ||
8465 2898 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8466 120870 : continue;
8467 :
8468 : /* OK, we need info for this table */
8469 2374 : if (tbloids->len > 1) /* do we have more than the '{'? */
8470 2258 : appendPQExpBufferChar(tbloids, ',');
8471 2374 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8472 : }
8473 468 : appendPQExpBufferChar(tbloids, '}');
8474 :
8475 468 : if (fout->remoteVersion >= 150000)
8476 : {
8477 : /*
8478 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8479 : * result in non-forward-compatible dumps of WHEN clauses due to
8480 : * under-parenthesization.
8481 : *
8482 : * NB: We need to see partition triggers in case the tgenabled flag
8483 : * has been changed from the parent.
8484 : */
8485 468 : appendPQExpBuffer(query,
8486 : "SELECT t.tgrelid, t.tgname, "
8487 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8488 : "t.tgenabled, t.tableoid, t.oid, "
8489 : "t.tgparentid <> 0 AS tgispartition\n"
8490 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8491 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8492 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8493 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8494 : "OR t.tgenabled != u.tgenabled) "
8495 : "ORDER BY t.tgrelid, t.tgname",
8496 : tbloids->data);
8497 : }
8498 0 : else if (fout->remoteVersion >= 130000)
8499 : {
8500 : /*
8501 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8502 : * result in non-forward-compatible dumps of WHEN clauses due to
8503 : * under-parenthesization.
8504 : *
8505 : * NB: We need to see tgisinternal triggers in partitions, in case the
8506 : * tgenabled flag has been changed from the parent.
8507 : */
8508 0 : appendPQExpBuffer(query,
8509 : "SELECT t.tgrelid, t.tgname, "
8510 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8511 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8512 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8513 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8514 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8515 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8516 : "ORDER BY t.tgrelid, t.tgname",
8517 : tbloids->data);
8518 : }
8519 0 : else if (fout->remoteVersion >= 110000)
8520 : {
8521 : /*
8522 : * NB: We need to see tgisinternal triggers in partitions, in case the
8523 : * tgenabled flag has been changed from the parent. No tgparentid in
8524 : * version 11-12, so we have to match them via pg_depend.
8525 : *
8526 : * See above about pretty=true in pg_get_triggerdef.
8527 : */
8528 0 : appendPQExpBuffer(query,
8529 : "SELECT t.tgrelid, t.tgname, "
8530 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8531 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8532 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8533 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8534 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8535 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8536 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8537 : " d.objid = t.oid "
8538 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8539 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8540 : "ORDER BY t.tgrelid, t.tgname",
8541 : tbloids->data);
8542 : }
8543 : else
8544 : {
8545 : /* See above about pretty=true in pg_get_triggerdef */
8546 0 : appendPQExpBuffer(query,
8547 : "SELECT t.tgrelid, t.tgname, "
8548 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8549 : "t.tgenabled, false as tgispartition, "
8550 : "t.tableoid, t.oid "
8551 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8552 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8553 : "WHERE NOT tgisinternal "
8554 : "ORDER BY t.tgrelid, t.tgname",
8555 : tbloids->data);
8556 : }
8557 :
8558 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8559 :
8560 468 : ntups = PQntuples(res);
8561 :
8562 468 : i_tableoid = PQfnumber(res, "tableoid");
8563 468 : i_oid = PQfnumber(res, "oid");
8564 468 : i_tgrelid = PQfnumber(res, "tgrelid");
8565 468 : i_tgname = PQfnumber(res, "tgname");
8566 468 : i_tgenabled = PQfnumber(res, "tgenabled");
8567 468 : i_tgispartition = PQfnumber(res, "tgispartition");
8568 468 : i_tgdef = PQfnumber(res, "tgdef");
8569 :
8570 468 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8571 :
8572 : /*
8573 : * Outer loop iterates once per table, not once per row. Incrementing of
8574 : * j is handled by the inner loop.
8575 : */
8576 468 : curtblindx = -1;
8577 1252 : for (int j = 0; j < ntups;)
8578 : {
8579 784 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8580 784 : TableInfo *tbinfo = NULL;
8581 : int numtrigs;
8582 :
8583 : /* Count rows for this table */
8584 1476 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8585 1360 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8586 668 : break;
8587 :
8588 : /*
8589 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8590 : * order.
8591 : */
8592 39620 : while (++curtblindx < numTables)
8593 : {
8594 39620 : tbinfo = &tblinfo[curtblindx];
8595 39620 : if (tbinfo->dobj.catId.oid == tgrelid)
8596 784 : break;
8597 : }
8598 784 : if (curtblindx >= numTables)
8599 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8600 :
8601 : /* Save data for this table */
8602 784 : tbinfo->triggers = tginfo + j;
8603 784 : tbinfo->numTriggers = numtrigs;
8604 :
8605 2260 : for (int c = 0; c < numtrigs; c++, j++)
8606 : {
8607 1476 : tginfo[j].dobj.objType = DO_TRIGGER;
8608 1476 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8609 1476 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8610 1476 : AssignDumpId(&tginfo[j].dobj);
8611 1476 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8612 1476 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8613 1476 : tginfo[j].tgtable = tbinfo;
8614 1476 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8615 1476 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8616 1476 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8617 : }
8618 : }
8619 :
8620 468 : PQclear(res);
8621 :
8622 468 : destroyPQExpBuffer(query);
8623 468 : destroyPQExpBuffer(tbloids);
8624 468 : }
8625 :
8626 : /*
8627 : * getEventTriggers
8628 : * get information about event triggers
8629 : */
8630 : void
8631 468 : getEventTriggers(Archive *fout)
8632 : {
8633 : int i;
8634 : PQExpBuffer query;
8635 : PGresult *res;
8636 : EventTriggerInfo *evtinfo;
8637 : int i_tableoid,
8638 : i_oid,
8639 : i_evtname,
8640 : i_evtevent,
8641 : i_evtowner,
8642 : i_evttags,
8643 : i_evtfname,
8644 : i_evtenabled;
8645 : int ntups;
8646 :
8647 : /* Before 9.3, there are no event triggers */
8648 468 : if (fout->remoteVersion < 90300)
8649 0 : return;
8650 :
8651 468 : query = createPQExpBuffer();
8652 :
8653 468 : appendPQExpBufferStr(query,
8654 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8655 : "evtevent, evtowner, "
8656 : "array_to_string(array("
8657 : "select quote_literal(x) "
8658 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8659 : "e.evtfoid::regproc as evtfname "
8660 : "FROM pg_event_trigger e "
8661 : "ORDER BY e.oid");
8662 :
8663 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8664 :
8665 468 : ntups = PQntuples(res);
8666 :
8667 468 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8668 :
8669 468 : i_tableoid = PQfnumber(res, "tableoid");
8670 468 : i_oid = PQfnumber(res, "oid");
8671 468 : i_evtname = PQfnumber(res, "evtname");
8672 468 : i_evtevent = PQfnumber(res, "evtevent");
8673 468 : i_evtowner = PQfnumber(res, "evtowner");
8674 468 : i_evttags = PQfnumber(res, "evttags");
8675 468 : i_evtfname = PQfnumber(res, "evtfname");
8676 468 : i_evtenabled = PQfnumber(res, "evtenabled");
8677 :
8678 586 : for (i = 0; i < ntups; i++)
8679 : {
8680 118 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8681 118 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8682 118 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8683 118 : AssignDumpId(&evtinfo[i].dobj);
8684 118 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8685 118 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8686 118 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8687 118 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8688 118 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8689 118 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8690 118 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8691 :
8692 : /* Decide whether we want to dump it */
8693 118 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8694 : }
8695 :
8696 468 : PQclear(res);
8697 :
8698 468 : destroyPQExpBuffer(query);
8699 : }
8700 :
8701 : /*
8702 : * getProcLangs
8703 : * get basic information about every procedural language in the system
8704 : *
8705 : * NB: this must run after getFuncs() because we assume we can do
8706 : * findFuncByOid().
8707 : */
8708 : void
8709 468 : getProcLangs(Archive *fout)
8710 : {
8711 : PGresult *res;
8712 : int ntups;
8713 : int i;
8714 468 : PQExpBuffer query = createPQExpBuffer();
8715 : ProcLangInfo *planginfo;
8716 : int i_tableoid;
8717 : int i_oid;
8718 : int i_lanname;
8719 : int i_lanpltrusted;
8720 : int i_lanplcallfoid;
8721 : int i_laninline;
8722 : int i_lanvalidator;
8723 : int i_lanacl;
8724 : int i_acldefault;
8725 : int i_lanowner;
8726 :
8727 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8728 : "lanname, lanpltrusted, lanplcallfoid, "
8729 : "laninline, lanvalidator, "
8730 : "lanacl, "
8731 : "acldefault('l', lanowner) AS acldefault, "
8732 : "lanowner "
8733 : "FROM pg_language "
8734 : "WHERE lanispl "
8735 : "ORDER BY oid");
8736 :
8737 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8738 :
8739 468 : ntups = PQntuples(res);
8740 :
8741 468 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8742 :
8743 468 : i_tableoid = PQfnumber(res, "tableoid");
8744 468 : i_oid = PQfnumber(res, "oid");
8745 468 : i_lanname = PQfnumber(res, "lanname");
8746 468 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8747 468 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8748 468 : i_laninline = PQfnumber(res, "laninline");
8749 468 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8750 468 : i_lanacl = PQfnumber(res, "lanacl");
8751 468 : i_acldefault = PQfnumber(res, "acldefault");
8752 468 : i_lanowner = PQfnumber(res, "lanowner");
8753 :
8754 1034 : for (i = 0; i < ntups; i++)
8755 : {
8756 566 : planginfo[i].dobj.objType = DO_PROCLANG;
8757 566 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8758 566 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8759 566 : AssignDumpId(&planginfo[i].dobj);
8760 :
8761 566 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8762 566 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8763 566 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8764 566 : planginfo[i].dacl.privtype = 0;
8765 566 : planginfo[i].dacl.initprivs = NULL;
8766 566 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8767 566 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8768 566 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8769 566 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8770 566 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8771 :
8772 : /* Decide whether we want to dump it */
8773 566 : selectDumpableProcLang(&(planginfo[i]), fout);
8774 :
8775 : /* Mark whether language has an ACL */
8776 566 : if (!PQgetisnull(res, i, i_lanacl))
8777 98 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8778 : }
8779 :
8780 468 : PQclear(res);
8781 :
8782 468 : destroyPQExpBuffer(query);
8783 468 : }
8784 :
8785 : /*
8786 : * getCasts
8787 : * get basic information about most casts in the system
8788 : *
8789 : * Skip casts from a range to its multirange, since we'll create those
8790 : * automatically.
8791 : */
8792 : void
8793 468 : getCasts(Archive *fout)
8794 : {
8795 : PGresult *res;
8796 : int ntups;
8797 : int i;
8798 468 : PQExpBuffer query = createPQExpBuffer();
8799 : CastInfo *castinfo;
8800 : int i_tableoid;
8801 : int i_oid;
8802 : int i_castsource;
8803 : int i_casttarget;
8804 : int i_castfunc;
8805 : int i_castcontext;
8806 : int i_castmethod;
8807 :
8808 468 : if (fout->remoteVersion >= 140000)
8809 : {
8810 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8811 : "castsource, casttarget, castfunc, castcontext, "
8812 : "castmethod "
8813 : "FROM pg_cast c "
8814 : "WHERE NOT EXISTS ( "
8815 : "SELECT 1 FROM pg_range r "
8816 : "WHERE c.castsource = r.rngtypid "
8817 : "AND c.casttarget = r.rngmultitypid "
8818 : ") "
8819 : "ORDER BY 3,4");
8820 : }
8821 : else
8822 : {
8823 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8824 : "castsource, casttarget, castfunc, castcontext, "
8825 : "castmethod "
8826 : "FROM pg_cast ORDER BY 3,4");
8827 : }
8828 :
8829 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8830 :
8831 468 : ntups = PQntuples(res);
8832 :
8833 468 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8834 :
8835 468 : i_tableoid = PQfnumber(res, "tableoid");
8836 468 : i_oid = PQfnumber(res, "oid");
8837 468 : i_castsource = PQfnumber(res, "castsource");
8838 468 : i_casttarget = PQfnumber(res, "casttarget");
8839 468 : i_castfunc = PQfnumber(res, "castfunc");
8840 468 : i_castcontext = PQfnumber(res, "castcontext");
8841 468 : i_castmethod = PQfnumber(res, "castmethod");
8842 :
8843 111134 : for (i = 0; i < ntups; i++)
8844 : {
8845 : PQExpBufferData namebuf;
8846 : TypeInfo *sTypeInfo;
8847 : TypeInfo *tTypeInfo;
8848 :
8849 110666 : castinfo[i].dobj.objType = DO_CAST;
8850 110666 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8851 110666 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8852 110666 : AssignDumpId(&castinfo[i].dobj);
8853 110666 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8854 110666 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8855 110666 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8856 110666 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8857 110666 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8858 :
8859 : /*
8860 : * Try to name cast as concatenation of typnames. This is only used
8861 : * for purposes of sorting. If we fail to find either type, the name
8862 : * will be an empty string.
8863 : */
8864 110666 : initPQExpBuffer(&namebuf);
8865 110666 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8866 110666 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8867 110666 : if (sTypeInfo && tTypeInfo)
8868 110666 : appendPQExpBuffer(&namebuf, "%s %s",
8869 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8870 110666 : castinfo[i].dobj.name = namebuf.data;
8871 :
8872 : /* Decide whether we want to dump it */
8873 110666 : selectDumpableCast(&(castinfo[i]), fout);
8874 : }
8875 :
8876 468 : PQclear(res);
8877 :
8878 468 : destroyPQExpBuffer(query);
8879 468 : }
8880 :
8881 : static char *
8882 204 : get_language_name(Archive *fout, Oid langid)
8883 : {
8884 : PQExpBuffer query;
8885 : PGresult *res;
8886 : char *lanname;
8887 :
8888 204 : query = createPQExpBuffer();
8889 204 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8890 204 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8891 204 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8892 204 : destroyPQExpBuffer(query);
8893 204 : PQclear(res);
8894 :
8895 204 : return lanname;
8896 : }
8897 :
8898 : /*
8899 : * getTransforms
8900 : * get basic information about every transform in the system
8901 : */
8902 : void
8903 468 : getTransforms(Archive *fout)
8904 : {
8905 : PGresult *res;
8906 : int ntups;
8907 : int i;
8908 : PQExpBuffer query;
8909 : TransformInfo *transforminfo;
8910 : int i_tableoid;
8911 : int i_oid;
8912 : int i_trftype;
8913 : int i_trflang;
8914 : int i_trffromsql;
8915 : int i_trftosql;
8916 :
8917 : /* Transforms didn't exist pre-9.5 */
8918 468 : if (fout->remoteVersion < 90500)
8919 0 : return;
8920 :
8921 468 : query = createPQExpBuffer();
8922 :
8923 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8924 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8925 : "FROM pg_transform "
8926 : "ORDER BY 3,4");
8927 :
8928 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8929 :
8930 468 : ntups = PQntuples(res);
8931 :
8932 468 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8933 :
8934 468 : i_tableoid = PQfnumber(res, "tableoid");
8935 468 : i_oid = PQfnumber(res, "oid");
8936 468 : i_trftype = PQfnumber(res, "trftype");
8937 468 : i_trflang = PQfnumber(res, "trflang");
8938 468 : i_trffromsql = PQfnumber(res, "trffromsql");
8939 468 : i_trftosql = PQfnumber(res, "trftosql");
8940 :
8941 586 : for (i = 0; i < ntups; i++)
8942 : {
8943 : PQExpBufferData namebuf;
8944 : TypeInfo *typeInfo;
8945 : char *lanname;
8946 :
8947 118 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8948 118 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8949 118 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8950 118 : AssignDumpId(&transforminfo[i].dobj);
8951 118 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8952 118 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8953 118 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8954 118 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8955 :
8956 : /*
8957 : * Try to name transform as concatenation of type and language name.
8958 : * This is only used for purposes of sorting. If we fail to find
8959 : * either, the name will be an empty string.
8960 : */
8961 118 : initPQExpBuffer(&namebuf);
8962 118 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8963 118 : lanname = get_language_name(fout, transforminfo[i].trflang);
8964 118 : if (typeInfo && lanname)
8965 118 : appendPQExpBuffer(&namebuf, "%s %s",
8966 : typeInfo->dobj.name, lanname);
8967 118 : transforminfo[i].dobj.name = namebuf.data;
8968 118 : free(lanname);
8969 :
8970 : /* Decide whether we want to dump it */
8971 118 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8972 : }
8973 :
8974 468 : PQclear(res);
8975 :
8976 468 : destroyPQExpBuffer(query);
8977 : }
8978 :
8979 : /*
8980 : * getTableAttrs -
8981 : * for each interesting table, read info about its attributes
8982 : * (names, types, default values, CHECK constraints, etc)
8983 : *
8984 : * modifies tblinfo
8985 : */
8986 : void
8987 468 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8988 : {
8989 468 : DumpOptions *dopt = fout->dopt;
8990 468 : PQExpBuffer q = createPQExpBuffer();
8991 468 : PQExpBuffer tbloids = createPQExpBuffer();
8992 468 : PQExpBuffer checkoids = createPQExpBuffer();
8993 468 : PQExpBuffer invalidnotnulloids = NULL;
8994 : PGresult *res;
8995 : int ntups;
8996 : int curtblindx;
8997 : int i_attrelid;
8998 : int i_attnum;
8999 : int i_attname;
9000 : int i_atttypname;
9001 : int i_attstattarget;
9002 : int i_attstorage;
9003 : int i_typstorage;
9004 : int i_attidentity;
9005 : int i_attgenerated;
9006 : int i_attisdropped;
9007 : int i_attlen;
9008 : int i_attalign;
9009 : int i_attislocal;
9010 : int i_notnull_name;
9011 : int i_notnull_comment;
9012 : int i_notnull_noinherit;
9013 : int i_notnull_islocal;
9014 : int i_notnull_invalidoid;
9015 : int i_attoptions;
9016 : int i_attcollation;
9017 : int i_attcompression;
9018 : int i_attfdwoptions;
9019 : int i_attmissingval;
9020 : int i_atthasdef;
9021 :
9022 : /*
9023 : * We want to perform just one query against pg_attribute, and then just
9024 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9025 : * (for CHECK constraints and for NOT NULL constraints). However, we
9026 : * mustn't try to select every row of those catalogs and then sort it out
9027 : * on the client side, because some of the server-side functions we need
9028 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9029 : * build an array of the OIDs of tables we care about (and now have lock
9030 : * on!), and use a WHERE clause to constrain which rows are selected.
9031 : */
9032 468 : appendPQExpBufferChar(tbloids, '{');
9033 468 : appendPQExpBufferChar(checkoids, '{');
9034 123712 : for (int i = 0; i < numTables; i++)
9035 : {
9036 123244 : TableInfo *tbinfo = &tblinfo[i];
9037 :
9038 : /* Don't bother to collect info for sequences */
9039 123244 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9040 1632 : continue;
9041 :
9042 : /* Don't bother with uninteresting tables, either */
9043 121612 : if (!tbinfo->interesting)
9044 103366 : continue;
9045 :
9046 : /* OK, we need info for this table */
9047 18246 : if (tbloids->len > 1) /* do we have more than the '{'? */
9048 17924 : appendPQExpBufferChar(tbloids, ',');
9049 18246 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9050 :
9051 18246 : if (tbinfo->ncheck > 0)
9052 : {
9053 : /* Also make a list of the ones with check constraints */
9054 1362 : if (checkoids->len > 1) /* do we have more than the '{'? */
9055 1210 : appendPQExpBufferChar(checkoids, ',');
9056 1362 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9057 : }
9058 : }
9059 468 : appendPQExpBufferChar(tbloids, '}');
9060 468 : appendPQExpBufferChar(checkoids, '}');
9061 :
9062 : /*
9063 : * Find all the user attributes and their types.
9064 : *
9065 : * Since we only want to dump COLLATE clauses for attributes whose
9066 : * collation is different from their type's default, we use a CASE here to
9067 : * suppress uninteresting attcollations cheaply.
9068 : */
9069 468 : appendPQExpBufferStr(q,
9070 : "SELECT\n"
9071 : "a.attrelid,\n"
9072 : "a.attnum,\n"
9073 : "a.attname,\n"
9074 : "a.attstattarget,\n"
9075 : "a.attstorage,\n"
9076 : "t.typstorage,\n"
9077 : "a.atthasdef,\n"
9078 : "a.attisdropped,\n"
9079 : "a.attlen,\n"
9080 : "a.attalign,\n"
9081 : "a.attislocal,\n"
9082 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9083 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9084 : "CASE WHEN a.attcollation <> t.typcollation "
9085 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9086 : "pg_catalog.array_to_string(ARRAY("
9087 : "SELECT pg_catalog.quote_ident(option_name) || "
9088 : "' ' || pg_catalog.quote_literal(option_value) "
9089 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9090 : "ORDER BY option_name"
9091 : "), E',\n ') AS attfdwoptions,\n");
9092 :
9093 : /*
9094 : * Find out any NOT NULL markings for each column. In 18 and up we read
9095 : * pg_constraint to obtain the constraint name, and for valid constraints
9096 : * also pg_description to obtain its comment. notnull_noinherit is set
9097 : * according to the NO INHERIT property. For versions prior to 18, we
9098 : * store an empty string as the name when a constraint is marked as
9099 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9100 : * without a name); also, such cases are never NO INHERIT.
9101 : *
9102 : * For invalid constraints, we need to store their OIDs for processing
9103 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9104 : * is invalid, and NULL otherwise. Their comments are handled not here
9105 : * but by collectComments, because they're their own dumpable object.
9106 : *
9107 : * We track in notnull_islocal whether the constraint was defined directly
9108 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9109 : * might modify this later; that routine is also in charge of determining
9110 : * the correct inhcount.
9111 : */
9112 468 : if (fout->remoteVersion >= 180000)
9113 468 : appendPQExpBufferStr(q,
9114 : "co.conname AS notnull_name,\n"
9115 : "CASE WHEN co.convalidated THEN pt.description"
9116 : " ELSE NULL END AS notnull_comment,\n"
9117 : "CASE WHEN NOT co.convalidated THEN co.oid "
9118 : "ELSE NULL END AS notnull_invalidoid,\n"
9119 : "co.connoinherit AS notnull_noinherit,\n"
9120 : "co.conislocal AS notnull_islocal,\n");
9121 : else
9122 0 : appendPQExpBufferStr(q,
9123 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9124 : "NULL AS notnull_comment,\n"
9125 : "NULL AS notnull_invalidoid,\n"
9126 : "false AS notnull_noinherit,\n"
9127 : "a.attislocal AS notnull_islocal,\n");
9128 :
9129 468 : if (fout->remoteVersion >= 140000)
9130 468 : appendPQExpBufferStr(q,
9131 : "a.attcompression AS attcompression,\n");
9132 : else
9133 0 : appendPQExpBufferStr(q,
9134 : "'' AS attcompression,\n");
9135 :
9136 468 : if (fout->remoteVersion >= 100000)
9137 468 : appendPQExpBufferStr(q,
9138 : "a.attidentity,\n");
9139 : else
9140 0 : appendPQExpBufferStr(q,
9141 : "'' AS attidentity,\n");
9142 :
9143 468 : if (fout->remoteVersion >= 110000)
9144 468 : appendPQExpBufferStr(q,
9145 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9146 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9147 : else
9148 0 : appendPQExpBufferStr(q,
9149 : "NULL AS attmissingval,\n");
9150 :
9151 468 : if (fout->remoteVersion >= 120000)
9152 468 : appendPQExpBufferStr(q,
9153 : "a.attgenerated\n");
9154 : else
9155 0 : appendPQExpBufferStr(q,
9156 : "'' AS attgenerated\n");
9157 :
9158 : /* need left join to pg_type to not fail on dropped columns ... */
9159 468 : appendPQExpBuffer(q,
9160 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9161 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9162 : "LEFT JOIN pg_catalog.pg_type t "
9163 : "ON (a.atttypid = t.oid)\n",
9164 : tbloids->data);
9165 :
9166 : /*
9167 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9168 : * entries and pg_description to get their comments.
9169 : */
9170 468 : if (fout->remoteVersion >= 180000)
9171 468 : appendPQExpBufferStr(q,
9172 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9173 : "(a.attrelid = co.conrelid\n"
9174 : " AND co.contype = 'n' AND "
9175 : "co.conkey = array[a.attnum])\n"
9176 : " LEFT JOIN pg_catalog.pg_description pt ON "
9177 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9178 :
9179 468 : appendPQExpBufferStr(q,
9180 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9181 : "ORDER BY a.attrelid, a.attnum");
9182 :
9183 468 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9184 :
9185 468 : ntups = PQntuples(res);
9186 :
9187 468 : i_attrelid = PQfnumber(res, "attrelid");
9188 468 : i_attnum = PQfnumber(res, "attnum");
9189 468 : i_attname = PQfnumber(res, "attname");
9190 468 : i_atttypname = PQfnumber(res, "atttypname");
9191 468 : i_attstattarget = PQfnumber(res, "attstattarget");
9192 468 : i_attstorage = PQfnumber(res, "attstorage");
9193 468 : i_typstorage = PQfnumber(res, "typstorage");
9194 468 : i_attidentity = PQfnumber(res, "attidentity");
9195 468 : i_attgenerated = PQfnumber(res, "attgenerated");
9196 468 : i_attisdropped = PQfnumber(res, "attisdropped");
9197 468 : i_attlen = PQfnumber(res, "attlen");
9198 468 : i_attalign = PQfnumber(res, "attalign");
9199 468 : i_attislocal = PQfnumber(res, "attislocal");
9200 468 : i_notnull_name = PQfnumber(res, "notnull_name");
9201 468 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9202 468 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9203 468 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9204 468 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9205 468 : i_attoptions = PQfnumber(res, "attoptions");
9206 468 : i_attcollation = PQfnumber(res, "attcollation");
9207 468 : i_attcompression = PQfnumber(res, "attcompression");
9208 468 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9209 468 : i_attmissingval = PQfnumber(res, "attmissingval");
9210 468 : i_atthasdef = PQfnumber(res, "atthasdef");
9211 :
9212 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9213 468 : resetPQExpBuffer(tbloids);
9214 468 : appendPQExpBufferChar(tbloids, '{');
9215 :
9216 : /*
9217 : * Outer loop iterates once per table, not once per row. Incrementing of
9218 : * r is handled by the inner loop.
9219 : */
9220 468 : curtblindx = -1;
9221 18388 : for (int r = 0; r < ntups;)
9222 : {
9223 17920 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9224 17920 : TableInfo *tbinfo = NULL;
9225 : int numatts;
9226 : bool hasdefaults;
9227 :
9228 : /* Count rows for this table */
9229 68092 : for (numatts = 1; numatts < ntups - r; numatts++)
9230 67776 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9231 17604 : break;
9232 :
9233 : /*
9234 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9235 : * order.
9236 : */
9237 86510 : while (++curtblindx < numTables)
9238 : {
9239 86510 : tbinfo = &tblinfo[curtblindx];
9240 86510 : if (tbinfo->dobj.catId.oid == attrelid)
9241 17920 : break;
9242 : }
9243 17920 : if (curtblindx >= numTables)
9244 0 : pg_fatal("unrecognized table OID %u", attrelid);
9245 : /* cross-check that we only got requested tables */
9246 17920 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9247 17920 : !tbinfo->interesting)
9248 0 : pg_fatal("unexpected column data for table \"%s\"",
9249 : tbinfo->dobj.name);
9250 :
9251 : /* Save data for this table */
9252 17920 : tbinfo->numatts = numatts;
9253 17920 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9254 17920 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9255 17920 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9256 17920 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9257 17920 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9258 17920 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9259 17920 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9260 17920 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9261 17920 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9262 17920 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9263 17920 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9264 17920 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9265 17920 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9266 17920 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9267 17920 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9268 17920 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9269 17920 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9270 17920 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9271 17920 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9272 17920 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9273 17920 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9274 17920 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9275 17920 : hasdefaults = false;
9276 :
9277 86012 : for (int j = 0; j < numatts; j++, r++)
9278 : {
9279 68092 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9280 0 : pg_fatal("invalid column numbering in table \"%s\"",
9281 : tbinfo->dobj.name);
9282 68092 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9283 68092 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9284 68092 : if (PQgetisnull(res, r, i_attstattarget))
9285 68004 : tbinfo->attstattarget[j] = -1;
9286 : else
9287 88 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9288 68092 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9289 68092 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9290 68092 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9291 68092 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9292 68092 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9293 68092 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9294 68092 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9295 68092 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9296 68092 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9297 :
9298 : /* Handle not-null constraint name and flags */
9299 68092 : determineNotNullFlags(fout, res, r,
9300 : tbinfo, j,
9301 : i_notnull_name,
9302 : i_notnull_comment,
9303 : i_notnull_invalidoid,
9304 : i_notnull_noinherit,
9305 : i_notnull_islocal,
9306 : &invalidnotnulloids);
9307 :
9308 68092 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9309 68092 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9310 68092 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9311 68092 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9312 68092 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9313 68092 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9314 68092 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9315 68092 : tbinfo->attrdefs[j] = NULL; /* fix below */
9316 68092 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9317 3420 : hasdefaults = true;
9318 : }
9319 :
9320 17920 : if (hasdefaults)
9321 : {
9322 : /* Collect OIDs of interesting tables that have defaults */
9323 2598 : if (tbloids->len > 1) /* do we have more than the '{'? */
9324 2448 : appendPQExpBufferChar(tbloids, ',');
9325 2598 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9326 : }
9327 : }
9328 :
9329 : /* If invalidnotnulloids has any data, finalize it */
9330 468 : if (invalidnotnulloids != NULL)
9331 100 : appendPQExpBufferChar(invalidnotnulloids, '}');
9332 :
9333 468 : PQclear(res);
9334 :
9335 : /*
9336 : * Now get info about column defaults. This is skipped for a data-only
9337 : * dump, as it is only needed for table schemas.
9338 : */
9339 468 : if (dopt->dumpSchema && tbloids->len > 1)
9340 : {
9341 : AttrDefInfo *attrdefs;
9342 : int numDefaults;
9343 134 : TableInfo *tbinfo = NULL;
9344 :
9345 134 : pg_log_info("finding table default expressions");
9346 :
9347 134 : appendPQExpBufferChar(tbloids, '}');
9348 :
9349 134 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9350 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9351 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9352 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9353 : "ORDER BY a.adrelid, a.adnum",
9354 : tbloids->data);
9355 :
9356 134 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9357 :
9358 134 : numDefaults = PQntuples(res);
9359 134 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9360 :
9361 134 : curtblindx = -1;
9362 3358 : for (int j = 0; j < numDefaults; j++)
9363 : {
9364 3224 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9365 3224 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9366 3224 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9367 3224 : int adnum = atoi(PQgetvalue(res, j, 3));
9368 3224 : char *adsrc = PQgetvalue(res, j, 4);
9369 :
9370 : /*
9371 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9372 : * OID order.
9373 : */
9374 3224 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9375 : {
9376 45548 : while (++curtblindx < numTables)
9377 : {
9378 45548 : tbinfo = &tblinfo[curtblindx];
9379 45548 : if (tbinfo->dobj.catId.oid == adrelid)
9380 2462 : break;
9381 : }
9382 2462 : if (curtblindx >= numTables)
9383 0 : pg_fatal("unrecognized table OID %u", adrelid);
9384 : }
9385 :
9386 3224 : if (adnum <= 0 || adnum > tbinfo->numatts)
9387 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9388 : adnum, tbinfo->dobj.name);
9389 :
9390 : /*
9391 : * dropped columns shouldn't have defaults, but just in case,
9392 : * ignore 'em
9393 : */
9394 3224 : if (tbinfo->attisdropped[adnum - 1])
9395 0 : continue;
9396 :
9397 3224 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9398 3224 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9399 3224 : attrdefs[j].dobj.catId.oid = adoid;
9400 3224 : AssignDumpId(&attrdefs[j].dobj);
9401 3224 : attrdefs[j].adtable = tbinfo;
9402 3224 : attrdefs[j].adnum = adnum;
9403 3224 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9404 :
9405 3224 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9406 3224 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9407 :
9408 3224 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9409 :
9410 : /*
9411 : * Figure out whether the default/generation expression should be
9412 : * dumped as part of the main CREATE TABLE (or similar) command or
9413 : * as a separate ALTER TABLE (or similar) command. The preference
9414 : * is to put it into the CREATE command, but in some cases that's
9415 : * not possible.
9416 : */
9417 3224 : if (tbinfo->attgenerated[adnum - 1])
9418 : {
9419 : /*
9420 : * Column generation expressions cannot be dumped separately,
9421 : * because there is no syntax for it. By setting separate to
9422 : * false here we prevent the "default" from being processed as
9423 : * its own dumpable object. Later, flagInhAttrs() will mark
9424 : * it as not to be dumped at all, if possible (that is, if it
9425 : * can be inherited from a parent).
9426 : */
9427 1856 : attrdefs[j].separate = false;
9428 : }
9429 1368 : else if (tbinfo->relkind == RELKIND_VIEW)
9430 : {
9431 : /*
9432 : * Defaults on a VIEW must always be dumped as separate ALTER
9433 : * TABLE commands.
9434 : */
9435 72 : attrdefs[j].separate = true;
9436 : }
9437 1296 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9438 : {
9439 : /* column will be suppressed, print default separately */
9440 14 : attrdefs[j].separate = true;
9441 : }
9442 : else
9443 : {
9444 1282 : attrdefs[j].separate = false;
9445 : }
9446 :
9447 3224 : if (!attrdefs[j].separate)
9448 : {
9449 : /*
9450 : * Mark the default as needing to appear before the table, so
9451 : * that any dependencies it has must be emitted before the
9452 : * CREATE TABLE. If this is not possible, we'll change to
9453 : * "separate" mode while sorting dependencies.
9454 : */
9455 3138 : addObjectDependency(&tbinfo->dobj,
9456 3138 : attrdefs[j].dobj.dumpId);
9457 : }
9458 :
9459 3224 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9460 : }
9461 :
9462 134 : PQclear(res);
9463 : }
9464 :
9465 : /*
9466 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9467 : * data-only dump, as it is only needed for table schemas.
9468 : */
9469 468 : if (dopt->dumpSchema && invalidnotnulloids)
9470 : {
9471 : ConstraintInfo *constrs;
9472 : int numConstrs;
9473 : int i_tableoid;
9474 : int i_oid;
9475 : int i_conrelid;
9476 : int i_conname;
9477 : int i_consrc;
9478 : int i_conislocal;
9479 :
9480 88 : pg_log_info("finding invalid not-null constraints");
9481 :
9482 88 : resetPQExpBuffer(q);
9483 88 : appendPQExpBuffer(q,
9484 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9485 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9486 : "conislocal, convalidated "
9487 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9488 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9489 : "ORDER BY c.conrelid, c.conname",
9490 88 : invalidnotnulloids->data);
9491 :
9492 88 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9493 :
9494 88 : numConstrs = PQntuples(res);
9495 88 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9496 :
9497 88 : i_tableoid = PQfnumber(res, "tableoid");
9498 88 : i_oid = PQfnumber(res, "oid");
9499 88 : i_conrelid = PQfnumber(res, "conrelid");
9500 88 : i_conname = PQfnumber(res, "conname");
9501 88 : i_consrc = PQfnumber(res, "consrc");
9502 88 : i_conislocal = PQfnumber(res, "conislocal");
9503 :
9504 : /* As above, this loop iterates once per table, not once per row */
9505 88 : curtblindx = -1;
9506 272 : for (int j = 0; j < numConstrs;)
9507 : {
9508 184 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9509 184 : TableInfo *tbinfo = NULL;
9510 : int numcons;
9511 :
9512 : /* Count rows for this table */
9513 184 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9514 96 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9515 96 : break;
9516 :
9517 : /*
9518 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9519 : * OID order.
9520 : */
9521 31750 : while (++curtblindx < numTables)
9522 : {
9523 31750 : tbinfo = &tblinfo[curtblindx];
9524 31750 : if (tbinfo->dobj.catId.oid == conrelid)
9525 184 : break;
9526 : }
9527 184 : if (curtblindx >= numTables)
9528 0 : pg_fatal("unrecognized table OID %u", conrelid);
9529 :
9530 368 : for (int c = 0; c < numcons; c++, j++)
9531 : {
9532 184 : constrs[j].dobj.objType = DO_CONSTRAINT;
9533 184 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9534 184 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9535 184 : AssignDumpId(&constrs[j].dobj);
9536 184 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9537 184 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9538 184 : constrs[j].contable = tbinfo;
9539 184 : constrs[j].condomain = NULL;
9540 184 : constrs[j].contype = 'n';
9541 184 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9542 184 : constrs[j].confrelid = InvalidOid;
9543 184 : constrs[j].conindex = 0;
9544 184 : constrs[j].condeferrable = false;
9545 184 : constrs[j].condeferred = false;
9546 184 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9547 :
9548 : /*
9549 : * All invalid not-null constraints must be dumped separately,
9550 : * because CREATE TABLE would not create them as invalid, and
9551 : * also because they must be created after potentially
9552 : * violating data has been loaded.
9553 : */
9554 184 : constrs[j].separate = true;
9555 :
9556 184 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9557 : }
9558 : }
9559 88 : PQclear(res);
9560 : }
9561 :
9562 : /*
9563 : * Get info about table CHECK constraints. This is skipped for a
9564 : * data-only dump, as it is only needed for table schemas.
9565 : */
9566 468 : if (dopt->dumpSchema && checkoids->len > 2)
9567 : {
9568 : ConstraintInfo *constrs;
9569 : int numConstrs;
9570 : int i_tableoid;
9571 : int i_oid;
9572 : int i_conrelid;
9573 : int i_conname;
9574 : int i_consrc;
9575 : int i_conislocal;
9576 : int i_convalidated;
9577 :
9578 136 : pg_log_info("finding table check constraints");
9579 :
9580 136 : resetPQExpBuffer(q);
9581 136 : appendPQExpBuffer(q,
9582 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9583 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9584 : "conislocal, convalidated "
9585 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9586 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9587 : "WHERE contype = 'c' "
9588 : "ORDER BY c.conrelid, c.conname",
9589 : checkoids->data);
9590 :
9591 136 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9592 :
9593 136 : numConstrs = PQntuples(res);
9594 136 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9595 :
9596 136 : i_tableoid = PQfnumber(res, "tableoid");
9597 136 : i_oid = PQfnumber(res, "oid");
9598 136 : i_conrelid = PQfnumber(res, "conrelid");
9599 136 : i_conname = PQfnumber(res, "conname");
9600 136 : i_consrc = PQfnumber(res, "consrc");
9601 136 : i_conislocal = PQfnumber(res, "conislocal");
9602 136 : i_convalidated = PQfnumber(res, "convalidated");
9603 :
9604 : /* As above, this loop iterates once per table, not once per row */
9605 136 : curtblindx = -1;
9606 1396 : for (int j = 0; j < numConstrs;)
9607 : {
9608 1260 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9609 1260 : TableInfo *tbinfo = NULL;
9610 : int numcons;
9611 :
9612 : /* Count rows for this table */
9613 1630 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9614 1494 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9615 1124 : break;
9616 :
9617 : /*
9618 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9619 : * OID order.
9620 : */
9621 43434 : while (++curtblindx < numTables)
9622 : {
9623 43434 : tbinfo = &tblinfo[curtblindx];
9624 43434 : if (tbinfo->dobj.catId.oid == conrelid)
9625 1260 : break;
9626 : }
9627 1260 : if (curtblindx >= numTables)
9628 0 : pg_fatal("unrecognized table OID %u", conrelid);
9629 :
9630 1260 : if (numcons != tbinfo->ncheck)
9631 : {
9632 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9633 : "expected %d check constraints on table \"%s\" but found %d",
9634 : tbinfo->ncheck),
9635 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9636 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9637 0 : exit_nicely(1);
9638 : }
9639 :
9640 1260 : tbinfo->checkexprs = constrs + j;
9641 :
9642 2890 : for (int c = 0; c < numcons; c++, j++)
9643 : {
9644 1630 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9645 :
9646 1630 : constrs[j].dobj.objType = DO_CONSTRAINT;
9647 1630 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9648 1630 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9649 1630 : AssignDumpId(&constrs[j].dobj);
9650 1630 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9651 1630 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9652 1630 : constrs[j].contable = tbinfo;
9653 1630 : constrs[j].condomain = NULL;
9654 1630 : constrs[j].contype = 'c';
9655 1630 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9656 1630 : constrs[j].confrelid = InvalidOid;
9657 1630 : constrs[j].conindex = 0;
9658 1630 : constrs[j].condeferrable = false;
9659 1630 : constrs[j].condeferred = false;
9660 1630 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9661 :
9662 : /*
9663 : * An unvalidated constraint needs to be dumped separately, so
9664 : * that potentially-violating existing data is loaded before
9665 : * the constraint.
9666 : */
9667 1630 : constrs[j].separate = !validated;
9668 :
9669 1630 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9670 :
9671 : /*
9672 : * Mark the constraint as needing to appear before the table
9673 : * --- this is so that any other dependencies of the
9674 : * constraint will be emitted before we try to create the
9675 : * table. If the constraint is to be dumped separately, it
9676 : * will be dumped after data is loaded anyway, so don't do it.
9677 : * (There's an automatic dependency in the opposite direction
9678 : * anyway, so don't need to add one manually here.)
9679 : */
9680 1630 : if (!constrs[j].separate)
9681 1422 : addObjectDependency(&tbinfo->dobj,
9682 1422 : constrs[j].dobj.dumpId);
9683 :
9684 : /*
9685 : * We will detect later whether the constraint must be split
9686 : * out from the table definition.
9687 : */
9688 : }
9689 : }
9690 :
9691 136 : PQclear(res);
9692 : }
9693 :
9694 468 : destroyPQExpBuffer(q);
9695 468 : destroyPQExpBuffer(tbloids);
9696 468 : destroyPQExpBuffer(checkoids);
9697 468 : }
9698 :
9699 : /*
9700 : * Based on the getTableAttrs query's row corresponding to one column, set
9701 : * the name and flags to handle a not-null constraint for that column in
9702 : * the tbinfo struct.
9703 : *
9704 : * Result row 'r' is for tbinfo's attribute 'j'.
9705 : *
9706 : * There are four possibilities:
9707 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9708 : * (the constraint name) remains NULL.
9709 : * 2) The column has a constraint with no name (this is the case when
9710 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9711 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9712 : * 3) The column has an invalid not-null constraint. This must be treated
9713 : * as a separate object (because it must be created after the table data
9714 : * is loaded). So we add its OID to invalidnotnulloids for processing
9715 : * elsewhere and do nothing further with it here. We distinguish this
9716 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9717 : * value, which is the constraint OID. Valid constraints have a null OID.
9718 : * 4) The column has a constraint with a known name; in that case
9719 : * notnull_constrs carries that name and dumpTableSchema will print
9720 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9721 : * (table_column_not_null) and there's no comment on the constraint,
9722 : * there's no need to print that name in the dump, so notnull_constrs
9723 : * is set to the empty string and it behaves as case 2.
9724 : *
9725 : * In a child table that inherits from a parent already containing NOT NULL
9726 : * constraints and the columns in the child don't have their own NOT NULL
9727 : * declarations, we suppress printing constraints in the child: the
9728 : * constraints are acquired at the point where the child is attached to the
9729 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9730 : * set not here but in flagInhAttrs. That flag is also used when the
9731 : * constraint was validated in a child but all its parent have it as NOT
9732 : * VALID.
9733 : *
9734 : * Any of these constraints might have the NO INHERIT bit. If so we set
9735 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9736 : *
9737 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9738 : * to do the right thing in all but the trivial case. However, the downside
9739 : * of getting it wrong is simply that the name is printed rather than
9740 : * suppressed, so it's not a big deal.
9741 : *
9742 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9743 : * constraints are found, it is initialized and filled with the array of
9744 : * OIDs of such constraints, for later processing.
9745 : */
9746 : static void
9747 68092 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9748 : TableInfo *tbinfo, int j,
9749 : int i_notnull_name,
9750 : int i_notnull_comment,
9751 : int i_notnull_invalidoid,
9752 : int i_notnull_noinherit,
9753 : int i_notnull_islocal,
9754 : PQExpBuffer *invalidnotnulloids)
9755 : {
9756 68092 : DumpOptions *dopt = fout->dopt;
9757 :
9758 : /*
9759 : * If this not-null constraint is not valid, list its OID in
9760 : * invalidnotnulloids and do nothing further. It'll be processed
9761 : * elsewhere later.
9762 : *
9763 : * Because invalid not-null constraints are rare, we don't want to malloc
9764 : * invalidnotnulloids until we're sure we're going it need it, which
9765 : * happens here.
9766 : */
9767 68092 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
9768 : {
9769 196 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9770 :
9771 196 : if (*invalidnotnulloids == NULL)
9772 : {
9773 100 : *invalidnotnulloids = createPQExpBuffer();
9774 100 : appendPQExpBufferChar(*invalidnotnulloids, '{');
9775 100 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
9776 : }
9777 : else
9778 96 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
9779 :
9780 : /*
9781 : * Track when a parent constraint is invalid for the cases where a
9782 : * child constraint has been validated independenly.
9783 : */
9784 196 : tbinfo->notnull_invalid[j] = true;
9785 :
9786 : /* nothing else to do */
9787 196 : tbinfo->notnull_constrs[j] = NULL;
9788 196 : return;
9789 : }
9790 :
9791 : /*
9792 : * notnull_noinh is straight from the query result. notnull_islocal also,
9793 : * though flagInhAttrs may change that one later.
9794 : */
9795 67896 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9796 67896 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9797 67896 : tbinfo->notnull_invalid[j] = false;
9798 :
9799 : /*
9800 : * Determine a constraint name to use. If the column is not marked not-
9801 : * null, we set NULL which cues ... to do nothing. An empty string says
9802 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9803 : * use.
9804 : */
9805 67896 : if (fout->remoteVersion < 180000)
9806 : {
9807 : /*
9808 : * < 18 doesn't have not-null names, so an unnamed constraint is
9809 : * sufficient.
9810 : */
9811 0 : if (PQgetisnull(res, r, i_notnull_name))
9812 0 : tbinfo->notnull_constrs[j] = NULL;
9813 : else
9814 0 : tbinfo->notnull_constrs[j] = "";
9815 : }
9816 : else
9817 : {
9818 67896 : if (PQgetisnull(res, r, i_notnull_name))
9819 60992 : tbinfo->notnull_constrs[j] = NULL;
9820 : else
9821 : {
9822 : /*
9823 : * In binary upgrade of inheritance child tables, must have a
9824 : * constraint name that we can UPDATE later; same if there's a
9825 : * comment on the constraint.
9826 : */
9827 6904 : if ((dopt->binary_upgrade &&
9828 580 : !tbinfo->ispartition &&
9829 7328 : !tbinfo->notnull_islocal) ||
9830 6904 : !PQgetisnull(res, r, i_notnull_comment))
9831 : {
9832 116 : tbinfo->notnull_constrs[j] =
9833 116 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9834 : }
9835 : else
9836 : {
9837 : char *default_name;
9838 :
9839 : /* XXX should match ChooseConstraintName better */
9840 6788 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9841 6788 : tbinfo->attnames[j]);
9842 6788 : if (strcmp(default_name,
9843 6788 : PQgetvalue(res, r, i_notnull_name)) == 0)
9844 4484 : tbinfo->notnull_constrs[j] = "";
9845 : else
9846 : {
9847 2304 : tbinfo->notnull_constrs[j] =
9848 2304 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9849 : }
9850 6788 : free(default_name);
9851 : }
9852 : }
9853 : }
9854 : }
9855 :
9856 : /*
9857 : * Test whether a column should be printed as part of table's CREATE TABLE.
9858 : * Column number is zero-based.
9859 : *
9860 : * Normally this is always true, but it's false for dropped columns, as well
9861 : * as those that were inherited without any local definition. (If we print
9862 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9863 : * For partitions, it's always true, because we want the partitions to be
9864 : * created independently and ATTACH PARTITION used afterwards.
9865 : *
9866 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9867 : * attisdropped state later, so as to keep control of the physical column
9868 : * order.
9869 : *
9870 : * This function exists because there are scattered nonobvious places that
9871 : * must be kept in sync with this decision.
9872 : */
9873 : bool
9874 117296 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9875 : {
9876 117296 : if (dopt->binary_upgrade)
9877 12324 : return true;
9878 104972 : if (tbinfo->attisdropped[colno])
9879 2132 : return false;
9880 102840 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9881 : }
9882 :
9883 :
9884 : /*
9885 : * getTSParsers:
9886 : * get information about all text search parsers in the system catalogs
9887 : */
9888 : void
9889 468 : getTSParsers(Archive *fout)
9890 : {
9891 : PGresult *res;
9892 : int ntups;
9893 : int i;
9894 : PQExpBuffer query;
9895 : TSParserInfo *prsinfo;
9896 : int i_tableoid;
9897 : int i_oid;
9898 : int i_prsname;
9899 : int i_prsnamespace;
9900 : int i_prsstart;
9901 : int i_prstoken;
9902 : int i_prsend;
9903 : int i_prsheadline;
9904 : int i_prslextype;
9905 :
9906 468 : query = createPQExpBuffer();
9907 :
9908 : /*
9909 : * find all text search objects, including builtin ones; we filter out
9910 : * system-defined objects at dump-out time.
9911 : */
9912 :
9913 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9914 : "prsstart::oid, prstoken::oid, "
9915 : "prsend::oid, prsheadline::oid, prslextype::oid "
9916 : "FROM pg_ts_parser");
9917 :
9918 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9919 :
9920 468 : ntups = PQntuples(res);
9921 :
9922 468 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9923 :
9924 468 : i_tableoid = PQfnumber(res, "tableoid");
9925 468 : i_oid = PQfnumber(res, "oid");
9926 468 : i_prsname = PQfnumber(res, "prsname");
9927 468 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9928 468 : i_prsstart = PQfnumber(res, "prsstart");
9929 468 : i_prstoken = PQfnumber(res, "prstoken");
9930 468 : i_prsend = PQfnumber(res, "prsend");
9931 468 : i_prsheadline = PQfnumber(res, "prsheadline");
9932 468 : i_prslextype = PQfnumber(res, "prslextype");
9933 :
9934 1034 : for (i = 0; i < ntups; i++)
9935 : {
9936 566 : prsinfo[i].dobj.objType = DO_TSPARSER;
9937 566 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9938 566 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9939 566 : AssignDumpId(&prsinfo[i].dobj);
9940 566 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9941 1132 : prsinfo[i].dobj.namespace =
9942 566 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9943 566 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9944 566 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9945 566 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9946 566 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9947 566 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9948 :
9949 : /* Decide whether we want to dump it */
9950 566 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9951 : }
9952 :
9953 468 : PQclear(res);
9954 :
9955 468 : destroyPQExpBuffer(query);
9956 468 : }
9957 :
9958 : /*
9959 : * getTSDictionaries:
9960 : * get information about all text search dictionaries in the system catalogs
9961 : */
9962 : void
9963 468 : getTSDictionaries(Archive *fout)
9964 : {
9965 : PGresult *res;
9966 : int ntups;
9967 : int i;
9968 : PQExpBuffer query;
9969 : TSDictInfo *dictinfo;
9970 : int i_tableoid;
9971 : int i_oid;
9972 : int i_dictname;
9973 : int i_dictnamespace;
9974 : int i_dictowner;
9975 : int i_dicttemplate;
9976 : int i_dictinitoption;
9977 :
9978 468 : query = createPQExpBuffer();
9979 :
9980 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9981 : "dictnamespace, dictowner, "
9982 : "dicttemplate, dictinitoption "
9983 : "FROM pg_ts_dict");
9984 :
9985 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9986 :
9987 468 : ntups = PQntuples(res);
9988 :
9989 468 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9990 :
9991 468 : i_tableoid = PQfnumber(res, "tableoid");
9992 468 : i_oid = PQfnumber(res, "oid");
9993 468 : i_dictname = PQfnumber(res, "dictname");
9994 468 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9995 468 : i_dictowner = PQfnumber(res, "dictowner");
9996 468 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9997 468 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9998 :
9999 14786 : for (i = 0; i < ntups; i++)
10000 : {
10001 14318 : dictinfo[i].dobj.objType = DO_TSDICT;
10002 14318 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10003 14318 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10004 14318 : AssignDumpId(&dictinfo[i].dobj);
10005 14318 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10006 28636 : dictinfo[i].dobj.namespace =
10007 14318 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10008 14318 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10009 14318 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10010 14318 : if (PQgetisnull(res, i, i_dictinitoption))
10011 566 : dictinfo[i].dictinitoption = NULL;
10012 : else
10013 13752 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10014 :
10015 : /* Decide whether we want to dump it */
10016 14318 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10017 : }
10018 :
10019 468 : PQclear(res);
10020 :
10021 468 : destroyPQExpBuffer(query);
10022 468 : }
10023 :
10024 : /*
10025 : * getTSTemplates:
10026 : * get information about all text search templates in the system catalogs
10027 : */
10028 : void
10029 468 : getTSTemplates(Archive *fout)
10030 : {
10031 : PGresult *res;
10032 : int ntups;
10033 : int i;
10034 : PQExpBuffer query;
10035 : TSTemplateInfo *tmplinfo;
10036 : int i_tableoid;
10037 : int i_oid;
10038 : int i_tmplname;
10039 : int i_tmplnamespace;
10040 : int i_tmplinit;
10041 : int i_tmpllexize;
10042 :
10043 468 : query = createPQExpBuffer();
10044 :
10045 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10046 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10047 : "FROM pg_ts_template");
10048 :
10049 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10050 :
10051 468 : ntups = PQntuples(res);
10052 :
10053 468 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10054 :
10055 468 : i_tableoid = PQfnumber(res, "tableoid");
10056 468 : i_oid = PQfnumber(res, "oid");
10057 468 : i_tmplname = PQfnumber(res, "tmplname");
10058 468 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10059 468 : i_tmplinit = PQfnumber(res, "tmplinit");
10060 468 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10061 :
10062 2906 : for (i = 0; i < ntups; i++)
10063 : {
10064 2438 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10065 2438 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10066 2438 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10067 2438 : AssignDumpId(&tmplinfo[i].dobj);
10068 2438 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10069 4876 : tmplinfo[i].dobj.namespace =
10070 2438 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10071 2438 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10072 2438 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10073 :
10074 : /* Decide whether we want to dump it */
10075 2438 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10076 : }
10077 :
10078 468 : PQclear(res);
10079 :
10080 468 : destroyPQExpBuffer(query);
10081 468 : }
10082 :
10083 : /*
10084 : * getTSConfigurations:
10085 : * get information about all text search configurations
10086 : */
10087 : void
10088 468 : getTSConfigurations(Archive *fout)
10089 : {
10090 : PGresult *res;
10091 : int ntups;
10092 : int i;
10093 : PQExpBuffer query;
10094 : TSConfigInfo *cfginfo;
10095 : int i_tableoid;
10096 : int i_oid;
10097 : int i_cfgname;
10098 : int i_cfgnamespace;
10099 : int i_cfgowner;
10100 : int i_cfgparser;
10101 :
10102 468 : query = createPQExpBuffer();
10103 :
10104 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10105 : "cfgnamespace, cfgowner, cfgparser "
10106 : "FROM pg_ts_config");
10107 :
10108 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10109 :
10110 468 : ntups = PQntuples(res);
10111 :
10112 468 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10113 :
10114 468 : i_tableoid = PQfnumber(res, "tableoid");
10115 468 : i_oid = PQfnumber(res, "oid");
10116 468 : i_cfgname = PQfnumber(res, "cfgname");
10117 468 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10118 468 : i_cfgowner = PQfnumber(res, "cfgowner");
10119 468 : i_cfgparser = PQfnumber(res, "cfgparser");
10120 :
10121 14686 : for (i = 0; i < ntups; i++)
10122 : {
10123 14218 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10124 14218 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10125 14218 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10126 14218 : AssignDumpId(&cfginfo[i].dobj);
10127 14218 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10128 28436 : cfginfo[i].dobj.namespace =
10129 14218 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10130 14218 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10131 14218 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10132 :
10133 : /* Decide whether we want to dump it */
10134 14218 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10135 : }
10136 :
10137 468 : PQclear(res);
10138 :
10139 468 : destroyPQExpBuffer(query);
10140 468 : }
10141 :
10142 : /*
10143 : * getForeignDataWrappers:
10144 : * get information about all foreign-data wrappers in the system catalogs
10145 : */
10146 : void
10147 468 : getForeignDataWrappers(Archive *fout)
10148 : {
10149 : PGresult *res;
10150 : int ntups;
10151 : int i;
10152 : PQExpBuffer query;
10153 : FdwInfo *fdwinfo;
10154 : int i_tableoid;
10155 : int i_oid;
10156 : int i_fdwname;
10157 : int i_fdwowner;
10158 : int i_fdwhandler;
10159 : int i_fdwvalidator;
10160 : int i_fdwacl;
10161 : int i_acldefault;
10162 : int i_fdwoptions;
10163 :
10164 468 : query = createPQExpBuffer();
10165 :
10166 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10167 : "fdwowner, "
10168 : "fdwhandler::pg_catalog.regproc, "
10169 : "fdwvalidator::pg_catalog.regproc, "
10170 : "fdwacl, "
10171 : "acldefault('F', fdwowner) AS acldefault, "
10172 : "array_to_string(ARRAY("
10173 : "SELECT quote_ident(option_name) || ' ' || "
10174 : "quote_literal(option_value) "
10175 : "FROM pg_options_to_table(fdwoptions) "
10176 : "ORDER BY option_name"
10177 : "), E',\n ') AS fdwoptions "
10178 : "FROM pg_foreign_data_wrapper");
10179 :
10180 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10181 :
10182 468 : ntups = PQntuples(res);
10183 :
10184 468 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10185 :
10186 468 : i_tableoid = PQfnumber(res, "tableoid");
10187 468 : i_oid = PQfnumber(res, "oid");
10188 468 : i_fdwname = PQfnumber(res, "fdwname");
10189 468 : i_fdwowner = PQfnumber(res, "fdwowner");
10190 468 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10191 468 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10192 468 : i_fdwacl = PQfnumber(res, "fdwacl");
10193 468 : i_acldefault = PQfnumber(res, "acldefault");
10194 468 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10195 :
10196 624 : for (i = 0; i < ntups; i++)
10197 : {
10198 156 : fdwinfo[i].dobj.objType = DO_FDW;
10199 156 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10200 156 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10201 156 : AssignDumpId(&fdwinfo[i].dobj);
10202 156 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10203 156 : fdwinfo[i].dobj.namespace = NULL;
10204 156 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10205 156 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10206 156 : fdwinfo[i].dacl.privtype = 0;
10207 156 : fdwinfo[i].dacl.initprivs = NULL;
10208 156 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10209 156 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10210 156 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10211 156 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10212 :
10213 : /* Decide whether we want to dump it */
10214 156 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10215 :
10216 : /* Mark whether FDW has an ACL */
10217 156 : if (!PQgetisnull(res, i, i_fdwacl))
10218 98 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10219 : }
10220 :
10221 468 : PQclear(res);
10222 :
10223 468 : destroyPQExpBuffer(query);
10224 468 : }
10225 :
10226 : /*
10227 : * getForeignServers:
10228 : * get information about all foreign servers in the system catalogs
10229 : */
10230 : void
10231 468 : getForeignServers(Archive *fout)
10232 : {
10233 : PGresult *res;
10234 : int ntups;
10235 : int i;
10236 : PQExpBuffer query;
10237 : ForeignServerInfo *srvinfo;
10238 : int i_tableoid;
10239 : int i_oid;
10240 : int i_srvname;
10241 : int i_srvowner;
10242 : int i_srvfdw;
10243 : int i_srvtype;
10244 : int i_srvversion;
10245 : int i_srvacl;
10246 : int i_acldefault;
10247 : int i_srvoptions;
10248 :
10249 468 : query = createPQExpBuffer();
10250 :
10251 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10252 : "srvowner, "
10253 : "srvfdw, srvtype, srvversion, srvacl, "
10254 : "acldefault('S', srvowner) AS acldefault, "
10255 : "array_to_string(ARRAY("
10256 : "SELECT quote_ident(option_name) || ' ' || "
10257 : "quote_literal(option_value) "
10258 : "FROM pg_options_to_table(srvoptions) "
10259 : "ORDER BY option_name"
10260 : "), E',\n ') AS srvoptions "
10261 : "FROM pg_foreign_server");
10262 :
10263 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10264 :
10265 468 : ntups = PQntuples(res);
10266 :
10267 468 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10268 :
10269 468 : i_tableoid = PQfnumber(res, "tableoid");
10270 468 : i_oid = PQfnumber(res, "oid");
10271 468 : i_srvname = PQfnumber(res, "srvname");
10272 468 : i_srvowner = PQfnumber(res, "srvowner");
10273 468 : i_srvfdw = PQfnumber(res, "srvfdw");
10274 468 : i_srvtype = PQfnumber(res, "srvtype");
10275 468 : i_srvversion = PQfnumber(res, "srvversion");
10276 468 : i_srvacl = PQfnumber(res, "srvacl");
10277 468 : i_acldefault = PQfnumber(res, "acldefault");
10278 468 : i_srvoptions = PQfnumber(res, "srvoptions");
10279 :
10280 632 : for (i = 0; i < ntups; i++)
10281 : {
10282 164 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10283 164 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10284 164 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10285 164 : AssignDumpId(&srvinfo[i].dobj);
10286 164 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10287 164 : srvinfo[i].dobj.namespace = NULL;
10288 164 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10289 164 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10290 164 : srvinfo[i].dacl.privtype = 0;
10291 164 : srvinfo[i].dacl.initprivs = NULL;
10292 164 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10293 164 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10294 164 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10295 164 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10296 164 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10297 :
10298 : /* Decide whether we want to dump it */
10299 164 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10300 :
10301 : /* Servers have user mappings */
10302 164 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10303 :
10304 : /* Mark whether server has an ACL */
10305 164 : if (!PQgetisnull(res, i, i_srvacl))
10306 98 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10307 : }
10308 :
10309 468 : PQclear(res);
10310 :
10311 468 : destroyPQExpBuffer(query);
10312 468 : }
10313 :
10314 : /*
10315 : * getDefaultACLs:
10316 : * get information about all default ACL information in the system catalogs
10317 : */
10318 : void
10319 468 : getDefaultACLs(Archive *fout)
10320 : {
10321 468 : DumpOptions *dopt = fout->dopt;
10322 : DefaultACLInfo *daclinfo;
10323 : PQExpBuffer query;
10324 : PGresult *res;
10325 : int i_oid;
10326 : int i_tableoid;
10327 : int i_defaclrole;
10328 : int i_defaclnamespace;
10329 : int i_defaclobjtype;
10330 : int i_defaclacl;
10331 : int i_acldefault;
10332 : int i,
10333 : ntups;
10334 :
10335 468 : query = createPQExpBuffer();
10336 :
10337 : /*
10338 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10339 : * ACL for their object type. We should dump them as deltas from the
10340 : * default ACL, since that will be used as a starting point for
10341 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10342 : * non-global entries can only add privileges not revoke them. We must
10343 : * dump those as-is (i.e., as deltas from an empty ACL).
10344 : *
10345 : * We can use defaclobjtype as the object type for acldefault(), except
10346 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10347 : * 's'.
10348 : */
10349 468 : appendPQExpBufferStr(query,
10350 : "SELECT oid, tableoid, "
10351 : "defaclrole, "
10352 : "defaclnamespace, "
10353 : "defaclobjtype, "
10354 : "defaclacl, "
10355 : "CASE WHEN defaclnamespace = 0 THEN "
10356 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10357 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10358 : "defaclrole) ELSE '{}' END AS acldefault "
10359 : "FROM pg_default_acl");
10360 :
10361 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10362 :
10363 468 : ntups = PQntuples(res);
10364 :
10365 468 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10366 :
10367 468 : i_oid = PQfnumber(res, "oid");
10368 468 : i_tableoid = PQfnumber(res, "tableoid");
10369 468 : i_defaclrole = PQfnumber(res, "defaclrole");
10370 468 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10371 468 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10372 468 : i_defaclacl = PQfnumber(res, "defaclacl");
10373 468 : i_acldefault = PQfnumber(res, "acldefault");
10374 :
10375 860 : for (i = 0; i < ntups; i++)
10376 : {
10377 392 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10378 :
10379 392 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10380 392 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10381 392 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10382 392 : AssignDumpId(&daclinfo[i].dobj);
10383 : /* cheesy ... is it worth coming up with a better object name? */
10384 392 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10385 :
10386 392 : if (nspid != InvalidOid)
10387 196 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10388 : else
10389 196 : daclinfo[i].dobj.namespace = NULL;
10390 :
10391 392 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10392 392 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10393 392 : daclinfo[i].dacl.privtype = 0;
10394 392 : daclinfo[i].dacl.initprivs = NULL;
10395 392 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10396 392 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10397 :
10398 : /* Default ACLs are ACLs, of course */
10399 392 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10400 :
10401 : /* Decide whether we want to dump it */
10402 392 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10403 : }
10404 :
10405 468 : PQclear(res);
10406 :
10407 468 : destroyPQExpBuffer(query);
10408 468 : }
10409 :
10410 : /*
10411 : * getRoleName -- look up the name of a role, given its OID
10412 : *
10413 : * In current usage, we don't expect failures, so error out for a bad OID.
10414 : */
10415 : static const char *
10416 1479546 : getRoleName(const char *roleoid_str)
10417 : {
10418 1479546 : Oid roleoid = atooid(roleoid_str);
10419 :
10420 : /*
10421 : * Do binary search to find the appropriate item.
10422 : */
10423 1479546 : if (nrolenames > 0)
10424 : {
10425 1479546 : RoleNameItem *low = &rolenames[0];
10426 1479546 : RoleNameItem *high = &rolenames[nrolenames - 1];
10427 :
10428 5918336 : while (low <= high)
10429 : {
10430 5918336 : RoleNameItem *middle = low + (high - low) / 2;
10431 :
10432 5918336 : if (roleoid < middle->roleoid)
10433 4436140 : high = middle - 1;
10434 1482196 : else if (roleoid > middle->roleoid)
10435 2650 : low = middle + 1;
10436 : else
10437 1479546 : return middle->rolename; /* found a match */
10438 : }
10439 : }
10440 :
10441 0 : pg_fatal("role with OID %u does not exist", roleoid);
10442 : return NULL; /* keep compiler quiet */
10443 : }
10444 :
10445 : /*
10446 : * collectRoleNames --
10447 : *
10448 : * Construct a table of all known roles.
10449 : * The table is sorted by OID for speed in lookup.
10450 : */
10451 : static void
10452 470 : collectRoleNames(Archive *fout)
10453 : {
10454 : PGresult *res;
10455 : const char *query;
10456 : int i;
10457 :
10458 470 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10459 :
10460 470 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10461 :
10462 470 : nrolenames = PQntuples(res);
10463 :
10464 470 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10465 :
10466 10180 : for (i = 0; i < nrolenames; i++)
10467 : {
10468 9710 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10469 9710 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10470 : }
10471 :
10472 470 : PQclear(res);
10473 470 : }
10474 :
10475 : /*
10476 : * getAdditionalACLs
10477 : *
10478 : * We have now created all the DumpableObjects, and collected the ACL data
10479 : * that appears in the directly-associated catalog entries. However, there's
10480 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10481 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10482 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10483 : * Also, in versions having the pg_init_privs catalog, read that and load the
10484 : * information into the relevant DumpableObjects.
10485 : */
10486 : static void
10487 464 : getAdditionalACLs(Archive *fout)
10488 : {
10489 464 : PQExpBuffer query = createPQExpBuffer();
10490 : PGresult *res;
10491 : int ntups,
10492 : i;
10493 :
10494 : /* Check for per-column ACLs */
10495 464 : appendPQExpBufferStr(query,
10496 : "SELECT DISTINCT attrelid FROM pg_attribute "
10497 : "WHERE attacl IS NOT NULL");
10498 :
10499 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10500 :
10501 464 : ntups = PQntuples(res);
10502 1304 : for (i = 0; i < ntups; i++)
10503 : {
10504 840 : Oid relid = atooid(PQgetvalue(res, i, 0));
10505 : TableInfo *tblinfo;
10506 :
10507 840 : tblinfo = findTableByOid(relid);
10508 : /* OK to ignore tables we haven't got a DumpableObject for */
10509 840 : if (tblinfo)
10510 : {
10511 840 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10512 840 : tblinfo->hascolumnACLs = true;
10513 : }
10514 : }
10515 464 : PQclear(res);
10516 :
10517 : /* Fetch initial-privileges data */
10518 464 : if (fout->remoteVersion >= 90600)
10519 : {
10520 464 : printfPQExpBuffer(query,
10521 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10522 : "FROM pg_init_privs");
10523 :
10524 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10525 :
10526 464 : ntups = PQntuples(res);
10527 107336 : for (i = 0; i < ntups; i++)
10528 : {
10529 106872 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10530 106872 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10531 106872 : int objsubid = atoi(PQgetvalue(res, i, 2));
10532 106872 : char privtype = *(PQgetvalue(res, i, 3));
10533 106872 : char *initprivs = PQgetvalue(res, i, 4);
10534 : CatalogId objId;
10535 : DumpableObject *dobj;
10536 :
10537 106872 : objId.tableoid = classoid;
10538 106872 : objId.oid = objoid;
10539 106872 : dobj = findObjectByCatalogId(objId);
10540 : /* OK to ignore entries we haven't got a DumpableObject for */
10541 106872 : if (dobj)
10542 : {
10543 : /* Cope with sub-object initprivs */
10544 76344 : if (objsubid != 0)
10545 : {
10546 7936 : if (dobj->objType == DO_TABLE)
10547 : {
10548 : /* For a column initprivs, set the table's ACL flags */
10549 7936 : dobj->components |= DUMP_COMPONENT_ACL;
10550 7936 : ((TableInfo *) dobj)->hascolumnACLs = true;
10551 : }
10552 : else
10553 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10554 : classoid, objoid, objsubid);
10555 8392 : continue;
10556 : }
10557 :
10558 : /*
10559 : * We ignore any pg_init_privs.initprivs entry for the public
10560 : * schema, as explained in getNamespaces().
10561 : */
10562 68408 : if (dobj->objType == DO_NAMESPACE &&
10563 920 : strcmp(dobj->name, "public") == 0)
10564 456 : continue;
10565 :
10566 : /* Else it had better be of a type we think has ACLs */
10567 67952 : if (dobj->objType == DO_NAMESPACE ||
10568 67488 : dobj->objType == DO_TYPE ||
10569 67440 : dobj->objType == DO_FUNC ||
10570 67248 : dobj->objType == DO_AGG ||
10571 67200 : dobj->objType == DO_TABLE ||
10572 0 : dobj->objType == DO_PROCLANG ||
10573 0 : dobj->objType == DO_FDW ||
10574 0 : dobj->objType == DO_FOREIGN_SERVER)
10575 67952 : {
10576 67952 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10577 :
10578 67952 : daobj->dacl.privtype = privtype;
10579 67952 : daobj->dacl.initprivs = pstrdup(initprivs);
10580 : }
10581 : else
10582 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10583 : classoid, objoid, objsubid);
10584 : }
10585 : }
10586 464 : PQclear(res);
10587 : }
10588 :
10589 464 : destroyPQExpBuffer(query);
10590 464 : }
10591 :
10592 : /*
10593 : * dumpCommentExtended --
10594 : *
10595 : * This routine is used to dump any comments associated with the
10596 : * object handed to this routine. The routine takes the object type
10597 : * and object name (ready to print, except for schema decoration), plus
10598 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10599 : * plus catalog ID and subid which are the lookup key for pg_description,
10600 : * plus the dump ID for the object (for setting a dependency).
10601 : * If a matching pg_description entry is found, it is dumped.
10602 : *
10603 : * Note: in some cases, such as comments for triggers and rules, the "type"
10604 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10605 : * but it doesn't seem worth complicating the API for all callers to make
10606 : * it cleaner.
10607 : *
10608 : * Note: although this routine takes a dumpId for dependency purposes,
10609 : * that purpose is just to mark the dependency in the emitted dump file
10610 : * for possible future use by pg_restore. We do NOT use it for determining
10611 : * ordering of the comment in the dump file, because this routine is called
10612 : * after dependency sorting occurs. This routine should be called just after
10613 : * calling ArchiveEntry() for the specified object.
10614 : */
10615 : static void
10616 13094 : dumpCommentExtended(Archive *fout, const char *type,
10617 : const char *name, const char *namespace,
10618 : const char *owner, CatalogId catalogId,
10619 : int subid, DumpId dumpId,
10620 : const char *initdb_comment)
10621 : {
10622 13094 : DumpOptions *dopt = fout->dopt;
10623 : CommentItem *comments;
10624 : int ncomments;
10625 :
10626 : /* do nothing, if --no-comments is supplied */
10627 13094 : if (dopt->no_comments)
10628 0 : return;
10629 :
10630 : /* Comments are schema not data ... except LO comments are data */
10631 13094 : if (strcmp(type, "LARGE OBJECT") != 0)
10632 : {
10633 12972 : if (!dopt->dumpSchema)
10634 0 : return;
10635 : }
10636 : else
10637 : {
10638 : /* We do dump LO comments in binary-upgrade mode */
10639 122 : if (!dopt->dumpData && !dopt->binary_upgrade)
10640 0 : return;
10641 : }
10642 :
10643 : /* Search for comments associated with catalogId, using table */
10644 13094 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10645 : &comments);
10646 :
10647 : /* Is there one matching the subid? */
10648 13094 : while (ncomments > 0)
10649 : {
10650 12998 : if (comments->objsubid == subid)
10651 12998 : break;
10652 0 : comments++;
10653 0 : ncomments--;
10654 : }
10655 :
10656 13094 : if (initdb_comment != NULL)
10657 : {
10658 : static CommentItem empty_comment = {.descr = ""};
10659 :
10660 : /*
10661 : * initdb creates this object with a comment. Skip dumping the
10662 : * initdb-provided comment, which would complicate matters for
10663 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10664 : * comment, replicate that.
10665 : */
10666 340 : if (ncomments == 0)
10667 : {
10668 8 : comments = &empty_comment;
10669 8 : ncomments = 1;
10670 : }
10671 332 : else if (strcmp(comments->descr, initdb_comment) == 0)
10672 332 : ncomments = 0;
10673 : }
10674 :
10675 : /* If a comment exists, build COMMENT ON statement */
10676 13094 : if (ncomments > 0)
10677 : {
10678 12674 : PQExpBuffer query = createPQExpBuffer();
10679 12674 : PQExpBuffer tag = createPQExpBuffer();
10680 :
10681 12674 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10682 12674 : if (namespace && *namespace)
10683 12316 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10684 12674 : appendPQExpBuffer(query, "%s IS ", name);
10685 12674 : appendStringLiteralAH(query, comments->descr, fout);
10686 12674 : appendPQExpBufferStr(query, ";\n");
10687 :
10688 12674 : appendPQExpBuffer(tag, "%s %s", type, name);
10689 :
10690 : /*
10691 : * We mark comments as SECTION_NONE because they really belong in the
10692 : * same section as their parent, whether that is pre-data or
10693 : * post-data.
10694 : */
10695 12674 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10696 12674 : ARCHIVE_OPTS(.tag = tag->data,
10697 : .namespace = namespace,
10698 : .owner = owner,
10699 : .description = "COMMENT",
10700 : .section = SECTION_NONE,
10701 : .createStmt = query->data,
10702 : .deps = &dumpId,
10703 : .nDeps = 1));
10704 :
10705 12674 : destroyPQExpBuffer(query);
10706 12674 : destroyPQExpBuffer(tag);
10707 : }
10708 : }
10709 :
10710 : /*
10711 : * dumpComment --
10712 : *
10713 : * Typical simplification of the above function.
10714 : */
10715 : static inline void
10716 12686 : dumpComment(Archive *fout, const char *type,
10717 : const char *name, const char *namespace,
10718 : const char *owner, CatalogId catalogId,
10719 : int subid, DumpId dumpId)
10720 : {
10721 12686 : dumpCommentExtended(fout, type, name, namespace, owner,
10722 : catalogId, subid, dumpId, NULL);
10723 12686 : }
10724 :
10725 : /*
10726 : * appendNamedArgument --
10727 : *
10728 : * Convenience routine for constructing parameters of the form:
10729 : * 'paraname', 'value'::type
10730 : */
10731 : static void
10732 11264 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10733 : const char *argtype, const char *argval)
10734 : {
10735 11264 : appendPQExpBufferStr(out, ",\n\t");
10736 :
10737 11264 : appendStringLiteralAH(out, argname, fout);
10738 11264 : appendPQExpBufferStr(out, ", ");
10739 :
10740 11264 : appendStringLiteralAH(out, argval, fout);
10741 11264 : appendPQExpBuffer(out, "::%s", argtype);
10742 11264 : }
10743 :
10744 : /*
10745 : * fetchAttributeStats --
10746 : *
10747 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10748 : */
10749 : static PGresult *
10750 2242 : fetchAttributeStats(Archive *fout)
10751 : {
10752 2242 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10753 2242 : PQExpBuffer nspnames = createPQExpBuffer();
10754 2242 : PQExpBuffer relnames = createPQExpBuffer();
10755 2242 : int count = 0;
10756 2242 : PGresult *res = NULL;
10757 : static TocEntry *te;
10758 : static bool restarted;
10759 2242 : int max_rels = MAX_ATTR_STATS_RELS;
10760 :
10761 : /*
10762 : * Our query for retrieving statistics for multiple relations uses WITH
10763 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10764 : * in v9.4. For older versions, we resort to gathering statistics for a
10765 : * single relation at a time.
10766 : */
10767 2242 : if (fout->remoteVersion < 90400)
10768 0 : max_rels = 1;
10769 :
10770 : /* If we're just starting, set our TOC pointer. */
10771 2242 : if (!te)
10772 106 : te = AH->toc->next;
10773 :
10774 : /*
10775 : * We can't easily avoid a second TOC scan for the tar format because it
10776 : * writes restore.sql separately, which means we must execute the queries
10777 : * twice. This feels risky, but there is no known reason it should
10778 : * generate different output than the first pass. Even if it does, the
10779 : * worst-case scenario is that restore.sql might have different statistics
10780 : * data than the archive.
10781 : */
10782 2242 : if (!restarted && te == AH->toc && AH->format == archTar)
10783 : {
10784 2 : te = AH->toc->next;
10785 2 : restarted = true;
10786 : }
10787 :
10788 2242 : appendPQExpBufferChar(nspnames, '{');
10789 2242 : appendPQExpBufferChar(relnames, '{');
10790 :
10791 : /*
10792 : * Scan the TOC for the next set of relevant stats entries. We assume
10793 : * that statistics are dumped in the order they are listed in the TOC.
10794 : * This is perhaps not the sturdiest assumption, so we verify it matches
10795 : * reality in dumpRelationStats_dumper().
10796 : */
10797 33376 : for (; te != AH->toc && count < max_rels; te = te->next)
10798 : {
10799 31134 : if ((te->reqs & REQ_STATS) != 0 &&
10800 6912 : strcmp(te->desc, "STATISTICS DATA") == 0)
10801 : {
10802 6912 : appendPGArray(nspnames, te->namespace);
10803 6912 : appendPGArray(relnames, te->tag);
10804 6912 : count++;
10805 : }
10806 : }
10807 :
10808 2242 : appendPQExpBufferChar(nspnames, '}');
10809 2242 : appendPQExpBufferChar(relnames, '}');
10810 :
10811 : /* Execute the query for the next batch of relations. */
10812 2242 : if (count > 0)
10813 : {
10814 200 : PQExpBuffer query = createPQExpBuffer();
10815 :
10816 200 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
10817 200 : appendStringLiteralAH(query, nspnames->data, fout);
10818 200 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
10819 200 : appendStringLiteralAH(query, relnames->data, fout);
10820 200 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
10821 200 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10822 200 : destroyPQExpBuffer(query);
10823 : }
10824 :
10825 2242 : destroyPQExpBuffer(nspnames);
10826 2242 : destroyPQExpBuffer(relnames);
10827 2242 : return res;
10828 : }
10829 :
10830 : /*
10831 : * dumpRelationStats_dumper --
10832 : *
10833 : * Generate command to import stats into the relation on the new database.
10834 : * This routine is called by the Archiver when it wants the statistics to be
10835 : * dumped.
10836 : */
10837 : static char *
10838 6912 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
10839 : {
10840 6912 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
10841 : static PGresult *res;
10842 : static int rownum;
10843 : PQExpBuffer query;
10844 : PQExpBufferData out_data;
10845 6912 : PQExpBuffer out = &out_data;
10846 : int i_schemaname;
10847 : int i_tablename;
10848 : int i_attname;
10849 : int i_inherited;
10850 : int i_null_frac;
10851 : int i_avg_width;
10852 : int i_n_distinct;
10853 : int i_most_common_vals;
10854 : int i_most_common_freqs;
10855 : int i_histogram_bounds;
10856 : int i_correlation;
10857 : int i_most_common_elems;
10858 : int i_most_common_elem_freqs;
10859 : int i_elem_count_histogram;
10860 : int i_range_length_histogram;
10861 : int i_range_empty_frac;
10862 : int i_range_bounds_histogram;
10863 : static TocEntry *expected_te;
10864 :
10865 : /*
10866 : * fetchAttributeStats() assumes that the statistics are dumped in the
10867 : * order they are listed in the TOC. We verify that here for safety.
10868 : */
10869 6912 : if (!expected_te)
10870 106 : expected_te = ((ArchiveHandle *) fout)->toc;
10871 :
10872 6912 : expected_te = expected_te->next;
10873 27378 : while ((expected_te->reqs & REQ_STATS) == 0 ||
10874 6912 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
10875 20466 : expected_te = expected_te->next;
10876 :
10877 6912 : if (te != expected_te)
10878 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
10879 : te->dumpId, te->desc, te->tag,
10880 : expected_te->dumpId, expected_te->desc, expected_te->tag);
10881 :
10882 6912 : query = createPQExpBuffer();
10883 6912 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
10884 : {
10885 106 : appendPQExpBufferStr(query,
10886 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
10887 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
10888 : "s.null_frac, s.avg_width, s.n_distinct, "
10889 : "s.most_common_vals, s.most_common_freqs, "
10890 : "s.histogram_bounds, s.correlation, "
10891 : "s.most_common_elems, s.most_common_elem_freqs, "
10892 : "s.elem_count_histogram, ");
10893 :
10894 106 : if (fout->remoteVersion >= 170000)
10895 106 : appendPQExpBufferStr(query,
10896 : "s.range_length_histogram, "
10897 : "s.range_empty_frac, "
10898 : "s.range_bounds_histogram ");
10899 : else
10900 0 : appendPQExpBufferStr(query,
10901 : "NULL AS range_length_histogram,"
10902 : "NULL AS range_empty_frac,"
10903 : "NULL AS range_bounds_histogram ");
10904 :
10905 : /*
10906 : * The results must be in the order of the relations supplied in the
10907 : * parameters to ensure we remain in sync as we walk through the TOC.
10908 : * The redundant filter clause on s.tablename = ANY(...) seems
10909 : * sufficient to convince the planner to use
10910 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
10911 : * This may not work for all versions.
10912 : *
10913 : * Our query for retrieving statistics for multiple relations uses
10914 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
10915 : * introduced in v9.4. For older versions, we resort to gathering
10916 : * statistics for a single relation at a time.
10917 : */
10918 106 : if (fout->remoteVersion >= 90400)
10919 106 : appendPQExpBufferStr(query,
10920 : "FROM pg_catalog.pg_stats s "
10921 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
10922 : "ON s.schemaname = u.schemaname "
10923 : "AND s.tablename = u.tablename "
10924 : "WHERE s.tablename = ANY($2) "
10925 : "ORDER BY u.ord, s.attname, s.inherited");
10926 : else
10927 0 : appendPQExpBufferStr(query,
10928 : "FROM pg_catalog.pg_stats s "
10929 : "WHERE s.schemaname = $1[1] "
10930 : "AND s.tablename = $2[1] "
10931 : "ORDER BY s.attname, s.inherited");
10932 :
10933 106 : ExecuteSqlStatement(fout, query->data);
10934 :
10935 106 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
10936 106 : resetPQExpBuffer(query);
10937 : }
10938 :
10939 6912 : initPQExpBuffer(out);
10940 :
10941 : /* restore relation stats */
10942 6912 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
10943 6912 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
10944 : fout->remoteVersion);
10945 6912 : appendPQExpBufferStr(out, "\t'schemaname', ");
10946 6912 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
10947 6912 : appendPQExpBufferStr(out, ",\n");
10948 6912 : appendPQExpBufferStr(out, "\t'relname', ");
10949 6912 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
10950 6912 : appendPQExpBufferStr(out, ",\n");
10951 6912 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
10952 :
10953 : /*
10954 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
10955 : * the relation is empty, or it could mean that it hadn't yet been
10956 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
10957 : * This ambiguity allegedly can cause the planner to choose inefficient
10958 : * plans after restoring to v18 or newer. To deal with this, let's just
10959 : * set reltuples to -1 in that case.
10960 : */
10961 6912 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
10962 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
10963 : else
10964 6912 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
10965 :
10966 6912 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
10967 6912 : rsinfo->relallvisible);
10968 :
10969 6912 : if (fout->remoteVersion >= 180000)
10970 6912 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
10971 :
10972 6912 : appendPQExpBufferStr(out, "\n);\n");
10973 :
10974 : /* Fetch the next batch of attribute statistics if needed. */
10975 6912 : if (rownum >= PQntuples(res))
10976 : {
10977 2242 : PQclear(res);
10978 2242 : res = fetchAttributeStats(fout);
10979 2242 : rownum = 0;
10980 : }
10981 :
10982 6912 : i_schemaname = PQfnumber(res, "schemaname");
10983 6912 : i_tablename = PQfnumber(res, "tablename");
10984 6912 : i_attname = PQfnumber(res, "attname");
10985 6912 : i_inherited = PQfnumber(res, "inherited");
10986 6912 : i_null_frac = PQfnumber(res, "null_frac");
10987 6912 : i_avg_width = PQfnumber(res, "avg_width");
10988 6912 : i_n_distinct = PQfnumber(res, "n_distinct");
10989 6912 : i_most_common_vals = PQfnumber(res, "most_common_vals");
10990 6912 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
10991 6912 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
10992 6912 : i_correlation = PQfnumber(res, "correlation");
10993 6912 : i_most_common_elems = PQfnumber(res, "most_common_elems");
10994 6912 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
10995 6912 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
10996 6912 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
10997 6912 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
10998 6912 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
10999 :
11000 : /* restore attribute stats */
11001 8606 : for (; rownum < PQntuples(res); rownum++)
11002 : {
11003 : const char *attname;
11004 :
11005 : /* Stop if the next stat row in our cache isn't for this relation. */
11006 6364 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11007 1694 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11008 : break;
11009 :
11010 1694 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11011 1694 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11012 : fout->remoteVersion);
11013 1694 : appendPQExpBufferStr(out, "\t'schemaname', ");
11014 1694 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11015 1694 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11016 1694 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11017 :
11018 1694 : if (PQgetisnull(res, rownum, i_attname))
11019 0 : pg_fatal("unexpected null attname");
11020 1694 : attname = PQgetvalue(res, rownum, i_attname);
11021 :
11022 : /*
11023 : * Indexes look up attname in indAttNames to derive attnum, all others
11024 : * use attname directly. We must specify attnum for indexes, since
11025 : * their attnames are not necessarily stable across dump/reload.
11026 : */
11027 1694 : if (rsinfo->nindAttNames == 0)
11028 : {
11029 1616 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11030 1616 : appendStringLiteralAH(out, attname, fout);
11031 : }
11032 : else
11033 : {
11034 78 : bool found = false;
11035 :
11036 148 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11037 : {
11038 148 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11039 : {
11040 78 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11041 : i + 1);
11042 78 : found = true;
11043 78 : break;
11044 : }
11045 : }
11046 :
11047 78 : if (!found)
11048 0 : pg_fatal("could not find index attname \"%s\"", attname);
11049 : }
11050 :
11051 1694 : if (!PQgetisnull(res, rownum, i_inherited))
11052 1694 : appendNamedArgument(out, fout, "inherited", "boolean",
11053 1694 : PQgetvalue(res, rownum, i_inherited));
11054 1694 : if (!PQgetisnull(res, rownum, i_null_frac))
11055 1694 : appendNamedArgument(out, fout, "null_frac", "real",
11056 1694 : PQgetvalue(res, rownum, i_null_frac));
11057 1694 : if (!PQgetisnull(res, rownum, i_avg_width))
11058 1694 : appendNamedArgument(out, fout, "avg_width", "integer",
11059 1694 : PQgetvalue(res, rownum, i_avg_width));
11060 1694 : if (!PQgetisnull(res, rownum, i_n_distinct))
11061 1694 : appendNamedArgument(out, fout, "n_distinct", "real",
11062 1694 : PQgetvalue(res, rownum, i_n_distinct));
11063 1694 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11064 874 : appendNamedArgument(out, fout, "most_common_vals", "text",
11065 874 : PQgetvalue(res, rownum, i_most_common_vals));
11066 1694 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11067 874 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11068 874 : PQgetvalue(res, rownum, i_most_common_freqs));
11069 1694 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11070 1056 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11071 1056 : PQgetvalue(res, rownum, i_histogram_bounds));
11072 1694 : if (!PQgetisnull(res, rownum, i_correlation))
11073 1614 : appendNamedArgument(out, fout, "correlation", "real",
11074 1614 : PQgetvalue(res, rownum, i_correlation));
11075 1694 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11076 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11077 16 : PQgetvalue(res, rownum, i_most_common_elems));
11078 1694 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11079 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11080 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11081 1694 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11082 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11083 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11084 1694 : if (fout->remoteVersion >= 170000)
11085 : {
11086 1694 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11087 8 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11088 8 : PQgetvalue(res, rownum, i_range_length_histogram));
11089 1694 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11090 8 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11091 8 : PQgetvalue(res, rownum, i_range_empty_frac));
11092 1694 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11093 8 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11094 8 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11095 : }
11096 1694 : appendPQExpBufferStr(out, "\n);\n");
11097 : }
11098 :
11099 6912 : destroyPQExpBuffer(query);
11100 6912 : return out->data;
11101 : }
11102 :
11103 : /*
11104 : * dumpRelationStats --
11105 : *
11106 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11107 : * care of gathering the statistics and generating the restore commands when
11108 : * they are needed.
11109 : */
11110 : static void
11111 7050 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11112 : {
11113 7050 : const DumpableObject *dobj = &rsinfo->dobj;
11114 :
11115 : /* nothing to do if we are not dumping statistics */
11116 7050 : if (!fout->dopt->dumpStatistics)
11117 0 : return;
11118 :
11119 7050 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11120 7050 : ARCHIVE_OPTS(.tag = dobj->name,
11121 : .namespace = dobj->namespace->dobj.name,
11122 : .description = "STATISTICS DATA",
11123 : .section = rsinfo->section,
11124 : .defnFn = dumpRelationStats_dumper,
11125 : .defnArg = rsinfo,
11126 : .deps = dobj->dependencies,
11127 : .nDeps = dobj->nDeps));
11128 : }
11129 :
11130 : /*
11131 : * dumpTableComment --
11132 : *
11133 : * As above, but dump comments for both the specified table (or view)
11134 : * and its columns.
11135 : */
11136 : static void
11137 176 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11138 : const char *reltypename)
11139 : {
11140 176 : DumpOptions *dopt = fout->dopt;
11141 : CommentItem *comments;
11142 : int ncomments;
11143 : PQExpBuffer query;
11144 : PQExpBuffer tag;
11145 :
11146 : /* do nothing, if --no-comments is supplied */
11147 176 : if (dopt->no_comments)
11148 0 : return;
11149 :
11150 : /* Comments are SCHEMA not data */
11151 176 : if (!dopt->dumpSchema)
11152 0 : return;
11153 :
11154 : /* Search for comments associated with relation, using table */
11155 176 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11156 176 : tbinfo->dobj.catId.oid,
11157 : &comments);
11158 :
11159 : /* If comments exist, build COMMENT ON statements */
11160 176 : if (ncomments <= 0)
11161 0 : return;
11162 :
11163 176 : query = createPQExpBuffer();
11164 176 : tag = createPQExpBuffer();
11165 :
11166 496 : while (ncomments > 0)
11167 : {
11168 320 : const char *descr = comments->descr;
11169 320 : int objsubid = comments->objsubid;
11170 :
11171 320 : if (objsubid == 0)
11172 : {
11173 72 : resetPQExpBuffer(tag);
11174 72 : appendPQExpBuffer(tag, "%s %s", reltypename,
11175 72 : fmtId(tbinfo->dobj.name));
11176 :
11177 72 : resetPQExpBuffer(query);
11178 72 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11179 72 : fmtQualifiedDumpable(tbinfo));
11180 72 : appendStringLiteralAH(query, descr, fout);
11181 72 : appendPQExpBufferStr(query, ";\n");
11182 :
11183 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11184 72 : ARCHIVE_OPTS(.tag = tag->data,
11185 : .namespace = tbinfo->dobj.namespace->dobj.name,
11186 : .owner = tbinfo->rolname,
11187 : .description = "COMMENT",
11188 : .section = SECTION_NONE,
11189 : .createStmt = query->data,
11190 : .deps = &(tbinfo->dobj.dumpId),
11191 : .nDeps = 1));
11192 : }
11193 248 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11194 : {
11195 248 : resetPQExpBuffer(tag);
11196 248 : appendPQExpBuffer(tag, "COLUMN %s.",
11197 248 : fmtId(tbinfo->dobj.name));
11198 248 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11199 :
11200 248 : resetPQExpBuffer(query);
11201 248 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11202 248 : fmtQualifiedDumpable(tbinfo));
11203 248 : appendPQExpBuffer(query, "%s IS ",
11204 248 : fmtId(tbinfo->attnames[objsubid - 1]));
11205 248 : appendStringLiteralAH(query, descr, fout);
11206 248 : appendPQExpBufferStr(query, ";\n");
11207 :
11208 248 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11209 248 : ARCHIVE_OPTS(.tag = tag->data,
11210 : .namespace = tbinfo->dobj.namespace->dobj.name,
11211 : .owner = tbinfo->rolname,
11212 : .description = "COMMENT",
11213 : .section = SECTION_NONE,
11214 : .createStmt = query->data,
11215 : .deps = &(tbinfo->dobj.dumpId),
11216 : .nDeps = 1));
11217 : }
11218 :
11219 320 : comments++;
11220 320 : ncomments--;
11221 : }
11222 :
11223 176 : destroyPQExpBuffer(query);
11224 176 : destroyPQExpBuffer(tag);
11225 : }
11226 :
11227 : /*
11228 : * findComments --
11229 : *
11230 : * Find the comment(s), if any, associated with the given object. All the
11231 : * objsubid values associated with the given classoid/objoid are found with
11232 : * one search.
11233 : */
11234 : static int
11235 13342 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11236 : {
11237 13342 : CommentItem *middle = NULL;
11238 : CommentItem *low;
11239 : CommentItem *high;
11240 : int nmatch;
11241 :
11242 : /*
11243 : * Do binary search to find some item matching the object.
11244 : */
11245 13342 : low = &comments[0];
11246 13342 : high = &comments[ncomments - 1];
11247 133088 : while (low <= high)
11248 : {
11249 132992 : middle = low + (high - low) / 2;
11250 :
11251 132992 : if (classoid < middle->classoid)
11252 15794 : high = middle - 1;
11253 117198 : else if (classoid > middle->classoid)
11254 14758 : low = middle + 1;
11255 102440 : else if (objoid < middle->objoid)
11256 43182 : high = middle - 1;
11257 59258 : else if (objoid > middle->objoid)
11258 46012 : low = middle + 1;
11259 : else
11260 13246 : break; /* found a match */
11261 : }
11262 :
11263 13342 : if (low > high) /* no matches */
11264 : {
11265 96 : *items = NULL;
11266 96 : return 0;
11267 : }
11268 :
11269 : /*
11270 : * Now determine how many items match the object. The search loop
11271 : * invariant still holds: only items between low and high inclusive could
11272 : * match.
11273 : */
11274 13246 : nmatch = 1;
11275 13390 : while (middle > low)
11276 : {
11277 6084 : if (classoid != middle[-1].classoid ||
11278 5898 : objoid != middle[-1].objoid)
11279 : break;
11280 144 : middle--;
11281 144 : nmatch++;
11282 : }
11283 :
11284 13246 : *items = middle;
11285 :
11286 13246 : middle += nmatch;
11287 13246 : while (middle <= high)
11288 : {
11289 7092 : if (classoid != middle->classoid ||
11290 6382 : objoid != middle->objoid)
11291 : break;
11292 0 : middle++;
11293 0 : nmatch++;
11294 : }
11295 :
11296 13246 : return nmatch;
11297 : }
11298 :
11299 : /*
11300 : * collectComments --
11301 : *
11302 : * Construct a table of all comments available for database objects;
11303 : * also set the has-comment component flag for each relevant object.
11304 : *
11305 : * We used to do per-object queries for the comments, but it's much faster
11306 : * to pull them all over at once, and on most databases the memory cost
11307 : * isn't high.
11308 : *
11309 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11310 : */
11311 : static void
11312 468 : collectComments(Archive *fout)
11313 : {
11314 : PGresult *res;
11315 : PQExpBuffer query;
11316 : int i_description;
11317 : int i_classoid;
11318 : int i_objoid;
11319 : int i_objsubid;
11320 : int ntups;
11321 : int i;
11322 : DumpableObject *dobj;
11323 :
11324 468 : query = createPQExpBuffer();
11325 :
11326 468 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11327 : "FROM pg_catalog.pg_description "
11328 : "ORDER BY classoid, objoid, objsubid");
11329 :
11330 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11331 :
11332 : /* Construct lookup table containing OIDs in numeric form */
11333 :
11334 468 : i_description = PQfnumber(res, "description");
11335 468 : i_classoid = PQfnumber(res, "classoid");
11336 468 : i_objoid = PQfnumber(res, "objoid");
11337 468 : i_objsubid = PQfnumber(res, "objsubid");
11338 :
11339 468 : ntups = PQntuples(res);
11340 :
11341 468 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11342 468 : ncomments = 0;
11343 468 : dobj = NULL;
11344 :
11345 2491798 : for (i = 0; i < ntups; i++)
11346 : {
11347 : CatalogId objId;
11348 : int subid;
11349 :
11350 2491330 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11351 2491330 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11352 2491330 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11353 :
11354 : /* We needn't remember comments that don't match any dumpable object */
11355 2491330 : if (dobj == NULL ||
11356 897586 : dobj->catId.tableoid != objId.tableoid ||
11357 891990 : dobj->catId.oid != objId.oid)
11358 2491134 : dobj = findObjectByCatalogId(objId);
11359 2491330 : if (dobj == NULL)
11360 1593292 : continue;
11361 :
11362 : /*
11363 : * Comments on columns of composite types are linked to the type's
11364 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11365 : * in the type's own DumpableObject.
11366 : */
11367 898038 : if (subid != 0 && dobj->objType == DO_TABLE &&
11368 432 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11369 98 : {
11370 : TypeInfo *cTypeInfo;
11371 :
11372 98 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11373 98 : if (cTypeInfo)
11374 98 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11375 : }
11376 : else
11377 897940 : dobj->components |= DUMP_COMPONENT_COMMENT;
11378 :
11379 898038 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11380 898038 : comments[ncomments].classoid = objId.tableoid;
11381 898038 : comments[ncomments].objoid = objId.oid;
11382 898038 : comments[ncomments].objsubid = subid;
11383 898038 : ncomments++;
11384 : }
11385 :
11386 468 : PQclear(res);
11387 468 : destroyPQExpBuffer(query);
11388 468 : }
11389 :
11390 : /*
11391 : * dumpDumpableObject
11392 : *
11393 : * This routine and its subsidiaries are responsible for creating
11394 : * ArchiveEntries (TOC objects) for each object to be dumped.
11395 : */
11396 : static void
11397 1737526 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11398 : {
11399 : /*
11400 : * Clear any dump-request bits for components that don't exist for this
11401 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11402 : * request for every kind of object.)
11403 : */
11404 1737526 : dobj->dump &= dobj->components;
11405 :
11406 : /* Now, short-circuit if there's nothing to be done here. */
11407 1737526 : if (dobj->dump == 0)
11408 1541058 : return;
11409 :
11410 196468 : switch (dobj->objType)
11411 : {
11412 1218 : case DO_NAMESPACE:
11413 1218 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11414 1218 : break;
11415 38 : case DO_EXTENSION:
11416 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11417 38 : break;
11418 2154 : case DO_TYPE:
11419 2154 : dumpType(fout, (const TypeInfo *) dobj);
11420 2154 : break;
11421 196 : case DO_SHELL_TYPE:
11422 196 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11423 196 : break;
11424 5376 : case DO_FUNC:
11425 5376 : dumpFunc(fout, (const FuncInfo *) dobj);
11426 5376 : break;
11427 874 : case DO_AGG:
11428 874 : dumpAgg(fout, (const AggInfo *) dobj);
11429 874 : break;
11430 5098 : case DO_OPERATOR:
11431 5098 : dumpOpr(fout, (const OprInfo *) dobj);
11432 5098 : break;
11433 182 : case DO_ACCESS_METHOD:
11434 182 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11435 182 : break;
11436 1362 : case DO_OPCLASS:
11437 1362 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11438 1362 : break;
11439 1156 : case DO_OPFAMILY:
11440 1156 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11441 1156 : break;
11442 5108 : case DO_COLLATION:
11443 5108 : dumpCollation(fout, (const CollInfo *) dobj);
11444 5108 : break;
11445 852 : case DO_CONVERSION:
11446 852 : dumpConversion(fout, (const ConvInfo *) dobj);
11447 852 : break;
11448 80688 : case DO_TABLE:
11449 80688 : dumpTable(fout, (const TableInfo *) dobj);
11450 80688 : break;
11451 3900 : case DO_TABLE_ATTACH:
11452 3900 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11453 3900 : break;
11454 2916 : case DO_ATTRDEF:
11455 2916 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11456 2916 : break;
11457 6970 : case DO_INDEX:
11458 6970 : dumpIndex(fout, (const IndxInfo *) dobj);
11459 6970 : break;
11460 1480 : case DO_INDEX_ATTACH:
11461 1480 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11462 1480 : break;
11463 314 : case DO_STATSEXT:
11464 314 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11465 314 : break;
11466 852 : case DO_REFRESH_MATVIEW:
11467 852 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11468 852 : break;
11469 2984 : case DO_RULE:
11470 2984 : dumpRule(fout, (const RuleInfo *) dobj);
11471 2984 : break;
11472 1476 : case DO_TRIGGER:
11473 1476 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11474 1476 : break;
11475 98 : case DO_EVENT_TRIGGER:
11476 98 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11477 98 : break;
11478 5930 : case DO_CONSTRAINT:
11479 5930 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11480 5930 : break;
11481 472 : case DO_FK_CONSTRAINT:
11482 472 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11483 472 : break;
11484 180 : case DO_PROCLANG:
11485 180 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11486 180 : break;
11487 178 : case DO_CAST:
11488 178 : dumpCast(fout, (const CastInfo *) dobj);
11489 178 : break;
11490 98 : case DO_TRANSFORM:
11491 98 : dumpTransform(fout, (const TransformInfo *) dobj);
11492 98 : break;
11493 1136 : case DO_SEQUENCE_SET:
11494 1136 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11495 1136 : break;
11496 12414 : case DO_TABLE_DATA:
11497 12414 : dumpTableData(fout, (const TableDataInfo *) dobj);
11498 12414 : break;
11499 38754 : case DO_DUMMY_TYPE:
11500 : /* table rowtypes and array types are never dumped separately */
11501 38754 : break;
11502 90 : case DO_TSPARSER:
11503 90 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11504 90 : break;
11505 408 : case DO_TSDICT:
11506 408 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11507 408 : break;
11508 114 : case DO_TSTEMPLATE:
11509 114 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11510 114 : break;
11511 328 : case DO_TSCONFIG:
11512 328 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11513 328 : break;
11514 118 : case DO_FDW:
11515 118 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11516 118 : break;
11517 126 : case DO_FOREIGN_SERVER:
11518 126 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11519 126 : break;
11520 332 : case DO_DEFAULT_ACL:
11521 332 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11522 332 : break;
11523 164 : case DO_LARGE_OBJECT:
11524 164 : dumpLO(fout, (const LoInfo *) dobj);
11525 164 : break;
11526 164 : case DO_LARGE_OBJECT_DATA:
11527 164 : if (dobj->dump & DUMP_COMPONENT_DATA)
11528 : {
11529 : LoInfo *loinfo;
11530 : TocEntry *te;
11531 :
11532 164 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11533 164 : if (loinfo == NULL)
11534 0 : pg_fatal("missing metadata for large objects \"%s\"",
11535 : dobj->name);
11536 :
11537 164 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11538 164 : ARCHIVE_OPTS(.tag = dobj->name,
11539 : .owner = loinfo->rolname,
11540 : .description = "BLOBS",
11541 : .section = SECTION_DATA,
11542 : .deps = dobj->dependencies,
11543 : .nDeps = dobj->nDeps,
11544 : .dumpFn = dumpLOs,
11545 : .dumpArg = loinfo));
11546 :
11547 : /*
11548 : * Set the TocEntry's dataLength in case we are doing a
11549 : * parallel dump and want to order dump jobs by table size.
11550 : * (We need some size estimate for every TocEntry with a
11551 : * DataDumper function.) We don't currently have any cheap
11552 : * way to estimate the size of LOs, but fortunately it doesn't
11553 : * matter too much as long as we get large batches of LOs
11554 : * processed reasonably early. Assume 8K per blob.
11555 : */
11556 164 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11557 : }
11558 164 : break;
11559 780 : case DO_POLICY:
11560 780 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11561 780 : break;
11562 412 : case DO_PUBLICATION:
11563 412 : dumpPublication(fout, (const PublicationInfo *) dobj);
11564 412 : break;
11565 574 : case DO_PUBLICATION_REL:
11566 574 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11567 574 : break;
11568 164 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11569 164 : dumpPublicationNamespace(fout,
11570 : (const PublicationSchemaInfo *) dobj);
11571 164 : break;
11572 250 : case DO_SUBSCRIPTION:
11573 250 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11574 250 : break;
11575 4 : case DO_SUBSCRIPTION_REL:
11576 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11577 4 : break;
11578 7050 : case DO_REL_STATS:
11579 7050 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11580 7050 : break;
11581 936 : case DO_PRE_DATA_BOUNDARY:
11582 : case DO_POST_DATA_BOUNDARY:
11583 : /* never dumped, nothing to do */
11584 936 : break;
11585 : }
11586 : }
11587 :
11588 : /*
11589 : * dumpNamespace
11590 : * writes out to fout the queries to recreate a user-defined namespace
11591 : */
11592 : static void
11593 1218 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11594 : {
11595 1218 : DumpOptions *dopt = fout->dopt;
11596 : PQExpBuffer q;
11597 : PQExpBuffer delq;
11598 : char *qnspname;
11599 :
11600 : /* Do nothing if not dumping schema */
11601 1218 : if (!dopt->dumpSchema)
11602 56 : return;
11603 :
11604 1162 : q = createPQExpBuffer();
11605 1162 : delq = createPQExpBuffer();
11606 :
11607 1162 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11608 :
11609 1162 : if (nspinfo->create)
11610 : {
11611 764 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11612 764 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11613 : }
11614 : else
11615 : {
11616 : /* see selectDumpableNamespace() */
11617 398 : appendPQExpBufferStr(delq,
11618 : "-- *not* dropping schema, since initdb creates it\n");
11619 398 : appendPQExpBufferStr(q,
11620 : "-- *not* creating schema, since initdb creates it\n");
11621 : }
11622 :
11623 1162 : if (dopt->binary_upgrade)
11624 152 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11625 : "SCHEMA", qnspname, NULL);
11626 :
11627 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11628 434 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11629 434 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11630 : .owner = nspinfo->rolname,
11631 : .description = "SCHEMA",
11632 : .section = SECTION_PRE_DATA,
11633 : .createStmt = q->data,
11634 : .dropStmt = delq->data));
11635 :
11636 : /* Dump Schema Comments and Security Labels */
11637 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11638 : {
11639 408 : const char *initdb_comment = NULL;
11640 :
11641 408 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11642 340 : initdb_comment = "standard public schema";
11643 408 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11644 408 : NULL, nspinfo->rolname,
11645 408 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11646 : initdb_comment);
11647 : }
11648 :
11649 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11650 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11651 0 : NULL, nspinfo->rolname,
11652 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11653 :
11654 1162 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11655 936 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11656 : qnspname, NULL, NULL,
11657 936 : NULL, nspinfo->rolname, &nspinfo->dacl);
11658 :
11659 1162 : free(qnspname);
11660 :
11661 1162 : destroyPQExpBuffer(q);
11662 1162 : destroyPQExpBuffer(delq);
11663 : }
11664 :
11665 : /*
11666 : * dumpExtension
11667 : * writes out to fout the queries to recreate an extension
11668 : */
11669 : static void
11670 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11671 : {
11672 38 : DumpOptions *dopt = fout->dopt;
11673 : PQExpBuffer q;
11674 : PQExpBuffer delq;
11675 : char *qextname;
11676 :
11677 : /* Do nothing if not dumping schema */
11678 38 : if (!dopt->dumpSchema)
11679 2 : return;
11680 :
11681 36 : q = createPQExpBuffer();
11682 36 : delq = createPQExpBuffer();
11683 :
11684 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11685 :
11686 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11687 :
11688 36 : if (!dopt->binary_upgrade)
11689 : {
11690 : /*
11691 : * In a regular dump, we simply create the extension, intentionally
11692 : * not specifying a version, so that the destination installation's
11693 : * default version is used.
11694 : *
11695 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11696 : * types; but there are various scenarios in which it's convenient to
11697 : * manually create the desired extension before restoring, so we
11698 : * prefer to allow it to exist already.
11699 : */
11700 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11701 34 : qextname, fmtId(extinfo->namespace));
11702 : }
11703 : else
11704 : {
11705 : /*
11706 : * In binary-upgrade mode, it's critical to reproduce the state of the
11707 : * database exactly, so our procedure is to create an empty extension,
11708 : * restore all the contained objects normally, and add them to the
11709 : * extension one by one. This function performs just the first of
11710 : * those steps. binary_upgrade_extension_member() takes care of
11711 : * adding member objects as they're created.
11712 : */
11713 : int i;
11714 : int n;
11715 :
11716 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11717 :
11718 : /*
11719 : * We unconditionally create the extension, so we must drop it if it
11720 : * exists. This could happen if the user deleted 'plpgsql' and then
11721 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11722 : */
11723 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11724 :
11725 2 : appendPQExpBufferStr(q,
11726 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11727 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11728 2 : appendPQExpBufferStr(q, ", ");
11729 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11730 2 : appendPQExpBufferStr(q, ", ");
11731 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11732 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11733 2 : appendPQExpBufferStr(q, ", ");
11734 :
11735 : /*
11736 : * Note that we're pushing extconfig (an OID array) back into
11737 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11738 : * preserved in binary upgrade.
11739 : */
11740 2 : if (strlen(extinfo->extconfig) > 2)
11741 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11742 : else
11743 0 : appendPQExpBufferStr(q, "NULL");
11744 2 : appendPQExpBufferStr(q, ", ");
11745 2 : if (strlen(extinfo->extcondition) > 2)
11746 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11747 : else
11748 0 : appendPQExpBufferStr(q, "NULL");
11749 2 : appendPQExpBufferStr(q, ", ");
11750 2 : appendPQExpBufferStr(q, "ARRAY[");
11751 2 : n = 0;
11752 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11753 : {
11754 : DumpableObject *extobj;
11755 :
11756 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11757 2 : if (extobj && extobj->objType == DO_EXTENSION)
11758 : {
11759 0 : if (n++ > 0)
11760 0 : appendPQExpBufferChar(q, ',');
11761 0 : appendStringLiteralAH(q, extobj->name, fout);
11762 : }
11763 : }
11764 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11765 2 : appendPQExpBufferStr(q, ");\n");
11766 : }
11767 :
11768 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11769 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11770 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11771 : .description = "EXTENSION",
11772 : .section = SECTION_PRE_DATA,
11773 : .createStmt = q->data,
11774 : .dropStmt = delq->data));
11775 :
11776 : /* Dump Extension Comments and Security Labels */
11777 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11778 36 : dumpComment(fout, "EXTENSION", qextname,
11779 : NULL, "",
11780 36 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11781 :
11782 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11783 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11784 : NULL, "",
11785 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11786 :
11787 36 : free(qextname);
11788 :
11789 36 : destroyPQExpBuffer(q);
11790 36 : destroyPQExpBuffer(delq);
11791 : }
11792 :
11793 : /*
11794 : * dumpType
11795 : * writes out to fout the queries to recreate a user-defined type
11796 : */
11797 : static void
11798 2154 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11799 : {
11800 2154 : DumpOptions *dopt = fout->dopt;
11801 :
11802 : /* Do nothing if not dumping schema */
11803 2154 : if (!dopt->dumpSchema)
11804 86 : return;
11805 :
11806 : /* Dump out in proper style */
11807 2068 : if (tyinfo->typtype == TYPTYPE_BASE)
11808 610 : dumpBaseType(fout, tyinfo);
11809 1458 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11810 440 : dumpDomain(fout, tyinfo);
11811 1018 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11812 364 : dumpCompositeType(fout, tyinfo);
11813 654 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11814 140 : dumpEnumType(fout, tyinfo);
11815 514 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11816 276 : dumpRangeType(fout, tyinfo);
11817 238 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11818 88 : dumpUndefinedType(fout, tyinfo);
11819 : else
11820 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11821 : tyinfo->dobj.name);
11822 : }
11823 :
11824 : /*
11825 : * dumpEnumType
11826 : * writes out to fout the queries to recreate a user-defined enum type
11827 : */
11828 : static void
11829 140 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11830 : {
11831 140 : DumpOptions *dopt = fout->dopt;
11832 140 : PQExpBuffer q = createPQExpBuffer();
11833 140 : PQExpBuffer delq = createPQExpBuffer();
11834 140 : PQExpBuffer query = createPQExpBuffer();
11835 : PGresult *res;
11836 : int num,
11837 : i;
11838 : Oid enum_oid;
11839 : char *qtypname;
11840 : char *qualtypname;
11841 : char *label;
11842 : int i_enumlabel;
11843 : int i_oid;
11844 :
11845 140 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11846 : {
11847 : /* Set up query for enum-specific details */
11848 92 : appendPQExpBufferStr(query,
11849 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11850 : "SELECT oid, enumlabel "
11851 : "FROM pg_catalog.pg_enum "
11852 : "WHERE enumtypid = $1 "
11853 : "ORDER BY enumsortorder");
11854 :
11855 92 : ExecuteSqlStatement(fout, query->data);
11856 :
11857 92 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11858 : }
11859 :
11860 140 : printfPQExpBuffer(query,
11861 : "EXECUTE dumpEnumType('%u')",
11862 140 : tyinfo->dobj.catId.oid);
11863 :
11864 140 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11865 :
11866 140 : num = PQntuples(res);
11867 :
11868 140 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11869 140 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11870 :
11871 : /*
11872 : * CASCADE shouldn't be required here as for normal types since the I/O
11873 : * functions are generic and do not get dropped.
11874 : */
11875 140 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11876 :
11877 140 : if (dopt->binary_upgrade)
11878 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11879 10 : tyinfo->dobj.catId.oid,
11880 : false, false);
11881 :
11882 140 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11883 : qualtypname);
11884 :
11885 140 : if (!dopt->binary_upgrade)
11886 : {
11887 130 : i_enumlabel = PQfnumber(res, "enumlabel");
11888 :
11889 : /* Labels with server-assigned oids */
11890 1080 : for (i = 0; i < num; i++)
11891 : {
11892 950 : label = PQgetvalue(res, i, i_enumlabel);
11893 950 : if (i > 0)
11894 820 : appendPQExpBufferChar(q, ',');
11895 950 : appendPQExpBufferStr(q, "\n ");
11896 950 : appendStringLiteralAH(q, label, fout);
11897 : }
11898 : }
11899 :
11900 140 : appendPQExpBufferStr(q, "\n);\n");
11901 :
11902 140 : if (dopt->binary_upgrade)
11903 : {
11904 10 : i_oid = PQfnumber(res, "oid");
11905 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11906 :
11907 : /* Labels with dump-assigned (preserved) oids */
11908 116 : for (i = 0; i < num; i++)
11909 : {
11910 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11911 106 : label = PQgetvalue(res, i, i_enumlabel);
11912 :
11913 106 : if (i == 0)
11914 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11915 106 : appendPQExpBuffer(q,
11916 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11917 : enum_oid);
11918 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11919 106 : appendStringLiteralAH(q, label, fout);
11920 106 : appendPQExpBufferStr(q, ";\n\n");
11921 : }
11922 : }
11923 :
11924 140 : if (dopt->binary_upgrade)
11925 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11926 : "TYPE", qtypname,
11927 10 : tyinfo->dobj.namespace->dobj.name);
11928 :
11929 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11930 140 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11931 140 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11932 : .namespace = tyinfo->dobj.namespace->dobj.name,
11933 : .owner = tyinfo->rolname,
11934 : .description = "TYPE",
11935 : .section = SECTION_PRE_DATA,
11936 : .createStmt = q->data,
11937 : .dropStmt = delq->data));
11938 :
11939 : /* Dump Type Comments and Security Labels */
11940 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11941 72 : dumpComment(fout, "TYPE", qtypname,
11942 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11943 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11944 :
11945 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11946 0 : dumpSecLabel(fout, "TYPE", qtypname,
11947 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11948 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11949 :
11950 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11951 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11952 : qtypname, NULL,
11953 72 : tyinfo->dobj.namespace->dobj.name,
11954 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
11955 :
11956 140 : PQclear(res);
11957 140 : destroyPQExpBuffer(q);
11958 140 : destroyPQExpBuffer(delq);
11959 140 : destroyPQExpBuffer(query);
11960 140 : free(qtypname);
11961 140 : free(qualtypname);
11962 140 : }
11963 :
11964 : /*
11965 : * dumpRangeType
11966 : * writes out to fout the queries to recreate a user-defined range type
11967 : */
11968 : static void
11969 276 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11970 : {
11971 276 : DumpOptions *dopt = fout->dopt;
11972 276 : PQExpBuffer q = createPQExpBuffer();
11973 276 : PQExpBuffer delq = createPQExpBuffer();
11974 276 : PQExpBuffer query = createPQExpBuffer();
11975 : PGresult *res;
11976 : Oid collationOid;
11977 : char *qtypname;
11978 : char *qualtypname;
11979 : char *procname;
11980 :
11981 276 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11982 : {
11983 : /* Set up query for range-specific details */
11984 94 : appendPQExpBufferStr(query,
11985 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11986 :
11987 94 : appendPQExpBufferStr(query,
11988 : "SELECT ");
11989 :
11990 94 : if (fout->remoteVersion >= 140000)
11991 94 : appendPQExpBufferStr(query,
11992 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11993 : else
11994 0 : appendPQExpBufferStr(query,
11995 : "NULL AS rngmultitype, ");
11996 :
11997 94 : appendPQExpBufferStr(query,
11998 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11999 : "opc.opcname AS opcname, "
12000 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12001 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12002 : "opc.opcdefault, "
12003 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12004 : " ELSE rngcollation END AS collation, "
12005 : "rngcanonical, rngsubdiff "
12006 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12007 : " pg_catalog.pg_opclass opc "
12008 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12009 : "rngtypid = $1");
12010 :
12011 94 : ExecuteSqlStatement(fout, query->data);
12012 :
12013 94 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12014 : }
12015 :
12016 276 : printfPQExpBuffer(query,
12017 : "EXECUTE dumpRangeType('%u')",
12018 276 : tyinfo->dobj.catId.oid);
12019 :
12020 276 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12021 :
12022 276 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12023 276 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12024 :
12025 : /*
12026 : * CASCADE shouldn't be required here as for normal types since the I/O
12027 : * functions are generic and do not get dropped.
12028 : */
12029 276 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12030 :
12031 276 : if (dopt->binary_upgrade)
12032 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12033 16 : tyinfo->dobj.catId.oid,
12034 : false, true);
12035 :
12036 276 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12037 : qualtypname);
12038 :
12039 276 : appendPQExpBuffer(q, "\n subtype = %s",
12040 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12041 :
12042 276 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12043 276 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12044 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12045 :
12046 : /* print subtype_opclass only if not default for subtype */
12047 276 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12048 : {
12049 72 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12050 72 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12051 :
12052 72 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12053 : fmtId(nspname));
12054 72 : appendPQExpBufferStr(q, fmtId(opcname));
12055 : }
12056 :
12057 276 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12058 276 : if (OidIsValid(collationOid))
12059 : {
12060 88 : CollInfo *coll = findCollationByOid(collationOid);
12061 :
12062 88 : if (coll)
12063 88 : appendPQExpBuffer(q, ",\n collation = %s",
12064 88 : fmtQualifiedDumpable(coll));
12065 : }
12066 :
12067 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12068 276 : if (strcmp(procname, "-") != 0)
12069 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12070 :
12071 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12072 276 : if (strcmp(procname, "-") != 0)
12073 52 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12074 :
12075 276 : appendPQExpBufferStr(q, "\n);\n");
12076 :
12077 276 : if (dopt->binary_upgrade)
12078 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12079 : "TYPE", qtypname,
12080 16 : tyinfo->dobj.namespace->dobj.name);
12081 :
12082 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12083 276 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12084 276 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12085 : .namespace = tyinfo->dobj.namespace->dobj.name,
12086 : .owner = tyinfo->rolname,
12087 : .description = "TYPE",
12088 : .section = SECTION_PRE_DATA,
12089 : .createStmt = q->data,
12090 : .dropStmt = delq->data));
12091 :
12092 : /* Dump Type Comments and Security Labels */
12093 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12094 108 : dumpComment(fout, "TYPE", qtypname,
12095 108 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12096 108 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12097 :
12098 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12099 0 : dumpSecLabel(fout, "TYPE", qtypname,
12100 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12101 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12102 :
12103 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12104 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12105 : qtypname, NULL,
12106 72 : tyinfo->dobj.namespace->dobj.name,
12107 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12108 :
12109 276 : PQclear(res);
12110 276 : destroyPQExpBuffer(q);
12111 276 : destroyPQExpBuffer(delq);
12112 276 : destroyPQExpBuffer(query);
12113 276 : free(qtypname);
12114 276 : free(qualtypname);
12115 276 : }
12116 :
12117 : /*
12118 : * dumpUndefinedType
12119 : * writes out to fout the queries to recreate a !typisdefined type
12120 : *
12121 : * This is a shell type, but we use different terminology to distinguish
12122 : * this case from where we have to emit a shell type definition to break
12123 : * circular dependencies. An undefined type shouldn't ever have anything
12124 : * depending on it.
12125 : */
12126 : static void
12127 88 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12128 : {
12129 88 : DumpOptions *dopt = fout->dopt;
12130 88 : PQExpBuffer q = createPQExpBuffer();
12131 88 : PQExpBuffer delq = createPQExpBuffer();
12132 : char *qtypname;
12133 : char *qualtypname;
12134 :
12135 88 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12136 88 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12137 :
12138 88 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12139 :
12140 88 : if (dopt->binary_upgrade)
12141 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12142 4 : tyinfo->dobj.catId.oid,
12143 : false, false);
12144 :
12145 88 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12146 : qualtypname);
12147 :
12148 88 : if (dopt->binary_upgrade)
12149 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12150 : "TYPE", qtypname,
12151 4 : tyinfo->dobj.namespace->dobj.name);
12152 :
12153 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12154 88 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12155 88 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12156 : .namespace = tyinfo->dobj.namespace->dobj.name,
12157 : .owner = tyinfo->rolname,
12158 : .description = "TYPE",
12159 : .section = SECTION_PRE_DATA,
12160 : .createStmt = q->data,
12161 : .dropStmt = delq->data));
12162 :
12163 : /* Dump Type Comments and Security Labels */
12164 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12165 72 : dumpComment(fout, "TYPE", qtypname,
12166 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12167 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12168 :
12169 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12170 0 : dumpSecLabel(fout, "TYPE", qtypname,
12171 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12172 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12173 :
12174 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12175 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12176 : qtypname, NULL,
12177 0 : tyinfo->dobj.namespace->dobj.name,
12178 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12179 :
12180 88 : destroyPQExpBuffer(q);
12181 88 : destroyPQExpBuffer(delq);
12182 88 : free(qtypname);
12183 88 : free(qualtypname);
12184 88 : }
12185 :
12186 : /*
12187 : * dumpBaseType
12188 : * writes out to fout the queries to recreate a user-defined base type
12189 : */
12190 : static void
12191 610 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12192 : {
12193 610 : DumpOptions *dopt = fout->dopt;
12194 610 : PQExpBuffer q = createPQExpBuffer();
12195 610 : PQExpBuffer delq = createPQExpBuffer();
12196 610 : PQExpBuffer query = createPQExpBuffer();
12197 : PGresult *res;
12198 : char *qtypname;
12199 : char *qualtypname;
12200 : char *typlen;
12201 : char *typinput;
12202 : char *typoutput;
12203 : char *typreceive;
12204 : char *typsend;
12205 : char *typmodin;
12206 : char *typmodout;
12207 : char *typanalyze;
12208 : char *typsubscript;
12209 : Oid typreceiveoid;
12210 : Oid typsendoid;
12211 : Oid typmodinoid;
12212 : Oid typmodoutoid;
12213 : Oid typanalyzeoid;
12214 : Oid typsubscriptoid;
12215 : char *typcategory;
12216 : char *typispreferred;
12217 : char *typdelim;
12218 : char *typbyval;
12219 : char *typalign;
12220 : char *typstorage;
12221 : char *typcollatable;
12222 : char *typdefault;
12223 610 : bool typdefault_is_literal = false;
12224 :
12225 610 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12226 : {
12227 : /* Set up query for type-specific details */
12228 94 : appendPQExpBufferStr(query,
12229 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12230 : "SELECT typlen, "
12231 : "typinput, typoutput, typreceive, typsend, "
12232 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12233 : "typsend::pg_catalog.oid AS typsendoid, "
12234 : "typanalyze, "
12235 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12236 : "typdelim, typbyval, typalign, typstorage, "
12237 : "typmodin, typmodout, "
12238 : "typmodin::pg_catalog.oid AS typmodinoid, "
12239 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12240 : "typcategory, typispreferred, "
12241 : "(typcollation <> 0) AS typcollatable, "
12242 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12243 :
12244 94 : if (fout->remoteVersion >= 140000)
12245 94 : appendPQExpBufferStr(query,
12246 : "typsubscript, "
12247 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12248 : else
12249 0 : appendPQExpBufferStr(query,
12250 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12251 :
12252 94 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12253 : "WHERE oid = $1");
12254 :
12255 94 : ExecuteSqlStatement(fout, query->data);
12256 :
12257 94 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12258 : }
12259 :
12260 610 : printfPQExpBuffer(query,
12261 : "EXECUTE dumpBaseType('%u')",
12262 610 : tyinfo->dobj.catId.oid);
12263 :
12264 610 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12265 :
12266 610 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12267 610 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12268 610 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12269 610 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12270 610 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12271 610 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12272 610 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12273 610 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12274 610 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12275 610 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12276 610 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12277 610 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12278 610 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12279 610 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12280 610 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12281 610 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12282 610 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12283 610 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12284 610 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12285 610 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12286 610 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12287 610 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12288 610 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12289 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12290 610 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12291 : {
12292 104 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12293 104 : typdefault_is_literal = true; /* it needs quotes */
12294 : }
12295 : else
12296 506 : typdefault = NULL;
12297 :
12298 610 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12299 610 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12300 :
12301 : /*
12302 : * The reason we include CASCADE is that the circular dependency between
12303 : * the type and its I/O functions makes it impossible to drop the type any
12304 : * other way.
12305 : */
12306 610 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12307 :
12308 : /*
12309 : * We might already have a shell type, but setting pg_type_oid is
12310 : * harmless, and in any case we'd better set the array type OID.
12311 : */
12312 610 : if (dopt->binary_upgrade)
12313 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12314 16 : tyinfo->dobj.catId.oid,
12315 : false, false);
12316 :
12317 610 : appendPQExpBuffer(q,
12318 : "CREATE TYPE %s (\n"
12319 : " INTERNALLENGTH = %s",
12320 : qualtypname,
12321 610 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12322 :
12323 : /* regproc result is sufficiently quoted already */
12324 610 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12325 610 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12326 610 : if (OidIsValid(typreceiveoid))
12327 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12328 610 : if (OidIsValid(typsendoid))
12329 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12330 610 : if (OidIsValid(typmodinoid))
12331 76 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12332 610 : if (OidIsValid(typmodoutoid))
12333 76 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12334 610 : if (OidIsValid(typanalyzeoid))
12335 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12336 :
12337 610 : if (strcmp(typcollatable, "t") == 0)
12338 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12339 :
12340 610 : if (typdefault != NULL)
12341 : {
12342 104 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12343 104 : if (typdefault_is_literal)
12344 104 : appendStringLiteralAH(q, typdefault, fout);
12345 : else
12346 0 : appendPQExpBufferStr(q, typdefault);
12347 : }
12348 :
12349 610 : if (OidIsValid(typsubscriptoid))
12350 64 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12351 :
12352 610 : if (OidIsValid(tyinfo->typelem))
12353 58 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12354 58 : getFormattedTypeName(fout, tyinfo->typelem,
12355 : zeroIsError));
12356 :
12357 610 : if (strcmp(typcategory, "U") != 0)
12358 : {
12359 322 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12360 322 : appendStringLiteralAH(q, typcategory, fout);
12361 : }
12362 :
12363 610 : if (strcmp(typispreferred, "t") == 0)
12364 64 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12365 :
12366 610 : if (typdelim && strcmp(typdelim, ",") != 0)
12367 : {
12368 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12369 6 : appendStringLiteralAH(q, typdelim, fout);
12370 : }
12371 :
12372 610 : if (*typalign == TYPALIGN_CHAR)
12373 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12374 586 : else if (*typalign == TYPALIGN_SHORT)
12375 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12376 574 : else if (*typalign == TYPALIGN_INT)
12377 406 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12378 168 : else if (*typalign == TYPALIGN_DOUBLE)
12379 168 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12380 :
12381 610 : if (*typstorage == TYPSTORAGE_PLAIN)
12382 460 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12383 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12384 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12385 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12386 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12387 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12388 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12389 :
12390 610 : if (strcmp(typbyval, "t") == 0)
12391 294 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12392 :
12393 610 : appendPQExpBufferStr(q, "\n);\n");
12394 :
12395 610 : if (dopt->binary_upgrade)
12396 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12397 : "TYPE", qtypname,
12398 16 : tyinfo->dobj.namespace->dobj.name);
12399 :
12400 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12401 610 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12402 610 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12403 : .namespace = tyinfo->dobj.namespace->dobj.name,
12404 : .owner = tyinfo->rolname,
12405 : .description = "TYPE",
12406 : .section = SECTION_PRE_DATA,
12407 : .createStmt = q->data,
12408 : .dropStmt = delq->data));
12409 :
12410 : /* Dump Type Comments and Security Labels */
12411 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12412 498 : dumpComment(fout, "TYPE", qtypname,
12413 498 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12414 498 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12415 :
12416 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12417 0 : dumpSecLabel(fout, "TYPE", qtypname,
12418 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12419 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12420 :
12421 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12422 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12423 : qtypname, NULL,
12424 72 : tyinfo->dobj.namespace->dobj.name,
12425 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12426 :
12427 610 : PQclear(res);
12428 610 : destroyPQExpBuffer(q);
12429 610 : destroyPQExpBuffer(delq);
12430 610 : destroyPQExpBuffer(query);
12431 610 : free(qtypname);
12432 610 : free(qualtypname);
12433 610 : }
12434 :
12435 : /*
12436 : * dumpDomain
12437 : * writes out to fout the queries to recreate a user-defined domain
12438 : */
12439 : static void
12440 440 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12441 : {
12442 440 : DumpOptions *dopt = fout->dopt;
12443 440 : PQExpBuffer q = createPQExpBuffer();
12444 440 : PQExpBuffer delq = createPQExpBuffer();
12445 440 : PQExpBuffer query = createPQExpBuffer();
12446 : PGresult *res;
12447 : int i;
12448 : char *qtypname;
12449 : char *qualtypname;
12450 : char *typnotnull;
12451 : char *typdefn;
12452 : char *typdefault;
12453 : Oid typcollation;
12454 440 : bool typdefault_is_literal = false;
12455 :
12456 440 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12457 : {
12458 : /* Set up query for domain-specific details */
12459 88 : appendPQExpBufferStr(query,
12460 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12461 :
12462 88 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12463 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12464 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12465 : "t.typdefault, "
12466 : "CASE WHEN t.typcollation <> u.typcollation "
12467 : "THEN t.typcollation ELSE 0 END AS typcollation "
12468 : "FROM pg_catalog.pg_type t "
12469 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12470 : "WHERE t.oid = $1");
12471 :
12472 88 : ExecuteSqlStatement(fout, query->data);
12473 :
12474 88 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12475 : }
12476 :
12477 440 : printfPQExpBuffer(query,
12478 : "EXECUTE dumpDomain('%u')",
12479 440 : tyinfo->dobj.catId.oid);
12480 :
12481 440 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12482 :
12483 440 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12484 440 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12485 440 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12486 88 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12487 352 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12488 : {
12489 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12490 0 : typdefault_is_literal = true; /* it needs quotes */
12491 : }
12492 : else
12493 352 : typdefault = NULL;
12494 440 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12495 :
12496 440 : if (dopt->binary_upgrade)
12497 48 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12498 48 : tyinfo->dobj.catId.oid,
12499 : true, /* force array type */
12500 : false); /* force multirange type */
12501 :
12502 440 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12503 440 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12504 :
12505 440 : appendPQExpBuffer(q,
12506 : "CREATE DOMAIN %s AS %s",
12507 : qualtypname,
12508 : typdefn);
12509 :
12510 : /* Print collation only if different from base type's collation */
12511 440 : if (OidIsValid(typcollation))
12512 : {
12513 : CollInfo *coll;
12514 :
12515 72 : coll = findCollationByOid(typcollation);
12516 72 : if (coll)
12517 72 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12518 : }
12519 :
12520 440 : if (typnotnull[0] == 't')
12521 48 : appendPQExpBufferStr(q, " NOT NULL");
12522 :
12523 440 : if (typdefault != NULL)
12524 : {
12525 88 : appendPQExpBufferStr(q, " DEFAULT ");
12526 88 : if (typdefault_is_literal)
12527 0 : appendStringLiteralAH(q, typdefault, fout);
12528 : else
12529 88 : appendPQExpBufferStr(q, typdefault);
12530 : }
12531 :
12532 440 : PQclear(res);
12533 :
12534 : /*
12535 : * Add any CHECK constraints for the domain
12536 : */
12537 736 : for (i = 0; i < tyinfo->nDomChecks; i++)
12538 : {
12539 296 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12540 :
12541 296 : if (!domcheck->separate)
12542 296 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12543 296 : fmtId(domcheck->dobj.name), domcheck->condef);
12544 : }
12545 :
12546 440 : appendPQExpBufferStr(q, ";\n");
12547 :
12548 440 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12549 :
12550 440 : if (dopt->binary_upgrade)
12551 48 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12552 : "DOMAIN", qtypname,
12553 48 : tyinfo->dobj.namespace->dobj.name);
12554 :
12555 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12556 440 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12557 440 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12558 : .namespace = tyinfo->dobj.namespace->dobj.name,
12559 : .owner = tyinfo->rolname,
12560 : .description = "DOMAIN",
12561 : .section = SECTION_PRE_DATA,
12562 : .createStmt = q->data,
12563 : .dropStmt = delq->data));
12564 :
12565 : /* Dump Domain Comments and Security Labels */
12566 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12567 0 : dumpComment(fout, "DOMAIN", qtypname,
12568 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12569 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12570 :
12571 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12572 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12573 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12574 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12575 :
12576 440 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12577 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12578 : qtypname, NULL,
12579 72 : tyinfo->dobj.namespace->dobj.name,
12580 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12581 :
12582 : /* Dump any per-constraint comments */
12583 736 : for (i = 0; i < tyinfo->nDomChecks; i++)
12584 : {
12585 296 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12586 296 : PQExpBuffer conprefix = createPQExpBuffer();
12587 :
12588 296 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12589 296 : fmtId(domcheck->dobj.name));
12590 :
12591 296 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12592 72 : dumpComment(fout, conprefix->data, qtypname,
12593 72 : tyinfo->dobj.namespace->dobj.name,
12594 72 : tyinfo->rolname,
12595 72 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12596 :
12597 296 : destroyPQExpBuffer(conprefix);
12598 : }
12599 :
12600 440 : destroyPQExpBuffer(q);
12601 440 : destroyPQExpBuffer(delq);
12602 440 : destroyPQExpBuffer(query);
12603 440 : free(qtypname);
12604 440 : free(qualtypname);
12605 440 : }
12606 :
12607 : /*
12608 : * dumpCompositeType
12609 : * writes out to fout the queries to recreate a user-defined stand-alone
12610 : * composite type
12611 : */
12612 : static void
12613 364 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12614 : {
12615 364 : DumpOptions *dopt = fout->dopt;
12616 364 : PQExpBuffer q = createPQExpBuffer();
12617 364 : PQExpBuffer dropped = createPQExpBuffer();
12618 364 : PQExpBuffer delq = createPQExpBuffer();
12619 364 : PQExpBuffer query = createPQExpBuffer();
12620 : PGresult *res;
12621 : char *qtypname;
12622 : char *qualtypname;
12623 : int ntups;
12624 : int i_attname;
12625 : int i_atttypdefn;
12626 : int i_attlen;
12627 : int i_attalign;
12628 : int i_attisdropped;
12629 : int i_attcollation;
12630 : int i;
12631 : int actual_atts;
12632 :
12633 364 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12634 : {
12635 : /*
12636 : * Set up query for type-specific details.
12637 : *
12638 : * Since we only want to dump COLLATE clauses for attributes whose
12639 : * collation is different from their type's default, we use a CASE
12640 : * here to suppress uninteresting attcollations cheaply. atttypid
12641 : * will be 0 for dropped columns; collation does not matter for those.
12642 : */
12643 124 : appendPQExpBufferStr(query,
12644 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12645 : "SELECT a.attname, a.attnum, "
12646 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12647 : "a.attlen, a.attalign, a.attisdropped, "
12648 : "CASE WHEN a.attcollation <> at.typcollation "
12649 : "THEN a.attcollation ELSE 0 END AS attcollation "
12650 : "FROM pg_catalog.pg_type ct "
12651 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12652 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12653 : "WHERE ct.oid = $1 "
12654 : "ORDER BY a.attnum");
12655 :
12656 124 : ExecuteSqlStatement(fout, query->data);
12657 :
12658 124 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12659 : }
12660 :
12661 364 : printfPQExpBuffer(query,
12662 : "EXECUTE dumpCompositeType('%u')",
12663 364 : tyinfo->dobj.catId.oid);
12664 :
12665 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12666 :
12667 364 : ntups = PQntuples(res);
12668 :
12669 364 : i_attname = PQfnumber(res, "attname");
12670 364 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12671 364 : i_attlen = PQfnumber(res, "attlen");
12672 364 : i_attalign = PQfnumber(res, "attalign");
12673 364 : i_attisdropped = PQfnumber(res, "attisdropped");
12674 364 : i_attcollation = PQfnumber(res, "attcollation");
12675 :
12676 364 : if (dopt->binary_upgrade)
12677 : {
12678 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12679 36 : tyinfo->dobj.catId.oid,
12680 : false, false);
12681 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12682 : }
12683 :
12684 364 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12685 364 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12686 :
12687 364 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12688 : qualtypname);
12689 :
12690 364 : actual_atts = 0;
12691 1180 : for (i = 0; i < ntups; i++)
12692 : {
12693 : char *attname;
12694 : char *atttypdefn;
12695 : char *attlen;
12696 : char *attalign;
12697 : bool attisdropped;
12698 : Oid attcollation;
12699 :
12700 816 : attname = PQgetvalue(res, i, i_attname);
12701 816 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12702 816 : attlen = PQgetvalue(res, i, i_attlen);
12703 816 : attalign = PQgetvalue(res, i, i_attalign);
12704 816 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12705 816 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12706 :
12707 816 : if (attisdropped && !dopt->binary_upgrade)
12708 24 : continue;
12709 :
12710 : /* Format properly if not first attr */
12711 792 : if (actual_atts++ > 0)
12712 428 : appendPQExpBufferChar(q, ',');
12713 792 : appendPQExpBufferStr(q, "\n\t");
12714 :
12715 792 : if (!attisdropped)
12716 : {
12717 788 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12718 :
12719 : /* Add collation if not default for the column type */
12720 788 : if (OidIsValid(attcollation))
12721 : {
12722 : CollInfo *coll;
12723 :
12724 0 : coll = findCollationByOid(attcollation);
12725 0 : if (coll)
12726 0 : appendPQExpBuffer(q, " COLLATE %s",
12727 0 : fmtQualifiedDumpable(coll));
12728 : }
12729 : }
12730 : else
12731 : {
12732 : /*
12733 : * This is a dropped attribute and we're in binary_upgrade mode.
12734 : * Insert a placeholder for it in the CREATE TYPE command, and set
12735 : * length and alignment with direct UPDATE to the catalogs
12736 : * afterwards. See similar code in dumpTableSchema().
12737 : */
12738 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12739 :
12740 : /* stash separately for insertion after the CREATE TYPE */
12741 4 : appendPQExpBufferStr(dropped,
12742 : "\n-- For binary upgrade, recreate dropped column.\n");
12743 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12744 : "SET attlen = %s, "
12745 : "attalign = '%s', attbyval = false\n"
12746 : "WHERE attname = ", attlen, attalign);
12747 4 : appendStringLiteralAH(dropped, attname, fout);
12748 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12749 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12750 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12751 :
12752 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12753 : qualtypname);
12754 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12755 : fmtId(attname));
12756 : }
12757 : }
12758 364 : appendPQExpBufferStr(q, "\n);\n");
12759 364 : appendPQExpBufferStr(q, dropped->data);
12760 :
12761 364 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12762 :
12763 364 : if (dopt->binary_upgrade)
12764 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12765 : "TYPE", qtypname,
12766 36 : tyinfo->dobj.namespace->dobj.name);
12767 :
12768 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12769 330 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12770 330 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12771 : .namespace = tyinfo->dobj.namespace->dobj.name,
12772 : .owner = tyinfo->rolname,
12773 : .description = "TYPE",
12774 : .section = SECTION_PRE_DATA,
12775 : .createStmt = q->data,
12776 : .dropStmt = delq->data));
12777 :
12778 :
12779 : /* Dump Type Comments and Security Labels */
12780 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12781 72 : dumpComment(fout, "TYPE", qtypname,
12782 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12783 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12784 :
12785 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12786 0 : dumpSecLabel(fout, "TYPE", qtypname,
12787 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12788 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12789 :
12790 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12791 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12792 : qtypname, NULL,
12793 36 : tyinfo->dobj.namespace->dobj.name,
12794 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
12795 :
12796 : /* Dump any per-column comments */
12797 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12798 72 : dumpCompositeTypeColComments(fout, tyinfo, res);
12799 :
12800 364 : PQclear(res);
12801 364 : destroyPQExpBuffer(q);
12802 364 : destroyPQExpBuffer(dropped);
12803 364 : destroyPQExpBuffer(delq);
12804 364 : destroyPQExpBuffer(query);
12805 364 : free(qtypname);
12806 364 : free(qualtypname);
12807 364 : }
12808 :
12809 : /*
12810 : * dumpCompositeTypeColComments
12811 : * writes out to fout the queries to recreate comments on the columns of
12812 : * a user-defined stand-alone composite type.
12813 : *
12814 : * The caller has already made a query to collect the names and attnums
12815 : * of the type's columns, so we just pass that result into here rather
12816 : * than reading them again.
12817 : */
12818 : static void
12819 72 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12820 : PGresult *res)
12821 : {
12822 : CommentItem *comments;
12823 : int ncomments;
12824 : PQExpBuffer query;
12825 : PQExpBuffer target;
12826 : int i;
12827 : int ntups;
12828 : int i_attname;
12829 : int i_attnum;
12830 : int i_attisdropped;
12831 :
12832 : /* do nothing, if --no-comments is supplied */
12833 72 : if (fout->dopt->no_comments)
12834 0 : return;
12835 :
12836 : /* Search for comments associated with type's pg_class OID */
12837 72 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12838 : &comments);
12839 :
12840 : /* If no comments exist, we're done */
12841 72 : if (ncomments <= 0)
12842 0 : return;
12843 :
12844 : /* Build COMMENT ON statements */
12845 72 : query = createPQExpBuffer();
12846 72 : target = createPQExpBuffer();
12847 :
12848 72 : ntups = PQntuples(res);
12849 72 : i_attnum = PQfnumber(res, "attnum");
12850 72 : i_attname = PQfnumber(res, "attname");
12851 72 : i_attisdropped = PQfnumber(res, "attisdropped");
12852 144 : while (ncomments > 0)
12853 : {
12854 : const char *attname;
12855 :
12856 72 : attname = NULL;
12857 72 : for (i = 0; i < ntups; i++)
12858 : {
12859 72 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12860 72 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12861 : {
12862 72 : attname = PQgetvalue(res, i, i_attname);
12863 72 : break;
12864 : }
12865 : }
12866 72 : if (attname) /* just in case we don't find it */
12867 : {
12868 72 : const char *descr = comments->descr;
12869 :
12870 72 : resetPQExpBuffer(target);
12871 72 : appendPQExpBuffer(target, "COLUMN %s.",
12872 72 : fmtId(tyinfo->dobj.name));
12873 72 : appendPQExpBufferStr(target, fmtId(attname));
12874 :
12875 72 : resetPQExpBuffer(query);
12876 72 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12877 72 : fmtQualifiedDumpable(tyinfo));
12878 72 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12879 72 : appendStringLiteralAH(query, descr, fout);
12880 72 : appendPQExpBufferStr(query, ";\n");
12881 :
12882 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12883 72 : ARCHIVE_OPTS(.tag = target->data,
12884 : .namespace = tyinfo->dobj.namespace->dobj.name,
12885 : .owner = tyinfo->rolname,
12886 : .description = "COMMENT",
12887 : .section = SECTION_NONE,
12888 : .createStmt = query->data,
12889 : .deps = &(tyinfo->dobj.dumpId),
12890 : .nDeps = 1));
12891 : }
12892 :
12893 72 : comments++;
12894 72 : ncomments--;
12895 : }
12896 :
12897 72 : destroyPQExpBuffer(query);
12898 72 : destroyPQExpBuffer(target);
12899 : }
12900 :
12901 : /*
12902 : * dumpShellType
12903 : * writes out to fout the queries to create a shell type
12904 : *
12905 : * We dump a shell definition in advance of the I/O functions for the type.
12906 : */
12907 : static void
12908 196 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12909 : {
12910 196 : DumpOptions *dopt = fout->dopt;
12911 : PQExpBuffer q;
12912 :
12913 : /* Do nothing if not dumping schema */
12914 196 : if (!dopt->dumpSchema)
12915 12 : return;
12916 :
12917 184 : q = createPQExpBuffer();
12918 :
12919 : /*
12920 : * Note the lack of a DROP command for the shell type; any required DROP
12921 : * is driven off the base type entry, instead. This interacts with
12922 : * _printTocEntry()'s use of the presence of a DROP command to decide
12923 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12924 : * the shell type's owner immediately on creation; that should happen only
12925 : * after it's filled in, otherwise the backend complains.
12926 : */
12927 :
12928 184 : if (dopt->binary_upgrade)
12929 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12930 16 : stinfo->baseType->dobj.catId.oid,
12931 : false, false);
12932 :
12933 184 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12934 184 : fmtQualifiedDumpable(stinfo));
12935 :
12936 184 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12937 184 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12938 184 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12939 : .namespace = stinfo->dobj.namespace->dobj.name,
12940 : .owner = stinfo->baseType->rolname,
12941 : .description = "SHELL TYPE",
12942 : .section = SECTION_PRE_DATA,
12943 : .createStmt = q->data));
12944 :
12945 184 : destroyPQExpBuffer(q);
12946 : }
12947 :
12948 : /*
12949 : * dumpProcLang
12950 : * writes out to fout the queries to recreate a user-defined
12951 : * procedural language
12952 : */
12953 : static void
12954 180 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12955 : {
12956 180 : DumpOptions *dopt = fout->dopt;
12957 : PQExpBuffer defqry;
12958 : PQExpBuffer delqry;
12959 : bool useParams;
12960 : char *qlanname;
12961 : FuncInfo *funcInfo;
12962 180 : FuncInfo *inlineInfo = NULL;
12963 180 : FuncInfo *validatorInfo = NULL;
12964 :
12965 : /* Do nothing if not dumping schema */
12966 180 : if (!dopt->dumpSchema)
12967 26 : return;
12968 :
12969 : /*
12970 : * Try to find the support function(s). It is not an error if we don't
12971 : * find them --- if the functions are in the pg_catalog schema, as is
12972 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12973 : * we will emit a parameterless CREATE LANGUAGE command, which will
12974 : * require PL template knowledge in the backend to reload.)
12975 : */
12976 :
12977 154 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12978 154 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12979 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12980 :
12981 154 : if (OidIsValid(plang->laninline))
12982 : {
12983 84 : inlineInfo = findFuncByOid(plang->laninline);
12984 84 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12985 2 : inlineInfo = NULL;
12986 : }
12987 :
12988 154 : if (OidIsValid(plang->lanvalidator))
12989 : {
12990 84 : validatorInfo = findFuncByOid(plang->lanvalidator);
12991 84 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12992 2 : validatorInfo = NULL;
12993 : }
12994 :
12995 : /*
12996 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12997 : * parameters. Otherwise, we'll write a parameterless command, which will
12998 : * be interpreted as CREATE EXTENSION.
12999 : */
13000 68 : useParams = (funcInfo != NULL &&
13001 290 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13002 68 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13003 :
13004 154 : defqry = createPQExpBuffer();
13005 154 : delqry = createPQExpBuffer();
13006 :
13007 154 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13008 :
13009 154 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13010 : qlanname);
13011 :
13012 154 : if (useParams)
13013 : {
13014 68 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13015 68 : plang->lanpltrusted ? "TRUSTED " : "",
13016 : qlanname);
13017 68 : appendPQExpBuffer(defqry, " HANDLER %s",
13018 68 : fmtQualifiedDumpable(funcInfo));
13019 68 : if (OidIsValid(plang->laninline))
13020 0 : appendPQExpBuffer(defqry, " INLINE %s",
13021 0 : fmtQualifiedDumpable(inlineInfo));
13022 68 : if (OidIsValid(plang->lanvalidator))
13023 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13024 0 : fmtQualifiedDumpable(validatorInfo));
13025 : }
13026 : else
13027 : {
13028 : /*
13029 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13030 : * command will not fail if the language is preinstalled in the target
13031 : * database.
13032 : *
13033 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13034 : * EXISTS; perhaps we should emit that instead? But it might just add
13035 : * confusion.
13036 : */
13037 86 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13038 : qlanname);
13039 : }
13040 154 : appendPQExpBufferStr(defqry, ";\n");
13041 :
13042 154 : if (dopt->binary_upgrade)
13043 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13044 : "LANGUAGE", qlanname, NULL);
13045 :
13046 154 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13047 70 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13048 70 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13049 : .owner = plang->lanowner,
13050 : .description = "PROCEDURAL LANGUAGE",
13051 : .section = SECTION_PRE_DATA,
13052 : .createStmt = defqry->data,
13053 : .dropStmt = delqry->data,
13054 : ));
13055 :
13056 : /* Dump Proc Lang Comments and Security Labels */
13057 154 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13058 0 : dumpComment(fout, "LANGUAGE", qlanname,
13059 0 : NULL, plang->lanowner,
13060 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13061 :
13062 154 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13063 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13064 0 : NULL, plang->lanowner,
13065 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13066 :
13067 154 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13068 84 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13069 : qlanname, NULL, NULL,
13070 84 : NULL, plang->lanowner, &plang->dacl);
13071 :
13072 154 : free(qlanname);
13073 :
13074 154 : destroyPQExpBuffer(defqry);
13075 154 : destroyPQExpBuffer(delqry);
13076 : }
13077 :
13078 : /*
13079 : * format_function_arguments: generate function name and argument list
13080 : *
13081 : * This is used when we can rely on pg_get_function_arguments to format
13082 : * the argument list. Note, however, that pg_get_function_arguments
13083 : * does not special-case zero-argument aggregates.
13084 : */
13085 : static char *
13086 12224 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13087 : {
13088 : PQExpBufferData fn;
13089 :
13090 12224 : initPQExpBuffer(&fn);
13091 12224 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13092 12224 : if (is_agg && finfo->nargs == 0)
13093 256 : appendPQExpBufferStr(&fn, "(*)");
13094 : else
13095 11968 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13096 12224 : return fn.data;
13097 : }
13098 :
13099 : /*
13100 : * format_function_signature: generate function name and argument list
13101 : *
13102 : * Only a minimal list of input argument types is generated; this is
13103 : * sufficient to reference the function, but not to define it.
13104 : *
13105 : * If honor_quotes is false then the function name is never quoted.
13106 : * This is appropriate for use in TOC tags, but not in SQL commands.
13107 : */
13108 : static char *
13109 6370 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13110 : {
13111 : PQExpBufferData fn;
13112 : int j;
13113 :
13114 6370 : initPQExpBuffer(&fn);
13115 6370 : if (honor_quotes)
13116 1118 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13117 : else
13118 5252 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13119 11838 : for (j = 0; j < finfo->nargs; j++)
13120 : {
13121 5468 : if (j > 0)
13122 1348 : appendPQExpBufferStr(&fn, ", ");
13123 :
13124 5468 : appendPQExpBufferStr(&fn,
13125 5468 : getFormattedTypeName(fout, finfo->argtypes[j],
13126 : zeroIsError));
13127 : }
13128 6370 : appendPQExpBufferChar(&fn, ')');
13129 6370 : return fn.data;
13130 : }
13131 :
13132 :
13133 : /*
13134 : * dumpFunc:
13135 : * dump out one function
13136 : */
13137 : static void
13138 5376 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13139 : {
13140 5376 : DumpOptions *dopt = fout->dopt;
13141 : PQExpBuffer query;
13142 : PQExpBuffer q;
13143 : PQExpBuffer delqry;
13144 : PQExpBuffer asPart;
13145 : PGresult *res;
13146 : char *funcsig; /* identity signature */
13147 5376 : char *funcfullsig = NULL; /* full signature */
13148 : char *funcsig_tag;
13149 : char *qual_funcsig;
13150 : char *proretset;
13151 : char *prosrc;
13152 : char *probin;
13153 : char *prosqlbody;
13154 : char *funcargs;
13155 : char *funciargs;
13156 : char *funcresult;
13157 : char *protrftypes;
13158 : char *prokind;
13159 : char *provolatile;
13160 : char *proisstrict;
13161 : char *prosecdef;
13162 : char *proleakproof;
13163 : char *proconfig;
13164 : char *procost;
13165 : char *prorows;
13166 : char *prosupport;
13167 : char *proparallel;
13168 : char *lanname;
13169 5376 : char **configitems = NULL;
13170 5376 : int nconfigitems = 0;
13171 : const char *keyword;
13172 :
13173 : /* Do nothing if not dumping schema */
13174 5376 : if (!dopt->dumpSchema)
13175 124 : return;
13176 :
13177 5252 : query = createPQExpBuffer();
13178 5252 : q = createPQExpBuffer();
13179 5252 : delqry = createPQExpBuffer();
13180 5252 : asPart = createPQExpBuffer();
13181 :
13182 5252 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13183 : {
13184 : /* Set up query for function-specific details */
13185 150 : appendPQExpBufferStr(query,
13186 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13187 :
13188 150 : appendPQExpBufferStr(query,
13189 : "SELECT\n"
13190 : "proretset,\n"
13191 : "prosrc,\n"
13192 : "probin,\n"
13193 : "provolatile,\n"
13194 : "proisstrict,\n"
13195 : "prosecdef,\n"
13196 : "lanname,\n"
13197 : "proconfig,\n"
13198 : "procost,\n"
13199 : "prorows,\n"
13200 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13201 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13202 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13203 : "proleakproof,\n");
13204 :
13205 150 : if (fout->remoteVersion >= 90500)
13206 150 : appendPQExpBufferStr(query,
13207 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13208 : else
13209 0 : appendPQExpBufferStr(query,
13210 : "NULL AS protrftypes,\n");
13211 :
13212 150 : if (fout->remoteVersion >= 90600)
13213 150 : appendPQExpBufferStr(query,
13214 : "proparallel,\n");
13215 : else
13216 0 : appendPQExpBufferStr(query,
13217 : "'u' AS proparallel,\n");
13218 :
13219 150 : if (fout->remoteVersion >= 110000)
13220 150 : appendPQExpBufferStr(query,
13221 : "prokind,\n");
13222 : else
13223 0 : appendPQExpBufferStr(query,
13224 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13225 :
13226 150 : if (fout->remoteVersion >= 120000)
13227 150 : appendPQExpBufferStr(query,
13228 : "prosupport,\n");
13229 : else
13230 0 : appendPQExpBufferStr(query,
13231 : "'-' AS prosupport,\n");
13232 :
13233 150 : if (fout->remoteVersion >= 140000)
13234 150 : appendPQExpBufferStr(query,
13235 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13236 : else
13237 0 : appendPQExpBufferStr(query,
13238 : "NULL AS prosqlbody\n");
13239 :
13240 150 : appendPQExpBufferStr(query,
13241 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13242 : "WHERE p.oid = $1 "
13243 : "AND l.oid = p.prolang");
13244 :
13245 150 : ExecuteSqlStatement(fout, query->data);
13246 :
13247 150 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13248 : }
13249 :
13250 5252 : printfPQExpBuffer(query,
13251 : "EXECUTE dumpFunc('%u')",
13252 5252 : finfo->dobj.catId.oid);
13253 :
13254 5252 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13255 :
13256 5252 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13257 5252 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13258 : {
13259 5130 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13260 5130 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13261 5130 : prosqlbody = NULL;
13262 : }
13263 : else
13264 : {
13265 122 : prosrc = NULL;
13266 122 : probin = NULL;
13267 122 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13268 : }
13269 5252 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13270 5252 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13271 5252 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13272 5252 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13273 5252 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13274 5252 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13275 5252 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13276 5252 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13277 5252 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13278 5252 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13279 5252 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13280 5252 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13281 5252 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13282 5252 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13283 5252 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13284 :
13285 : /*
13286 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13287 : * is used.
13288 : */
13289 5252 : if (prosqlbody)
13290 : {
13291 122 : appendPQExpBufferStr(asPart, prosqlbody);
13292 : }
13293 5130 : else if (probin[0] != '\0')
13294 : {
13295 392 : appendPQExpBufferStr(asPart, "AS ");
13296 392 : appendStringLiteralAH(asPart, probin, fout);
13297 392 : if (prosrc[0] != '\0')
13298 : {
13299 392 : appendPQExpBufferStr(asPart, ", ");
13300 :
13301 : /*
13302 : * where we have bin, use dollar quoting if allowed and src
13303 : * contains quote or backslash; else use regular quoting.
13304 : */
13305 392 : if (dopt->disable_dollar_quoting ||
13306 392 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13307 392 : appendStringLiteralAH(asPart, prosrc, fout);
13308 : else
13309 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13310 : }
13311 : }
13312 : else
13313 : {
13314 4738 : appendPQExpBufferStr(asPart, "AS ");
13315 : /* with no bin, dollar quote src unconditionally if allowed */
13316 4738 : if (dopt->disable_dollar_quoting)
13317 0 : appendStringLiteralAH(asPart, prosrc, fout);
13318 : else
13319 4738 : appendStringLiteralDQ(asPart, prosrc, NULL);
13320 : }
13321 :
13322 5252 : if (*proconfig)
13323 : {
13324 48 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13325 0 : pg_fatal("could not parse %s array", "proconfig");
13326 : }
13327 : else
13328 : {
13329 5204 : configitems = NULL;
13330 5204 : nconfigitems = 0;
13331 : }
13332 :
13333 5252 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13334 5252 : funcsig = format_function_arguments(finfo, funciargs, false);
13335 :
13336 5252 : funcsig_tag = format_function_signature(fout, finfo, false);
13337 :
13338 5252 : qual_funcsig = psprintf("%s.%s",
13339 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13340 : funcsig);
13341 :
13342 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13343 264 : keyword = "PROCEDURE";
13344 : else
13345 4988 : keyword = "FUNCTION"; /* works for window functions too */
13346 :
13347 5252 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13348 : keyword, qual_funcsig);
13349 :
13350 10504 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13351 : keyword,
13352 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13353 : funcfullsig ? funcfullsig :
13354 : funcsig);
13355 :
13356 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13357 : /* no result type to output */ ;
13358 4988 : else if (funcresult)
13359 4988 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13360 : else
13361 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13362 0 : (proretset[0] == 't') ? "SETOF " : "",
13363 0 : getFormattedTypeName(fout, finfo->prorettype,
13364 : zeroIsError));
13365 :
13366 5252 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13367 :
13368 5252 : if (*protrftypes)
13369 : {
13370 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13371 : int i;
13372 :
13373 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13374 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13375 0 : for (i = 0; typeids[i]; i++)
13376 : {
13377 0 : if (i != 0)
13378 0 : appendPQExpBufferStr(q, ", ");
13379 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13380 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13381 : }
13382 :
13383 0 : free(typeids);
13384 : }
13385 :
13386 5252 : if (prokind[0] == PROKIND_WINDOW)
13387 16 : appendPQExpBufferStr(q, " WINDOW");
13388 :
13389 5252 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13390 : {
13391 1054 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13392 988 : appendPQExpBufferStr(q, " IMMUTABLE");
13393 66 : else if (provolatile[0] == PROVOLATILE_STABLE)
13394 66 : appendPQExpBufferStr(q, " STABLE");
13395 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13396 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13397 : finfo->dobj.name);
13398 : }
13399 :
13400 5252 : if (proisstrict[0] == 't')
13401 1030 : appendPQExpBufferStr(q, " STRICT");
13402 :
13403 5252 : if (prosecdef[0] == 't')
13404 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13405 :
13406 5252 : if (proleakproof[0] == 't')
13407 32 : appendPQExpBufferStr(q, " LEAKPROOF");
13408 :
13409 : /*
13410 : * COST and ROWS are emitted only if present and not default, so as not to
13411 : * break backwards-compatibility of the dump without need. Keep this code
13412 : * in sync with the defaults in functioncmds.c.
13413 : */
13414 5252 : if (strcmp(procost, "0") != 0)
13415 : {
13416 5252 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13417 : {
13418 : /* default cost is 1 */
13419 996 : if (strcmp(procost, "1") != 0)
13420 0 : appendPQExpBuffer(q, " COST %s", procost);
13421 : }
13422 : else
13423 : {
13424 : /* default cost is 100 */
13425 4256 : if (strcmp(procost, "100") != 0)
13426 18 : appendPQExpBuffer(q, " COST %s", procost);
13427 : }
13428 : }
13429 5252 : if (proretset[0] == 't' &&
13430 568 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13431 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13432 :
13433 5252 : if (strcmp(prosupport, "-") != 0)
13434 : {
13435 : /* We rely on regprocout to provide quoting and qualification */
13436 104 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13437 : }
13438 :
13439 5252 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13440 : {
13441 302 : if (proparallel[0] == PROPARALLEL_SAFE)
13442 286 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13443 16 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13444 16 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13445 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13446 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13447 : finfo->dobj.name);
13448 : }
13449 :
13450 5364 : for (int i = 0; i < nconfigitems; i++)
13451 : {
13452 : /* we feel free to scribble on configitems[] here */
13453 112 : char *configitem = configitems[i];
13454 : char *pos;
13455 :
13456 112 : pos = strchr(configitem, '=');
13457 112 : if (pos == NULL)
13458 0 : continue;
13459 112 : *pos++ = '\0';
13460 112 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13461 :
13462 : /*
13463 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13464 : * by flatten_set_variable_args() before they were put into the
13465 : * proconfig array. However, because the quoting rules used there
13466 : * aren't exactly like SQL's, we have to break the list value apart
13467 : * and then quote the elements as string literals. (The elements may
13468 : * be double-quoted as-is, but we can't just feed them to the SQL
13469 : * parser; it would do the wrong thing with elements that are
13470 : * zero-length or longer than NAMEDATALEN.)
13471 : *
13472 : * Variables that are not so marked should just be emitted as simple
13473 : * string literals. If the variable is not known to
13474 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13475 : * to use GUC_LIST_QUOTE for extension variables.
13476 : */
13477 112 : if (variable_is_guc_list_quote(configitem))
13478 : {
13479 : char **namelist;
13480 : char **nameptr;
13481 :
13482 : /* Parse string into list of identifiers */
13483 : /* this shouldn't fail really */
13484 32 : if (SplitGUCList(pos, ',', &namelist))
13485 : {
13486 112 : for (nameptr = namelist; *nameptr; nameptr++)
13487 : {
13488 80 : if (nameptr != namelist)
13489 48 : appendPQExpBufferStr(q, ", ");
13490 80 : appendStringLiteralAH(q, *nameptr, fout);
13491 : }
13492 : }
13493 32 : pg_free(namelist);
13494 : }
13495 : else
13496 80 : appendStringLiteralAH(q, pos, fout);
13497 : }
13498 :
13499 5252 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13500 :
13501 5252 : append_depends_on_extension(fout, q, &finfo->dobj,
13502 : "pg_catalog.pg_proc", keyword,
13503 : qual_funcsig);
13504 :
13505 5252 : if (dopt->binary_upgrade)
13506 576 : binary_upgrade_extension_member(q, &finfo->dobj,
13507 : keyword, funcsig,
13508 576 : finfo->dobj.namespace->dobj.name);
13509 :
13510 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13511 5044 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13512 5044 : ARCHIVE_OPTS(.tag = funcsig_tag,
13513 : .namespace = finfo->dobj.namespace->dobj.name,
13514 : .owner = finfo->rolname,
13515 : .description = keyword,
13516 : .section = finfo->postponed_def ?
13517 : SECTION_POST_DATA : SECTION_PRE_DATA,
13518 : .createStmt = q->data,
13519 : .dropStmt = delqry->data));
13520 :
13521 : /* Dump Function Comments and Security Labels */
13522 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13523 18 : dumpComment(fout, keyword, funcsig,
13524 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13525 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13526 :
13527 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13528 0 : dumpSecLabel(fout, keyword, funcsig,
13529 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13530 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13531 :
13532 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13533 230 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13534 : funcsig, NULL,
13535 230 : finfo->dobj.namespace->dobj.name,
13536 230 : NULL, finfo->rolname, &finfo->dacl);
13537 :
13538 5252 : PQclear(res);
13539 :
13540 5252 : destroyPQExpBuffer(query);
13541 5252 : destroyPQExpBuffer(q);
13542 5252 : destroyPQExpBuffer(delqry);
13543 5252 : destroyPQExpBuffer(asPart);
13544 5252 : free(funcsig);
13545 5252 : free(funcfullsig);
13546 5252 : free(funcsig_tag);
13547 5252 : free(qual_funcsig);
13548 5252 : free(configitems);
13549 : }
13550 :
13551 :
13552 : /*
13553 : * Dump a user-defined cast
13554 : */
13555 : static void
13556 178 : dumpCast(Archive *fout, const CastInfo *cast)
13557 : {
13558 178 : DumpOptions *dopt = fout->dopt;
13559 : PQExpBuffer defqry;
13560 : PQExpBuffer delqry;
13561 : PQExpBuffer labelq;
13562 : PQExpBuffer castargs;
13563 178 : FuncInfo *funcInfo = NULL;
13564 : const char *sourceType;
13565 : const char *targetType;
13566 :
13567 : /* Do nothing if not dumping schema */
13568 178 : if (!dopt->dumpSchema)
13569 12 : return;
13570 :
13571 : /* Cannot dump if we don't have the cast function's info */
13572 166 : if (OidIsValid(cast->castfunc))
13573 : {
13574 86 : funcInfo = findFuncByOid(cast->castfunc);
13575 86 : if (funcInfo == NULL)
13576 0 : pg_fatal("could not find function definition for function with OID %u",
13577 : cast->castfunc);
13578 : }
13579 :
13580 166 : defqry = createPQExpBuffer();
13581 166 : delqry = createPQExpBuffer();
13582 166 : labelq = createPQExpBuffer();
13583 166 : castargs = createPQExpBuffer();
13584 :
13585 166 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13586 166 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13587 166 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13588 : sourceType, targetType);
13589 :
13590 166 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13591 : sourceType, targetType);
13592 :
13593 166 : switch (cast->castmethod)
13594 : {
13595 80 : case COERCION_METHOD_BINARY:
13596 80 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13597 80 : break;
13598 0 : case COERCION_METHOD_INOUT:
13599 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13600 0 : break;
13601 86 : case COERCION_METHOD_FUNCTION:
13602 86 : if (funcInfo)
13603 : {
13604 86 : char *fsig = format_function_signature(fout, funcInfo, true);
13605 :
13606 : /*
13607 : * Always qualify the function name (format_function_signature
13608 : * won't qualify it).
13609 : */
13610 86 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13611 86 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13612 86 : free(fsig);
13613 : }
13614 : else
13615 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13616 86 : break;
13617 0 : default:
13618 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13619 : }
13620 :
13621 166 : if (cast->castcontext == 'a')
13622 70 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13623 96 : else if (cast->castcontext == 'i')
13624 32 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13625 166 : appendPQExpBufferStr(defqry, ";\n");
13626 :
13627 166 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13628 : sourceType, targetType);
13629 :
13630 166 : appendPQExpBuffer(castargs, "(%s AS %s)",
13631 : sourceType, targetType);
13632 :
13633 166 : if (dopt->binary_upgrade)
13634 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13635 14 : "CAST", castargs->data, NULL);
13636 :
13637 166 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13638 166 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13639 166 : ARCHIVE_OPTS(.tag = labelq->data,
13640 : .description = "CAST",
13641 : .section = SECTION_PRE_DATA,
13642 : .createStmt = defqry->data,
13643 : .dropStmt = delqry->data));
13644 :
13645 : /* Dump Cast Comments */
13646 166 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13647 0 : dumpComment(fout, "CAST", castargs->data,
13648 : NULL, "",
13649 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13650 :
13651 166 : destroyPQExpBuffer(defqry);
13652 166 : destroyPQExpBuffer(delqry);
13653 166 : destroyPQExpBuffer(labelq);
13654 166 : destroyPQExpBuffer(castargs);
13655 : }
13656 :
13657 : /*
13658 : * Dump a transform
13659 : */
13660 : static void
13661 98 : dumpTransform(Archive *fout, const TransformInfo *transform)
13662 : {
13663 98 : DumpOptions *dopt = fout->dopt;
13664 : PQExpBuffer defqry;
13665 : PQExpBuffer delqry;
13666 : PQExpBuffer labelq;
13667 : PQExpBuffer transformargs;
13668 98 : FuncInfo *fromsqlFuncInfo = NULL;
13669 98 : FuncInfo *tosqlFuncInfo = NULL;
13670 : char *lanname;
13671 : const char *transformType;
13672 :
13673 : /* Do nothing if not dumping schema */
13674 98 : if (!dopt->dumpSchema)
13675 12 : return;
13676 :
13677 : /* Cannot dump if we don't have the transform functions' info */
13678 86 : if (OidIsValid(transform->trffromsql))
13679 : {
13680 86 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13681 86 : if (fromsqlFuncInfo == NULL)
13682 0 : pg_fatal("could not find function definition for function with OID %u",
13683 : transform->trffromsql);
13684 : }
13685 86 : if (OidIsValid(transform->trftosql))
13686 : {
13687 86 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13688 86 : if (tosqlFuncInfo == NULL)
13689 0 : pg_fatal("could not find function definition for function with OID %u",
13690 : transform->trftosql);
13691 : }
13692 :
13693 86 : defqry = createPQExpBuffer();
13694 86 : delqry = createPQExpBuffer();
13695 86 : labelq = createPQExpBuffer();
13696 86 : transformargs = createPQExpBuffer();
13697 :
13698 86 : lanname = get_language_name(fout, transform->trflang);
13699 86 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13700 :
13701 86 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13702 : transformType, lanname);
13703 :
13704 86 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13705 : transformType, lanname);
13706 :
13707 86 : if (!transform->trffromsql && !transform->trftosql)
13708 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13709 :
13710 86 : if (transform->trffromsql)
13711 : {
13712 86 : if (fromsqlFuncInfo)
13713 : {
13714 86 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13715 :
13716 : /*
13717 : * Always qualify the function name (format_function_signature
13718 : * won't qualify it).
13719 : */
13720 86 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13721 86 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13722 86 : free(fsig);
13723 : }
13724 : else
13725 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13726 : }
13727 :
13728 86 : if (transform->trftosql)
13729 : {
13730 86 : if (transform->trffromsql)
13731 86 : appendPQExpBufferStr(defqry, ", ");
13732 :
13733 86 : if (tosqlFuncInfo)
13734 : {
13735 86 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13736 :
13737 : /*
13738 : * Always qualify the function name (format_function_signature
13739 : * won't qualify it).
13740 : */
13741 86 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13742 86 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13743 86 : free(fsig);
13744 : }
13745 : else
13746 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13747 : }
13748 :
13749 86 : appendPQExpBufferStr(defqry, ");\n");
13750 :
13751 86 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13752 : transformType, lanname);
13753 :
13754 86 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13755 : transformType, lanname);
13756 :
13757 86 : if (dopt->binary_upgrade)
13758 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13759 4 : "TRANSFORM", transformargs->data, NULL);
13760 :
13761 86 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13762 86 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13763 86 : ARCHIVE_OPTS(.tag = labelq->data,
13764 : .description = "TRANSFORM",
13765 : .section = SECTION_PRE_DATA,
13766 : .createStmt = defqry->data,
13767 : .dropStmt = delqry->data,
13768 : .deps = transform->dobj.dependencies,
13769 : .nDeps = transform->dobj.nDeps));
13770 :
13771 : /* Dump Transform Comments */
13772 86 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13773 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13774 : NULL, "",
13775 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
13776 :
13777 86 : free(lanname);
13778 86 : destroyPQExpBuffer(defqry);
13779 86 : destroyPQExpBuffer(delqry);
13780 86 : destroyPQExpBuffer(labelq);
13781 86 : destroyPQExpBuffer(transformargs);
13782 : }
13783 :
13784 :
13785 : /*
13786 : * dumpOpr
13787 : * write out a single operator definition
13788 : */
13789 : static void
13790 5098 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
13791 : {
13792 5098 : DumpOptions *dopt = fout->dopt;
13793 : PQExpBuffer query;
13794 : PQExpBuffer q;
13795 : PQExpBuffer delq;
13796 : PQExpBuffer oprid;
13797 : PQExpBuffer details;
13798 : PGresult *res;
13799 : int i_oprkind;
13800 : int i_oprcode;
13801 : int i_oprleft;
13802 : int i_oprright;
13803 : int i_oprcom;
13804 : int i_oprnegate;
13805 : int i_oprrest;
13806 : int i_oprjoin;
13807 : int i_oprcanmerge;
13808 : int i_oprcanhash;
13809 : char *oprkind;
13810 : char *oprcode;
13811 : char *oprleft;
13812 : char *oprright;
13813 : char *oprcom;
13814 : char *oprnegate;
13815 : char *oprrest;
13816 : char *oprjoin;
13817 : char *oprcanmerge;
13818 : char *oprcanhash;
13819 : char *oprregproc;
13820 : char *oprref;
13821 :
13822 : /* Do nothing if not dumping schema */
13823 5098 : if (!dopt->dumpSchema)
13824 12 : return;
13825 :
13826 : /*
13827 : * some operators are invalid because they were the result of user
13828 : * defining operators before commutators exist
13829 : */
13830 5086 : if (!OidIsValid(oprinfo->oprcode))
13831 44 : return;
13832 :
13833 5042 : query = createPQExpBuffer();
13834 5042 : q = createPQExpBuffer();
13835 5042 : delq = createPQExpBuffer();
13836 5042 : oprid = createPQExpBuffer();
13837 5042 : details = createPQExpBuffer();
13838 :
13839 5042 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13840 : {
13841 : /* Set up query for operator-specific details */
13842 94 : appendPQExpBufferStr(query,
13843 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13844 : "SELECT oprkind, "
13845 : "oprcode::pg_catalog.regprocedure, "
13846 : "oprleft::pg_catalog.regtype, "
13847 : "oprright::pg_catalog.regtype, "
13848 : "oprcom, "
13849 : "oprnegate, "
13850 : "oprrest::pg_catalog.regprocedure, "
13851 : "oprjoin::pg_catalog.regprocedure, "
13852 : "oprcanmerge, oprcanhash "
13853 : "FROM pg_catalog.pg_operator "
13854 : "WHERE oid = $1");
13855 :
13856 94 : ExecuteSqlStatement(fout, query->data);
13857 :
13858 94 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13859 : }
13860 :
13861 5042 : printfPQExpBuffer(query,
13862 : "EXECUTE dumpOpr('%u')",
13863 5042 : oprinfo->dobj.catId.oid);
13864 :
13865 5042 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13866 :
13867 5042 : i_oprkind = PQfnumber(res, "oprkind");
13868 5042 : i_oprcode = PQfnumber(res, "oprcode");
13869 5042 : i_oprleft = PQfnumber(res, "oprleft");
13870 5042 : i_oprright = PQfnumber(res, "oprright");
13871 5042 : i_oprcom = PQfnumber(res, "oprcom");
13872 5042 : i_oprnegate = PQfnumber(res, "oprnegate");
13873 5042 : i_oprrest = PQfnumber(res, "oprrest");
13874 5042 : i_oprjoin = PQfnumber(res, "oprjoin");
13875 5042 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13876 5042 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13877 :
13878 5042 : oprkind = PQgetvalue(res, 0, i_oprkind);
13879 5042 : oprcode = PQgetvalue(res, 0, i_oprcode);
13880 5042 : oprleft = PQgetvalue(res, 0, i_oprleft);
13881 5042 : oprright = PQgetvalue(res, 0, i_oprright);
13882 5042 : oprcom = PQgetvalue(res, 0, i_oprcom);
13883 5042 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13884 5042 : oprrest = PQgetvalue(res, 0, i_oprrest);
13885 5042 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13886 5042 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13887 5042 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13888 :
13889 : /* In PG14 upwards postfix operator support does not exist anymore. */
13890 5042 : if (strcmp(oprkind, "r") == 0)
13891 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13892 : oprcode);
13893 :
13894 5042 : oprregproc = convertRegProcReference(oprcode);
13895 5042 : if (oprregproc)
13896 : {
13897 5042 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13898 5042 : free(oprregproc);
13899 : }
13900 :
13901 5042 : appendPQExpBuffer(oprid, "%s (",
13902 5042 : oprinfo->dobj.name);
13903 :
13904 : /*
13905 : * right unary means there's a left arg and left unary means there's a
13906 : * right arg. (Although the "r" case is dead code for PG14 and later,
13907 : * continue to support it in case we're dumping from an old server.)
13908 : */
13909 5042 : if (strcmp(oprkind, "r") == 0 ||
13910 5042 : strcmp(oprkind, "b") == 0)
13911 : {
13912 4732 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13913 4732 : appendPQExpBufferStr(oprid, oprleft);
13914 : }
13915 : else
13916 310 : appendPQExpBufferStr(oprid, "NONE");
13917 :
13918 5042 : if (strcmp(oprkind, "l") == 0 ||
13919 4732 : strcmp(oprkind, "b") == 0)
13920 : {
13921 5042 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13922 5042 : appendPQExpBuffer(oprid, ", %s)", oprright);
13923 : }
13924 : else
13925 0 : appendPQExpBufferStr(oprid, ", NONE)");
13926 :
13927 5042 : oprref = getFormattedOperatorName(oprcom);
13928 5042 : if (oprref)
13929 : {
13930 3346 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13931 3346 : free(oprref);
13932 : }
13933 :
13934 5042 : oprref = getFormattedOperatorName(oprnegate);
13935 5042 : if (oprref)
13936 : {
13937 2332 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13938 2332 : free(oprref);
13939 : }
13940 :
13941 5042 : if (strcmp(oprcanmerge, "t") == 0)
13942 394 : appendPQExpBufferStr(details, ",\n MERGES");
13943 :
13944 5042 : if (strcmp(oprcanhash, "t") == 0)
13945 276 : appendPQExpBufferStr(details, ",\n HASHES");
13946 :
13947 5042 : oprregproc = convertRegProcReference(oprrest);
13948 5042 : if (oprregproc)
13949 : {
13950 3052 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13951 3052 : free(oprregproc);
13952 : }
13953 :
13954 5042 : oprregproc = convertRegProcReference(oprjoin);
13955 5042 : if (oprregproc)
13956 : {
13957 3052 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13958 3052 : free(oprregproc);
13959 : }
13960 :
13961 5042 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13962 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
13963 : oprid->data);
13964 :
13965 5042 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13966 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
13967 5042 : oprinfo->dobj.name, details->data);
13968 :
13969 5042 : if (dopt->binary_upgrade)
13970 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13971 24 : "OPERATOR", oprid->data,
13972 24 : oprinfo->dobj.namespace->dobj.name);
13973 :
13974 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13975 5042 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13976 5042 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13977 : .namespace = oprinfo->dobj.namespace->dobj.name,
13978 : .owner = oprinfo->rolname,
13979 : .description = "OPERATOR",
13980 : .section = SECTION_PRE_DATA,
13981 : .createStmt = q->data,
13982 : .dropStmt = delq->data));
13983 :
13984 : /* Dump Operator Comments */
13985 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13986 4794 : dumpComment(fout, "OPERATOR", oprid->data,
13987 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13988 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13989 :
13990 5042 : PQclear(res);
13991 :
13992 5042 : destroyPQExpBuffer(query);
13993 5042 : destroyPQExpBuffer(q);
13994 5042 : destroyPQExpBuffer(delq);
13995 5042 : destroyPQExpBuffer(oprid);
13996 5042 : destroyPQExpBuffer(details);
13997 : }
13998 :
13999 : /*
14000 : * Convert a function reference obtained from pg_operator
14001 : *
14002 : * Returns allocated string of what to print, or NULL if function references
14003 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14004 : *
14005 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14006 : * part.
14007 : */
14008 : static char *
14009 15126 : convertRegProcReference(const char *proc)
14010 : {
14011 : char *name;
14012 : char *paren;
14013 : bool inquote;
14014 :
14015 : /* In all cases "-" means a null reference */
14016 15126 : if (strcmp(proc, "-") == 0)
14017 3980 : return NULL;
14018 :
14019 11146 : name = pg_strdup(proc);
14020 : /* find non-double-quoted left paren */
14021 11146 : inquote = false;
14022 134312 : for (paren = name; *paren; paren++)
14023 : {
14024 134312 : if (*paren == '(' && !inquote)
14025 : {
14026 11146 : *paren = '\0';
14027 11146 : break;
14028 : }
14029 123166 : if (*paren == '"')
14030 100 : inquote = !inquote;
14031 : }
14032 11146 : return name;
14033 : }
14034 :
14035 : /*
14036 : * getFormattedOperatorName - retrieve the operator name for the
14037 : * given operator OID (presented in string form).
14038 : *
14039 : * Returns an allocated string, or NULL if the given OID is invalid.
14040 : * Caller is responsible for free'ing result string.
14041 : *
14042 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14043 : * useful in commands where the operator's argument types can be inferred from
14044 : * context. We always schema-qualify the name, though. The predecessor to
14045 : * this code tried to skip the schema qualification if possible, but that led
14046 : * to wrong results in corner cases, such as if an operator and its negator
14047 : * are in different schemas.
14048 : */
14049 : static char *
14050 10944 : getFormattedOperatorName(const char *oproid)
14051 : {
14052 : OprInfo *oprInfo;
14053 :
14054 : /* In all cases "0" means a null reference */
14055 10944 : if (strcmp(oproid, "0") == 0)
14056 5266 : return NULL;
14057 :
14058 5678 : oprInfo = findOprByOid(atooid(oproid));
14059 5678 : if (oprInfo == NULL)
14060 : {
14061 0 : pg_log_warning("could not find operator with OID %s",
14062 : oproid);
14063 0 : return NULL;
14064 : }
14065 :
14066 5678 : return psprintf("OPERATOR(%s.%s)",
14067 5678 : fmtId(oprInfo->dobj.namespace->dobj.name),
14068 : oprInfo->dobj.name);
14069 : }
14070 :
14071 : /*
14072 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14073 : *
14074 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14075 : * argument lists of these functions are predetermined. Note that the
14076 : * caller should ensure we are in the proper schema, because the results
14077 : * are search path dependent!
14078 : */
14079 : static char *
14080 450 : convertTSFunction(Archive *fout, Oid funcOid)
14081 : {
14082 : char *result;
14083 : char query[128];
14084 : PGresult *res;
14085 :
14086 450 : snprintf(query, sizeof(query),
14087 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14088 450 : res = ExecuteSqlQueryForSingleRow(fout, query);
14089 :
14090 450 : result = pg_strdup(PQgetvalue(res, 0, 0));
14091 :
14092 450 : PQclear(res);
14093 :
14094 450 : return result;
14095 : }
14096 :
14097 : /*
14098 : * dumpAccessMethod
14099 : * write out a single access method definition
14100 : */
14101 : static void
14102 182 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14103 : {
14104 182 : DumpOptions *dopt = fout->dopt;
14105 : PQExpBuffer q;
14106 : PQExpBuffer delq;
14107 : char *qamname;
14108 :
14109 : /* Do nothing if not dumping schema */
14110 182 : if (!dopt->dumpSchema)
14111 24 : return;
14112 :
14113 158 : q = createPQExpBuffer();
14114 158 : delq = createPQExpBuffer();
14115 :
14116 158 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14117 :
14118 158 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14119 :
14120 158 : switch (aminfo->amtype)
14121 : {
14122 72 : case AMTYPE_INDEX:
14123 72 : appendPQExpBufferStr(q, "TYPE INDEX ");
14124 72 : break;
14125 86 : case AMTYPE_TABLE:
14126 86 : appendPQExpBufferStr(q, "TYPE TABLE ");
14127 86 : break;
14128 0 : default:
14129 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14130 : aminfo->amtype, qamname);
14131 0 : destroyPQExpBuffer(q);
14132 0 : destroyPQExpBuffer(delq);
14133 0 : free(qamname);
14134 0 : return;
14135 : }
14136 :
14137 158 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14138 :
14139 158 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14140 : qamname);
14141 :
14142 158 : if (dopt->binary_upgrade)
14143 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14144 : "ACCESS METHOD", qamname, NULL);
14145 :
14146 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14147 158 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14148 158 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14149 : .description = "ACCESS METHOD",
14150 : .section = SECTION_PRE_DATA,
14151 : .createStmt = q->data,
14152 : .dropStmt = delq->data));
14153 :
14154 : /* Dump Access Method Comments */
14155 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14156 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14157 : NULL, "",
14158 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14159 :
14160 158 : destroyPQExpBuffer(q);
14161 158 : destroyPQExpBuffer(delq);
14162 158 : free(qamname);
14163 : }
14164 :
14165 : /*
14166 : * dumpOpclass
14167 : * write out a single operator class definition
14168 : */
14169 : static void
14170 1362 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14171 : {
14172 1362 : DumpOptions *dopt = fout->dopt;
14173 : PQExpBuffer query;
14174 : PQExpBuffer q;
14175 : PQExpBuffer delq;
14176 : PQExpBuffer nameusing;
14177 : PGresult *res;
14178 : int ntups;
14179 : int i_opcintype;
14180 : int i_opckeytype;
14181 : int i_opcdefault;
14182 : int i_opcfamily;
14183 : int i_opcfamilyname;
14184 : int i_opcfamilynsp;
14185 : int i_amname;
14186 : int i_amopstrategy;
14187 : int i_amopopr;
14188 : int i_sortfamily;
14189 : int i_sortfamilynsp;
14190 : int i_amprocnum;
14191 : int i_amproc;
14192 : int i_amproclefttype;
14193 : int i_amprocrighttype;
14194 : char *opcintype;
14195 : char *opckeytype;
14196 : char *opcdefault;
14197 : char *opcfamily;
14198 : char *opcfamilyname;
14199 : char *opcfamilynsp;
14200 : char *amname;
14201 : char *amopstrategy;
14202 : char *amopopr;
14203 : char *sortfamily;
14204 : char *sortfamilynsp;
14205 : char *amprocnum;
14206 : char *amproc;
14207 : char *amproclefttype;
14208 : char *amprocrighttype;
14209 : bool needComma;
14210 : int i;
14211 :
14212 : /* Do nothing if not dumping schema */
14213 1362 : if (!dopt->dumpSchema)
14214 36 : return;
14215 :
14216 1326 : query = createPQExpBuffer();
14217 1326 : q = createPQExpBuffer();
14218 1326 : delq = createPQExpBuffer();
14219 1326 : nameusing = createPQExpBuffer();
14220 :
14221 : /* Get additional fields from the pg_opclass row */
14222 1326 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14223 : "opckeytype::pg_catalog.regtype, "
14224 : "opcdefault, opcfamily, "
14225 : "opfname AS opcfamilyname, "
14226 : "nspname AS opcfamilynsp, "
14227 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14228 : "FROM pg_catalog.pg_opclass c "
14229 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14230 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14231 : "WHERE c.oid = '%u'::pg_catalog.oid",
14232 1326 : opcinfo->dobj.catId.oid);
14233 :
14234 1326 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14235 :
14236 1326 : i_opcintype = PQfnumber(res, "opcintype");
14237 1326 : i_opckeytype = PQfnumber(res, "opckeytype");
14238 1326 : i_opcdefault = PQfnumber(res, "opcdefault");
14239 1326 : i_opcfamily = PQfnumber(res, "opcfamily");
14240 1326 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14241 1326 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14242 1326 : i_amname = PQfnumber(res, "amname");
14243 :
14244 : /* opcintype may still be needed after we PQclear res */
14245 1326 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14246 1326 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14247 1326 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14248 : /* opcfamily will still be needed after we PQclear res */
14249 1326 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14250 1326 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14251 1326 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14252 : /* amname will still be needed after we PQclear res */
14253 1326 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14254 :
14255 1326 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14256 1326 : fmtQualifiedDumpable(opcinfo));
14257 1326 : appendPQExpBuffer(delq, " USING %s;\n",
14258 : fmtId(amname));
14259 :
14260 : /* Build the fixed portion of the CREATE command */
14261 1326 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14262 1326 : fmtQualifiedDumpable(opcinfo));
14263 1326 : if (strcmp(opcdefault, "t") == 0)
14264 714 : appendPQExpBufferStr(q, "DEFAULT ");
14265 1326 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14266 : opcintype,
14267 : fmtId(amname));
14268 1326 : if (strlen(opcfamilyname) > 0)
14269 : {
14270 1326 : appendPQExpBufferStr(q, " FAMILY ");
14271 1326 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14272 1326 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14273 : }
14274 1326 : appendPQExpBufferStr(q, " AS\n ");
14275 :
14276 1326 : needComma = false;
14277 :
14278 1326 : if (strcmp(opckeytype, "-") != 0)
14279 : {
14280 504 : appendPQExpBuffer(q, "STORAGE %s",
14281 : opckeytype);
14282 504 : needComma = true;
14283 : }
14284 :
14285 1326 : PQclear(res);
14286 :
14287 : /*
14288 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14289 : *
14290 : * Print only those opfamily members that are tied to the opclass by
14291 : * pg_depend entries.
14292 : */
14293 1326 : resetPQExpBuffer(query);
14294 1326 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14295 : "amopopr::pg_catalog.regoperator, "
14296 : "opfname AS sortfamily, "
14297 : "nspname AS sortfamilynsp "
14298 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14299 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14300 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14301 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14302 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14303 : "AND refobjid = '%u'::pg_catalog.oid "
14304 : "AND amopfamily = '%s'::pg_catalog.oid "
14305 : "ORDER BY amopstrategy",
14306 1326 : opcinfo->dobj.catId.oid,
14307 : opcfamily);
14308 :
14309 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14310 :
14311 1326 : ntups = PQntuples(res);
14312 :
14313 1326 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14314 1326 : i_amopopr = PQfnumber(res, "amopopr");
14315 1326 : i_sortfamily = PQfnumber(res, "sortfamily");
14316 1326 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14317 :
14318 1790 : for (i = 0; i < ntups; i++)
14319 : {
14320 464 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14321 464 : amopopr = PQgetvalue(res, i, i_amopopr);
14322 464 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14323 464 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14324 :
14325 464 : if (needComma)
14326 288 : appendPQExpBufferStr(q, " ,\n ");
14327 :
14328 464 : appendPQExpBuffer(q, "OPERATOR %s %s",
14329 : amopstrategy, amopopr);
14330 :
14331 464 : if (strlen(sortfamily) > 0)
14332 : {
14333 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14334 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14335 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14336 : }
14337 :
14338 464 : needComma = true;
14339 : }
14340 :
14341 1326 : PQclear(res);
14342 :
14343 : /*
14344 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14345 : *
14346 : * Print only those opfamily members that are tied to the opclass by
14347 : * pg_depend entries.
14348 : *
14349 : * We print the amproclefttype/amprocrighttype even though in most cases
14350 : * the backend could deduce the right values, because of the corner case
14351 : * of a btree sort support function for a cross-type comparison.
14352 : */
14353 1326 : resetPQExpBuffer(query);
14354 :
14355 1326 : appendPQExpBuffer(query, "SELECT amprocnum, "
14356 : "amproc::pg_catalog.regprocedure, "
14357 : "amproclefttype::pg_catalog.regtype, "
14358 : "amprocrighttype::pg_catalog.regtype "
14359 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14360 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14361 : "AND refobjid = '%u'::pg_catalog.oid "
14362 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14363 : "AND objid = ap.oid "
14364 : "ORDER BY amprocnum",
14365 1326 : opcinfo->dobj.catId.oid);
14366 :
14367 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14368 :
14369 1326 : ntups = PQntuples(res);
14370 :
14371 1326 : i_amprocnum = PQfnumber(res, "amprocnum");
14372 1326 : i_amproc = PQfnumber(res, "amproc");
14373 1326 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14374 1326 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14375 :
14376 1398 : for (i = 0; i < ntups; i++)
14377 : {
14378 72 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14379 72 : amproc = PQgetvalue(res, i, i_amproc);
14380 72 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14381 72 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14382 :
14383 72 : if (needComma)
14384 72 : appendPQExpBufferStr(q, " ,\n ");
14385 :
14386 72 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14387 :
14388 72 : if (*amproclefttype && *amprocrighttype)
14389 72 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14390 :
14391 72 : appendPQExpBuffer(q, " %s", amproc);
14392 :
14393 72 : needComma = true;
14394 : }
14395 :
14396 1326 : PQclear(res);
14397 :
14398 : /*
14399 : * If needComma is still false it means we haven't added anything after
14400 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14401 : * clause with the same datatype. This isn't sanctioned by the
14402 : * documentation, but actually DefineOpClass will treat it as a no-op.
14403 : */
14404 1326 : if (!needComma)
14405 646 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14406 :
14407 1326 : appendPQExpBufferStr(q, ";\n");
14408 :
14409 1326 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14410 1326 : appendPQExpBuffer(nameusing, " USING %s",
14411 : fmtId(amname));
14412 :
14413 1326 : if (dopt->binary_upgrade)
14414 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14415 12 : "OPERATOR CLASS", nameusing->data,
14416 12 : opcinfo->dobj.namespace->dobj.name);
14417 :
14418 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14419 1326 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14420 1326 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14421 : .namespace = opcinfo->dobj.namespace->dobj.name,
14422 : .owner = opcinfo->rolname,
14423 : .description = "OPERATOR CLASS",
14424 : .section = SECTION_PRE_DATA,
14425 : .createStmt = q->data,
14426 : .dropStmt = delq->data));
14427 :
14428 : /* Dump Operator Class Comments */
14429 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14430 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14431 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14432 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14433 :
14434 1326 : free(opcintype);
14435 1326 : free(opcfamily);
14436 1326 : free(amname);
14437 1326 : destroyPQExpBuffer(query);
14438 1326 : destroyPQExpBuffer(q);
14439 1326 : destroyPQExpBuffer(delq);
14440 1326 : destroyPQExpBuffer(nameusing);
14441 : }
14442 :
14443 : /*
14444 : * dumpOpfamily
14445 : * write out a single operator family definition
14446 : *
14447 : * Note: this also dumps any "loose" operator members that aren't bound to a
14448 : * specific opclass within the opfamily.
14449 : */
14450 : static void
14451 1156 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14452 : {
14453 1156 : DumpOptions *dopt = fout->dopt;
14454 : PQExpBuffer query;
14455 : PQExpBuffer q;
14456 : PQExpBuffer delq;
14457 : PQExpBuffer nameusing;
14458 : PGresult *res;
14459 : PGresult *res_ops;
14460 : PGresult *res_procs;
14461 : int ntups;
14462 : int i_amname;
14463 : int i_amopstrategy;
14464 : int i_amopopr;
14465 : int i_sortfamily;
14466 : int i_sortfamilynsp;
14467 : int i_amprocnum;
14468 : int i_amproc;
14469 : int i_amproclefttype;
14470 : int i_amprocrighttype;
14471 : char *amname;
14472 : char *amopstrategy;
14473 : char *amopopr;
14474 : char *sortfamily;
14475 : char *sortfamilynsp;
14476 : char *amprocnum;
14477 : char *amproc;
14478 : char *amproclefttype;
14479 : char *amprocrighttype;
14480 : bool needComma;
14481 : int i;
14482 :
14483 : /* Do nothing if not dumping schema */
14484 1156 : if (!dopt->dumpSchema)
14485 24 : return;
14486 :
14487 1132 : query = createPQExpBuffer();
14488 1132 : q = createPQExpBuffer();
14489 1132 : delq = createPQExpBuffer();
14490 1132 : nameusing = createPQExpBuffer();
14491 :
14492 : /*
14493 : * Fetch only those opfamily members that are tied directly to the
14494 : * opfamily by pg_depend entries.
14495 : */
14496 1132 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14497 : "amopopr::pg_catalog.regoperator, "
14498 : "opfname AS sortfamily, "
14499 : "nspname AS sortfamilynsp "
14500 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14501 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14502 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14503 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14504 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14505 : "AND refobjid = '%u'::pg_catalog.oid "
14506 : "AND amopfamily = '%u'::pg_catalog.oid "
14507 : "ORDER BY amopstrategy",
14508 1132 : opfinfo->dobj.catId.oid,
14509 1132 : opfinfo->dobj.catId.oid);
14510 :
14511 1132 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14512 :
14513 1132 : resetPQExpBuffer(query);
14514 :
14515 1132 : appendPQExpBuffer(query, "SELECT amprocnum, "
14516 : "amproc::pg_catalog.regprocedure, "
14517 : "amproclefttype::pg_catalog.regtype, "
14518 : "amprocrighttype::pg_catalog.regtype "
14519 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14520 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14521 : "AND refobjid = '%u'::pg_catalog.oid "
14522 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14523 : "AND objid = ap.oid "
14524 : "ORDER BY amprocnum",
14525 1132 : opfinfo->dobj.catId.oid);
14526 :
14527 1132 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14528 :
14529 : /* Get additional fields from the pg_opfamily row */
14530 1132 : resetPQExpBuffer(query);
14531 :
14532 1132 : appendPQExpBuffer(query, "SELECT "
14533 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14534 : "FROM pg_catalog.pg_opfamily "
14535 : "WHERE oid = '%u'::pg_catalog.oid",
14536 1132 : opfinfo->dobj.catId.oid);
14537 :
14538 1132 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14539 :
14540 1132 : i_amname = PQfnumber(res, "amname");
14541 :
14542 : /* amname will still be needed after we PQclear res */
14543 1132 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14544 :
14545 1132 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14546 1132 : fmtQualifiedDumpable(opfinfo));
14547 1132 : appendPQExpBuffer(delq, " USING %s;\n",
14548 : fmtId(amname));
14549 :
14550 : /* Build the fixed portion of the CREATE command */
14551 1132 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14552 1132 : fmtQualifiedDumpable(opfinfo));
14553 1132 : appendPQExpBuffer(q, " USING %s;\n",
14554 : fmtId(amname));
14555 :
14556 1132 : PQclear(res);
14557 :
14558 : /* Do we need an ALTER to add loose members? */
14559 1132 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14560 : {
14561 120 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14562 120 : fmtQualifiedDumpable(opfinfo));
14563 120 : appendPQExpBuffer(q, " USING %s ADD\n ",
14564 : fmtId(amname));
14565 :
14566 120 : needComma = false;
14567 :
14568 : /*
14569 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14570 : */
14571 120 : ntups = PQntuples(res_ops);
14572 :
14573 120 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14574 120 : i_amopopr = PQfnumber(res_ops, "amopopr");
14575 120 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14576 120 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14577 :
14578 480 : for (i = 0; i < ntups; i++)
14579 : {
14580 360 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14581 360 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14582 360 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14583 360 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14584 :
14585 360 : if (needComma)
14586 288 : appendPQExpBufferStr(q, " ,\n ");
14587 :
14588 360 : appendPQExpBuffer(q, "OPERATOR %s %s",
14589 : amopstrategy, amopopr);
14590 :
14591 360 : if (strlen(sortfamily) > 0)
14592 : {
14593 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14594 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14595 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14596 : }
14597 :
14598 360 : needComma = true;
14599 : }
14600 :
14601 : /*
14602 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14603 : */
14604 120 : ntups = PQntuples(res_procs);
14605 :
14606 120 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14607 120 : i_amproc = PQfnumber(res_procs, "amproc");
14608 120 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14609 120 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14610 :
14611 528 : for (i = 0; i < ntups; i++)
14612 : {
14613 408 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14614 408 : amproc = PQgetvalue(res_procs, i, i_amproc);
14615 408 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14616 408 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14617 :
14618 408 : if (needComma)
14619 360 : appendPQExpBufferStr(q, " ,\n ");
14620 :
14621 408 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14622 : amprocnum, amproclefttype, amprocrighttype,
14623 : amproc);
14624 :
14625 408 : needComma = true;
14626 : }
14627 :
14628 120 : appendPQExpBufferStr(q, ";\n");
14629 : }
14630 :
14631 1132 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14632 1132 : appendPQExpBuffer(nameusing, " USING %s",
14633 : fmtId(amname));
14634 :
14635 1132 : if (dopt->binary_upgrade)
14636 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14637 18 : "OPERATOR FAMILY", nameusing->data,
14638 18 : opfinfo->dobj.namespace->dobj.name);
14639 :
14640 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14641 1132 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14642 1132 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14643 : .namespace = opfinfo->dobj.namespace->dobj.name,
14644 : .owner = opfinfo->rolname,
14645 : .description = "OPERATOR FAMILY",
14646 : .section = SECTION_PRE_DATA,
14647 : .createStmt = q->data,
14648 : .dropStmt = delq->data));
14649 :
14650 : /* Dump Operator Family Comments */
14651 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14652 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14653 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14654 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14655 :
14656 1132 : free(amname);
14657 1132 : PQclear(res_ops);
14658 1132 : PQclear(res_procs);
14659 1132 : destroyPQExpBuffer(query);
14660 1132 : destroyPQExpBuffer(q);
14661 1132 : destroyPQExpBuffer(delq);
14662 1132 : destroyPQExpBuffer(nameusing);
14663 : }
14664 :
14665 : /*
14666 : * dumpCollation
14667 : * write out a single collation definition
14668 : */
14669 : static void
14670 5108 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14671 : {
14672 5108 : DumpOptions *dopt = fout->dopt;
14673 : PQExpBuffer query;
14674 : PQExpBuffer q;
14675 : PQExpBuffer delq;
14676 : char *qcollname;
14677 : PGresult *res;
14678 : int i_collprovider;
14679 : int i_collisdeterministic;
14680 : int i_collcollate;
14681 : int i_collctype;
14682 : int i_colllocale;
14683 : int i_collicurules;
14684 : const char *collprovider;
14685 : const char *collcollate;
14686 : const char *collctype;
14687 : const char *colllocale;
14688 : const char *collicurules;
14689 :
14690 : /* Do nothing if not dumping schema */
14691 5108 : if (!dopt->dumpSchema)
14692 24 : return;
14693 :
14694 5084 : query = createPQExpBuffer();
14695 5084 : q = createPQExpBuffer();
14696 5084 : delq = createPQExpBuffer();
14697 :
14698 5084 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14699 :
14700 : /* Get collation-specific details */
14701 5084 : appendPQExpBufferStr(query, "SELECT ");
14702 :
14703 5084 : if (fout->remoteVersion >= 100000)
14704 5084 : appendPQExpBufferStr(query,
14705 : "collprovider, "
14706 : "collversion, ");
14707 : else
14708 0 : appendPQExpBufferStr(query,
14709 : "'c' AS collprovider, "
14710 : "NULL AS collversion, ");
14711 :
14712 5084 : if (fout->remoteVersion >= 120000)
14713 5084 : appendPQExpBufferStr(query,
14714 : "collisdeterministic, ");
14715 : else
14716 0 : appendPQExpBufferStr(query,
14717 : "true AS collisdeterministic, ");
14718 :
14719 5084 : if (fout->remoteVersion >= 170000)
14720 5084 : appendPQExpBufferStr(query,
14721 : "colllocale, ");
14722 0 : else if (fout->remoteVersion >= 150000)
14723 0 : appendPQExpBufferStr(query,
14724 : "colliculocale AS colllocale, ");
14725 : else
14726 0 : appendPQExpBufferStr(query,
14727 : "NULL AS colllocale, ");
14728 :
14729 5084 : if (fout->remoteVersion >= 160000)
14730 5084 : appendPQExpBufferStr(query,
14731 : "collicurules, ");
14732 : else
14733 0 : appendPQExpBufferStr(query,
14734 : "NULL AS collicurules, ");
14735 :
14736 5084 : appendPQExpBuffer(query,
14737 : "collcollate, "
14738 : "collctype "
14739 : "FROM pg_catalog.pg_collation c "
14740 : "WHERE c.oid = '%u'::pg_catalog.oid",
14741 5084 : collinfo->dobj.catId.oid);
14742 :
14743 5084 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14744 :
14745 5084 : i_collprovider = PQfnumber(res, "collprovider");
14746 5084 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14747 5084 : i_collcollate = PQfnumber(res, "collcollate");
14748 5084 : i_collctype = PQfnumber(res, "collctype");
14749 5084 : i_colllocale = PQfnumber(res, "colllocale");
14750 5084 : i_collicurules = PQfnumber(res, "collicurules");
14751 :
14752 5084 : collprovider = PQgetvalue(res, 0, i_collprovider);
14753 :
14754 5084 : if (!PQgetisnull(res, 0, i_collcollate))
14755 100 : collcollate = PQgetvalue(res, 0, i_collcollate);
14756 : else
14757 4984 : collcollate = NULL;
14758 :
14759 5084 : if (!PQgetisnull(res, 0, i_collctype))
14760 100 : collctype = PQgetvalue(res, 0, i_collctype);
14761 : else
14762 4984 : collctype = NULL;
14763 :
14764 : /*
14765 : * Before version 15, collcollate and collctype were of type NAME and
14766 : * non-nullable. Treat empty strings as NULL for consistency.
14767 : */
14768 5084 : if (fout->remoteVersion < 150000)
14769 : {
14770 0 : if (collcollate[0] == '\0')
14771 0 : collcollate = NULL;
14772 0 : if (collctype[0] == '\0')
14773 0 : collctype = NULL;
14774 : }
14775 :
14776 5084 : if (!PQgetisnull(res, 0, i_colllocale))
14777 4978 : colllocale = PQgetvalue(res, 0, i_colllocale);
14778 : else
14779 106 : colllocale = NULL;
14780 :
14781 5084 : if (!PQgetisnull(res, 0, i_collicurules))
14782 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
14783 : else
14784 5084 : collicurules = NULL;
14785 :
14786 5084 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
14787 5084 : fmtQualifiedDumpable(collinfo));
14788 :
14789 5084 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
14790 5084 : fmtQualifiedDumpable(collinfo));
14791 :
14792 5084 : appendPQExpBufferStr(q, "provider = ");
14793 5084 : if (collprovider[0] == 'b')
14794 50 : appendPQExpBufferStr(q, "builtin");
14795 5034 : else if (collprovider[0] == 'c')
14796 100 : appendPQExpBufferStr(q, "libc");
14797 4934 : else if (collprovider[0] == 'i')
14798 4928 : appendPQExpBufferStr(q, "icu");
14799 6 : else if (collprovider[0] == 'd')
14800 : /* to allow dumping pg_catalog; not accepted on input */
14801 6 : appendPQExpBufferStr(q, "default");
14802 : else
14803 0 : pg_fatal("unrecognized collation provider: %s",
14804 : collprovider);
14805 :
14806 5084 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14807 0 : appendPQExpBufferStr(q, ", deterministic = false");
14808 :
14809 5084 : if (collprovider[0] == 'd')
14810 : {
14811 6 : if (collcollate || collctype || colllocale || collicurules)
14812 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14813 :
14814 : /* no locale -- the default collation cannot be reloaded anyway */
14815 : }
14816 5078 : else if (collprovider[0] == 'b')
14817 : {
14818 50 : if (collcollate || collctype || !colllocale || collicurules)
14819 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14820 :
14821 50 : appendPQExpBufferStr(q, ", locale = ");
14822 50 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14823 : fout);
14824 : }
14825 5028 : else if (collprovider[0] == 'i')
14826 : {
14827 4928 : if (fout->remoteVersion >= 150000)
14828 : {
14829 4928 : if (collcollate || collctype || !colllocale)
14830 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14831 :
14832 4928 : appendPQExpBufferStr(q, ", locale = ");
14833 4928 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14834 : fout);
14835 : }
14836 : else
14837 : {
14838 0 : if (!collcollate || !collctype || colllocale ||
14839 0 : strcmp(collcollate, collctype) != 0)
14840 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14841 :
14842 0 : appendPQExpBufferStr(q, ", locale = ");
14843 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14844 : }
14845 :
14846 4928 : if (collicurules)
14847 : {
14848 0 : appendPQExpBufferStr(q, ", rules = ");
14849 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14850 : }
14851 : }
14852 100 : else if (collprovider[0] == 'c')
14853 : {
14854 100 : if (colllocale || collicurules || !collcollate || !collctype)
14855 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14856 :
14857 100 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14858 : {
14859 100 : appendPQExpBufferStr(q, ", locale = ");
14860 100 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14861 : }
14862 : else
14863 : {
14864 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14865 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14866 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14867 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14868 : }
14869 : }
14870 : else
14871 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14872 :
14873 : /*
14874 : * For binary upgrade, carry over the collation version. For normal
14875 : * dump/restore, omit the version, so that it is computed upon restore.
14876 : */
14877 5084 : if (dopt->binary_upgrade)
14878 : {
14879 : int i_collversion;
14880 :
14881 10 : i_collversion = PQfnumber(res, "collversion");
14882 10 : if (!PQgetisnull(res, 0, i_collversion))
14883 : {
14884 8 : appendPQExpBufferStr(q, ", version = ");
14885 8 : appendStringLiteralAH(q,
14886 : PQgetvalue(res, 0, i_collversion),
14887 : fout);
14888 : }
14889 : }
14890 :
14891 5084 : appendPQExpBufferStr(q, ");\n");
14892 :
14893 5084 : if (dopt->binary_upgrade)
14894 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
14895 : "COLLATION", qcollname,
14896 10 : collinfo->dobj.namespace->dobj.name);
14897 :
14898 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14899 5084 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14900 5084 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14901 : .namespace = collinfo->dobj.namespace->dobj.name,
14902 : .owner = collinfo->rolname,
14903 : .description = "COLLATION",
14904 : .section = SECTION_PRE_DATA,
14905 : .createStmt = q->data,
14906 : .dropStmt = delq->data));
14907 :
14908 : /* Dump Collation Comments */
14909 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14910 4870 : dumpComment(fout, "COLLATION", qcollname,
14911 4870 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14912 4870 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14913 :
14914 5084 : PQclear(res);
14915 :
14916 5084 : destroyPQExpBuffer(query);
14917 5084 : destroyPQExpBuffer(q);
14918 5084 : destroyPQExpBuffer(delq);
14919 5084 : free(qcollname);
14920 : }
14921 :
14922 : /*
14923 : * dumpConversion
14924 : * write out a single conversion definition
14925 : */
14926 : static void
14927 852 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14928 : {
14929 852 : DumpOptions *dopt = fout->dopt;
14930 : PQExpBuffer query;
14931 : PQExpBuffer q;
14932 : PQExpBuffer delq;
14933 : char *qconvname;
14934 : PGresult *res;
14935 : int i_conforencoding;
14936 : int i_contoencoding;
14937 : int i_conproc;
14938 : int i_condefault;
14939 : const char *conforencoding;
14940 : const char *contoencoding;
14941 : const char *conproc;
14942 : bool condefault;
14943 :
14944 : /* Do nothing if not dumping schema */
14945 852 : if (!dopt->dumpSchema)
14946 12 : return;
14947 :
14948 840 : query = createPQExpBuffer();
14949 840 : q = createPQExpBuffer();
14950 840 : delq = createPQExpBuffer();
14951 :
14952 840 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14953 :
14954 : /* Get conversion-specific details */
14955 840 : appendPQExpBuffer(query, "SELECT "
14956 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14957 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14958 : "conproc, condefault "
14959 : "FROM pg_catalog.pg_conversion c "
14960 : "WHERE c.oid = '%u'::pg_catalog.oid",
14961 840 : convinfo->dobj.catId.oid);
14962 :
14963 840 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14964 :
14965 840 : i_conforencoding = PQfnumber(res, "conforencoding");
14966 840 : i_contoencoding = PQfnumber(res, "contoencoding");
14967 840 : i_conproc = PQfnumber(res, "conproc");
14968 840 : i_condefault = PQfnumber(res, "condefault");
14969 :
14970 840 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14971 840 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14972 840 : conproc = PQgetvalue(res, 0, i_conproc);
14973 840 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14974 :
14975 840 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14976 840 : fmtQualifiedDumpable(convinfo));
14977 :
14978 840 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14979 : (condefault) ? "DEFAULT " : "",
14980 840 : fmtQualifiedDumpable(convinfo));
14981 840 : appendStringLiteralAH(q, conforencoding, fout);
14982 840 : appendPQExpBufferStr(q, " TO ");
14983 840 : appendStringLiteralAH(q, contoencoding, fout);
14984 : /* regproc output is already sufficiently quoted */
14985 840 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14986 :
14987 840 : if (dopt->binary_upgrade)
14988 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14989 : "CONVERSION", qconvname,
14990 2 : convinfo->dobj.namespace->dobj.name);
14991 :
14992 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14993 840 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14994 840 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14995 : .namespace = convinfo->dobj.namespace->dobj.name,
14996 : .owner = convinfo->rolname,
14997 : .description = "CONVERSION",
14998 : .section = SECTION_PRE_DATA,
14999 : .createStmt = q->data,
15000 : .dropStmt = delq->data));
15001 :
15002 : /* Dump Conversion Comments */
15003 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15004 840 : dumpComment(fout, "CONVERSION", qconvname,
15005 840 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15006 840 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15007 :
15008 840 : PQclear(res);
15009 :
15010 840 : destroyPQExpBuffer(query);
15011 840 : destroyPQExpBuffer(q);
15012 840 : destroyPQExpBuffer(delq);
15013 840 : free(qconvname);
15014 : }
15015 :
15016 : /*
15017 : * format_aggregate_signature: generate aggregate name and argument list
15018 : *
15019 : * The argument type names are qualified if needed. The aggregate name
15020 : * is never qualified.
15021 : */
15022 : static char *
15023 860 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15024 : {
15025 : PQExpBufferData buf;
15026 : int j;
15027 :
15028 860 : initPQExpBuffer(&buf);
15029 860 : if (honor_quotes)
15030 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15031 : else
15032 860 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15033 :
15034 860 : if (agginfo->aggfn.nargs == 0)
15035 128 : appendPQExpBufferStr(&buf, "(*)");
15036 : else
15037 : {
15038 732 : appendPQExpBufferChar(&buf, '(');
15039 1608 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15040 876 : appendPQExpBuffer(&buf, "%s%s",
15041 : (j > 0) ? ", " : "",
15042 : getFormattedTypeName(fout,
15043 876 : agginfo->aggfn.argtypes[j],
15044 : zeroIsError));
15045 732 : appendPQExpBufferChar(&buf, ')');
15046 : }
15047 860 : return buf.data;
15048 : }
15049 :
15050 : /*
15051 : * dumpAgg
15052 : * write out a single aggregate definition
15053 : */
15054 : static void
15055 874 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15056 : {
15057 874 : DumpOptions *dopt = fout->dopt;
15058 : PQExpBuffer query;
15059 : PQExpBuffer q;
15060 : PQExpBuffer delq;
15061 : PQExpBuffer details;
15062 : char *aggsig; /* identity signature */
15063 874 : char *aggfullsig = NULL; /* full signature */
15064 : char *aggsig_tag;
15065 : PGresult *res;
15066 : int i_agginitval;
15067 : int i_aggminitval;
15068 : const char *aggtransfn;
15069 : const char *aggfinalfn;
15070 : const char *aggcombinefn;
15071 : const char *aggserialfn;
15072 : const char *aggdeserialfn;
15073 : const char *aggmtransfn;
15074 : const char *aggminvtransfn;
15075 : const char *aggmfinalfn;
15076 : bool aggfinalextra;
15077 : bool aggmfinalextra;
15078 : char aggfinalmodify;
15079 : char aggmfinalmodify;
15080 : const char *aggsortop;
15081 : char *aggsortconvop;
15082 : char aggkind;
15083 : const char *aggtranstype;
15084 : const char *aggtransspace;
15085 : const char *aggmtranstype;
15086 : const char *aggmtransspace;
15087 : const char *agginitval;
15088 : const char *aggminitval;
15089 : const char *proparallel;
15090 : char defaultfinalmodify;
15091 :
15092 : /* Do nothing if not dumping schema */
15093 874 : if (!dopt->dumpSchema)
15094 14 : return;
15095 :
15096 860 : query = createPQExpBuffer();
15097 860 : q = createPQExpBuffer();
15098 860 : delq = createPQExpBuffer();
15099 860 : details = createPQExpBuffer();
15100 :
15101 860 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15102 : {
15103 : /* Set up query for aggregate-specific details */
15104 124 : appendPQExpBufferStr(query,
15105 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15106 :
15107 124 : appendPQExpBufferStr(query,
15108 : "SELECT "
15109 : "aggtransfn,\n"
15110 : "aggfinalfn,\n"
15111 : "aggtranstype::pg_catalog.regtype,\n"
15112 : "agginitval,\n"
15113 : "aggsortop,\n"
15114 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15115 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15116 :
15117 124 : if (fout->remoteVersion >= 90400)
15118 124 : appendPQExpBufferStr(query,
15119 : "aggkind,\n"
15120 : "aggmtransfn,\n"
15121 : "aggminvtransfn,\n"
15122 : "aggmfinalfn,\n"
15123 : "aggmtranstype::pg_catalog.regtype,\n"
15124 : "aggfinalextra,\n"
15125 : "aggmfinalextra,\n"
15126 : "aggtransspace,\n"
15127 : "aggmtransspace,\n"
15128 : "aggminitval,\n");
15129 : else
15130 0 : appendPQExpBufferStr(query,
15131 : "'n' AS aggkind,\n"
15132 : "'-' AS aggmtransfn,\n"
15133 : "'-' AS aggminvtransfn,\n"
15134 : "'-' AS aggmfinalfn,\n"
15135 : "0 AS aggmtranstype,\n"
15136 : "false AS aggfinalextra,\n"
15137 : "false AS aggmfinalextra,\n"
15138 : "0 AS aggtransspace,\n"
15139 : "0 AS aggmtransspace,\n"
15140 : "NULL AS aggminitval,\n");
15141 :
15142 124 : if (fout->remoteVersion >= 90600)
15143 124 : appendPQExpBufferStr(query,
15144 : "aggcombinefn,\n"
15145 : "aggserialfn,\n"
15146 : "aggdeserialfn,\n"
15147 : "proparallel,\n");
15148 : else
15149 0 : appendPQExpBufferStr(query,
15150 : "'-' AS aggcombinefn,\n"
15151 : "'-' AS aggserialfn,\n"
15152 : "'-' AS aggdeserialfn,\n"
15153 : "'u' AS proparallel,\n");
15154 :
15155 124 : if (fout->remoteVersion >= 110000)
15156 124 : appendPQExpBufferStr(query,
15157 : "aggfinalmodify,\n"
15158 : "aggmfinalmodify\n");
15159 : else
15160 0 : appendPQExpBufferStr(query,
15161 : "'0' AS aggfinalmodify,\n"
15162 : "'0' AS aggmfinalmodify\n");
15163 :
15164 124 : appendPQExpBufferStr(query,
15165 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15166 : "WHERE a.aggfnoid = p.oid "
15167 : "AND p.oid = $1");
15168 :
15169 124 : ExecuteSqlStatement(fout, query->data);
15170 :
15171 124 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15172 : }
15173 :
15174 860 : printfPQExpBuffer(query,
15175 : "EXECUTE dumpAgg('%u')",
15176 860 : agginfo->aggfn.dobj.catId.oid);
15177 :
15178 860 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15179 :
15180 860 : i_agginitval = PQfnumber(res, "agginitval");
15181 860 : i_aggminitval = PQfnumber(res, "aggminitval");
15182 :
15183 860 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15184 860 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15185 860 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15186 860 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15187 860 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15188 860 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15189 860 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15190 860 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15191 860 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15192 860 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15193 860 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15194 860 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15195 860 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15196 860 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15197 860 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15198 860 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15199 860 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15200 860 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15201 860 : agginitval = PQgetvalue(res, 0, i_agginitval);
15202 860 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15203 860 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15204 :
15205 : {
15206 : char *funcargs;
15207 : char *funciargs;
15208 :
15209 860 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15210 860 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15211 860 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15212 860 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15213 : }
15214 :
15215 860 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15216 :
15217 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15218 860 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15219 : /* replace omitted flags for old versions */
15220 860 : if (aggfinalmodify == '0')
15221 0 : aggfinalmodify = defaultfinalmodify;
15222 860 : if (aggmfinalmodify == '0')
15223 0 : aggmfinalmodify = defaultfinalmodify;
15224 :
15225 : /* regproc and regtype output is already sufficiently quoted */
15226 860 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15227 : aggtransfn, aggtranstype);
15228 :
15229 860 : if (strcmp(aggtransspace, "0") != 0)
15230 : {
15231 16 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15232 : aggtransspace);
15233 : }
15234 :
15235 860 : if (!PQgetisnull(res, 0, i_agginitval))
15236 : {
15237 632 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15238 632 : appendStringLiteralAH(details, agginitval, fout);
15239 : }
15240 :
15241 860 : if (strcmp(aggfinalfn, "-") != 0)
15242 : {
15243 392 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15244 : aggfinalfn);
15245 392 : if (aggfinalextra)
15246 32 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15247 392 : if (aggfinalmodify != defaultfinalmodify)
15248 : {
15249 72 : switch (aggfinalmodify)
15250 : {
15251 0 : case AGGMODIFY_READ_ONLY:
15252 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15253 0 : break;
15254 72 : case AGGMODIFY_SHAREABLE:
15255 72 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15256 72 : break;
15257 0 : case AGGMODIFY_READ_WRITE:
15258 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15259 0 : break;
15260 0 : default:
15261 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15262 : agginfo->aggfn.dobj.name);
15263 : break;
15264 : }
15265 : }
15266 : }
15267 :
15268 860 : if (strcmp(aggcombinefn, "-") != 0)
15269 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15270 :
15271 860 : if (strcmp(aggserialfn, "-") != 0)
15272 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15273 :
15274 860 : if (strcmp(aggdeserialfn, "-") != 0)
15275 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15276 :
15277 860 : if (strcmp(aggmtransfn, "-") != 0)
15278 : {
15279 96 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15280 : aggmtransfn,
15281 : aggminvtransfn,
15282 : aggmtranstype);
15283 : }
15284 :
15285 860 : if (strcmp(aggmtransspace, "0") != 0)
15286 : {
15287 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15288 : aggmtransspace);
15289 : }
15290 :
15291 860 : if (!PQgetisnull(res, 0, i_aggminitval))
15292 : {
15293 32 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15294 32 : appendStringLiteralAH(details, aggminitval, fout);
15295 : }
15296 :
15297 860 : if (strcmp(aggmfinalfn, "-") != 0)
15298 : {
15299 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15300 : aggmfinalfn);
15301 0 : if (aggmfinalextra)
15302 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15303 0 : if (aggmfinalmodify != defaultfinalmodify)
15304 : {
15305 0 : switch (aggmfinalmodify)
15306 : {
15307 0 : case AGGMODIFY_READ_ONLY:
15308 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15309 0 : break;
15310 0 : case AGGMODIFY_SHAREABLE:
15311 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15312 0 : break;
15313 0 : case AGGMODIFY_READ_WRITE:
15314 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15315 0 : break;
15316 0 : default:
15317 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15318 : agginfo->aggfn.dobj.name);
15319 : break;
15320 : }
15321 : }
15322 : }
15323 :
15324 860 : aggsortconvop = getFormattedOperatorName(aggsortop);
15325 860 : if (aggsortconvop)
15326 : {
15327 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15328 : aggsortconvop);
15329 0 : free(aggsortconvop);
15330 : }
15331 :
15332 860 : if (aggkind == AGGKIND_HYPOTHETICAL)
15333 16 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15334 :
15335 860 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15336 : {
15337 16 : if (proparallel[0] == PROPARALLEL_SAFE)
15338 16 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15339 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15340 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15341 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15342 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15343 : agginfo->aggfn.dobj.name);
15344 : }
15345 :
15346 860 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15347 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15348 : aggsig);
15349 :
15350 1720 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15351 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15352 : aggfullsig ? aggfullsig : aggsig, details->data);
15353 :
15354 860 : if (dopt->binary_upgrade)
15355 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15356 : "AGGREGATE", aggsig,
15357 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15358 :
15359 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15360 826 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15361 826 : agginfo->aggfn.dobj.dumpId,
15362 826 : ARCHIVE_OPTS(.tag = aggsig_tag,
15363 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15364 : .owner = agginfo->aggfn.rolname,
15365 : .description = "AGGREGATE",
15366 : .section = SECTION_PRE_DATA,
15367 : .createStmt = q->data,
15368 : .dropStmt = delq->data));
15369 :
15370 : /* Dump Aggregate Comments */
15371 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15372 32 : dumpComment(fout, "AGGREGATE", aggsig,
15373 32 : agginfo->aggfn.dobj.namespace->dobj.name,
15374 32 : agginfo->aggfn.rolname,
15375 32 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15376 :
15377 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15378 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15379 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15380 0 : agginfo->aggfn.rolname,
15381 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15382 :
15383 : /*
15384 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15385 : * command look like a function's GRANT; in particular this affects the
15386 : * syntax for zero-argument aggregates and ordered-set aggregates.
15387 : */
15388 860 : free(aggsig);
15389 :
15390 860 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15391 :
15392 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15393 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15394 : "FUNCTION", aggsig, NULL,
15395 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15396 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15397 :
15398 860 : free(aggsig);
15399 860 : free(aggfullsig);
15400 860 : free(aggsig_tag);
15401 :
15402 860 : PQclear(res);
15403 :
15404 860 : destroyPQExpBuffer(query);
15405 860 : destroyPQExpBuffer(q);
15406 860 : destroyPQExpBuffer(delq);
15407 860 : destroyPQExpBuffer(details);
15408 : }
15409 :
15410 : /*
15411 : * dumpTSParser
15412 : * write out a single text search parser
15413 : */
15414 : static void
15415 90 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15416 : {
15417 90 : DumpOptions *dopt = fout->dopt;
15418 : PQExpBuffer q;
15419 : PQExpBuffer delq;
15420 : char *qprsname;
15421 :
15422 : /* Do nothing if not dumping schema */
15423 90 : if (!dopt->dumpSchema)
15424 12 : return;
15425 :
15426 78 : q = createPQExpBuffer();
15427 78 : delq = createPQExpBuffer();
15428 :
15429 78 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15430 :
15431 78 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15432 78 : fmtQualifiedDumpable(prsinfo));
15433 :
15434 78 : appendPQExpBuffer(q, " START = %s,\n",
15435 78 : convertTSFunction(fout, prsinfo->prsstart));
15436 78 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15437 78 : convertTSFunction(fout, prsinfo->prstoken));
15438 78 : appendPQExpBuffer(q, " END = %s,\n",
15439 78 : convertTSFunction(fout, prsinfo->prsend));
15440 78 : if (prsinfo->prsheadline != InvalidOid)
15441 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15442 6 : convertTSFunction(fout, prsinfo->prsheadline));
15443 78 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15444 78 : convertTSFunction(fout, prsinfo->prslextype));
15445 :
15446 78 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15447 78 : fmtQualifiedDumpable(prsinfo));
15448 :
15449 78 : if (dopt->binary_upgrade)
15450 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15451 : "TEXT SEARCH PARSER", qprsname,
15452 2 : prsinfo->dobj.namespace->dobj.name);
15453 :
15454 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15455 78 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15456 78 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15457 : .namespace = prsinfo->dobj.namespace->dobj.name,
15458 : .description = "TEXT SEARCH PARSER",
15459 : .section = SECTION_PRE_DATA,
15460 : .createStmt = q->data,
15461 : .dropStmt = delq->data));
15462 :
15463 : /* Dump Parser Comments */
15464 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15465 78 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15466 78 : prsinfo->dobj.namespace->dobj.name, "",
15467 78 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15468 :
15469 78 : destroyPQExpBuffer(q);
15470 78 : destroyPQExpBuffer(delq);
15471 78 : free(qprsname);
15472 : }
15473 :
15474 : /*
15475 : * dumpTSDictionary
15476 : * write out a single text search dictionary
15477 : */
15478 : static void
15479 408 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15480 : {
15481 408 : DumpOptions *dopt = fout->dopt;
15482 : PQExpBuffer q;
15483 : PQExpBuffer delq;
15484 : PQExpBuffer query;
15485 : char *qdictname;
15486 : PGresult *res;
15487 : char *nspname;
15488 : char *tmplname;
15489 :
15490 : /* Do nothing if not dumping schema */
15491 408 : if (!dopt->dumpSchema)
15492 12 : return;
15493 :
15494 396 : q = createPQExpBuffer();
15495 396 : delq = createPQExpBuffer();
15496 396 : query = createPQExpBuffer();
15497 :
15498 396 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15499 :
15500 : /* Fetch name and namespace of the dictionary's template */
15501 396 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15502 : "FROM pg_ts_template p, pg_namespace n "
15503 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15504 396 : dictinfo->dicttemplate);
15505 396 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15506 396 : nspname = PQgetvalue(res, 0, 0);
15507 396 : tmplname = PQgetvalue(res, 0, 1);
15508 :
15509 396 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15510 396 : fmtQualifiedDumpable(dictinfo));
15511 :
15512 396 : appendPQExpBufferStr(q, " TEMPLATE = ");
15513 396 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15514 396 : appendPQExpBufferStr(q, fmtId(tmplname));
15515 :
15516 396 : PQclear(res);
15517 :
15518 : /* the dictinitoption can be dumped straight into the command */
15519 396 : if (dictinfo->dictinitoption)
15520 318 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15521 :
15522 396 : appendPQExpBufferStr(q, " );\n");
15523 :
15524 396 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15525 396 : fmtQualifiedDumpable(dictinfo));
15526 :
15527 396 : if (dopt->binary_upgrade)
15528 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15529 : "TEXT SEARCH DICTIONARY", qdictname,
15530 20 : dictinfo->dobj.namespace->dobj.name);
15531 :
15532 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15533 396 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15534 396 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15535 : .namespace = dictinfo->dobj.namespace->dobj.name,
15536 : .owner = dictinfo->rolname,
15537 : .description = "TEXT SEARCH DICTIONARY",
15538 : .section = SECTION_PRE_DATA,
15539 : .createStmt = q->data,
15540 : .dropStmt = delq->data));
15541 :
15542 : /* Dump Dictionary Comments */
15543 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15544 252 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15545 252 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15546 252 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15547 :
15548 396 : destroyPQExpBuffer(q);
15549 396 : destroyPQExpBuffer(delq);
15550 396 : destroyPQExpBuffer(query);
15551 396 : free(qdictname);
15552 : }
15553 :
15554 : /*
15555 : * dumpTSTemplate
15556 : * write out a single text search template
15557 : */
15558 : static void
15559 114 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15560 : {
15561 114 : DumpOptions *dopt = fout->dopt;
15562 : PQExpBuffer q;
15563 : PQExpBuffer delq;
15564 : char *qtmplname;
15565 :
15566 : /* Do nothing if not dumping schema */
15567 114 : if (!dopt->dumpSchema)
15568 12 : return;
15569 :
15570 102 : q = createPQExpBuffer();
15571 102 : delq = createPQExpBuffer();
15572 :
15573 102 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15574 :
15575 102 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15576 102 : fmtQualifiedDumpable(tmplinfo));
15577 :
15578 102 : if (tmplinfo->tmplinit != InvalidOid)
15579 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15580 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15581 102 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15582 102 : convertTSFunction(fout, tmplinfo->tmpllexize));
15583 :
15584 102 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15585 102 : fmtQualifiedDumpable(tmplinfo));
15586 :
15587 102 : if (dopt->binary_upgrade)
15588 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15589 : "TEXT SEARCH TEMPLATE", qtmplname,
15590 2 : tmplinfo->dobj.namespace->dobj.name);
15591 :
15592 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15593 102 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15594 102 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15595 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15596 : .description = "TEXT SEARCH TEMPLATE",
15597 : .section = SECTION_PRE_DATA,
15598 : .createStmt = q->data,
15599 : .dropStmt = delq->data));
15600 :
15601 : /* Dump Template Comments */
15602 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15603 102 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15604 102 : tmplinfo->dobj.namespace->dobj.name, "",
15605 102 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15606 :
15607 102 : destroyPQExpBuffer(q);
15608 102 : destroyPQExpBuffer(delq);
15609 102 : free(qtmplname);
15610 : }
15611 :
15612 : /*
15613 : * dumpTSConfig
15614 : * write out a single text search configuration
15615 : */
15616 : static void
15617 328 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15618 : {
15619 328 : DumpOptions *dopt = fout->dopt;
15620 : PQExpBuffer q;
15621 : PQExpBuffer delq;
15622 : PQExpBuffer query;
15623 : char *qcfgname;
15624 : PGresult *res;
15625 : char *nspname;
15626 : char *prsname;
15627 : int ntups,
15628 : i;
15629 : int i_tokenname;
15630 : int i_dictname;
15631 :
15632 : /* Do nothing if not dumping schema */
15633 328 : if (!dopt->dumpSchema)
15634 12 : return;
15635 :
15636 316 : q = createPQExpBuffer();
15637 316 : delq = createPQExpBuffer();
15638 316 : query = createPQExpBuffer();
15639 :
15640 316 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15641 :
15642 : /* Fetch name and namespace of the config's parser */
15643 316 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15644 : "FROM pg_ts_parser p, pg_namespace n "
15645 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15646 316 : cfginfo->cfgparser);
15647 316 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15648 316 : nspname = PQgetvalue(res, 0, 0);
15649 316 : prsname = PQgetvalue(res, 0, 1);
15650 :
15651 316 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15652 316 : fmtQualifiedDumpable(cfginfo));
15653 :
15654 316 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15655 316 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15656 :
15657 316 : PQclear(res);
15658 :
15659 316 : resetPQExpBuffer(query);
15660 316 : appendPQExpBuffer(query,
15661 : "SELECT\n"
15662 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15663 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15664 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15665 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15666 : "WHERE m.mapcfg = '%u'\n"
15667 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15668 316 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15669 :
15670 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15671 316 : ntups = PQntuples(res);
15672 :
15673 316 : i_tokenname = PQfnumber(res, "tokenname");
15674 316 : i_dictname = PQfnumber(res, "dictname");
15675 :
15676 6752 : for (i = 0; i < ntups; i++)
15677 : {
15678 6436 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15679 6436 : char *dictname = PQgetvalue(res, i, i_dictname);
15680 :
15681 6436 : if (i == 0 ||
15682 6120 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15683 : {
15684 : /* starting a new token type, so start a new command */
15685 6004 : if (i > 0)
15686 5688 : appendPQExpBufferStr(q, ";\n");
15687 6004 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15688 6004 : fmtQualifiedDumpable(cfginfo));
15689 : /* tokenname needs quoting, dictname does NOT */
15690 6004 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15691 : fmtId(tokenname), dictname);
15692 : }
15693 : else
15694 432 : appendPQExpBuffer(q, ", %s", dictname);
15695 : }
15696 :
15697 316 : if (ntups > 0)
15698 316 : appendPQExpBufferStr(q, ";\n");
15699 :
15700 316 : PQclear(res);
15701 :
15702 316 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15703 316 : fmtQualifiedDumpable(cfginfo));
15704 :
15705 316 : if (dopt->binary_upgrade)
15706 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15707 : "TEXT SEARCH CONFIGURATION", qcfgname,
15708 10 : cfginfo->dobj.namespace->dobj.name);
15709 :
15710 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15711 316 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15712 316 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15713 : .namespace = cfginfo->dobj.namespace->dobj.name,
15714 : .owner = cfginfo->rolname,
15715 : .description = "TEXT SEARCH CONFIGURATION",
15716 : .section = SECTION_PRE_DATA,
15717 : .createStmt = q->data,
15718 : .dropStmt = delq->data));
15719 :
15720 : /* Dump Configuration Comments */
15721 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15722 252 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15723 252 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15724 252 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15725 :
15726 316 : destroyPQExpBuffer(q);
15727 316 : destroyPQExpBuffer(delq);
15728 316 : destroyPQExpBuffer(query);
15729 316 : free(qcfgname);
15730 : }
15731 :
15732 : /*
15733 : * dumpForeignDataWrapper
15734 : * write out a single foreign-data wrapper definition
15735 : */
15736 : static void
15737 118 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15738 : {
15739 118 : DumpOptions *dopt = fout->dopt;
15740 : PQExpBuffer q;
15741 : PQExpBuffer delq;
15742 : char *qfdwname;
15743 :
15744 : /* Do nothing if not dumping schema */
15745 118 : if (!dopt->dumpSchema)
15746 14 : return;
15747 :
15748 104 : q = createPQExpBuffer();
15749 104 : delq = createPQExpBuffer();
15750 :
15751 104 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15752 :
15753 104 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15754 : qfdwname);
15755 :
15756 104 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15757 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15758 :
15759 104 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15760 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15761 :
15762 104 : if (strlen(fdwinfo->fdwoptions) > 0)
15763 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15764 :
15765 104 : appendPQExpBufferStr(q, ";\n");
15766 :
15767 104 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15768 : qfdwname);
15769 :
15770 104 : if (dopt->binary_upgrade)
15771 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15772 : "FOREIGN DATA WRAPPER", qfdwname,
15773 : NULL);
15774 :
15775 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15776 104 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15777 104 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15778 : .owner = fdwinfo->rolname,
15779 : .description = "FOREIGN DATA WRAPPER",
15780 : .section = SECTION_PRE_DATA,
15781 : .createStmt = q->data,
15782 : .dropStmt = delq->data));
15783 :
15784 : /* Dump Foreign Data Wrapper Comments */
15785 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15786 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
15787 0 : NULL, fdwinfo->rolname,
15788 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
15789 :
15790 : /* Handle the ACL */
15791 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
15792 70 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15793 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15794 70 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15795 :
15796 104 : free(qfdwname);
15797 :
15798 104 : destroyPQExpBuffer(q);
15799 104 : destroyPQExpBuffer(delq);
15800 : }
15801 :
15802 : /*
15803 : * dumpForeignServer
15804 : * write out a foreign server definition
15805 : */
15806 : static void
15807 126 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15808 : {
15809 126 : DumpOptions *dopt = fout->dopt;
15810 : PQExpBuffer q;
15811 : PQExpBuffer delq;
15812 : PQExpBuffer query;
15813 : PGresult *res;
15814 : char *qsrvname;
15815 : char *fdwname;
15816 :
15817 : /* Do nothing if not dumping schema */
15818 126 : if (!dopt->dumpSchema)
15819 18 : return;
15820 :
15821 108 : q = createPQExpBuffer();
15822 108 : delq = createPQExpBuffer();
15823 108 : query = createPQExpBuffer();
15824 :
15825 108 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15826 :
15827 : /* look up the foreign-data wrapper */
15828 108 : appendPQExpBuffer(query, "SELECT fdwname "
15829 : "FROM pg_foreign_data_wrapper w "
15830 : "WHERE w.oid = '%u'",
15831 108 : srvinfo->srvfdw);
15832 108 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15833 108 : fdwname = PQgetvalue(res, 0, 0);
15834 :
15835 108 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15836 108 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15837 : {
15838 0 : appendPQExpBufferStr(q, " TYPE ");
15839 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15840 : }
15841 108 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15842 : {
15843 0 : appendPQExpBufferStr(q, " VERSION ");
15844 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15845 : }
15846 :
15847 108 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15848 108 : appendPQExpBufferStr(q, fmtId(fdwname));
15849 :
15850 108 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15851 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15852 :
15853 108 : appendPQExpBufferStr(q, ";\n");
15854 :
15855 108 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15856 : qsrvname);
15857 :
15858 108 : if (dopt->binary_upgrade)
15859 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15860 : "SERVER", qsrvname, NULL);
15861 :
15862 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15863 108 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15864 108 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15865 : .owner = srvinfo->rolname,
15866 : .description = "SERVER",
15867 : .section = SECTION_PRE_DATA,
15868 : .createStmt = q->data,
15869 : .dropStmt = delq->data));
15870 :
15871 : /* Dump Foreign Server Comments */
15872 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15873 0 : dumpComment(fout, "SERVER", qsrvname,
15874 0 : NULL, srvinfo->rolname,
15875 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15876 :
15877 : /* Handle the ACL */
15878 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15879 70 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15880 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15881 70 : NULL, srvinfo->rolname, &srvinfo->dacl);
15882 :
15883 : /* Dump user mappings */
15884 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15885 108 : dumpUserMappings(fout,
15886 108 : srvinfo->dobj.name, NULL,
15887 108 : srvinfo->rolname,
15888 108 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15889 :
15890 108 : PQclear(res);
15891 :
15892 108 : free(qsrvname);
15893 :
15894 108 : destroyPQExpBuffer(q);
15895 108 : destroyPQExpBuffer(delq);
15896 108 : destroyPQExpBuffer(query);
15897 : }
15898 :
15899 : /*
15900 : * dumpUserMappings
15901 : *
15902 : * This routine is used to dump any user mappings associated with the
15903 : * server handed to this routine. Should be called after ArchiveEntry()
15904 : * for the server.
15905 : */
15906 : static void
15907 108 : dumpUserMappings(Archive *fout,
15908 : const char *servername, const char *namespace,
15909 : const char *owner,
15910 : CatalogId catalogId, DumpId dumpId)
15911 : {
15912 : PQExpBuffer q;
15913 : PQExpBuffer delq;
15914 : PQExpBuffer query;
15915 : PQExpBuffer tag;
15916 : PGresult *res;
15917 : int ntups;
15918 : int i_usename;
15919 : int i_umoptions;
15920 : int i;
15921 :
15922 108 : q = createPQExpBuffer();
15923 108 : tag = createPQExpBuffer();
15924 108 : delq = createPQExpBuffer();
15925 108 : query = createPQExpBuffer();
15926 :
15927 : /*
15928 : * We read from the publicly accessible view pg_user_mappings, so as not
15929 : * to fail if run by a non-superuser. Note that the view will show
15930 : * umoptions as null if the user hasn't got privileges for the associated
15931 : * server; this means that pg_dump will dump such a mapping, but with no
15932 : * OPTIONS clause. A possible alternative is to skip such mappings
15933 : * altogether, but it's not clear that that's an improvement.
15934 : */
15935 108 : appendPQExpBuffer(query,
15936 : "SELECT usename, "
15937 : "array_to_string(ARRAY("
15938 : "SELECT quote_ident(option_name) || ' ' || "
15939 : "quote_literal(option_value) "
15940 : "FROM pg_options_to_table(umoptions) "
15941 : "ORDER BY option_name"
15942 : "), E',\n ') AS umoptions "
15943 : "FROM pg_user_mappings "
15944 : "WHERE srvid = '%u' "
15945 : "ORDER BY usename",
15946 : catalogId.oid);
15947 :
15948 108 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15949 :
15950 108 : ntups = PQntuples(res);
15951 108 : i_usename = PQfnumber(res, "usename");
15952 108 : i_umoptions = PQfnumber(res, "umoptions");
15953 :
15954 178 : for (i = 0; i < ntups; i++)
15955 : {
15956 : char *usename;
15957 : char *umoptions;
15958 :
15959 70 : usename = PQgetvalue(res, i, i_usename);
15960 70 : umoptions = PQgetvalue(res, i, i_umoptions);
15961 :
15962 70 : resetPQExpBuffer(q);
15963 70 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15964 70 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15965 :
15966 70 : if (umoptions && strlen(umoptions) > 0)
15967 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15968 :
15969 70 : appendPQExpBufferStr(q, ";\n");
15970 :
15971 70 : resetPQExpBuffer(delq);
15972 70 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15973 70 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15974 :
15975 70 : resetPQExpBuffer(tag);
15976 70 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15977 : usename, servername);
15978 :
15979 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15980 70 : ARCHIVE_OPTS(.tag = tag->data,
15981 : .namespace = namespace,
15982 : .owner = owner,
15983 : .description = "USER MAPPING",
15984 : .section = SECTION_PRE_DATA,
15985 : .createStmt = q->data,
15986 : .dropStmt = delq->data));
15987 : }
15988 :
15989 108 : PQclear(res);
15990 :
15991 108 : destroyPQExpBuffer(query);
15992 108 : destroyPQExpBuffer(delq);
15993 108 : destroyPQExpBuffer(tag);
15994 108 : destroyPQExpBuffer(q);
15995 108 : }
15996 :
15997 : /*
15998 : * Write out default privileges information
15999 : */
16000 : static void
16001 332 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16002 : {
16003 332 : DumpOptions *dopt = fout->dopt;
16004 : PQExpBuffer q;
16005 : PQExpBuffer tag;
16006 : const char *type;
16007 :
16008 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16009 332 : if (!dopt->dumpSchema || dopt->aclsSkip)
16010 56 : return;
16011 :
16012 276 : q = createPQExpBuffer();
16013 276 : tag = createPQExpBuffer();
16014 :
16015 276 : switch (daclinfo->defaclobjtype)
16016 : {
16017 138 : case DEFACLOBJ_RELATION:
16018 138 : type = "TABLES";
16019 138 : break;
16020 0 : case DEFACLOBJ_SEQUENCE:
16021 0 : type = "SEQUENCES";
16022 0 : break;
16023 138 : case DEFACLOBJ_FUNCTION:
16024 138 : type = "FUNCTIONS";
16025 138 : break;
16026 0 : case DEFACLOBJ_TYPE:
16027 0 : type = "TYPES";
16028 0 : break;
16029 0 : case DEFACLOBJ_NAMESPACE:
16030 0 : type = "SCHEMAS";
16031 0 : break;
16032 0 : case DEFACLOBJ_LARGEOBJECT:
16033 0 : type = "LARGE OBJECTS";
16034 0 : break;
16035 0 : default:
16036 : /* shouldn't get here */
16037 0 : pg_fatal("unrecognized object type in default privileges: %d",
16038 : (int) daclinfo->defaclobjtype);
16039 : type = ""; /* keep compiler quiet */
16040 : }
16041 :
16042 276 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16043 :
16044 : /* build the actual command(s) for this tuple */
16045 276 : if (!buildDefaultACLCommands(type,
16046 276 : daclinfo->dobj.namespace != NULL ?
16047 140 : daclinfo->dobj.namespace->dobj.name : NULL,
16048 276 : daclinfo->dacl.acl,
16049 276 : daclinfo->dacl.acldefault,
16050 276 : daclinfo->defaclrole,
16051 : fout->remoteVersion,
16052 : q))
16053 0 : pg_fatal("could not parse default ACL list (%s)",
16054 : daclinfo->dacl.acl);
16055 :
16056 276 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16057 276 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16058 276 : ARCHIVE_OPTS(.tag = tag->data,
16059 : .namespace = daclinfo->dobj.namespace ?
16060 : daclinfo->dobj.namespace->dobj.name : NULL,
16061 : .owner = daclinfo->defaclrole,
16062 : .description = "DEFAULT ACL",
16063 : .section = SECTION_POST_DATA,
16064 : .createStmt = q->data));
16065 :
16066 276 : destroyPQExpBuffer(tag);
16067 276 : destroyPQExpBuffer(q);
16068 : }
16069 :
16070 : /*----------
16071 : * Write out grant/revoke information
16072 : *
16073 : * 'objDumpId' is the dump ID of the underlying object.
16074 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16075 : * or InvalidDumpId if there is no need for a second dependency.
16076 : * 'type' must be one of
16077 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16078 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16079 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16080 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16081 : * (Currently we assume that subname is only provided for table columns.)
16082 : * 'nspname' is the namespace the object is in (NULL if none).
16083 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16084 : * to use the default for the object type.
16085 : * 'owner' is the owner, NULL if there is no owner (for languages).
16086 : * 'dacl' is the DumpableAcl struct for the object.
16087 : *
16088 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16089 : * no ACL entry was created.
16090 : *----------
16091 : */
16092 : static DumpId
16093 72052 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16094 : const char *type, const char *name, const char *subname,
16095 : const char *nspname, const char *tag, const char *owner,
16096 : const DumpableAcl *dacl)
16097 : {
16098 72052 : DumpId aclDumpId = InvalidDumpId;
16099 72052 : DumpOptions *dopt = fout->dopt;
16100 72052 : const char *acls = dacl->acl;
16101 72052 : const char *acldefault = dacl->acldefault;
16102 72052 : char privtype = dacl->privtype;
16103 72052 : const char *initprivs = dacl->initprivs;
16104 : const char *baseacls;
16105 : PQExpBuffer sql;
16106 :
16107 : /* Do nothing if ACL dump is not enabled */
16108 72052 : if (dopt->aclsSkip)
16109 644 : return InvalidDumpId;
16110 :
16111 : /* --data-only skips ACLs *except* large object ACLs */
16112 71408 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16113 0 : return InvalidDumpId;
16114 :
16115 71408 : sql = createPQExpBuffer();
16116 :
16117 : /*
16118 : * In binary upgrade mode, we don't run an extension's script but instead
16119 : * dump out the objects independently and then recreate them. To preserve
16120 : * any initial privileges which were set on extension objects, we need to
16121 : * compute the set of GRANT and REVOKE commands necessary to get from the
16122 : * default privileges of an object to its initial privileges as recorded
16123 : * in pg_init_privs.
16124 : *
16125 : * At restore time, we apply these commands after having called
16126 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16127 : * copy the results into pg_init_privs. This is how we preserve the
16128 : * contents of that catalog across binary upgrades.
16129 : */
16130 71408 : if (dopt->binary_upgrade && privtype == 'e' &&
16131 26 : initprivs && *initprivs != '\0')
16132 : {
16133 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16134 26 : if (!buildACLCommands(name, subname, nspname, type,
16135 : initprivs, acldefault, owner,
16136 : "", fout->remoteVersion, sql))
16137 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16138 : initprivs, acldefault, name, type);
16139 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16140 : }
16141 :
16142 : /*
16143 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16144 : * actual current ACL, starting from the initprivs if given, else from the
16145 : * object-type-specific default. Also, while buildACLCommands will assume
16146 : * that a NULL/empty acls string means it needn't do anything, what that
16147 : * actually represents is the object-type-specific default; so we need to
16148 : * substitute the acldefault string to get the right results in that case.
16149 : */
16150 71408 : if (initprivs && *initprivs != '\0')
16151 : {
16152 67330 : baseacls = initprivs;
16153 67330 : if (acls == NULL || *acls == '\0')
16154 34 : acls = acldefault;
16155 : }
16156 : else
16157 4078 : baseacls = acldefault;
16158 :
16159 71408 : if (!buildACLCommands(name, subname, nspname, type,
16160 : acls, baseacls, owner,
16161 : "", fout->remoteVersion, sql))
16162 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16163 : acls, baseacls, name, type);
16164 :
16165 71408 : if (sql->len > 0)
16166 : {
16167 4138 : PQExpBuffer tagbuf = createPQExpBuffer();
16168 : DumpId aclDeps[2];
16169 4138 : int nDeps = 0;
16170 :
16171 4138 : if (tag)
16172 0 : appendPQExpBufferStr(tagbuf, tag);
16173 4138 : else if (subname)
16174 2368 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16175 : else
16176 1770 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16177 :
16178 4138 : aclDeps[nDeps++] = objDumpId;
16179 4138 : if (altDumpId != InvalidDumpId)
16180 2174 : aclDeps[nDeps++] = altDumpId;
16181 :
16182 4138 : aclDumpId = createDumpId();
16183 :
16184 4138 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16185 4138 : ARCHIVE_OPTS(.tag = tagbuf->data,
16186 : .namespace = nspname,
16187 : .owner = owner,
16188 : .description = "ACL",
16189 : .section = SECTION_NONE,
16190 : .createStmt = sql->data,
16191 : .deps = aclDeps,
16192 : .nDeps = nDeps));
16193 :
16194 4138 : destroyPQExpBuffer(tagbuf);
16195 : }
16196 :
16197 71408 : destroyPQExpBuffer(sql);
16198 :
16199 71408 : return aclDumpId;
16200 : }
16201 :
16202 : /*
16203 : * dumpSecLabel
16204 : *
16205 : * This routine is used to dump any security labels associated with the
16206 : * object handed to this routine. The routine takes the object type
16207 : * and object name (ready to print, except for schema decoration), plus
16208 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16209 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16210 : * plus the dump ID for the object (for setting a dependency).
16211 : * If a matching pg_seclabel entry is found, it is dumped.
16212 : *
16213 : * Note: although this routine takes a dumpId for dependency purposes,
16214 : * that purpose is just to mark the dependency in the emitted dump file
16215 : * for possible future use by pg_restore. We do NOT use it for determining
16216 : * ordering of the label in the dump file, because this routine is called
16217 : * after dependency sorting occurs. This routine should be called just after
16218 : * calling ArchiveEntry() for the specified object.
16219 : */
16220 : static void
16221 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16222 : const char *namespace, const char *owner,
16223 : CatalogId catalogId, int subid, DumpId dumpId)
16224 : {
16225 0 : DumpOptions *dopt = fout->dopt;
16226 : SecLabelItem *labels;
16227 : int nlabels;
16228 : int i;
16229 : PQExpBuffer query;
16230 :
16231 : /* do nothing, if --no-security-labels is supplied */
16232 0 : if (dopt->no_security_labels)
16233 0 : return;
16234 :
16235 : /*
16236 : * Security labels are schema not data ... except large object labels are
16237 : * data
16238 : */
16239 0 : if (strcmp(type, "LARGE OBJECT") != 0)
16240 : {
16241 0 : if (!dopt->dumpSchema)
16242 0 : return;
16243 : }
16244 : else
16245 : {
16246 : /* We do dump large object security labels in binary-upgrade mode */
16247 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
16248 0 : return;
16249 : }
16250 :
16251 : /* Search for security labels associated with catalogId, using table */
16252 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16253 :
16254 0 : query = createPQExpBuffer();
16255 :
16256 0 : for (i = 0; i < nlabels; i++)
16257 : {
16258 : /*
16259 : * Ignore label entries for which the subid doesn't match.
16260 : */
16261 0 : if (labels[i].objsubid != subid)
16262 0 : continue;
16263 :
16264 0 : appendPQExpBuffer(query,
16265 : "SECURITY LABEL FOR %s ON %s ",
16266 0 : fmtId(labels[i].provider), type);
16267 0 : if (namespace && *namespace)
16268 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16269 0 : appendPQExpBuffer(query, "%s IS ", name);
16270 0 : appendStringLiteralAH(query, labels[i].label, fout);
16271 0 : appendPQExpBufferStr(query, ";\n");
16272 : }
16273 :
16274 0 : if (query->len > 0)
16275 : {
16276 0 : PQExpBuffer tag = createPQExpBuffer();
16277 :
16278 0 : appendPQExpBuffer(tag, "%s %s", type, name);
16279 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16280 0 : ARCHIVE_OPTS(.tag = tag->data,
16281 : .namespace = namespace,
16282 : .owner = owner,
16283 : .description = "SECURITY LABEL",
16284 : .section = SECTION_NONE,
16285 : .createStmt = query->data,
16286 : .deps = &dumpId,
16287 : .nDeps = 1));
16288 0 : destroyPQExpBuffer(tag);
16289 : }
16290 :
16291 0 : destroyPQExpBuffer(query);
16292 : }
16293 :
16294 : /*
16295 : * dumpTableSecLabel
16296 : *
16297 : * As above, but dump security label for both the specified table (or view)
16298 : * and its columns.
16299 : */
16300 : static void
16301 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16302 : {
16303 0 : DumpOptions *dopt = fout->dopt;
16304 : SecLabelItem *labels;
16305 : int nlabels;
16306 : int i;
16307 : PQExpBuffer query;
16308 : PQExpBuffer target;
16309 :
16310 : /* do nothing, if --no-security-labels is supplied */
16311 0 : if (dopt->no_security_labels)
16312 0 : return;
16313 :
16314 : /* SecLabel are SCHEMA not data */
16315 0 : if (!dopt->dumpSchema)
16316 0 : return;
16317 :
16318 : /* Search for comments associated with relation, using table */
16319 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16320 0 : tbinfo->dobj.catId.oid,
16321 : &labels);
16322 :
16323 : /* If security labels exist, build SECURITY LABEL statements */
16324 0 : if (nlabels <= 0)
16325 0 : return;
16326 :
16327 0 : query = createPQExpBuffer();
16328 0 : target = createPQExpBuffer();
16329 :
16330 0 : for (i = 0; i < nlabels; i++)
16331 : {
16332 : const char *colname;
16333 0 : const char *provider = labels[i].provider;
16334 0 : const char *label = labels[i].label;
16335 0 : int objsubid = labels[i].objsubid;
16336 :
16337 0 : resetPQExpBuffer(target);
16338 0 : if (objsubid == 0)
16339 : {
16340 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16341 0 : fmtQualifiedDumpable(tbinfo));
16342 : }
16343 : else
16344 : {
16345 0 : colname = getAttrName(objsubid, tbinfo);
16346 : /* first fmtXXX result must be consumed before calling again */
16347 0 : appendPQExpBuffer(target, "COLUMN %s",
16348 0 : fmtQualifiedDumpable(tbinfo));
16349 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16350 : }
16351 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16352 : fmtId(provider), target->data);
16353 0 : appendStringLiteralAH(query, label, fout);
16354 0 : appendPQExpBufferStr(query, ";\n");
16355 : }
16356 0 : if (query->len > 0)
16357 : {
16358 0 : resetPQExpBuffer(target);
16359 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16360 0 : fmtId(tbinfo->dobj.name));
16361 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16362 0 : ARCHIVE_OPTS(.tag = target->data,
16363 : .namespace = tbinfo->dobj.namespace->dobj.name,
16364 : .owner = tbinfo->rolname,
16365 : .description = "SECURITY LABEL",
16366 : .section = SECTION_NONE,
16367 : .createStmt = query->data,
16368 : .deps = &(tbinfo->dobj.dumpId),
16369 : .nDeps = 1));
16370 : }
16371 0 : destroyPQExpBuffer(query);
16372 0 : destroyPQExpBuffer(target);
16373 : }
16374 :
16375 : /*
16376 : * findSecLabels
16377 : *
16378 : * Find the security label(s), if any, associated with the given object.
16379 : * All the objsubid values associated with the given classoid/objoid are
16380 : * found with one search.
16381 : */
16382 : static int
16383 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16384 : {
16385 0 : SecLabelItem *middle = NULL;
16386 : SecLabelItem *low;
16387 : SecLabelItem *high;
16388 : int nmatch;
16389 :
16390 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16391 : {
16392 0 : *items = NULL;
16393 0 : return 0;
16394 : }
16395 :
16396 : /*
16397 : * Do binary search to find some item matching the object.
16398 : */
16399 0 : low = &seclabels[0];
16400 0 : high = &seclabels[nseclabels - 1];
16401 0 : while (low <= high)
16402 : {
16403 0 : middle = low + (high - low) / 2;
16404 :
16405 0 : if (classoid < middle->classoid)
16406 0 : high = middle - 1;
16407 0 : else if (classoid > middle->classoid)
16408 0 : low = middle + 1;
16409 0 : else if (objoid < middle->objoid)
16410 0 : high = middle - 1;
16411 0 : else if (objoid > middle->objoid)
16412 0 : low = middle + 1;
16413 : else
16414 0 : break; /* found a match */
16415 : }
16416 :
16417 0 : if (low > high) /* no matches */
16418 : {
16419 0 : *items = NULL;
16420 0 : return 0;
16421 : }
16422 :
16423 : /*
16424 : * Now determine how many items match the object. The search loop
16425 : * invariant still holds: only items between low and high inclusive could
16426 : * match.
16427 : */
16428 0 : nmatch = 1;
16429 0 : while (middle > low)
16430 : {
16431 0 : if (classoid != middle[-1].classoid ||
16432 0 : objoid != middle[-1].objoid)
16433 : break;
16434 0 : middle--;
16435 0 : nmatch++;
16436 : }
16437 :
16438 0 : *items = middle;
16439 :
16440 0 : middle += nmatch;
16441 0 : while (middle <= high)
16442 : {
16443 0 : if (classoid != middle->classoid ||
16444 0 : objoid != middle->objoid)
16445 : break;
16446 0 : middle++;
16447 0 : nmatch++;
16448 : }
16449 :
16450 0 : return nmatch;
16451 : }
16452 :
16453 : /*
16454 : * collectSecLabels
16455 : *
16456 : * Construct a table of all security labels available for database objects;
16457 : * also set the has-seclabel component flag for each relevant object.
16458 : *
16459 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16460 : */
16461 : static void
16462 468 : collectSecLabels(Archive *fout)
16463 : {
16464 : PGresult *res;
16465 : PQExpBuffer query;
16466 : int i_label;
16467 : int i_provider;
16468 : int i_classoid;
16469 : int i_objoid;
16470 : int i_objsubid;
16471 : int ntups;
16472 : int i;
16473 : DumpableObject *dobj;
16474 :
16475 468 : query = createPQExpBuffer();
16476 :
16477 468 : appendPQExpBufferStr(query,
16478 : "SELECT label, provider, classoid, objoid, objsubid "
16479 : "FROM pg_catalog.pg_seclabel "
16480 : "ORDER BY classoid, objoid, objsubid");
16481 :
16482 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16483 :
16484 : /* Construct lookup table containing OIDs in numeric form */
16485 468 : i_label = PQfnumber(res, "label");
16486 468 : i_provider = PQfnumber(res, "provider");
16487 468 : i_classoid = PQfnumber(res, "classoid");
16488 468 : i_objoid = PQfnumber(res, "objoid");
16489 468 : i_objsubid = PQfnumber(res, "objsubid");
16490 :
16491 468 : ntups = PQntuples(res);
16492 :
16493 468 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16494 468 : nseclabels = 0;
16495 468 : dobj = NULL;
16496 :
16497 468 : for (i = 0; i < ntups; i++)
16498 : {
16499 : CatalogId objId;
16500 : int subid;
16501 :
16502 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16503 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16504 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16505 :
16506 : /* We needn't remember labels that don't match any dumpable object */
16507 0 : if (dobj == NULL ||
16508 0 : dobj->catId.tableoid != objId.tableoid ||
16509 0 : dobj->catId.oid != objId.oid)
16510 0 : dobj = findObjectByCatalogId(objId);
16511 0 : if (dobj == NULL)
16512 0 : continue;
16513 :
16514 : /*
16515 : * Labels on columns of composite types are linked to the type's
16516 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16517 : * in the type's own DumpableObject.
16518 : */
16519 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16520 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16521 0 : {
16522 : TypeInfo *cTypeInfo;
16523 :
16524 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16525 0 : if (cTypeInfo)
16526 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16527 : }
16528 : else
16529 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16530 :
16531 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16532 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16533 0 : seclabels[nseclabels].classoid = objId.tableoid;
16534 0 : seclabels[nseclabels].objoid = objId.oid;
16535 0 : seclabels[nseclabels].objsubid = subid;
16536 0 : nseclabels++;
16537 : }
16538 :
16539 468 : PQclear(res);
16540 468 : destroyPQExpBuffer(query);
16541 468 : }
16542 :
16543 : /*
16544 : * dumpTable
16545 : * write out to fout the declarations (not data) of a user-defined table
16546 : */
16547 : static void
16548 80688 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16549 : {
16550 80688 : DumpOptions *dopt = fout->dopt;
16551 80688 : DumpId tableAclDumpId = InvalidDumpId;
16552 : char *namecopy;
16553 :
16554 : /* Do nothing if not dumping schema */
16555 80688 : if (!dopt->dumpSchema)
16556 2984 : return;
16557 :
16558 77704 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16559 : {
16560 18646 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16561 1106 : dumpSequence(fout, tbinfo);
16562 : else
16563 17540 : dumpTableSchema(fout, tbinfo);
16564 : }
16565 :
16566 : /* Handle the ACL here */
16567 77704 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16568 77704 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16569 : {
16570 60626 : const char *objtype =
16571 60626 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16572 :
16573 : tableAclDumpId =
16574 60626 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16575 : objtype, namecopy, NULL,
16576 60626 : tbinfo->dobj.namespace->dobj.name,
16577 60626 : NULL, tbinfo->rolname, &tbinfo->dacl);
16578 : }
16579 :
16580 : /*
16581 : * Handle column ACLs, if any. Note: we pull these with a separate query
16582 : * rather than trying to fetch them during getTableAttrs, so that we won't
16583 : * miss ACLs on system columns. Doing it this way also allows us to dump
16584 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16585 : */
16586 77704 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16587 : {
16588 694 : PQExpBuffer query = createPQExpBuffer();
16589 : PGresult *res;
16590 : int i;
16591 :
16592 694 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16593 : {
16594 : /* Set up query for column ACLs */
16595 416 : appendPQExpBufferStr(query,
16596 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16597 :
16598 416 : if (fout->remoteVersion >= 90600)
16599 : {
16600 : /*
16601 : * In principle we should call acldefault('c', relowner) to
16602 : * get the default ACL for a column. However, we don't
16603 : * currently store the numeric OID of the relowner in
16604 : * TableInfo. We could convert the owner name using regrole,
16605 : * but that creates a risk of failure due to concurrent role
16606 : * renames. Given that the default ACL for columns is empty
16607 : * and is likely to stay that way, it's not worth extra cycles
16608 : * and risk to avoid hard-wiring that knowledge here.
16609 : */
16610 416 : appendPQExpBufferStr(query,
16611 : "SELECT at.attname, "
16612 : "at.attacl, "
16613 : "'{}' AS acldefault, "
16614 : "pip.privtype, pip.initprivs "
16615 : "FROM pg_catalog.pg_attribute at "
16616 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16617 : "(at.attrelid = pip.objoid "
16618 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16619 : "AND at.attnum = pip.objsubid) "
16620 : "WHERE at.attrelid = $1 AND "
16621 : "NOT at.attisdropped "
16622 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16623 : "ORDER BY at.attnum");
16624 : }
16625 : else
16626 : {
16627 0 : appendPQExpBufferStr(query,
16628 : "SELECT attname, attacl, '{}' AS acldefault, "
16629 : "NULL AS privtype, NULL AS initprivs "
16630 : "FROM pg_catalog.pg_attribute "
16631 : "WHERE attrelid = $1 AND NOT attisdropped "
16632 : "AND attacl IS NOT NULL "
16633 : "ORDER BY attnum");
16634 : }
16635 :
16636 416 : ExecuteSqlStatement(fout, query->data);
16637 :
16638 416 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16639 : }
16640 :
16641 694 : printfPQExpBuffer(query,
16642 : "EXECUTE getColumnACLs('%u')",
16643 694 : tbinfo->dobj.catId.oid);
16644 :
16645 694 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16646 :
16647 10032 : for (i = 0; i < PQntuples(res); i++)
16648 : {
16649 9338 : char *attname = PQgetvalue(res, i, 0);
16650 9338 : char *attacl = PQgetvalue(res, i, 1);
16651 9338 : char *acldefault = PQgetvalue(res, i, 2);
16652 9338 : char privtype = *(PQgetvalue(res, i, 3));
16653 9338 : char *initprivs = PQgetvalue(res, i, 4);
16654 : DumpableAcl coldacl;
16655 : char *attnamecopy;
16656 :
16657 9338 : coldacl.acl = attacl;
16658 9338 : coldacl.acldefault = acldefault;
16659 9338 : coldacl.privtype = privtype;
16660 9338 : coldacl.initprivs = initprivs;
16661 9338 : attnamecopy = pg_strdup(fmtId(attname));
16662 :
16663 : /*
16664 : * Column's GRANT type is always TABLE. Each column ACL depends
16665 : * on the table-level ACL, since we can restore column ACLs in
16666 : * parallel but the table-level ACL has to be done first.
16667 : */
16668 9338 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16669 : "TABLE", namecopy, attnamecopy,
16670 9338 : tbinfo->dobj.namespace->dobj.name,
16671 9338 : NULL, tbinfo->rolname, &coldacl);
16672 9338 : free(attnamecopy);
16673 : }
16674 694 : PQclear(res);
16675 694 : destroyPQExpBuffer(query);
16676 : }
16677 :
16678 77704 : free(namecopy);
16679 : }
16680 :
16681 : /*
16682 : * Create the AS clause for a view or materialized view. The semicolon is
16683 : * stripped because a materialized view must add a WITH NO DATA clause.
16684 : *
16685 : * This returns a new buffer which must be freed by the caller.
16686 : */
16687 : static PQExpBuffer
16688 2216 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16689 : {
16690 2216 : PQExpBuffer query = createPQExpBuffer();
16691 2216 : PQExpBuffer result = createPQExpBuffer();
16692 : PGresult *res;
16693 : int len;
16694 :
16695 : /* Fetch the view definition */
16696 2216 : appendPQExpBuffer(query,
16697 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16698 2216 : tbinfo->dobj.catId.oid);
16699 :
16700 2216 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16701 :
16702 2216 : if (PQntuples(res) != 1)
16703 : {
16704 0 : if (PQntuples(res) < 1)
16705 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16706 : tbinfo->dobj.name);
16707 : else
16708 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16709 : tbinfo->dobj.name);
16710 : }
16711 :
16712 2216 : len = PQgetlength(res, 0, 0);
16713 :
16714 2216 : if (len == 0)
16715 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16716 : tbinfo->dobj.name);
16717 :
16718 : /* Strip off the trailing semicolon so that other things may follow. */
16719 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16720 2216 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16721 :
16722 2216 : PQclear(res);
16723 2216 : destroyPQExpBuffer(query);
16724 :
16725 2216 : return result;
16726 : }
16727 :
16728 : /*
16729 : * Create a dummy AS clause for a view. This is used when the real view
16730 : * definition has to be postponed because of circular dependencies.
16731 : * We must duplicate the view's external properties -- column names and types
16732 : * (including collation) -- so that it works for subsequent references.
16733 : *
16734 : * This returns a new buffer which must be freed by the caller.
16735 : */
16736 : static PQExpBuffer
16737 64 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16738 : {
16739 64 : PQExpBuffer result = createPQExpBuffer();
16740 : int j;
16741 :
16742 64 : appendPQExpBufferStr(result, "SELECT");
16743 :
16744 128 : for (j = 0; j < tbinfo->numatts; j++)
16745 : {
16746 64 : if (j > 0)
16747 32 : appendPQExpBufferChar(result, ',');
16748 64 : appendPQExpBufferStr(result, "\n ");
16749 :
16750 64 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16751 :
16752 : /*
16753 : * Must add collation if not default for the type, because CREATE OR
16754 : * REPLACE VIEW won't change it
16755 : */
16756 64 : if (OidIsValid(tbinfo->attcollation[j]))
16757 : {
16758 : CollInfo *coll;
16759 :
16760 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16761 0 : if (coll)
16762 0 : appendPQExpBuffer(result, " COLLATE %s",
16763 0 : fmtQualifiedDumpable(coll));
16764 : }
16765 :
16766 64 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16767 : }
16768 :
16769 64 : return result;
16770 : }
16771 :
16772 : /*
16773 : * dumpTableSchema
16774 : * write the declaration (not data) of one user-defined table or view
16775 : */
16776 : static void
16777 17540 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16778 : {
16779 17540 : DumpOptions *dopt = fout->dopt;
16780 17540 : PQExpBuffer q = createPQExpBuffer();
16781 17540 : PQExpBuffer delq = createPQExpBuffer();
16782 17540 : PQExpBuffer extra = createPQExpBuffer();
16783 : char *qrelname;
16784 : char *qualrelname;
16785 : int numParents;
16786 : TableInfo **parents;
16787 : int actual_atts; /* number of attrs in this CREATE statement */
16788 : const char *reltypename;
16789 : char *storage;
16790 : int j,
16791 : k;
16792 :
16793 : /* We had better have loaded per-column details about this table */
16794 : Assert(tbinfo->interesting);
16795 :
16796 17540 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16797 17540 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16798 :
16799 17540 : if (tbinfo->hasoids)
16800 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16801 : qrelname);
16802 :
16803 17540 : if (dopt->binary_upgrade)
16804 1696 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16805 :
16806 : /* Is it a table or a view? */
16807 17540 : if (tbinfo->relkind == RELKIND_VIEW)
16808 : {
16809 : PQExpBuffer result;
16810 :
16811 : /*
16812 : * Note: keep this code in sync with the is_view case in dumpRule()
16813 : */
16814 :
16815 1368 : reltypename = "VIEW";
16816 :
16817 1368 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16818 :
16819 1368 : if (dopt->binary_upgrade)
16820 104 : binary_upgrade_set_pg_class_oids(fout, q,
16821 104 : tbinfo->dobj.catId.oid);
16822 :
16823 1368 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16824 :
16825 1368 : if (tbinfo->dummy_view)
16826 32 : result = createDummyViewAsClause(fout, tbinfo);
16827 : else
16828 : {
16829 1336 : if (nonemptyReloptions(tbinfo->reloptions))
16830 : {
16831 154 : appendPQExpBufferStr(q, " WITH (");
16832 154 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16833 154 : appendPQExpBufferChar(q, ')');
16834 : }
16835 1336 : result = createViewAsClause(fout, tbinfo);
16836 : }
16837 1368 : appendPQExpBuffer(q, " AS\n%s", result->data);
16838 1368 : destroyPQExpBuffer(result);
16839 :
16840 1368 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16841 72 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16842 1368 : appendPQExpBufferStr(q, ";\n");
16843 : }
16844 : else
16845 : {
16846 16172 : char *partkeydef = NULL;
16847 16172 : char *ftoptions = NULL;
16848 16172 : char *srvname = NULL;
16849 16172 : const char *foreign = "";
16850 :
16851 : /*
16852 : * Set reltypename, and collect any relkind-specific data that we
16853 : * didn't fetch during getTables().
16854 : */
16855 16172 : switch (tbinfo->relkind)
16856 : {
16857 1616 : case RELKIND_PARTITIONED_TABLE:
16858 : {
16859 1616 : PQExpBuffer query = createPQExpBuffer();
16860 : PGresult *res;
16861 :
16862 1616 : reltypename = "TABLE";
16863 :
16864 : /* retrieve partition key definition */
16865 1616 : appendPQExpBuffer(query,
16866 : "SELECT pg_get_partkeydef('%u')",
16867 1616 : tbinfo->dobj.catId.oid);
16868 1616 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16869 1616 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16870 1616 : PQclear(res);
16871 1616 : destroyPQExpBuffer(query);
16872 1616 : break;
16873 : }
16874 76 : case RELKIND_FOREIGN_TABLE:
16875 : {
16876 76 : PQExpBuffer query = createPQExpBuffer();
16877 : PGresult *res;
16878 : int i_srvname;
16879 : int i_ftoptions;
16880 :
16881 76 : reltypename = "FOREIGN TABLE";
16882 :
16883 : /* retrieve name of foreign server and generic options */
16884 76 : appendPQExpBuffer(query,
16885 : "SELECT fs.srvname, "
16886 : "pg_catalog.array_to_string(ARRAY("
16887 : "SELECT pg_catalog.quote_ident(option_name) || "
16888 : "' ' || pg_catalog.quote_literal(option_value) "
16889 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16890 : "ORDER BY option_name"
16891 : "), E',\n ') AS ftoptions "
16892 : "FROM pg_catalog.pg_foreign_table ft "
16893 : "JOIN pg_catalog.pg_foreign_server fs "
16894 : "ON (fs.oid = ft.ftserver) "
16895 : "WHERE ft.ftrelid = '%u'",
16896 76 : tbinfo->dobj.catId.oid);
16897 76 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16898 76 : i_srvname = PQfnumber(res, "srvname");
16899 76 : i_ftoptions = PQfnumber(res, "ftoptions");
16900 76 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16901 76 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16902 76 : PQclear(res);
16903 76 : destroyPQExpBuffer(query);
16904 :
16905 76 : foreign = "FOREIGN ";
16906 76 : break;
16907 : }
16908 848 : case RELKIND_MATVIEW:
16909 848 : reltypename = "MATERIALIZED VIEW";
16910 848 : break;
16911 13632 : default:
16912 13632 : reltypename = "TABLE";
16913 13632 : break;
16914 : }
16915 :
16916 16172 : numParents = tbinfo->numParents;
16917 16172 : parents = tbinfo->parents;
16918 :
16919 16172 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16920 :
16921 16172 : if (dopt->binary_upgrade)
16922 1592 : binary_upgrade_set_pg_class_oids(fout, q,
16923 1592 : tbinfo->dobj.catId.oid);
16924 :
16925 : /*
16926 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16927 : * ignore it when dumping if it was set in this case.
16928 : */
16929 16172 : appendPQExpBuffer(q, "CREATE %s%s %s",
16930 16172 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16931 64 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16932 : "UNLOGGED " : "",
16933 : reltypename,
16934 : qualrelname);
16935 :
16936 : /*
16937 : * Attach to type, if reloftype; except in case of a binary upgrade,
16938 : * we dump the table normally and attach it to the type afterward.
16939 : */
16940 16172 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16941 84 : appendPQExpBuffer(q, " OF %s",
16942 84 : getFormattedTypeName(fout, tbinfo->reloftype,
16943 : zeroIsError));
16944 :
16945 16172 : if (tbinfo->relkind != RELKIND_MATVIEW)
16946 : {
16947 : /* Dump the attributes */
16948 15324 : actual_atts = 0;
16949 73386 : for (j = 0; j < tbinfo->numatts; j++)
16950 : {
16951 : /*
16952 : * Normally, dump if it's locally defined in this table, and
16953 : * not dropped. But for binary upgrade, we'll dump all the
16954 : * columns, and then fix up the dropped and nonlocal cases
16955 : * below.
16956 : */
16957 58062 : if (shouldPrintColumn(dopt, tbinfo, j))
16958 : {
16959 : bool print_default;
16960 : bool print_notnull;
16961 :
16962 : /*
16963 : * Default value --- suppress if to be printed separately
16964 : * or not at all.
16965 : */
16966 113224 : print_default = (tbinfo->attrdefs[j] != NULL &&
16967 57964 : tbinfo->attrdefs[j]->dobj.dump &&
16968 2830 : !tbinfo->attrdefs[j]->separate);
16969 :
16970 : /*
16971 : * Not Null constraint --- print it if it is locally
16972 : * defined, or if binary upgrade. (In the latter case, we
16973 : * reset conislocal below.)
16974 : */
16975 61190 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16976 6056 : (tbinfo->notnull_islocal[j] ||
16977 1640 : dopt->binary_upgrade ||
16978 1472 : tbinfo->ispartition));
16979 :
16980 : /*
16981 : * Skip column if fully defined by reloftype, except in
16982 : * binary upgrade
16983 : */
16984 55134 : if (OidIsValid(tbinfo->reloftype) &&
16985 160 : !print_default && !print_notnull &&
16986 96 : !dopt->binary_upgrade)
16987 84 : continue;
16988 :
16989 : /* Format properly if not first attr */
16990 55050 : if (actual_atts == 0)
16991 14466 : appendPQExpBufferStr(q, " (");
16992 : else
16993 40584 : appendPQExpBufferChar(q, ',');
16994 55050 : appendPQExpBufferStr(q, "\n ");
16995 55050 : actual_atts++;
16996 :
16997 : /* Attribute name */
16998 55050 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16999 :
17000 55050 : if (tbinfo->attisdropped[j])
17001 : {
17002 : /*
17003 : * ALTER TABLE DROP COLUMN clears
17004 : * pg_attribute.atttypid, so we will not have gotten a
17005 : * valid type name; insert INTEGER as a stopgap. We'll
17006 : * clean things up later.
17007 : */
17008 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17009 : /* and skip to the next column */
17010 168 : continue;
17011 : }
17012 :
17013 : /*
17014 : * Attribute type; print it except when creating a typed
17015 : * table ('OF type_name'), but in binary-upgrade mode,
17016 : * print it in that case too.
17017 : */
17018 54882 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17019 : {
17020 54826 : appendPQExpBuffer(q, " %s",
17021 54826 : tbinfo->atttypnames[j]);
17022 : }
17023 :
17024 54882 : if (print_default)
17025 : {
17026 2476 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17027 788 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17028 788 : tbinfo->attrdefs[j]->adef_expr);
17029 1688 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17030 676 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17031 676 : tbinfo->attrdefs[j]->adef_expr);
17032 : else
17033 1012 : appendPQExpBuffer(q, " DEFAULT %s",
17034 1012 : tbinfo->attrdefs[j]->adef_expr);
17035 : }
17036 :
17037 54882 : if (print_notnull)
17038 : {
17039 5986 : if (tbinfo->notnull_constrs[j][0] == '\0')
17040 4268 : appendPQExpBufferStr(q, " NOT NULL");
17041 : else
17042 1718 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17043 1718 : fmtId(tbinfo->notnull_constrs[j]));
17044 :
17045 5986 : if (tbinfo->notnull_noinh[j])
17046 0 : appendPQExpBufferStr(q, " NO INHERIT");
17047 : }
17048 :
17049 : /* Add collation if not default for the type */
17050 54882 : if (OidIsValid(tbinfo->attcollation[j]))
17051 : {
17052 : CollInfo *coll;
17053 :
17054 400 : coll = findCollationByOid(tbinfo->attcollation[j]);
17055 400 : if (coll)
17056 400 : appendPQExpBuffer(q, " COLLATE %s",
17057 400 : fmtQualifiedDumpable(coll));
17058 : }
17059 : }
17060 :
17061 : /*
17062 : * On the other hand, if we choose not to print a column
17063 : * (likely because it is created by inheritance), but the
17064 : * column has a locally-defined not-null constraint, we need
17065 : * to dump the constraint as a standalone object.
17066 : *
17067 : * This syntax isn't SQL-conforming, but if you wanted
17068 : * standard output you wouldn't be creating non-standard
17069 : * objects to begin with.
17070 : */
17071 57810 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17072 2928 : !tbinfo->attisdropped[j] &&
17073 1862 : tbinfo->notnull_constrs[j] != NULL &&
17074 490 : tbinfo->notnull_islocal[j])
17075 : {
17076 : /* Format properly if not first attr */
17077 154 : if (actual_atts == 0)
17078 140 : appendPQExpBufferStr(q, " (");
17079 : else
17080 14 : appendPQExpBufferChar(q, ',');
17081 154 : appendPQExpBufferStr(q, "\n ");
17082 154 : actual_atts++;
17083 :
17084 154 : if (tbinfo->notnull_constrs[j][0] == '\0')
17085 14 : appendPQExpBuffer(q, "NOT NULL %s",
17086 14 : fmtId(tbinfo->attnames[j]));
17087 : else
17088 280 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17089 140 : tbinfo->notnull_constrs[j],
17090 140 : fmtId(tbinfo->attnames[j]));
17091 : }
17092 : }
17093 :
17094 : /*
17095 : * Add non-inherited CHECK constraints, if any.
17096 : *
17097 : * For partitions, we need to include check constraints even if
17098 : * they're not defined locally, because the ALTER TABLE ATTACH
17099 : * PARTITION that we'll emit later expects the constraint to be
17100 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17101 : */
17102 16876 : for (j = 0; j < tbinfo->ncheck; j++)
17103 : {
17104 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17105 :
17106 1552 : if (constr->separate ||
17107 1328 : (!constr->conislocal && !tbinfo->ispartition))
17108 312 : continue;
17109 :
17110 1240 : if (actual_atts == 0)
17111 56 : appendPQExpBufferStr(q, " (\n ");
17112 : else
17113 1184 : appendPQExpBufferStr(q, ",\n ");
17114 :
17115 1240 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17116 1240 : fmtId(constr->dobj.name));
17117 1240 : appendPQExpBufferStr(q, constr->condef);
17118 :
17119 1240 : actual_atts++;
17120 : }
17121 :
17122 15324 : if (actual_atts)
17123 14662 : appendPQExpBufferStr(q, "\n)");
17124 662 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17125 : {
17126 : /*
17127 : * No attributes? we must have a parenthesized attribute list,
17128 : * even though empty, when not using the OF TYPE syntax.
17129 : */
17130 620 : appendPQExpBufferStr(q, " (\n)");
17131 : }
17132 :
17133 : /*
17134 : * Emit the INHERITS clause (not for partitions), except in
17135 : * binary-upgrade mode.
17136 : */
17137 15324 : if (numParents > 0 && !tbinfo->ispartition &&
17138 1342 : !dopt->binary_upgrade)
17139 : {
17140 1218 : appendPQExpBufferStr(q, "\nINHERITS (");
17141 2632 : for (k = 0; k < numParents; k++)
17142 : {
17143 1414 : TableInfo *parentRel = parents[k];
17144 :
17145 1414 : if (k > 0)
17146 196 : appendPQExpBufferStr(q, ", ");
17147 1414 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17148 : }
17149 1218 : appendPQExpBufferChar(q, ')');
17150 : }
17151 :
17152 15324 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17153 1616 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17154 :
17155 15324 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17156 76 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17157 : }
17158 :
17159 31920 : if (nonemptyReloptions(tbinfo->reloptions) ||
17160 15748 : nonemptyReloptions(tbinfo->toast_reloptions))
17161 : {
17162 424 : bool addcomma = false;
17163 :
17164 424 : appendPQExpBufferStr(q, "\nWITH (");
17165 424 : if (nonemptyReloptions(tbinfo->reloptions))
17166 : {
17167 424 : addcomma = true;
17168 424 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17169 : }
17170 424 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17171 : {
17172 16 : if (addcomma)
17173 16 : appendPQExpBufferStr(q, ", ");
17174 16 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17175 : fout);
17176 : }
17177 424 : appendPQExpBufferChar(q, ')');
17178 : }
17179 :
17180 : /* Dump generic options if any */
17181 16172 : if (ftoptions && ftoptions[0])
17182 72 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17183 :
17184 : /*
17185 : * For materialized views, create the AS clause just like a view. At
17186 : * this point, we always mark the view as not populated.
17187 : */
17188 16172 : if (tbinfo->relkind == RELKIND_MATVIEW)
17189 : {
17190 : PQExpBuffer result;
17191 :
17192 848 : result = createViewAsClause(fout, tbinfo);
17193 848 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17194 : result->data);
17195 848 : destroyPQExpBuffer(result);
17196 : }
17197 : else
17198 15324 : appendPQExpBufferStr(q, ";\n");
17199 :
17200 : /* Materialized views can depend on extensions */
17201 16172 : if (tbinfo->relkind == RELKIND_MATVIEW)
17202 848 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17203 : "pg_catalog.pg_class",
17204 : "MATERIALIZED VIEW",
17205 : qualrelname);
17206 :
17207 : /*
17208 : * in binary upgrade mode, update the catalog with any missing values
17209 : * that might be present.
17210 : */
17211 16172 : if (dopt->binary_upgrade)
17212 : {
17213 7820 : for (j = 0; j < tbinfo->numatts; j++)
17214 : {
17215 6228 : if (tbinfo->attmissingval[j][0] != '\0')
17216 : {
17217 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17218 4 : appendPQExpBufferStr(q,
17219 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17220 4 : appendStringLiteralAH(q, qualrelname, fout);
17221 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17222 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17223 4 : appendPQExpBufferChar(q, ',');
17224 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17225 4 : appendPQExpBufferStr(q, ");\n\n");
17226 : }
17227 : }
17228 : }
17229 :
17230 : /*
17231 : * To create binary-compatible heap files, we have to ensure the same
17232 : * physical column order, including dropped columns, as in the
17233 : * original. Therefore, we create dropped columns above and drop them
17234 : * here, also updating their attlen/attalign values so that the
17235 : * dropped column can be skipped properly. (We do not bother with
17236 : * restoring the original attbyval setting.) Also, inheritance
17237 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17238 : * using an INHERITS clause --- the latter would possibly mess up the
17239 : * column order. That also means we have to take care about setting
17240 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17241 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17242 : *
17243 : * We process foreign and partitioned tables here, even though they
17244 : * lack heap storage, because they can participate in inheritance
17245 : * relationships and we want this stuff to be consistent across the
17246 : * inheritance tree. We can exclude indexes, toast tables, sequences
17247 : * and matviews, even though they have storage, because we don't
17248 : * support altering or dropping columns in them, nor can they be part
17249 : * of inheritance trees.
17250 : */
17251 16172 : if (dopt->binary_upgrade &&
17252 1592 : (tbinfo->relkind == RELKIND_RELATION ||
17253 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17254 216 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17255 : {
17256 : bool firstitem;
17257 : bool firstitem_extra;
17258 :
17259 : /*
17260 : * Drop any dropped columns. Merge the pg_attribute manipulations
17261 : * into a single SQL command, so that we don't cause repeated
17262 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17263 : * relcache bloat while dropping N columns.
17264 : */
17265 1556 : resetPQExpBuffer(extra);
17266 1556 : firstitem = true;
17267 7740 : for (j = 0; j < tbinfo->numatts; j++)
17268 : {
17269 6184 : if (tbinfo->attisdropped[j])
17270 : {
17271 168 : if (firstitem)
17272 : {
17273 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17274 : "UPDATE pg_catalog.pg_attribute\n"
17275 : "SET attlen = v.dlen, "
17276 : "attalign = v.dalign, "
17277 : "attbyval = false\n"
17278 : "FROM (VALUES ");
17279 76 : firstitem = false;
17280 : }
17281 : else
17282 92 : appendPQExpBufferStr(q, ",\n ");
17283 168 : appendPQExpBufferChar(q, '(');
17284 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17285 168 : appendPQExpBuffer(q, ", %d, '%c')",
17286 168 : tbinfo->attlen[j],
17287 168 : tbinfo->attalign[j]);
17288 : /* The ALTER ... DROP COLUMN commands must come after */
17289 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17290 : foreign, qualrelname);
17291 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17292 168 : fmtId(tbinfo->attnames[j]));
17293 : }
17294 : }
17295 1556 : if (!firstitem)
17296 : {
17297 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17298 : "WHERE attrelid = ");
17299 76 : appendStringLiteralAH(q, qualrelname, fout);
17300 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17301 : " AND attname = v.dname;\n");
17302 : /* Now we can issue the actual DROP COLUMN commands */
17303 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17304 : }
17305 :
17306 : /*
17307 : * Fix up inherited columns. As above, do the pg_attribute
17308 : * manipulations in a single SQL command.
17309 : */
17310 1556 : firstitem = true;
17311 7740 : for (j = 0; j < tbinfo->numatts; j++)
17312 : {
17313 6184 : if (!tbinfo->attisdropped[j] &&
17314 6016 : !tbinfo->attislocal[j])
17315 : {
17316 1200 : if (firstitem)
17317 : {
17318 526 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17319 526 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17320 : "SET attislocal = false\n"
17321 : "WHERE attrelid = ");
17322 526 : appendStringLiteralAH(q, qualrelname, fout);
17323 526 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17324 : " AND attname IN (");
17325 526 : firstitem = false;
17326 : }
17327 : else
17328 674 : appendPQExpBufferStr(q, ", ");
17329 1200 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17330 : }
17331 : }
17332 1556 : if (!firstitem)
17333 526 : appendPQExpBufferStr(q, ");\n");
17334 :
17335 : /*
17336 : * Fix up not-null constraints that come from inheritance. As
17337 : * above, do the pg_constraint manipulations in a single SQL
17338 : * command. (Actually, two in special cases, if we're doing an
17339 : * upgrade from < 18).
17340 : */
17341 1556 : firstitem = true;
17342 1556 : firstitem_extra = true;
17343 1556 : resetPQExpBuffer(extra);
17344 7740 : for (j = 0; j < tbinfo->numatts; j++)
17345 : {
17346 : /*
17347 : * If a not-null constraint comes from inheritance, reset
17348 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17349 : * below. Special hack: in versions < 18, columns with no
17350 : * local definition need their constraint to be matched by
17351 : * column number in conkeys instead of by constraint name,
17352 : * because the latter is not available. (We distinguish the
17353 : * case because the constraint name is the empty string.)
17354 : */
17355 6184 : if (tbinfo->notnull_constrs[j] != NULL &&
17356 580 : !tbinfo->notnull_islocal[j])
17357 : {
17358 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17359 : {
17360 142 : if (firstitem)
17361 : {
17362 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17363 : "SET conislocal = false\n"
17364 : "WHERE contype = 'n' AND conrelid = ");
17365 122 : appendStringLiteralAH(q, qualrelname, fout);
17366 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17367 : "conname IN (");
17368 122 : firstitem = false;
17369 : }
17370 : else
17371 20 : appendPQExpBufferStr(q, ", ");
17372 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17373 : }
17374 : else
17375 : {
17376 26 : if (firstitem_extra)
17377 : {
17378 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17379 : "SET conislocal = false\n"
17380 : "WHERE contype = 'n' AND conrelid = ");
17381 26 : appendStringLiteralAH(extra, qualrelname, fout);
17382 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17383 : "conkey IN (");
17384 26 : firstitem_extra = false;
17385 : }
17386 : else
17387 0 : appendPQExpBufferStr(extra, ", ");
17388 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17389 : }
17390 : }
17391 : }
17392 1556 : if (!firstitem)
17393 122 : appendPQExpBufferStr(q, ");\n");
17394 1556 : if (!firstitem_extra)
17395 26 : appendPQExpBufferStr(extra, ");\n");
17396 :
17397 1556 : if (extra->len > 0)
17398 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17399 :
17400 : /*
17401 : * Add inherited CHECK constraints, if any.
17402 : *
17403 : * For partitions, they were already dumped, and conislocal
17404 : * doesn't need fixing.
17405 : *
17406 : * As above, issue only one direct manipulation of pg_constraint.
17407 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17408 : * commands into one as well, refrain for now due to concern about
17409 : * possible backend memory bloat if there are many such
17410 : * constraints.
17411 : */
17412 1556 : resetPQExpBuffer(extra);
17413 1556 : firstitem = true;
17414 1684 : for (k = 0; k < tbinfo->ncheck; k++)
17415 : {
17416 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17417 :
17418 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17419 124 : continue;
17420 :
17421 4 : if (firstitem)
17422 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17423 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17424 : foreign, qualrelname,
17425 4 : fmtId(constr->dobj.name),
17426 : constr->condef);
17427 : /* Update pg_constraint after all the ALTER TABLEs */
17428 4 : if (firstitem)
17429 : {
17430 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17431 : "SET conislocal = false\n"
17432 : "WHERE contype = 'c' AND conrelid = ");
17433 4 : appendStringLiteralAH(extra, qualrelname, fout);
17434 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17435 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17436 4 : firstitem = false;
17437 : }
17438 : else
17439 0 : appendPQExpBufferStr(extra, ", ");
17440 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17441 : }
17442 1556 : if (!firstitem)
17443 : {
17444 4 : appendPQExpBufferStr(extra, ");\n");
17445 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17446 : }
17447 :
17448 1556 : if (numParents > 0 && !tbinfo->ispartition)
17449 : {
17450 124 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17451 268 : for (k = 0; k < numParents; k++)
17452 : {
17453 144 : TableInfo *parentRel = parents[k];
17454 :
17455 144 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17456 : qualrelname,
17457 144 : fmtQualifiedDumpable(parentRel));
17458 : }
17459 : }
17460 :
17461 1556 : if (OidIsValid(tbinfo->reloftype))
17462 : {
17463 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17464 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17465 : qualrelname,
17466 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17467 : zeroIsError));
17468 : }
17469 : }
17470 :
17471 : /*
17472 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17473 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17474 : * TOAST tables semi-independently, here we see them only as children
17475 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17476 : * child toast table is handled below.)
17477 : */
17478 16172 : if (dopt->binary_upgrade &&
17479 1592 : (tbinfo->relkind == RELKIND_RELATION ||
17480 218 : tbinfo->relkind == RELKIND_MATVIEW))
17481 : {
17482 1410 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17483 1410 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17484 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17485 : "WHERE oid = ",
17486 1410 : tbinfo->frozenxid, tbinfo->minmxid);
17487 1410 : appendStringLiteralAH(q, qualrelname, fout);
17488 1410 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17489 :
17490 1410 : if (tbinfo->toast_oid)
17491 : {
17492 : /*
17493 : * The toast table will have the same OID at restore, so we
17494 : * can safely target it by OID.
17495 : */
17496 552 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17497 552 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17498 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17499 : "WHERE oid = '%u';\n",
17500 552 : tbinfo->toast_frozenxid,
17501 552 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17502 : }
17503 : }
17504 :
17505 : /*
17506 : * In binary_upgrade mode, restore matviews' populated status by
17507 : * poking pg_class directly. This is pretty ugly, but we can't use
17508 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17509 : * matview is not populated even though this matview is; in any case,
17510 : * we want to transfer the matview's heap storage, not run REFRESH.
17511 : */
17512 16172 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17513 36 : tbinfo->relispopulated)
17514 : {
17515 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17516 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17517 : "SET relispopulated = 't'\n"
17518 : "WHERE oid = ");
17519 32 : appendStringLiteralAH(q, qualrelname, fout);
17520 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17521 : }
17522 :
17523 : /*
17524 : * Dump additional per-column properties that we can't handle in the
17525 : * main CREATE TABLE command.
17526 : */
17527 75258 : for (j = 0; j < tbinfo->numatts; j++)
17528 : {
17529 : /* None of this applies to dropped columns */
17530 59086 : if (tbinfo->attisdropped[j])
17531 1234 : continue;
17532 :
17533 : /*
17534 : * Dump per-column statistics information. We only issue an ALTER
17535 : * TABLE statement if the attstattarget entry for this column is
17536 : * not the default value.
17537 : */
17538 57852 : if (tbinfo->attstattarget[j] >= 0)
17539 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17540 : foreign, qualrelname,
17541 72 : fmtId(tbinfo->attnames[j]),
17542 72 : tbinfo->attstattarget[j]);
17543 :
17544 : /*
17545 : * Dump per-column storage information. The statement is only
17546 : * dumped if the storage has been changed from the type's default.
17547 : */
17548 57852 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17549 : {
17550 192 : switch (tbinfo->attstorage[j])
17551 : {
17552 32 : case TYPSTORAGE_PLAIN:
17553 32 : storage = "PLAIN";
17554 32 : break;
17555 88 : case TYPSTORAGE_EXTERNAL:
17556 88 : storage = "EXTERNAL";
17557 88 : break;
17558 0 : case TYPSTORAGE_EXTENDED:
17559 0 : storage = "EXTENDED";
17560 0 : break;
17561 72 : case TYPSTORAGE_MAIN:
17562 72 : storage = "MAIN";
17563 72 : break;
17564 0 : default:
17565 0 : storage = NULL;
17566 : }
17567 :
17568 : /*
17569 : * Only dump the statement if it's a storage type we recognize
17570 : */
17571 192 : if (storage != NULL)
17572 192 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17573 : foreign, qualrelname,
17574 192 : fmtId(tbinfo->attnames[j]),
17575 : storage);
17576 : }
17577 :
17578 : /*
17579 : * Dump per-column compression, if it's been set.
17580 : */
17581 57852 : if (!dopt->no_toast_compression)
17582 : {
17583 : const char *cmname;
17584 :
17585 57662 : switch (tbinfo->attcompression[j])
17586 : {
17587 150 : case 'p':
17588 150 : cmname = "pglz";
17589 150 : break;
17590 236 : case 'l':
17591 236 : cmname = "lz4";
17592 236 : break;
17593 57276 : default:
17594 57276 : cmname = NULL;
17595 57276 : break;
17596 : }
17597 :
17598 57662 : if (cmname != NULL)
17599 386 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17600 : foreign, qualrelname,
17601 386 : fmtId(tbinfo->attnames[j]),
17602 : cmname);
17603 : }
17604 :
17605 : /*
17606 : * Dump per-column attributes.
17607 : */
17608 57852 : if (tbinfo->attoptions[j][0] != '\0')
17609 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17610 : foreign, qualrelname,
17611 72 : fmtId(tbinfo->attnames[j]),
17612 72 : tbinfo->attoptions[j]);
17613 :
17614 : /*
17615 : * Dump per-column fdw options.
17616 : */
17617 57852 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17618 76 : tbinfo->attfdwoptions[j][0] != '\0')
17619 72 : appendPQExpBuffer(q,
17620 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17621 : " %s\n"
17622 : ");\n",
17623 : qualrelname,
17624 72 : fmtId(tbinfo->attnames[j]),
17625 72 : tbinfo->attfdwoptions[j]);
17626 : } /* end loop over columns */
17627 :
17628 16172 : free(partkeydef);
17629 16172 : free(ftoptions);
17630 16172 : free(srvname);
17631 : }
17632 :
17633 : /*
17634 : * dump properties we only have ALTER TABLE syntax for
17635 : */
17636 17540 : if ((tbinfo->relkind == RELKIND_RELATION ||
17637 3908 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17638 2292 : tbinfo->relkind == RELKIND_MATVIEW) &&
17639 16096 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17640 : {
17641 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17642 : {
17643 : /* nothing to do, will be set when the index is dumped */
17644 : }
17645 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17646 : {
17647 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17648 : qualrelname);
17649 : }
17650 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17651 : {
17652 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17653 : qualrelname);
17654 : }
17655 : }
17656 :
17657 17540 : if (tbinfo->forcerowsec)
17658 16 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17659 : qualrelname);
17660 :
17661 17540 : if (dopt->binary_upgrade)
17662 1696 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17663 : reltypename, qrelname,
17664 1696 : tbinfo->dobj.namespace->dobj.name);
17665 :
17666 17540 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17667 : {
17668 17540 : char *tablespace = NULL;
17669 17540 : char *tableam = NULL;
17670 :
17671 : /*
17672 : * _selectTablespace() relies on tablespace-enabled objects in the
17673 : * default tablespace to have a tablespace of "" (empty string) versus
17674 : * non-tablespace-enabled objects to have a tablespace of NULL.
17675 : * getTables() sets tbinfo->reltablespace to "" for the default
17676 : * tablespace (not NULL).
17677 : */
17678 17540 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17679 16096 : tablespace = tbinfo->reltablespace;
17680 :
17681 17540 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17682 3060 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17683 16096 : tableam = tbinfo->amname;
17684 :
17685 17540 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17686 17540 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17687 : .namespace = tbinfo->dobj.namespace->dobj.name,
17688 : .tablespace = tablespace,
17689 : .tableam = tableam,
17690 : .relkind = tbinfo->relkind,
17691 : .owner = tbinfo->rolname,
17692 : .description = reltypename,
17693 : .section = tbinfo->postponed_def ?
17694 : SECTION_POST_DATA : SECTION_PRE_DATA,
17695 : .createStmt = q->data,
17696 : .dropStmt = delq->data));
17697 : }
17698 :
17699 : /* Dump Table Comments */
17700 17540 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17701 176 : dumpTableComment(fout, tbinfo, reltypename);
17702 :
17703 : /* Dump Table Security Labels */
17704 17540 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17705 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17706 :
17707 : /*
17708 : * Dump comments for not-null constraints that aren't to be dumped
17709 : * separately (those are processed by collectComments/dumpComment).
17710 : */
17711 17540 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
17712 17540 : fout->remoteVersion >= 180000)
17713 : {
17714 17540 : PQExpBuffer comment = NULL;
17715 17540 : PQExpBuffer tag = NULL;
17716 :
17717 84302 : for (j = 0; j < tbinfo->numatts; j++)
17718 : {
17719 66762 : if (tbinfo->notnull_constrs[j] != NULL &&
17720 6546 : tbinfo->notnull_comment[j] != NULL)
17721 : {
17722 104 : if (comment == NULL)
17723 : {
17724 104 : comment = createPQExpBuffer();
17725 104 : tag = createPQExpBuffer();
17726 : }
17727 : else
17728 : {
17729 0 : resetPQExpBuffer(comment);
17730 0 : resetPQExpBuffer(tag);
17731 : }
17732 :
17733 104 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
17734 104 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
17735 104 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
17736 104 : appendPQExpBufferStr(comment, ";\n");
17737 :
17738 104 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
17739 104 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
17740 :
17741 104 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17742 104 : ARCHIVE_OPTS(.tag = tag->data,
17743 : .namespace = tbinfo->dobj.namespace->dobj.name,
17744 : .owner = tbinfo->rolname,
17745 : .description = "COMMENT",
17746 : .section = SECTION_NONE,
17747 : .createStmt = comment->data,
17748 : .deps = &(tbinfo->dobj.dumpId),
17749 : .nDeps = 1));
17750 : }
17751 : }
17752 :
17753 17540 : destroyPQExpBuffer(comment);
17754 17540 : destroyPQExpBuffer(tag);
17755 : }
17756 :
17757 : /* Dump comments on inlined table constraints */
17758 19092 : for (j = 0; j < tbinfo->ncheck; j++)
17759 : {
17760 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17761 :
17762 1552 : if (constr->separate || !constr->conislocal)
17763 624 : continue;
17764 :
17765 928 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17766 88 : dumpTableConstraintComment(fout, constr);
17767 : }
17768 :
17769 17540 : destroyPQExpBuffer(q);
17770 17540 : destroyPQExpBuffer(delq);
17771 17540 : destroyPQExpBuffer(extra);
17772 17540 : free(qrelname);
17773 17540 : free(qualrelname);
17774 17540 : }
17775 :
17776 : /*
17777 : * dumpTableAttach
17778 : * write to fout the commands to attach a child partition
17779 : *
17780 : * Child partitions are always made by creating them separately
17781 : * and then using ATTACH PARTITION, rather than using
17782 : * CREATE TABLE ... PARTITION OF. This is important for preserving
17783 : * any possible discrepancy in column layout, to allow assigning the
17784 : * correct tablespace if different, and so that it's possible to restore
17785 : * a partition without restoring its parent. (You'll get an error from
17786 : * the ATTACH PARTITION command, but that can be ignored, or skipped
17787 : * using "pg_restore -L" if you prefer.) The last point motivates
17788 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
17789 : * rather than emitting it within the child partition's ArchiveEntry.
17790 : */
17791 : static void
17792 3900 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
17793 : {
17794 3900 : DumpOptions *dopt = fout->dopt;
17795 : PQExpBuffer q;
17796 : PGresult *res;
17797 : char *partbound;
17798 :
17799 : /* Do nothing if not dumping schema */
17800 3900 : if (!dopt->dumpSchema)
17801 84 : return;
17802 :
17803 3816 : q = createPQExpBuffer();
17804 :
17805 3816 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
17806 : {
17807 : /* Set up query for partbound details */
17808 100 : appendPQExpBufferStr(q,
17809 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
17810 :
17811 100 : appendPQExpBufferStr(q,
17812 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
17813 : "FROM pg_class c "
17814 : "WHERE c.oid = $1");
17815 :
17816 100 : ExecuteSqlStatement(fout, q->data);
17817 :
17818 100 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
17819 : }
17820 :
17821 3816 : printfPQExpBuffer(q,
17822 : "EXECUTE dumpTableAttach('%u')",
17823 3816 : attachinfo->partitionTbl->dobj.catId.oid);
17824 :
17825 3816 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
17826 3816 : partbound = PQgetvalue(res, 0, 0);
17827 :
17828 : /* Perform ALTER TABLE on the parent */
17829 3816 : printfPQExpBuffer(q,
17830 : "ALTER TABLE ONLY %s ",
17831 3816 : fmtQualifiedDumpable(attachinfo->parentTbl));
17832 3816 : appendPQExpBuffer(q,
17833 : "ATTACH PARTITION %s %s;\n",
17834 3816 : fmtQualifiedDumpable(attachinfo->partitionTbl),
17835 : partbound);
17836 :
17837 : /*
17838 : * There is no point in creating a drop query as the drop is done by table
17839 : * drop. (If you think to change this, see also _printTocEntry().)
17840 : * Although this object doesn't really have ownership as such, set the
17841 : * owner field anyway to ensure that the command is run by the correct
17842 : * role at restore time.
17843 : */
17844 3816 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17845 3816 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17846 : .namespace = attachinfo->dobj.namespace->dobj.name,
17847 : .owner = attachinfo->partitionTbl->rolname,
17848 : .description = "TABLE ATTACH",
17849 : .section = SECTION_PRE_DATA,
17850 : .createStmt = q->data));
17851 :
17852 3816 : PQclear(res);
17853 3816 : destroyPQExpBuffer(q);
17854 : }
17855 :
17856 : /*
17857 : * dumpAttrDef --- dump an attribute's default-value declaration
17858 : */
17859 : static void
17860 2916 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
17861 : {
17862 2916 : DumpOptions *dopt = fout->dopt;
17863 2916 : TableInfo *tbinfo = adinfo->adtable;
17864 2916 : int adnum = adinfo->adnum;
17865 : PQExpBuffer q;
17866 : PQExpBuffer delq;
17867 : char *qualrelname;
17868 : char *tag;
17869 : char *foreign;
17870 :
17871 : /* Do nothing if not dumping schema */
17872 2916 : if (!dopt->dumpSchema)
17873 0 : return;
17874 :
17875 : /* Skip if not "separate"; it was dumped in the table's definition */
17876 2916 : if (!adinfo->separate)
17877 2476 : return;
17878 :
17879 440 : q = createPQExpBuffer();
17880 440 : delq = createPQExpBuffer();
17881 :
17882 440 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17883 :
17884 440 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17885 :
17886 440 : appendPQExpBuffer(q,
17887 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17888 440 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17889 440 : adinfo->adef_expr);
17890 :
17891 440 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17892 : foreign, qualrelname,
17893 440 : fmtId(tbinfo->attnames[adnum - 1]));
17894 :
17895 440 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17896 :
17897 440 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17898 440 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17899 440 : ARCHIVE_OPTS(.tag = tag,
17900 : .namespace = tbinfo->dobj.namespace->dobj.name,
17901 : .owner = tbinfo->rolname,
17902 : .description = "DEFAULT",
17903 : .section = SECTION_PRE_DATA,
17904 : .createStmt = q->data,
17905 : .dropStmt = delq->data));
17906 :
17907 440 : free(tag);
17908 440 : destroyPQExpBuffer(q);
17909 440 : destroyPQExpBuffer(delq);
17910 440 : free(qualrelname);
17911 : }
17912 :
17913 : /*
17914 : * getAttrName: extract the correct name for an attribute
17915 : *
17916 : * The array tblInfo->attnames[] only provides names of user attributes;
17917 : * if a system attribute number is supplied, we have to fake it.
17918 : * We also do a little bit of bounds checking for safety's sake.
17919 : */
17920 : static const char *
17921 5220 : getAttrName(int attrnum, const TableInfo *tblInfo)
17922 : {
17923 5220 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
17924 5220 : return tblInfo->attnames[attrnum - 1];
17925 0 : switch (attrnum)
17926 : {
17927 0 : case SelfItemPointerAttributeNumber:
17928 0 : return "ctid";
17929 0 : case MinTransactionIdAttributeNumber:
17930 0 : return "xmin";
17931 0 : case MinCommandIdAttributeNumber:
17932 0 : return "cmin";
17933 0 : case MaxTransactionIdAttributeNumber:
17934 0 : return "xmax";
17935 0 : case MaxCommandIdAttributeNumber:
17936 0 : return "cmax";
17937 0 : case TableOidAttributeNumber:
17938 0 : return "tableoid";
17939 : }
17940 0 : pg_fatal("invalid column number %d for table \"%s\"",
17941 : attrnum, tblInfo->dobj.name);
17942 : return NULL; /* keep compiler quiet */
17943 : }
17944 :
17945 : /*
17946 : * dumpIndex
17947 : * write out to fout a user-defined index
17948 : */
17949 : static void
17950 6970 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17951 : {
17952 6970 : DumpOptions *dopt = fout->dopt;
17953 6970 : TableInfo *tbinfo = indxinfo->indextable;
17954 6970 : bool is_constraint = (indxinfo->indexconstraint != 0);
17955 : PQExpBuffer q;
17956 : PQExpBuffer delq;
17957 : char *qindxname;
17958 : char *qqindxname;
17959 :
17960 : /* Do nothing if not dumping schema */
17961 6970 : if (!dopt->dumpSchema)
17962 234 : return;
17963 :
17964 6736 : q = createPQExpBuffer();
17965 6736 : delq = createPQExpBuffer();
17966 :
17967 6736 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17968 6736 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17969 :
17970 : /*
17971 : * If there's an associated constraint, don't dump the index per se, but
17972 : * do dump any comment for it. (This is safe because dependency ordering
17973 : * will have ensured the constraint is emitted first.) Note that the
17974 : * emitted comment has to be shown as depending on the constraint, not the
17975 : * index, in such cases.
17976 : */
17977 6736 : if (!is_constraint)
17978 : {
17979 2998 : char *indstatcols = indxinfo->indstatcols;
17980 2998 : char *indstatvals = indxinfo->indstatvals;
17981 2998 : char **indstatcolsarray = NULL;
17982 2998 : char **indstatvalsarray = NULL;
17983 2998 : int nstatcols = 0;
17984 2998 : int nstatvals = 0;
17985 :
17986 2998 : if (dopt->binary_upgrade)
17987 310 : binary_upgrade_set_pg_class_oids(fout, q,
17988 310 : indxinfo->dobj.catId.oid);
17989 :
17990 : /* Plain secondary index */
17991 2998 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17992 :
17993 : /*
17994 : * Append ALTER TABLE commands as needed to set properties that we
17995 : * only have ALTER TABLE syntax for. Keep this in sync with the
17996 : * similar code in dumpConstraint!
17997 : */
17998 :
17999 : /* If the index is clustered, we need to record that. */
18000 2998 : if (indxinfo->indisclustered)
18001 : {
18002 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18003 0 : fmtQualifiedDumpable(tbinfo));
18004 : /* index name is not qualified in this syntax */
18005 0 : appendPQExpBuffer(q, " ON %s;\n",
18006 : qindxname);
18007 : }
18008 :
18009 : /*
18010 : * If the index has any statistics on some of its columns, generate
18011 : * the associated ALTER INDEX queries.
18012 : */
18013 2998 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18014 : {
18015 : int j;
18016 :
18017 72 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18018 0 : pg_fatal("could not parse index statistic columns");
18019 72 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18020 0 : pg_fatal("could not parse index statistic values");
18021 72 : if (nstatcols != nstatvals)
18022 0 : pg_fatal("mismatched number of columns and values for index statistics");
18023 :
18024 216 : for (j = 0; j < nstatcols; j++)
18025 : {
18026 144 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18027 :
18028 : /*
18029 : * Note that this is a column number, so no quotes should be
18030 : * used.
18031 : */
18032 144 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18033 144 : indstatcolsarray[j]);
18034 144 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18035 144 : indstatvalsarray[j]);
18036 : }
18037 : }
18038 :
18039 : /* Indexes can depend on extensions */
18040 2998 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18041 : "pg_catalog.pg_class",
18042 : "INDEX", qqindxname);
18043 :
18044 : /* If the index defines identity, we need to record that. */
18045 2998 : if (indxinfo->indisreplident)
18046 : {
18047 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18048 0 : fmtQualifiedDumpable(tbinfo));
18049 : /* index name is not qualified in this syntax */
18050 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18051 : qindxname);
18052 : }
18053 :
18054 : /*
18055 : * If this index is a member of a partitioned index, the backend will
18056 : * not allow us to drop it separately, so don't try. It will go away
18057 : * automatically when we drop either the index's table or the
18058 : * partitioned index. (If, in a selective restore with --clean, we
18059 : * drop neither of those, then this index will not be dropped either.
18060 : * But that's fine, and even if you think it's not, the backend won't
18061 : * let us do differently.)
18062 : */
18063 2998 : if (indxinfo->parentidx == 0)
18064 2542 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18065 :
18066 2998 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18067 2998 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18068 2998 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18069 : .namespace = tbinfo->dobj.namespace->dobj.name,
18070 : .tablespace = indxinfo->tablespace,
18071 : .owner = tbinfo->rolname,
18072 : .description = "INDEX",
18073 : .section = SECTION_POST_DATA,
18074 : .createStmt = q->data,
18075 : .dropStmt = delq->data));
18076 :
18077 2998 : free(indstatcolsarray);
18078 2998 : free(indstatvalsarray);
18079 : }
18080 :
18081 : /* Dump Index Comments */
18082 6736 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18083 48 : dumpComment(fout, "INDEX", qindxname,
18084 48 : tbinfo->dobj.namespace->dobj.name,
18085 : tbinfo->rolname,
18086 : indxinfo->dobj.catId, 0,
18087 : is_constraint ? indxinfo->indexconstraint :
18088 : indxinfo->dobj.dumpId);
18089 :
18090 6736 : destroyPQExpBuffer(q);
18091 6736 : destroyPQExpBuffer(delq);
18092 6736 : free(qindxname);
18093 6736 : free(qqindxname);
18094 : }
18095 :
18096 : /*
18097 : * dumpIndexAttach
18098 : * write out to fout a partitioned-index attachment clause
18099 : */
18100 : static void
18101 1480 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18102 : {
18103 : /* Do nothing if not dumping schema */
18104 1480 : if (!fout->dopt->dumpSchema)
18105 96 : return;
18106 :
18107 1384 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18108 : {
18109 1384 : PQExpBuffer q = createPQExpBuffer();
18110 :
18111 1384 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18112 1384 : fmtQualifiedDumpable(attachinfo->parentIdx));
18113 1384 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18114 1384 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18115 :
18116 : /*
18117 : * There is no need for a dropStmt since the drop is done implicitly
18118 : * when we drop either the index's table or the partitioned index.
18119 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18120 : * there's no way to do it anyway. (If you think to change this,
18121 : * consider also what to do with --if-exists.)
18122 : *
18123 : * Although this object doesn't really have ownership as such, set the
18124 : * owner field anyway to ensure that the command is run by the correct
18125 : * role at restore time.
18126 : */
18127 1384 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18128 1384 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18129 : .namespace = attachinfo->dobj.namespace->dobj.name,
18130 : .owner = attachinfo->parentIdx->indextable->rolname,
18131 : .description = "INDEX ATTACH",
18132 : .section = SECTION_POST_DATA,
18133 : .createStmt = q->data));
18134 :
18135 1384 : destroyPQExpBuffer(q);
18136 : }
18137 : }
18138 :
18139 : /*
18140 : * dumpStatisticsExt
18141 : * write out to fout an extended statistics object
18142 : */
18143 : static void
18144 314 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18145 : {
18146 314 : DumpOptions *dopt = fout->dopt;
18147 : PQExpBuffer q;
18148 : PQExpBuffer delq;
18149 : PQExpBuffer query;
18150 : char *qstatsextname;
18151 : PGresult *res;
18152 : char *stxdef;
18153 :
18154 : /* Do nothing if not dumping schema */
18155 314 : if (!dopt->dumpSchema)
18156 36 : return;
18157 :
18158 278 : q = createPQExpBuffer();
18159 278 : delq = createPQExpBuffer();
18160 278 : query = createPQExpBuffer();
18161 :
18162 278 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18163 :
18164 278 : appendPQExpBuffer(query, "SELECT "
18165 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18166 278 : statsextinfo->dobj.catId.oid);
18167 :
18168 278 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18169 :
18170 278 : stxdef = PQgetvalue(res, 0, 0);
18171 :
18172 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18173 278 : appendPQExpBuffer(q, "%s;\n", stxdef);
18174 :
18175 : /*
18176 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18177 : * for this statistics object is not the default value.
18178 : */
18179 278 : if (statsextinfo->stattarget >= 0)
18180 : {
18181 72 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18182 72 : fmtQualifiedDumpable(statsextinfo));
18183 72 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18184 72 : statsextinfo->stattarget);
18185 : }
18186 :
18187 278 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18188 278 : fmtQualifiedDumpable(statsextinfo));
18189 :
18190 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18191 278 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18192 278 : statsextinfo->dobj.dumpId,
18193 278 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18194 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18195 : .owner = statsextinfo->rolname,
18196 : .description = "STATISTICS",
18197 : .section = SECTION_POST_DATA,
18198 : .createStmt = q->data,
18199 : .dropStmt = delq->data));
18200 :
18201 : /* Dump Statistics Comments */
18202 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18203 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18204 0 : statsextinfo->dobj.namespace->dobj.name,
18205 0 : statsextinfo->rolname,
18206 : statsextinfo->dobj.catId, 0,
18207 0 : statsextinfo->dobj.dumpId);
18208 :
18209 278 : PQclear(res);
18210 278 : destroyPQExpBuffer(q);
18211 278 : destroyPQExpBuffer(delq);
18212 278 : destroyPQExpBuffer(query);
18213 278 : free(qstatsextname);
18214 : }
18215 :
18216 : /*
18217 : * dumpConstraint
18218 : * write out to fout a user-defined constraint
18219 : */
18220 : static void
18221 6402 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18222 : {
18223 6402 : DumpOptions *dopt = fout->dopt;
18224 6402 : TableInfo *tbinfo = coninfo->contable;
18225 : PQExpBuffer q;
18226 : PQExpBuffer delq;
18227 6402 : char *tag = NULL;
18228 : char *foreign;
18229 :
18230 : /* Do nothing if not dumping schema */
18231 6402 : if (!dopt->dumpSchema)
18232 184 : return;
18233 :
18234 6218 : q = createPQExpBuffer();
18235 6218 : delq = createPQExpBuffer();
18236 :
18237 12140 : foreign = tbinfo &&
18238 6218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18239 :
18240 6218 : if (coninfo->contype == 'p' ||
18241 3020 : coninfo->contype == 'u' ||
18242 2512 : coninfo->contype == 'x')
18243 3738 : {
18244 : /* Index-related constraint */
18245 : IndxInfo *indxinfo;
18246 : int k;
18247 :
18248 3738 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18249 :
18250 3738 : if (indxinfo == NULL)
18251 0 : pg_fatal("missing index for constraint \"%s\"",
18252 : coninfo->dobj.name);
18253 :
18254 3738 : if (dopt->binary_upgrade)
18255 292 : binary_upgrade_set_pg_class_oids(fout, q,
18256 : indxinfo->dobj.catId.oid);
18257 :
18258 3738 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18259 3738 : fmtQualifiedDumpable(tbinfo));
18260 3738 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18261 3738 : fmtId(coninfo->dobj.name));
18262 :
18263 3738 : if (coninfo->condef)
18264 : {
18265 : /* pg_get_constraintdef should have provided everything */
18266 32 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18267 : }
18268 : else
18269 : {
18270 3706 : appendPQExpBufferStr(q,
18271 3706 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18272 :
18273 : /*
18274 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18275 : * indexes. Being able to create this was fixed, but we need to
18276 : * make the index distinct in order to be able to restore the
18277 : * dump.
18278 : */
18279 3706 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18280 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18281 3706 : appendPQExpBufferStr(q, " (");
18282 8798 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18283 : {
18284 5092 : int indkey = (int) indxinfo->indkeys[k];
18285 : const char *attname;
18286 :
18287 5092 : if (indkey == InvalidAttrNumber)
18288 0 : break;
18289 5092 : attname = getAttrName(indkey, tbinfo);
18290 :
18291 5092 : appendPQExpBuffer(q, "%s%s",
18292 : (k == 0) ? "" : ", ",
18293 : fmtId(attname));
18294 : }
18295 3706 : if (coninfo->conperiod)
18296 272 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18297 :
18298 3706 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18299 64 : appendPQExpBufferStr(q, ") INCLUDE (");
18300 :
18301 3834 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18302 : {
18303 128 : int indkey = (int) indxinfo->indkeys[k];
18304 : const char *attname;
18305 :
18306 128 : if (indkey == InvalidAttrNumber)
18307 0 : break;
18308 128 : attname = getAttrName(indkey, tbinfo);
18309 :
18310 256 : appendPQExpBuffer(q, "%s%s",
18311 128 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18312 : fmtId(attname));
18313 : }
18314 :
18315 3706 : appendPQExpBufferChar(q, ')');
18316 :
18317 3706 : if (nonemptyReloptions(indxinfo->indreloptions))
18318 : {
18319 0 : appendPQExpBufferStr(q, " WITH (");
18320 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18321 0 : appendPQExpBufferChar(q, ')');
18322 : }
18323 :
18324 3706 : if (coninfo->condeferrable)
18325 : {
18326 80 : appendPQExpBufferStr(q, " DEFERRABLE");
18327 80 : if (coninfo->condeferred)
18328 48 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18329 : }
18330 :
18331 3706 : appendPQExpBufferStr(q, ";\n");
18332 : }
18333 :
18334 : /*
18335 : * Append ALTER TABLE commands as needed to set properties that we
18336 : * only have ALTER TABLE syntax for. Keep this in sync with the
18337 : * similar code in dumpIndex!
18338 : */
18339 :
18340 : /* If the index is clustered, we need to record that. */
18341 3738 : if (indxinfo->indisclustered)
18342 : {
18343 72 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18344 72 : fmtQualifiedDumpable(tbinfo));
18345 : /* index name is not qualified in this syntax */
18346 72 : appendPQExpBuffer(q, " ON %s;\n",
18347 72 : fmtId(indxinfo->dobj.name));
18348 : }
18349 :
18350 : /* If the index defines identity, we need to record that. */
18351 3738 : if (indxinfo->indisreplident)
18352 : {
18353 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18354 0 : fmtQualifiedDumpable(tbinfo));
18355 : /* index name is not qualified in this syntax */
18356 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18357 0 : fmtId(indxinfo->dobj.name));
18358 : }
18359 :
18360 : /* Indexes can depend on extensions */
18361 3738 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18362 : "pg_catalog.pg_class", "INDEX",
18363 3738 : fmtQualifiedDumpable(indxinfo));
18364 :
18365 3738 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18366 3738 : fmtQualifiedDumpable(tbinfo));
18367 3738 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18368 3738 : fmtId(coninfo->dobj.name));
18369 :
18370 3738 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18371 :
18372 3738 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18373 3738 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18374 3738 : ARCHIVE_OPTS(.tag = tag,
18375 : .namespace = tbinfo->dobj.namespace->dobj.name,
18376 : .tablespace = indxinfo->tablespace,
18377 : .owner = tbinfo->rolname,
18378 : .description = "CONSTRAINT",
18379 : .section = SECTION_POST_DATA,
18380 : .createStmt = q->data,
18381 : .dropStmt = delq->data));
18382 : }
18383 2480 : else if (coninfo->contype == 'f')
18384 : {
18385 : char *only;
18386 :
18387 : /*
18388 : * Foreign keys on partitioned tables are always declared as
18389 : * inheriting to partitions; for all other cases, emit them as
18390 : * applying ONLY directly to the named table, because that's how they
18391 : * work for regular inherited tables.
18392 : */
18393 448 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18394 :
18395 : /*
18396 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18397 : * current table data is not processed
18398 : */
18399 448 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18400 448 : only, fmtQualifiedDumpable(tbinfo));
18401 448 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18402 448 : fmtId(coninfo->dobj.name),
18403 448 : coninfo->condef);
18404 :
18405 448 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18406 448 : only, fmtQualifiedDumpable(tbinfo));
18407 448 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18408 448 : fmtId(coninfo->dobj.name));
18409 :
18410 448 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18411 :
18412 448 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18413 448 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18414 448 : ARCHIVE_OPTS(.tag = tag,
18415 : .namespace = tbinfo->dobj.namespace->dobj.name,
18416 : .owner = tbinfo->rolname,
18417 : .description = "FK CONSTRAINT",
18418 : .section = SECTION_POST_DATA,
18419 : .createStmt = q->data,
18420 : .dropStmt = delq->data));
18421 : }
18422 2032 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18423 : {
18424 : /* CHECK or invalid not-null constraint on a table */
18425 :
18426 : /* Ignore if not to be dumped separately, or if it was inherited */
18427 1736 : if (coninfo->separate && coninfo->conislocal)
18428 : {
18429 : const char *keyword;
18430 :
18431 312 : if (coninfo->contype == 'c')
18432 144 : keyword = "CHECK CONSTRAINT";
18433 : else
18434 168 : keyword = "CONSTRAINT";
18435 :
18436 : /* not ONLY since we want it to propagate to children */
18437 312 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18438 312 : fmtQualifiedDumpable(tbinfo));
18439 312 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18440 312 : fmtId(coninfo->dobj.name),
18441 312 : coninfo->condef);
18442 :
18443 312 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18444 312 : fmtQualifiedDumpable(tbinfo));
18445 312 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18446 312 : fmtId(coninfo->dobj.name));
18447 :
18448 312 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18449 :
18450 312 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18451 312 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18452 312 : ARCHIVE_OPTS(.tag = tag,
18453 : .namespace = tbinfo->dobj.namespace->dobj.name,
18454 : .owner = tbinfo->rolname,
18455 : .description = keyword,
18456 : .section = SECTION_POST_DATA,
18457 : .createStmt = q->data,
18458 : .dropStmt = delq->data));
18459 : }
18460 : }
18461 296 : else if (coninfo->contype == 'c' && tbinfo == NULL)
18462 296 : {
18463 : /* CHECK constraint on a domain */
18464 296 : TypeInfo *tyinfo = coninfo->condomain;
18465 :
18466 : /* Ignore if not to be dumped separately */
18467 296 : if (coninfo->separate)
18468 : {
18469 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18470 0 : fmtQualifiedDumpable(tyinfo));
18471 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18472 0 : fmtId(coninfo->dobj.name),
18473 0 : coninfo->condef);
18474 :
18475 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18476 0 : fmtQualifiedDumpable(tyinfo));
18477 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18478 0 : fmtId(coninfo->dobj.name));
18479 :
18480 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18481 :
18482 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18483 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18484 0 : ARCHIVE_OPTS(.tag = tag,
18485 : .namespace = tyinfo->dobj.namespace->dobj.name,
18486 : .owner = tyinfo->rolname,
18487 : .description = "CHECK CONSTRAINT",
18488 : .section = SECTION_POST_DATA,
18489 : .createStmt = q->data,
18490 : .dropStmt = delq->data));
18491 : }
18492 : }
18493 : else
18494 : {
18495 0 : pg_fatal("unrecognized constraint type: %c",
18496 : coninfo->contype);
18497 : }
18498 :
18499 : /* Dump Constraint Comments --- only works for table constraints */
18500 6218 : if (tbinfo && coninfo->separate &&
18501 4594 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18502 120 : dumpTableConstraintComment(fout, coninfo);
18503 :
18504 6218 : free(tag);
18505 6218 : destroyPQExpBuffer(q);
18506 6218 : destroyPQExpBuffer(delq);
18507 : }
18508 :
18509 : /*
18510 : * dumpTableConstraintComment --- dump a constraint's comment if any
18511 : *
18512 : * This is split out because we need the function in two different places
18513 : * depending on whether the constraint is dumped as part of CREATE TABLE
18514 : * or as a separate ALTER command.
18515 : */
18516 : static void
18517 208 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18518 : {
18519 208 : TableInfo *tbinfo = coninfo->contable;
18520 208 : PQExpBuffer conprefix = createPQExpBuffer();
18521 : char *qtabname;
18522 :
18523 208 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18524 :
18525 208 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18526 208 : fmtId(coninfo->dobj.name));
18527 :
18528 208 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18529 208 : dumpComment(fout, conprefix->data, qtabname,
18530 208 : tbinfo->dobj.namespace->dobj.name,
18531 : tbinfo->rolname,
18532 : coninfo->dobj.catId, 0,
18533 208 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18534 :
18535 208 : destroyPQExpBuffer(conprefix);
18536 208 : free(qtabname);
18537 208 : }
18538 :
18539 : static inline SeqType
18540 1632 : parse_sequence_type(const char *name)
18541 : {
18542 3612 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18543 : {
18544 3612 : if (strcmp(SeqTypeNames[i], name) == 0)
18545 1632 : return (SeqType) i;
18546 : }
18547 :
18548 0 : pg_fatal("unrecognized sequence type: %s", name);
18549 : return (SeqType) 0; /* keep compiler quiet */
18550 : }
18551 :
18552 : /*
18553 : * bsearch() comparator for SequenceItem
18554 : */
18555 : static int
18556 9144 : SequenceItemCmp(const void *p1, const void *p2)
18557 : {
18558 9144 : SequenceItem v1 = *((const SequenceItem *) p1);
18559 9144 : SequenceItem v2 = *((const SequenceItem *) p2);
18560 :
18561 9144 : return pg_cmp_u32(v1.oid, v2.oid);
18562 : }
18563 :
18564 : /*
18565 : * collectSequences
18566 : *
18567 : * Construct a table of sequence information. This table is sorted by OID for
18568 : * speed in lookup.
18569 : */
18570 : static void
18571 468 : collectSequences(Archive *fout)
18572 : {
18573 : PGresult *res;
18574 : const char *query;
18575 :
18576 : /*
18577 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18578 : * some extra effort, we might be able to use the sorted table for those
18579 : * versions, but for now it seems unlikely to be worth it.
18580 : *
18581 : * Since version 18, we can gather the sequence data in this query with
18582 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18583 : */
18584 468 : if (fout->remoteVersion < 100000)
18585 0 : return;
18586 468 : else if (fout->remoteVersion < 180000 ||
18587 468 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18588 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18589 : "seqstart, seqincrement, "
18590 : "seqmax, seqmin, "
18591 : "seqcache, seqcycle, "
18592 : "NULL, 'f' "
18593 : "FROM pg_catalog.pg_sequence "
18594 : "ORDER BY seqrelid";
18595 : else
18596 452 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18597 : "seqstart, seqincrement, "
18598 : "seqmax, seqmin, "
18599 : "seqcache, seqcycle, "
18600 : "last_value, is_called "
18601 : "FROM pg_catalog.pg_sequence, "
18602 : "pg_get_sequence_data(seqrelid) "
18603 : "ORDER BY seqrelid;";
18604 :
18605 468 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18606 :
18607 468 : nsequences = PQntuples(res);
18608 468 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18609 :
18610 2100 : for (int i = 0; i < nsequences; i++)
18611 : {
18612 1632 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18613 1632 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18614 1632 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18615 1632 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18616 1632 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18617 1632 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18618 1632 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18619 1632 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18620 1632 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18621 1632 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18622 : }
18623 :
18624 468 : PQclear(res);
18625 : }
18626 :
18627 : /*
18628 : * dumpSequence
18629 : * write the declaration (not data) of one user-defined sequence
18630 : */
18631 : static void
18632 1106 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18633 : {
18634 1106 : DumpOptions *dopt = fout->dopt;
18635 : SequenceItem *seq;
18636 : bool is_ascending;
18637 : int64 default_minv,
18638 : default_maxv;
18639 1106 : PQExpBuffer query = createPQExpBuffer();
18640 1106 : PQExpBuffer delqry = createPQExpBuffer();
18641 : char *qseqname;
18642 1106 : TableInfo *owning_tab = NULL;
18643 :
18644 1106 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18645 :
18646 : /*
18647 : * For versions >= 10, the sequence information is gathered in a sorted
18648 : * table before any calls to dumpSequence(). See collectSequences() for
18649 : * more information.
18650 : */
18651 1106 : if (fout->remoteVersion >= 100000)
18652 : {
18653 1106 : SequenceItem key = {0};
18654 :
18655 : Assert(sequences);
18656 :
18657 1106 : key.oid = tbinfo->dobj.catId.oid;
18658 1106 : seq = bsearch(&key, sequences, nsequences,
18659 : sizeof(SequenceItem), SequenceItemCmp);
18660 : }
18661 : else
18662 : {
18663 : PGresult *res;
18664 :
18665 : /*
18666 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18667 : *
18668 : * Note: it might seem that 'bigint' potentially needs to be
18669 : * schema-qualified, but actually that's a keyword.
18670 : */
18671 0 : appendPQExpBuffer(query,
18672 : "SELECT 'bigint' AS sequence_type, "
18673 : "start_value, increment_by, max_value, min_value, "
18674 : "cache_value, is_cycled FROM %s",
18675 0 : fmtQualifiedDumpable(tbinfo));
18676 :
18677 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18678 :
18679 0 : if (PQntuples(res) != 1)
18680 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18681 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18682 : PQntuples(res)),
18683 : tbinfo->dobj.name, PQntuples(res));
18684 :
18685 0 : seq = pg_malloc0(sizeof(SequenceItem));
18686 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18687 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18688 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18689 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18690 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18691 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18692 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18693 :
18694 0 : PQclear(res);
18695 : }
18696 :
18697 : /* Calculate default limits for a sequence of this type */
18698 1106 : is_ascending = (seq->incby >= 0);
18699 1106 : if (seq->seqtype == SEQTYPE_SMALLINT)
18700 : {
18701 80 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18702 80 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18703 : }
18704 1026 : else if (seq->seqtype == SEQTYPE_INTEGER)
18705 : {
18706 814 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18707 814 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18708 : }
18709 212 : else if (seq->seqtype == SEQTYPE_BIGINT)
18710 : {
18711 212 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18712 212 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18713 : }
18714 : else
18715 : {
18716 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18717 : default_minv = default_maxv = 0; /* keep compiler quiet */
18718 : }
18719 :
18720 : /*
18721 : * Identity sequences are not to be dropped separately.
18722 : */
18723 1106 : if (!tbinfo->is_identity_sequence)
18724 : {
18725 682 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18726 682 : fmtQualifiedDumpable(tbinfo));
18727 : }
18728 :
18729 1106 : resetPQExpBuffer(query);
18730 :
18731 1106 : if (dopt->binary_upgrade)
18732 : {
18733 132 : binary_upgrade_set_pg_class_oids(fout, query,
18734 132 : tbinfo->dobj.catId.oid);
18735 :
18736 : /*
18737 : * In older PG versions a sequence will have a pg_type entry, but v14
18738 : * and up don't use that, so don't attempt to preserve the type OID.
18739 : */
18740 : }
18741 :
18742 1106 : if (tbinfo->is_identity_sequence)
18743 : {
18744 424 : owning_tab = findTableByOid(tbinfo->owning_tab);
18745 :
18746 424 : appendPQExpBuffer(query,
18747 : "ALTER TABLE %s ",
18748 424 : fmtQualifiedDumpable(owning_tab));
18749 424 : appendPQExpBuffer(query,
18750 : "ALTER COLUMN %s ADD GENERATED ",
18751 424 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18752 424 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18753 296 : appendPQExpBufferStr(query, "ALWAYS");
18754 128 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18755 128 : appendPQExpBufferStr(query, "BY DEFAULT");
18756 424 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
18757 424 : fmtQualifiedDumpable(tbinfo));
18758 :
18759 : /*
18760 : * Emit persistence option only if it's different from the owning
18761 : * table's. This avoids using this new syntax unnecessarily.
18762 : */
18763 424 : if (tbinfo->relpersistence != owning_tab->relpersistence)
18764 32 : appendPQExpBuffer(query, " %s\n",
18765 32 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18766 : "UNLOGGED" : "LOGGED");
18767 : }
18768 : else
18769 : {
18770 682 : appendPQExpBuffer(query,
18771 : "CREATE %sSEQUENCE %s\n",
18772 682 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18773 : "UNLOGGED " : "",
18774 682 : fmtQualifiedDumpable(tbinfo));
18775 :
18776 682 : if (seq->seqtype != SEQTYPE_BIGINT)
18777 518 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
18778 : }
18779 :
18780 1106 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
18781 :
18782 1106 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
18783 :
18784 1106 : if (seq->minv != default_minv)
18785 48 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
18786 : else
18787 1058 : appendPQExpBufferStr(query, " NO MINVALUE\n");
18788 :
18789 1106 : if (seq->maxv != default_maxv)
18790 48 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
18791 : else
18792 1058 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
18793 :
18794 1106 : appendPQExpBuffer(query,
18795 : " CACHE " INT64_FORMAT "%s",
18796 1106 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
18797 :
18798 1106 : if (tbinfo->is_identity_sequence)
18799 424 : appendPQExpBufferStr(query, "\n);\n");
18800 : else
18801 682 : appendPQExpBufferStr(query, ";\n");
18802 :
18803 : /* binary_upgrade: no need to clear TOAST table oid */
18804 :
18805 1106 : if (dopt->binary_upgrade)
18806 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
18807 : "SEQUENCE", qseqname,
18808 132 : tbinfo->dobj.namespace->dobj.name);
18809 :
18810 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18811 1106 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18812 1106 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18813 : .namespace = tbinfo->dobj.namespace->dobj.name,
18814 : .owner = tbinfo->rolname,
18815 : .description = "SEQUENCE",
18816 : .section = SECTION_PRE_DATA,
18817 : .createStmt = query->data,
18818 : .dropStmt = delqry->data));
18819 :
18820 : /*
18821 : * If the sequence is owned by a table column, emit the ALTER for it as a
18822 : * separate TOC entry immediately following the sequence's own entry. It's
18823 : * OK to do this rather than using full sorting logic, because the
18824 : * dependency that tells us it's owned will have forced the table to be
18825 : * created first. We can't just include the ALTER in the TOC entry
18826 : * because it will fail if we haven't reassigned the sequence owner to
18827 : * match the table's owner.
18828 : *
18829 : * We need not schema-qualify the table reference because both sequence
18830 : * and table must be in the same schema.
18831 : */
18832 1106 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
18833 : {
18834 374 : owning_tab = findTableByOid(tbinfo->owning_tab);
18835 :
18836 374 : if (owning_tab == NULL)
18837 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
18838 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
18839 :
18840 374 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
18841 : {
18842 370 : resetPQExpBuffer(query);
18843 370 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
18844 370 : fmtQualifiedDumpable(tbinfo));
18845 370 : appendPQExpBuffer(query, " OWNED BY %s",
18846 370 : fmtQualifiedDumpable(owning_tab));
18847 370 : appendPQExpBuffer(query, ".%s;\n",
18848 370 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18849 :
18850 370 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18851 370 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18852 370 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18853 : .namespace = tbinfo->dobj.namespace->dobj.name,
18854 : .owner = tbinfo->rolname,
18855 : .description = "SEQUENCE OWNED BY",
18856 : .section = SECTION_PRE_DATA,
18857 : .createStmt = query->data,
18858 : .deps = &(tbinfo->dobj.dumpId),
18859 : .nDeps = 1));
18860 : }
18861 : }
18862 :
18863 : /* Dump Sequence Comments and Security Labels */
18864 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18865 0 : dumpComment(fout, "SEQUENCE", qseqname,
18866 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18867 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18868 :
18869 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18870 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
18871 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18872 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18873 :
18874 1106 : if (fout->remoteVersion < 100000)
18875 0 : pg_free(seq);
18876 1106 : destroyPQExpBuffer(query);
18877 1106 : destroyPQExpBuffer(delqry);
18878 1106 : free(qseqname);
18879 1106 : }
18880 :
18881 : /*
18882 : * dumpSequenceData
18883 : * write the data of one user-defined sequence
18884 : */
18885 : static void
18886 1136 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
18887 : {
18888 1136 : TableInfo *tbinfo = tdinfo->tdtable;
18889 : int64 last;
18890 : bool called;
18891 1136 : PQExpBuffer query = createPQExpBuffer();
18892 :
18893 : /*
18894 : * For versions >= 18, the sequence information is gathered in the sorted
18895 : * array before any calls to dumpSequenceData(). See collectSequences()
18896 : * for more information.
18897 : *
18898 : * For older versions, we have to query the sequence relations
18899 : * individually.
18900 : */
18901 1136 : if (fout->remoteVersion < 180000)
18902 : {
18903 : PGresult *res;
18904 :
18905 0 : appendPQExpBuffer(query,
18906 : "SELECT last_value, is_called FROM %s",
18907 0 : fmtQualifiedDumpable(tbinfo));
18908 :
18909 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18910 :
18911 0 : if (PQntuples(res) != 1)
18912 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18913 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18914 : PQntuples(res)),
18915 : tbinfo->dobj.name, PQntuples(res));
18916 :
18917 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18918 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18919 :
18920 0 : PQclear(res);
18921 : }
18922 : else
18923 : {
18924 1136 : SequenceItem key = {0};
18925 : SequenceItem *entry;
18926 :
18927 : Assert(sequences);
18928 : Assert(tbinfo->dobj.catId.oid);
18929 :
18930 1136 : key.oid = tbinfo->dobj.catId.oid;
18931 1136 : entry = bsearch(&key, sequences, nsequences,
18932 : sizeof(SequenceItem), SequenceItemCmp);
18933 :
18934 1136 : last = entry->last_value;
18935 1136 : called = entry->is_called;
18936 : }
18937 :
18938 1136 : resetPQExpBuffer(query);
18939 1136 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18940 1136 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18941 1136 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18942 : last, (called ? "true" : "false"));
18943 :
18944 1136 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18945 1136 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18946 1136 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18947 : .namespace = tbinfo->dobj.namespace->dobj.name,
18948 : .owner = tbinfo->rolname,
18949 : .description = "SEQUENCE SET",
18950 : .section = SECTION_DATA,
18951 : .createStmt = query->data,
18952 : .deps = &(tbinfo->dobj.dumpId),
18953 : .nDeps = 1));
18954 :
18955 1136 : destroyPQExpBuffer(query);
18956 1136 : }
18957 :
18958 : /*
18959 : * dumpTrigger
18960 : * write the declaration of one user-defined table trigger
18961 : */
18962 : static void
18963 1476 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18964 : {
18965 1476 : DumpOptions *dopt = fout->dopt;
18966 1476 : TableInfo *tbinfo = tginfo->tgtable;
18967 : PQExpBuffer query;
18968 : PQExpBuffer delqry;
18969 : PQExpBuffer trigprefix;
18970 : PQExpBuffer trigidentity;
18971 : char *qtabname;
18972 : char *tag;
18973 :
18974 : /* Do nothing if not dumping schema */
18975 1476 : if (!dopt->dumpSchema)
18976 62 : return;
18977 :
18978 1414 : query = createPQExpBuffer();
18979 1414 : delqry = createPQExpBuffer();
18980 1414 : trigprefix = createPQExpBuffer();
18981 1414 : trigidentity = createPQExpBuffer();
18982 :
18983 1414 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18984 :
18985 1414 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18986 1414 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18987 :
18988 1414 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18989 1414 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18990 :
18991 : /* Triggers can depend on extensions */
18992 1414 : append_depends_on_extension(fout, query, &tginfo->dobj,
18993 : "pg_catalog.pg_trigger", "TRIGGER",
18994 1414 : trigidentity->data);
18995 :
18996 1414 : if (tginfo->tgispartition)
18997 : {
18998 : Assert(tbinfo->ispartition);
18999 :
19000 : /*
19001 : * Partition triggers only appear here because their 'tgenabled' flag
19002 : * differs from its parent's. The trigger is created already, so
19003 : * remove the CREATE and replace it with an ALTER. (Clear out the
19004 : * DROP query too, so that pg_dump --create does not cause errors.)
19005 : */
19006 254 : resetPQExpBuffer(query);
19007 254 : resetPQExpBuffer(delqry);
19008 254 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19009 254 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19010 254 : fmtQualifiedDumpable(tbinfo));
19011 254 : switch (tginfo->tgenabled)
19012 : {
19013 90 : case 'f':
19014 : case 'D':
19015 90 : appendPQExpBufferStr(query, "DISABLE");
19016 90 : break;
19017 0 : case 't':
19018 : case 'O':
19019 0 : appendPQExpBufferStr(query, "ENABLE");
19020 0 : break;
19021 74 : case 'R':
19022 74 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19023 74 : break;
19024 90 : case 'A':
19025 90 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19026 90 : break;
19027 : }
19028 254 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19029 254 : fmtId(tginfo->dobj.name));
19030 : }
19031 1160 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19032 : {
19033 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19034 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19035 0 : fmtQualifiedDumpable(tbinfo));
19036 0 : switch (tginfo->tgenabled)
19037 : {
19038 0 : case 'D':
19039 : case 'f':
19040 0 : appendPQExpBufferStr(query, "DISABLE");
19041 0 : break;
19042 0 : case 'A':
19043 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19044 0 : break;
19045 0 : case 'R':
19046 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19047 0 : break;
19048 0 : default:
19049 0 : appendPQExpBufferStr(query, "ENABLE");
19050 0 : break;
19051 : }
19052 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19053 0 : fmtId(tginfo->dobj.name));
19054 : }
19055 :
19056 1414 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19057 1414 : fmtId(tginfo->dobj.name));
19058 :
19059 1414 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19060 :
19061 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19062 1414 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19063 1414 : ARCHIVE_OPTS(.tag = tag,
19064 : .namespace = tbinfo->dobj.namespace->dobj.name,
19065 : .owner = tbinfo->rolname,
19066 : .description = "TRIGGER",
19067 : .section = SECTION_POST_DATA,
19068 : .createStmt = query->data,
19069 : .dropStmt = delqry->data));
19070 :
19071 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19072 0 : dumpComment(fout, trigprefix->data, qtabname,
19073 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19074 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19075 :
19076 1414 : free(tag);
19077 1414 : destroyPQExpBuffer(query);
19078 1414 : destroyPQExpBuffer(delqry);
19079 1414 : destroyPQExpBuffer(trigprefix);
19080 1414 : destroyPQExpBuffer(trigidentity);
19081 1414 : free(qtabname);
19082 : }
19083 :
19084 : /*
19085 : * dumpEventTrigger
19086 : * write the declaration of one user-defined event trigger
19087 : */
19088 : static void
19089 98 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19090 : {
19091 98 : DumpOptions *dopt = fout->dopt;
19092 : PQExpBuffer query;
19093 : PQExpBuffer delqry;
19094 : char *qevtname;
19095 :
19096 : /* Do nothing if not dumping schema */
19097 98 : if (!dopt->dumpSchema)
19098 12 : return;
19099 :
19100 86 : query = createPQExpBuffer();
19101 86 : delqry = createPQExpBuffer();
19102 :
19103 86 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19104 :
19105 86 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19106 86 : appendPQExpBufferStr(query, qevtname);
19107 86 : appendPQExpBufferStr(query, " ON ");
19108 86 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19109 :
19110 86 : if (strcmp("", evtinfo->evttags) != 0)
19111 : {
19112 16 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19113 16 : appendPQExpBufferStr(query, evtinfo->evttags);
19114 16 : appendPQExpBufferChar(query, ')');
19115 : }
19116 :
19117 86 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19118 86 : appendPQExpBufferStr(query, evtinfo->evtfname);
19119 86 : appendPQExpBufferStr(query, "();\n");
19120 :
19121 86 : if (evtinfo->evtenabled != 'O')
19122 : {
19123 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19124 : qevtname);
19125 0 : switch (evtinfo->evtenabled)
19126 : {
19127 0 : case 'D':
19128 0 : appendPQExpBufferStr(query, "DISABLE");
19129 0 : break;
19130 0 : case 'A':
19131 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19132 0 : break;
19133 0 : case 'R':
19134 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19135 0 : break;
19136 0 : default:
19137 0 : appendPQExpBufferStr(query, "ENABLE");
19138 0 : break;
19139 : }
19140 0 : appendPQExpBufferStr(query, ";\n");
19141 : }
19142 :
19143 86 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19144 : qevtname);
19145 :
19146 86 : if (dopt->binary_upgrade)
19147 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19148 : "EVENT TRIGGER", qevtname, NULL);
19149 :
19150 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19151 86 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19152 86 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19153 : .owner = evtinfo->evtowner,
19154 : .description = "EVENT TRIGGER",
19155 : .section = SECTION_POST_DATA,
19156 : .createStmt = query->data,
19157 : .dropStmt = delqry->data));
19158 :
19159 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19160 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19161 0 : NULL, evtinfo->evtowner,
19162 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19163 :
19164 86 : destroyPQExpBuffer(query);
19165 86 : destroyPQExpBuffer(delqry);
19166 86 : free(qevtname);
19167 : }
19168 :
19169 : /*
19170 : * dumpRule
19171 : * Dump a rule
19172 : */
19173 : static void
19174 2984 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19175 : {
19176 2984 : DumpOptions *dopt = fout->dopt;
19177 2984 : TableInfo *tbinfo = rinfo->ruletable;
19178 : bool is_view;
19179 : PQExpBuffer query;
19180 : PQExpBuffer cmd;
19181 : PQExpBuffer delcmd;
19182 : PQExpBuffer ruleprefix;
19183 : char *qtabname;
19184 : PGresult *res;
19185 : char *tag;
19186 :
19187 : /* Do nothing if not dumping schema */
19188 2984 : if (!dopt->dumpSchema)
19189 132 : return;
19190 :
19191 : /*
19192 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19193 : * we do not want to dump it as a separate object.
19194 : */
19195 2852 : if (!rinfo->separate)
19196 2184 : return;
19197 :
19198 : /*
19199 : * If it's an ON SELECT rule, we want to print it as a view definition,
19200 : * instead of a rule.
19201 : */
19202 668 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19203 :
19204 668 : query = createPQExpBuffer();
19205 668 : cmd = createPQExpBuffer();
19206 668 : delcmd = createPQExpBuffer();
19207 668 : ruleprefix = createPQExpBuffer();
19208 :
19209 668 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19210 :
19211 668 : if (is_view)
19212 : {
19213 : PQExpBuffer result;
19214 :
19215 : /*
19216 : * We need OR REPLACE here because we'll be replacing a dummy view.
19217 : * Otherwise this should look largely like the regular view dump code.
19218 : */
19219 32 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19220 32 : fmtQualifiedDumpable(tbinfo));
19221 32 : if (nonemptyReloptions(tbinfo->reloptions))
19222 : {
19223 0 : appendPQExpBufferStr(cmd, " WITH (");
19224 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19225 0 : appendPQExpBufferChar(cmd, ')');
19226 : }
19227 32 : result = createViewAsClause(fout, tbinfo);
19228 32 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19229 32 : destroyPQExpBuffer(result);
19230 32 : if (tbinfo->checkoption != NULL)
19231 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19232 : tbinfo->checkoption);
19233 32 : appendPQExpBufferStr(cmd, ";\n");
19234 : }
19235 : else
19236 : {
19237 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19238 636 : appendPQExpBuffer(query,
19239 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19240 636 : rinfo->dobj.catId.oid);
19241 :
19242 636 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19243 :
19244 636 : if (PQntuples(res) != 1)
19245 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19246 : rinfo->dobj.name, tbinfo->dobj.name);
19247 :
19248 636 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19249 :
19250 636 : PQclear(res);
19251 : }
19252 :
19253 : /*
19254 : * Add the command to alter the rules replication firing semantics if it
19255 : * differs from the default.
19256 : */
19257 668 : if (rinfo->ev_enabled != 'O')
19258 : {
19259 48 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19260 48 : switch (rinfo->ev_enabled)
19261 : {
19262 0 : case 'A':
19263 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19264 0 : fmtId(rinfo->dobj.name));
19265 0 : break;
19266 0 : case 'R':
19267 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19268 0 : fmtId(rinfo->dobj.name));
19269 0 : break;
19270 48 : case 'D':
19271 48 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19272 48 : fmtId(rinfo->dobj.name));
19273 48 : break;
19274 : }
19275 : }
19276 :
19277 668 : if (is_view)
19278 : {
19279 : /*
19280 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19281 : * REPLACE VIEW to replace the rule with something with minimal
19282 : * dependencies.
19283 : */
19284 : PQExpBuffer result;
19285 :
19286 32 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19287 32 : fmtQualifiedDumpable(tbinfo));
19288 32 : result = createDummyViewAsClause(fout, tbinfo);
19289 32 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19290 32 : destroyPQExpBuffer(result);
19291 : }
19292 : else
19293 : {
19294 636 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19295 636 : fmtId(rinfo->dobj.name));
19296 636 : appendPQExpBuffer(delcmd, "ON %s;\n",
19297 636 : fmtQualifiedDumpable(tbinfo));
19298 : }
19299 :
19300 668 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19301 668 : fmtId(rinfo->dobj.name));
19302 :
19303 668 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19304 :
19305 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19306 668 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19307 668 : ARCHIVE_OPTS(.tag = tag,
19308 : .namespace = tbinfo->dobj.namespace->dobj.name,
19309 : .owner = tbinfo->rolname,
19310 : .description = "RULE",
19311 : .section = SECTION_POST_DATA,
19312 : .createStmt = cmd->data,
19313 : .dropStmt = delcmd->data));
19314 :
19315 : /* Dump rule comments */
19316 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19317 0 : dumpComment(fout, ruleprefix->data, qtabname,
19318 0 : tbinfo->dobj.namespace->dobj.name,
19319 : tbinfo->rolname,
19320 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19321 :
19322 668 : free(tag);
19323 668 : destroyPQExpBuffer(query);
19324 668 : destroyPQExpBuffer(cmd);
19325 668 : destroyPQExpBuffer(delcmd);
19326 668 : destroyPQExpBuffer(ruleprefix);
19327 668 : free(qtabname);
19328 : }
19329 :
19330 : /*
19331 : * getExtensionMembership --- obtain extension membership data
19332 : *
19333 : * We need to identify objects that are extension members as soon as they're
19334 : * loaded, so that we can correctly determine whether they need to be dumped.
19335 : * Generally speaking, extension member objects will get marked as *not* to
19336 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19337 : * command. However, in binary upgrade mode we still need to dump the members
19338 : * individually.
19339 : */
19340 : void
19341 470 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19342 : int numExtensions)
19343 : {
19344 : PQExpBuffer query;
19345 : PGresult *res;
19346 : int ntups,
19347 : i;
19348 : int i_classid,
19349 : i_objid,
19350 : i_refobjid;
19351 : ExtensionInfo *ext;
19352 :
19353 : /* Nothing to do if no extensions */
19354 470 : if (numExtensions == 0)
19355 0 : return;
19356 :
19357 470 : query = createPQExpBuffer();
19358 :
19359 : /* refclassid constraint is redundant but may speed the search */
19360 470 : appendPQExpBufferStr(query, "SELECT "
19361 : "classid, objid, refobjid "
19362 : "FROM pg_depend "
19363 : "WHERE refclassid = 'pg_extension'::regclass "
19364 : "AND deptype = 'e' "
19365 : "ORDER BY 3");
19366 :
19367 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19368 :
19369 470 : ntups = PQntuples(res);
19370 :
19371 470 : i_classid = PQfnumber(res, "classid");
19372 470 : i_objid = PQfnumber(res, "objid");
19373 470 : i_refobjid = PQfnumber(res, "refobjid");
19374 :
19375 : /*
19376 : * Since we ordered the SELECT by referenced ID, we can expect that
19377 : * multiple entries for the same extension will appear together; this
19378 : * saves on searches.
19379 : */
19380 470 : ext = NULL;
19381 :
19382 3550 : for (i = 0; i < ntups; i++)
19383 : {
19384 : CatalogId objId;
19385 : Oid extId;
19386 :
19387 3080 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19388 3080 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19389 3080 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19390 :
19391 3080 : if (ext == NULL ||
19392 2610 : ext->dobj.catId.oid != extId)
19393 520 : ext = findExtensionByOid(extId);
19394 :
19395 3080 : if (ext == NULL)
19396 : {
19397 : /* shouldn't happen */
19398 0 : pg_log_warning("could not find referenced extension %u", extId);
19399 0 : continue;
19400 : }
19401 :
19402 3080 : recordExtensionMembership(objId, ext);
19403 : }
19404 :
19405 470 : PQclear(res);
19406 :
19407 470 : destroyPQExpBuffer(query);
19408 : }
19409 :
19410 : /*
19411 : * processExtensionTables --- deal with extension configuration tables
19412 : *
19413 : * There are two parts to this process:
19414 : *
19415 : * 1. Identify and create dump records for extension configuration tables.
19416 : *
19417 : * Extensions can mark tables as "configuration", which means that the user
19418 : * is able and expected to modify those tables after the extension has been
19419 : * loaded. For these tables, we dump out only the data- the structure is
19420 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19421 : * foreign keys, which brings us to-
19422 : *
19423 : * 2. Record FK dependencies between configuration tables.
19424 : *
19425 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19426 : * the data is loaded, we have to work out what the best order for reloading
19427 : * the data is, to avoid FK violations when the tables are restored. This is
19428 : * not perfect- we can't handle circular dependencies and if any exist they
19429 : * will cause an invalid dump to be produced (though at least all of the data
19430 : * is included for a user to manually restore). This is currently documented
19431 : * but perhaps we can provide a better solution in the future.
19432 : */
19433 : void
19434 468 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19435 : int numExtensions)
19436 : {
19437 468 : DumpOptions *dopt = fout->dopt;
19438 : PQExpBuffer query;
19439 : PGresult *res;
19440 : int ntups,
19441 : i;
19442 : int i_conrelid,
19443 : i_confrelid;
19444 :
19445 : /* Nothing to do if no extensions */
19446 468 : if (numExtensions == 0)
19447 0 : return;
19448 :
19449 : /*
19450 : * Identify extension configuration tables and create TableDataInfo
19451 : * objects for them, ensuring their data will be dumped even though the
19452 : * tables themselves won't be.
19453 : *
19454 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19455 : * user data in a configuration table is treated like schema data. This
19456 : * seems appropriate since system data in a config table would get
19457 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19458 : * list of extensions to be included, none of its data is dumped.
19459 : */
19460 986 : for (i = 0; i < numExtensions; i++)
19461 : {
19462 518 : ExtensionInfo *curext = &(extinfo[i]);
19463 518 : char *extconfig = curext->extconfig;
19464 518 : char *extcondition = curext->extcondition;
19465 518 : char **extconfigarray = NULL;
19466 518 : char **extconditionarray = NULL;
19467 518 : int nconfigitems = 0;
19468 518 : int nconditionitems = 0;
19469 :
19470 : /*
19471 : * Check if this extension is listed as to include in the dump. If
19472 : * not, any table data associated with it is discarded.
19473 : */
19474 518 : if (extension_include_oids.head != NULL &&
19475 16 : !simple_oid_list_member(&extension_include_oids,
19476 : curext->dobj.catId.oid))
19477 12 : continue;
19478 :
19479 : /*
19480 : * Check if this extension is listed as to exclude in the dump. If
19481 : * yes, any table data associated with it is discarded.
19482 : */
19483 518 : if (extension_exclude_oids.head != NULL &&
19484 8 : simple_oid_list_member(&extension_exclude_oids,
19485 : curext->dobj.catId.oid))
19486 4 : continue;
19487 :
19488 506 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19489 : {
19490 : int j;
19491 :
19492 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19493 0 : pg_fatal("could not parse %s array", "extconfig");
19494 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19495 0 : pg_fatal("could not parse %s array", "extcondition");
19496 40 : if (nconfigitems != nconditionitems)
19497 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19498 :
19499 120 : for (j = 0; j < nconfigitems; j++)
19500 : {
19501 : TableInfo *configtbl;
19502 80 : Oid configtbloid = atooid(extconfigarray[j]);
19503 80 : bool dumpobj =
19504 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19505 :
19506 80 : configtbl = findTableByOid(configtbloid);
19507 80 : if (configtbl == NULL)
19508 0 : continue;
19509 :
19510 : /*
19511 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19512 : * unless the table or its schema is explicitly included
19513 : */
19514 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19515 : {
19516 : /* check table explicitly requested */
19517 4 : if (table_include_oids.head != NULL &&
19518 0 : simple_oid_list_member(&table_include_oids,
19519 : configtbloid))
19520 0 : dumpobj = true;
19521 :
19522 : /* check table's schema explicitly requested */
19523 4 : if (configtbl->dobj.namespace->dobj.dump &
19524 : DUMP_COMPONENT_DATA)
19525 4 : dumpobj = true;
19526 : }
19527 :
19528 : /* check table excluded by an exclusion switch */
19529 88 : if (table_exclude_oids.head != NULL &&
19530 8 : simple_oid_list_member(&table_exclude_oids,
19531 : configtbloid))
19532 2 : dumpobj = false;
19533 :
19534 : /* check schema excluded by an exclusion switch */
19535 80 : if (simple_oid_list_member(&schema_exclude_oids,
19536 80 : configtbl->dobj.namespace->dobj.catId.oid))
19537 0 : dumpobj = false;
19538 :
19539 80 : if (dumpobj)
19540 : {
19541 78 : makeTableDataInfo(dopt, configtbl);
19542 78 : if (configtbl->dataObj != NULL)
19543 : {
19544 78 : if (strlen(extconditionarray[j]) > 0)
19545 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19546 : }
19547 : }
19548 : }
19549 : }
19550 506 : if (extconfigarray)
19551 40 : free(extconfigarray);
19552 506 : if (extconditionarray)
19553 40 : free(extconditionarray);
19554 : }
19555 :
19556 : /*
19557 : * Now that all the TableDataInfo objects have been created for all the
19558 : * extensions, check their FK dependencies and register them to try and
19559 : * dump the data out in an order that they can be restored in.
19560 : *
19561 : * Note that this is not a problem for user tables as their FKs are
19562 : * recreated after the data has been loaded.
19563 : */
19564 :
19565 468 : query = createPQExpBuffer();
19566 :
19567 468 : printfPQExpBuffer(query,
19568 : "SELECT conrelid, confrelid "
19569 : "FROM pg_constraint "
19570 : "JOIN pg_depend ON (objid = confrelid) "
19571 : "WHERE contype = 'f' "
19572 : "AND refclassid = 'pg_extension'::regclass "
19573 : "AND classid = 'pg_class'::regclass;");
19574 :
19575 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19576 468 : ntups = PQntuples(res);
19577 :
19578 468 : i_conrelid = PQfnumber(res, "conrelid");
19579 468 : i_confrelid = PQfnumber(res, "confrelid");
19580 :
19581 : /* Now get the dependencies and register them */
19582 468 : for (i = 0; i < ntups; i++)
19583 : {
19584 : Oid conrelid,
19585 : confrelid;
19586 : TableInfo *reftable,
19587 : *contable;
19588 :
19589 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19590 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19591 0 : contable = findTableByOid(conrelid);
19592 0 : reftable = findTableByOid(confrelid);
19593 :
19594 0 : if (reftable == NULL ||
19595 0 : reftable->dataObj == NULL ||
19596 0 : contable == NULL ||
19597 0 : contable->dataObj == NULL)
19598 0 : continue;
19599 :
19600 : /*
19601 : * Make referencing TABLE_DATA object depend on the referenced table's
19602 : * TABLE_DATA object.
19603 : */
19604 0 : addObjectDependency(&contable->dataObj->dobj,
19605 0 : reftable->dataObj->dobj.dumpId);
19606 : }
19607 468 : PQclear(res);
19608 468 : destroyPQExpBuffer(query);
19609 : }
19610 :
19611 : /*
19612 : * getDependencies --- obtain available dependency data
19613 : */
19614 : static void
19615 468 : getDependencies(Archive *fout)
19616 : {
19617 : PQExpBuffer query;
19618 : PGresult *res;
19619 : int ntups,
19620 : i;
19621 : int i_classid,
19622 : i_objid,
19623 : i_refclassid,
19624 : i_refobjid,
19625 : i_deptype;
19626 : DumpableObject *dobj,
19627 : *refdobj;
19628 :
19629 468 : pg_log_info("reading dependency data");
19630 :
19631 468 : query = createPQExpBuffer();
19632 :
19633 : /*
19634 : * Messy query to collect the dependency data we need. Note that we
19635 : * ignore the sub-object column, so that dependencies of or on a column
19636 : * look the same as dependencies of or on a whole table.
19637 : *
19638 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19639 : * already processed by getExtensionMembership.
19640 : */
19641 468 : appendPQExpBufferStr(query, "SELECT "
19642 : "classid, objid, refclassid, refobjid, deptype "
19643 : "FROM pg_depend "
19644 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19645 :
19646 : /*
19647 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19648 : * have to translate their dependencies into dependencies of their parent
19649 : * opfamily. Ignore internal dependencies though, as those will point to
19650 : * their parent opclass, which we needn't consider here (and if we did,
19651 : * it'd just result in circular dependencies). Also, "loose" opfamily
19652 : * entries will have dependencies on their parent opfamily, which we
19653 : * should drop since they'd likewise become useless self-dependencies.
19654 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19655 : */
19656 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19657 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19658 : "FROM pg_depend d, pg_amop o "
19659 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19660 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19661 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19662 :
19663 : /* Likewise for pg_amproc entries */
19664 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19665 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19666 : "FROM pg_depend d, pg_amproc p "
19667 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19668 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19669 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19670 :
19671 : /* Sort the output for efficiency below */
19672 468 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19673 :
19674 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19675 :
19676 468 : ntups = PQntuples(res);
19677 :
19678 468 : i_classid = PQfnumber(res, "classid");
19679 468 : i_objid = PQfnumber(res, "objid");
19680 468 : i_refclassid = PQfnumber(res, "refclassid");
19681 468 : i_refobjid = PQfnumber(res, "refobjid");
19682 468 : i_deptype = PQfnumber(res, "deptype");
19683 :
19684 : /*
19685 : * Since we ordered the SELECT by referencing ID, we can expect that
19686 : * multiple entries for the same object will appear together; this saves
19687 : * on searches.
19688 : */
19689 468 : dobj = NULL;
19690 :
19691 1010832 : for (i = 0; i < ntups; i++)
19692 : {
19693 : CatalogId objId;
19694 : CatalogId refobjId;
19695 : char deptype;
19696 :
19697 1010364 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19698 1010364 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19699 1010364 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19700 1010364 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19701 1010364 : deptype = *(PQgetvalue(res, i, i_deptype));
19702 :
19703 1010364 : if (dobj == NULL ||
19704 948266 : dobj->catId.tableoid != objId.tableoid ||
19705 943224 : dobj->catId.oid != objId.oid)
19706 442914 : dobj = findObjectByCatalogId(objId);
19707 :
19708 : /*
19709 : * Failure to find objects mentioned in pg_depend is not unexpected,
19710 : * since for example we don't collect info about TOAST tables.
19711 : */
19712 1010364 : if (dobj == NULL)
19713 : {
19714 : #ifdef NOT_USED
19715 : pg_log_warning("no referencing object %u %u",
19716 : objId.tableoid, objId.oid);
19717 : #endif
19718 63854 : continue;
19719 : }
19720 :
19721 948734 : refdobj = findObjectByCatalogId(refobjId);
19722 :
19723 948734 : if (refdobj == NULL)
19724 : {
19725 : #ifdef NOT_USED
19726 : pg_log_warning("no referenced object %u %u",
19727 : refobjId.tableoid, refobjId.oid);
19728 : #endif
19729 2224 : continue;
19730 : }
19731 :
19732 : /*
19733 : * For 'x' dependencies, mark the object for later; we still add the
19734 : * normal dependency, for possible ordering purposes. Currently
19735 : * pg_dump_sort.c knows to put extensions ahead of all object types
19736 : * that could possibly depend on them, but this is safer.
19737 : */
19738 946510 : if (deptype == 'x')
19739 88 : dobj->depends_on_ext = true;
19740 :
19741 : /*
19742 : * Ordinarily, table rowtypes have implicit dependencies on their
19743 : * tables. However, for a composite type the implicit dependency goes
19744 : * the other way in pg_depend; which is the right thing for DROP but
19745 : * it doesn't produce the dependency ordering we need. So in that one
19746 : * case, we reverse the direction of the dependency.
19747 : */
19748 946510 : if (deptype == 'i' &&
19749 265576 : dobj->objType == DO_TABLE &&
19750 3248 : refdobj->objType == DO_TYPE)
19751 468 : addObjectDependency(refdobj, dobj->dumpId);
19752 : else
19753 : /* normal case */
19754 946042 : addObjectDependency(dobj, refdobj->dumpId);
19755 : }
19756 :
19757 468 : PQclear(res);
19758 :
19759 468 : destroyPQExpBuffer(query);
19760 468 : }
19761 :
19762 :
19763 : /*
19764 : * createBoundaryObjects - create dummy DumpableObjects to represent
19765 : * dump section boundaries.
19766 : */
19767 : static DumpableObject *
19768 468 : createBoundaryObjects(void)
19769 : {
19770 : DumpableObject *dobjs;
19771 :
19772 468 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
19773 :
19774 468 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
19775 468 : dobjs[0].catId = nilCatalogId;
19776 468 : AssignDumpId(dobjs + 0);
19777 468 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
19778 :
19779 468 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
19780 468 : dobjs[1].catId = nilCatalogId;
19781 468 : AssignDumpId(dobjs + 1);
19782 468 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
19783 :
19784 468 : return dobjs;
19785 : }
19786 :
19787 : /*
19788 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
19789 : * section boundaries.
19790 : */
19791 : static void
19792 468 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
19793 : DumpableObject *boundaryObjs)
19794 : {
19795 468 : DumpableObject *preDataBound = boundaryObjs + 0;
19796 468 : DumpableObject *postDataBound = boundaryObjs + 1;
19797 : int i;
19798 :
19799 1737994 : for (i = 0; i < numObjs; i++)
19800 : {
19801 1737526 : DumpableObject *dobj = dobjs[i];
19802 :
19803 : /*
19804 : * The classification of object types here must match the SECTION_xxx
19805 : * values assigned during subsequent ArchiveEntry calls!
19806 : */
19807 1737526 : switch (dobj->objType)
19808 : {
19809 1623214 : case DO_NAMESPACE:
19810 : case DO_EXTENSION:
19811 : case DO_TYPE:
19812 : case DO_SHELL_TYPE:
19813 : case DO_FUNC:
19814 : case DO_AGG:
19815 : case DO_OPERATOR:
19816 : case DO_ACCESS_METHOD:
19817 : case DO_OPCLASS:
19818 : case DO_OPFAMILY:
19819 : case DO_COLLATION:
19820 : case DO_CONVERSION:
19821 : case DO_TABLE:
19822 : case DO_TABLE_ATTACH:
19823 : case DO_ATTRDEF:
19824 : case DO_PROCLANG:
19825 : case DO_CAST:
19826 : case DO_DUMMY_TYPE:
19827 : case DO_TSPARSER:
19828 : case DO_TSDICT:
19829 : case DO_TSTEMPLATE:
19830 : case DO_TSCONFIG:
19831 : case DO_FDW:
19832 : case DO_FOREIGN_SERVER:
19833 : case DO_TRANSFORM:
19834 : /* Pre-data objects: must come before the pre-data boundary */
19835 1623214 : addObjectDependency(preDataBound, dobj->dumpId);
19836 1623214 : break;
19837 13878 : case DO_TABLE_DATA:
19838 : case DO_SEQUENCE_SET:
19839 : case DO_LARGE_OBJECT:
19840 : case DO_LARGE_OBJECT_DATA:
19841 : /* Data objects: must come between the boundaries */
19842 13878 : addObjectDependency(dobj, preDataBound->dumpId);
19843 13878 : addObjectDependency(postDataBound, dobj->dumpId);
19844 13878 : break;
19845 14124 : case DO_INDEX:
19846 : case DO_INDEX_ATTACH:
19847 : case DO_STATSEXT:
19848 : case DO_REFRESH_MATVIEW:
19849 : case DO_TRIGGER:
19850 : case DO_EVENT_TRIGGER:
19851 : case DO_DEFAULT_ACL:
19852 : case DO_POLICY:
19853 : case DO_PUBLICATION:
19854 : case DO_PUBLICATION_REL:
19855 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
19856 : case DO_SUBSCRIPTION:
19857 : case DO_SUBSCRIPTION_REL:
19858 : /* Post-data objects: must come after the post-data boundary */
19859 14124 : addObjectDependency(dobj, postDataBound->dumpId);
19860 14124 : break;
19861 71834 : case DO_RULE:
19862 : /* Rules are post-data, but only if dumped separately */
19863 71834 : if (((RuleInfo *) dobj)->separate)
19864 1716 : addObjectDependency(dobj, postDataBound->dumpId);
19865 71834 : break;
19866 6490 : case DO_CONSTRAINT:
19867 : case DO_FK_CONSTRAINT:
19868 : /* Constraints are post-data, but only if dumped separately */
19869 6490 : if (((ConstraintInfo *) dobj)->separate)
19870 4760 : addObjectDependency(dobj, postDataBound->dumpId);
19871 6490 : break;
19872 468 : case DO_PRE_DATA_BOUNDARY:
19873 : /* nothing to do */
19874 468 : break;
19875 468 : case DO_POST_DATA_BOUNDARY:
19876 : /* must come after the pre-data boundary */
19877 468 : addObjectDependency(dobj, preDataBound->dumpId);
19878 468 : break;
19879 7050 : case DO_REL_STATS:
19880 : /* stats section varies by parent object type, DATA or POST */
19881 7050 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
19882 : {
19883 4448 : addObjectDependency(dobj, preDataBound->dumpId);
19884 4448 : addObjectDependency(postDataBound, dobj->dumpId);
19885 : }
19886 : else
19887 2602 : addObjectDependency(dobj, postDataBound->dumpId);
19888 7050 : break;
19889 : }
19890 : }
19891 468 : }
19892 :
19893 :
19894 : /*
19895 : * BuildArchiveDependencies - create dependency data for archive TOC entries
19896 : *
19897 : * The raw dependency data obtained by getDependencies() is not terribly
19898 : * useful in an archive dump, because in many cases there are dependency
19899 : * chains linking through objects that don't appear explicitly in the dump.
19900 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
19901 : * will depend on other objects --- but the rule will not appear as a separate
19902 : * object in the dump. We need to adjust the view's dependencies to include
19903 : * whatever the rule depends on that is included in the dump.
19904 : *
19905 : * Just to make things more complicated, there are also "special" dependencies
19906 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
19907 : * not rearrange because pg_restore knows that TABLE DATA only depends on
19908 : * its table. In these cases we must leave the dependencies strictly as-is
19909 : * even if they refer to not-to-be-dumped objects.
19910 : *
19911 : * To handle this, the convention is that "special" dependencies are created
19912 : * during ArchiveEntry calls, and an archive TOC item that has any such
19913 : * entries will not be touched here. Otherwise, we recursively search the
19914 : * DumpableObject data structures to build the correct dependencies for each
19915 : * archive TOC item.
19916 : */
19917 : static void
19918 206 : BuildArchiveDependencies(Archive *fout)
19919 : {
19920 206 : ArchiveHandle *AH = (ArchiveHandle *) fout;
19921 : TocEntry *te;
19922 :
19923 : /* Scan all TOC entries in the archive */
19924 20548 : for (te = AH->toc->next; te != AH->toc; te = te->next)
19925 : {
19926 : DumpableObject *dobj;
19927 : DumpId *dependencies;
19928 : int nDeps;
19929 : int allocDeps;
19930 :
19931 : /* No need to process entries that will not be dumped */
19932 20342 : if (te->reqs == 0)
19933 9042 : continue;
19934 : /* Ignore entries that already have "special" dependencies */
19935 20336 : if (te->nDeps > 0)
19936 7790 : continue;
19937 : /* Otherwise, look up the item's original DumpableObject, if any */
19938 12546 : dobj = findObjectByDumpId(te->dumpId);
19939 12546 : if (dobj == NULL)
19940 978 : continue;
19941 : /* No work if it has no dependencies */
19942 11568 : if (dobj->nDeps <= 0)
19943 268 : continue;
19944 : /* Set up work array */
19945 11300 : allocDeps = 64;
19946 11300 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19947 11300 : nDeps = 0;
19948 : /* Recursively find all dumpable dependencies */
19949 11300 : findDumpableDependencies(AH, dobj,
19950 : &dependencies, &nDeps, &allocDeps);
19951 : /* And save 'em ... */
19952 11300 : if (nDeps > 0)
19953 : {
19954 8016 : dependencies = (DumpId *) pg_realloc(dependencies,
19955 : nDeps * sizeof(DumpId));
19956 8016 : te->dependencies = dependencies;
19957 8016 : te->nDeps = nDeps;
19958 : }
19959 : else
19960 3284 : free(dependencies);
19961 : }
19962 206 : }
19963 :
19964 : /* Recursive search subroutine for BuildArchiveDependencies */
19965 : static void
19966 26712 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19967 : DumpId **dependencies, int *nDeps, int *allocDeps)
19968 : {
19969 : int i;
19970 :
19971 : /*
19972 : * Ignore section boundary objects: if we search through them, we'll
19973 : * report lots of bogus dependencies.
19974 : */
19975 26712 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19976 26672 : dobj->objType == DO_POST_DATA_BOUNDARY)
19977 4312 : return;
19978 :
19979 54692 : for (i = 0; i < dobj->nDeps; i++)
19980 : {
19981 32292 : DumpId depid = dobj->dependencies[i];
19982 :
19983 32292 : if (TocIDRequired(AH, depid) != 0)
19984 : {
19985 : /* Object will be dumped, so just reference it as a dependency */
19986 16880 : if (*nDeps >= *allocDeps)
19987 : {
19988 0 : *allocDeps *= 2;
19989 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
19990 0 : *allocDeps * sizeof(DumpId));
19991 : }
19992 16880 : (*dependencies)[*nDeps] = depid;
19993 16880 : (*nDeps)++;
19994 : }
19995 : else
19996 : {
19997 : /*
19998 : * Object will not be dumped, so recursively consider its deps. We
19999 : * rely on the assumption that sortDumpableObjects already broke
20000 : * any dependency loops, else we might recurse infinitely.
20001 : */
20002 15412 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20003 :
20004 15412 : if (otherdobj)
20005 15412 : findDumpableDependencies(AH, otherdobj,
20006 : dependencies, nDeps, allocDeps);
20007 : }
20008 : }
20009 : }
20010 :
20011 :
20012 : /*
20013 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20014 : * given type OID.
20015 : *
20016 : * This does not guarantee to schema-qualify the output, so it should not
20017 : * be used to create the target object name for CREATE or ALTER commands.
20018 : *
20019 : * Note that the result is cached and must not be freed by the caller.
20020 : */
20021 : static const char *
20022 6916 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20023 : {
20024 : TypeInfo *typeInfo;
20025 : char *result;
20026 : PQExpBuffer query;
20027 : PGresult *res;
20028 :
20029 6916 : if (oid == 0)
20030 : {
20031 0 : if ((opts & zeroAsStar) != 0)
20032 0 : return "*";
20033 0 : else if ((opts & zeroAsNone) != 0)
20034 0 : return "NONE";
20035 : }
20036 :
20037 : /* see if we have the result cached in the type's TypeInfo record */
20038 6916 : typeInfo = findTypeByOid(oid);
20039 6916 : if (typeInfo && typeInfo->ftypname)
20040 5680 : return typeInfo->ftypname;
20041 :
20042 1236 : query = createPQExpBuffer();
20043 1236 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20044 : oid);
20045 :
20046 1236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20047 :
20048 : /* result of format_type is already quoted */
20049 1236 : result = pg_strdup(PQgetvalue(res, 0, 0));
20050 :
20051 1236 : PQclear(res);
20052 1236 : destroyPQExpBuffer(query);
20053 :
20054 : /*
20055 : * Cache the result for re-use in later requests, if possible. If we
20056 : * don't have a TypeInfo for the type, the string will be leaked once the
20057 : * caller is done with it ... but that case really should not happen, so
20058 : * leaking if it does seems acceptable.
20059 : */
20060 1236 : if (typeInfo)
20061 1236 : typeInfo->ftypname = result;
20062 :
20063 1236 : return result;
20064 : }
20065 :
20066 : /*
20067 : * Return a column list clause for the given relation.
20068 : *
20069 : * Special case: if there are no undropped columns in the relation, return
20070 : * "", not an invalid "()" column list.
20071 : */
20072 : static const char *
20073 24344 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20074 : {
20075 24344 : int numatts = ti->numatts;
20076 24344 : char **attnames = ti->attnames;
20077 24344 : bool *attisdropped = ti->attisdropped;
20078 24344 : char *attgenerated = ti->attgenerated;
20079 : bool needComma;
20080 : int i;
20081 :
20082 24344 : appendPQExpBufferChar(buffer, '(');
20083 24344 : needComma = false;
20084 120832 : for (i = 0; i < numatts; i++)
20085 : {
20086 96488 : if (attisdropped[i])
20087 1744 : continue;
20088 94744 : if (attgenerated[i])
20089 3216 : continue;
20090 91528 : if (needComma)
20091 67716 : appendPQExpBufferStr(buffer, ", ");
20092 91528 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20093 91528 : needComma = true;
20094 : }
20095 :
20096 24344 : if (!needComma)
20097 532 : return ""; /* no undropped columns */
20098 :
20099 23812 : appendPQExpBufferChar(buffer, ')');
20100 23812 : return buffer->data;
20101 : }
20102 :
20103 : /*
20104 : * Check if a reloptions array is nonempty.
20105 : */
20106 : static bool
20107 37842 : nonemptyReloptions(const char *reloptions)
20108 : {
20109 : /* Don't want to print it if it's just "{}" */
20110 37842 : return (reloptions != NULL && strlen(reloptions) > 2);
20111 : }
20112 :
20113 : /*
20114 : * Format a reloptions array and append it to the given buffer.
20115 : *
20116 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20117 : */
20118 : static void
20119 594 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20120 : const char *prefix, Archive *fout)
20121 : {
20122 : bool res;
20123 :
20124 594 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20125 594 : fout->std_strings);
20126 594 : if (!res)
20127 0 : pg_log_warning("could not parse %s array", "reloptions");
20128 594 : }
20129 :
20130 : /*
20131 : * read_dump_filters - retrieve object identifier patterns from file
20132 : *
20133 : * Parse the specified filter file for include and exclude patterns, and add
20134 : * them to the relevant lists. If the filename is "-" then filters will be
20135 : * read from STDIN rather than a file.
20136 : */
20137 : static void
20138 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20139 : {
20140 : FilterStateData fstate;
20141 : char *objname;
20142 : FilterCommandType comtype;
20143 : FilterObjectType objtype;
20144 :
20145 52 : filter_init(&fstate, filename, exit_nicely);
20146 :
20147 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20148 : {
20149 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20150 : {
20151 34 : switch (objtype)
20152 : {
20153 0 : case FILTER_OBJECT_TYPE_NONE:
20154 0 : break;
20155 0 : case FILTER_OBJECT_TYPE_DATABASE:
20156 : case FILTER_OBJECT_TYPE_FUNCTION:
20157 : case FILTER_OBJECT_TYPE_INDEX:
20158 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20159 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20160 : case FILTER_OBJECT_TYPE_TRIGGER:
20161 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20162 : "include",
20163 : filter_object_type_name(objtype));
20164 0 : exit_nicely(1);
20165 : break; /* unreachable */
20166 :
20167 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20168 2 : simple_string_list_append(&extension_include_patterns, objname);
20169 2 : break;
20170 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20171 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20172 2 : break;
20173 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20174 2 : simple_string_list_append(&schema_include_patterns, objname);
20175 2 : dopt->include_everything = false;
20176 2 : break;
20177 26 : case FILTER_OBJECT_TYPE_TABLE:
20178 26 : simple_string_list_append(&table_include_patterns, objname);
20179 26 : dopt->include_everything = false;
20180 26 : break;
20181 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20182 2 : simple_string_list_append(&table_include_patterns_and_children,
20183 : objname);
20184 2 : dopt->include_everything = false;
20185 2 : break;
20186 : }
20187 : }
20188 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20189 : {
20190 18 : switch (objtype)
20191 : {
20192 0 : case FILTER_OBJECT_TYPE_NONE:
20193 0 : break;
20194 2 : case FILTER_OBJECT_TYPE_DATABASE:
20195 : case FILTER_OBJECT_TYPE_FUNCTION:
20196 : case FILTER_OBJECT_TYPE_INDEX:
20197 : case FILTER_OBJECT_TYPE_TRIGGER:
20198 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20199 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20200 : "exclude",
20201 : filter_object_type_name(objtype));
20202 2 : exit_nicely(1);
20203 : break;
20204 :
20205 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20206 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20207 2 : break;
20208 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20209 2 : simple_string_list_append(&tabledata_exclude_patterns,
20210 : objname);
20211 2 : break;
20212 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20213 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20214 : objname);
20215 2 : break;
20216 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20217 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20218 4 : break;
20219 4 : case FILTER_OBJECT_TYPE_TABLE:
20220 4 : simple_string_list_append(&table_exclude_patterns, objname);
20221 4 : break;
20222 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20223 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20224 : objname);
20225 2 : break;
20226 : }
20227 : }
20228 : else
20229 : {
20230 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20231 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20232 : }
20233 :
20234 64 : if (objname)
20235 50 : free(objname);
20236 : }
20237 :
20238 44 : filter_free(&fstate);
20239 44 : }
|