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 "compress_io.h"
60 : #include "dumputils.h"
61 : #include "fe_utils/option_utils.h"
62 : #include "fe_utils/string_utils.h"
63 : #include "filter.h"
64 : #include "getopt_long.h"
65 : #include "libpq/libpq-fs.h"
66 : #include "parallel.h"
67 : #include "pg_backup_db.h"
68 : #include "pg_backup_utils.h"
69 : #include "pg_dump.h"
70 : #include "storage/block.h"
71 :
72 : typedef struct
73 : {
74 : Oid roleoid; /* role's OID */
75 : const char *rolename; /* role's name */
76 : } RoleNameItem;
77 :
78 : typedef struct
79 : {
80 : const char *descr; /* comment for an object */
81 : Oid classoid; /* object class (catalog OID) */
82 : Oid objoid; /* object OID */
83 : int objsubid; /* subobject (table column #) */
84 : } CommentItem;
85 :
86 : typedef struct
87 : {
88 : const char *provider; /* label provider of this security label */
89 : const char *label; /* security label for an object */
90 : Oid classoid; /* object class (catalog OID) */
91 : Oid objoid; /* object OID */
92 : int objsubid; /* subobject (table column #) */
93 : } SecLabelItem;
94 :
95 : typedef struct
96 : {
97 : Oid oid; /* object OID */
98 : char relkind; /* object kind */
99 : RelFileNumber relfilenumber; /* object filenode */
100 : Oid toast_oid; /* toast table OID */
101 : RelFileNumber toast_relfilenumber; /* toast table filenode */
102 : Oid toast_index_oid; /* toast table index OID */
103 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
104 : } BinaryUpgradeClassOidItem;
105 :
106 : /* sequence types */
107 : typedef enum SeqType
108 : {
109 : SEQTYPE_SMALLINT,
110 : SEQTYPE_INTEGER,
111 : SEQTYPE_BIGINT,
112 : } SeqType;
113 :
114 : static const char *const SeqTypeNames[] =
115 : {
116 : [SEQTYPE_SMALLINT] = "smallint",
117 : [SEQTYPE_INTEGER] = "integer",
118 : [SEQTYPE_BIGINT] = "bigint",
119 : };
120 :
121 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
122 : "array length mismatch");
123 :
124 : typedef struct
125 : {
126 : Oid oid; /* sequence OID */
127 : SeqType seqtype; /* data type of sequence */
128 : bool cycled; /* whether sequence cycles */
129 : int64 minv; /* minimum value */
130 : int64 maxv; /* maximum value */
131 : int64 startv; /* start value */
132 : int64 incby; /* increment value */
133 : int64 cache; /* cache size */
134 : int64 last_value; /* last value of sequence */
135 : bool is_called; /* whether nextval advances before returning */
136 : } SequenceItem;
137 :
138 : typedef enum OidOptions
139 : {
140 : zeroIsError = 1,
141 : zeroAsStar = 2,
142 : zeroAsNone = 4,
143 : } OidOptions;
144 :
145 : /* global decls */
146 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
147 :
148 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
149 :
150 : /* The specified names/patterns should to match at least one entity */
151 : static int strict_names = 0;
152 :
153 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
154 :
155 : /*
156 : * Object inclusion/exclusion lists
157 : *
158 : * The string lists record the patterns given by command-line switches,
159 : * which we then convert to lists of OIDs of matching objects.
160 : */
161 : static SimpleStringList schema_include_patterns = {NULL, NULL};
162 : static SimpleOidList schema_include_oids = {NULL, NULL};
163 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
164 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
165 :
166 : static SimpleStringList table_include_patterns = {NULL, NULL};
167 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
168 : static SimpleOidList table_include_oids = {NULL, NULL};
169 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
170 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
171 : static SimpleOidList table_exclude_oids = {NULL, NULL};
172 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
173 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
174 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
175 :
176 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
177 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
178 :
179 : static SimpleStringList extension_include_patterns = {NULL, NULL};
180 : static SimpleOidList extension_include_oids = {NULL, NULL};
181 :
182 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
183 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
184 :
185 : static const CatalogId nilCatalogId = {0, 0};
186 :
187 : /* override for standard extra_float_digits setting */
188 : static bool have_extra_float_digits = false;
189 : static int extra_float_digits;
190 :
191 : /* sorted table of role names */
192 : static RoleNameItem *rolenames = NULL;
193 : static int nrolenames = 0;
194 :
195 : /* sorted table of comments */
196 : static CommentItem *comments = NULL;
197 : static int ncomments = 0;
198 :
199 : /* sorted table of security labels */
200 : static SecLabelItem *seclabels = NULL;
201 : static int nseclabels = 0;
202 :
203 : /* sorted table of pg_class information for binary upgrade */
204 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
205 : static int nbinaryUpgradeClassOids = 0;
206 :
207 : /* sorted table of sequences */
208 : static SequenceItem *sequences = NULL;
209 : static int nsequences = 0;
210 :
211 : /*
212 : * The default number of rows per INSERT when
213 : * --inserts is specified without --rows-per-insert
214 : */
215 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
216 :
217 : /*
218 : * Maximum number of large objects to group into a single ArchiveEntry.
219 : * At some point we might want to make this user-controllable, but for now
220 : * a hard-wired setting will suffice.
221 : */
222 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
223 :
224 : /*
225 : * Macro for producing quoted, schema-qualified name of a dumpable object.
226 : */
227 : #define fmtQualifiedDumpable(obj) \
228 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
229 : (obj)->dobj.name)
230 :
231 : static void help(const char *progname);
232 : static void setup_connection(Archive *AH,
233 : const char *dumpencoding, const char *dumpsnapshot,
234 : char *use_role);
235 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
236 : static void expand_schema_name_patterns(Archive *fout,
237 : SimpleStringList *patterns,
238 : SimpleOidList *oids,
239 : bool strict_names);
240 : static void expand_extension_name_patterns(Archive *fout,
241 : SimpleStringList *patterns,
242 : SimpleOidList *oids,
243 : bool strict_names);
244 : static void expand_foreign_server_name_patterns(Archive *fout,
245 : SimpleStringList *patterns,
246 : SimpleOidList *oids);
247 : static void expand_table_name_patterns(Archive *fout,
248 : SimpleStringList *patterns,
249 : SimpleOidList *oids,
250 : bool strict_names,
251 : bool with_child_tables);
252 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
253 : const char *pattern);
254 :
255 : static NamespaceInfo *findNamespace(Oid nsoid);
256 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
257 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
258 : static const char *getRoleName(const char *roleoid_str);
259 : static void collectRoleNames(Archive *fout);
260 : static void getAdditionalACLs(Archive *fout);
261 : static void dumpCommentExtended(Archive *fout, const char *type,
262 : const char *name, const char *namespace,
263 : const char *owner, CatalogId catalogId,
264 : int subid, DumpId dumpId,
265 : const char *initdb_comment);
266 : static inline void dumpComment(Archive *fout, const char *type,
267 : const char *name, const char *namespace,
268 : const char *owner, CatalogId catalogId,
269 : int subid, DumpId dumpId);
270 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
271 : static void collectComments(Archive *fout);
272 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
273 : const char *namespace, const char *owner,
274 : CatalogId catalogId, int subid, DumpId dumpId);
275 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
276 : static void collectSecLabels(Archive *fout);
277 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
278 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
279 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
280 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
281 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
282 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
283 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
284 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
285 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
286 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
287 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
288 : PGresult *res);
289 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
290 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
291 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
292 : static void dumpCast(Archive *fout, const CastInfo *cast);
293 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
294 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
295 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
296 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
297 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
298 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
299 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
300 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
301 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
302 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
303 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
304 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
305 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
306 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
307 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
308 : static void collectSequences(Archive *fout);
309 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
310 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
311 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
312 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
313 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
314 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
315 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
316 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
317 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
318 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
319 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
320 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
321 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
322 : static void dumpUserMappings(Archive *fout,
323 : const char *servername, const char *namespace,
324 : const char *owner, CatalogId catalogId, DumpId dumpId);
325 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
326 :
327 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
328 : const char *type, const char *name, const char *subname,
329 : const char *nspname, const char *tag, const char *owner,
330 : const DumpableAcl *dacl);
331 :
332 : static void getDependencies(Archive *fout);
333 : static void BuildArchiveDependencies(Archive *fout);
334 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
335 : DumpId **dependencies, int *nDeps, int *allocDeps);
336 :
337 : static DumpableObject *createBoundaryObjects(void);
338 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
339 : DumpableObject *boundaryObjs);
340 :
341 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
342 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
343 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
344 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
345 : static void buildMatViewRefreshDependencies(Archive *fout);
346 : static void getTableDataFKConstraints(void);
347 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
348 : TableInfo *tbinfo, int j,
349 : int i_notnull_name, int i_notnull_noinherit,
350 : int i_notnull_islocal);
351 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
352 : bool is_agg);
353 : static char *format_function_signature(Archive *fout,
354 : const FuncInfo *finfo, bool honor_quotes);
355 : static char *convertRegProcReference(const char *proc);
356 : static char *getFormattedOperatorName(const char *oproid);
357 : static char *convertTSFunction(Archive *fout, Oid funcOid);
358 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
359 : static void getLOs(Archive *fout);
360 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
361 : static int dumpLOs(Archive *fout, const void *arg);
362 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
363 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
364 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
365 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
366 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
367 : static void dumpDatabase(Archive *fout);
368 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
369 : const char *dbname, Oid dboid);
370 : static void dumpEncoding(Archive *AH);
371 : static void dumpStdStrings(Archive *AH);
372 : static void dumpSearchPath(Archive *AH);
373 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
374 : PQExpBuffer upgrade_buffer,
375 : Oid pg_type_oid,
376 : bool force_array_type,
377 : bool include_multirange_type);
378 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
379 : PQExpBuffer upgrade_buffer,
380 : const TableInfo *tbinfo);
381 : static void collectBinaryUpgradeClassOids(Archive *fout);
382 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
383 : PQExpBuffer upgrade_buffer,
384 : Oid pg_class_oid);
385 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
386 : const DumpableObject *dobj,
387 : const char *objtype,
388 : const char *objname,
389 : const char *objnamespace);
390 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
391 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
392 : static bool nonemptyReloptions(const char *reloptions);
393 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
394 : const char *prefix, Archive *fout);
395 : static char *get_synchronized_snapshot(Archive *fout);
396 : static void set_restrict_relation_kind(Archive *AH, const char *value);
397 : static void setupDumpWorker(Archive *AH);
398 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
399 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
400 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
401 :
402 :
403 : int
404 508 : main(int argc, char **argv)
405 : {
406 : int c;
407 508 : const char *filename = NULL;
408 508 : const char *format = "p";
409 : TableInfo *tblinfo;
410 : int numTables;
411 : DumpableObject **dobjs;
412 : int numObjs;
413 : DumpableObject *boundaryObjs;
414 : int i;
415 : int optindex;
416 : RestoreOptions *ropt;
417 : Archive *fout; /* the script file */
418 508 : bool g_verbose = false;
419 508 : const char *dumpencoding = NULL;
420 508 : const char *dumpsnapshot = NULL;
421 508 : char *use_role = NULL;
422 508 : int numWorkers = 1;
423 508 : int plainText = 0;
424 508 : ArchiveFormat archiveFormat = archUnknown;
425 : ArchiveMode archiveMode;
426 508 : pg_compress_specification compression_spec = {0};
427 508 : char *compression_detail = NULL;
428 508 : char *compression_algorithm_str = "none";
429 508 : char *error_detail = NULL;
430 508 : bool user_compression_defined = false;
431 508 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
432 508 : bool data_only = false;
433 508 : bool schema_only = false;
434 508 : bool statistics_only = false;
435 508 : bool no_data = false;
436 508 : bool no_schema = false;
437 508 : bool no_statistics = false;
438 :
439 : static DumpOptions dopt;
440 :
441 : static struct option long_options[] = {
442 : {"data-only", no_argument, NULL, 'a'},
443 : {"blobs", no_argument, NULL, 'b'},
444 : {"large-objects", no_argument, NULL, 'b'},
445 : {"no-blobs", no_argument, NULL, 'B'},
446 : {"no-large-objects", no_argument, NULL, 'B'},
447 : {"clean", no_argument, NULL, 'c'},
448 : {"create", no_argument, NULL, 'C'},
449 : {"dbname", required_argument, NULL, 'd'},
450 : {"extension", required_argument, NULL, 'e'},
451 : {"file", required_argument, NULL, 'f'},
452 : {"format", required_argument, NULL, 'F'},
453 : {"host", required_argument, NULL, 'h'},
454 : {"jobs", 1, NULL, 'j'},
455 : {"no-reconnect", no_argument, NULL, 'R'},
456 : {"no-owner", no_argument, NULL, 'O'},
457 : {"port", required_argument, NULL, 'p'},
458 : {"schema", required_argument, NULL, 'n'},
459 : {"exclude-schema", required_argument, NULL, 'N'},
460 : {"schema-only", no_argument, NULL, 's'},
461 : {"superuser", required_argument, NULL, 'S'},
462 : {"table", required_argument, NULL, 't'},
463 : {"exclude-table", required_argument, NULL, 'T'},
464 : {"no-password", no_argument, NULL, 'w'},
465 : {"password", no_argument, NULL, 'W'},
466 : {"username", required_argument, NULL, 'U'},
467 : {"verbose", no_argument, NULL, 'v'},
468 : {"no-privileges", no_argument, NULL, 'x'},
469 : {"no-acl", no_argument, NULL, 'x'},
470 : {"compress", required_argument, NULL, 'Z'},
471 : {"encoding", required_argument, NULL, 'E'},
472 : {"help", no_argument, NULL, '?'},
473 : {"version", no_argument, NULL, 'V'},
474 :
475 : /*
476 : * the following options don't have an equivalent short option letter
477 : */
478 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
479 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
480 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
481 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
482 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
483 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
484 : {"exclude-table-data", required_argument, NULL, 4},
485 : {"extra-float-digits", required_argument, NULL, 8},
486 : {"if-exists", no_argument, &dopt.if_exists, 1},
487 : {"inserts", no_argument, NULL, 9},
488 : {"lock-wait-timeout", required_argument, NULL, 2},
489 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
490 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
491 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
492 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
493 : {"role", required_argument, NULL, 3},
494 : {"section", required_argument, NULL, 5},
495 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
496 : {"snapshot", required_argument, NULL, 6},
497 : {"statistics-only", no_argument, NULL, 18},
498 : {"strict-names", no_argument, &strict_names, 1},
499 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
500 : {"no-comments", no_argument, &dopt.no_comments, 1},
501 : {"no-data", no_argument, NULL, 19},
502 : {"no-publications", no_argument, &dopt.no_publications, 1},
503 : {"no-schema", no_argument, NULL, 20},
504 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
505 : {"no-statistics", no_argument, NULL, 21},
506 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
507 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
508 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
509 : {"no-sync", no_argument, NULL, 7},
510 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
511 : {"rows-per-insert", required_argument, NULL, 10},
512 : {"include-foreign-data", required_argument, NULL, 11},
513 : {"table-and-children", required_argument, NULL, 12},
514 : {"exclude-table-and-children", required_argument, NULL, 13},
515 : {"exclude-table-data-and-children", required_argument, NULL, 14},
516 : {"sync-method", required_argument, NULL, 15},
517 : {"filter", required_argument, NULL, 16},
518 : {"exclude-extension", required_argument, NULL, 17},
519 :
520 : {NULL, 0, NULL, 0}
521 : };
522 :
523 508 : pg_logging_init(argv[0]);
524 508 : pg_logging_set_level(PG_LOG_WARNING);
525 508 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
526 :
527 : /*
528 : * Initialize what we need for parallel execution, especially for thread
529 : * support on Windows.
530 : */
531 508 : init_parallel_dump_utils();
532 :
533 508 : progname = get_progname(argv[0]);
534 :
535 508 : if (argc > 1)
536 : {
537 508 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
538 : {
539 2 : help(progname);
540 2 : exit_nicely(0);
541 : }
542 506 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
543 : {
544 98 : puts("pg_dump (PostgreSQL) " PG_VERSION);
545 98 : exit_nicely(0);
546 : }
547 : }
548 :
549 408 : InitDumpOptions(&dopt);
550 :
551 1816 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
552 : long_options, &optindex)) != -1)
553 : {
554 1424 : switch (c)
555 : {
556 18 : case 'a': /* Dump data only */
557 18 : data_only = true;
558 18 : break;
559 :
560 2 : case 'b': /* Dump LOs */
561 2 : dopt.outputLOs = true;
562 2 : break;
563 :
564 4 : case 'B': /* Don't dump LOs */
565 4 : dopt.dontOutputLOs = true;
566 4 : break;
567 :
568 12 : case 'c': /* clean (i.e., drop) schema prior to create */
569 12 : dopt.outputClean = 1;
570 12 : break;
571 :
572 58 : case 'C': /* Create DB */
573 58 : dopt.outputCreateDB = 1;
574 58 : break;
575 :
576 10 : case 'd': /* database name */
577 10 : dopt.cparams.dbname = pg_strdup(optarg);
578 10 : break;
579 :
580 8 : case 'e': /* include extension(s) */
581 8 : simple_string_list_append(&extension_include_patterns, optarg);
582 8 : dopt.include_everything = false;
583 8 : break;
584 :
585 4 : case 'E': /* Dump encoding */
586 4 : dumpencoding = pg_strdup(optarg);
587 4 : break;
588 :
589 318 : case 'f':
590 318 : filename = pg_strdup(optarg);
591 318 : break;
592 :
593 174 : case 'F':
594 174 : format = pg_strdup(optarg);
595 174 : break;
596 :
597 28 : case 'h': /* server host */
598 28 : dopt.cparams.pghost = pg_strdup(optarg);
599 28 : break;
600 :
601 22 : case 'j': /* number of dump jobs */
602 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
603 : PG_MAX_JOBS,
604 : &numWorkers))
605 2 : exit_nicely(1);
606 20 : break;
607 :
608 34 : case 'n': /* include schema(s) */
609 34 : simple_string_list_append(&schema_include_patterns, optarg);
610 34 : dopt.include_everything = false;
611 34 : break;
612 :
613 2 : case 'N': /* exclude schema(s) */
614 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
615 2 : break;
616 :
617 4 : case 'O': /* Don't reconnect to match owner */
618 4 : dopt.outputNoOwner = 1;
619 4 : break;
620 :
621 104 : case 'p': /* server port */
622 104 : dopt.cparams.pgport = pg_strdup(optarg);
623 104 : break;
624 :
625 4 : case 'R':
626 : /* no-op, still accepted for backwards compatibility */
627 4 : break;
628 :
629 12 : case 's': /* dump schema only */
630 12 : schema_only = true;
631 12 : break;
632 :
633 2 : case 'S': /* Username for superuser in plain text output */
634 2 : dopt.outputSuperuser = pg_strdup(optarg);
635 2 : break;
636 :
637 16 : case 't': /* include table(s) */
638 16 : simple_string_list_append(&table_include_patterns, optarg);
639 16 : dopt.include_everything = false;
640 16 : break;
641 :
642 8 : case 'T': /* exclude table(s) */
643 8 : simple_string_list_append(&table_exclude_patterns, optarg);
644 8 : break;
645 :
646 32 : case 'U':
647 32 : dopt.cparams.username = pg_strdup(optarg);
648 32 : break;
649 :
650 12 : case 'v': /* verbose */
651 12 : g_verbose = true;
652 12 : pg_logging_increase_verbosity();
653 12 : break;
654 :
655 2 : case 'w':
656 2 : dopt.cparams.promptPassword = TRI_NO;
657 2 : break;
658 :
659 0 : case 'W':
660 0 : dopt.cparams.promptPassword = TRI_YES;
661 0 : break;
662 :
663 4 : case 'x': /* skip ACL dump */
664 4 : dopt.aclsSkip = true;
665 4 : break;
666 :
667 24 : case 'Z': /* Compression */
668 24 : parse_compress_options(optarg, &compression_algorithm_str,
669 : &compression_detail);
670 24 : user_compression_defined = true;
671 24 : break;
672 :
673 108 : case 0:
674 : /* This covers the long options. */
675 108 : break;
676 :
677 4 : case 2: /* lock-wait-timeout */
678 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
679 4 : break;
680 :
681 6 : case 3: /* SET ROLE */
682 6 : use_role = pg_strdup(optarg);
683 6 : break;
684 :
685 2 : case 4: /* exclude table(s) data */
686 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
687 2 : break;
688 :
689 12 : case 5: /* section */
690 12 : set_dump_section(optarg, &dopt.dumpSections);
691 12 : break;
692 :
693 0 : case 6: /* snapshot */
694 0 : dumpsnapshot = pg_strdup(optarg);
695 0 : break;
696 :
697 234 : case 7: /* no-sync */
698 234 : dosync = false;
699 234 : break;
700 :
701 2 : case 8:
702 2 : have_extra_float_digits = true;
703 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
704 : &extra_float_digits))
705 2 : exit_nicely(1);
706 0 : break;
707 :
708 4 : case 9: /* inserts */
709 :
710 : /*
711 : * dump_inserts also stores --rows-per-insert, careful not to
712 : * overwrite that.
713 : */
714 4 : if (dopt.dump_inserts == 0)
715 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
716 4 : break;
717 :
718 4 : case 10: /* rows per insert */
719 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
720 : &dopt.dump_inserts))
721 2 : exit_nicely(1);
722 2 : break;
723 :
724 8 : case 11: /* include foreign data */
725 8 : simple_string_list_append(&foreign_servers_include_patterns,
726 : optarg);
727 8 : break;
728 :
729 2 : case 12: /* include table(s) and their children */
730 2 : simple_string_list_append(&table_include_patterns_and_children,
731 : optarg);
732 2 : dopt.include_everything = false;
733 2 : break;
734 :
735 2 : case 13: /* exclude table(s) and their children */
736 2 : simple_string_list_append(&table_exclude_patterns_and_children,
737 : optarg);
738 2 : break;
739 :
740 2 : case 14: /* exclude data of table(s) and children */
741 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
742 : optarg);
743 2 : break;
744 :
745 0 : case 15:
746 0 : if (!parse_sync_method(optarg, &sync_method))
747 0 : exit_nicely(1);
748 0 : break;
749 :
750 52 : case 16: /* read object filters from file */
751 52 : read_dump_filters(optarg, &dopt);
752 44 : break;
753 :
754 2 : case 17: /* exclude extension(s) */
755 2 : simple_string_list_append(&extension_exclude_patterns,
756 : optarg);
757 2 : break;
758 :
759 8 : case 18:
760 8 : statistics_only = true;
761 8 : break;
762 :
763 32 : case 19:
764 32 : no_data = true;
765 32 : break;
766 :
767 4 : case 20:
768 4 : no_schema = true;
769 4 : break;
770 :
771 16 : case 21:
772 16 : no_statistics = true;
773 16 : break;
774 :
775 2 : default:
776 : /* getopt_long already emitted a complaint */
777 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
778 2 : exit_nicely(1);
779 : }
780 : }
781 :
782 : /*
783 : * Non-option argument specifies database name as long as it wasn't
784 : * already specified with -d / --dbname
785 : */
786 392 : if (optind < argc && dopt.cparams.dbname == NULL)
787 322 : dopt.cparams.dbname = argv[optind++];
788 :
789 : /* Complain if any arguments remain */
790 392 : if (optind < argc)
791 : {
792 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
793 : argv[optind]);
794 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
795 2 : exit_nicely(1);
796 : }
797 :
798 : /* --column-inserts implies --inserts */
799 390 : if (dopt.column_inserts && dopt.dump_inserts == 0)
800 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
801 :
802 : /*
803 : * Binary upgrade mode implies dumping sequence data even in schema-only
804 : * mode. This is not exposed as a separate option, but kept separate
805 : * internally for clarity.
806 : */
807 390 : if (dopt.binary_upgrade)
808 32 : dopt.sequence_data = 1;
809 :
810 390 : if (data_only && schema_only)
811 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
812 388 : if (schema_only && statistics_only)
813 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
814 386 : if (data_only && statistics_only)
815 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
816 :
817 384 : if (data_only && no_data)
818 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
819 384 : if (schema_only && no_schema)
820 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
821 384 : if (statistics_only && no_statistics)
822 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
823 :
824 382 : if (schema_only && foreign_servers_include_patterns.head != NULL)
825 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
826 :
827 380 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
828 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
829 :
830 378 : if (data_only && dopt.outputClean)
831 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
832 :
833 376 : if (dopt.if_exists && !dopt.outputClean)
834 2 : pg_fatal("option --if-exists requires option -c/--clean");
835 :
836 : /* set derivative flags */
837 374 : dopt.dumpData = data_only || (!schema_only && !statistics_only && !no_data);
838 374 : dopt.dumpSchema = schema_only || (!data_only && !statistics_only && !no_schema);
839 374 : dopt.dumpStatistics = statistics_only || (!data_only && !schema_only && !no_statistics);
840 :
841 : /*
842 : * --inserts are already implied above if --column-inserts or
843 : * --rows-per-insert were specified.
844 : */
845 374 : if (dopt.do_nothing && dopt.dump_inserts == 0)
846 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
847 :
848 : /* Identify archive format to emit */
849 372 : archiveFormat = parseArchiveFormat(format, &archiveMode);
850 :
851 : /* archiveFormat specific setup */
852 370 : if (archiveFormat == archNull)
853 302 : plainText = 1;
854 :
855 : /*
856 : * Custom and directory formats are compressed by default with gzip when
857 : * available, not the others. If gzip is not available, no compression is
858 : * done by default.
859 : */
860 370 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
861 62 : !user_compression_defined)
862 : {
863 : #ifdef HAVE_LIBZ
864 52 : compression_algorithm_str = "gzip";
865 : #else
866 : compression_algorithm_str = "none";
867 : #endif
868 : }
869 :
870 : /*
871 : * Compression options
872 : */
873 370 : if (!parse_compress_algorithm(compression_algorithm_str,
874 : &compression_algorithm))
875 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
876 : compression_algorithm_str);
877 :
878 368 : parse_compress_specification(compression_algorithm, compression_detail,
879 : &compression_spec);
880 368 : error_detail = validate_compress_specification(&compression_spec);
881 368 : if (error_detail != NULL)
882 6 : pg_fatal("invalid compression specification: %s",
883 : error_detail);
884 :
885 362 : error_detail = supports_compression(compression_spec);
886 362 : if (error_detail != NULL)
887 0 : pg_fatal("%s", error_detail);
888 :
889 : /*
890 : * Disable support for zstd workers for now - these are based on
891 : * threading, and it's unclear how it interacts with parallel dumps on
892 : * platforms where that relies on threads too (e.g. Windows).
893 : */
894 362 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
895 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
896 : "workers");
897 :
898 : /*
899 : * If emitting an archive format, we always want to emit a DATABASE item,
900 : * in case --create is specified at pg_restore time.
901 : */
902 362 : if (!plainText)
903 68 : dopt.outputCreateDB = 1;
904 :
905 : /* Parallel backup only in the directory archive format so far */
906 362 : if (archiveFormat != archDirectory && numWorkers > 1)
907 2 : pg_fatal("parallel backup only supported by the directory format");
908 :
909 : /* Open the output file */
910 360 : fout = CreateArchive(filename, archiveFormat, compression_spec,
911 : dosync, archiveMode, setupDumpWorker, sync_method);
912 :
913 : /* Make dump options accessible right away */
914 358 : SetArchiveOptions(fout, &dopt, NULL);
915 :
916 : /* Register the cleanup hook */
917 358 : on_exit_close_archive(fout);
918 :
919 : /* Let the archiver know how noisy to be */
920 358 : fout->verbose = g_verbose;
921 :
922 :
923 : /*
924 : * We allow the server to be back to 9.2, and up to any minor release of
925 : * our own major version. (See also version check in pg_dumpall.c.)
926 : */
927 358 : fout->minRemoteVersion = 90200;
928 358 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
929 :
930 358 : fout->numWorkers = numWorkers;
931 :
932 : /*
933 : * Open the database using the Archiver, so it knows about it. Errors mean
934 : * death.
935 : */
936 358 : ConnectDatabase(fout, &dopt.cparams, false);
937 354 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
938 :
939 : /*
940 : * On hot standbys, never try to dump unlogged table data, since it will
941 : * just throw an error.
942 : */
943 354 : if (fout->isStandby)
944 8 : dopt.no_unlogged_table_data = true;
945 :
946 : /*
947 : * Find the last built-in OID, if needed (prior to 8.1)
948 : *
949 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
950 : */
951 354 : g_last_builtin_oid = FirstNormalObjectId - 1;
952 :
953 354 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
954 :
955 : /* Expand schema selection patterns into OID lists */
956 354 : if (schema_include_patterns.head != NULL)
957 : {
958 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
959 : &schema_include_oids,
960 : strict_names);
961 24 : if (schema_include_oids.head == NULL)
962 2 : pg_fatal("no matching schemas were found");
963 : }
964 340 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
965 : &schema_exclude_oids,
966 : false);
967 : /* non-matching exclusion patterns aren't an error */
968 :
969 : /* Expand table selection patterns into OID lists */
970 340 : expand_table_name_patterns(fout, &table_include_patterns,
971 : &table_include_oids,
972 : strict_names, false);
973 330 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
974 : &table_include_oids,
975 : strict_names, true);
976 330 : if ((table_include_patterns.head != NULL ||
977 308 : table_include_patterns_and_children.head != NULL) &&
978 26 : table_include_oids.head == NULL)
979 4 : pg_fatal("no matching tables were found");
980 :
981 326 : expand_table_name_patterns(fout, &table_exclude_patterns,
982 : &table_exclude_oids,
983 : false, false);
984 326 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
985 : &table_exclude_oids,
986 : false, true);
987 :
988 326 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
989 : &tabledata_exclude_oids,
990 : false, false);
991 326 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
992 : &tabledata_exclude_oids,
993 : false, true);
994 :
995 326 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
996 : &foreign_servers_include_oids);
997 :
998 : /* non-matching exclusion patterns aren't an error */
999 :
1000 : /* Expand extension selection patterns into OID lists */
1001 324 : if (extension_include_patterns.head != NULL)
1002 : {
1003 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1004 : &extension_include_oids,
1005 : strict_names);
1006 10 : if (extension_include_oids.head == NULL)
1007 2 : pg_fatal("no matching extensions were found");
1008 : }
1009 322 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1010 : &extension_exclude_oids,
1011 : false);
1012 : /* non-matching exclusion patterns aren't an error */
1013 :
1014 : /*
1015 : * Dumping LOs is the default for dumps where an inclusion switch is not
1016 : * used (an "include everything" dump). -B can be used to exclude LOs
1017 : * from those dumps. -b can be used to include LOs even when an inclusion
1018 : * switch is used.
1019 : *
1020 : * -s means "schema only" and LOs are data, not schema, so we never
1021 : * include LOs when -s is used.
1022 : */
1023 322 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1024 232 : dopt.outputLOs = true;
1025 :
1026 : /*
1027 : * Collect role names so we can map object owner OIDs to names.
1028 : */
1029 322 : collectRoleNames(fout);
1030 :
1031 : /*
1032 : * Now scan the database and create DumpableObject structs for all the
1033 : * objects we intend to dump.
1034 : */
1035 322 : tblinfo = getSchemaData(fout, &numTables);
1036 :
1037 320 : if (dopt.dumpData)
1038 : {
1039 280 : getTableData(&dopt, tblinfo, numTables, 0);
1040 280 : buildMatViewRefreshDependencies(fout);
1041 280 : if (!dopt.dumpSchema)
1042 14 : getTableDataFKConstraints();
1043 : }
1044 :
1045 320 : if (!dopt.dumpData && dopt.sequence_data)
1046 32 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1047 :
1048 : /*
1049 : * In binary-upgrade mode, we do not have to worry about the actual LO
1050 : * data or the associated metadata that resides in the pg_largeobject and
1051 : * pg_largeobject_metadata tables, respectively.
1052 : *
1053 : * However, we do need to collect LO information as there may be comments
1054 : * or other information on LOs that we do need to dump out.
1055 : */
1056 320 : if (dopt.outputLOs || dopt.binary_upgrade)
1057 264 : getLOs(fout);
1058 :
1059 : /*
1060 : * Collect dependency data to assist in ordering the objects.
1061 : */
1062 320 : getDependencies(fout);
1063 :
1064 : /*
1065 : * Collect ACLs, comments, and security labels, if wanted.
1066 : */
1067 320 : if (!dopt.aclsSkip)
1068 316 : getAdditionalACLs(fout);
1069 320 : if (!dopt.no_comments)
1070 320 : collectComments(fout);
1071 320 : if (!dopt.no_security_labels)
1072 320 : collectSecLabels(fout);
1073 :
1074 : /* For binary upgrade mode, collect required pg_class information. */
1075 320 : if (dopt.binary_upgrade)
1076 32 : collectBinaryUpgradeClassOids(fout);
1077 :
1078 : /* Collect sequence information. */
1079 320 : collectSequences(fout);
1080 :
1081 : /* Lastly, create dummy objects to represent the section boundaries */
1082 320 : boundaryObjs = createBoundaryObjects();
1083 :
1084 : /* Get pointers to all the known DumpableObjects */
1085 320 : getDumpableObjects(&dobjs, &numObjs);
1086 :
1087 : /*
1088 : * Add dummy dependencies to enforce the dump section ordering.
1089 : */
1090 320 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1091 :
1092 : /*
1093 : * Sort the objects into a safe dump order (no forward references).
1094 : *
1095 : * We rely on dependency information to help us determine a safe order, so
1096 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1097 : * ensure that logically identical schemas will dump identically.
1098 : */
1099 320 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1100 :
1101 320 : sortDumpableObjects(dobjs, numObjs,
1102 320 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1103 :
1104 : /*
1105 : * Create archive TOC entries for all the objects to be dumped, in a safe
1106 : * order.
1107 : */
1108 :
1109 : /*
1110 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1111 : */
1112 320 : dumpEncoding(fout);
1113 320 : dumpStdStrings(fout);
1114 320 : dumpSearchPath(fout);
1115 :
1116 : /* The database items are always next, unless we don't want them at all */
1117 320 : if (dopt.outputCreateDB)
1118 124 : dumpDatabase(fout);
1119 :
1120 : /* Now the rearrangeable objects. */
1121 1187560 : for (i = 0; i < numObjs; i++)
1122 1187240 : dumpDumpableObject(fout, dobjs[i]);
1123 :
1124 : /*
1125 : * Set up options info to ensure we dump what we want.
1126 : */
1127 320 : ropt = NewRestoreOptions();
1128 320 : ropt->filename = filename;
1129 :
1130 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1131 320 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1132 320 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1133 320 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1134 320 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1135 320 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1136 320 : ropt->dropSchema = dopt.outputClean;
1137 320 : ropt->dumpData = dopt.dumpData;
1138 320 : ropt->dumpSchema = dopt.dumpSchema;
1139 320 : ropt->dumpStatistics = dopt.dumpStatistics;
1140 320 : ropt->if_exists = dopt.if_exists;
1141 320 : ropt->column_inserts = dopt.column_inserts;
1142 320 : ropt->dumpSections = dopt.dumpSections;
1143 320 : ropt->aclsSkip = dopt.aclsSkip;
1144 320 : ropt->superuser = dopt.outputSuperuser;
1145 320 : ropt->createDB = dopt.outputCreateDB;
1146 320 : ropt->noOwner = dopt.outputNoOwner;
1147 320 : ropt->noTableAm = dopt.outputNoTableAm;
1148 320 : ropt->noTablespace = dopt.outputNoTablespaces;
1149 320 : ropt->disable_triggers = dopt.disable_triggers;
1150 320 : ropt->use_setsessauth = dopt.use_setsessauth;
1151 320 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1152 320 : ropt->dump_inserts = dopt.dump_inserts;
1153 320 : ropt->no_comments = dopt.no_comments;
1154 320 : ropt->no_publications = dopt.no_publications;
1155 320 : ropt->no_security_labels = dopt.no_security_labels;
1156 320 : ropt->no_subscriptions = dopt.no_subscriptions;
1157 320 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1158 320 : ropt->include_everything = dopt.include_everything;
1159 320 : ropt->enable_row_security = dopt.enable_row_security;
1160 320 : ropt->sequence_data = dopt.sequence_data;
1161 320 : ropt->binary_upgrade = dopt.binary_upgrade;
1162 :
1163 320 : ropt->compression_spec = compression_spec;
1164 :
1165 320 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1166 :
1167 320 : SetArchiveOptions(fout, &dopt, ropt);
1168 :
1169 : /* Mark which entries should be output */
1170 320 : ProcessArchiveRestoreOptions(fout);
1171 :
1172 : /*
1173 : * The archive's TOC entries are now marked as to which ones will actually
1174 : * be output, so we can set up their dependency lists properly. This isn't
1175 : * necessary for plain-text output, though.
1176 : */
1177 320 : if (!plainText)
1178 66 : BuildArchiveDependencies(fout);
1179 :
1180 : /*
1181 : * And finally we can do the actual output.
1182 : *
1183 : * Note: for non-plain-text output formats, the output file is written
1184 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1185 : * right now.
1186 : */
1187 320 : if (plainText)
1188 254 : RestoreArchive(fout);
1189 :
1190 318 : CloseArchive(fout);
1191 :
1192 318 : exit_nicely(0);
1193 : }
1194 :
1195 :
1196 : static void
1197 2 : help(const char *progname)
1198 : {
1199 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1200 2 : printf(_("Usage:\n"));
1201 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1202 :
1203 2 : printf(_("\nGeneral options:\n"));
1204 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1205 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1206 : " plain text (default))\n"));
1207 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1208 2 : printf(_(" -v, --verbose verbose mode\n"));
1209 2 : printf(_(" -V, --version output version information, then exit\n"));
1210 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1211 : " compress as specified\n"));
1212 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1213 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1214 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1215 2 : printf(_(" -?, --help show this help, then exit\n"));
1216 :
1217 2 : printf(_("\nOptions controlling the output content:\n"));
1218 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1219 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1220 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1221 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1222 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1223 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1224 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1225 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1226 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1227 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1228 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1229 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1230 : " plain-text format\n"));
1231 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1232 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1233 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1234 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1235 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1236 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1237 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1238 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1239 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1240 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1241 : " access to)\n"));
1242 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1243 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1244 : " do NOT dump the specified table(s), including\n"
1245 : " child and partition tables\n"));
1246 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1247 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1248 : " do NOT dump data for the specified table(s),\n"
1249 : " including child and partition tables\n"));
1250 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1251 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1252 : " based on expressions in FILENAME\n"));
1253 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1254 2 : printf(_(" --include-foreign-data=PATTERN\n"
1255 : " include data of foreign tables on foreign\n"
1256 : " servers matching PATTERN\n"));
1257 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1258 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1259 2 : printf(_(" --no-comments do not dump comment commands\n"));
1260 2 : printf(_(" --no-data do not dump data\n"));
1261 2 : printf(_(" --no-publications do not dump publications\n"));
1262 2 : printf(_(" --no-schema do not dump schema\n"));
1263 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1264 2 : printf(_(" --no-statistics do not dump statistics\n"));
1265 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1266 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1267 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1268 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1269 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1270 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1271 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1272 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1273 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1274 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1275 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1276 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1277 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1278 : " match at least one entity each\n"));
1279 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1280 : " child and partition tables\n"));
1281 2 : printf(_(" --use-set-session-authorization\n"
1282 : " use SET SESSION AUTHORIZATION commands instead of\n"
1283 : " ALTER OWNER commands to set ownership\n"));
1284 :
1285 2 : printf(_("\nConnection options:\n"));
1286 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1287 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1288 2 : printf(_(" -p, --port=PORT database server port number\n"));
1289 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1290 2 : printf(_(" -w, --no-password never prompt for password\n"));
1291 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1292 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1293 :
1294 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1295 : "variable value is used.\n\n"));
1296 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1297 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1298 2 : }
1299 :
1300 : static void
1301 386 : setup_connection(Archive *AH, const char *dumpencoding,
1302 : const char *dumpsnapshot, char *use_role)
1303 : {
1304 386 : DumpOptions *dopt = AH->dopt;
1305 386 : PGconn *conn = GetConnection(AH);
1306 : const char *std_strings;
1307 :
1308 386 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1309 :
1310 : /*
1311 : * Set the client encoding if requested.
1312 : */
1313 386 : if (dumpencoding)
1314 : {
1315 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1316 0 : pg_fatal("invalid client encoding \"%s\" specified",
1317 : dumpencoding);
1318 : }
1319 :
1320 : /*
1321 : * Get the active encoding and the standard_conforming_strings setting, so
1322 : * we know how to escape strings.
1323 : */
1324 386 : AH->encoding = PQclientEncoding(conn);
1325 386 : setFmtEncoding(AH->encoding);
1326 :
1327 386 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1328 386 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1329 :
1330 : /*
1331 : * Set the role if requested. In a parallel dump worker, we'll be passed
1332 : * use_role == NULL, but AH->use_role is already set (if user specified it
1333 : * originally) and we should use that.
1334 : */
1335 386 : if (!use_role && AH->use_role)
1336 4 : use_role = AH->use_role;
1337 :
1338 : /* Set the role if requested */
1339 386 : if (use_role)
1340 : {
1341 10 : PQExpBuffer query = createPQExpBuffer();
1342 :
1343 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1344 10 : ExecuteSqlStatement(AH, query->data);
1345 10 : destroyPQExpBuffer(query);
1346 :
1347 : /* save it for possible later use by parallel workers */
1348 10 : if (!AH->use_role)
1349 6 : AH->use_role = pg_strdup(use_role);
1350 : }
1351 :
1352 : /* Set the datestyle to ISO to ensure the dump's portability */
1353 386 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1354 :
1355 : /* Likewise, avoid using sql_standard intervalstyle */
1356 386 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1357 :
1358 : /*
1359 : * Use an explicitly specified extra_float_digits if it has been provided.
1360 : * Otherwise, set extra_float_digits so that we can dump float data
1361 : * exactly (given correctly implemented float I/O code, anyway).
1362 : */
1363 386 : if (have_extra_float_digits)
1364 : {
1365 0 : PQExpBuffer q = createPQExpBuffer();
1366 :
1367 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1368 : extra_float_digits);
1369 0 : ExecuteSqlStatement(AH, q->data);
1370 0 : destroyPQExpBuffer(q);
1371 : }
1372 : else
1373 386 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1374 :
1375 : /*
1376 : * Disable synchronized scanning, to prevent unpredictable changes in row
1377 : * ordering across a dump and reload.
1378 : */
1379 386 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1380 :
1381 : /*
1382 : * Disable timeouts if supported.
1383 : */
1384 386 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1385 386 : if (AH->remoteVersion >= 90300)
1386 386 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1387 386 : if (AH->remoteVersion >= 90600)
1388 386 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1389 386 : if (AH->remoteVersion >= 170000)
1390 386 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1391 :
1392 : /*
1393 : * Quote all identifiers, if requested.
1394 : */
1395 386 : if (quote_all_identifiers)
1396 28 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1397 :
1398 : /*
1399 : * Adjust row-security mode, if supported.
1400 : */
1401 386 : if (AH->remoteVersion >= 90500)
1402 : {
1403 386 : if (dopt->enable_row_security)
1404 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1405 : else
1406 386 : ExecuteSqlStatement(AH, "SET row_security = off");
1407 : }
1408 :
1409 : /*
1410 : * For security reasons, we restrict the expansion of non-system views and
1411 : * access to foreign tables during the pg_dump process. This restriction
1412 : * is adjusted when dumping foreign table data.
1413 : */
1414 386 : set_restrict_relation_kind(AH, "view, foreign-table");
1415 :
1416 : /*
1417 : * Initialize prepared-query state to "nothing prepared". We do this here
1418 : * so that a parallel dump worker will have its own state.
1419 : */
1420 386 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1421 :
1422 : /*
1423 : * Start transaction-snapshot mode transaction to dump consistent data.
1424 : */
1425 386 : ExecuteSqlStatement(AH, "BEGIN");
1426 :
1427 : /*
1428 : * To support the combination of serializable_deferrable with the jobs
1429 : * option we use REPEATABLE READ for the worker connections that are
1430 : * passed a snapshot. As long as the snapshot is acquired in a
1431 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1432 : * REPEATABLE READ transaction provides the appropriate integrity
1433 : * guarantees. This is a kluge, but safe for back-patching.
1434 : */
1435 386 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1436 0 : ExecuteSqlStatement(AH,
1437 : "SET TRANSACTION ISOLATION LEVEL "
1438 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1439 : else
1440 386 : ExecuteSqlStatement(AH,
1441 : "SET TRANSACTION ISOLATION LEVEL "
1442 : "REPEATABLE READ, READ ONLY");
1443 :
1444 : /*
1445 : * If user specified a snapshot to use, select that. In a parallel dump
1446 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1447 : * is already set (if the server can handle it) and we should use that.
1448 : */
1449 386 : if (dumpsnapshot)
1450 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1451 :
1452 386 : if (AH->sync_snapshot_id)
1453 : {
1454 32 : PQExpBuffer query = createPQExpBuffer();
1455 :
1456 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1457 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1458 32 : ExecuteSqlStatement(AH, query->data);
1459 32 : destroyPQExpBuffer(query);
1460 : }
1461 354 : else if (AH->numWorkers > 1)
1462 : {
1463 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1464 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1465 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1466 : }
1467 386 : }
1468 :
1469 : /* Set up connection for a parallel worker process */
1470 : static void
1471 32 : setupDumpWorker(Archive *AH)
1472 : {
1473 : /*
1474 : * We want to re-select all the same values the leader connection is
1475 : * using. We'll have inherited directly-usable values in
1476 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1477 : * inherited encoding value back to a string to pass to setup_connection.
1478 : */
1479 32 : setup_connection(AH,
1480 : pg_encoding_to_char(AH->encoding),
1481 : NULL,
1482 : NULL);
1483 32 : }
1484 :
1485 : static char *
1486 16 : get_synchronized_snapshot(Archive *fout)
1487 : {
1488 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1489 : char *result;
1490 : PGresult *res;
1491 :
1492 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1493 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1494 16 : PQclear(res);
1495 :
1496 16 : return result;
1497 : }
1498 :
1499 : static ArchiveFormat
1500 372 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1501 : {
1502 : ArchiveFormat archiveFormat;
1503 :
1504 372 : *mode = archModeWrite;
1505 :
1506 372 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1507 : {
1508 : /* This is used by pg_dumpall, and is not documented */
1509 86 : archiveFormat = archNull;
1510 86 : *mode = archModeAppend;
1511 : }
1512 286 : else if (pg_strcasecmp(format, "c") == 0)
1513 2 : archiveFormat = archCustom;
1514 284 : else if (pg_strcasecmp(format, "custom") == 0)
1515 40 : archiveFormat = archCustom;
1516 244 : else if (pg_strcasecmp(format, "d") == 0)
1517 4 : archiveFormat = archDirectory;
1518 240 : else if (pg_strcasecmp(format, "directory") == 0)
1519 16 : archiveFormat = archDirectory;
1520 224 : else if (pg_strcasecmp(format, "p") == 0)
1521 210 : archiveFormat = archNull;
1522 14 : else if (pg_strcasecmp(format, "plain") == 0)
1523 6 : archiveFormat = archNull;
1524 8 : else if (pg_strcasecmp(format, "t") == 0)
1525 2 : archiveFormat = archTar;
1526 6 : else if (pg_strcasecmp(format, "tar") == 0)
1527 4 : archiveFormat = archTar;
1528 : else
1529 2 : pg_fatal("invalid output format \"%s\" specified", format);
1530 370 : return archiveFormat;
1531 : }
1532 :
1533 : /*
1534 : * Find the OIDs of all schemas matching the given list of patterns,
1535 : * and append them to the given OID list.
1536 : */
1537 : static void
1538 376 : expand_schema_name_patterns(Archive *fout,
1539 : SimpleStringList *patterns,
1540 : SimpleOidList *oids,
1541 : bool strict_names)
1542 : {
1543 : PQExpBuffer query;
1544 : PGresult *res;
1545 : SimpleStringListCell *cell;
1546 : int i;
1547 :
1548 376 : if (patterns->head == NULL)
1549 334 : return; /* nothing to do */
1550 :
1551 42 : query = createPQExpBuffer();
1552 :
1553 : /*
1554 : * The loop below runs multiple SELECTs might sometimes result in
1555 : * duplicate entries in the OID list, but we don't care.
1556 : */
1557 :
1558 72 : for (cell = patterns->head; cell; cell = cell->next)
1559 : {
1560 : PQExpBufferData dbbuf;
1561 : int dotcnt;
1562 :
1563 42 : appendPQExpBufferStr(query,
1564 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1565 42 : initPQExpBuffer(&dbbuf);
1566 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1567 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1568 : &dotcnt);
1569 42 : if (dotcnt > 1)
1570 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1571 : cell->val);
1572 38 : else if (dotcnt == 1)
1573 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1574 32 : termPQExpBuffer(&dbbuf);
1575 :
1576 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1577 32 : if (strict_names && PQntuples(res) == 0)
1578 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1579 :
1580 58 : for (i = 0; i < PQntuples(res); i++)
1581 : {
1582 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1583 : }
1584 :
1585 30 : PQclear(res);
1586 30 : resetPQExpBuffer(query);
1587 : }
1588 :
1589 30 : destroyPQExpBuffer(query);
1590 : }
1591 :
1592 : /*
1593 : * Find the OIDs of all extensions matching the given list of patterns,
1594 : * and append them to the given OID list.
1595 : */
1596 : static void
1597 332 : expand_extension_name_patterns(Archive *fout,
1598 : SimpleStringList *patterns,
1599 : SimpleOidList *oids,
1600 : bool strict_names)
1601 : {
1602 : PQExpBuffer query;
1603 : PGresult *res;
1604 : SimpleStringListCell *cell;
1605 : int i;
1606 :
1607 332 : if (patterns->head == NULL)
1608 318 : return; /* nothing to do */
1609 :
1610 14 : query = createPQExpBuffer();
1611 :
1612 : /*
1613 : * The loop below runs multiple SELECTs might sometimes result in
1614 : * duplicate entries in the OID list, but we don't care.
1615 : */
1616 28 : for (cell = patterns->head; cell; cell = cell->next)
1617 : {
1618 : int dotcnt;
1619 :
1620 14 : appendPQExpBufferStr(query,
1621 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1622 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1623 : false, NULL, "e.extname", NULL, NULL, NULL,
1624 : &dotcnt);
1625 14 : if (dotcnt > 0)
1626 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1627 : cell->val);
1628 :
1629 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1630 14 : if (strict_names && PQntuples(res) == 0)
1631 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1632 :
1633 26 : for (i = 0; i < PQntuples(res); i++)
1634 : {
1635 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1636 : }
1637 :
1638 14 : PQclear(res);
1639 14 : resetPQExpBuffer(query);
1640 : }
1641 :
1642 14 : destroyPQExpBuffer(query);
1643 : }
1644 :
1645 : /*
1646 : * Find the OIDs of all foreign servers matching the given list of patterns,
1647 : * and append them to the given OID list.
1648 : */
1649 : static void
1650 326 : expand_foreign_server_name_patterns(Archive *fout,
1651 : SimpleStringList *patterns,
1652 : SimpleOidList *oids)
1653 : {
1654 : PQExpBuffer query;
1655 : PGresult *res;
1656 : SimpleStringListCell *cell;
1657 : int i;
1658 :
1659 326 : if (patterns->head == NULL)
1660 320 : return; /* nothing to do */
1661 :
1662 6 : query = createPQExpBuffer();
1663 :
1664 : /*
1665 : * The loop below runs multiple SELECTs might sometimes result in
1666 : * duplicate entries in the OID list, but we don't care.
1667 : */
1668 :
1669 10 : for (cell = patterns->head; cell; cell = cell->next)
1670 : {
1671 : int dotcnt;
1672 :
1673 6 : appendPQExpBufferStr(query,
1674 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1675 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1676 : false, NULL, "s.srvname", NULL, NULL, NULL,
1677 : &dotcnt);
1678 6 : if (dotcnt > 0)
1679 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1680 : cell->val);
1681 :
1682 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1683 6 : if (PQntuples(res) == 0)
1684 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1685 :
1686 8 : for (i = 0; i < PQntuples(res); i++)
1687 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1688 :
1689 4 : PQclear(res);
1690 4 : resetPQExpBuffer(query);
1691 : }
1692 :
1693 4 : destroyPQExpBuffer(query);
1694 : }
1695 :
1696 : /*
1697 : * Find the OIDs of all tables matching the given list of patterns,
1698 : * and append them to the given OID list. See also expand_dbname_patterns()
1699 : * in pg_dumpall.c
1700 : */
1701 : static void
1702 1974 : expand_table_name_patterns(Archive *fout,
1703 : SimpleStringList *patterns, SimpleOidList *oids,
1704 : bool strict_names, bool with_child_tables)
1705 : {
1706 : PQExpBuffer query;
1707 : PGresult *res;
1708 : SimpleStringListCell *cell;
1709 : int i;
1710 :
1711 1974 : if (patterns->head == NULL)
1712 1916 : return; /* nothing to do */
1713 :
1714 58 : query = createPQExpBuffer();
1715 :
1716 : /*
1717 : * this might sometimes result in duplicate entries in the OID list, but
1718 : * we don't care.
1719 : */
1720 :
1721 118 : for (cell = patterns->head; cell; cell = cell->next)
1722 : {
1723 : PQExpBufferData dbbuf;
1724 : int dotcnt;
1725 :
1726 : /*
1727 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1728 : * would be unnecessary given a pg_table_is_visible() variant taking a
1729 : * search_path argument.
1730 : *
1731 : * For with_child_tables, we start with the basic query's results and
1732 : * recursively search the inheritance tree to add child tables.
1733 : */
1734 70 : if (with_child_tables)
1735 : {
1736 12 : appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1737 : }
1738 :
1739 70 : appendPQExpBuffer(query,
1740 : "SELECT c.oid"
1741 : "\nFROM pg_catalog.pg_class c"
1742 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1743 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1744 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1745 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1746 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1747 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1748 : RELKIND_PARTITIONED_TABLE);
1749 70 : initPQExpBuffer(&dbbuf);
1750 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1751 : false, "n.nspname", "c.relname", NULL,
1752 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1753 : &dotcnt);
1754 70 : if (dotcnt > 2)
1755 2 : pg_fatal("improper relation name (too many dotted names): %s",
1756 : cell->val);
1757 68 : else if (dotcnt == 2)
1758 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1759 64 : termPQExpBuffer(&dbbuf);
1760 :
1761 64 : if (with_child_tables)
1762 : {
1763 12 : appendPQExpBuffer(query, "UNION"
1764 : "\nSELECT i.inhrelid"
1765 : "\nFROM partition_tree p"
1766 : "\n JOIN pg_catalog.pg_inherits i"
1767 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1768 : "\n)"
1769 : "\nSELECT relid FROM partition_tree");
1770 : }
1771 :
1772 64 : ExecuteSqlStatement(fout, "RESET search_path");
1773 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1774 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1775 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1776 64 : if (strict_names && PQntuples(res) == 0)
1777 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1778 :
1779 148 : for (i = 0; i < PQntuples(res); i++)
1780 : {
1781 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1782 : }
1783 :
1784 60 : PQclear(res);
1785 60 : resetPQExpBuffer(query);
1786 : }
1787 :
1788 48 : destroyPQExpBuffer(query);
1789 : }
1790 :
1791 : /*
1792 : * Verifies that the connected database name matches the given database name,
1793 : * and if not, dies with an error about the given pattern.
1794 : *
1795 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1796 : */
1797 : static void
1798 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1799 : {
1800 : const char *db;
1801 :
1802 10 : db = PQdb(conn);
1803 10 : if (db == NULL)
1804 0 : pg_fatal("You are currently not connected to a database.");
1805 :
1806 10 : if (strcmp(db, dbname) != 0)
1807 10 : pg_fatal("cross-database references are not implemented: %s",
1808 : pattern);
1809 0 : }
1810 :
1811 : /*
1812 : * checkExtensionMembership
1813 : * Determine whether object is an extension member, and if so,
1814 : * record an appropriate dependency and set the object's dump flag.
1815 : *
1816 : * It's important to call this for each object that could be an extension
1817 : * member. Generally, we integrate this with determining the object's
1818 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1819 : *
1820 : * Returns true if object is an extension member, else false.
1821 : */
1822 : static bool
1823 999650 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1824 : {
1825 999650 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1826 :
1827 999650 : if (ext == NULL)
1828 998234 : return false;
1829 :
1830 1416 : dobj->ext_member = true;
1831 :
1832 : /* Record dependency so that getDependencies needn't deal with that */
1833 1416 : addObjectDependency(dobj, ext->dobj.dumpId);
1834 :
1835 : /*
1836 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1837 : * dumped. (Any initial ACLs will be removed later, using data from
1838 : * pg_init_privs, so that we'll dump only the delta from the extension's
1839 : * initial setup.)
1840 : *
1841 : * Prior to 9.6, we do not include any extension member components.
1842 : *
1843 : * In binary upgrades, we still dump all components of the members
1844 : * individually, since the idea is to exactly reproduce the database
1845 : * contents rather than replace the extension contents with something
1846 : * different.
1847 : *
1848 : * Note: it might be interesting someday to implement storage and delta
1849 : * dumping of extension members' RLS policies and/or security labels.
1850 : * However there is a pitfall for RLS policies: trying to dump them
1851 : * requires getting a lock on their tables, and the calling user might not
1852 : * have privileges for that. We need no lock to examine a table's ACLs,
1853 : * so the current feature doesn't have a problem of that sort.
1854 : */
1855 1416 : if (fout->dopt->binary_upgrade)
1856 168 : dobj->dump = ext->dobj.dump;
1857 : else
1858 : {
1859 1248 : if (fout->remoteVersion < 90600)
1860 0 : dobj->dump = DUMP_COMPONENT_NONE;
1861 : else
1862 1248 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1863 : }
1864 :
1865 1416 : return true;
1866 : }
1867 :
1868 : /*
1869 : * selectDumpableNamespace: policy-setting subroutine
1870 : * Mark a namespace as to be dumped or not
1871 : */
1872 : static void
1873 2620 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1874 : {
1875 : /*
1876 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1877 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1878 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1879 : */
1880 2620 : nsinfo->create = true;
1881 :
1882 : /*
1883 : * If specific tables are being dumped, do not dump any complete
1884 : * namespaces. If specific namespaces are being dumped, dump just those
1885 : * namespaces. Otherwise, dump all non-system namespaces.
1886 : */
1887 2620 : if (table_include_oids.head != NULL)
1888 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1889 2520 : else if (schema_include_oids.head != NULL)
1890 358 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1891 358 : simple_oid_list_member(&schema_include_oids,
1892 : nsinfo->dobj.catId.oid) ?
1893 358 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1894 2162 : else if (fout->remoteVersion >= 90600 &&
1895 2162 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1896 : {
1897 : /*
1898 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1899 : * they are interesting (and not the original ACLs which were set at
1900 : * initdb time, see pg_init_privs).
1901 : */
1902 278 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1903 : }
1904 1884 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1905 870 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1906 : {
1907 : /* Other system schemas don't get dumped */
1908 1292 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1909 : }
1910 592 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1911 : {
1912 : /*
1913 : * The public schema is a strange beast that sits in a sort of
1914 : * no-mans-land between being a system object and a user object.
1915 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1916 : * a comment and an indication of ownership. If the owner is the
1917 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1918 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1919 : */
1920 270 : nsinfo->create = false;
1921 270 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1922 270 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1923 182 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1924 270 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1925 :
1926 : /*
1927 : * Also, make like it has a comment even if it doesn't; this is so
1928 : * that we'll emit a command to drop the comment, if appropriate.
1929 : * (Without this, we'd not call dumpCommentExtended for it.)
1930 : */
1931 270 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1932 : }
1933 : else
1934 322 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1935 :
1936 : /*
1937 : * In any case, a namespace can be excluded by an exclusion switch
1938 : */
1939 3512 : if (nsinfo->dobj.dump_contains &&
1940 892 : simple_oid_list_member(&schema_exclude_oids,
1941 : nsinfo->dobj.catId.oid))
1942 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1943 :
1944 : /*
1945 : * If the schema belongs to an extension, allow extension membership to
1946 : * override the dump decision for the schema itself. However, this does
1947 : * not change dump_contains, so this won't change what we do with objects
1948 : * within the schema. (If they belong to the extension, they'll get
1949 : * suppressed by it, otherwise not.)
1950 : */
1951 2620 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1952 2620 : }
1953 :
1954 : /*
1955 : * selectDumpableTable: policy-setting subroutine
1956 : * Mark a table as to be dumped or not
1957 : */
1958 : static void
1959 84892 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1960 : {
1961 84892 : if (checkExtensionMembership(&tbinfo->dobj, fout))
1962 450 : return; /* extension membership overrides all else */
1963 :
1964 : /*
1965 : * If specific tables are being dumped, dump just those tables; else, dump
1966 : * according to the parent namespace's dump flag.
1967 : */
1968 84442 : if (table_include_oids.head != NULL)
1969 10128 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1970 : tbinfo->dobj.catId.oid) ?
1971 5064 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1972 : else
1973 79378 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1974 :
1975 : /*
1976 : * In any case, a table can be excluded by an exclusion switch
1977 : */
1978 137234 : if (tbinfo->dobj.dump &&
1979 52792 : simple_oid_list_member(&table_exclude_oids,
1980 : tbinfo->dobj.catId.oid))
1981 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1982 : }
1983 :
1984 : /*
1985 : * selectDumpableType: policy-setting subroutine
1986 : * Mark a type as to be dumped or not
1987 : *
1988 : * If it's a table's rowtype or an autogenerated array type, we also apply a
1989 : * special type code to facilitate sorting into the desired order. (We don't
1990 : * want to consider those to be ordinary types because that would bring tables
1991 : * up into the datatype part of the dump order.) We still set the object's
1992 : * dump flag; that's not going to cause the dummy type to be dumped, but we
1993 : * need it so that casts involving such types will be dumped correctly -- see
1994 : * dumpCast. This means the flag should be set the same as for the underlying
1995 : * object (the table or base type).
1996 : */
1997 : static void
1998 231988 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1999 : {
2000 : /* skip complex types, except for standalone composite types */
2001 231988 : if (OidIsValid(tyinfo->typrelid) &&
2002 83462 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2003 : {
2004 83094 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2005 :
2006 83094 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2007 83094 : if (tytable != NULL)
2008 83094 : tyinfo->dobj.dump = tytable->dobj.dump;
2009 : else
2010 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2011 83094 : return;
2012 : }
2013 :
2014 : /* skip auto-generated array and multirange types */
2015 148894 : if (tyinfo->isArray || tyinfo->isMultirange)
2016 : {
2017 113492 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2018 :
2019 : /*
2020 : * Fall through to set the dump flag; we assume that the subsequent
2021 : * rules will do the same thing as they would for the array's base
2022 : * type or multirange's range type. (We cannot reliably look up the
2023 : * base type here, since getTypes may not have processed it yet.)
2024 : */
2025 : }
2026 :
2027 148894 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2028 300 : return; /* extension membership overrides all else */
2029 :
2030 : /* Dump based on if the contents of the namespace are being dumped */
2031 148594 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2032 : }
2033 :
2034 : /*
2035 : * selectDumpableDefaultACL: policy-setting subroutine
2036 : * Mark a default ACL as to be dumped or not
2037 : *
2038 : * For per-schema default ACLs, dump if the schema is to be dumped.
2039 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2040 : * and aclsSkip are checked separately.
2041 : */
2042 : static void
2043 376 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2044 : {
2045 : /* Default ACLs can't be extension members */
2046 :
2047 376 : if (dinfo->dobj.namespace)
2048 : /* default ACLs are considered part of the namespace */
2049 188 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2050 : else
2051 188 : dinfo->dobj.dump = dopt->include_everything ?
2052 188 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2053 376 : }
2054 :
2055 : /*
2056 : * selectDumpableCast: policy-setting subroutine
2057 : * Mark a cast as to be dumped or not
2058 : *
2059 : * Casts do not belong to any particular namespace (since they haven't got
2060 : * names), nor do they have identifiable owners. To distinguish user-defined
2061 : * casts from built-in ones, we must resort to checking whether the cast's
2062 : * OID is in the range reserved for initdb.
2063 : */
2064 : static void
2065 71538 : selectDumpableCast(CastInfo *cast, Archive *fout)
2066 : {
2067 71538 : if (checkExtensionMembership(&cast->dobj, fout))
2068 0 : return; /* extension membership overrides all else */
2069 :
2070 : /*
2071 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2072 : * support ACLs currently.
2073 : */
2074 71538 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2075 71360 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2076 : else
2077 178 : cast->dobj.dump = fout->dopt->include_everything ?
2078 178 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2079 : }
2080 :
2081 : /*
2082 : * selectDumpableProcLang: policy-setting subroutine
2083 : * Mark a procedural language as to be dumped or not
2084 : *
2085 : * Procedural languages do not belong to any particular namespace. To
2086 : * identify built-in languages, we must resort to checking whether the
2087 : * language's OID is in the range reserved for initdb.
2088 : */
2089 : static void
2090 414 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2091 : {
2092 414 : if (checkExtensionMembership(&plang->dobj, fout))
2093 320 : return; /* extension membership overrides all else */
2094 :
2095 : /*
2096 : * Only include procedural languages when we are dumping everything.
2097 : *
2098 : * For from-initdb procedural languages, only include ACLs, as we do for
2099 : * the pg_catalog namespace. We need this because procedural languages do
2100 : * not live in any namespace.
2101 : */
2102 94 : if (!fout->dopt->include_everything)
2103 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2104 : else
2105 : {
2106 78 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2107 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2108 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2109 : else
2110 78 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2111 : }
2112 : }
2113 :
2114 : /*
2115 : * selectDumpableAccessMethod: policy-setting subroutine
2116 : * Mark an access method as to be dumped or not
2117 : *
2118 : * Access methods do not belong to any particular namespace. To identify
2119 : * built-in access methods, we must resort to checking whether the
2120 : * method's OID is in the range reserved for initdb.
2121 : */
2122 : static void
2123 2492 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2124 : {
2125 2492 : if (checkExtensionMembership(&method->dobj, fout))
2126 50 : return; /* extension membership overrides all else */
2127 :
2128 : /*
2129 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2130 : * they do not support ACLs currently.
2131 : */
2132 2442 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2133 2240 : method->dobj.dump = DUMP_COMPONENT_NONE;
2134 : else
2135 202 : method->dobj.dump = fout->dopt->include_everything ?
2136 202 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2137 : }
2138 :
2139 : /*
2140 : * selectDumpableExtension: policy-setting subroutine
2141 : * Mark an extension as to be dumped or not
2142 : *
2143 : * Built-in extensions should be skipped except for checking ACLs, since we
2144 : * assume those will already be installed in the target database. We identify
2145 : * such extensions by their having OIDs in the range reserved for initdb.
2146 : * We dump all user-added extensions by default. No extensions are dumped
2147 : * if include_everything is false (i.e., a --schema or --table switch was
2148 : * given), except if --extension specifies a list of extensions to dump.
2149 : */
2150 : static void
2151 372 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2152 : {
2153 : /*
2154 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2155 : * change permissions on their member objects, if they wish to, and have
2156 : * those changes preserved.
2157 : */
2158 372 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2159 322 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2160 : else
2161 : {
2162 : /* check if there is a list of extensions to dump */
2163 50 : if (extension_include_oids.head != NULL)
2164 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2165 8 : simple_oid_list_member(&extension_include_oids,
2166 : extinfo->dobj.catId.oid) ?
2167 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2168 : else
2169 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2170 42 : dopt->include_everything ?
2171 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2172 :
2173 : /* check that the extension is not explicitly excluded */
2174 92 : if (extinfo->dobj.dump &&
2175 42 : simple_oid_list_member(&extension_exclude_oids,
2176 : extinfo->dobj.catId.oid))
2177 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2178 : }
2179 372 : }
2180 :
2181 : /*
2182 : * selectDumpablePublicationObject: policy-setting subroutine
2183 : * Mark a publication object as to be dumped or not
2184 : *
2185 : * A publication can have schemas and tables which have schemas, but those are
2186 : * ignored in decision making, because publications are only dumped when we are
2187 : * dumping everything.
2188 : */
2189 : static void
2190 846 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2191 : {
2192 846 : if (checkExtensionMembership(dobj, fout))
2193 0 : return; /* extension membership overrides all else */
2194 :
2195 846 : dobj->dump = fout->dopt->include_everything ?
2196 846 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2197 : }
2198 :
2199 : /*
2200 : * selectDumpableStatisticsObject: policy-setting subroutine
2201 : * Mark an extended statistics object as to be dumped or not
2202 : *
2203 : * We dump an extended statistics object if the schema it's in and the table
2204 : * it's for are being dumped. (This'll need more thought if statistics
2205 : * objects ever support cross-table stats.)
2206 : */
2207 : static void
2208 338 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2209 : {
2210 338 : if (checkExtensionMembership(&sobj->dobj, fout))
2211 0 : return; /* extension membership overrides all else */
2212 :
2213 338 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2214 338 : if (sobj->stattable == NULL ||
2215 338 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2216 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2217 : }
2218 :
2219 : /*
2220 : * selectDumpableObject: policy-setting subroutine
2221 : * Mark a generic dumpable object as to be dumped or not
2222 : *
2223 : * Use this only for object types without a special-case routine above.
2224 : */
2225 : static void
2226 687616 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2227 : {
2228 687616 : if (checkExtensionMembership(dobj, fout))
2229 246 : return; /* extension membership overrides all else */
2230 :
2231 : /*
2232 : * Default policy is to dump if parent namespace is dumpable, or for
2233 : * non-namespace-associated items, dump if we're dumping "everything".
2234 : */
2235 687370 : if (dobj->namespace)
2236 686104 : dobj->dump = dobj->namespace->dobj.dump_contains;
2237 : else
2238 1266 : dobj->dump = fout->dopt->include_everything ?
2239 1266 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2240 : }
2241 :
2242 : /*
2243 : * Dump a table's contents for loading using the COPY command
2244 : * - this routine is called by the Archiver when it wants the table
2245 : * to be dumped.
2246 : */
2247 : static int
2248 7554 : dumpTableData_copy(Archive *fout, const void *dcontext)
2249 : {
2250 7554 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2251 7554 : TableInfo *tbinfo = tdinfo->tdtable;
2252 7554 : const char *classname = tbinfo->dobj.name;
2253 7554 : PQExpBuffer q = createPQExpBuffer();
2254 :
2255 : /*
2256 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2257 : * which uses it already.
2258 : */
2259 7554 : PQExpBuffer clistBuf = createPQExpBuffer();
2260 7554 : PGconn *conn = GetConnection(fout);
2261 : PGresult *res;
2262 : int ret;
2263 : char *copybuf;
2264 : const char *column_list;
2265 :
2266 7554 : pg_log_info("dumping contents of table \"%s.%s\"",
2267 : tbinfo->dobj.namespace->dobj.name, classname);
2268 :
2269 : /*
2270 : * Specify the column list explicitly so that we have no possibility of
2271 : * retrieving data in the wrong column order. (The default column
2272 : * ordering of COPY will not be what we want in certain corner cases
2273 : * involving ADD COLUMN and inheritance.)
2274 : */
2275 7554 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2276 :
2277 : /*
2278 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2279 : * a filter condition was specified. For other cases a simple COPY
2280 : * suffices.
2281 : */
2282 7554 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2283 : {
2284 : /* Temporary allows to access to foreign tables to dump data */
2285 2 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2286 2 : set_restrict_relation_kind(fout, "view");
2287 :
2288 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2289 : /* klugery to get rid of parens in column list */
2290 2 : if (strlen(column_list) > 2)
2291 : {
2292 2 : appendPQExpBufferStr(q, column_list + 1);
2293 2 : q->data[q->len - 1] = ' ';
2294 : }
2295 : else
2296 0 : appendPQExpBufferStr(q, "* ");
2297 :
2298 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2299 2 : fmtQualifiedDumpable(tbinfo),
2300 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2301 : }
2302 : else
2303 : {
2304 7552 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2305 7552 : fmtQualifiedDumpable(tbinfo),
2306 : column_list);
2307 : }
2308 7554 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2309 7552 : PQclear(res);
2310 7552 : destroyPQExpBuffer(clistBuf);
2311 :
2312 : for (;;)
2313 : {
2314 3614714 : ret = PQgetCopyData(conn, ©buf, 0);
2315 :
2316 3614714 : if (ret < 0)
2317 7552 : break; /* done or error */
2318 :
2319 3607162 : if (copybuf)
2320 : {
2321 3607162 : WriteData(fout, copybuf, ret);
2322 3607162 : PQfreemem(copybuf);
2323 : }
2324 :
2325 : /* ----------
2326 : * THROTTLE:
2327 : *
2328 : * There was considerable discussion in late July, 2000 regarding
2329 : * slowing down pg_dump when backing up large tables. Users with both
2330 : * slow & fast (multi-processor) machines experienced performance
2331 : * degradation when doing a backup.
2332 : *
2333 : * Initial attempts based on sleeping for a number of ms for each ms
2334 : * of work were deemed too complex, then a simple 'sleep in each loop'
2335 : * implementation was suggested. The latter failed because the loop
2336 : * was too tight. Finally, the following was implemented:
2337 : *
2338 : * If throttle is non-zero, then
2339 : * See how long since the last sleep.
2340 : * Work out how long to sleep (based on ratio).
2341 : * If sleep is more than 100ms, then
2342 : * sleep
2343 : * reset timer
2344 : * EndIf
2345 : * EndIf
2346 : *
2347 : * where the throttle value was the number of ms to sleep per ms of
2348 : * work. The calculation was done in each loop.
2349 : *
2350 : * Most of the hard work is done in the backend, and this solution
2351 : * still did not work particularly well: on slow machines, the ratio
2352 : * was 50:1, and on medium paced machines, 1:1, and on fast
2353 : * multi-processor machines, it had little or no effect, for reasons
2354 : * that were unclear.
2355 : *
2356 : * Further discussion ensued, and the proposal was dropped.
2357 : *
2358 : * For those people who want this feature, it can be implemented using
2359 : * gettimeofday in each loop, calculating the time since last sleep,
2360 : * multiplying that by the sleep ratio, then if the result is more
2361 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2362 : * function to sleep for a subsecond period ie.
2363 : *
2364 : * select(0, NULL, NULL, NULL, &tvi);
2365 : *
2366 : * This will return after the interval specified in the structure tvi.
2367 : * Finally, call gettimeofday again to save the 'last sleep time'.
2368 : * ----------
2369 : */
2370 : }
2371 7552 : archprintf(fout, "\\.\n\n\n");
2372 :
2373 7552 : if (ret == -2)
2374 : {
2375 : /* copy data transfer failed */
2376 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2377 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2378 0 : pg_log_error_detail("Command was: %s", q->data);
2379 0 : exit_nicely(1);
2380 : }
2381 :
2382 : /* Check command status and return to normal libpq state */
2383 7552 : res = PQgetResult(conn);
2384 7552 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2385 : {
2386 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2387 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2388 0 : pg_log_error_detail("Command was: %s", q->data);
2389 0 : exit_nicely(1);
2390 : }
2391 7552 : PQclear(res);
2392 :
2393 : /* Do this to ensure we've pumped libpq back to idle state */
2394 7552 : if (PQgetResult(conn) != NULL)
2395 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2396 : classname);
2397 :
2398 7552 : destroyPQExpBuffer(q);
2399 :
2400 : /* Revert back the setting */
2401 7552 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2402 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2403 :
2404 7552 : return 1;
2405 : }
2406 :
2407 : /*
2408 : * Dump table data using INSERT commands.
2409 : *
2410 : * Caution: when we restore from an archive file direct to database, the
2411 : * INSERT commands emitted by this function have to be parsed by
2412 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2413 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2414 : */
2415 : static int
2416 142 : dumpTableData_insert(Archive *fout, const void *dcontext)
2417 : {
2418 142 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2419 142 : TableInfo *tbinfo = tdinfo->tdtable;
2420 142 : DumpOptions *dopt = fout->dopt;
2421 142 : PQExpBuffer q = createPQExpBuffer();
2422 142 : PQExpBuffer insertStmt = NULL;
2423 : char *attgenerated;
2424 : PGresult *res;
2425 : int nfields,
2426 : i;
2427 142 : int rows_per_statement = dopt->dump_inserts;
2428 142 : int rows_this_statement = 0;
2429 :
2430 : /* Temporary allows to access to foreign tables to dump data */
2431 142 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2432 0 : set_restrict_relation_kind(fout, "view");
2433 :
2434 : /*
2435 : * If we're going to emit INSERTs with column names, the most efficient
2436 : * way to deal with generated columns is to exclude them entirely. For
2437 : * INSERTs without column names, we have to emit DEFAULT rather than the
2438 : * actual column value --- but we can save a few cycles by fetching nulls
2439 : * rather than the uninteresting-to-us value.
2440 : */
2441 142 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2442 142 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2443 142 : nfields = 0;
2444 466 : for (i = 0; i < tbinfo->numatts; i++)
2445 : {
2446 324 : if (tbinfo->attisdropped[i])
2447 4 : continue;
2448 320 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2449 16 : continue;
2450 304 : if (nfields > 0)
2451 176 : appendPQExpBufferStr(q, ", ");
2452 304 : if (tbinfo->attgenerated[i])
2453 16 : appendPQExpBufferStr(q, "NULL");
2454 : else
2455 288 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2456 304 : attgenerated[nfields] = tbinfo->attgenerated[i];
2457 304 : nfields++;
2458 : }
2459 : /* Servers before 9.4 will complain about zero-column SELECT */
2460 142 : if (nfields == 0)
2461 14 : appendPQExpBufferStr(q, "NULL");
2462 142 : appendPQExpBuffer(q, " FROM ONLY %s",
2463 142 : fmtQualifiedDumpable(tbinfo));
2464 142 : if (tdinfo->filtercond)
2465 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2466 :
2467 142 : ExecuteSqlStatement(fout, q->data);
2468 :
2469 : while (1)
2470 : {
2471 246 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2472 : PGRES_TUPLES_OK);
2473 :
2474 : /* cross-check field count, allowing for dummy NULL if any */
2475 246 : if (nfields != PQnfields(res) &&
2476 20 : !(nfields == 0 && PQnfields(res) == 1))
2477 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2478 : tbinfo->dobj.name);
2479 :
2480 : /*
2481 : * First time through, we build as much of the INSERT statement as
2482 : * possible in "insertStmt", which we can then just print for each
2483 : * statement. If the table happens to have zero dumpable columns then
2484 : * this will be a complete statement, otherwise it will end in
2485 : * "VALUES" and be ready to have the row's column values printed.
2486 : */
2487 246 : if (insertStmt == NULL)
2488 : {
2489 : TableInfo *targettab;
2490 :
2491 142 : insertStmt = createPQExpBuffer();
2492 :
2493 : /*
2494 : * When load-via-partition-root is set or forced, get the root
2495 : * table name for the partition table, so that we can reload data
2496 : * through the root table.
2497 : */
2498 142 : if (tbinfo->ispartition &&
2499 80 : (dopt->load_via_partition_root ||
2500 40 : forcePartitionRootLoad(tbinfo)))
2501 6 : targettab = getRootTableInfo(tbinfo);
2502 : else
2503 136 : targettab = tbinfo;
2504 :
2505 142 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2506 142 : fmtQualifiedDumpable(targettab));
2507 :
2508 : /* corner case for zero-column table */
2509 142 : if (nfields == 0)
2510 : {
2511 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2512 : }
2513 : else
2514 : {
2515 : /* append the list of column names if required */
2516 128 : if (dopt->column_inserts)
2517 : {
2518 56 : appendPQExpBufferChar(insertStmt, '(');
2519 182 : for (int field = 0; field < nfields; field++)
2520 : {
2521 126 : if (field > 0)
2522 70 : appendPQExpBufferStr(insertStmt, ", ");
2523 126 : appendPQExpBufferStr(insertStmt,
2524 126 : fmtId(PQfname(res, field)));
2525 : }
2526 56 : appendPQExpBufferStr(insertStmt, ") ");
2527 : }
2528 :
2529 128 : if (tbinfo->needs_override)
2530 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2531 :
2532 128 : appendPQExpBufferStr(insertStmt, "VALUES");
2533 : }
2534 : }
2535 :
2536 6788 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2537 : {
2538 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2539 6542 : if (rows_this_statement == 0)
2540 6530 : archputs(insertStmt->data, fout);
2541 :
2542 : /*
2543 : * If it is zero-column table then we've already written the
2544 : * complete statement, which will mean we've disobeyed
2545 : * --rows-per-insert when it's set greater than 1. We do support
2546 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2547 : * UNION ALL ... but that's non-standard so we should avoid it
2548 : * given that using INSERTs is mostly only ever needed for
2549 : * cross-database exports.
2550 : */
2551 6542 : if (nfields == 0)
2552 12 : continue;
2553 :
2554 : /* Emit a row heading */
2555 6530 : if (rows_per_statement == 1)
2556 6512 : archputs(" (", fout);
2557 18 : else if (rows_this_statement > 0)
2558 12 : archputs(",\n\t(", fout);
2559 : else
2560 6 : archputs("\n\t(", fout);
2561 :
2562 19698 : for (int field = 0; field < nfields; field++)
2563 : {
2564 13168 : if (field > 0)
2565 6638 : archputs(", ", fout);
2566 13168 : if (attgenerated[field])
2567 : {
2568 4 : archputs("DEFAULT", fout);
2569 4 : continue;
2570 : }
2571 13164 : if (PQgetisnull(res, tuple, field))
2572 : {
2573 166 : archputs("NULL", fout);
2574 166 : continue;
2575 : }
2576 :
2577 : /* XXX This code is partially duplicated in ruleutils.c */
2578 12998 : switch (PQftype(res, field))
2579 : {
2580 8938 : case INT2OID:
2581 : case INT4OID:
2582 : case INT8OID:
2583 : case OIDOID:
2584 : case FLOAT4OID:
2585 : case FLOAT8OID:
2586 : case NUMERICOID:
2587 : {
2588 : /*
2589 : * These types are printed without quotes unless
2590 : * they contain values that aren't accepted by the
2591 : * scanner unquoted (e.g., 'NaN'). Note that
2592 : * strtod() and friends might accept NaN, so we
2593 : * can't use that to test.
2594 : *
2595 : * In reality we only need to defend against
2596 : * infinity and NaN, so we need not get too crazy
2597 : * about pattern matching here.
2598 : */
2599 8938 : const char *s = PQgetvalue(res, tuple, field);
2600 :
2601 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2602 8934 : archputs(s, fout);
2603 : else
2604 4 : archprintf(fout, "'%s'", s);
2605 : }
2606 8938 : break;
2607 :
2608 4 : case BITOID:
2609 : case VARBITOID:
2610 4 : archprintf(fout, "B'%s'",
2611 : PQgetvalue(res, tuple, field));
2612 4 : break;
2613 :
2614 8 : case BOOLOID:
2615 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2616 4 : archputs("true", fout);
2617 : else
2618 4 : archputs("false", fout);
2619 8 : break;
2620 :
2621 4048 : default:
2622 : /* All other types are printed as string literals. */
2623 4048 : resetPQExpBuffer(q);
2624 4048 : appendStringLiteralAH(q,
2625 : PQgetvalue(res, tuple, field),
2626 : fout);
2627 4048 : archputs(q->data, fout);
2628 4048 : break;
2629 : }
2630 : }
2631 :
2632 : /* Terminate the row ... */
2633 6530 : archputs(")", fout);
2634 :
2635 : /* ... and the statement, if the target no. of rows is reached */
2636 6530 : if (++rows_this_statement >= rows_per_statement)
2637 : {
2638 6516 : if (dopt->do_nothing)
2639 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2640 : else
2641 6516 : archputs(";\n", fout);
2642 : /* Reset the row counter */
2643 6516 : rows_this_statement = 0;
2644 : }
2645 : }
2646 :
2647 246 : if (PQntuples(res) <= 0)
2648 : {
2649 142 : PQclear(res);
2650 142 : break;
2651 : }
2652 104 : PQclear(res);
2653 : }
2654 :
2655 : /* Terminate any statements that didn't make the row count. */
2656 142 : if (rows_this_statement > 0)
2657 : {
2658 2 : if (dopt->do_nothing)
2659 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2660 : else
2661 2 : archputs(";\n", fout);
2662 : }
2663 :
2664 142 : archputs("\n\n", fout);
2665 :
2666 142 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2667 :
2668 142 : destroyPQExpBuffer(q);
2669 142 : if (insertStmt != NULL)
2670 142 : destroyPQExpBuffer(insertStmt);
2671 142 : free(attgenerated);
2672 :
2673 : /* Revert back the setting */
2674 142 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2675 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2676 :
2677 142 : return 1;
2678 : }
2679 :
2680 : /*
2681 : * getRootTableInfo:
2682 : * get the root TableInfo for the given partition table.
2683 : */
2684 : static TableInfo *
2685 18 : getRootTableInfo(const TableInfo *tbinfo)
2686 : {
2687 : TableInfo *parentTbinfo;
2688 :
2689 : Assert(tbinfo->ispartition);
2690 : Assert(tbinfo->numParents == 1);
2691 :
2692 18 : parentTbinfo = tbinfo->parents[0];
2693 18 : while (parentTbinfo->ispartition)
2694 : {
2695 : Assert(parentTbinfo->numParents == 1);
2696 0 : parentTbinfo = parentTbinfo->parents[0];
2697 : }
2698 :
2699 18 : return parentTbinfo;
2700 : }
2701 :
2702 : /*
2703 : * forcePartitionRootLoad
2704 : * Check if we must force load_via_partition_root for this partition.
2705 : *
2706 : * This is required if any level of ancestral partitioned table has an
2707 : * unsafe partitioning scheme.
2708 : */
2709 : static bool
2710 1936 : forcePartitionRootLoad(const TableInfo *tbinfo)
2711 : {
2712 : TableInfo *parentTbinfo;
2713 :
2714 : Assert(tbinfo->ispartition);
2715 : Assert(tbinfo->numParents == 1);
2716 :
2717 1936 : parentTbinfo = tbinfo->parents[0];
2718 1936 : if (parentTbinfo->unsafe_partitions)
2719 18 : return true;
2720 2350 : while (parentTbinfo->ispartition)
2721 : {
2722 : Assert(parentTbinfo->numParents == 1);
2723 432 : parentTbinfo = parentTbinfo->parents[0];
2724 432 : if (parentTbinfo->unsafe_partitions)
2725 0 : return true;
2726 : }
2727 :
2728 1918 : return false;
2729 : }
2730 :
2731 : /*
2732 : * dumpTableData -
2733 : * dump the contents of a single table
2734 : *
2735 : * Actually, this just makes an ArchiveEntry for the table contents.
2736 : */
2737 : static void
2738 7836 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2739 : {
2740 7836 : DumpOptions *dopt = fout->dopt;
2741 7836 : TableInfo *tbinfo = tdinfo->tdtable;
2742 7836 : PQExpBuffer copyBuf = createPQExpBuffer();
2743 7836 : PQExpBuffer clistBuf = createPQExpBuffer();
2744 : DataDumperPtr dumpFn;
2745 7836 : char *tdDefn = NULL;
2746 : char *copyStmt;
2747 : const char *copyFrom;
2748 :
2749 : /* We had better have loaded per-column details about this table */
2750 : Assert(tbinfo->interesting);
2751 :
2752 : /*
2753 : * When load-via-partition-root is set or forced, get the root table name
2754 : * for the partition table, so that we can reload data through the root
2755 : * table. Then construct a comment to be inserted into the TOC entry's
2756 : * defn field, so that such cases can be identified reliably.
2757 : */
2758 7836 : if (tbinfo->ispartition &&
2759 3792 : (dopt->load_via_partition_root ||
2760 1896 : forcePartitionRootLoad(tbinfo)))
2761 12 : {
2762 : TableInfo *parentTbinfo;
2763 :
2764 12 : parentTbinfo = getRootTableInfo(tbinfo);
2765 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2766 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2767 : copyFrom);
2768 12 : tdDefn = pg_strdup(copyBuf->data);
2769 : }
2770 : else
2771 7824 : copyFrom = fmtQualifiedDumpable(tbinfo);
2772 :
2773 7836 : if (dopt->dump_inserts == 0)
2774 : {
2775 : /* Dump/restore using COPY */
2776 7694 : dumpFn = dumpTableData_copy;
2777 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2778 7694 : printfPQExpBuffer(copyBuf, "COPY %s ",
2779 : copyFrom);
2780 7694 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2781 : fmtCopyColumnList(tbinfo, clistBuf));
2782 7694 : copyStmt = copyBuf->data;
2783 : }
2784 : else
2785 : {
2786 : /* Restore using INSERT */
2787 142 : dumpFn = dumpTableData_insert;
2788 142 : copyStmt = NULL;
2789 : }
2790 :
2791 : /*
2792 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2793 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2794 : * See comments for BuildArchiveDependencies.
2795 : */
2796 7836 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2797 : {
2798 : TocEntry *te;
2799 :
2800 7836 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2801 7836 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2802 : .namespace = tbinfo->dobj.namespace->dobj.name,
2803 : .owner = tbinfo->rolname,
2804 : .description = "TABLE DATA",
2805 : .section = SECTION_DATA,
2806 : .createStmt = tdDefn,
2807 : .copyStmt = copyStmt,
2808 : .deps = &(tbinfo->dobj.dumpId),
2809 : .nDeps = 1,
2810 : .dumpFn = dumpFn,
2811 : .dumpArg = tdinfo));
2812 :
2813 : /*
2814 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2815 : * and want to order dump jobs by table size. We choose to measure
2816 : * dataLength in table pages (including TOAST pages) during dump, so
2817 : * no scaling is needed.
2818 : *
2819 : * However, relpages is declared as "integer" in pg_class, and hence
2820 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2821 : * Cast so that we get the right interpretation of table sizes
2822 : * exceeding INT_MAX pages.
2823 : */
2824 7836 : te->dataLength = (BlockNumber) tbinfo->relpages;
2825 7836 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2826 :
2827 : /*
2828 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2829 : * and instead we'd better worry about integer overflow. Clamp to
2830 : * INT_MAX if the correct result exceeds that.
2831 : */
2832 : if (sizeof(te->dataLength) == 4 &&
2833 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2834 : te->dataLength < 0))
2835 : te->dataLength = INT_MAX;
2836 : }
2837 :
2838 7836 : destroyPQExpBuffer(copyBuf);
2839 7836 : destroyPQExpBuffer(clistBuf);
2840 7836 : }
2841 :
2842 : /*
2843 : * refreshMatViewData -
2844 : * load or refresh the contents of a single materialized view
2845 : *
2846 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2847 : * statement.
2848 : */
2849 : static void
2850 784 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2851 : {
2852 784 : TableInfo *tbinfo = tdinfo->tdtable;
2853 : PQExpBuffer q;
2854 :
2855 : /* If the materialized view is not flagged as populated, skip this. */
2856 784 : if (!tbinfo->relispopulated)
2857 144 : return;
2858 :
2859 640 : q = createPQExpBuffer();
2860 :
2861 640 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2862 640 : fmtQualifiedDumpable(tbinfo));
2863 :
2864 640 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2865 640 : ArchiveEntry(fout,
2866 : tdinfo->dobj.catId, /* catalog ID */
2867 : tdinfo->dobj.dumpId, /* dump ID */
2868 640 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2869 : .namespace = tbinfo->dobj.namespace->dobj.name,
2870 : .owner = tbinfo->rolname,
2871 : .description = "MATERIALIZED VIEW DATA",
2872 : .section = SECTION_POST_DATA,
2873 : .createStmt = q->data,
2874 : .deps = tdinfo->dobj.dependencies,
2875 : .nDeps = tdinfo->dobj.nDeps));
2876 :
2877 640 : destroyPQExpBuffer(q);
2878 : }
2879 :
2880 : /*
2881 : * getTableData -
2882 : * set up dumpable objects representing the contents of tables
2883 : */
2884 : static void
2885 312 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2886 : {
2887 : int i;
2888 :
2889 83040 : for (i = 0; i < numTables; i++)
2890 : {
2891 82728 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2892 1742 : (!relkind || tblinfo[i].relkind == relkind))
2893 11300 : makeTableDataInfo(dopt, &(tblinfo[i]));
2894 : }
2895 312 : }
2896 :
2897 : /*
2898 : * Make a dumpable object for the data of this specific table
2899 : *
2900 : * Note: we make a TableDataInfo if and only if we are going to dump the
2901 : * table data; the "dump" field in such objects isn't very interesting.
2902 : */
2903 : static void
2904 11378 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2905 : {
2906 : TableDataInfo *tdinfo;
2907 :
2908 : /*
2909 : * Nothing to do if we already decided to dump the table. This will
2910 : * happen for "config" tables.
2911 : */
2912 11378 : if (tbinfo->dataObj != NULL)
2913 2 : return;
2914 :
2915 : /* Skip VIEWs (no data to dump) */
2916 11376 : if (tbinfo->relkind == RELKIND_VIEW)
2917 940 : return;
2918 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2919 10436 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2920 80 : (foreign_servers_include_oids.head == NULL ||
2921 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2922 : tbinfo->foreign_server)))
2923 78 : return;
2924 : /* Skip partitioned tables (data in partitions) */
2925 10358 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2926 896 : return;
2927 :
2928 : /* Don't dump data in unlogged tables, if so requested */
2929 9462 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2930 82 : dopt->no_unlogged_table_data)
2931 36 : return;
2932 :
2933 : /* Check that the data is not explicitly excluded */
2934 9426 : if (simple_oid_list_member(&tabledata_exclude_oids,
2935 : tbinfo->dobj.catId.oid))
2936 16 : return;
2937 :
2938 : /* OK, let's dump it */
2939 9410 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2940 :
2941 9410 : if (tbinfo->relkind == RELKIND_MATVIEW)
2942 784 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2943 8626 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2944 790 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2945 : else
2946 7836 : tdinfo->dobj.objType = DO_TABLE_DATA;
2947 :
2948 : /*
2949 : * Note: use tableoid 0 so that this object won't be mistaken for
2950 : * something that pg_depend entries apply to.
2951 : */
2952 9410 : tdinfo->dobj.catId.tableoid = 0;
2953 9410 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2954 9410 : AssignDumpId(&tdinfo->dobj);
2955 9410 : tdinfo->dobj.name = tbinfo->dobj.name;
2956 9410 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2957 9410 : tdinfo->tdtable = tbinfo;
2958 9410 : tdinfo->filtercond = NULL; /* might get set later */
2959 9410 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2960 :
2961 : /* A TableDataInfo contains data, of course */
2962 9410 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2963 :
2964 9410 : tbinfo->dataObj = tdinfo;
2965 :
2966 : /* Make sure that we'll collect per-column info for this table. */
2967 9410 : tbinfo->interesting = true;
2968 : }
2969 :
2970 : /*
2971 : * The refresh for a materialized view must be dependent on the refresh for
2972 : * any materialized view that this one is dependent on.
2973 : *
2974 : * This must be called after all the objects are created, but before they are
2975 : * sorted.
2976 : */
2977 : static void
2978 280 : buildMatViewRefreshDependencies(Archive *fout)
2979 : {
2980 : PQExpBuffer query;
2981 : PGresult *res;
2982 : int ntups,
2983 : i;
2984 : int i_classid,
2985 : i_objid,
2986 : i_refobjid;
2987 :
2988 : /* No Mat Views before 9.3. */
2989 280 : if (fout->remoteVersion < 90300)
2990 0 : return;
2991 :
2992 280 : query = createPQExpBuffer();
2993 :
2994 280 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2995 : "( "
2996 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2997 : "FROM pg_depend d1 "
2998 : "JOIN pg_class c1 ON c1.oid = d1.objid "
2999 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3000 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3001 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3002 : "AND d2.objid = r1.oid "
3003 : "AND d2.refobjid <> d1.objid "
3004 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3005 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3006 : CppAsString2(RELKIND_VIEW) ") "
3007 : "WHERE d1.classid = 'pg_class'::regclass "
3008 : "UNION "
3009 : "SELECT w.objid, d3.refobjid, c3.relkind "
3010 : "FROM w "
3011 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3012 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3013 : "AND d3.objid = r3.oid "
3014 : "AND d3.refobjid <> w.refobjid "
3015 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3016 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3017 : CppAsString2(RELKIND_VIEW) ") "
3018 : ") "
3019 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3020 : "FROM w "
3021 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3022 :
3023 280 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3024 :
3025 280 : ntups = PQntuples(res);
3026 :
3027 280 : i_classid = PQfnumber(res, "classid");
3028 280 : i_objid = PQfnumber(res, "objid");
3029 280 : i_refobjid = PQfnumber(res, "refobjid");
3030 :
3031 832 : for (i = 0; i < ntups; i++)
3032 : {
3033 : CatalogId objId;
3034 : CatalogId refobjId;
3035 : DumpableObject *dobj;
3036 : DumpableObject *refdobj;
3037 : TableInfo *tbinfo;
3038 : TableInfo *reftbinfo;
3039 :
3040 552 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3041 552 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3042 552 : refobjId.tableoid = objId.tableoid;
3043 552 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3044 :
3045 552 : dobj = findObjectByCatalogId(objId);
3046 552 : if (dobj == NULL)
3047 96 : continue;
3048 :
3049 : Assert(dobj->objType == DO_TABLE);
3050 552 : tbinfo = (TableInfo *) dobj;
3051 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3052 552 : dobj = (DumpableObject *) tbinfo->dataObj;
3053 552 : if (dobj == NULL)
3054 96 : continue;
3055 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3056 :
3057 456 : refdobj = findObjectByCatalogId(refobjId);
3058 456 : if (refdobj == NULL)
3059 0 : continue;
3060 :
3061 : Assert(refdobj->objType == DO_TABLE);
3062 456 : reftbinfo = (TableInfo *) refdobj;
3063 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3064 456 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3065 456 : if (refdobj == NULL)
3066 0 : continue;
3067 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3068 :
3069 456 : addObjectDependency(dobj, refdobj->dumpId);
3070 :
3071 456 : if (!reftbinfo->relispopulated)
3072 72 : tbinfo->relispopulated = false;
3073 : }
3074 :
3075 280 : PQclear(res);
3076 :
3077 280 : destroyPQExpBuffer(query);
3078 : }
3079 :
3080 : /*
3081 : * getTableDataFKConstraints -
3082 : * add dump-order dependencies reflecting foreign key constraints
3083 : *
3084 : * This code is executed only in a data-only dump --- in schema+data dumps
3085 : * we handle foreign key issues by not creating the FK constraints until
3086 : * after the data is loaded. In a data-only dump, however, we want to
3087 : * order the table data objects in such a way that a table's referenced
3088 : * tables are restored first. (In the presence of circular references or
3089 : * self-references this may be impossible; we'll detect and complain about
3090 : * that during the dependency sorting step.)
3091 : */
3092 : static void
3093 14 : getTableDataFKConstraints(void)
3094 : {
3095 : DumpableObject **dobjs;
3096 : int numObjs;
3097 : int i;
3098 :
3099 : /* Search through all the dumpable objects for FK constraints */
3100 14 : getDumpableObjects(&dobjs, &numObjs);
3101 50484 : for (i = 0; i < numObjs; i++)
3102 : {
3103 50470 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3104 : {
3105 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3106 : TableInfo *ftable;
3107 :
3108 : /* Not interesting unless both tables are to be dumped */
3109 16 : if (cinfo->contable == NULL ||
3110 16 : cinfo->contable->dataObj == NULL)
3111 8 : continue;
3112 8 : ftable = findTableByOid(cinfo->confrelid);
3113 8 : if (ftable == NULL ||
3114 8 : ftable->dataObj == NULL)
3115 0 : continue;
3116 :
3117 : /*
3118 : * Okay, make referencing table's TABLE_DATA object depend on the
3119 : * referenced table's TABLE_DATA object.
3120 : */
3121 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3122 8 : ftable->dataObj->dobj.dumpId);
3123 : }
3124 : }
3125 14 : free(dobjs);
3126 14 : }
3127 :
3128 :
3129 : /*
3130 : * dumpDatabase:
3131 : * dump the database definition
3132 : */
3133 : static void
3134 124 : dumpDatabase(Archive *fout)
3135 : {
3136 124 : DumpOptions *dopt = fout->dopt;
3137 124 : PQExpBuffer dbQry = createPQExpBuffer();
3138 124 : PQExpBuffer delQry = createPQExpBuffer();
3139 124 : PQExpBuffer creaQry = createPQExpBuffer();
3140 124 : PQExpBuffer labelq = createPQExpBuffer();
3141 124 : PGconn *conn = GetConnection(fout);
3142 : PGresult *res;
3143 : int i_tableoid,
3144 : i_oid,
3145 : i_datname,
3146 : i_datdba,
3147 : i_encoding,
3148 : i_datlocprovider,
3149 : i_collate,
3150 : i_ctype,
3151 : i_datlocale,
3152 : i_daticurules,
3153 : i_frozenxid,
3154 : i_minmxid,
3155 : i_datacl,
3156 : i_acldefault,
3157 : i_datistemplate,
3158 : i_datconnlimit,
3159 : i_datcollversion,
3160 : i_tablespace;
3161 : CatalogId dbCatId;
3162 : DumpId dbDumpId;
3163 : DumpableAcl dbdacl;
3164 : const char *datname,
3165 : *dba,
3166 : *encoding,
3167 : *datlocprovider,
3168 : *collate,
3169 : *ctype,
3170 : *locale,
3171 : *icurules,
3172 : *datistemplate,
3173 : *datconnlimit,
3174 : *tablespace;
3175 : uint32 frozenxid,
3176 : minmxid;
3177 : char *qdatname;
3178 :
3179 124 : pg_log_info("saving database definition");
3180 :
3181 : /*
3182 : * Fetch the database-level properties for this database.
3183 : */
3184 124 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3185 : "datdba, "
3186 : "pg_encoding_to_char(encoding) AS encoding, "
3187 : "datcollate, datctype, datfrozenxid, "
3188 : "datacl, acldefault('d', datdba) AS acldefault, "
3189 : "datistemplate, datconnlimit, ");
3190 124 : if (fout->remoteVersion >= 90300)
3191 124 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3192 : else
3193 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3194 124 : if (fout->remoteVersion >= 170000)
3195 124 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3196 0 : else if (fout->remoteVersion >= 150000)
3197 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3198 : else
3199 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3200 124 : if (fout->remoteVersion >= 160000)
3201 124 : appendPQExpBufferStr(dbQry, "daticurules, ");
3202 : else
3203 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3204 124 : appendPQExpBufferStr(dbQry,
3205 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3206 : "shobj_description(oid, 'pg_database') AS description "
3207 : "FROM pg_database "
3208 : "WHERE datname = current_database()");
3209 :
3210 124 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3211 :
3212 124 : i_tableoid = PQfnumber(res, "tableoid");
3213 124 : i_oid = PQfnumber(res, "oid");
3214 124 : i_datname = PQfnumber(res, "datname");
3215 124 : i_datdba = PQfnumber(res, "datdba");
3216 124 : i_encoding = PQfnumber(res, "encoding");
3217 124 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3218 124 : i_collate = PQfnumber(res, "datcollate");
3219 124 : i_ctype = PQfnumber(res, "datctype");
3220 124 : i_datlocale = PQfnumber(res, "datlocale");
3221 124 : i_daticurules = PQfnumber(res, "daticurules");
3222 124 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3223 124 : i_minmxid = PQfnumber(res, "datminmxid");
3224 124 : i_datacl = PQfnumber(res, "datacl");
3225 124 : i_acldefault = PQfnumber(res, "acldefault");
3226 124 : i_datistemplate = PQfnumber(res, "datistemplate");
3227 124 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3228 124 : i_datcollversion = PQfnumber(res, "datcollversion");
3229 124 : i_tablespace = PQfnumber(res, "tablespace");
3230 :
3231 124 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3232 124 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3233 124 : datname = PQgetvalue(res, 0, i_datname);
3234 124 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3235 124 : encoding = PQgetvalue(res, 0, i_encoding);
3236 124 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3237 124 : collate = PQgetvalue(res, 0, i_collate);
3238 124 : ctype = PQgetvalue(res, 0, i_ctype);
3239 124 : if (!PQgetisnull(res, 0, i_datlocale))
3240 28 : locale = PQgetvalue(res, 0, i_datlocale);
3241 : else
3242 96 : locale = NULL;
3243 124 : if (!PQgetisnull(res, 0, i_daticurules))
3244 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3245 : else
3246 124 : icurules = NULL;
3247 124 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3248 124 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3249 124 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3250 124 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3251 124 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3252 124 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3253 124 : tablespace = PQgetvalue(res, 0, i_tablespace);
3254 :
3255 124 : qdatname = pg_strdup(fmtId(datname));
3256 :
3257 : /*
3258 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3259 : * to preserve that), as well as the encoding, locale, and tablespace
3260 : * since those can't be altered later. Other DB properties are left to
3261 : * the DATABASE PROPERTIES entry, so that they can be applied after
3262 : * reconnecting to the target DB.
3263 : *
3264 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3265 : * shown it to be faster. When the server is in binary upgrade mode, it
3266 : * will also skip the checkpoints this strategy ordinarily performs.
3267 : */
3268 124 : if (dopt->binary_upgrade)
3269 : {
3270 30 : appendPQExpBuffer(creaQry,
3271 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3272 : "OID = %u STRATEGY = FILE_COPY",
3273 : qdatname, dbCatId.oid);
3274 : }
3275 : else
3276 : {
3277 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3278 : qdatname);
3279 : }
3280 124 : if (strlen(encoding) > 0)
3281 : {
3282 124 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3283 124 : appendStringLiteralAH(creaQry, encoding, fout);
3284 : }
3285 :
3286 124 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3287 124 : if (datlocprovider[0] == 'b')
3288 28 : appendPQExpBufferStr(creaQry, "builtin");
3289 96 : else if (datlocprovider[0] == 'c')
3290 96 : appendPQExpBufferStr(creaQry, "libc");
3291 0 : else if (datlocprovider[0] == 'i')
3292 0 : appendPQExpBufferStr(creaQry, "icu");
3293 : else
3294 0 : pg_fatal("unrecognized locale provider: %s",
3295 : datlocprovider);
3296 :
3297 124 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3298 : {
3299 124 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3300 124 : appendStringLiteralAH(creaQry, collate, fout);
3301 : }
3302 : else
3303 : {
3304 0 : if (strlen(collate) > 0)
3305 : {
3306 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3307 0 : appendStringLiteralAH(creaQry, collate, fout);
3308 : }
3309 0 : if (strlen(ctype) > 0)
3310 : {
3311 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3312 0 : appendStringLiteralAH(creaQry, ctype, fout);
3313 : }
3314 : }
3315 124 : if (locale)
3316 : {
3317 28 : if (datlocprovider[0] == 'b')
3318 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3319 : else
3320 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3321 :
3322 28 : appendStringLiteralAH(creaQry, locale, fout);
3323 : }
3324 :
3325 124 : if (icurules)
3326 : {
3327 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3328 0 : appendStringLiteralAH(creaQry, icurules, fout);
3329 : }
3330 :
3331 : /*
3332 : * For binary upgrade, carry over the collation version. For normal
3333 : * dump/restore, omit the version, so that it is computed upon restore.
3334 : */
3335 124 : if (dopt->binary_upgrade)
3336 : {
3337 30 : if (!PQgetisnull(res, 0, i_datcollversion))
3338 : {
3339 30 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3340 30 : appendStringLiteralAH(creaQry,
3341 : PQgetvalue(res, 0, i_datcollversion),
3342 : fout);
3343 : }
3344 : }
3345 :
3346 : /*
3347 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3348 : * thing; the decision whether to specify a tablespace should be left till
3349 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3350 : * label the DATABASE entry with the tablespace and let the normal
3351 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3352 : * attention to default_tablespace, so that won't work.
3353 : */
3354 124 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3355 0 : !dopt->outputNoTablespaces)
3356 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3357 : fmtId(tablespace));
3358 124 : appendPQExpBufferStr(creaQry, ";\n");
3359 :
3360 124 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3361 : qdatname);
3362 :
3363 124 : dbDumpId = createDumpId();
3364 :
3365 124 : ArchiveEntry(fout,
3366 : dbCatId, /* catalog ID */
3367 : dbDumpId, /* dump ID */
3368 124 : ARCHIVE_OPTS(.tag = datname,
3369 : .owner = dba,
3370 : .description = "DATABASE",
3371 : .section = SECTION_PRE_DATA,
3372 : .createStmt = creaQry->data,
3373 : .dropStmt = delQry->data));
3374 :
3375 : /* Compute correct tag for archive entry */
3376 124 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3377 :
3378 : /* Dump DB comment if any */
3379 : {
3380 : /*
3381 : * 8.2 and up keep comments on shared objects in a shared table, so we
3382 : * cannot use the dumpComment() code used for other database objects.
3383 : * Be careful that the ArchiveEntry parameters match that function.
3384 : */
3385 124 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3386 :
3387 124 : if (comment && *comment && !dopt->no_comments)
3388 : {
3389 54 : resetPQExpBuffer(dbQry);
3390 :
3391 : /*
3392 : * Generates warning when loaded into a differently-named
3393 : * database.
3394 : */
3395 54 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3396 54 : appendStringLiteralAH(dbQry, comment, fout);
3397 54 : appendPQExpBufferStr(dbQry, ";\n");
3398 :
3399 54 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3400 54 : ARCHIVE_OPTS(.tag = labelq->data,
3401 : .owner = dba,
3402 : .description = "COMMENT",
3403 : .section = SECTION_NONE,
3404 : .createStmt = dbQry->data,
3405 : .deps = &dbDumpId,
3406 : .nDeps = 1));
3407 : }
3408 : }
3409 :
3410 : /* Dump DB security label, if enabled */
3411 124 : if (!dopt->no_security_labels)
3412 : {
3413 : PGresult *shres;
3414 : PQExpBuffer seclabelQry;
3415 :
3416 124 : seclabelQry = createPQExpBuffer();
3417 :
3418 124 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3419 124 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3420 124 : resetPQExpBuffer(seclabelQry);
3421 124 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3422 124 : if (seclabelQry->len > 0)
3423 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3424 0 : ARCHIVE_OPTS(.tag = labelq->data,
3425 : .owner = dba,
3426 : .description = "SECURITY LABEL",
3427 : .section = SECTION_NONE,
3428 : .createStmt = seclabelQry->data,
3429 : .deps = &dbDumpId,
3430 : .nDeps = 1));
3431 124 : destroyPQExpBuffer(seclabelQry);
3432 124 : PQclear(shres);
3433 : }
3434 :
3435 : /*
3436 : * Dump ACL if any. Note that we do not support initial privileges
3437 : * (pg_init_privs) on databases.
3438 : */
3439 124 : dbdacl.privtype = 0;
3440 124 : dbdacl.initprivs = NULL;
3441 :
3442 124 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3443 : qdatname, NULL, NULL,
3444 : NULL, dba, &dbdacl);
3445 :
3446 : /*
3447 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3448 : * non-default database-level properties. (The reason this must be
3449 : * separate is that we cannot put any additional commands into the TOC
3450 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3451 : * in an implicit transaction block, and the backend won't allow CREATE
3452 : * DATABASE in that context.)
3453 : */
3454 124 : resetPQExpBuffer(creaQry);
3455 124 : resetPQExpBuffer(delQry);
3456 :
3457 124 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3458 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3459 : qdatname, datconnlimit);
3460 :
3461 124 : if (strcmp(datistemplate, "t") == 0)
3462 : {
3463 10 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3464 : qdatname);
3465 :
3466 : /*
3467 : * The backend won't accept DROP DATABASE on a template database. We
3468 : * can deal with that by removing the template marking before the DROP
3469 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3470 : * since no such command is currently supported, fake it with a direct
3471 : * UPDATE on pg_database.
3472 : */
3473 10 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3474 : "SET datistemplate = false WHERE datname = ");
3475 10 : appendStringLiteralAH(delQry, datname, fout);
3476 10 : appendPQExpBufferStr(delQry, ";\n");
3477 : }
3478 :
3479 : /*
3480 : * We do not restore pg_database.dathasloginevt because it is set
3481 : * automatically on login event trigger creation.
3482 : */
3483 :
3484 : /* Add database-specific SET options */
3485 124 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3486 :
3487 : /*
3488 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3489 : * entry, too, for lack of a better place.
3490 : */
3491 124 : if (dopt->binary_upgrade)
3492 : {
3493 30 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3494 30 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3495 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3496 : "WHERE datname = ",
3497 : frozenxid, minmxid);
3498 30 : appendStringLiteralAH(creaQry, datname, fout);
3499 30 : appendPQExpBufferStr(creaQry, ";\n");
3500 : }
3501 :
3502 124 : if (creaQry->len > 0)
3503 38 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3504 38 : ARCHIVE_OPTS(.tag = datname,
3505 : .owner = dba,
3506 : .description = "DATABASE PROPERTIES",
3507 : .section = SECTION_PRE_DATA,
3508 : .createStmt = creaQry->data,
3509 : .dropStmt = delQry->data,
3510 : .deps = &dbDumpId));
3511 :
3512 : /*
3513 : * pg_largeobject comes from the old system intact, so set its
3514 : * relfrozenxids, relminmxids and relfilenode.
3515 : */
3516 124 : if (dopt->binary_upgrade)
3517 : {
3518 : PGresult *lo_res;
3519 30 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3520 30 : PQExpBuffer loOutQry = createPQExpBuffer();
3521 30 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3522 : int ii_relfrozenxid,
3523 : ii_relfilenode,
3524 : ii_oid,
3525 : ii_relminmxid;
3526 :
3527 : /*
3528 : * pg_largeobject
3529 : */
3530 30 : if (fout->remoteVersion >= 90300)
3531 30 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3532 : "FROM pg_catalog.pg_class\n"
3533 : "WHERE oid IN (%u, %u);\n",
3534 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3535 : else
3536 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3537 : "FROM pg_catalog.pg_class\n"
3538 : "WHERE oid IN (%u, %u);\n",
3539 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3540 :
3541 30 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3542 :
3543 30 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3544 30 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3545 30 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3546 30 : ii_oid = PQfnumber(lo_res, "oid");
3547 :
3548 30 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3549 30 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3550 90 : for (int i = 0; i < PQntuples(lo_res); ++i)
3551 : {
3552 : Oid oid;
3553 : RelFileNumber relfilenumber;
3554 :
3555 60 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3556 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3557 : "WHERE oid = %u;\n",
3558 60 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3559 60 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3560 60 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3561 :
3562 60 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3563 60 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3564 :
3565 60 : if (oid == LargeObjectRelationId)
3566 30 : appendPQExpBuffer(loOutQry,
3567 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3568 : relfilenumber);
3569 30 : else if (oid == LargeObjectLOidPNIndexId)
3570 30 : appendPQExpBuffer(loOutQry,
3571 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3572 : relfilenumber);
3573 : }
3574 :
3575 30 : appendPQExpBufferStr(loOutQry,
3576 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3577 30 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3578 :
3579 30 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3580 30 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3581 : .description = "pg_largeobject",
3582 : .section = SECTION_PRE_DATA,
3583 : .createStmt = loOutQry->data));
3584 :
3585 30 : PQclear(lo_res);
3586 :
3587 30 : destroyPQExpBuffer(loFrozenQry);
3588 30 : destroyPQExpBuffer(loHorizonQry);
3589 30 : destroyPQExpBuffer(loOutQry);
3590 : }
3591 :
3592 124 : PQclear(res);
3593 :
3594 124 : free(qdatname);
3595 124 : destroyPQExpBuffer(dbQry);
3596 124 : destroyPQExpBuffer(delQry);
3597 124 : destroyPQExpBuffer(creaQry);
3598 124 : destroyPQExpBuffer(labelq);
3599 124 : }
3600 :
3601 : /*
3602 : * Collect any database-specific or role-and-database-specific SET options
3603 : * for this database, and append them to outbuf.
3604 : */
3605 : static void
3606 124 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3607 : const char *dbname, Oid dboid)
3608 : {
3609 124 : PGconn *conn = GetConnection(AH);
3610 124 : PQExpBuffer buf = createPQExpBuffer();
3611 : PGresult *res;
3612 :
3613 : /* First collect database-specific options */
3614 124 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3615 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3616 : dboid);
3617 :
3618 124 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3619 :
3620 184 : for (int i = 0; i < PQntuples(res); i++)
3621 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3622 : "DATABASE", dbname, NULL, NULL,
3623 : outbuf);
3624 :
3625 124 : PQclear(res);
3626 :
3627 : /* Now look for role-and-database-specific options */
3628 124 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3629 : "FROM pg_db_role_setting s, pg_roles r "
3630 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3631 : dboid);
3632 :
3633 124 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3634 :
3635 124 : for (int i = 0; i < PQntuples(res); i++)
3636 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3637 0 : "ROLE", PQgetvalue(res, i, 0),
3638 : "DATABASE", dbname,
3639 : outbuf);
3640 :
3641 124 : PQclear(res);
3642 :
3643 124 : destroyPQExpBuffer(buf);
3644 124 : }
3645 :
3646 : /*
3647 : * dumpEncoding: put the correct encoding into the archive
3648 : */
3649 : static void
3650 320 : dumpEncoding(Archive *AH)
3651 : {
3652 320 : const char *encname = pg_encoding_to_char(AH->encoding);
3653 320 : PQExpBuffer qry = createPQExpBuffer();
3654 :
3655 320 : pg_log_info("saving encoding = %s", encname);
3656 :
3657 320 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3658 320 : appendStringLiteralAH(qry, encname, AH);
3659 320 : appendPQExpBufferStr(qry, ";\n");
3660 :
3661 320 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3662 320 : ARCHIVE_OPTS(.tag = "ENCODING",
3663 : .description = "ENCODING",
3664 : .section = SECTION_PRE_DATA,
3665 : .createStmt = qry->data));
3666 :
3667 320 : destroyPQExpBuffer(qry);
3668 320 : }
3669 :
3670 :
3671 : /*
3672 : * dumpStdStrings: put the correct escape string behavior into the archive
3673 : */
3674 : static void
3675 320 : dumpStdStrings(Archive *AH)
3676 : {
3677 320 : const char *stdstrings = AH->std_strings ? "on" : "off";
3678 320 : PQExpBuffer qry = createPQExpBuffer();
3679 :
3680 320 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3681 : stdstrings);
3682 :
3683 320 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3684 : stdstrings);
3685 :
3686 320 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3687 320 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3688 : .description = "STDSTRINGS",
3689 : .section = SECTION_PRE_DATA,
3690 : .createStmt = qry->data));
3691 :
3692 320 : destroyPQExpBuffer(qry);
3693 320 : }
3694 :
3695 : /*
3696 : * dumpSearchPath: record the active search_path in the archive
3697 : */
3698 : static void
3699 320 : dumpSearchPath(Archive *AH)
3700 : {
3701 320 : PQExpBuffer qry = createPQExpBuffer();
3702 320 : PQExpBuffer path = createPQExpBuffer();
3703 : PGresult *res;
3704 320 : char **schemanames = NULL;
3705 320 : int nschemanames = 0;
3706 : int i;
3707 :
3708 : /*
3709 : * We use the result of current_schemas(), not the search_path GUC,
3710 : * because that might contain wildcards such as "$user", which won't
3711 : * necessarily have the same value during restore. Also, this way avoids
3712 : * listing schemas that may appear in search_path but not actually exist,
3713 : * which seems like a prudent exclusion.
3714 : */
3715 320 : res = ExecuteSqlQueryForSingleRow(AH,
3716 : "SELECT pg_catalog.current_schemas(false)");
3717 :
3718 320 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3719 0 : pg_fatal("could not parse result of current_schemas()");
3720 :
3721 : /*
3722 : * We use set_config(), not a simple "SET search_path" command, because
3723 : * the latter has less-clean behavior if the search path is empty. While
3724 : * that's likely to get fixed at some point, it seems like a good idea to
3725 : * be as backwards-compatible as possible in what we put into archives.
3726 : */
3727 320 : for (i = 0; i < nschemanames; i++)
3728 : {
3729 0 : if (i > 0)
3730 0 : appendPQExpBufferStr(path, ", ");
3731 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3732 : }
3733 :
3734 320 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3735 320 : appendStringLiteralAH(qry, path->data, AH);
3736 320 : appendPQExpBufferStr(qry, ", false);\n");
3737 :
3738 320 : pg_log_info("saving \"search_path = %s\"", path->data);
3739 :
3740 320 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3741 320 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3742 : .description = "SEARCHPATH",
3743 : .section = SECTION_PRE_DATA,
3744 : .createStmt = qry->data));
3745 :
3746 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3747 320 : AH->searchpath = pg_strdup(qry->data);
3748 :
3749 320 : free(schemanames);
3750 320 : PQclear(res);
3751 320 : destroyPQExpBuffer(qry);
3752 320 : destroyPQExpBuffer(path);
3753 320 : }
3754 :
3755 :
3756 : /*
3757 : * getLOs:
3758 : * Collect schema-level data about large objects
3759 : */
3760 : static void
3761 264 : getLOs(Archive *fout)
3762 : {
3763 264 : DumpOptions *dopt = fout->dopt;
3764 264 : PQExpBuffer loQry = createPQExpBuffer();
3765 : PGresult *res;
3766 : int ntups;
3767 : int i;
3768 : int n;
3769 : int i_oid;
3770 : int i_lomowner;
3771 : int i_lomacl;
3772 : int i_acldefault;
3773 :
3774 264 : pg_log_info("reading large objects");
3775 :
3776 : /*
3777 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3778 : * with the same owner/ACL appear together.
3779 : */
3780 264 : appendPQExpBufferStr(loQry,
3781 : "SELECT oid, lomowner, lomacl, "
3782 : "acldefault('L', lomowner) AS acldefault "
3783 : "FROM pg_largeobject_metadata "
3784 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3785 :
3786 264 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3787 :
3788 264 : i_oid = PQfnumber(res, "oid");
3789 264 : i_lomowner = PQfnumber(res, "lomowner");
3790 264 : i_lomacl = PQfnumber(res, "lomacl");
3791 264 : i_acldefault = PQfnumber(res, "acldefault");
3792 :
3793 264 : ntups = PQntuples(res);
3794 :
3795 : /*
3796 : * Group the blobs into suitably-sized groups that have the same owner and
3797 : * ACL setting, and build a metadata and a data DumpableObject for each
3798 : * group. (If we supported initprivs for blobs, we'd have to insist that
3799 : * groups also share initprivs settings, since the DumpableObject only has
3800 : * room for one.) i is the index of the first tuple in the current group,
3801 : * and n is the number of tuples we include in the group.
3802 : */
3803 418 : for (i = 0; i < ntups; i += n)
3804 : {
3805 154 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3806 154 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3807 154 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3808 : LoInfo *loinfo;
3809 : DumpableObject *lodata;
3810 : char namebuf[64];
3811 :
3812 : /* Scan to find first tuple not to be included in group */
3813 154 : n = 1;
3814 174 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3815 : {
3816 92 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3817 92 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3818 : break;
3819 20 : n++;
3820 : }
3821 :
3822 : /* Build the metadata DumpableObject */
3823 154 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3824 :
3825 154 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3826 154 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3827 154 : loinfo->dobj.catId.oid = thisoid;
3828 154 : AssignDumpId(&loinfo->dobj);
3829 :
3830 154 : if (n > 1)
3831 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3832 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3833 : else
3834 144 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3835 154 : loinfo->dobj.name = pg_strdup(namebuf);
3836 154 : loinfo->dacl.acl = pg_strdup(thisacl);
3837 154 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3838 154 : loinfo->dacl.privtype = 0;
3839 154 : loinfo->dacl.initprivs = NULL;
3840 154 : loinfo->rolname = getRoleName(thisowner);
3841 154 : loinfo->numlos = n;
3842 154 : loinfo->looids[0] = thisoid;
3843 : /* Collect OIDs of the remaining blobs in this group */
3844 174 : for (int k = 1; k < n; k++)
3845 : {
3846 : CatalogId extraID;
3847 :
3848 20 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3849 :
3850 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3851 20 : extraID.tableoid = LargeObjectRelationId;
3852 20 : extraID.oid = loinfo->looids[k];
3853 20 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3854 : }
3855 :
3856 : /* LOs have data */
3857 154 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3858 :
3859 : /* Mark whether LO group has a non-empty ACL */
3860 154 : if (!PQgetisnull(res, i, i_lomacl))
3861 72 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3862 :
3863 : /*
3864 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3865 : * as it will be copied by pg_upgrade, which simply copies the
3866 : * pg_largeobject table. We *do* however dump out anything but the
3867 : * data, as pg_upgrade copies just pg_largeobject, but not
3868 : * pg_largeobject_metadata, after the dump is restored.
3869 : */
3870 154 : if (dopt->binary_upgrade)
3871 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3872 :
3873 : /*
3874 : * Create a "BLOBS" data item for the group, too. This is just a
3875 : * placeholder for sorting; it carries no data now.
3876 : */
3877 154 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3878 154 : lodata->objType = DO_LARGE_OBJECT_DATA;
3879 154 : lodata->catId = nilCatalogId;
3880 154 : AssignDumpId(lodata);
3881 154 : lodata->name = pg_strdup(namebuf);
3882 154 : lodata->components |= DUMP_COMPONENT_DATA;
3883 : /* Set up explicit dependency from data to metadata */
3884 154 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3885 154 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3886 154 : lodata->nDeps = lodata->allocDeps = 1;
3887 : }
3888 :
3889 264 : PQclear(res);
3890 264 : destroyPQExpBuffer(loQry);
3891 264 : }
3892 :
3893 : /*
3894 : * dumpLO
3895 : *
3896 : * dump the definition (metadata) of the given large object group
3897 : */
3898 : static void
3899 154 : dumpLO(Archive *fout, const LoInfo *loinfo)
3900 : {
3901 154 : PQExpBuffer cquery = createPQExpBuffer();
3902 :
3903 : /*
3904 : * The "definition" is just a newline-separated list of OIDs. We need to
3905 : * put something into the dropStmt too, but it can just be a comment.
3906 : */
3907 328 : for (int i = 0; i < loinfo->numlos; i++)
3908 174 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3909 :
3910 154 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3911 154 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3912 154 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3913 : .owner = loinfo->rolname,
3914 : .description = "BLOB METADATA",
3915 : .section = SECTION_DATA,
3916 : .createStmt = cquery->data,
3917 : .dropStmt = "-- dummy"));
3918 :
3919 : /*
3920 : * Dump per-blob comments and seclabels if any. We assume these are rare
3921 : * enough that it's okay to generate retail TOC entries for them.
3922 : */
3923 154 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3924 : DUMP_COMPONENT_SECLABEL))
3925 : {
3926 184 : for (int i = 0; i < loinfo->numlos; i++)
3927 : {
3928 : CatalogId catId;
3929 : char namebuf[32];
3930 :
3931 : /* Build identifying info for this blob */
3932 102 : catId.tableoid = loinfo->dobj.catId.tableoid;
3933 102 : catId.oid = loinfo->looids[i];
3934 102 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3935 :
3936 102 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3937 102 : dumpComment(fout, "LARGE OBJECT", namebuf,
3938 : NULL, loinfo->rolname,
3939 : catId, 0, loinfo->dobj.dumpId);
3940 :
3941 102 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3942 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
3943 : NULL, loinfo->rolname,
3944 : catId, 0, loinfo->dobj.dumpId);
3945 : }
3946 : }
3947 :
3948 : /*
3949 : * Dump the ACLs if any (remember that all blobs in the group will have
3950 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
3951 : * there's more, make a "LARGE OBJECTS" entry that really contains only
3952 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
3953 : * string to emit a mutated version for each blob.
3954 : */
3955 154 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3956 : {
3957 : char namebuf[32];
3958 :
3959 : /* Build identifying info for the first blob */
3960 72 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
3961 :
3962 72 : if (loinfo->numlos > 1)
3963 : {
3964 : char tagbuf[64];
3965 :
3966 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
3967 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
3968 :
3969 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3970 : "LARGE OBJECT", namebuf, NULL, NULL,
3971 : tagbuf, loinfo->rolname, &loinfo->dacl);
3972 : }
3973 : else
3974 : {
3975 72 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3976 : "LARGE OBJECT", namebuf, NULL, NULL,
3977 : NULL, loinfo->rolname, &loinfo->dacl);
3978 : }
3979 : }
3980 :
3981 154 : destroyPQExpBuffer(cquery);
3982 154 : }
3983 :
3984 : /*
3985 : * dumpLOs:
3986 : * dump the data contents of the large objects in the given group
3987 : */
3988 : static int
3989 140 : dumpLOs(Archive *fout, const void *arg)
3990 : {
3991 140 : const LoInfo *loinfo = (const LoInfo *) arg;
3992 140 : PGconn *conn = GetConnection(fout);
3993 : char buf[LOBBUFSIZE];
3994 :
3995 140 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
3996 :
3997 296 : for (int i = 0; i < loinfo->numlos; i++)
3998 : {
3999 156 : Oid loOid = loinfo->looids[i];
4000 : int loFd;
4001 : int cnt;
4002 :
4003 : /* Open the LO */
4004 156 : loFd = lo_open(conn, loOid, INV_READ);
4005 156 : if (loFd == -1)
4006 0 : pg_fatal("could not open large object %u: %s",
4007 : loOid, PQerrorMessage(conn));
4008 :
4009 156 : StartLO(fout, loOid);
4010 :
4011 : /* Now read it in chunks, sending data to archive */
4012 : do
4013 : {
4014 238 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4015 238 : if (cnt < 0)
4016 0 : pg_fatal("error reading large object %u: %s",
4017 : loOid, PQerrorMessage(conn));
4018 :
4019 238 : WriteData(fout, buf, cnt);
4020 238 : } while (cnt > 0);
4021 :
4022 156 : lo_close(conn, loFd);
4023 :
4024 156 : EndLO(fout, loOid);
4025 : }
4026 :
4027 140 : return 1;
4028 : }
4029 :
4030 : /*
4031 : * getPolicies
4032 : * get information about all RLS policies on dumpable tables.
4033 : */
4034 : void
4035 320 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4036 : {
4037 : PQExpBuffer query;
4038 : PQExpBuffer tbloids;
4039 : PGresult *res;
4040 : PolicyInfo *polinfo;
4041 : int i_oid;
4042 : int i_tableoid;
4043 : int i_polrelid;
4044 : int i_polname;
4045 : int i_polcmd;
4046 : int i_polpermissive;
4047 : int i_polroles;
4048 : int i_polqual;
4049 : int i_polwithcheck;
4050 : int i,
4051 : j,
4052 : ntups;
4053 :
4054 : /* No policies before 9.5 */
4055 320 : if (fout->remoteVersion < 90500)
4056 0 : return;
4057 :
4058 320 : query = createPQExpBuffer();
4059 320 : tbloids = createPQExpBuffer();
4060 :
4061 : /*
4062 : * Identify tables of interest, and check which ones have RLS enabled.
4063 : */
4064 320 : appendPQExpBufferChar(tbloids, '{');
4065 85060 : for (i = 0; i < numTables; i++)
4066 : {
4067 84740 : TableInfo *tbinfo = &tblinfo[i];
4068 :
4069 : /* Ignore row security on tables not to be dumped */
4070 84740 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4071 71522 : continue;
4072 :
4073 : /* It can't have RLS or policies if it's not a table */
4074 13218 : if (tbinfo->relkind != RELKIND_RELATION &&
4075 3894 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4076 2806 : continue;
4077 :
4078 : /* Add it to the list of table OIDs to be probed below */
4079 10412 : if (tbloids->len > 1) /* do we have more than the '{'? */
4080 10208 : appendPQExpBufferChar(tbloids, ',');
4081 10412 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4082 :
4083 : /* Is RLS enabled? (That's separate from whether it has policies) */
4084 10412 : if (tbinfo->rowsec)
4085 : {
4086 112 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4087 :
4088 : /*
4089 : * We represent RLS being enabled on a table by creating a
4090 : * PolicyInfo object with null polname.
4091 : *
4092 : * Note: use tableoid 0 so that this object won't be mistaken for
4093 : * something that pg_depend entries apply to.
4094 : */
4095 112 : polinfo = pg_malloc(sizeof(PolicyInfo));
4096 112 : polinfo->dobj.objType = DO_POLICY;
4097 112 : polinfo->dobj.catId.tableoid = 0;
4098 112 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4099 112 : AssignDumpId(&polinfo->dobj);
4100 112 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4101 112 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4102 112 : polinfo->poltable = tbinfo;
4103 112 : polinfo->polname = NULL;
4104 112 : polinfo->polcmd = '\0';
4105 112 : polinfo->polpermissive = 0;
4106 112 : polinfo->polroles = NULL;
4107 112 : polinfo->polqual = NULL;
4108 112 : polinfo->polwithcheck = NULL;
4109 : }
4110 : }
4111 320 : appendPQExpBufferChar(tbloids, '}');
4112 :
4113 : /*
4114 : * Now, read all RLS policies belonging to the tables of interest, and
4115 : * create PolicyInfo objects for them. (Note that we must filter the
4116 : * results server-side not locally, because we dare not apply pg_get_expr
4117 : * to tables we don't have lock on.)
4118 : */
4119 320 : pg_log_info("reading row-level security policies");
4120 :
4121 320 : printfPQExpBuffer(query,
4122 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4123 320 : if (fout->remoteVersion >= 100000)
4124 320 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4125 : else
4126 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4127 320 : appendPQExpBuffer(query,
4128 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4129 : " 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, "
4130 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4131 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4132 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4133 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4134 : tbloids->data);
4135 :
4136 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4137 :
4138 320 : ntups = PQntuples(res);
4139 320 : if (ntups > 0)
4140 : {
4141 92 : i_oid = PQfnumber(res, "oid");
4142 92 : i_tableoid = PQfnumber(res, "tableoid");
4143 92 : i_polrelid = PQfnumber(res, "polrelid");
4144 92 : i_polname = PQfnumber(res, "polname");
4145 92 : i_polcmd = PQfnumber(res, "polcmd");
4146 92 : i_polpermissive = PQfnumber(res, "polpermissive");
4147 92 : i_polroles = PQfnumber(res, "polroles");
4148 92 : i_polqual = PQfnumber(res, "polqual");
4149 92 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4150 :
4151 92 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4152 :
4153 674 : for (j = 0; j < ntups; j++)
4154 : {
4155 582 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4156 582 : TableInfo *tbinfo = findTableByOid(polrelid);
4157 :
4158 582 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4159 :
4160 582 : polinfo[j].dobj.objType = DO_POLICY;
4161 582 : polinfo[j].dobj.catId.tableoid =
4162 582 : atooid(PQgetvalue(res, j, i_tableoid));
4163 582 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4164 582 : AssignDumpId(&polinfo[j].dobj);
4165 582 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4166 582 : polinfo[j].poltable = tbinfo;
4167 582 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4168 582 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4169 :
4170 582 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4171 582 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4172 :
4173 582 : if (PQgetisnull(res, j, i_polroles))
4174 254 : polinfo[j].polroles = NULL;
4175 : else
4176 328 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4177 :
4178 582 : if (PQgetisnull(res, j, i_polqual))
4179 82 : polinfo[j].polqual = NULL;
4180 : else
4181 500 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4182 :
4183 582 : if (PQgetisnull(res, j, i_polwithcheck))
4184 306 : polinfo[j].polwithcheck = NULL;
4185 : else
4186 276 : polinfo[j].polwithcheck
4187 276 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4188 : }
4189 : }
4190 :
4191 320 : PQclear(res);
4192 :
4193 320 : destroyPQExpBuffer(query);
4194 320 : destroyPQExpBuffer(tbloids);
4195 : }
4196 :
4197 : /*
4198 : * dumpPolicy
4199 : * dump the definition of the given policy
4200 : */
4201 : static void
4202 694 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4203 : {
4204 694 : DumpOptions *dopt = fout->dopt;
4205 694 : TableInfo *tbinfo = polinfo->poltable;
4206 : PQExpBuffer query;
4207 : PQExpBuffer delqry;
4208 : PQExpBuffer polprefix;
4209 : char *qtabname;
4210 : const char *cmd;
4211 : char *tag;
4212 :
4213 : /* Do nothing if not dumping schema */
4214 694 : if (!dopt->dumpSchema)
4215 98 : return;
4216 :
4217 : /*
4218 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4219 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4220 : * ROW LEVEL SECURITY.
4221 : */
4222 596 : if (polinfo->polname == NULL)
4223 : {
4224 98 : query = createPQExpBuffer();
4225 :
4226 98 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4227 98 : fmtQualifiedDumpable(tbinfo));
4228 :
4229 : /*
4230 : * We must emit the ROW SECURITY object's dependency on its table
4231 : * explicitly, because it will not match anything in pg_depend (unlike
4232 : * the case for other PolicyInfo objects).
4233 : */
4234 98 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4235 98 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4236 98 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4237 : .namespace = polinfo->dobj.namespace->dobj.name,
4238 : .owner = tbinfo->rolname,
4239 : .description = "ROW SECURITY",
4240 : .section = SECTION_POST_DATA,
4241 : .createStmt = query->data,
4242 : .deps = &(tbinfo->dobj.dumpId),
4243 : .nDeps = 1));
4244 :
4245 98 : destroyPQExpBuffer(query);
4246 98 : return;
4247 : }
4248 :
4249 498 : if (polinfo->polcmd == '*')
4250 166 : cmd = "";
4251 332 : else if (polinfo->polcmd == 'r')
4252 88 : cmd = " FOR SELECT";
4253 244 : else if (polinfo->polcmd == 'a')
4254 68 : cmd = " FOR INSERT";
4255 176 : else if (polinfo->polcmd == 'w')
4256 88 : cmd = " FOR UPDATE";
4257 88 : else if (polinfo->polcmd == 'd')
4258 88 : cmd = " FOR DELETE";
4259 : else
4260 0 : pg_fatal("unexpected policy command type: %c",
4261 : polinfo->polcmd);
4262 :
4263 498 : query = createPQExpBuffer();
4264 498 : delqry = createPQExpBuffer();
4265 498 : polprefix = createPQExpBuffer();
4266 :
4267 498 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4268 :
4269 498 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4270 :
4271 498 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4272 498 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4273 :
4274 498 : if (polinfo->polroles != NULL)
4275 272 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4276 :
4277 498 : if (polinfo->polqual != NULL)
4278 430 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4279 :
4280 498 : if (polinfo->polwithcheck != NULL)
4281 234 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4282 :
4283 498 : appendPQExpBufferStr(query, ";\n");
4284 :
4285 498 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4286 498 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4287 :
4288 498 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4289 498 : fmtId(polinfo->polname));
4290 :
4291 498 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4292 :
4293 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4294 498 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4295 498 : ARCHIVE_OPTS(.tag = tag,
4296 : .namespace = polinfo->dobj.namespace->dobj.name,
4297 : .owner = tbinfo->rolname,
4298 : .description = "POLICY",
4299 : .section = SECTION_POST_DATA,
4300 : .createStmt = query->data,
4301 : .dropStmt = delqry->data));
4302 :
4303 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4304 0 : dumpComment(fout, polprefix->data, qtabname,
4305 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4306 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4307 :
4308 498 : free(tag);
4309 498 : destroyPQExpBuffer(query);
4310 498 : destroyPQExpBuffer(delqry);
4311 498 : destroyPQExpBuffer(polprefix);
4312 498 : free(qtabname);
4313 : }
4314 :
4315 : /*
4316 : * getPublications
4317 : * get information about publications
4318 : */
4319 : void
4320 320 : getPublications(Archive *fout)
4321 : {
4322 320 : DumpOptions *dopt = fout->dopt;
4323 : PQExpBuffer query;
4324 : PGresult *res;
4325 : PublicationInfo *pubinfo;
4326 : int i_tableoid;
4327 : int i_oid;
4328 : int i_pubname;
4329 : int i_pubowner;
4330 : int i_puballtables;
4331 : int i_pubinsert;
4332 : int i_pubupdate;
4333 : int i_pubdelete;
4334 : int i_pubtruncate;
4335 : int i_pubviaroot;
4336 : int i_pubgencols;
4337 : int i,
4338 : ntups;
4339 :
4340 320 : if (dopt->no_publications || fout->remoteVersion < 100000)
4341 0 : return;
4342 :
4343 320 : query = createPQExpBuffer();
4344 :
4345 : /* Get the publications. */
4346 320 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4347 : "p.pubowner, p.puballtables, p.pubinsert, "
4348 : "p.pubupdate, p.pubdelete, ");
4349 :
4350 320 : if (fout->remoteVersion >= 110000)
4351 320 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4352 : else
4353 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4354 :
4355 320 : if (fout->remoteVersion >= 130000)
4356 320 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4357 : else
4358 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4359 :
4360 320 : if (fout->remoteVersion >= 180000)
4361 320 : appendPQExpBufferStr(query, "p.pubgencols ");
4362 : else
4363 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4364 :
4365 320 : appendPQExpBufferStr(query, "FROM pg_publication p");
4366 :
4367 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4368 :
4369 320 : ntups = PQntuples(res);
4370 :
4371 320 : if (ntups == 0)
4372 224 : goto cleanup;
4373 :
4374 96 : i_tableoid = PQfnumber(res, "tableoid");
4375 96 : i_oid = PQfnumber(res, "oid");
4376 96 : i_pubname = PQfnumber(res, "pubname");
4377 96 : i_pubowner = PQfnumber(res, "pubowner");
4378 96 : i_puballtables = PQfnumber(res, "puballtables");
4379 96 : i_pubinsert = PQfnumber(res, "pubinsert");
4380 96 : i_pubupdate = PQfnumber(res, "pubupdate");
4381 96 : i_pubdelete = PQfnumber(res, "pubdelete");
4382 96 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4383 96 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4384 96 : i_pubgencols = PQfnumber(res, "pubgencols");
4385 :
4386 96 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4387 :
4388 568 : for (i = 0; i < ntups; i++)
4389 : {
4390 472 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4391 472 : pubinfo[i].dobj.catId.tableoid =
4392 472 : atooid(PQgetvalue(res, i, i_tableoid));
4393 472 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4394 472 : AssignDumpId(&pubinfo[i].dobj);
4395 472 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4396 472 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4397 472 : pubinfo[i].puballtables =
4398 472 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4399 472 : pubinfo[i].pubinsert =
4400 472 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4401 472 : pubinfo[i].pubupdate =
4402 472 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4403 472 : pubinfo[i].pubdelete =
4404 472 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4405 472 : pubinfo[i].pubtruncate =
4406 472 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4407 472 : pubinfo[i].pubviaroot =
4408 472 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4409 472 : pubinfo[i].pubgencols_type =
4410 472 : *(PQgetvalue(res, i, i_pubgencols));
4411 :
4412 : /* Decide whether we want to dump it */
4413 472 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4414 : }
4415 :
4416 96 : cleanup:
4417 320 : PQclear(res);
4418 :
4419 320 : destroyPQExpBuffer(query);
4420 : }
4421 :
4422 : /*
4423 : * dumpPublication
4424 : * dump the definition of the given publication
4425 : */
4426 : static void
4427 392 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4428 : {
4429 392 : DumpOptions *dopt = fout->dopt;
4430 : PQExpBuffer delq;
4431 : PQExpBuffer query;
4432 : char *qpubname;
4433 392 : bool first = true;
4434 :
4435 : /* Do nothing if not dumping schema */
4436 392 : if (!dopt->dumpSchema)
4437 60 : return;
4438 :
4439 332 : delq = createPQExpBuffer();
4440 332 : query = createPQExpBuffer();
4441 :
4442 332 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4443 :
4444 332 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4445 : qpubname);
4446 :
4447 332 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4448 : qpubname);
4449 :
4450 332 : if (pubinfo->puballtables)
4451 68 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4452 :
4453 332 : appendPQExpBufferStr(query, " WITH (publish = '");
4454 332 : if (pubinfo->pubinsert)
4455 : {
4456 266 : appendPQExpBufferStr(query, "insert");
4457 266 : first = false;
4458 : }
4459 :
4460 332 : if (pubinfo->pubupdate)
4461 : {
4462 266 : if (!first)
4463 266 : appendPQExpBufferStr(query, ", ");
4464 :
4465 266 : appendPQExpBufferStr(query, "update");
4466 266 : first = false;
4467 : }
4468 :
4469 332 : if (pubinfo->pubdelete)
4470 : {
4471 266 : if (!first)
4472 266 : appendPQExpBufferStr(query, ", ");
4473 :
4474 266 : appendPQExpBufferStr(query, "delete");
4475 266 : first = false;
4476 : }
4477 :
4478 332 : if (pubinfo->pubtruncate)
4479 : {
4480 266 : if (!first)
4481 266 : appendPQExpBufferStr(query, ", ");
4482 :
4483 266 : appendPQExpBufferStr(query, "truncate");
4484 266 : first = false;
4485 : }
4486 :
4487 332 : appendPQExpBufferChar(query, '\'');
4488 :
4489 332 : if (pubinfo->pubviaroot)
4490 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4491 :
4492 332 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4493 66 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4494 :
4495 332 : appendPQExpBufferStr(query, ");\n");
4496 :
4497 332 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4498 332 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4499 332 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4500 : .owner = pubinfo->rolname,
4501 : .description = "PUBLICATION",
4502 : .section = SECTION_POST_DATA,
4503 : .createStmt = query->data,
4504 : .dropStmt = delq->data));
4505 :
4506 332 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4507 66 : dumpComment(fout, "PUBLICATION", qpubname,
4508 : NULL, pubinfo->rolname,
4509 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4510 :
4511 332 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4512 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4513 : NULL, pubinfo->rolname,
4514 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4515 :
4516 332 : destroyPQExpBuffer(delq);
4517 332 : destroyPQExpBuffer(query);
4518 332 : free(qpubname);
4519 : }
4520 :
4521 : /*
4522 : * getPublicationNamespaces
4523 : * get information about publication membership for dumpable schemas.
4524 : */
4525 : void
4526 320 : getPublicationNamespaces(Archive *fout)
4527 : {
4528 : PQExpBuffer query;
4529 : PGresult *res;
4530 : PublicationSchemaInfo *pubsinfo;
4531 320 : DumpOptions *dopt = fout->dopt;
4532 : int i_tableoid;
4533 : int i_oid;
4534 : int i_pnpubid;
4535 : int i_pnnspid;
4536 : int i,
4537 : j,
4538 : ntups;
4539 :
4540 320 : if (dopt->no_publications || fout->remoteVersion < 150000)
4541 0 : return;
4542 :
4543 320 : query = createPQExpBuffer();
4544 :
4545 : /* Collect all publication membership info. */
4546 320 : appendPQExpBufferStr(query,
4547 : "SELECT tableoid, oid, pnpubid, pnnspid "
4548 : "FROM pg_catalog.pg_publication_namespace");
4549 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4550 :
4551 320 : ntups = PQntuples(res);
4552 :
4553 320 : i_tableoid = PQfnumber(res, "tableoid");
4554 320 : i_oid = PQfnumber(res, "oid");
4555 320 : i_pnpubid = PQfnumber(res, "pnpubid");
4556 320 : i_pnnspid = PQfnumber(res, "pnnspid");
4557 :
4558 : /* this allocation may be more than we need */
4559 320 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4560 320 : j = 0;
4561 :
4562 508 : for (i = 0; i < ntups; i++)
4563 : {
4564 188 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4565 188 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4566 : PublicationInfo *pubinfo;
4567 : NamespaceInfo *nspinfo;
4568 :
4569 : /*
4570 : * Ignore any entries for which we aren't interested in either the
4571 : * publication or the rel.
4572 : */
4573 188 : pubinfo = findPublicationByOid(pnpubid);
4574 188 : if (pubinfo == NULL)
4575 0 : continue;
4576 188 : nspinfo = findNamespaceByOid(pnnspid);
4577 188 : if (nspinfo == NULL)
4578 0 : continue;
4579 :
4580 : /* OK, make a DumpableObject for this relationship */
4581 188 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4582 188 : pubsinfo[j].dobj.catId.tableoid =
4583 188 : atooid(PQgetvalue(res, i, i_tableoid));
4584 188 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4585 188 : AssignDumpId(&pubsinfo[j].dobj);
4586 188 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4587 188 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4588 188 : pubsinfo[j].publication = pubinfo;
4589 188 : pubsinfo[j].pubschema = nspinfo;
4590 :
4591 : /* Decide whether we want to dump it */
4592 188 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4593 :
4594 188 : j++;
4595 : }
4596 :
4597 320 : PQclear(res);
4598 320 : destroyPQExpBuffer(query);
4599 : }
4600 :
4601 : /*
4602 : * getPublicationTables
4603 : * get information about publication membership for dumpable tables.
4604 : */
4605 : void
4606 320 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4607 : {
4608 : PQExpBuffer query;
4609 : PGresult *res;
4610 : PublicationRelInfo *pubrinfo;
4611 320 : DumpOptions *dopt = fout->dopt;
4612 : int i_tableoid;
4613 : int i_oid;
4614 : int i_prpubid;
4615 : int i_prrelid;
4616 : int i_prrelqual;
4617 : int i_prattrs;
4618 : int i,
4619 : j,
4620 : ntups;
4621 :
4622 320 : if (dopt->no_publications || fout->remoteVersion < 100000)
4623 0 : return;
4624 :
4625 320 : query = createPQExpBuffer();
4626 :
4627 : /* Collect all publication membership info. */
4628 320 : if (fout->remoteVersion >= 150000)
4629 320 : appendPQExpBufferStr(query,
4630 : "SELECT tableoid, oid, prpubid, prrelid, "
4631 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4632 : "(CASE\n"
4633 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4634 : " (SELECT array_agg(attname)\n"
4635 : " FROM\n"
4636 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4637 : " pg_catalog.pg_attribute\n"
4638 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4639 : " ELSE NULL END) prattrs "
4640 : "FROM pg_catalog.pg_publication_rel pr");
4641 : else
4642 0 : appendPQExpBufferStr(query,
4643 : "SELECT tableoid, oid, prpubid, prrelid, "
4644 : "NULL AS prrelqual, NULL AS prattrs "
4645 : "FROM pg_catalog.pg_publication_rel");
4646 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4647 :
4648 320 : ntups = PQntuples(res);
4649 :
4650 320 : i_tableoid = PQfnumber(res, "tableoid");
4651 320 : i_oid = PQfnumber(res, "oid");
4652 320 : i_prpubid = PQfnumber(res, "prpubid");
4653 320 : i_prrelid = PQfnumber(res, "prrelid");
4654 320 : i_prrelqual = PQfnumber(res, "prrelqual");
4655 320 : i_prattrs = PQfnumber(res, "prattrs");
4656 :
4657 : /* this allocation may be more than we need */
4658 320 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4659 320 : j = 0;
4660 :
4661 978 : for (i = 0; i < ntups; i++)
4662 : {
4663 658 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4664 658 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4665 : PublicationInfo *pubinfo;
4666 : TableInfo *tbinfo;
4667 :
4668 : /*
4669 : * Ignore any entries for which we aren't interested in either the
4670 : * publication or the rel.
4671 : */
4672 658 : pubinfo = findPublicationByOid(prpubid);
4673 658 : if (pubinfo == NULL)
4674 0 : continue;
4675 658 : tbinfo = findTableByOid(prrelid);
4676 658 : if (tbinfo == NULL)
4677 0 : continue;
4678 :
4679 : /* OK, make a DumpableObject for this relationship */
4680 658 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4681 658 : pubrinfo[j].dobj.catId.tableoid =
4682 658 : atooid(PQgetvalue(res, i, i_tableoid));
4683 658 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4684 658 : AssignDumpId(&pubrinfo[j].dobj);
4685 658 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4686 658 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4687 658 : pubrinfo[j].publication = pubinfo;
4688 658 : pubrinfo[j].pubtable = tbinfo;
4689 658 : if (PQgetisnull(res, i, i_prrelqual))
4690 376 : pubrinfo[j].pubrelqual = NULL;
4691 : else
4692 282 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4693 :
4694 658 : if (!PQgetisnull(res, i, i_prattrs))
4695 : {
4696 : char **attnames;
4697 : int nattnames;
4698 : PQExpBuffer attribs;
4699 :
4700 188 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4701 : &attnames, &nattnames))
4702 0 : pg_fatal("could not parse %s array", "prattrs");
4703 188 : attribs = createPQExpBuffer();
4704 564 : for (int k = 0; k < nattnames; k++)
4705 : {
4706 376 : if (k > 0)
4707 188 : appendPQExpBufferStr(attribs, ", ");
4708 :
4709 376 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4710 : }
4711 188 : pubrinfo[j].pubrattrs = attribs->data;
4712 188 : free(attribs); /* but not attribs->data */
4713 188 : free(attnames);
4714 : }
4715 : else
4716 470 : pubrinfo[j].pubrattrs = NULL;
4717 :
4718 : /* Decide whether we want to dump it */
4719 658 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4720 :
4721 658 : j++;
4722 : }
4723 :
4724 320 : PQclear(res);
4725 320 : destroyPQExpBuffer(query);
4726 : }
4727 :
4728 : /*
4729 : * dumpPublicationNamespace
4730 : * dump the definition of the given publication schema mapping.
4731 : */
4732 : static void
4733 156 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4734 : {
4735 156 : DumpOptions *dopt = fout->dopt;
4736 156 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4737 156 : PublicationInfo *pubinfo = pubsinfo->publication;
4738 : PQExpBuffer query;
4739 : char *tag;
4740 :
4741 : /* Do nothing if not dumping schema */
4742 156 : if (!dopt->dumpSchema)
4743 24 : return;
4744 :
4745 132 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4746 :
4747 132 : query = createPQExpBuffer();
4748 :
4749 132 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4750 132 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4751 :
4752 : /*
4753 : * There is no point in creating drop query as the drop is done by schema
4754 : * drop.
4755 : */
4756 132 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4757 132 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4758 132 : ARCHIVE_OPTS(.tag = tag,
4759 : .namespace = schemainfo->dobj.name,
4760 : .owner = pubinfo->rolname,
4761 : .description = "PUBLICATION TABLES IN SCHEMA",
4762 : .section = SECTION_POST_DATA,
4763 : .createStmt = query->data));
4764 :
4765 : /* These objects can't currently have comments or seclabels */
4766 :
4767 132 : free(tag);
4768 132 : destroyPQExpBuffer(query);
4769 : }
4770 :
4771 : /*
4772 : * dumpPublicationTable
4773 : * dump the definition of the given publication table mapping
4774 : */
4775 : static void
4776 546 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4777 : {
4778 546 : DumpOptions *dopt = fout->dopt;
4779 546 : PublicationInfo *pubinfo = pubrinfo->publication;
4780 546 : TableInfo *tbinfo = pubrinfo->pubtable;
4781 : PQExpBuffer query;
4782 : char *tag;
4783 :
4784 : /* Do nothing if not dumping schema */
4785 546 : if (!dopt->dumpSchema)
4786 84 : return;
4787 :
4788 462 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4789 :
4790 462 : query = createPQExpBuffer();
4791 :
4792 462 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4793 462 : fmtId(pubinfo->dobj.name));
4794 462 : appendPQExpBuffer(query, " %s",
4795 462 : fmtQualifiedDumpable(tbinfo));
4796 :
4797 462 : if (pubrinfo->pubrattrs)
4798 132 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4799 :
4800 462 : if (pubrinfo->pubrelqual)
4801 : {
4802 : /*
4803 : * It's necessary to add parentheses around the expression because
4804 : * pg_get_expr won't supply the parentheses for things like WHERE
4805 : * TRUE.
4806 : */
4807 198 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4808 : }
4809 462 : appendPQExpBufferStr(query, ";\n");
4810 :
4811 : /*
4812 : * There is no point in creating a drop query as the drop is done by table
4813 : * drop. (If you think to change this, see also _printTocEntry().)
4814 : * Although this object doesn't really have ownership as such, set the
4815 : * owner field anyway to ensure that the command is run by the correct
4816 : * role at restore time.
4817 : */
4818 462 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4819 462 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4820 462 : ARCHIVE_OPTS(.tag = tag,
4821 : .namespace = tbinfo->dobj.namespace->dobj.name,
4822 : .owner = pubinfo->rolname,
4823 : .description = "PUBLICATION TABLE",
4824 : .section = SECTION_POST_DATA,
4825 : .createStmt = query->data));
4826 :
4827 : /* These objects can't currently have comments or seclabels */
4828 :
4829 462 : free(tag);
4830 462 : destroyPQExpBuffer(query);
4831 : }
4832 :
4833 : /*
4834 : * Is the currently connected user a superuser?
4835 : */
4836 : static bool
4837 320 : is_superuser(Archive *fout)
4838 : {
4839 320 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4840 : const char *val;
4841 :
4842 320 : val = PQparameterStatus(AH->connection, "is_superuser");
4843 :
4844 320 : if (val && strcmp(val, "on") == 0)
4845 314 : return true;
4846 :
4847 6 : return false;
4848 : }
4849 :
4850 : /*
4851 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4852 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4853 : * the setting query is effective only where available.
4854 : */
4855 : static void
4856 388 : set_restrict_relation_kind(Archive *AH, const char *value)
4857 : {
4858 388 : PQExpBuffer query = createPQExpBuffer();
4859 : PGresult *res;
4860 :
4861 388 : appendPQExpBuffer(query,
4862 : "SELECT set_config(name, '%s', false) "
4863 : "FROM pg_settings "
4864 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4865 : value);
4866 388 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4867 :
4868 388 : PQclear(res);
4869 388 : destroyPQExpBuffer(query);
4870 388 : }
4871 :
4872 : /*
4873 : * getSubscriptions
4874 : * get information about subscriptions
4875 : */
4876 : void
4877 320 : getSubscriptions(Archive *fout)
4878 : {
4879 320 : DumpOptions *dopt = fout->dopt;
4880 : PQExpBuffer query;
4881 : PGresult *res;
4882 : SubscriptionInfo *subinfo;
4883 : int i_tableoid;
4884 : int i_oid;
4885 : int i_subname;
4886 : int i_subowner;
4887 : int i_subbinary;
4888 : int i_substream;
4889 : int i_subtwophasestate;
4890 : int i_subdisableonerr;
4891 : int i_subpasswordrequired;
4892 : int i_subrunasowner;
4893 : int i_subconninfo;
4894 : int i_subslotname;
4895 : int i_subsynccommit;
4896 : int i_subpublications;
4897 : int i_suborigin;
4898 : int i_suboriginremotelsn;
4899 : int i_subenabled;
4900 : int i_subfailover;
4901 : int i,
4902 : ntups;
4903 :
4904 320 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4905 0 : return;
4906 :
4907 320 : if (!is_superuser(fout))
4908 : {
4909 : int n;
4910 :
4911 6 : res = ExecuteSqlQuery(fout,
4912 : "SELECT count(*) FROM pg_subscription "
4913 : "WHERE subdbid = (SELECT oid FROM pg_database"
4914 : " WHERE datname = current_database())",
4915 : PGRES_TUPLES_OK);
4916 6 : n = atoi(PQgetvalue(res, 0, 0));
4917 6 : if (n > 0)
4918 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4919 6 : PQclear(res);
4920 6 : return;
4921 : }
4922 :
4923 314 : query = createPQExpBuffer();
4924 :
4925 : /* Get the subscriptions in current database. */
4926 314 : appendPQExpBufferStr(query,
4927 : "SELECT s.tableoid, s.oid, s.subname,\n"
4928 : " s.subowner,\n"
4929 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4930 : " s.subpublications,\n");
4931 :
4932 314 : if (fout->remoteVersion >= 140000)
4933 314 : appendPQExpBufferStr(query, " s.subbinary,\n");
4934 : else
4935 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4936 :
4937 314 : if (fout->remoteVersion >= 140000)
4938 314 : appendPQExpBufferStr(query, " s.substream,\n");
4939 : else
4940 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
4941 :
4942 314 : if (fout->remoteVersion >= 150000)
4943 314 : appendPQExpBufferStr(query,
4944 : " s.subtwophasestate,\n"
4945 : " s.subdisableonerr,\n");
4946 : else
4947 0 : appendPQExpBuffer(query,
4948 : " '%c' AS subtwophasestate,\n"
4949 : " false AS subdisableonerr,\n",
4950 : LOGICALREP_TWOPHASE_STATE_DISABLED);
4951 :
4952 314 : if (fout->remoteVersion >= 160000)
4953 314 : appendPQExpBufferStr(query,
4954 : " s.subpasswordrequired,\n"
4955 : " s.subrunasowner,\n"
4956 : " s.suborigin,\n");
4957 : else
4958 0 : appendPQExpBuffer(query,
4959 : " 't' AS subpasswordrequired,\n"
4960 : " 't' AS subrunasowner,\n"
4961 : " '%s' AS suborigin,\n",
4962 : LOGICALREP_ORIGIN_ANY);
4963 :
4964 314 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4965 32 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
4966 : " s.subenabled,\n");
4967 : else
4968 282 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
4969 : " false AS subenabled,\n");
4970 :
4971 314 : if (fout->remoteVersion >= 170000)
4972 314 : appendPQExpBufferStr(query,
4973 : " s.subfailover\n");
4974 : else
4975 0 : appendPQExpBuffer(query,
4976 : " false AS subfailover\n");
4977 :
4978 314 : appendPQExpBufferStr(query,
4979 : "FROM pg_subscription s\n");
4980 :
4981 314 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4982 32 : appendPQExpBufferStr(query,
4983 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
4984 : " ON o.external_id = 'pg_' || s.oid::text \n");
4985 :
4986 314 : appendPQExpBufferStr(query,
4987 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4988 : " WHERE datname = current_database())");
4989 :
4990 314 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4991 :
4992 314 : ntups = PQntuples(res);
4993 :
4994 : /*
4995 : * Get subscription fields. We don't include subskiplsn in the dump as
4996 : * after restoring the dump this value may no longer be relevant.
4997 : */
4998 314 : i_tableoid = PQfnumber(res, "tableoid");
4999 314 : i_oid = PQfnumber(res, "oid");
5000 314 : i_subname = PQfnumber(res, "subname");
5001 314 : i_subowner = PQfnumber(res, "subowner");
5002 314 : i_subenabled = PQfnumber(res, "subenabled");
5003 314 : i_subbinary = PQfnumber(res, "subbinary");
5004 314 : i_substream = PQfnumber(res, "substream");
5005 314 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5006 314 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5007 314 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5008 314 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5009 314 : i_subfailover = PQfnumber(res, "subfailover");
5010 314 : i_subconninfo = PQfnumber(res, "subconninfo");
5011 314 : i_subslotname = PQfnumber(res, "subslotname");
5012 314 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5013 314 : i_subpublications = PQfnumber(res, "subpublications");
5014 314 : i_suborigin = PQfnumber(res, "suborigin");
5015 314 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5016 :
5017 314 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5018 :
5019 588 : for (i = 0; i < ntups; i++)
5020 : {
5021 274 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5022 274 : subinfo[i].dobj.catId.tableoid =
5023 274 : atooid(PQgetvalue(res, i, i_tableoid));
5024 274 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5025 274 : AssignDumpId(&subinfo[i].dobj);
5026 274 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5027 274 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5028 :
5029 274 : subinfo[i].subenabled =
5030 274 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5031 274 : subinfo[i].subbinary =
5032 274 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5033 274 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5034 274 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5035 274 : subinfo[i].subdisableonerr =
5036 274 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5037 274 : subinfo[i].subpasswordrequired =
5038 274 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5039 274 : subinfo[i].subrunasowner =
5040 274 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5041 274 : subinfo[i].subfailover =
5042 274 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5043 548 : subinfo[i].subconninfo =
5044 274 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5045 274 : if (PQgetisnull(res, i, i_subslotname))
5046 0 : subinfo[i].subslotname = NULL;
5047 : else
5048 274 : subinfo[i].subslotname =
5049 274 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5050 548 : subinfo[i].subsynccommit =
5051 274 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5052 548 : subinfo[i].subpublications =
5053 274 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5054 274 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5055 274 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5056 272 : subinfo[i].suboriginremotelsn = NULL;
5057 : else
5058 2 : subinfo[i].suboriginremotelsn =
5059 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5060 :
5061 : /* Decide whether we want to dump it */
5062 274 : selectDumpableObject(&(subinfo[i].dobj), fout);
5063 : }
5064 314 : PQclear(res);
5065 :
5066 314 : destroyPQExpBuffer(query);
5067 : }
5068 :
5069 : /*
5070 : * getSubscriptionTables
5071 : * Get information about subscription membership for dumpable tables. This
5072 : * will be used only in binary-upgrade mode for PG17 or later versions.
5073 : */
5074 : void
5075 320 : getSubscriptionTables(Archive *fout)
5076 : {
5077 320 : DumpOptions *dopt = fout->dopt;
5078 320 : SubscriptionInfo *subinfo = NULL;
5079 : SubRelInfo *subrinfo;
5080 : PGresult *res;
5081 : int i_srsubid;
5082 : int i_srrelid;
5083 : int i_srsubstate;
5084 : int i_srsublsn;
5085 : int ntups;
5086 320 : Oid last_srsubid = InvalidOid;
5087 :
5088 320 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5089 32 : fout->remoteVersion < 170000)
5090 288 : return;
5091 :
5092 32 : res = ExecuteSqlQuery(fout,
5093 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5094 : "FROM pg_catalog.pg_subscription_rel "
5095 : "ORDER BY srsubid",
5096 : PGRES_TUPLES_OK);
5097 32 : ntups = PQntuples(res);
5098 32 : if (ntups == 0)
5099 30 : goto cleanup;
5100 :
5101 : /* Get pg_subscription_rel attributes */
5102 2 : i_srsubid = PQfnumber(res, "srsubid");
5103 2 : i_srrelid = PQfnumber(res, "srrelid");
5104 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5105 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5106 :
5107 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5108 6 : for (int i = 0; i < ntups; i++)
5109 : {
5110 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5111 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5112 : TableInfo *tblinfo;
5113 :
5114 : /*
5115 : * If we switched to a new subscription, check if the subscription
5116 : * exists.
5117 : */
5118 4 : if (cur_srsubid != last_srsubid)
5119 : {
5120 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5121 4 : if (subinfo == NULL)
5122 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5123 :
5124 4 : last_srsubid = cur_srsubid;
5125 : }
5126 :
5127 4 : tblinfo = findTableByOid(relid);
5128 4 : if (tblinfo == NULL)
5129 0 : pg_fatal("failed sanity check, table with OID %u not found",
5130 : relid);
5131 :
5132 : /* OK, make a DumpableObject for this relationship */
5133 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5134 4 : subrinfo[i].dobj.catId.tableoid = relid;
5135 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5136 4 : AssignDumpId(&subrinfo[i].dobj);
5137 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5138 4 : subrinfo[i].tblinfo = tblinfo;
5139 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5140 4 : if (PQgetisnull(res, i, i_srsublsn))
5141 2 : subrinfo[i].srsublsn = NULL;
5142 : else
5143 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5144 :
5145 4 : subrinfo[i].subinfo = subinfo;
5146 :
5147 : /* Decide whether we want to dump it */
5148 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5149 : }
5150 :
5151 2 : cleanup:
5152 32 : PQclear(res);
5153 : }
5154 :
5155 : /*
5156 : * dumpSubscriptionTable
5157 : * Dump the definition of the given subscription table mapping. This will be
5158 : * used only in binary-upgrade mode for PG17 or later versions.
5159 : */
5160 : static void
5161 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5162 : {
5163 4 : DumpOptions *dopt = fout->dopt;
5164 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5165 : PQExpBuffer query;
5166 : char *tag;
5167 :
5168 : /* Do nothing if not dumping schema */
5169 4 : if (!dopt->dumpSchema)
5170 0 : return;
5171 :
5172 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5173 :
5174 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5175 :
5176 4 : query = createPQExpBuffer();
5177 :
5178 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5179 : {
5180 : /*
5181 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5182 : * to pg_subscription_rel table. This will be used only in
5183 : * binary-upgrade mode.
5184 : */
5185 4 : appendPQExpBufferStr(query,
5186 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5187 4 : appendPQExpBufferStr(query,
5188 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5189 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5190 4 : appendPQExpBuffer(query,
5191 : ", %u, '%c'",
5192 4 : subrinfo->tblinfo->dobj.catId.oid,
5193 4 : subrinfo->srsubstate);
5194 :
5195 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5196 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5197 : else
5198 2 : appendPQExpBuffer(query, ", NULL");
5199 :
5200 4 : appendPQExpBufferStr(query, ");\n");
5201 : }
5202 :
5203 : /*
5204 : * There is no point in creating a drop query as the drop is done by table
5205 : * drop. (If you think to change this, see also _printTocEntry().)
5206 : * Although this object doesn't really have ownership as such, set the
5207 : * owner field anyway to ensure that the command is run by the correct
5208 : * role at restore time.
5209 : */
5210 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5211 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5212 4 : ARCHIVE_OPTS(.tag = tag,
5213 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5214 : .owner = subinfo->rolname,
5215 : .description = "SUBSCRIPTION TABLE",
5216 : .section = SECTION_POST_DATA,
5217 : .createStmt = query->data));
5218 :
5219 : /* These objects can't currently have comments or seclabels */
5220 :
5221 4 : free(tag);
5222 4 : destroyPQExpBuffer(query);
5223 : }
5224 :
5225 : /*
5226 : * dumpSubscription
5227 : * dump the definition of the given subscription
5228 : */
5229 : static void
5230 238 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5231 : {
5232 238 : DumpOptions *dopt = fout->dopt;
5233 : PQExpBuffer delq;
5234 : PQExpBuffer query;
5235 : PQExpBuffer publications;
5236 : char *qsubname;
5237 238 : char **pubnames = NULL;
5238 238 : int npubnames = 0;
5239 : int i;
5240 :
5241 : /* Do nothing if not dumping schema */
5242 238 : if (!dopt->dumpSchema)
5243 36 : return;
5244 :
5245 202 : delq = createPQExpBuffer();
5246 202 : query = createPQExpBuffer();
5247 :
5248 202 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5249 :
5250 202 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5251 : qsubname);
5252 :
5253 202 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5254 : qsubname);
5255 202 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5256 :
5257 : /* Build list of quoted publications and append them to query. */
5258 202 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5259 0 : pg_fatal("could not parse %s array", "subpublications");
5260 :
5261 202 : publications = createPQExpBuffer();
5262 404 : for (i = 0; i < npubnames; i++)
5263 : {
5264 202 : if (i > 0)
5265 0 : appendPQExpBufferStr(publications, ", ");
5266 :
5267 202 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5268 : }
5269 :
5270 202 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5271 202 : if (subinfo->subslotname)
5272 202 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5273 : else
5274 0 : appendPQExpBufferStr(query, "NONE");
5275 :
5276 202 : if (subinfo->subbinary)
5277 0 : appendPQExpBufferStr(query, ", binary = true");
5278 :
5279 202 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5280 66 : appendPQExpBufferStr(query, ", streaming = on");
5281 136 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5282 70 : appendPQExpBufferStr(query, ", streaming = parallel");
5283 : else
5284 66 : appendPQExpBufferStr(query, ", streaming = off");
5285 :
5286 202 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5287 0 : appendPQExpBufferStr(query, ", two_phase = on");
5288 :
5289 202 : if (subinfo->subdisableonerr)
5290 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5291 :
5292 202 : if (!subinfo->subpasswordrequired)
5293 0 : appendPQExpBuffer(query, ", password_required = false");
5294 :
5295 202 : if (subinfo->subrunasowner)
5296 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5297 :
5298 202 : if (subinfo->subfailover)
5299 2 : appendPQExpBufferStr(query, ", failover = true");
5300 :
5301 202 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5302 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5303 :
5304 202 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5305 66 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5306 :
5307 202 : appendPQExpBufferStr(query, ");\n");
5308 :
5309 : /*
5310 : * In binary-upgrade mode, we allow the replication to continue after the
5311 : * upgrade.
5312 : */
5313 202 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5314 : {
5315 10 : if (subinfo->suboriginremotelsn)
5316 : {
5317 : /*
5318 : * Preserve the remote_lsn for the subscriber's replication
5319 : * origin. This value is required to start the replication from
5320 : * the position before the upgrade. This value will be stale if
5321 : * the publisher gets upgraded before the subscriber node.
5322 : * However, this shouldn't be a problem as the upgrade of the
5323 : * publisher ensures that all the transactions were replicated
5324 : * before upgrading it.
5325 : */
5326 2 : appendPQExpBufferStr(query,
5327 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5328 2 : appendPQExpBufferStr(query,
5329 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5330 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5331 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5332 : }
5333 :
5334 10 : if (subinfo->subenabled)
5335 : {
5336 : /*
5337 : * Enable the subscription to allow the replication to continue
5338 : * after the upgrade.
5339 : */
5340 2 : appendPQExpBufferStr(query,
5341 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5342 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5343 : }
5344 : }
5345 :
5346 202 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5347 202 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5348 202 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5349 : .owner = subinfo->rolname,
5350 : .description = "SUBSCRIPTION",
5351 : .section = SECTION_POST_DATA,
5352 : .createStmt = query->data,
5353 : .dropStmt = delq->data));
5354 :
5355 202 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5356 66 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5357 : NULL, subinfo->rolname,
5358 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5359 :
5360 202 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5361 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5362 : NULL, subinfo->rolname,
5363 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5364 :
5365 202 : destroyPQExpBuffer(publications);
5366 202 : free(pubnames);
5367 :
5368 202 : destroyPQExpBuffer(delq);
5369 202 : destroyPQExpBuffer(query);
5370 202 : free(qsubname);
5371 : }
5372 :
5373 : /*
5374 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5375 : * the object needs.
5376 : */
5377 : static void
5378 10286 : append_depends_on_extension(Archive *fout,
5379 : PQExpBuffer create,
5380 : const DumpableObject *dobj,
5381 : const char *catalog,
5382 : const char *keyword,
5383 : const char *objname)
5384 : {
5385 10286 : if (dobj->depends_on_ext)
5386 : {
5387 : char *nm;
5388 : PGresult *res;
5389 : PQExpBuffer query;
5390 : int ntups;
5391 : int i_extname;
5392 : int i;
5393 :
5394 : /* dodge fmtId() non-reentrancy */
5395 84 : nm = pg_strdup(objname);
5396 :
5397 84 : query = createPQExpBuffer();
5398 84 : appendPQExpBuffer(query,
5399 : "SELECT e.extname "
5400 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5401 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5402 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5403 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5404 : catalog,
5405 : dobj->catId.oid);
5406 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5407 84 : ntups = PQntuples(res);
5408 84 : i_extname = PQfnumber(res, "extname");
5409 168 : for (i = 0; i < ntups; i++)
5410 : {
5411 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5412 : keyword, nm,
5413 84 : fmtId(PQgetvalue(res, i, i_extname)));
5414 : }
5415 :
5416 84 : PQclear(res);
5417 84 : destroyPQExpBuffer(query);
5418 84 : pg_free(nm);
5419 : }
5420 10286 : }
5421 :
5422 : static Oid
5423 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5424 : {
5425 : /*
5426 : * If the old version didn't assign an array type, but the new version
5427 : * does, we must select an unused type OID to assign. This currently only
5428 : * happens for domains, when upgrading pre-v11 to v11 and up.
5429 : *
5430 : * Note: local state here is kind of ugly, but we must have some, since we
5431 : * mustn't choose the same unused OID more than once.
5432 : */
5433 : static Oid next_possible_free_oid = FirstNormalObjectId;
5434 : PGresult *res;
5435 : bool is_dup;
5436 :
5437 : do
5438 : {
5439 0 : ++next_possible_free_oid;
5440 0 : printfPQExpBuffer(upgrade_query,
5441 : "SELECT EXISTS(SELECT 1 "
5442 : "FROM pg_catalog.pg_type "
5443 : "WHERE oid = '%u'::pg_catalog.oid);",
5444 : next_possible_free_oid);
5445 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5446 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5447 0 : PQclear(res);
5448 0 : } while (is_dup);
5449 :
5450 0 : return next_possible_free_oid;
5451 : }
5452 :
5453 : static void
5454 1762 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5455 : PQExpBuffer upgrade_buffer,
5456 : Oid pg_type_oid,
5457 : bool force_array_type,
5458 : bool include_multirange_type)
5459 : {
5460 1762 : PQExpBuffer upgrade_query = createPQExpBuffer();
5461 : PGresult *res;
5462 : Oid pg_type_array_oid;
5463 : Oid pg_type_multirange_oid;
5464 : Oid pg_type_multirange_array_oid;
5465 : TypeInfo *tinfo;
5466 :
5467 1762 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5468 1762 : appendPQExpBuffer(upgrade_buffer,
5469 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5470 : pg_type_oid);
5471 :
5472 1762 : tinfo = findTypeByOid(pg_type_oid);
5473 1762 : if (tinfo)
5474 1762 : pg_type_array_oid = tinfo->typarray;
5475 : else
5476 0 : pg_type_array_oid = InvalidOid;
5477 :
5478 1762 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5479 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5480 :
5481 1762 : if (OidIsValid(pg_type_array_oid))
5482 : {
5483 1758 : appendPQExpBufferStr(upgrade_buffer,
5484 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5485 1758 : appendPQExpBuffer(upgrade_buffer,
5486 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5487 : pg_type_array_oid);
5488 : }
5489 :
5490 : /*
5491 : * Pre-set the multirange type oid and its own array type oid.
5492 : */
5493 1762 : if (include_multirange_type)
5494 : {
5495 16 : if (fout->remoteVersion >= 140000)
5496 : {
5497 16 : printfPQExpBuffer(upgrade_query,
5498 : "SELECT t.oid, t.typarray "
5499 : "FROM pg_catalog.pg_type t "
5500 : "JOIN pg_catalog.pg_range r "
5501 : "ON t.oid = r.rngmultitypid "
5502 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5503 : pg_type_oid);
5504 :
5505 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5506 :
5507 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5508 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5509 :
5510 16 : PQclear(res);
5511 : }
5512 : else
5513 : {
5514 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5515 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5516 : }
5517 :
5518 16 : appendPQExpBufferStr(upgrade_buffer,
5519 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5520 16 : appendPQExpBuffer(upgrade_buffer,
5521 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5522 : pg_type_multirange_oid);
5523 16 : appendPQExpBufferStr(upgrade_buffer,
5524 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5525 16 : appendPQExpBuffer(upgrade_buffer,
5526 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5527 : pg_type_multirange_array_oid);
5528 : }
5529 :
5530 1762 : destroyPQExpBuffer(upgrade_query);
5531 1762 : }
5532 :
5533 : static void
5534 1620 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5535 : PQExpBuffer upgrade_buffer,
5536 : const TableInfo *tbinfo)
5537 : {
5538 1620 : Oid pg_type_oid = tbinfo->reltype;
5539 :
5540 1620 : if (OidIsValid(pg_type_oid))
5541 1620 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5542 : pg_type_oid, false, false);
5543 1620 : }
5544 :
5545 : /*
5546 : * bsearch() comparator for BinaryUpgradeClassOidItem
5547 : */
5548 : static int
5549 23476 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5550 : {
5551 23476 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5552 23476 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5553 :
5554 23476 : return pg_cmp_u32(v1.oid, v2.oid);
5555 : }
5556 :
5557 : /*
5558 : * collectBinaryUpgradeClassOids
5559 : *
5560 : * Construct a table of pg_class information required for
5561 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5562 : * lookup.
5563 : */
5564 : static void
5565 32 : collectBinaryUpgradeClassOids(Archive *fout)
5566 : {
5567 : PGresult *res;
5568 : const char *query;
5569 :
5570 32 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5571 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5572 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5573 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5574 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5575 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5576 : "ORDER BY c.oid;";
5577 :
5578 32 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5579 :
5580 32 : nbinaryUpgradeClassOids = PQntuples(res);
5581 32 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5582 32 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5583 :
5584 16790 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5585 : {
5586 16758 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5587 16758 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5588 16758 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5589 16758 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5590 16758 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5591 16758 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5592 16758 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5593 : }
5594 :
5595 32 : PQclear(res);
5596 32 : }
5597 :
5598 : static void
5599 2374 : binary_upgrade_set_pg_class_oids(Archive *fout,
5600 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5601 : {
5602 2374 : BinaryUpgradeClassOidItem key = {0};
5603 : BinaryUpgradeClassOidItem *entry;
5604 :
5605 : Assert(binaryUpgradeClassOids);
5606 :
5607 : /*
5608 : * Preserve the OID and relfilenumber of the table, table's index, table's
5609 : * toast table and toast table's index if any.
5610 : *
5611 : * One complexity is that the current table definition might not require
5612 : * the creation of a TOAST table, but the old database might have a TOAST
5613 : * table that was created earlier, before some wide columns were dropped.
5614 : * By setting the TOAST oid we force creation of the TOAST heap and index
5615 : * by the new backend, so we can copy the files during binary upgrade
5616 : * without worrying about this case.
5617 : */
5618 2374 : key.oid = pg_class_oid;
5619 2374 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5620 : sizeof(BinaryUpgradeClassOidItem),
5621 : BinaryUpgradeClassOidItemCmp);
5622 :
5623 2374 : appendPQExpBufferStr(upgrade_buffer,
5624 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5625 :
5626 2374 : if (entry->relkind != RELKIND_INDEX &&
5627 1828 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5628 : {
5629 1778 : appendPQExpBuffer(upgrade_buffer,
5630 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5631 : pg_class_oid);
5632 :
5633 : /*
5634 : * Not every relation has storage. Also, in a pre-v12 database,
5635 : * partitioned tables have a relfilenumber, which should not be
5636 : * preserved when upgrading.
5637 : */
5638 1778 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5639 1464 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5640 1464 : appendPQExpBuffer(upgrade_buffer,
5641 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5642 : entry->relfilenumber);
5643 :
5644 : /*
5645 : * In a pre-v12 database, partitioned tables might be marked as having
5646 : * toast tables, but we should ignore them if so.
5647 : */
5648 1778 : if (OidIsValid(entry->toast_oid) &&
5649 552 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5650 : {
5651 552 : appendPQExpBuffer(upgrade_buffer,
5652 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5653 : entry->toast_oid);
5654 552 : appendPQExpBuffer(upgrade_buffer,
5655 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5656 : entry->toast_relfilenumber);
5657 :
5658 : /* every toast table has an index */
5659 552 : appendPQExpBuffer(upgrade_buffer,
5660 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5661 : entry->toast_index_oid);
5662 552 : appendPQExpBuffer(upgrade_buffer,
5663 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5664 : entry->toast_index_relfilenumber);
5665 : }
5666 : }
5667 : else
5668 : {
5669 : /* Preserve the OID and relfilenumber of the index */
5670 596 : appendPQExpBuffer(upgrade_buffer,
5671 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5672 : pg_class_oid);
5673 596 : appendPQExpBuffer(upgrade_buffer,
5674 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5675 : entry->relfilenumber);
5676 : }
5677 :
5678 2374 : appendPQExpBufferChar(upgrade_buffer, '\n');
5679 2374 : }
5680 :
5681 : /*
5682 : * If the DumpableObject is a member of an extension, add a suitable
5683 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5684 : *
5685 : * For somewhat historical reasons, objname should already be quoted,
5686 : * but not objnamespace (if any).
5687 : */
5688 : static void
5689 2782 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5690 : const DumpableObject *dobj,
5691 : const char *objtype,
5692 : const char *objname,
5693 : const char *objnamespace)
5694 : {
5695 2782 : DumpableObject *extobj = NULL;
5696 : int i;
5697 :
5698 2782 : if (!dobj->ext_member)
5699 2750 : return;
5700 :
5701 : /*
5702 : * Find the parent extension. We could avoid this search if we wanted to
5703 : * add a link field to DumpableObject, but the space costs of that would
5704 : * be considerable. We assume that member objects could only have a
5705 : * direct dependency on their own extension, not any others.
5706 : */
5707 32 : for (i = 0; i < dobj->nDeps; i++)
5708 : {
5709 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5710 32 : if (extobj && extobj->objType == DO_EXTENSION)
5711 32 : break;
5712 0 : extobj = NULL;
5713 : }
5714 32 : if (extobj == NULL)
5715 0 : pg_fatal("could not find parent extension for %s %s",
5716 : objtype, objname);
5717 :
5718 32 : appendPQExpBufferStr(upgrade_buffer,
5719 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5720 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5721 32 : fmtId(extobj->name),
5722 : objtype);
5723 32 : if (objnamespace && *objnamespace)
5724 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5725 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5726 : }
5727 :
5728 : /*
5729 : * getNamespaces:
5730 : * get information about all namespaces in the system catalogs
5731 : */
5732 : void
5733 322 : getNamespaces(Archive *fout)
5734 : {
5735 : PGresult *res;
5736 : int ntups;
5737 : int i;
5738 : PQExpBuffer query;
5739 : NamespaceInfo *nsinfo;
5740 : int i_tableoid;
5741 : int i_oid;
5742 : int i_nspname;
5743 : int i_nspowner;
5744 : int i_nspacl;
5745 : int i_acldefault;
5746 :
5747 322 : query = createPQExpBuffer();
5748 :
5749 : /*
5750 : * we fetch all namespaces including system ones, so that every object we
5751 : * read in can be linked to a containing namespace.
5752 : */
5753 322 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5754 : "n.nspowner, "
5755 : "n.nspacl, "
5756 : "acldefault('n', n.nspowner) AS acldefault "
5757 : "FROM pg_namespace n");
5758 :
5759 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5760 :
5761 322 : ntups = PQntuples(res);
5762 :
5763 322 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5764 :
5765 322 : i_tableoid = PQfnumber(res, "tableoid");
5766 322 : i_oid = PQfnumber(res, "oid");
5767 322 : i_nspname = PQfnumber(res, "nspname");
5768 322 : i_nspowner = PQfnumber(res, "nspowner");
5769 322 : i_nspacl = PQfnumber(res, "nspacl");
5770 322 : i_acldefault = PQfnumber(res, "acldefault");
5771 :
5772 2942 : for (i = 0; i < ntups; i++)
5773 : {
5774 : const char *nspowner;
5775 :
5776 2620 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5777 2620 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5778 2620 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5779 2620 : AssignDumpId(&nsinfo[i].dobj);
5780 2620 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5781 2620 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5782 2620 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5783 2620 : nsinfo[i].dacl.privtype = 0;
5784 2620 : nsinfo[i].dacl.initprivs = NULL;
5785 2620 : nspowner = PQgetvalue(res, i, i_nspowner);
5786 2620 : nsinfo[i].nspowner = atooid(nspowner);
5787 2620 : nsinfo[i].rolname = getRoleName(nspowner);
5788 :
5789 : /* Decide whether to dump this namespace */
5790 2620 : selectDumpableNamespace(&nsinfo[i], fout);
5791 :
5792 : /* Mark whether namespace has an ACL */
5793 2620 : if (!PQgetisnull(res, i, i_nspacl))
5794 1094 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5795 :
5796 : /*
5797 : * We ignore any pg_init_privs.initprivs entry for the public schema
5798 : * and assume a predetermined default, for several reasons. First,
5799 : * dropping and recreating the schema removes its pg_init_privs entry,
5800 : * but an empty destination database starts with this ACL nonetheless.
5801 : * Second, we support dump/reload of public schema ownership changes.
5802 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5803 : * initprivs continues to reflect the initial owner. Hence,
5804 : * synthesize the value that nspacl will have after the restore's
5805 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5806 : * match the source's ACL, even if the latter was an initdb-default
5807 : * ACL, which changed in v15. An upgrade pulls in changes to most
5808 : * system object ACLs that the DBA had not customized. We've made the
5809 : * public schema depart from that, because changing its ACL so easily
5810 : * breaks applications.
5811 : */
5812 2620 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5813 : {
5814 314 : PQExpBuffer aclarray = createPQExpBuffer();
5815 314 : PQExpBuffer aclitem = createPQExpBuffer();
5816 :
5817 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5818 314 : appendPQExpBufferChar(aclarray, '{');
5819 314 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5820 314 : appendPQExpBufferStr(aclitem, "=UC/");
5821 314 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5822 314 : appendPGArray(aclarray, aclitem->data);
5823 314 : resetPQExpBuffer(aclitem);
5824 314 : appendPQExpBufferStr(aclitem, "=U/");
5825 314 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5826 314 : appendPGArray(aclarray, aclitem->data);
5827 314 : appendPQExpBufferChar(aclarray, '}');
5828 :
5829 314 : nsinfo[i].dacl.privtype = 'i';
5830 314 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5831 314 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5832 :
5833 314 : destroyPQExpBuffer(aclarray);
5834 314 : destroyPQExpBuffer(aclitem);
5835 : }
5836 : }
5837 :
5838 322 : PQclear(res);
5839 322 : destroyPQExpBuffer(query);
5840 322 : }
5841 :
5842 : /*
5843 : * findNamespace:
5844 : * given a namespace OID, look up the info read by getNamespaces
5845 : */
5846 : static NamespaceInfo *
5847 1004126 : findNamespace(Oid nsoid)
5848 : {
5849 : NamespaceInfo *nsinfo;
5850 :
5851 1004126 : nsinfo = findNamespaceByOid(nsoid);
5852 1004126 : if (nsinfo == NULL)
5853 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5854 1004126 : return nsinfo;
5855 : }
5856 :
5857 : /*
5858 : * getExtensions:
5859 : * read all extensions in the system catalogs and return them in the
5860 : * ExtensionInfo* structure
5861 : *
5862 : * numExtensions is set to the number of extensions read in
5863 : */
5864 : ExtensionInfo *
5865 322 : getExtensions(Archive *fout, int *numExtensions)
5866 : {
5867 322 : DumpOptions *dopt = fout->dopt;
5868 : PGresult *res;
5869 : int ntups;
5870 : int i;
5871 : PQExpBuffer query;
5872 322 : ExtensionInfo *extinfo = NULL;
5873 : int i_tableoid;
5874 : int i_oid;
5875 : int i_extname;
5876 : int i_nspname;
5877 : int i_extrelocatable;
5878 : int i_extversion;
5879 : int i_extconfig;
5880 : int i_extcondition;
5881 :
5882 322 : query = createPQExpBuffer();
5883 :
5884 322 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5885 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5886 : "FROM pg_extension x "
5887 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5888 :
5889 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5890 :
5891 322 : ntups = PQntuples(res);
5892 322 : if (ntups == 0)
5893 0 : goto cleanup;
5894 :
5895 322 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5896 :
5897 322 : i_tableoid = PQfnumber(res, "tableoid");
5898 322 : i_oid = PQfnumber(res, "oid");
5899 322 : i_extname = PQfnumber(res, "extname");
5900 322 : i_nspname = PQfnumber(res, "nspname");
5901 322 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5902 322 : i_extversion = PQfnumber(res, "extversion");
5903 322 : i_extconfig = PQfnumber(res, "extconfig");
5904 322 : i_extcondition = PQfnumber(res, "extcondition");
5905 :
5906 694 : for (i = 0; i < ntups; i++)
5907 : {
5908 372 : extinfo[i].dobj.objType = DO_EXTENSION;
5909 372 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5910 372 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5911 372 : AssignDumpId(&extinfo[i].dobj);
5912 372 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5913 372 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5914 372 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5915 372 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5916 372 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5917 372 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5918 :
5919 : /* Decide whether we want to dump it */
5920 372 : selectDumpableExtension(&(extinfo[i]), dopt);
5921 : }
5922 :
5923 322 : cleanup:
5924 322 : PQclear(res);
5925 322 : destroyPQExpBuffer(query);
5926 :
5927 322 : *numExtensions = ntups;
5928 :
5929 322 : return extinfo;
5930 : }
5931 :
5932 : /*
5933 : * getTypes:
5934 : * get information about all types in the system catalogs
5935 : *
5936 : * NB: this must run after getFuncs() because we assume we can do
5937 : * findFuncByOid().
5938 : */
5939 : void
5940 320 : getTypes(Archive *fout)
5941 : {
5942 : PGresult *res;
5943 : int ntups;
5944 : int i;
5945 320 : PQExpBuffer query = createPQExpBuffer();
5946 : TypeInfo *tyinfo;
5947 : ShellTypeInfo *stinfo;
5948 : int i_tableoid;
5949 : int i_oid;
5950 : int i_typname;
5951 : int i_typnamespace;
5952 : int i_typacl;
5953 : int i_acldefault;
5954 : int i_typowner;
5955 : int i_typelem;
5956 : int i_typrelid;
5957 : int i_typrelkind;
5958 : int i_typtype;
5959 : int i_typisdefined;
5960 : int i_isarray;
5961 : int i_typarray;
5962 :
5963 : /*
5964 : * we include even the built-in types because those may be used as array
5965 : * elements by user-defined types
5966 : *
5967 : * we filter out the built-in types when we dump out the types
5968 : *
5969 : * same approach for undefined (shell) types and array types
5970 : *
5971 : * Note: as of 8.3 we can reliably detect whether a type is an
5972 : * auto-generated array type by checking the element type's typarray.
5973 : * (Before that the test is capable of generating false positives.) We
5974 : * still check for name beginning with '_', though, so as to avoid the
5975 : * cost of the subselect probe for all standard types. This would have to
5976 : * be revisited if the backend ever allows renaming of array types.
5977 : */
5978 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5979 : "typnamespace, typacl, "
5980 : "acldefault('T', typowner) AS acldefault, "
5981 : "typowner, "
5982 : "typelem, typrelid, typarray, "
5983 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5984 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5985 : "typtype, typisdefined, "
5986 : "typname[0] = '_' AND typelem != 0 AND "
5987 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5988 : "FROM pg_type");
5989 :
5990 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5991 :
5992 320 : ntups = PQntuples(res);
5993 :
5994 320 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5995 :
5996 320 : i_tableoid = PQfnumber(res, "tableoid");
5997 320 : i_oid = PQfnumber(res, "oid");
5998 320 : i_typname = PQfnumber(res, "typname");
5999 320 : i_typnamespace = PQfnumber(res, "typnamespace");
6000 320 : i_typacl = PQfnumber(res, "typacl");
6001 320 : i_acldefault = PQfnumber(res, "acldefault");
6002 320 : i_typowner = PQfnumber(res, "typowner");
6003 320 : i_typelem = PQfnumber(res, "typelem");
6004 320 : i_typrelid = PQfnumber(res, "typrelid");
6005 320 : i_typrelkind = PQfnumber(res, "typrelkind");
6006 320 : i_typtype = PQfnumber(res, "typtype");
6007 320 : i_typisdefined = PQfnumber(res, "typisdefined");
6008 320 : i_isarray = PQfnumber(res, "isarray");
6009 320 : i_typarray = PQfnumber(res, "typarray");
6010 :
6011 232308 : for (i = 0; i < ntups; i++)
6012 : {
6013 231988 : tyinfo[i].dobj.objType = DO_TYPE;
6014 231988 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6015 231988 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6016 231988 : AssignDumpId(&tyinfo[i].dobj);
6017 231988 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6018 463976 : tyinfo[i].dobj.namespace =
6019 231988 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6020 231988 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6021 231988 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6022 231988 : tyinfo[i].dacl.privtype = 0;
6023 231988 : tyinfo[i].dacl.initprivs = NULL;
6024 231988 : tyinfo[i].ftypname = NULL; /* may get filled later */
6025 231988 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6026 231988 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6027 231988 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6028 231988 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6029 231988 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6030 231988 : tyinfo[i].shellType = NULL;
6031 :
6032 231988 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6033 231880 : tyinfo[i].isDefined = true;
6034 : else
6035 108 : tyinfo[i].isDefined = false;
6036 :
6037 231988 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6038 111300 : tyinfo[i].isArray = true;
6039 : else
6040 120688 : tyinfo[i].isArray = false;
6041 :
6042 231988 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6043 :
6044 231988 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6045 2192 : tyinfo[i].isMultirange = true;
6046 : else
6047 229796 : tyinfo[i].isMultirange = false;
6048 :
6049 : /* Decide whether we want to dump it */
6050 231988 : selectDumpableType(&tyinfo[i], fout);
6051 :
6052 : /* Mark whether type has an ACL */
6053 231988 : if (!PQgetisnull(res, i, i_typacl))
6054 426 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6055 :
6056 : /*
6057 : * If it's a domain, fetch info about its constraints, if any
6058 : */
6059 231988 : tyinfo[i].nDomChecks = 0;
6060 231988 : tyinfo[i].domChecks = NULL;
6061 231988 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6062 28530 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6063 290 : getDomainConstraints(fout, &(tyinfo[i]));
6064 :
6065 : /*
6066 : * If it's a base type, make a DumpableObject representing a shell
6067 : * definition of the type. We will need to dump that ahead of the I/O
6068 : * functions for the type. Similarly, range types need a shell
6069 : * definition in case they have a canonicalize function.
6070 : *
6071 : * Note: the shell type doesn't have a catId. You might think it
6072 : * should copy the base type's catId, but then it might capture the
6073 : * pg_depend entries for the type, which we don't want.
6074 : */
6075 231988 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6076 28530 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6077 13842 : tyinfo[i].typtype == TYPTYPE_RANGE))
6078 : {
6079 14944 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6080 14944 : stinfo->dobj.objType = DO_SHELL_TYPE;
6081 14944 : stinfo->dobj.catId = nilCatalogId;
6082 14944 : AssignDumpId(&stinfo->dobj);
6083 14944 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6084 14944 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6085 14944 : stinfo->baseType = &(tyinfo[i]);
6086 14944 : tyinfo[i].shellType = stinfo;
6087 :
6088 : /*
6089 : * Initially mark the shell type as not to be dumped. We'll only
6090 : * dump it if the I/O or canonicalize functions need to be dumped;
6091 : * this is taken care of while sorting dependencies.
6092 : */
6093 14944 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6094 : }
6095 : }
6096 :
6097 320 : PQclear(res);
6098 :
6099 320 : destroyPQExpBuffer(query);
6100 320 : }
6101 :
6102 : /*
6103 : * getOperators:
6104 : * get information about all operators in the system catalogs
6105 : */
6106 : void
6107 320 : getOperators(Archive *fout)
6108 : {
6109 : PGresult *res;
6110 : int ntups;
6111 : int i;
6112 320 : PQExpBuffer query = createPQExpBuffer();
6113 : OprInfo *oprinfo;
6114 : int i_tableoid;
6115 : int i_oid;
6116 : int i_oprname;
6117 : int i_oprnamespace;
6118 : int i_oprowner;
6119 : int i_oprkind;
6120 : int i_oprcode;
6121 :
6122 : /*
6123 : * find all operators, including builtin operators; we filter out
6124 : * system-defined operators at dump-out time.
6125 : */
6126 :
6127 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6128 : "oprnamespace, "
6129 : "oprowner, "
6130 : "oprkind, "
6131 : "oprcode::oid AS oprcode "
6132 : "FROM pg_operator");
6133 :
6134 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6135 :
6136 320 : ntups = PQntuples(res);
6137 :
6138 320 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6139 :
6140 320 : i_tableoid = PQfnumber(res, "tableoid");
6141 320 : i_oid = PQfnumber(res, "oid");
6142 320 : i_oprname = PQfnumber(res, "oprname");
6143 320 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6144 320 : i_oprowner = PQfnumber(res, "oprowner");
6145 320 : i_oprkind = PQfnumber(res, "oprkind");
6146 320 : i_oprcode = PQfnumber(res, "oprcode");
6147 :
6148 256288 : for (i = 0; i < ntups; i++)
6149 : {
6150 255968 : oprinfo[i].dobj.objType = DO_OPERATOR;
6151 255968 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6152 255968 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6153 255968 : AssignDumpId(&oprinfo[i].dobj);
6154 255968 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6155 511936 : oprinfo[i].dobj.namespace =
6156 255968 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6157 255968 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6158 255968 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6159 255968 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6160 :
6161 : /* Decide whether we want to dump it */
6162 255968 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6163 : }
6164 :
6165 320 : PQclear(res);
6166 :
6167 320 : destroyPQExpBuffer(query);
6168 320 : }
6169 :
6170 : /*
6171 : * getCollations:
6172 : * get information about all collations in the system catalogs
6173 : */
6174 : void
6175 320 : getCollations(Archive *fout)
6176 : {
6177 : PGresult *res;
6178 : int ntups;
6179 : int i;
6180 : PQExpBuffer query;
6181 : CollInfo *collinfo;
6182 : int i_tableoid;
6183 : int i_oid;
6184 : int i_collname;
6185 : int i_collnamespace;
6186 : int i_collowner;
6187 :
6188 320 : query = createPQExpBuffer();
6189 :
6190 : /*
6191 : * find all collations, including builtin collations; we filter out
6192 : * system-defined collations at dump-out time.
6193 : */
6194 :
6195 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6196 : "collnamespace, "
6197 : "collowner "
6198 : "FROM pg_collation");
6199 :
6200 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6201 :
6202 320 : ntups = PQntuples(res);
6203 :
6204 320 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6205 :
6206 320 : i_tableoid = PQfnumber(res, "tableoid");
6207 320 : i_oid = PQfnumber(res, "oid");
6208 320 : i_collname = PQfnumber(res, "collname");
6209 320 : i_collnamespace = PQfnumber(res, "collnamespace");
6210 320 : i_collowner = PQfnumber(res, "collowner");
6211 :
6212 254310 : for (i = 0; i < ntups; i++)
6213 : {
6214 253990 : collinfo[i].dobj.objType = DO_COLLATION;
6215 253990 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6216 253990 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6217 253990 : AssignDumpId(&collinfo[i].dobj);
6218 253990 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6219 507980 : collinfo[i].dobj.namespace =
6220 253990 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6221 253990 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6222 :
6223 : /* Decide whether we want to dump it */
6224 253990 : selectDumpableObject(&(collinfo[i].dobj), fout);
6225 : }
6226 :
6227 320 : PQclear(res);
6228 :
6229 320 : destroyPQExpBuffer(query);
6230 320 : }
6231 :
6232 : /*
6233 : * getConversions:
6234 : * get information about all conversions in the system catalogs
6235 : */
6236 : void
6237 320 : getConversions(Archive *fout)
6238 : {
6239 : PGresult *res;
6240 : int ntups;
6241 : int i;
6242 : PQExpBuffer query;
6243 : ConvInfo *convinfo;
6244 : int i_tableoid;
6245 : int i_oid;
6246 : int i_conname;
6247 : int i_connamespace;
6248 : int i_conowner;
6249 :
6250 320 : query = createPQExpBuffer();
6251 :
6252 : /*
6253 : * find all conversions, including builtin conversions; we filter out
6254 : * system-defined conversions at dump-out time.
6255 : */
6256 :
6257 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6258 : "connamespace, "
6259 : "conowner "
6260 : "FROM pg_conversion");
6261 :
6262 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6263 :
6264 320 : ntups = PQntuples(res);
6265 :
6266 320 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6267 :
6268 320 : i_tableoid = PQfnumber(res, "tableoid");
6269 320 : i_oid = PQfnumber(res, "oid");
6270 320 : i_conname = PQfnumber(res, "conname");
6271 320 : i_connamespace = PQfnumber(res, "connamespace");
6272 320 : i_conowner = PQfnumber(res, "conowner");
6273 :
6274 41374 : for (i = 0; i < ntups; i++)
6275 : {
6276 41054 : convinfo[i].dobj.objType = DO_CONVERSION;
6277 41054 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6278 41054 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6279 41054 : AssignDumpId(&convinfo[i].dobj);
6280 41054 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6281 82108 : convinfo[i].dobj.namespace =
6282 41054 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6283 41054 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6284 :
6285 : /* Decide whether we want to dump it */
6286 41054 : selectDumpableObject(&(convinfo[i].dobj), fout);
6287 : }
6288 :
6289 320 : PQclear(res);
6290 :
6291 320 : destroyPQExpBuffer(query);
6292 320 : }
6293 :
6294 : /*
6295 : * getAccessMethods:
6296 : * get information about all user-defined access methods
6297 : */
6298 : void
6299 320 : getAccessMethods(Archive *fout)
6300 : {
6301 : PGresult *res;
6302 : int ntups;
6303 : int i;
6304 : PQExpBuffer query;
6305 : AccessMethodInfo *aminfo;
6306 : int i_tableoid;
6307 : int i_oid;
6308 : int i_amname;
6309 : int i_amhandler;
6310 : int i_amtype;
6311 :
6312 : /* Before 9.6, there are no user-defined access methods */
6313 320 : if (fout->remoteVersion < 90600)
6314 0 : return;
6315 :
6316 320 : query = createPQExpBuffer();
6317 :
6318 : /* Select all access methods from pg_am table */
6319 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6320 : "amhandler::pg_catalog.regproc AS amhandler "
6321 : "FROM pg_am");
6322 :
6323 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6324 :
6325 320 : ntups = PQntuples(res);
6326 :
6327 320 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6328 :
6329 320 : i_tableoid = PQfnumber(res, "tableoid");
6330 320 : i_oid = PQfnumber(res, "oid");
6331 320 : i_amname = PQfnumber(res, "amname");
6332 320 : i_amhandler = PQfnumber(res, "amhandler");
6333 320 : i_amtype = PQfnumber(res, "amtype");
6334 :
6335 2812 : for (i = 0; i < ntups; i++)
6336 : {
6337 2492 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6338 2492 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6339 2492 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6340 2492 : AssignDumpId(&aminfo[i].dobj);
6341 2492 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6342 2492 : aminfo[i].dobj.namespace = NULL;
6343 2492 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6344 2492 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6345 :
6346 : /* Decide whether we want to dump it */
6347 2492 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6348 : }
6349 :
6350 320 : PQclear(res);
6351 :
6352 320 : destroyPQExpBuffer(query);
6353 : }
6354 :
6355 :
6356 : /*
6357 : * getOpclasses:
6358 : * get information about all opclasses in the system catalogs
6359 : */
6360 : void
6361 320 : getOpclasses(Archive *fout)
6362 : {
6363 : PGresult *res;
6364 : int ntups;
6365 : int i;
6366 320 : PQExpBuffer query = createPQExpBuffer();
6367 : OpclassInfo *opcinfo;
6368 : int i_tableoid;
6369 : int i_oid;
6370 : int i_opcname;
6371 : int i_opcnamespace;
6372 : int i_opcowner;
6373 :
6374 : /*
6375 : * find all opclasses, including builtin opclasses; we filter out
6376 : * system-defined opclasses at dump-out time.
6377 : */
6378 :
6379 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6380 : "opcnamespace, "
6381 : "opcowner "
6382 : "FROM pg_opclass");
6383 :
6384 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6385 :
6386 320 : ntups = PQntuples(res);
6387 :
6388 320 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6389 :
6390 320 : i_tableoid = PQfnumber(res, "tableoid");
6391 320 : i_oid = PQfnumber(res, "oid");
6392 320 : i_opcname = PQfnumber(res, "opcname");
6393 320 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6394 320 : i_opcowner = PQfnumber(res, "opcowner");
6395 :
6396 57284 : for (i = 0; i < ntups; i++)
6397 : {
6398 56964 : opcinfo[i].dobj.objType = DO_OPCLASS;
6399 56964 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6400 56964 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6401 56964 : AssignDumpId(&opcinfo[i].dobj);
6402 56964 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6403 113928 : opcinfo[i].dobj.namespace =
6404 56964 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6405 56964 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6406 :
6407 : /* Decide whether we want to dump it */
6408 56964 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6409 : }
6410 :
6411 320 : PQclear(res);
6412 :
6413 320 : destroyPQExpBuffer(query);
6414 320 : }
6415 :
6416 : /*
6417 : * getOpfamilies:
6418 : * get information about all opfamilies in the system catalogs
6419 : */
6420 : void
6421 320 : getOpfamilies(Archive *fout)
6422 : {
6423 : PGresult *res;
6424 : int ntups;
6425 : int i;
6426 : PQExpBuffer query;
6427 : OpfamilyInfo *opfinfo;
6428 : int i_tableoid;
6429 : int i_oid;
6430 : int i_opfname;
6431 : int i_opfnamespace;
6432 : int i_opfowner;
6433 :
6434 320 : query = createPQExpBuffer();
6435 :
6436 : /*
6437 : * find all opfamilies, including builtin opfamilies; we filter out
6438 : * system-defined opfamilies at dump-out time.
6439 : */
6440 :
6441 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6442 : "opfnamespace, "
6443 : "opfowner "
6444 : "FROM pg_opfamily");
6445 :
6446 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6447 :
6448 320 : ntups = PQntuples(res);
6449 :
6450 320 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6451 :
6452 320 : i_tableoid = PQfnumber(res, "tableoid");
6453 320 : i_oid = PQfnumber(res, "oid");
6454 320 : i_opfname = PQfnumber(res, "opfname");
6455 320 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6456 320 : i_opfowner = PQfnumber(res, "opfowner");
6457 :
6458 47326 : for (i = 0; i < ntups; i++)
6459 : {
6460 47006 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6461 47006 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6462 47006 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6463 47006 : AssignDumpId(&opfinfo[i].dobj);
6464 47006 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6465 94012 : opfinfo[i].dobj.namespace =
6466 47006 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6467 47006 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6468 :
6469 : /* Decide whether we want to dump it */
6470 47006 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6471 : }
6472 :
6473 320 : PQclear(res);
6474 :
6475 320 : destroyPQExpBuffer(query);
6476 320 : }
6477 :
6478 : /*
6479 : * getAggregates:
6480 : * get information about all user-defined aggregates in the system catalogs
6481 : */
6482 : void
6483 320 : getAggregates(Archive *fout)
6484 : {
6485 320 : DumpOptions *dopt = fout->dopt;
6486 : PGresult *res;
6487 : int ntups;
6488 : int i;
6489 320 : PQExpBuffer query = createPQExpBuffer();
6490 : AggInfo *agginfo;
6491 : int i_tableoid;
6492 : int i_oid;
6493 : int i_aggname;
6494 : int i_aggnamespace;
6495 : int i_pronargs;
6496 : int i_proargtypes;
6497 : int i_proowner;
6498 : int i_aggacl;
6499 : int i_acldefault;
6500 :
6501 : /*
6502 : * Find all interesting aggregates. See comment in getFuncs() for the
6503 : * rationale behind the filtering logic.
6504 : */
6505 320 : if (fout->remoteVersion >= 90600)
6506 : {
6507 : const char *agg_check;
6508 :
6509 640 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6510 320 : : "p.proisagg");
6511 :
6512 320 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6513 : "p.proname AS aggname, "
6514 : "p.pronamespace AS aggnamespace, "
6515 : "p.pronargs, p.proargtypes, "
6516 : "p.proowner, "
6517 : "p.proacl AS aggacl, "
6518 : "acldefault('f', p.proowner) AS acldefault "
6519 : "FROM pg_proc p "
6520 : "LEFT JOIN pg_init_privs pip ON "
6521 : "(p.oid = pip.objoid "
6522 : "AND pip.classoid = 'pg_proc'::regclass "
6523 : "AND pip.objsubid = 0) "
6524 : "WHERE %s AND ("
6525 : "p.pronamespace != "
6526 : "(SELECT oid FROM pg_namespace "
6527 : "WHERE nspname = 'pg_catalog') OR "
6528 : "p.proacl IS DISTINCT FROM pip.initprivs",
6529 : agg_check);
6530 320 : if (dopt->binary_upgrade)
6531 32 : appendPQExpBufferStr(query,
6532 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6533 : "classid = 'pg_proc'::regclass AND "
6534 : "objid = p.oid AND "
6535 : "refclassid = 'pg_extension'::regclass AND "
6536 : "deptype = 'e')");
6537 320 : appendPQExpBufferChar(query, ')');
6538 : }
6539 : else
6540 : {
6541 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6542 : "pronamespace AS aggnamespace, "
6543 : "pronargs, proargtypes, "
6544 : "proowner, "
6545 : "proacl AS aggacl, "
6546 : "acldefault('f', proowner) AS acldefault "
6547 : "FROM pg_proc p "
6548 : "WHERE proisagg AND ("
6549 : "pronamespace != "
6550 : "(SELECT oid FROM pg_namespace "
6551 : "WHERE nspname = 'pg_catalog')");
6552 0 : if (dopt->binary_upgrade)
6553 0 : appendPQExpBufferStr(query,
6554 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6555 : "classid = 'pg_proc'::regclass AND "
6556 : "objid = p.oid AND "
6557 : "refclassid = 'pg_extension'::regclass AND "
6558 : "deptype = 'e')");
6559 0 : appendPQExpBufferChar(query, ')');
6560 : }
6561 :
6562 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6563 :
6564 320 : ntups = PQntuples(res);
6565 :
6566 320 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6567 :
6568 320 : i_tableoid = PQfnumber(res, "tableoid");
6569 320 : i_oid = PQfnumber(res, "oid");
6570 320 : i_aggname = PQfnumber(res, "aggname");
6571 320 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6572 320 : i_pronargs = PQfnumber(res, "pronargs");
6573 320 : i_proargtypes = PQfnumber(res, "proargtypes");
6574 320 : i_proowner = PQfnumber(res, "proowner");
6575 320 : i_aggacl = PQfnumber(res, "aggacl");
6576 320 : i_acldefault = PQfnumber(res, "acldefault");
6577 :
6578 1122 : for (i = 0; i < ntups; i++)
6579 : {
6580 802 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6581 802 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6582 802 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6583 802 : AssignDumpId(&agginfo[i].aggfn.dobj);
6584 802 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6585 1604 : agginfo[i].aggfn.dobj.namespace =
6586 802 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6587 802 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6588 802 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6589 802 : agginfo[i].aggfn.dacl.privtype = 0;
6590 802 : agginfo[i].aggfn.dacl.initprivs = NULL;
6591 802 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6592 802 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6593 802 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6594 802 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6595 802 : if (agginfo[i].aggfn.nargs == 0)
6596 112 : agginfo[i].aggfn.argtypes = NULL;
6597 : else
6598 : {
6599 690 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6600 690 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6601 690 : agginfo[i].aggfn.argtypes,
6602 690 : agginfo[i].aggfn.nargs);
6603 : }
6604 802 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6605 :
6606 : /* Decide whether we want to dump it */
6607 802 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6608 :
6609 : /* Mark whether aggregate has an ACL */
6610 802 : if (!PQgetisnull(res, i, i_aggacl))
6611 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6612 : }
6613 :
6614 320 : PQclear(res);
6615 :
6616 320 : destroyPQExpBuffer(query);
6617 320 : }
6618 :
6619 : /*
6620 : * getFuncs:
6621 : * get information about all user-defined functions in the system catalogs
6622 : */
6623 : void
6624 320 : getFuncs(Archive *fout)
6625 : {
6626 320 : DumpOptions *dopt = fout->dopt;
6627 : PGresult *res;
6628 : int ntups;
6629 : int i;
6630 320 : PQExpBuffer query = createPQExpBuffer();
6631 : FuncInfo *finfo;
6632 : int i_tableoid;
6633 : int i_oid;
6634 : int i_proname;
6635 : int i_pronamespace;
6636 : int i_proowner;
6637 : int i_prolang;
6638 : int i_pronargs;
6639 : int i_proargtypes;
6640 : int i_prorettype;
6641 : int i_proacl;
6642 : int i_acldefault;
6643 :
6644 : /*
6645 : * Find all interesting functions. This is a bit complicated:
6646 : *
6647 : * 1. Always exclude aggregates; those are handled elsewhere.
6648 : *
6649 : * 2. Always exclude functions that are internally dependent on something
6650 : * else, since presumably those will be created as a result of creating
6651 : * the something else. This currently acts only to suppress constructor
6652 : * functions for range types. Note this is OK only because the
6653 : * constructors don't have any dependencies the range type doesn't have;
6654 : * otherwise we might not get creation ordering correct.
6655 : *
6656 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6657 : * they're members of extensions and we are in binary-upgrade mode then
6658 : * include them, since we want to dump extension members individually in
6659 : * that mode. Also, if they are used by casts or transforms then we need
6660 : * to gather the information about them, though they won't be dumped if
6661 : * they are built-in. Also, in 9.6 and up, include functions in
6662 : * pg_catalog if they have an ACL different from what's shown in
6663 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6664 : */
6665 320 : if (fout->remoteVersion >= 90600)
6666 : {
6667 : const char *not_agg_check;
6668 :
6669 640 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6670 320 : : "NOT p.proisagg");
6671 :
6672 320 : appendPQExpBuffer(query,
6673 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6674 : "p.pronargs, p.proargtypes, p.prorettype, "
6675 : "p.proacl, "
6676 : "acldefault('f', p.proowner) AS acldefault, "
6677 : "p.pronamespace, "
6678 : "p.proowner "
6679 : "FROM pg_proc p "
6680 : "LEFT JOIN pg_init_privs pip ON "
6681 : "(p.oid = pip.objoid "
6682 : "AND pip.classoid = 'pg_proc'::regclass "
6683 : "AND pip.objsubid = 0) "
6684 : "WHERE %s"
6685 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6686 : "WHERE classid = 'pg_proc'::regclass AND "
6687 : "objid = p.oid AND deptype = 'i')"
6688 : "\n AND ("
6689 : "\n pronamespace != "
6690 : "(SELECT oid FROM pg_namespace "
6691 : "WHERE nspname = 'pg_catalog')"
6692 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6693 : "\n WHERE pg_cast.oid > %u "
6694 : "\n AND p.oid = pg_cast.castfunc)"
6695 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6696 : "\n WHERE pg_transform.oid > %u AND "
6697 : "\n (p.oid = pg_transform.trffromsql"
6698 : "\n OR p.oid = pg_transform.trftosql))",
6699 : not_agg_check,
6700 : g_last_builtin_oid,
6701 : g_last_builtin_oid);
6702 320 : if (dopt->binary_upgrade)
6703 32 : appendPQExpBufferStr(query,
6704 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6705 : "classid = 'pg_proc'::regclass AND "
6706 : "objid = p.oid AND "
6707 : "refclassid = 'pg_extension'::regclass AND "
6708 : "deptype = 'e')");
6709 320 : appendPQExpBufferStr(query,
6710 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6711 320 : appendPQExpBufferChar(query, ')');
6712 : }
6713 : else
6714 : {
6715 0 : appendPQExpBuffer(query,
6716 : "SELECT tableoid, oid, proname, prolang, "
6717 : "pronargs, proargtypes, prorettype, proacl, "
6718 : "acldefault('f', proowner) AS acldefault, "
6719 : "pronamespace, "
6720 : "proowner "
6721 : "FROM pg_proc p "
6722 : "WHERE NOT proisagg"
6723 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6724 : "WHERE classid = 'pg_proc'::regclass AND "
6725 : "objid = p.oid AND deptype = 'i')"
6726 : "\n AND ("
6727 : "\n pronamespace != "
6728 : "(SELECT oid FROM pg_namespace "
6729 : "WHERE nspname = 'pg_catalog')"
6730 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6731 : "\n WHERE pg_cast.oid > '%u'::oid"
6732 : "\n AND p.oid = pg_cast.castfunc)",
6733 : g_last_builtin_oid);
6734 :
6735 0 : if (fout->remoteVersion >= 90500)
6736 0 : appendPQExpBuffer(query,
6737 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6738 : "\n WHERE pg_transform.oid > '%u'::oid"
6739 : "\n AND (p.oid = pg_transform.trffromsql"
6740 : "\n OR p.oid = pg_transform.trftosql))",
6741 : g_last_builtin_oid);
6742 :
6743 0 : if (dopt->binary_upgrade)
6744 0 : appendPQExpBufferStr(query,
6745 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6746 : "classid = 'pg_proc'::regclass AND "
6747 : "objid = p.oid AND "
6748 : "refclassid = 'pg_extension'::regclass AND "
6749 : "deptype = 'e')");
6750 0 : appendPQExpBufferChar(query, ')');
6751 : }
6752 :
6753 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6754 :
6755 320 : ntups = PQntuples(res);
6756 :
6757 320 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6758 :
6759 320 : i_tableoid = PQfnumber(res, "tableoid");
6760 320 : i_oid = PQfnumber(res, "oid");
6761 320 : i_proname = PQfnumber(res, "proname");
6762 320 : i_pronamespace = PQfnumber(res, "pronamespace");
6763 320 : i_proowner = PQfnumber(res, "proowner");
6764 320 : i_prolang = PQfnumber(res, "prolang");
6765 320 : i_pronargs = PQfnumber(res, "pronargs");
6766 320 : i_proargtypes = PQfnumber(res, "proargtypes");
6767 320 : i_prorettype = PQfnumber(res, "prorettype");
6768 320 : i_proacl = PQfnumber(res, "proacl");
6769 320 : i_acldefault = PQfnumber(res, "acldefault");
6770 :
6771 9208 : for (i = 0; i < ntups; i++)
6772 : {
6773 8888 : finfo[i].dobj.objType = DO_FUNC;
6774 8888 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6775 8888 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6776 8888 : AssignDumpId(&finfo[i].dobj);
6777 8888 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6778 17776 : finfo[i].dobj.namespace =
6779 8888 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6780 8888 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6781 8888 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6782 8888 : finfo[i].dacl.privtype = 0;
6783 8888 : finfo[i].dacl.initprivs = NULL;
6784 8888 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6785 8888 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6786 8888 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6787 8888 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6788 8888 : if (finfo[i].nargs == 0)
6789 2142 : finfo[i].argtypes = NULL;
6790 : else
6791 : {
6792 6746 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6793 6746 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6794 6746 : finfo[i].argtypes, finfo[i].nargs);
6795 : }
6796 8888 : finfo[i].postponed_def = false; /* might get set during sort */
6797 :
6798 : /* Decide whether we want to dump it */
6799 8888 : selectDumpableObject(&(finfo[i].dobj), fout);
6800 :
6801 : /* Mark whether function has an ACL */
6802 8888 : if (!PQgetisnull(res, i, i_proacl))
6803 288 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6804 : }
6805 :
6806 320 : PQclear(res);
6807 :
6808 320 : destroyPQExpBuffer(query);
6809 320 : }
6810 :
6811 : /*
6812 : * getRelationStatistics
6813 : * register the statistics object as a dependent of the relation.
6814 : *
6815 : */
6816 : static RelStatsInfo *
6817 18512 : getRelationStatistics(Archive *fout, DumpableObject *rel, char relkind)
6818 : {
6819 18512 : if (!fout->dopt->dumpStatistics)
6820 5072 : return NULL;
6821 :
6822 13440 : if ((relkind == RELKIND_RELATION) ||
6823 6112 : (relkind == RELKIND_PARTITIONED_TABLE) ||
6824 3110 : (relkind == RELKIND_INDEX) ||
6825 2222 : (relkind == RELKIND_PARTITIONED_INDEX) ||
6826 : (relkind == RELKIND_MATVIEW))
6827 : {
6828 11986 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
6829 11986 : DumpableObject *dobj = &info->dobj;
6830 :
6831 11986 : dobj->objType = DO_REL_STATS;
6832 11986 : dobj->catId.tableoid = 0;
6833 11986 : dobj->catId.oid = 0;
6834 11986 : AssignDumpId(dobj);
6835 11986 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
6836 11986 : dobj->dependencies[0] = rel->dumpId;
6837 11986 : dobj->nDeps = 1;
6838 11986 : dobj->allocDeps = 1;
6839 11986 : dobj->components |= DUMP_COMPONENT_STATISTICS;
6840 11986 : dobj->name = pg_strdup(rel->name);
6841 11986 : dobj->namespace = rel->namespace;
6842 11986 : info->relkind = relkind;
6843 11986 : info->postponed_def = false;
6844 :
6845 11986 : return info;
6846 : }
6847 1454 : return NULL;
6848 : }
6849 :
6850 : /*
6851 : * getTables
6852 : * read all the tables (no indexes) in the system catalogs,
6853 : * and return them as an array of TableInfo structures
6854 : *
6855 : * *numTables is set to the number of tables read in
6856 : */
6857 : TableInfo *
6858 322 : getTables(Archive *fout, int *numTables)
6859 : {
6860 322 : DumpOptions *dopt = fout->dopt;
6861 : PGresult *res;
6862 : int ntups;
6863 : int i;
6864 322 : PQExpBuffer query = createPQExpBuffer();
6865 : TableInfo *tblinfo;
6866 : int i_reltableoid;
6867 : int i_reloid;
6868 : int i_relname;
6869 : int i_relnamespace;
6870 : int i_relkind;
6871 : int i_reltype;
6872 : int i_relowner;
6873 : int i_relchecks;
6874 : int i_relhasindex;
6875 : int i_relhasrules;
6876 : int i_relpages;
6877 : int i_toastpages;
6878 : int i_owning_tab;
6879 : int i_owning_col;
6880 : int i_reltablespace;
6881 : int i_relhasoids;
6882 : int i_relhastriggers;
6883 : int i_relpersistence;
6884 : int i_relispopulated;
6885 : int i_relreplident;
6886 : int i_relrowsec;
6887 : int i_relforcerowsec;
6888 : int i_relfrozenxid;
6889 : int i_toastfrozenxid;
6890 : int i_toastoid;
6891 : int i_relminmxid;
6892 : int i_toastminmxid;
6893 : int i_reloptions;
6894 : int i_checkoption;
6895 : int i_toastreloptions;
6896 : int i_reloftype;
6897 : int i_foreignserver;
6898 : int i_amname;
6899 : int i_is_identity_sequence;
6900 : int i_relacl;
6901 : int i_acldefault;
6902 : int i_ispartition;
6903 :
6904 : /*
6905 : * Find all the tables and table-like objects.
6906 : *
6907 : * We must fetch all tables in this phase because otherwise we cannot
6908 : * correctly identify inherited columns, owned sequences, etc.
6909 : *
6910 : * We include system catalogs, so that we can work if a user table is
6911 : * defined to inherit from a system catalog (pretty weird, but...)
6912 : *
6913 : * Note: in this phase we should collect only a minimal amount of
6914 : * information about each table, basically just enough to decide if it is
6915 : * interesting. In particular, since we do not yet have lock on any user
6916 : * table, we MUST NOT invoke any server-side data collection functions
6917 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6918 : * wrong answers if any concurrent DDL is happening.
6919 : */
6920 :
6921 322 : appendPQExpBufferStr(query,
6922 : "SELECT c.tableoid, c.oid, c.relname, "
6923 : "c.relnamespace, c.relkind, c.reltype, "
6924 : "c.relowner, "
6925 : "c.relchecks, "
6926 : "c.relhasindex, c.relhasrules, c.relpages, "
6927 : "c.relhastriggers, "
6928 : "c.relpersistence, "
6929 : "c.reloftype, "
6930 : "c.relacl, "
6931 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6932 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6933 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6934 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6935 : "ELSE 0 END AS foreignserver, "
6936 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6937 : "tc.oid AS toid, "
6938 : "tc.relpages AS toastpages, "
6939 : "tc.reloptions AS toast_reloptions, "
6940 : "d.refobjid AS owning_tab, "
6941 : "d.refobjsubid AS owning_col, "
6942 : "tsp.spcname AS reltablespace, ");
6943 :
6944 322 : if (fout->remoteVersion >= 120000)
6945 322 : appendPQExpBufferStr(query,
6946 : "false AS relhasoids, ");
6947 : else
6948 0 : appendPQExpBufferStr(query,
6949 : "c.relhasoids, ");
6950 :
6951 322 : if (fout->remoteVersion >= 90300)
6952 322 : appendPQExpBufferStr(query,
6953 : "c.relispopulated, ");
6954 : else
6955 0 : appendPQExpBufferStr(query,
6956 : "'t' as relispopulated, ");
6957 :
6958 322 : if (fout->remoteVersion >= 90400)
6959 322 : appendPQExpBufferStr(query,
6960 : "c.relreplident, ");
6961 : else
6962 0 : appendPQExpBufferStr(query,
6963 : "'d' AS relreplident, ");
6964 :
6965 322 : if (fout->remoteVersion >= 90500)
6966 322 : appendPQExpBufferStr(query,
6967 : "c.relrowsecurity, c.relforcerowsecurity, ");
6968 : else
6969 0 : appendPQExpBufferStr(query,
6970 : "false AS relrowsecurity, "
6971 : "false AS relforcerowsecurity, ");
6972 :
6973 322 : if (fout->remoteVersion >= 90300)
6974 322 : appendPQExpBufferStr(query,
6975 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
6976 : else
6977 0 : appendPQExpBufferStr(query,
6978 : "0 AS relminmxid, 0 AS tminmxid, ");
6979 :
6980 322 : if (fout->remoteVersion >= 90300)
6981 322 : appendPQExpBufferStr(query,
6982 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6983 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6984 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6985 : else
6986 0 : appendPQExpBufferStr(query,
6987 : "c.reloptions, NULL AS checkoption, ");
6988 :
6989 322 : if (fout->remoteVersion >= 90600)
6990 322 : appendPQExpBufferStr(query,
6991 : "am.amname, ");
6992 : else
6993 0 : appendPQExpBufferStr(query,
6994 : "NULL AS amname, ");
6995 :
6996 322 : if (fout->remoteVersion >= 90600)
6997 322 : appendPQExpBufferStr(query,
6998 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6999 : else
7000 0 : appendPQExpBufferStr(query,
7001 : "false AS is_identity_sequence, ");
7002 :
7003 322 : if (fout->remoteVersion >= 100000)
7004 322 : appendPQExpBufferStr(query,
7005 : "c.relispartition AS ispartition ");
7006 : else
7007 0 : appendPQExpBufferStr(query,
7008 : "false AS ispartition ");
7009 :
7010 : /*
7011 : * Left join to pg_depend to pick up dependency info linking sequences to
7012 : * their owning column, if any (note this dependency is AUTO except for
7013 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7014 : * collect the spcname.
7015 : */
7016 322 : appendPQExpBufferStr(query,
7017 : "\nFROM pg_class c\n"
7018 : "LEFT JOIN pg_depend d ON "
7019 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7020 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7021 : "d.objsubid = 0 AND "
7022 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7023 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7024 :
7025 : /*
7026 : * In 9.6 and up, left join to pg_am to pick up the amname.
7027 : */
7028 322 : if (fout->remoteVersion >= 90600)
7029 322 : appendPQExpBufferStr(query,
7030 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7031 :
7032 : /*
7033 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7034 : * that versions 10 and 11 have them, but later versions do not, so
7035 : * emitting them causes the upgrade to fail.
7036 : */
7037 322 : appendPQExpBufferStr(query,
7038 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7039 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7040 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7041 :
7042 : /*
7043 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7044 : * relkinds are possible in older servers, but it's not worth the trouble
7045 : * to emit a version-dependent list.
7046 : *
7047 : * Composite-type table entries won't be dumped as such, but we have to
7048 : * make a DumpableObject for them so that we can track dependencies of the
7049 : * composite type (pg_depend entries for columns of the composite type
7050 : * link to the pg_class entry not the pg_type entry).
7051 : */
7052 322 : appendPQExpBufferStr(query,
7053 : "WHERE c.relkind IN ("
7054 : CppAsString2(RELKIND_RELATION) ", "
7055 : CppAsString2(RELKIND_SEQUENCE) ", "
7056 : CppAsString2(RELKIND_VIEW) ", "
7057 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7058 : CppAsString2(RELKIND_MATVIEW) ", "
7059 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7060 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7061 : "ORDER BY c.oid");
7062 :
7063 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7064 :
7065 322 : ntups = PQntuples(res);
7066 :
7067 322 : *numTables = ntups;
7068 :
7069 : /*
7070 : * Extract data from result and lock dumpable tables. We do the locking
7071 : * before anything else, to minimize the window wherein a table could
7072 : * disappear under us.
7073 : *
7074 : * Note that we have to save info about all tables here, even when dumping
7075 : * only one, because we don't yet know which tables might be inheritance
7076 : * ancestors of the target table.
7077 : */
7078 322 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7079 :
7080 322 : i_reltableoid = PQfnumber(res, "tableoid");
7081 322 : i_reloid = PQfnumber(res, "oid");
7082 322 : i_relname = PQfnumber(res, "relname");
7083 322 : i_relnamespace = PQfnumber(res, "relnamespace");
7084 322 : i_relkind = PQfnumber(res, "relkind");
7085 322 : i_reltype = PQfnumber(res, "reltype");
7086 322 : i_relowner = PQfnumber(res, "relowner");
7087 322 : i_relchecks = PQfnumber(res, "relchecks");
7088 322 : i_relhasindex = PQfnumber(res, "relhasindex");
7089 322 : i_relhasrules = PQfnumber(res, "relhasrules");
7090 322 : i_relpages = PQfnumber(res, "relpages");
7091 322 : i_toastpages = PQfnumber(res, "toastpages");
7092 322 : i_owning_tab = PQfnumber(res, "owning_tab");
7093 322 : i_owning_col = PQfnumber(res, "owning_col");
7094 322 : i_reltablespace = PQfnumber(res, "reltablespace");
7095 322 : i_relhasoids = PQfnumber(res, "relhasoids");
7096 322 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7097 322 : i_relpersistence = PQfnumber(res, "relpersistence");
7098 322 : i_relispopulated = PQfnumber(res, "relispopulated");
7099 322 : i_relreplident = PQfnumber(res, "relreplident");
7100 322 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7101 322 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7102 322 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7103 322 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7104 322 : i_toastoid = PQfnumber(res, "toid");
7105 322 : i_relminmxid = PQfnumber(res, "relminmxid");
7106 322 : i_toastminmxid = PQfnumber(res, "tminmxid");
7107 322 : i_reloptions = PQfnumber(res, "reloptions");
7108 322 : i_checkoption = PQfnumber(res, "checkoption");
7109 322 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7110 322 : i_reloftype = PQfnumber(res, "reloftype");
7111 322 : i_foreignserver = PQfnumber(res, "foreignserver");
7112 322 : i_amname = PQfnumber(res, "amname");
7113 322 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7114 322 : i_relacl = PQfnumber(res, "relacl");
7115 322 : i_acldefault = PQfnumber(res, "acldefault");
7116 322 : i_ispartition = PQfnumber(res, "ispartition");
7117 :
7118 322 : if (dopt->lockWaitTimeout)
7119 : {
7120 : /*
7121 : * Arrange to fail instead of waiting forever for a table lock.
7122 : *
7123 : * NB: this coding assumes that the only queries issued within the
7124 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7125 : * applied to other things too.
7126 : */
7127 4 : resetPQExpBuffer(query);
7128 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7129 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7130 4 : ExecuteSqlStatement(fout, query->data);
7131 : }
7132 :
7133 322 : resetPQExpBuffer(query);
7134 :
7135 85584 : for (i = 0; i < ntups; i++)
7136 : {
7137 85262 : tblinfo[i].dobj.objType = DO_TABLE;
7138 85262 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7139 85262 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7140 85262 : AssignDumpId(&tblinfo[i].dobj);
7141 85262 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7142 170524 : tblinfo[i].dobj.namespace =
7143 85262 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7144 85262 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7145 85262 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7146 85262 : tblinfo[i].dacl.privtype = 0;
7147 85262 : tblinfo[i].dacl.initprivs = NULL;
7148 85262 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7149 85262 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7150 85262 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7151 85262 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7152 85262 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7153 85262 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7154 85262 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7155 85262 : if (PQgetisnull(res, i, i_toastpages))
7156 67424 : tblinfo[i].toastpages = 0;
7157 : else
7158 17838 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7159 85262 : if (PQgetisnull(res, i, i_owning_tab))
7160 : {
7161 84420 : tblinfo[i].owning_tab = InvalidOid;
7162 84420 : tblinfo[i].owning_col = 0;
7163 : }
7164 : else
7165 : {
7166 842 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7167 842 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7168 : }
7169 85262 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7170 85262 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7171 85262 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7172 85262 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7173 85262 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7174 85262 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7175 85262 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7176 85262 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7177 85262 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7178 85262 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7179 85262 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7180 85262 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7181 85262 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7182 85262 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7183 85262 : if (PQgetisnull(res, i, i_checkoption))
7184 85166 : tblinfo[i].checkoption = NULL;
7185 : else
7186 96 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7187 85262 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7188 85262 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7189 85262 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7190 85262 : if (PQgetisnull(res, i, i_amname))
7191 49976 : tblinfo[i].amname = NULL;
7192 : else
7193 35286 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7194 85262 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7195 85262 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7196 :
7197 : /* other fields were zeroed above */
7198 :
7199 : /*
7200 : * Decide whether we want to dump this table.
7201 : */
7202 85262 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7203 370 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7204 : else
7205 84892 : selectDumpableTable(&tblinfo[i], fout);
7206 :
7207 : /*
7208 : * Now, consider the table "interesting" if we need to dump its
7209 : * definition, data or its statistics. Later on, we'll skip a lot of
7210 : * data collection for uninteresting tables.
7211 : *
7212 : * Note: the "interesting" flag will also be set by flagInhTables for
7213 : * parents of interesting tables, so that we collect necessary
7214 : * inheritance info even when the parents are not themselves being
7215 : * dumped. This is the main reason why we need an "interesting" flag
7216 : * that's separate from the components-to-dump bitmask.
7217 : */
7218 85262 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7219 : (DUMP_COMPONENT_DEFINITION |
7220 : DUMP_COMPONENT_DATA |
7221 85262 : DUMP_COMPONENT_STATISTICS)) != 0;
7222 :
7223 85262 : tblinfo[i].dummy_view = false; /* might get set during sort */
7224 85262 : tblinfo[i].postponed_def = false; /* might get set during sort */
7225 :
7226 : /* Tables have data */
7227 85262 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7228 :
7229 : /* Mark whether table has an ACL */
7230 85262 : if (!PQgetisnull(res, i, i_relacl))
7231 66848 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7232 85262 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7233 :
7234 : /* Add statistics */
7235 85262 : if (tblinfo[i].interesting)
7236 13310 : getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relkind);
7237 :
7238 : /*
7239 : * Read-lock target tables to make sure they aren't DROPPED or altered
7240 : * in schema before we get around to dumping them.
7241 : *
7242 : * Note that we don't explicitly lock parents of the target tables; we
7243 : * assume our lock on the child is enough to prevent schema
7244 : * alterations to parent tables.
7245 : *
7246 : * NOTE: it'd be kinda nice to lock other relations too, not only
7247 : * plain or partitioned tables, but the backend doesn't presently
7248 : * allow that.
7249 : *
7250 : * We only need to lock the table for certain components; see
7251 : * pg_dump.h
7252 : */
7253 85262 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7254 13310 : (tblinfo[i].relkind == RELKIND_RELATION ||
7255 3924 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7256 : {
7257 : /*
7258 : * Tables are locked in batches. When dumping from a remote
7259 : * server this can save a significant amount of time by reducing
7260 : * the number of round trips.
7261 : */
7262 10480 : if (query->len == 0)
7263 206 : appendPQExpBuffer(query, "LOCK TABLE %s",
7264 206 : fmtQualifiedDumpable(&tblinfo[i]));
7265 : else
7266 : {
7267 10274 : appendPQExpBuffer(query, ", %s",
7268 10274 : fmtQualifiedDumpable(&tblinfo[i]));
7269 :
7270 : /* Arbitrarily end a batch when query length reaches 100K. */
7271 10274 : if (query->len >= 100000)
7272 : {
7273 : /* Lock another batch of tables. */
7274 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7275 0 : ExecuteSqlStatement(fout, query->data);
7276 0 : resetPQExpBuffer(query);
7277 : }
7278 : }
7279 : }
7280 : }
7281 :
7282 322 : if (query->len != 0)
7283 : {
7284 : /* Lock the tables in the last batch. */
7285 206 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7286 206 : ExecuteSqlStatement(fout, query->data);
7287 : }
7288 :
7289 320 : if (dopt->lockWaitTimeout)
7290 : {
7291 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7292 : }
7293 :
7294 320 : PQclear(res);
7295 :
7296 320 : destroyPQExpBuffer(query);
7297 :
7298 320 : return tblinfo;
7299 : }
7300 :
7301 : /*
7302 : * getOwnedSeqs
7303 : * identify owned sequences and mark them as dumpable if owning table is
7304 : *
7305 : * We used to do this in getTables(), but it's better to do it after the
7306 : * index used by findTableByOid() has been set up.
7307 : */
7308 : void
7309 320 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7310 : {
7311 : int i;
7312 :
7313 : /*
7314 : * Force sequences that are "owned" by table columns to be dumped whenever
7315 : * their owning table is being dumped.
7316 : */
7317 85060 : for (i = 0; i < numTables; i++)
7318 : {
7319 84740 : TableInfo *seqinfo = &tblinfo[i];
7320 : TableInfo *owning_tab;
7321 :
7322 84740 : if (!OidIsValid(seqinfo->owning_tab))
7323 83904 : continue; /* not an owned sequence */
7324 :
7325 836 : owning_tab = findTableByOid(seqinfo->owning_tab);
7326 836 : if (owning_tab == NULL)
7327 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7328 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7329 :
7330 : /*
7331 : * For an identity sequence, dump exactly the same components for the
7332 : * sequence as for the owning table. This is important because we
7333 : * treat the identity sequence as an integral part of the table. For
7334 : * example, there is not any DDL command that allows creation of such
7335 : * a sequence independently of the table.
7336 : *
7337 : * For other owned sequences such as serial sequences, we need to dump
7338 : * the components that are being dumped for the table and any
7339 : * components that the sequence is explicitly marked with.
7340 : *
7341 : * We can't simply use the set of components which are being dumped
7342 : * for the table as the table might be in an extension (and only the
7343 : * non-extension components, eg: ACLs if changed, security labels, and
7344 : * policies, are being dumped) while the sequence is not (and
7345 : * therefore the definition and other components should also be
7346 : * dumped).
7347 : *
7348 : * If the sequence is part of the extension then it should be properly
7349 : * marked by checkExtensionMembership() and this will be a no-op as
7350 : * the table will be equivalently marked.
7351 : */
7352 836 : if (seqinfo->is_identity_sequence)
7353 402 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7354 : else
7355 434 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7356 :
7357 : /* Make sure that necessary data is available if we're dumping it */
7358 836 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7359 : {
7360 644 : seqinfo->interesting = true;
7361 644 : owning_tab->interesting = true;
7362 : }
7363 : }
7364 320 : }
7365 :
7366 : /*
7367 : * getInherits
7368 : * read all the inheritance information
7369 : * from the system catalogs return them in the InhInfo* structure
7370 : *
7371 : * numInherits is set to the number of pairs read in
7372 : */
7373 : InhInfo *
7374 320 : getInherits(Archive *fout, int *numInherits)
7375 : {
7376 : PGresult *res;
7377 : int ntups;
7378 : int i;
7379 320 : PQExpBuffer query = createPQExpBuffer();
7380 : InhInfo *inhinfo;
7381 :
7382 : int i_inhrelid;
7383 : int i_inhparent;
7384 :
7385 : /* find all the inheritance information */
7386 320 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7387 :
7388 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7389 :
7390 320 : ntups = PQntuples(res);
7391 :
7392 320 : *numInherits = ntups;
7393 :
7394 320 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7395 :
7396 320 : i_inhrelid = PQfnumber(res, "inhrelid");
7397 320 : i_inhparent = PQfnumber(res, "inhparent");
7398 :
7399 6392 : for (i = 0; i < ntups; i++)
7400 : {
7401 6072 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7402 6072 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7403 : }
7404 :
7405 320 : PQclear(res);
7406 :
7407 320 : destroyPQExpBuffer(query);
7408 :
7409 320 : return inhinfo;
7410 : }
7411 :
7412 : /*
7413 : * getPartitioningInfo
7414 : * get information about partitioning
7415 : *
7416 : * For the most part, we only collect partitioning info about tables we
7417 : * intend to dump. However, this function has to consider all partitioned
7418 : * tables in the database, because we need to know about parents of partitions
7419 : * we are going to dump even if the parents themselves won't be dumped.
7420 : *
7421 : * Specifically, what we need to know is whether each partitioned table
7422 : * has an "unsafe" partitioning scheme that requires us to force
7423 : * load-via-partition-root mode for its children. Currently the only case
7424 : * for which we force that is hash partitioning on enum columns, since the
7425 : * hash codes depend on enum value OIDs which won't be replicated across
7426 : * dump-and-reload. There are other cases in which load-via-partition-root
7427 : * might be necessary, but we expect users to cope with them.
7428 : */
7429 : void
7430 320 : getPartitioningInfo(Archive *fout)
7431 : {
7432 : PQExpBuffer query;
7433 : PGresult *res;
7434 : int ntups;
7435 :
7436 : /* hash partitioning didn't exist before v11 */
7437 320 : if (fout->remoteVersion < 110000)
7438 0 : return;
7439 : /* needn't bother if not dumping data */
7440 320 : if (!fout->dopt->dumpData)
7441 40 : return;
7442 :
7443 280 : query = createPQExpBuffer();
7444 :
7445 : /*
7446 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7447 : * appears among the partition opclasses. We needn't check partstrat.
7448 : *
7449 : * Note that this query may well retrieve info about tables we aren't
7450 : * going to dump and hence have no lock on. That's okay since we need not
7451 : * invoke any unsafe server-side functions.
7452 : */
7453 280 : appendPQExpBufferStr(query,
7454 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7455 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7456 : "ON c.opcmethod = a.oid\n"
7457 : "WHERE opcname = 'enum_ops' "
7458 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7459 : "AND amname = 'hash') = ANY(partclass)");
7460 :
7461 280 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7462 :
7463 280 : ntups = PQntuples(res);
7464 :
7465 284 : for (int i = 0; i < ntups; i++)
7466 : {
7467 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7468 : TableInfo *tbinfo;
7469 :
7470 4 : tbinfo = findTableByOid(tabrelid);
7471 4 : if (tbinfo == NULL)
7472 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7473 : tabrelid);
7474 4 : tbinfo->unsafe_partitions = true;
7475 : }
7476 :
7477 280 : PQclear(res);
7478 :
7479 280 : destroyPQExpBuffer(query);
7480 : }
7481 :
7482 : /*
7483 : * getIndexes
7484 : * get information about every index on a dumpable table
7485 : *
7486 : * Note: index data is not returned directly to the caller, but it
7487 : * does get entered into the DumpableObject tables.
7488 : */
7489 : void
7490 320 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7491 : {
7492 320 : PQExpBuffer query = createPQExpBuffer();
7493 320 : PQExpBuffer tbloids = createPQExpBuffer();
7494 : PGresult *res;
7495 : int ntups;
7496 : int curtblindx;
7497 : IndxInfo *indxinfo;
7498 : int i_tableoid,
7499 : i_oid,
7500 : i_indrelid,
7501 : i_indexname,
7502 : i_parentidx,
7503 : i_indexdef,
7504 : i_indnkeyatts,
7505 : i_indnatts,
7506 : i_indkey,
7507 : i_indisclustered,
7508 : i_indisreplident,
7509 : i_indnullsnotdistinct,
7510 : i_contype,
7511 : i_conname,
7512 : i_condeferrable,
7513 : i_condeferred,
7514 : i_conperiod,
7515 : i_contableoid,
7516 : i_conoid,
7517 : i_condef,
7518 : i_tablespace,
7519 : i_indreloptions,
7520 : i_indstatcols,
7521 : i_indstatvals;
7522 :
7523 : /*
7524 : * We want to perform just one query against pg_index. However, we
7525 : * mustn't try to select every row of the catalog and then sort it out on
7526 : * the client side, because some of the server-side functions we need
7527 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7528 : * build an array of the OIDs of tables we care about (and now have lock
7529 : * on!), and use a WHERE clause to constrain which rows are selected.
7530 : */
7531 320 : appendPQExpBufferChar(tbloids, '{');
7532 85060 : for (int i = 0; i < numTables; i++)
7533 : {
7534 84740 : TableInfo *tbinfo = &tblinfo[i];
7535 :
7536 84740 : if (!tbinfo->hasindex)
7537 59552 : continue;
7538 :
7539 : /*
7540 : * We can ignore indexes of uninteresting tables.
7541 : */
7542 25188 : if (!tbinfo->interesting)
7543 21222 : continue;
7544 :
7545 : /* OK, we need info for this table */
7546 3966 : if (tbloids->len > 1) /* do we have more than the '{'? */
7547 3806 : appendPQExpBufferChar(tbloids, ',');
7548 3966 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7549 : }
7550 320 : appendPQExpBufferChar(tbloids, '}');
7551 :
7552 320 : appendPQExpBufferStr(query,
7553 : "SELECT t.tableoid, t.oid, i.indrelid, "
7554 : "t.relname AS indexname, "
7555 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7556 : "i.indkey, i.indisclustered, "
7557 : "c.contype, c.conname, "
7558 : "c.condeferrable, c.condeferred, "
7559 : "c.tableoid AS contableoid, "
7560 : "c.oid AS conoid, "
7561 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7562 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7563 : "t.reloptions AS indreloptions, ");
7564 :
7565 :
7566 320 : if (fout->remoteVersion >= 90400)
7567 320 : appendPQExpBufferStr(query,
7568 : "i.indisreplident, ");
7569 : else
7570 0 : appendPQExpBufferStr(query,
7571 : "false AS indisreplident, ");
7572 :
7573 320 : if (fout->remoteVersion >= 110000)
7574 320 : appendPQExpBufferStr(query,
7575 : "inh.inhparent AS parentidx, "
7576 : "i.indnkeyatts AS indnkeyatts, "
7577 : "i.indnatts AS indnatts, "
7578 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7579 : " FROM pg_catalog.pg_attribute "
7580 : " WHERE attrelid = i.indexrelid AND "
7581 : " attstattarget >= 0) AS indstatcols, "
7582 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7583 : " FROM pg_catalog.pg_attribute "
7584 : " WHERE attrelid = i.indexrelid AND "
7585 : " attstattarget >= 0) AS indstatvals, ");
7586 : else
7587 0 : appendPQExpBufferStr(query,
7588 : "0 AS parentidx, "
7589 : "i.indnatts AS indnkeyatts, "
7590 : "i.indnatts AS indnatts, "
7591 : "'' AS indstatcols, "
7592 : "'' AS indstatvals, ");
7593 :
7594 320 : if (fout->remoteVersion >= 150000)
7595 320 : appendPQExpBufferStr(query,
7596 : "i.indnullsnotdistinct, ");
7597 : else
7598 0 : appendPQExpBufferStr(query,
7599 : "false AS indnullsnotdistinct, ");
7600 :
7601 320 : if (fout->remoteVersion >= 180000)
7602 320 : appendPQExpBufferStr(query,
7603 : "c.conperiod ");
7604 : else
7605 0 : appendPQExpBufferStr(query,
7606 : "NULL AS conperiod ");
7607 :
7608 : /*
7609 : * The point of the messy-looking outer join is to find a constraint that
7610 : * is related by an internal dependency link to the index. If we find one,
7611 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7612 : * index won't have more than one internal dependency.
7613 : *
7614 : * Note: the check on conrelid is redundant, but useful because that
7615 : * column is indexed while conindid is not.
7616 : */
7617 320 : if (fout->remoteVersion >= 110000)
7618 : {
7619 320 : appendPQExpBuffer(query,
7620 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7621 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7622 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7623 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7624 : "LEFT JOIN pg_catalog.pg_constraint c "
7625 : "ON (i.indrelid = c.conrelid AND "
7626 : "i.indexrelid = c.conindid AND "
7627 : "c.contype IN ('p','u','x')) "
7628 : "LEFT JOIN pg_catalog.pg_inherits inh "
7629 : "ON (inh.inhrelid = indexrelid) "
7630 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7631 : "AND i.indisready "
7632 : "ORDER BY i.indrelid, indexname",
7633 : tbloids->data);
7634 : }
7635 : else
7636 : {
7637 : /*
7638 : * the test on indisready is necessary in 9.2, and harmless in
7639 : * earlier/later versions
7640 : */
7641 0 : appendPQExpBuffer(query,
7642 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7643 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7644 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7645 : "LEFT JOIN pg_catalog.pg_constraint c "
7646 : "ON (i.indrelid = c.conrelid AND "
7647 : "i.indexrelid = c.conindid AND "
7648 : "c.contype IN ('p','u','x')) "
7649 : "WHERE i.indisvalid AND i.indisready "
7650 : "ORDER BY i.indrelid, indexname",
7651 : tbloids->data);
7652 : }
7653 :
7654 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7655 :
7656 320 : ntups = PQntuples(res);
7657 :
7658 320 : i_tableoid = PQfnumber(res, "tableoid");
7659 320 : i_oid = PQfnumber(res, "oid");
7660 320 : i_indrelid = PQfnumber(res, "indrelid");
7661 320 : i_indexname = PQfnumber(res, "indexname");
7662 320 : i_parentidx = PQfnumber(res, "parentidx");
7663 320 : i_indexdef = PQfnumber(res, "indexdef");
7664 320 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7665 320 : i_indnatts = PQfnumber(res, "indnatts");
7666 320 : i_indkey = PQfnumber(res, "indkey");
7667 320 : i_indisclustered = PQfnumber(res, "indisclustered");
7668 320 : i_indisreplident = PQfnumber(res, "indisreplident");
7669 320 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7670 320 : i_contype = PQfnumber(res, "contype");
7671 320 : i_conname = PQfnumber(res, "conname");
7672 320 : i_condeferrable = PQfnumber(res, "condeferrable");
7673 320 : i_condeferred = PQfnumber(res, "condeferred");
7674 320 : i_conperiod = PQfnumber(res, "conperiod");
7675 320 : i_contableoid = PQfnumber(res, "contableoid");
7676 320 : i_conoid = PQfnumber(res, "conoid");
7677 320 : i_condef = PQfnumber(res, "condef");
7678 320 : i_tablespace = PQfnumber(res, "tablespace");
7679 320 : i_indreloptions = PQfnumber(res, "indreloptions");
7680 320 : i_indstatcols = PQfnumber(res, "indstatcols");
7681 320 : i_indstatvals = PQfnumber(res, "indstatvals");
7682 :
7683 320 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7684 :
7685 : /*
7686 : * Outer loop iterates once per table, not once per row. Incrementing of
7687 : * j is handled by the inner loop.
7688 : */
7689 320 : curtblindx = -1;
7690 4278 : for (int j = 0; j < ntups;)
7691 : {
7692 3958 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7693 3958 : TableInfo *tbinfo = NULL;
7694 : int numinds;
7695 :
7696 : /* Count rows for this table */
7697 5202 : for (numinds = 1; numinds < ntups - j; numinds++)
7698 5042 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7699 3798 : break;
7700 :
7701 : /*
7702 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7703 : * order.
7704 : */
7705 45896 : while (++curtblindx < numTables)
7706 : {
7707 45896 : tbinfo = &tblinfo[curtblindx];
7708 45896 : if (tbinfo->dobj.catId.oid == indrelid)
7709 3958 : break;
7710 : }
7711 3958 : if (curtblindx >= numTables)
7712 0 : pg_fatal("unrecognized table OID %u", indrelid);
7713 : /* cross-check that we only got requested tables */
7714 3958 : if (!tbinfo->hasindex ||
7715 3958 : !tbinfo->interesting)
7716 0 : pg_fatal("unexpected index data for table \"%s\"",
7717 : tbinfo->dobj.name);
7718 :
7719 : /* Save data for this table */
7720 3958 : tbinfo->indexes = indxinfo + j;
7721 3958 : tbinfo->numIndexes = numinds;
7722 :
7723 9160 : for (int c = 0; c < numinds; c++, j++)
7724 : {
7725 : char contype;
7726 : char indexkind;
7727 : RelStatsInfo *relstats;
7728 :
7729 5202 : indxinfo[j].dobj.objType = DO_INDEX;
7730 5202 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7731 5202 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7732 5202 : AssignDumpId(&indxinfo[j].dobj);
7733 5202 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7734 5202 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7735 5202 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7736 5202 : indxinfo[j].indextable = tbinfo;
7737 5202 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7738 5202 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7739 5202 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7740 5202 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7741 5202 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7742 5202 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7743 5202 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7744 5202 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7745 5202 : parseOidArray(PQgetvalue(res, j, i_indkey),
7746 5202 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7747 5202 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7748 5202 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7749 5202 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7750 5202 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7751 5202 : indxinfo[j].partattaches = (SimplePtrList)
7752 : {
7753 : NULL, NULL
7754 : };
7755 :
7756 5202 : if (indxinfo[j].parentidx == 0)
7757 4042 : indexkind = RELKIND_INDEX;
7758 : else
7759 1160 : indexkind = RELKIND_PARTITIONED_INDEX;
7760 :
7761 5202 : contype = *(PQgetvalue(res, j, i_contype));
7762 5202 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, indexkind);
7763 :
7764 5202 : if (contype == 'p' || contype == 'u' || contype == 'x')
7765 3020 : {
7766 : /*
7767 : * If we found a constraint matching the index, create an
7768 : * entry for it.
7769 : */
7770 : ConstraintInfo *constrinfo;
7771 :
7772 3020 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7773 3020 : constrinfo->dobj.objType = DO_CONSTRAINT;
7774 3020 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7775 3020 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7776 3020 : AssignDumpId(&constrinfo->dobj);
7777 3020 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7778 3020 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7779 3020 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7780 3020 : constrinfo->contable = tbinfo;
7781 3020 : constrinfo->condomain = NULL;
7782 3020 : constrinfo->contype = contype;
7783 3020 : if (contype == 'x')
7784 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7785 : else
7786 3000 : constrinfo->condef = NULL;
7787 3020 : constrinfo->confrelid = InvalidOid;
7788 3020 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7789 3020 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7790 3020 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7791 3020 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7792 3020 : constrinfo->conislocal = true;
7793 3020 : constrinfo->separate = true;
7794 :
7795 3020 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7796 3020 : if (relstats != NULL)
7797 2372 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
7798 : }
7799 : else
7800 : {
7801 : /* Plain secondary index */
7802 2182 : indxinfo[j].indexconstraint = 0;
7803 : }
7804 : }
7805 : }
7806 :
7807 320 : PQclear(res);
7808 :
7809 320 : destroyPQExpBuffer(query);
7810 320 : destroyPQExpBuffer(tbloids);
7811 320 : }
7812 :
7813 : /*
7814 : * getExtendedStatistics
7815 : * get information about extended-statistics objects.
7816 : *
7817 : * Note: extended statistics data is not returned directly to the caller, but
7818 : * it does get entered into the DumpableObject tables.
7819 : */
7820 : void
7821 320 : getExtendedStatistics(Archive *fout)
7822 : {
7823 : PQExpBuffer query;
7824 : PGresult *res;
7825 : StatsExtInfo *statsextinfo;
7826 : int ntups;
7827 : int i_tableoid;
7828 : int i_oid;
7829 : int i_stxname;
7830 : int i_stxnamespace;
7831 : int i_stxowner;
7832 : int i_stxrelid;
7833 : int i_stattarget;
7834 : int i;
7835 :
7836 : /* Extended statistics were new in v10 */
7837 320 : if (fout->remoteVersion < 100000)
7838 0 : return;
7839 :
7840 320 : query = createPQExpBuffer();
7841 :
7842 320 : if (fout->remoteVersion < 130000)
7843 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7844 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
7845 : "FROM pg_catalog.pg_statistic_ext");
7846 : else
7847 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7848 : "stxnamespace, stxowner, stxrelid, stxstattarget "
7849 : "FROM pg_catalog.pg_statistic_ext");
7850 :
7851 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7852 :
7853 320 : ntups = PQntuples(res);
7854 :
7855 320 : i_tableoid = PQfnumber(res, "tableoid");
7856 320 : i_oid = PQfnumber(res, "oid");
7857 320 : i_stxname = PQfnumber(res, "stxname");
7858 320 : i_stxnamespace = PQfnumber(res, "stxnamespace");
7859 320 : i_stxowner = PQfnumber(res, "stxowner");
7860 320 : i_stxrelid = PQfnumber(res, "stxrelid");
7861 320 : i_stattarget = PQfnumber(res, "stxstattarget");
7862 :
7863 320 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7864 :
7865 658 : for (i = 0; i < ntups; i++)
7866 : {
7867 338 : statsextinfo[i].dobj.objType = DO_STATSEXT;
7868 338 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7869 338 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7870 338 : AssignDumpId(&statsextinfo[i].dobj);
7871 338 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7872 676 : statsextinfo[i].dobj.namespace =
7873 338 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7874 338 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7875 676 : statsextinfo[i].stattable =
7876 338 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
7877 338 : if (PQgetisnull(res, i, i_stattarget))
7878 244 : statsextinfo[i].stattarget = -1;
7879 : else
7880 94 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7881 :
7882 : /* Decide whether we want to dump it */
7883 338 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
7884 : }
7885 :
7886 320 : PQclear(res);
7887 320 : destroyPQExpBuffer(query);
7888 : }
7889 :
7890 : /*
7891 : * getConstraints
7892 : *
7893 : * Get info about constraints on dumpable tables.
7894 : *
7895 : * Currently handles foreign keys only.
7896 : * Unique and primary key constraints are handled with indexes,
7897 : * while check constraints are processed in getTableAttrs().
7898 : */
7899 : void
7900 320 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7901 : {
7902 320 : PQExpBuffer query = createPQExpBuffer();
7903 320 : PQExpBuffer tbloids = createPQExpBuffer();
7904 : PGresult *res;
7905 : int ntups;
7906 : int curtblindx;
7907 320 : TableInfo *tbinfo = NULL;
7908 : ConstraintInfo *constrinfo;
7909 : int i_contableoid,
7910 : i_conoid,
7911 : i_conrelid,
7912 : i_conname,
7913 : i_confrelid,
7914 : i_conindid,
7915 : i_condef;
7916 :
7917 : /*
7918 : * We want to perform just one query against pg_constraint. However, we
7919 : * mustn't try to select every row of the catalog and then sort it out on
7920 : * the client side, because some of the server-side functions we need
7921 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7922 : * build an array of the OIDs of tables we care about (and now have lock
7923 : * on!), and use a WHERE clause to constrain which rows are selected.
7924 : */
7925 320 : appendPQExpBufferChar(tbloids, '{');
7926 85060 : for (int i = 0; i < numTables; i++)
7927 : {
7928 84740 : TableInfo *tinfo = &tblinfo[i];
7929 :
7930 : /*
7931 : * For partitioned tables, foreign keys have no triggers so they must
7932 : * be included anyway in case some foreign keys are defined.
7933 : */
7934 84740 : if ((!tinfo->hastriggers &&
7935 82472 : tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7936 3242 : !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7937 82284 : continue;
7938 :
7939 : /* OK, we need info for this table */
7940 2456 : if (tbloids->len > 1) /* do we have more than the '{'? */
7941 2346 : appendPQExpBufferChar(tbloids, ',');
7942 2456 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7943 : }
7944 320 : appendPQExpBufferChar(tbloids, '}');
7945 :
7946 320 : appendPQExpBufferStr(query,
7947 : "SELECT c.tableoid, c.oid, "
7948 : "conrelid, conname, confrelid, ");
7949 320 : if (fout->remoteVersion >= 110000)
7950 320 : appendPQExpBufferStr(query, "conindid, ");
7951 : else
7952 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
7953 320 : appendPQExpBuffer(query,
7954 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7955 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7956 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7957 : "WHERE contype = 'f' ",
7958 : tbloids->data);
7959 320 : if (fout->remoteVersion >= 110000)
7960 320 : appendPQExpBufferStr(query,
7961 : "AND conparentid = 0 ");
7962 320 : appendPQExpBufferStr(query,
7963 : "ORDER BY conrelid, conname");
7964 :
7965 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7966 :
7967 320 : ntups = PQntuples(res);
7968 :
7969 320 : i_contableoid = PQfnumber(res, "tableoid");
7970 320 : i_conoid = PQfnumber(res, "oid");
7971 320 : i_conrelid = PQfnumber(res, "conrelid");
7972 320 : i_conname = PQfnumber(res, "conname");
7973 320 : i_confrelid = PQfnumber(res, "confrelid");
7974 320 : i_conindid = PQfnumber(res, "conindid");
7975 320 : i_condef = PQfnumber(res, "condef");
7976 :
7977 320 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7978 :
7979 320 : curtblindx = -1;
7980 680 : for (int j = 0; j < ntups; j++)
7981 : {
7982 360 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7983 : TableInfo *reftable;
7984 :
7985 : /*
7986 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7987 : * order.
7988 : */
7989 360 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7990 : {
7991 27470 : while (++curtblindx < numTables)
7992 : {
7993 27470 : tbinfo = &tblinfo[curtblindx];
7994 27470 : if (tbinfo->dobj.catId.oid == conrelid)
7995 340 : break;
7996 : }
7997 340 : if (curtblindx >= numTables)
7998 0 : pg_fatal("unrecognized table OID %u", conrelid);
7999 : }
8000 :
8001 360 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8002 360 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8003 360 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8004 360 : AssignDumpId(&constrinfo[j].dobj);
8005 360 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8006 360 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8007 360 : constrinfo[j].contable = tbinfo;
8008 360 : constrinfo[j].condomain = NULL;
8009 360 : constrinfo[j].contype = 'f';
8010 360 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8011 360 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8012 360 : constrinfo[j].conindex = 0;
8013 360 : constrinfo[j].condeferrable = false;
8014 360 : constrinfo[j].condeferred = false;
8015 360 : constrinfo[j].conislocal = true;
8016 360 : constrinfo[j].separate = true;
8017 :
8018 : /*
8019 : * Restoring an FK that points to a partitioned table requires that
8020 : * all partition indexes have been attached beforehand. Ensure that
8021 : * happens by making the constraint depend on each index partition
8022 : * attach object.
8023 : */
8024 360 : reftable = findTableByOid(constrinfo[j].confrelid);
8025 360 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8026 : {
8027 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8028 :
8029 40 : if (indexOid != InvalidOid)
8030 : {
8031 40 : for (int k = 0; k < reftable->numIndexes; k++)
8032 : {
8033 : IndxInfo *refidx;
8034 :
8035 : /* not our index? */
8036 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8037 0 : continue;
8038 :
8039 40 : refidx = &reftable->indexes[k];
8040 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8041 40 : break;
8042 : }
8043 : }
8044 : }
8045 : }
8046 :
8047 320 : PQclear(res);
8048 :
8049 320 : destroyPQExpBuffer(query);
8050 320 : destroyPQExpBuffer(tbloids);
8051 320 : }
8052 :
8053 : /*
8054 : * addConstrChildIdxDeps
8055 : *
8056 : * Recursive subroutine for getConstraints
8057 : *
8058 : * Given an object representing a foreign key constraint and an index on the
8059 : * partitioned table it references, mark the constraint object as dependent
8060 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8061 : * drilling down to their partitions if any. This ensures that the FK is not
8062 : * restored until the index is fully marked valid.
8063 : */
8064 : static void
8065 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8066 : {
8067 : SimplePtrListCell *cell;
8068 :
8069 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8070 :
8071 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8072 : {
8073 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8074 :
8075 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8076 :
8077 220 : if (attach->partitionIdx->partattaches.head != NULL)
8078 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8079 : }
8080 90 : }
8081 :
8082 : /*
8083 : * getDomainConstraints
8084 : *
8085 : * Get info about constraints on a domain.
8086 : */
8087 : static void
8088 290 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8089 : {
8090 : int i;
8091 : ConstraintInfo *constrinfo;
8092 290 : PQExpBuffer query = createPQExpBuffer();
8093 : PGresult *res;
8094 : int i_tableoid,
8095 : i_oid,
8096 : i_conname,
8097 : i_consrc;
8098 : int ntups;
8099 :
8100 290 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8101 : {
8102 : /* Set up query for constraint-specific details */
8103 90 : appendPQExpBufferStr(query,
8104 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8105 : "SELECT tableoid, oid, conname, "
8106 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8107 : "convalidated "
8108 : "FROM pg_catalog.pg_constraint "
8109 : "WHERE contypid = $1 AND contype = 'c' "
8110 : "ORDER BY conname");
8111 :
8112 90 : ExecuteSqlStatement(fout, query->data);
8113 :
8114 90 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8115 : }
8116 :
8117 290 : printfPQExpBuffer(query,
8118 : "EXECUTE getDomainConstraints('%u')",
8119 : tyinfo->dobj.catId.oid);
8120 :
8121 290 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8122 :
8123 290 : ntups = PQntuples(res);
8124 :
8125 290 : i_tableoid = PQfnumber(res, "tableoid");
8126 290 : i_oid = PQfnumber(res, "oid");
8127 290 : i_conname = PQfnumber(res, "conname");
8128 290 : i_consrc = PQfnumber(res, "consrc");
8129 :
8130 290 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8131 :
8132 290 : tyinfo->nDomChecks = ntups;
8133 290 : tyinfo->domChecks = constrinfo;
8134 :
8135 490 : for (i = 0; i < ntups; i++)
8136 : {
8137 200 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
8138 :
8139 200 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
8140 200 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8141 200 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8142 200 : AssignDumpId(&constrinfo[i].dobj);
8143 200 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8144 200 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8145 200 : constrinfo[i].contable = NULL;
8146 200 : constrinfo[i].condomain = tyinfo;
8147 200 : constrinfo[i].contype = 'c';
8148 200 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8149 200 : constrinfo[i].confrelid = InvalidOid;
8150 200 : constrinfo[i].conindex = 0;
8151 200 : constrinfo[i].condeferrable = false;
8152 200 : constrinfo[i].condeferred = false;
8153 200 : constrinfo[i].conislocal = true;
8154 :
8155 200 : constrinfo[i].separate = !validated;
8156 :
8157 : /*
8158 : * Make the domain depend on the constraint, ensuring it won't be
8159 : * output till any constraint dependencies are OK. If the constraint
8160 : * has not been validated, it's going to be dumped after the domain
8161 : * anyway, so this doesn't matter.
8162 : */
8163 200 : if (validated)
8164 200 : addObjectDependency(&tyinfo->dobj,
8165 200 : constrinfo[i].dobj.dumpId);
8166 : }
8167 :
8168 290 : PQclear(res);
8169 :
8170 290 : destroyPQExpBuffer(query);
8171 290 : }
8172 :
8173 : /*
8174 : * getRules
8175 : * get basic information about every rule in the system
8176 : */
8177 : void
8178 320 : getRules(Archive *fout)
8179 : {
8180 : PGresult *res;
8181 : int ntups;
8182 : int i;
8183 320 : PQExpBuffer query = createPQExpBuffer();
8184 : RuleInfo *ruleinfo;
8185 : int i_tableoid;
8186 : int i_oid;
8187 : int i_rulename;
8188 : int i_ruletable;
8189 : int i_ev_type;
8190 : int i_is_instead;
8191 : int i_ev_enabled;
8192 :
8193 320 : appendPQExpBufferStr(query, "SELECT "
8194 : "tableoid, oid, rulename, "
8195 : "ev_class AS ruletable, ev_type, is_instead, "
8196 : "ev_enabled "
8197 : "FROM pg_rewrite "
8198 : "ORDER BY oid");
8199 :
8200 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8201 :
8202 320 : ntups = PQntuples(res);
8203 :
8204 320 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8205 :
8206 320 : i_tableoid = PQfnumber(res, "tableoid");
8207 320 : i_oid = PQfnumber(res, "oid");
8208 320 : i_rulename = PQfnumber(res, "rulename");
8209 320 : i_ruletable = PQfnumber(res, "ruletable");
8210 320 : i_ev_type = PQfnumber(res, "ev_type");
8211 320 : i_is_instead = PQfnumber(res, "is_instead");
8212 320 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8213 :
8214 49112 : for (i = 0; i < ntups; i++)
8215 : {
8216 : Oid ruletableoid;
8217 :
8218 48792 : ruleinfo[i].dobj.objType = DO_RULE;
8219 48792 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8220 48792 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8221 48792 : AssignDumpId(&ruleinfo[i].dobj);
8222 48792 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8223 48792 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8224 48792 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8225 48792 : if (ruleinfo[i].ruletable == NULL)
8226 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8227 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8228 48792 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8229 48792 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8230 48792 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8231 48792 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8232 48792 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8233 48792 : if (ruleinfo[i].ruletable)
8234 : {
8235 : /*
8236 : * If the table is a view or materialized view, force its ON
8237 : * SELECT rule to be sorted before the view itself --- this
8238 : * ensures that any dependencies for the rule affect the table's
8239 : * positioning. Other rules are forced to appear after their
8240 : * table.
8241 : */
8242 48792 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8243 1514 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8244 48330 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8245 : {
8246 47606 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8247 47606 : ruleinfo[i].dobj.dumpId);
8248 : /* We'll merge the rule into CREATE VIEW, if possible */
8249 47606 : ruleinfo[i].separate = false;
8250 : }
8251 : else
8252 : {
8253 1186 : addObjectDependency(&ruleinfo[i].dobj,
8254 1186 : ruleinfo[i].ruletable->dobj.dumpId);
8255 1186 : ruleinfo[i].separate = true;
8256 : }
8257 : }
8258 : else
8259 0 : ruleinfo[i].separate = true;
8260 : }
8261 :
8262 320 : PQclear(res);
8263 :
8264 320 : destroyPQExpBuffer(query);
8265 320 : }
8266 :
8267 : /*
8268 : * getTriggers
8269 : * get information about every trigger on a dumpable table
8270 : *
8271 : * Note: trigger data is not returned directly to the caller, but it
8272 : * does get entered into the DumpableObject tables.
8273 : */
8274 : void
8275 320 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8276 : {
8277 320 : PQExpBuffer query = createPQExpBuffer();
8278 320 : PQExpBuffer tbloids = createPQExpBuffer();
8279 : PGresult *res;
8280 : int ntups;
8281 : int curtblindx;
8282 : TriggerInfo *tginfo;
8283 : int i_tableoid,
8284 : i_oid,
8285 : i_tgrelid,
8286 : i_tgname,
8287 : i_tgenabled,
8288 : i_tgispartition,
8289 : i_tgdef;
8290 :
8291 : /*
8292 : * We want to perform just one query against pg_trigger. However, we
8293 : * mustn't try to select every row of the catalog and then sort it out on
8294 : * the client side, because some of the server-side functions we need
8295 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8296 : * build an array of the OIDs of tables we care about (and now have lock
8297 : * on!), and use a WHERE clause to constrain which rows are selected.
8298 : */
8299 320 : appendPQExpBufferChar(tbloids, '{');
8300 85060 : for (int i = 0; i < numTables; i++)
8301 : {
8302 84740 : TableInfo *tbinfo = &tblinfo[i];
8303 :
8304 84740 : if (!tbinfo->hastriggers ||
8305 2268 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8306 82996 : continue;
8307 :
8308 : /* OK, we need info for this table */
8309 1744 : if (tbloids->len > 1) /* do we have more than the '{'? */
8310 1638 : appendPQExpBufferChar(tbloids, ',');
8311 1744 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8312 : }
8313 320 : appendPQExpBufferChar(tbloids, '}');
8314 :
8315 320 : if (fout->remoteVersion >= 150000)
8316 : {
8317 : /*
8318 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8319 : * result in non-forward-compatible dumps of WHEN clauses due to
8320 : * under-parenthesization.
8321 : *
8322 : * NB: We need to see partition triggers in case the tgenabled flag
8323 : * has been changed from the parent.
8324 : */
8325 320 : appendPQExpBuffer(query,
8326 : "SELECT t.tgrelid, t.tgname, "
8327 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8328 : "t.tgenabled, t.tableoid, t.oid, "
8329 : "t.tgparentid <> 0 AS tgispartition\n"
8330 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8331 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8332 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8333 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8334 : "OR t.tgenabled != u.tgenabled) "
8335 : "ORDER BY t.tgrelid, t.tgname",
8336 : tbloids->data);
8337 : }
8338 0 : else if (fout->remoteVersion >= 130000)
8339 : {
8340 : /*
8341 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8342 : * result in non-forward-compatible dumps of WHEN clauses due to
8343 : * under-parenthesization.
8344 : *
8345 : * NB: We need to see tgisinternal triggers in partitions, in case the
8346 : * tgenabled flag has been changed from the parent.
8347 : */
8348 0 : appendPQExpBuffer(query,
8349 : "SELECT t.tgrelid, t.tgname, "
8350 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8351 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8352 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8353 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8354 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8355 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8356 : "ORDER BY t.tgrelid, t.tgname",
8357 : tbloids->data);
8358 : }
8359 0 : else if (fout->remoteVersion >= 110000)
8360 : {
8361 : /*
8362 : * NB: We need to see tgisinternal triggers in partitions, in case the
8363 : * tgenabled flag has been changed from the parent. No tgparentid in
8364 : * version 11-12, so we have to match them via pg_depend.
8365 : *
8366 : * See above about pretty=true in pg_get_triggerdef.
8367 : */
8368 0 : appendPQExpBuffer(query,
8369 : "SELECT t.tgrelid, t.tgname, "
8370 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8371 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8372 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8373 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8374 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8375 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8376 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8377 : " d.objid = t.oid "
8378 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8379 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8380 : "ORDER BY t.tgrelid, t.tgname",
8381 : tbloids->data);
8382 : }
8383 : else
8384 : {
8385 : /* See above about pretty=true in pg_get_triggerdef */
8386 0 : appendPQExpBuffer(query,
8387 : "SELECT t.tgrelid, t.tgname, "
8388 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8389 : "t.tgenabled, false as tgispartition, "
8390 : "t.tableoid, t.oid "
8391 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8392 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8393 : "WHERE NOT tgisinternal "
8394 : "ORDER BY t.tgrelid, t.tgname",
8395 : tbloids->data);
8396 : }
8397 :
8398 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8399 :
8400 320 : ntups = PQntuples(res);
8401 :
8402 320 : i_tableoid = PQfnumber(res, "tableoid");
8403 320 : i_oid = PQfnumber(res, "oid");
8404 320 : i_tgrelid = PQfnumber(res, "tgrelid");
8405 320 : i_tgname = PQfnumber(res, "tgname");
8406 320 : i_tgenabled = PQfnumber(res, "tgenabled");
8407 320 : i_tgispartition = PQfnumber(res, "tgispartition");
8408 320 : i_tgdef = PQfnumber(res, "tgdef");
8409 :
8410 320 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8411 :
8412 : /*
8413 : * Outer loop iterates once per table, not once per row. Incrementing of
8414 : * j is handled by the inner loop.
8415 : */
8416 320 : curtblindx = -1;
8417 952 : for (int j = 0; j < ntups;)
8418 : {
8419 632 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8420 632 : TableInfo *tbinfo = NULL;
8421 : int numtrigs;
8422 :
8423 : /* Count rows for this table */
8424 1066 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8425 960 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8426 526 : break;
8427 :
8428 : /*
8429 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8430 : * order.
8431 : */
8432 32592 : while (++curtblindx < numTables)
8433 : {
8434 32592 : tbinfo = &tblinfo[curtblindx];
8435 32592 : if (tbinfo->dobj.catId.oid == tgrelid)
8436 632 : break;
8437 : }
8438 632 : if (curtblindx >= numTables)
8439 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8440 :
8441 : /* Save data for this table */
8442 632 : tbinfo->triggers = tginfo + j;
8443 632 : tbinfo->numTriggers = numtrigs;
8444 :
8445 1698 : for (int c = 0; c < numtrigs; c++, j++)
8446 : {
8447 1066 : tginfo[j].dobj.objType = DO_TRIGGER;
8448 1066 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8449 1066 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8450 1066 : AssignDumpId(&tginfo[j].dobj);
8451 1066 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8452 1066 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8453 1066 : tginfo[j].tgtable = tbinfo;
8454 1066 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8455 1066 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8456 1066 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8457 : }
8458 : }
8459 :
8460 320 : PQclear(res);
8461 :
8462 320 : destroyPQExpBuffer(query);
8463 320 : destroyPQExpBuffer(tbloids);
8464 320 : }
8465 :
8466 : /*
8467 : * getEventTriggers
8468 : * get information about event triggers
8469 : */
8470 : void
8471 320 : getEventTriggers(Archive *fout)
8472 : {
8473 : int i;
8474 : PQExpBuffer query;
8475 : PGresult *res;
8476 : EventTriggerInfo *evtinfo;
8477 : int i_tableoid,
8478 : i_oid,
8479 : i_evtname,
8480 : i_evtevent,
8481 : i_evtowner,
8482 : i_evttags,
8483 : i_evtfname,
8484 : i_evtenabled;
8485 : int ntups;
8486 :
8487 : /* Before 9.3, there are no event triggers */
8488 320 : if (fout->remoteVersion < 90300)
8489 0 : return;
8490 :
8491 320 : query = createPQExpBuffer();
8492 :
8493 320 : appendPQExpBufferStr(query,
8494 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8495 : "evtevent, evtowner, "
8496 : "array_to_string(array("
8497 : "select quote_literal(x) "
8498 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8499 : "e.evtfoid::regproc as evtfname "
8500 : "FROM pg_event_trigger e "
8501 : "ORDER BY e.oid");
8502 :
8503 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8504 :
8505 320 : ntups = PQntuples(res);
8506 :
8507 320 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8508 :
8509 320 : i_tableoid = PQfnumber(res, "tableoid");
8510 320 : i_oid = PQfnumber(res, "oid");
8511 320 : i_evtname = PQfnumber(res, "evtname");
8512 320 : i_evtevent = PQfnumber(res, "evtevent");
8513 320 : i_evtowner = PQfnumber(res, "evtowner");
8514 320 : i_evttags = PQfnumber(res, "evttags");
8515 320 : i_evtfname = PQfnumber(res, "evtfname");
8516 320 : i_evtenabled = PQfnumber(res, "evtenabled");
8517 :
8518 428 : for (i = 0; i < ntups; i++)
8519 : {
8520 108 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8521 108 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8522 108 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8523 108 : AssignDumpId(&evtinfo[i].dobj);
8524 108 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8525 108 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8526 108 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8527 108 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8528 108 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8529 108 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8530 108 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8531 :
8532 : /* Decide whether we want to dump it */
8533 108 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8534 : }
8535 :
8536 320 : PQclear(res);
8537 :
8538 320 : destroyPQExpBuffer(query);
8539 : }
8540 :
8541 : /*
8542 : * getProcLangs
8543 : * get basic information about every procedural language in the system
8544 : *
8545 : * NB: this must run after getFuncs() because we assume we can do
8546 : * findFuncByOid().
8547 : */
8548 : void
8549 320 : getProcLangs(Archive *fout)
8550 : {
8551 : PGresult *res;
8552 : int ntups;
8553 : int i;
8554 320 : PQExpBuffer query = createPQExpBuffer();
8555 : ProcLangInfo *planginfo;
8556 : int i_tableoid;
8557 : int i_oid;
8558 : int i_lanname;
8559 : int i_lanpltrusted;
8560 : int i_lanplcallfoid;
8561 : int i_laninline;
8562 : int i_lanvalidator;
8563 : int i_lanacl;
8564 : int i_acldefault;
8565 : int i_lanowner;
8566 :
8567 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8568 : "lanname, lanpltrusted, lanplcallfoid, "
8569 : "laninline, lanvalidator, "
8570 : "lanacl, "
8571 : "acldefault('l', lanowner) AS acldefault, "
8572 : "lanowner "
8573 : "FROM pg_language "
8574 : "WHERE lanispl "
8575 : "ORDER BY oid");
8576 :
8577 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8578 :
8579 320 : ntups = PQntuples(res);
8580 :
8581 320 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8582 :
8583 320 : i_tableoid = PQfnumber(res, "tableoid");
8584 320 : i_oid = PQfnumber(res, "oid");
8585 320 : i_lanname = PQfnumber(res, "lanname");
8586 320 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8587 320 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8588 320 : i_laninline = PQfnumber(res, "laninline");
8589 320 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8590 320 : i_lanacl = PQfnumber(res, "lanacl");
8591 320 : i_acldefault = PQfnumber(res, "acldefault");
8592 320 : i_lanowner = PQfnumber(res, "lanowner");
8593 :
8594 734 : for (i = 0; i < ntups; i++)
8595 : {
8596 414 : planginfo[i].dobj.objType = DO_PROCLANG;
8597 414 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8598 414 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8599 414 : AssignDumpId(&planginfo[i].dobj);
8600 :
8601 414 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8602 414 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8603 414 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8604 414 : planginfo[i].dacl.privtype = 0;
8605 414 : planginfo[i].dacl.initprivs = NULL;
8606 414 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8607 414 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8608 414 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8609 414 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8610 414 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8611 :
8612 : /* Decide whether we want to dump it */
8613 414 : selectDumpableProcLang(&(planginfo[i]), fout);
8614 :
8615 : /* Mark whether language has an ACL */
8616 414 : if (!PQgetisnull(res, i, i_lanacl))
8617 94 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8618 : }
8619 :
8620 320 : PQclear(res);
8621 :
8622 320 : destroyPQExpBuffer(query);
8623 320 : }
8624 :
8625 : /*
8626 : * getCasts
8627 : * get basic information about most casts in the system
8628 : *
8629 : * Skip casts from a range to its multirange, since we'll create those
8630 : * automatically.
8631 : */
8632 : void
8633 320 : getCasts(Archive *fout)
8634 : {
8635 : PGresult *res;
8636 : int ntups;
8637 : int i;
8638 320 : PQExpBuffer query = createPQExpBuffer();
8639 : CastInfo *castinfo;
8640 : int i_tableoid;
8641 : int i_oid;
8642 : int i_castsource;
8643 : int i_casttarget;
8644 : int i_castfunc;
8645 : int i_castcontext;
8646 : int i_castmethod;
8647 :
8648 320 : if (fout->remoteVersion >= 140000)
8649 : {
8650 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8651 : "castsource, casttarget, castfunc, castcontext, "
8652 : "castmethod "
8653 : "FROM pg_cast c "
8654 : "WHERE NOT EXISTS ( "
8655 : "SELECT 1 FROM pg_range r "
8656 : "WHERE c.castsource = r.rngtypid "
8657 : "AND c.casttarget = r.rngmultitypid "
8658 : ") "
8659 : "ORDER BY 3,4");
8660 : }
8661 : else
8662 : {
8663 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8664 : "castsource, casttarget, castfunc, castcontext, "
8665 : "castmethod "
8666 : "FROM pg_cast ORDER BY 3,4");
8667 : }
8668 :
8669 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8670 :
8671 320 : ntups = PQntuples(res);
8672 :
8673 320 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8674 :
8675 320 : i_tableoid = PQfnumber(res, "tableoid");
8676 320 : i_oid = PQfnumber(res, "oid");
8677 320 : i_castsource = PQfnumber(res, "castsource");
8678 320 : i_casttarget = PQfnumber(res, "casttarget");
8679 320 : i_castfunc = PQfnumber(res, "castfunc");
8680 320 : i_castcontext = PQfnumber(res, "castcontext");
8681 320 : i_castmethod = PQfnumber(res, "castmethod");
8682 :
8683 71858 : for (i = 0; i < ntups; i++)
8684 : {
8685 : PQExpBufferData namebuf;
8686 : TypeInfo *sTypeInfo;
8687 : TypeInfo *tTypeInfo;
8688 :
8689 71538 : castinfo[i].dobj.objType = DO_CAST;
8690 71538 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8691 71538 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8692 71538 : AssignDumpId(&castinfo[i].dobj);
8693 71538 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8694 71538 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8695 71538 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8696 71538 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8697 71538 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8698 :
8699 : /*
8700 : * Try to name cast as concatenation of typnames. This is only used
8701 : * for purposes of sorting. If we fail to find either type, the name
8702 : * will be an empty string.
8703 : */
8704 71538 : initPQExpBuffer(&namebuf);
8705 71538 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8706 71538 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8707 71538 : if (sTypeInfo && tTypeInfo)
8708 71538 : appendPQExpBuffer(&namebuf, "%s %s",
8709 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8710 71538 : castinfo[i].dobj.name = namebuf.data;
8711 :
8712 : /* Decide whether we want to dump it */
8713 71538 : selectDumpableCast(&(castinfo[i]), fout);
8714 : }
8715 :
8716 320 : PQclear(res);
8717 :
8718 320 : destroyPQExpBuffer(query);
8719 320 : }
8720 :
8721 : static char *
8722 184 : get_language_name(Archive *fout, Oid langid)
8723 : {
8724 : PQExpBuffer query;
8725 : PGresult *res;
8726 : char *lanname;
8727 :
8728 184 : query = createPQExpBuffer();
8729 184 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8730 184 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8731 184 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8732 184 : destroyPQExpBuffer(query);
8733 184 : PQclear(res);
8734 :
8735 184 : return lanname;
8736 : }
8737 :
8738 : /*
8739 : * getTransforms
8740 : * get basic information about every transform in the system
8741 : */
8742 : void
8743 320 : getTransforms(Archive *fout)
8744 : {
8745 : PGresult *res;
8746 : int ntups;
8747 : int i;
8748 : PQExpBuffer query;
8749 : TransformInfo *transforminfo;
8750 : int i_tableoid;
8751 : int i_oid;
8752 : int i_trftype;
8753 : int i_trflang;
8754 : int i_trffromsql;
8755 : int i_trftosql;
8756 :
8757 : /* Transforms didn't exist pre-9.5 */
8758 320 : if (fout->remoteVersion < 90500)
8759 0 : return;
8760 :
8761 320 : query = createPQExpBuffer();
8762 :
8763 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8764 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8765 : "FROM pg_transform "
8766 : "ORDER BY 3,4");
8767 :
8768 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8769 :
8770 320 : ntups = PQntuples(res);
8771 :
8772 320 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8773 :
8774 320 : i_tableoid = PQfnumber(res, "tableoid");
8775 320 : i_oid = PQfnumber(res, "oid");
8776 320 : i_trftype = PQfnumber(res, "trftype");
8777 320 : i_trflang = PQfnumber(res, "trflang");
8778 320 : i_trffromsql = PQfnumber(res, "trffromsql");
8779 320 : i_trftosql = PQfnumber(res, "trftosql");
8780 :
8781 428 : for (i = 0; i < ntups; i++)
8782 : {
8783 : PQExpBufferData namebuf;
8784 : TypeInfo *typeInfo;
8785 : char *lanname;
8786 :
8787 108 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8788 108 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8789 108 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8790 108 : AssignDumpId(&transforminfo[i].dobj);
8791 108 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8792 108 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8793 108 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8794 108 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8795 :
8796 : /*
8797 : * Try to name transform as concatenation of type and language name.
8798 : * This is only used for purposes of sorting. If we fail to find
8799 : * either, the name will be an empty string.
8800 : */
8801 108 : initPQExpBuffer(&namebuf);
8802 108 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8803 108 : lanname = get_language_name(fout, transforminfo[i].trflang);
8804 108 : if (typeInfo && lanname)
8805 108 : appendPQExpBuffer(&namebuf, "%s %s",
8806 : typeInfo->dobj.name, lanname);
8807 108 : transforminfo[i].dobj.name = namebuf.data;
8808 108 : free(lanname);
8809 :
8810 : /* Decide whether we want to dump it */
8811 108 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8812 : }
8813 :
8814 320 : PQclear(res);
8815 :
8816 320 : destroyPQExpBuffer(query);
8817 : }
8818 :
8819 : /*
8820 : * getTableAttrs -
8821 : * for each interesting table, read info about its attributes
8822 : * (names, types, default values, CHECK constraints, etc)
8823 : *
8824 : * modifies tblinfo
8825 : */
8826 : void
8827 320 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8828 : {
8829 320 : DumpOptions *dopt = fout->dopt;
8830 320 : PQExpBuffer q = createPQExpBuffer();
8831 320 : PQExpBuffer tbloids = createPQExpBuffer();
8832 320 : PQExpBuffer checkoids = createPQExpBuffer();
8833 : PGresult *res;
8834 : int ntups;
8835 : int curtblindx;
8836 : int i_attrelid;
8837 : int i_attnum;
8838 : int i_attname;
8839 : int i_atttypname;
8840 : int i_attstattarget;
8841 : int i_attstorage;
8842 : int i_typstorage;
8843 : int i_attidentity;
8844 : int i_attgenerated;
8845 : int i_attisdropped;
8846 : int i_attlen;
8847 : int i_attalign;
8848 : int i_attislocal;
8849 : int i_notnull_name;
8850 : int i_notnull_noinherit;
8851 : int i_notnull_islocal;
8852 : int i_attoptions;
8853 : int i_attcollation;
8854 : int i_attcompression;
8855 : int i_attfdwoptions;
8856 : int i_attmissingval;
8857 : int i_atthasdef;
8858 :
8859 : /*
8860 : * We want to perform just one query against pg_attribute, and then just
8861 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8862 : * (for CHECK constraints and for NOT NULL constraints). However, we
8863 : * mustn't try to select every row of those catalogs and then sort it out
8864 : * on the client side, because some of the server-side functions we need
8865 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8866 : * build an array of the OIDs of tables we care about (and now have lock
8867 : * on!), and use a WHERE clause to constrain which rows are selected.
8868 : */
8869 320 : appendPQExpBufferChar(tbloids, '{');
8870 320 : appendPQExpBufferChar(checkoids, '{');
8871 85060 : for (int i = 0; i < numTables; i++)
8872 : {
8873 84740 : TableInfo *tbinfo = &tblinfo[i];
8874 :
8875 : /* Don't bother to collect info for sequences */
8876 84740 : if (tbinfo->relkind == RELKIND_SEQUENCE)
8877 1278 : continue;
8878 :
8879 : /* Don't bother with uninteresting tables, either */
8880 83462 : if (!tbinfo->interesting)
8881 70952 : continue;
8882 :
8883 : /* OK, we need info for this table */
8884 12510 : if (tbloids->len > 1) /* do we have more than the '{'? */
8885 12298 : appendPQExpBufferChar(tbloids, ',');
8886 12510 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8887 :
8888 12510 : if (tbinfo->ncheck > 0)
8889 : {
8890 : /* Also make a list of the ones with check constraints */
8891 1088 : if (checkoids->len > 1) /* do we have more than the '{'? */
8892 946 : appendPQExpBufferChar(checkoids, ',');
8893 1088 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8894 : }
8895 : }
8896 320 : appendPQExpBufferChar(tbloids, '}');
8897 320 : appendPQExpBufferChar(checkoids, '}');
8898 :
8899 : /*
8900 : * Find all the user attributes and their types.
8901 : *
8902 : * Since we only want to dump COLLATE clauses for attributes whose
8903 : * collation is different from their type's default, we use a CASE here to
8904 : * suppress uninteresting attcollations cheaply.
8905 : */
8906 320 : appendPQExpBufferStr(q,
8907 : "SELECT\n"
8908 : "a.attrelid,\n"
8909 : "a.attnum,\n"
8910 : "a.attname,\n"
8911 : "a.attstattarget,\n"
8912 : "a.attstorage,\n"
8913 : "t.typstorage,\n"
8914 : "a.atthasdef,\n"
8915 : "a.attisdropped,\n"
8916 : "a.attlen,\n"
8917 : "a.attalign,\n"
8918 : "a.attislocal,\n"
8919 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8920 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8921 : "CASE WHEN a.attcollation <> t.typcollation "
8922 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8923 : "pg_catalog.array_to_string(ARRAY("
8924 : "SELECT pg_catalog.quote_ident(option_name) || "
8925 : "' ' || pg_catalog.quote_literal(option_value) "
8926 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8927 : "ORDER BY option_name"
8928 : "), E',\n ') AS attfdwoptions,\n");
8929 :
8930 : /*
8931 : * Find out any NOT NULL markings for each column. In 18 and up we read
8932 : * pg_constraint to obtain the constraint name. notnull_noinherit is set
8933 : * according to the NO INHERIT property. For versions prior to 18, we
8934 : * store an empty string as the name when a constraint is marked as
8935 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
8936 : * without a name); also, such cases are never NO INHERIT.
8937 : *
8938 : * We track in notnull_islocal whether the constraint was defined directly
8939 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
8940 : * might modify this later for servers older than 18; it's also in charge
8941 : * of determining the correct inhcount.
8942 : */
8943 320 : if (fout->remoteVersion >= 180000)
8944 320 : appendPQExpBufferStr(q,
8945 : "co.conname AS notnull_name,\n"
8946 : "co.connoinherit AS notnull_noinherit,\n"
8947 : "co.conislocal AS notnull_islocal,\n");
8948 : else
8949 0 : appendPQExpBufferStr(q,
8950 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8951 : "false AS notnull_noinherit,\n"
8952 : "a.attislocal AS notnull_islocal,\n");
8953 :
8954 320 : if (fout->remoteVersion >= 140000)
8955 320 : appendPQExpBufferStr(q,
8956 : "a.attcompression AS attcompression,\n");
8957 : else
8958 0 : appendPQExpBufferStr(q,
8959 : "'' AS attcompression,\n");
8960 :
8961 320 : if (fout->remoteVersion >= 100000)
8962 320 : appendPQExpBufferStr(q,
8963 : "a.attidentity,\n");
8964 : else
8965 0 : appendPQExpBufferStr(q,
8966 : "'' AS attidentity,\n");
8967 :
8968 320 : if (fout->remoteVersion >= 110000)
8969 320 : appendPQExpBufferStr(q,
8970 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8971 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8972 : else
8973 0 : appendPQExpBufferStr(q,
8974 : "NULL AS attmissingval,\n");
8975 :
8976 320 : if (fout->remoteVersion >= 120000)
8977 320 : appendPQExpBufferStr(q,
8978 : "a.attgenerated\n");
8979 : else
8980 0 : appendPQExpBufferStr(q,
8981 : "'' AS attgenerated\n");
8982 :
8983 : /* need left join to pg_type to not fail on dropped columns ... */
8984 320 : appendPQExpBuffer(q,
8985 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8986 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8987 : "LEFT JOIN pg_catalog.pg_type t "
8988 : "ON (a.atttypid = t.oid)\n",
8989 : tbloids->data);
8990 :
8991 : /*
8992 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
8993 : * entries. Also, we need to know if the NOT NULL for each column is
8994 : * backing a primary key.
8995 : */
8996 320 : if (fout->remoteVersion >= 180000)
8997 320 : appendPQExpBufferStr(q,
8998 : " LEFT JOIN pg_catalog.pg_constraint co ON "
8999 : "(a.attrelid = co.conrelid\n"
9000 : " AND co.contype = 'n' AND "
9001 : "co.conkey = array[a.attnum])\n");
9002 :
9003 320 : appendPQExpBufferStr(q,
9004 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9005 : "ORDER BY a.attrelid, a.attnum");
9006 :
9007 320 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9008 :
9009 320 : ntups = PQntuples(res);
9010 :
9011 320 : i_attrelid = PQfnumber(res, "attrelid");
9012 320 : i_attnum = PQfnumber(res, "attnum");
9013 320 : i_attname = PQfnumber(res, "attname");
9014 320 : i_atttypname = PQfnumber(res, "atttypname");
9015 320 : i_attstattarget = PQfnumber(res, "attstattarget");
9016 320 : i_attstorage = PQfnumber(res, "attstorage");
9017 320 : i_typstorage = PQfnumber(res, "typstorage");
9018 320 : i_attidentity = PQfnumber(res, "attidentity");
9019 320 : i_attgenerated = PQfnumber(res, "attgenerated");
9020 320 : i_attisdropped = PQfnumber(res, "attisdropped");
9021 320 : i_attlen = PQfnumber(res, "attlen");
9022 320 : i_attalign = PQfnumber(res, "attalign");
9023 320 : i_attislocal = PQfnumber(res, "attislocal");
9024 320 : i_notnull_name = PQfnumber(res, "notnull_name");
9025 320 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9026 320 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9027 320 : i_attoptions = PQfnumber(res, "attoptions");
9028 320 : i_attcollation = PQfnumber(res, "attcollation");
9029 320 : i_attcompression = PQfnumber(res, "attcompression");
9030 320 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9031 320 : i_attmissingval = PQfnumber(res, "attmissingval");
9032 320 : i_atthasdef = PQfnumber(res, "atthasdef");
9033 :
9034 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9035 320 : resetPQExpBuffer(tbloids);
9036 320 : appendPQExpBufferChar(tbloids, '{');
9037 :
9038 : /*
9039 : * Outer loop iterates once per table, not once per row. Incrementing of
9040 : * r is handled by the inner loop.
9041 : */
9042 320 : curtblindx = -1;
9043 12542 : for (int r = 0; r < ntups;)
9044 : {
9045 12222 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9046 12222 : TableInfo *tbinfo = NULL;
9047 : int numatts;
9048 : bool hasdefaults;
9049 :
9050 : /* Count rows for this table */
9051 47592 : for (numatts = 1; numatts < ntups - r; numatts++)
9052 47386 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9053 12016 : break;
9054 :
9055 : /*
9056 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9057 : * order.
9058 : */
9059 56508 : while (++curtblindx < numTables)
9060 : {
9061 56508 : tbinfo = &tblinfo[curtblindx];
9062 56508 : if (tbinfo->dobj.catId.oid == attrelid)
9063 12222 : break;
9064 : }
9065 12222 : if (curtblindx >= numTables)
9066 0 : pg_fatal("unrecognized table OID %u", attrelid);
9067 : /* cross-check that we only got requested tables */
9068 12222 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9069 12222 : !tbinfo->interesting)
9070 0 : pg_fatal("unexpected column data for table \"%s\"",
9071 : tbinfo->dobj.name);
9072 :
9073 : /* Save data for this table */
9074 12222 : tbinfo->numatts = numatts;
9075 12222 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9076 12222 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9077 12222 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9078 12222 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9079 12222 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9080 12222 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9081 12222 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9082 12222 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9083 12222 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9084 12222 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9085 12222 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9086 12222 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9087 12222 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9088 12222 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9089 12222 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9090 12222 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9091 12222 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9092 12222 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9093 12222 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9094 12222 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9095 12222 : hasdefaults = false;
9096 :
9097 59814 : for (int j = 0; j < numatts; j++, r++)
9098 : {
9099 47592 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9100 0 : pg_fatal("invalid column numbering in table \"%s\"",
9101 : tbinfo->dobj.name);
9102 47592 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9103 47592 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9104 47592 : if (PQgetisnull(res, r, i_attstattarget))
9105 47508 : tbinfo->attstattarget[j] = -1;
9106 : else
9107 84 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9108 47592 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9109 47592 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9110 47592 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9111 47592 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9112 47592 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9113 47592 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9114 47592 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9115 47592 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9116 47592 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9117 :
9118 : /* Handle not-null constraint name and flags */
9119 47592 : determineNotNullFlags(fout, res, r,
9120 : tbinfo, j,
9121 : i_notnull_name, i_notnull_noinherit,
9122 : i_notnull_islocal);
9123 :
9124 47592 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9125 47592 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9126 47592 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9127 47592 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9128 47592 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9129 47592 : tbinfo->attrdefs[j] = NULL; /* fix below */
9130 47592 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9131 2490 : hasdefaults = true;
9132 : }
9133 :
9134 12222 : if (hasdefaults)
9135 : {
9136 : /* Collect OIDs of interesting tables that have defaults */
9137 1888 : if (tbloids->len > 1) /* do we have more than the '{'? */
9138 1748 : appendPQExpBufferChar(tbloids, ',');
9139 1888 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9140 : }
9141 : }
9142 :
9143 320 : PQclear(res);
9144 :
9145 : /*
9146 : * Now get info about column defaults. This is skipped for a data-only
9147 : * dump, as it is only needed for table schemas.
9148 : */
9149 320 : if (dopt->dumpSchema && tbloids->len > 1)
9150 : {
9151 : AttrDefInfo *attrdefs;
9152 : int numDefaults;
9153 124 : TableInfo *tbinfo = NULL;
9154 :
9155 124 : pg_log_info("finding table default expressions");
9156 :
9157 124 : appendPQExpBufferChar(tbloids, '}');
9158 :
9159 124 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9160 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9161 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9162 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9163 : "ORDER BY a.adrelid, a.adnum",
9164 : tbloids->data);
9165 :
9166 124 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9167 :
9168 124 : numDefaults = PQntuples(res);
9169 124 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9170 :
9171 124 : curtblindx = -1;
9172 2418 : for (int j = 0; j < numDefaults; j++)
9173 : {
9174 2294 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9175 2294 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9176 2294 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9177 2294 : int adnum = atoi(PQgetvalue(res, j, 3));
9178 2294 : char *adsrc = PQgetvalue(res, j, 4);
9179 :
9180 : /*
9181 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9182 : * OID order.
9183 : */
9184 2294 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9185 : {
9186 37794 : while (++curtblindx < numTables)
9187 : {
9188 37794 : tbinfo = &tblinfo[curtblindx];
9189 37794 : if (tbinfo->dobj.catId.oid == adrelid)
9190 1752 : break;
9191 : }
9192 1752 : if (curtblindx >= numTables)
9193 0 : pg_fatal("unrecognized table OID %u", adrelid);
9194 : }
9195 :
9196 2294 : if (adnum <= 0 || adnum > tbinfo->numatts)
9197 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9198 : adnum, tbinfo->dobj.name);
9199 :
9200 : /*
9201 : * dropped columns shouldn't have defaults, but just in case,
9202 : * ignore 'em
9203 : */
9204 2294 : if (tbinfo->attisdropped[adnum - 1])
9205 0 : continue;
9206 :
9207 2294 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9208 2294 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9209 2294 : attrdefs[j].dobj.catId.oid = adoid;
9210 2294 : AssignDumpId(&attrdefs[j].dobj);
9211 2294 : attrdefs[j].adtable = tbinfo;
9212 2294 : attrdefs[j].adnum = adnum;
9213 2294 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9214 :
9215 2294 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9216 2294 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9217 :
9218 2294 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9219 :
9220 : /*
9221 : * Figure out whether the default/generation expression should be
9222 : * dumped as part of the main CREATE TABLE (or similar) command or
9223 : * as a separate ALTER TABLE (or similar) command. The preference
9224 : * is to put it into the CREATE command, but in some cases that's
9225 : * not possible.
9226 : */
9227 2294 : if (tbinfo->attgenerated[adnum - 1])
9228 : {
9229 : /*
9230 : * Column generation expressions cannot be dumped separately,
9231 : * because there is no syntax for it. By setting separate to
9232 : * false here we prevent the "default" from being processed as
9233 : * its own dumpable object. Later, flagInhAttrs() will mark
9234 : * it as not to be dumped at all, if possible (that is, if it
9235 : * can be inherited from a parent).
9236 : */
9237 1254 : attrdefs[j].separate = false;
9238 : }
9239 1040 : else if (tbinfo->relkind == RELKIND_VIEW)
9240 : {
9241 : /*
9242 : * Defaults on a VIEW must always be dumped as separate ALTER
9243 : * TABLE commands.
9244 : */
9245 68 : attrdefs[j].separate = true;
9246 : }
9247 972 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9248 : {
9249 : /* column will be suppressed, print default separately */
9250 8 : attrdefs[j].separate = true;
9251 : }
9252 : else
9253 : {
9254 964 : attrdefs[j].separate = false;
9255 : }
9256 :
9257 2294 : if (!attrdefs[j].separate)
9258 : {
9259 : /*
9260 : * Mark the default as needing to appear before the table, so
9261 : * that any dependencies it has must be emitted before the
9262 : * CREATE TABLE. If this is not possible, we'll change to
9263 : * "separate" mode while sorting dependencies.
9264 : */
9265 2218 : addObjectDependency(&tbinfo->dobj,
9266 2218 : attrdefs[j].dobj.dumpId);
9267 : }
9268 :
9269 2294 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9270 : }
9271 :
9272 124 : PQclear(res);
9273 : }
9274 :
9275 : /*
9276 : * Get info about table CHECK constraints. This is skipped for a
9277 : * data-only dump, as it is only needed for table schemas.
9278 : */
9279 320 : if (dopt->dumpSchema && checkoids->len > 2)
9280 : {
9281 : ConstraintInfo *constrs;
9282 : int numConstrs;
9283 : int i_tableoid;
9284 : int i_oid;
9285 : int i_conrelid;
9286 : int i_conname;
9287 : int i_consrc;
9288 : int i_conislocal;
9289 : int i_convalidated;
9290 :
9291 126 : pg_log_info("finding table check constraints");
9292 :
9293 126 : resetPQExpBuffer(q);
9294 126 : appendPQExpBuffer(q,
9295 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9296 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9297 : "conislocal, convalidated "
9298 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9299 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9300 : "WHERE contype = 'c' "
9301 : "ORDER BY c.conrelid, c.conname",
9302 : checkoids->data);
9303 :
9304 126 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9305 :
9306 126 : numConstrs = PQntuples(res);
9307 126 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9308 :
9309 126 : i_tableoid = PQfnumber(res, "tableoid");
9310 126 : i_oid = PQfnumber(res, "oid");
9311 126 : i_conrelid = PQfnumber(res, "conrelid");
9312 126 : i_conname = PQfnumber(res, "conname");
9313 126 : i_consrc = PQfnumber(res, "consrc");
9314 126 : i_conislocal = PQfnumber(res, "conislocal");
9315 126 : i_convalidated = PQfnumber(res, "convalidated");
9316 :
9317 : /* As above, this loop iterates once per table, not once per row */
9318 126 : curtblindx = -1;
9319 1112 : for (int j = 0; j < numConstrs;)
9320 : {
9321 986 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9322 986 : TableInfo *tbinfo = NULL;
9323 : int numcons;
9324 :
9325 : /* Count rows for this table */
9326 1264 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9327 1138 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9328 860 : break;
9329 :
9330 : /*
9331 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9332 : * OID order.
9333 : */
9334 36490 : while (++curtblindx < numTables)
9335 : {
9336 36490 : tbinfo = &tblinfo[curtblindx];
9337 36490 : if (tbinfo->dobj.catId.oid == conrelid)
9338 986 : break;
9339 : }
9340 986 : if (curtblindx >= numTables)
9341 0 : pg_fatal("unrecognized table OID %u", conrelid);
9342 :
9343 986 : if (numcons != tbinfo->ncheck)
9344 : {
9345 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9346 : "expected %d check constraints on table \"%s\" but found %d",
9347 : tbinfo->ncheck),
9348 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9349 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9350 0 : exit_nicely(1);
9351 : }
9352 :
9353 986 : tbinfo->checkexprs = constrs + j;
9354 :
9355 2250 : for (int c = 0; c < numcons; c++, j++)
9356 : {
9357 1264 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9358 :
9359 1264 : constrs[j].dobj.objType = DO_CONSTRAINT;
9360 1264 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9361 1264 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9362 1264 : AssignDumpId(&constrs[j].dobj);
9363 1264 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9364 1264 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9365 1264 : constrs[j].contable = tbinfo;
9366 1264 : constrs[j].condomain = NULL;
9367 1264 : constrs[j].contype = 'c';
9368 1264 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9369 1264 : constrs[j].confrelid = InvalidOid;
9370 1264 : constrs[j].conindex = 0;
9371 1264 : constrs[j].condeferrable = false;
9372 1264 : constrs[j].condeferred = false;
9373 1264 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9374 :
9375 : /*
9376 : * An unvalidated constraint needs to be dumped separately, so
9377 : * that potentially-violating existing data is loaded before
9378 : * the constraint.
9379 : */
9380 1264 : constrs[j].separate = !validated;
9381 :
9382 1264 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9383 :
9384 : /*
9385 : * Mark the constraint as needing to appear before the table
9386 : * --- this is so that any other dependencies of the
9387 : * constraint will be emitted before we try to create the
9388 : * table. If the constraint is to be dumped separately, it
9389 : * will be dumped after data is loaded anyway, so don't do it.
9390 : * (There's an automatic dependency in the opposite direction
9391 : * anyway, so don't need to add one manually here.)
9392 : */
9393 1264 : if (!constrs[j].separate)
9394 1134 : addObjectDependency(&tbinfo->dobj,
9395 1134 : constrs[j].dobj.dumpId);
9396 :
9397 : /*
9398 : * We will detect later whether the constraint must be split
9399 : * out from the table definition.
9400 : */
9401 : }
9402 : }
9403 :
9404 126 : PQclear(res);
9405 : }
9406 :
9407 320 : destroyPQExpBuffer(q);
9408 320 : destroyPQExpBuffer(tbloids);
9409 320 : destroyPQExpBuffer(checkoids);
9410 320 : }
9411 :
9412 : /*
9413 : * Based on the getTableAttrs query's row corresponding to one column, set
9414 : * the name and flags to handle a not-null constraint for that column in
9415 : * the tbinfo struct.
9416 : *
9417 : * Result row 'r' is for tbinfo's attribute 'j'.
9418 : *
9419 : * There are three possibilities:
9420 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9421 : * (the constraint name) remains NULL.
9422 : * 2) The column has a constraint with no name (this is the case when
9423 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9424 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9425 : * 3) The column has a constraint with a known name; in that case
9426 : * notnull_constrs carries that name and dumpTableSchema will print
9427 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9428 : * (table_column_not_null), there's no need to print that name in the dump,
9429 : * so notnull_constrs is set to the empty string and it behaves as the case
9430 : * above.
9431 : *
9432 : * In a child table that inherits from a parent already containing NOT NULL
9433 : * constraints and the columns in the child don't have their own NOT NULL
9434 : * declarations, we suppress printing constraints in the child: the
9435 : * constraints are acquired at the point where the child is attached to the
9436 : * parent. This is tracked in ->notnull_islocal (which is set in flagInhAttrs
9437 : * for servers pre-18).
9438 : *
9439 : * Any of these constraints might have the NO INHERIT bit. If so we set
9440 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9441 : *
9442 : * In case 3 above, the name comparison is a bit of a hack; it actually fails
9443 : * to do the right thing in all but the trivial case. However, the downside
9444 : * of getting it wrong is simply that the name is printed rather than
9445 : * suppressed, so it's not a big deal.
9446 : */
9447 : static void
9448 47592 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9449 : TableInfo *tbinfo, int j,
9450 : int i_notnull_name, int i_notnull_noinherit,
9451 : int i_notnull_islocal)
9452 : {
9453 47592 : DumpOptions *dopt = fout->dopt;
9454 :
9455 : /*
9456 : * notnull_noinh is straight from the query result. notnull_islocal also,
9457 : * though flagInhAttrs may change that one later in versions < 18.
9458 : */
9459 47592 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9460 47592 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9461 :
9462 : /*
9463 : * Determine a constraint name to use. If the column is not marked not-
9464 : * null, we set NULL which cues ... to do nothing. An empty string says
9465 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9466 : * use.
9467 : */
9468 47592 : if (fout->remoteVersion < 180000)
9469 : {
9470 : /*
9471 : * < 18 doesn't have not-null names, so an unnamed constraint is
9472 : * sufficient.
9473 : */
9474 0 : if (PQgetisnull(res, r, i_notnull_name))
9475 0 : tbinfo->notnull_constrs[j] = NULL;
9476 : else
9477 0 : tbinfo->notnull_constrs[j] = "";
9478 : }
9479 : else
9480 : {
9481 47592 : if (PQgetisnull(res, r, i_notnull_name))
9482 42850 : tbinfo->notnull_constrs[j] = NULL;
9483 : else
9484 : {
9485 : /*
9486 : * In binary upgrade of inheritance child tables, must have a
9487 : * constraint name that we can UPDATE later.
9488 : */
9489 4742 : if (dopt->binary_upgrade &&
9490 538 : !tbinfo->ispartition &&
9491 396 : !tbinfo->notnull_islocal)
9492 : {
9493 0 : tbinfo->notnull_constrs[j] =
9494 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9495 : }
9496 : else
9497 : {
9498 : char *default_name;
9499 :
9500 : /* XXX should match ChooseConstraintName better */
9501 4742 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9502 4742 : tbinfo->attnames[j]);
9503 4742 : if (strcmp(default_name,
9504 4742 : PQgetvalue(res, r, i_notnull_name)) == 0)
9505 3226 : tbinfo->notnull_constrs[j] = "";
9506 : else
9507 : {
9508 1516 : tbinfo->notnull_constrs[j] =
9509 1516 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9510 : }
9511 4742 : free(default_name);
9512 : }
9513 : }
9514 : }
9515 47592 : }
9516 :
9517 : /*
9518 : * Test whether a column should be printed as part of table's CREATE TABLE.
9519 : * Column number is zero-based.
9520 : *
9521 : * Normally this is always true, but it's false for dropped columns, as well
9522 : * as those that were inherited without any local definition. (If we print
9523 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9524 : * For partitions, it's always true, because we want the partitions to be
9525 : * created independently and ATTACH PARTITION used afterwards.
9526 : *
9527 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9528 : * attisdropped state later, so as to keep control of the physical column
9529 : * order.
9530 : *
9531 : * This function exists because there are scattered nonobvious places that
9532 : * must be kept in sync with this decision.
9533 : */
9534 : bool
9535 78540 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9536 : {
9537 78540 : if (dopt->binary_upgrade)
9538 12080 : return true;
9539 66460 : if (tbinfo->attisdropped[colno])
9540 1460 : return false;
9541 65000 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9542 : }
9543 :
9544 :
9545 : /*
9546 : * getTSParsers:
9547 : * get information about all text search parsers in the system catalogs
9548 : */
9549 : void
9550 320 : getTSParsers(Archive *fout)
9551 : {
9552 : PGresult *res;
9553 : int ntups;
9554 : int i;
9555 : PQExpBuffer query;
9556 : TSParserInfo *prsinfo;
9557 : int i_tableoid;
9558 : int i_oid;
9559 : int i_prsname;
9560 : int i_prsnamespace;
9561 : int i_prsstart;
9562 : int i_prstoken;
9563 : int i_prsend;
9564 : int i_prsheadline;
9565 : int i_prslextype;
9566 :
9567 320 : query = createPQExpBuffer();
9568 :
9569 : /*
9570 : * find all text search objects, including builtin ones; we filter out
9571 : * system-defined objects at dump-out time.
9572 : */
9573 :
9574 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9575 : "prsstart::oid, prstoken::oid, "
9576 : "prsend::oid, prsheadline::oid, prslextype::oid "
9577 : "FROM pg_ts_parser");
9578 :
9579 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9580 :
9581 320 : ntups = PQntuples(res);
9582 :
9583 320 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9584 :
9585 320 : i_tableoid = PQfnumber(res, "tableoid");
9586 320 : i_oid = PQfnumber(res, "oid");
9587 320 : i_prsname = PQfnumber(res, "prsname");
9588 320 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9589 320 : i_prsstart = PQfnumber(res, "prsstart");
9590 320 : i_prstoken = PQfnumber(res, "prstoken");
9591 320 : i_prsend = PQfnumber(res, "prsend");
9592 320 : i_prsheadline = PQfnumber(res, "prsheadline");
9593 320 : i_prslextype = PQfnumber(res, "prslextype");
9594 :
9595 734 : for (i = 0; i < ntups; i++)
9596 : {
9597 414 : prsinfo[i].dobj.objType = DO_TSPARSER;
9598 414 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9599 414 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9600 414 : AssignDumpId(&prsinfo[i].dobj);
9601 414 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9602 828 : prsinfo[i].dobj.namespace =
9603 414 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9604 414 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9605 414 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9606 414 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9607 414 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9608 414 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9609 :
9610 : /* Decide whether we want to dump it */
9611 414 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9612 : }
9613 :
9614 320 : PQclear(res);
9615 :
9616 320 : destroyPQExpBuffer(query);
9617 320 : }
9618 :
9619 : /*
9620 : * getTSDictionaries:
9621 : * get information about all text search dictionaries in the system catalogs
9622 : */
9623 : void
9624 320 : getTSDictionaries(Archive *fout)
9625 : {
9626 : PGresult *res;
9627 : int ntups;
9628 : int i;
9629 : PQExpBuffer query;
9630 : TSDictInfo *dictinfo;
9631 : int i_tableoid;
9632 : int i_oid;
9633 : int i_dictname;
9634 : int i_dictnamespace;
9635 : int i_dictowner;
9636 : int i_dicttemplate;
9637 : int i_dictinitoption;
9638 :
9639 320 : query = createPQExpBuffer();
9640 :
9641 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9642 : "dictnamespace, dictowner, "
9643 : "dicttemplate, dictinitoption "
9644 : "FROM pg_ts_dict");
9645 :
9646 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9647 :
9648 320 : ntups = PQntuples(res);
9649 :
9650 320 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9651 :
9652 320 : i_tableoid = PQfnumber(res, "tableoid");
9653 320 : i_oid = PQfnumber(res, "oid");
9654 320 : i_dictname = PQfnumber(res, "dictname");
9655 320 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9656 320 : i_dictowner = PQfnumber(res, "dictowner");
9657 320 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9658 320 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9659 :
9660 10140 : for (i = 0; i < ntups; i++)
9661 : {
9662 9820 : dictinfo[i].dobj.objType = DO_TSDICT;
9663 9820 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9664 9820 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9665 9820 : AssignDumpId(&dictinfo[i].dobj);
9666 9820 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9667 19640 : dictinfo[i].dobj.namespace =
9668 9820 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9669 9820 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9670 9820 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9671 9820 : if (PQgetisnull(res, i, i_dictinitoption))
9672 414 : dictinfo[i].dictinitoption = NULL;
9673 : else
9674 9406 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9675 :
9676 : /* Decide whether we want to dump it */
9677 9820 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9678 : }
9679 :
9680 320 : PQclear(res);
9681 :
9682 320 : destroyPQExpBuffer(query);
9683 320 : }
9684 :
9685 : /*
9686 : * getTSTemplates:
9687 : * get information about all text search templates in the system catalogs
9688 : */
9689 : void
9690 320 : getTSTemplates(Archive *fout)
9691 : {
9692 : PGresult *res;
9693 : int ntups;
9694 : int i;
9695 : PQExpBuffer query;
9696 : TSTemplateInfo *tmplinfo;
9697 : int i_tableoid;
9698 : int i_oid;
9699 : int i_tmplname;
9700 : int i_tmplnamespace;
9701 : int i_tmplinit;
9702 : int i_tmpllexize;
9703 :
9704 320 : query = createPQExpBuffer();
9705 :
9706 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9707 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9708 : "FROM pg_ts_template");
9709 :
9710 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9711 :
9712 320 : ntups = PQntuples(res);
9713 :
9714 320 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9715 :
9716 320 : i_tableoid = PQfnumber(res, "tableoid");
9717 320 : i_oid = PQfnumber(res, "oid");
9718 320 : i_tmplname = PQfnumber(res, "tmplname");
9719 320 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9720 320 : i_tmplinit = PQfnumber(res, "tmplinit");
9721 320 : i_tmpllexize = PQfnumber(res, "tmpllexize");
9722 :
9723 2014 : for (i = 0; i < ntups; i++)
9724 : {
9725 1694 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9726 1694 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9727 1694 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9728 1694 : AssignDumpId(&tmplinfo[i].dobj);
9729 1694 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9730 3388 : tmplinfo[i].dobj.namespace =
9731 1694 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9732 1694 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9733 1694 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9734 :
9735 : /* Decide whether we want to dump it */
9736 1694 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
9737 : }
9738 :
9739 320 : PQclear(res);
9740 :
9741 320 : destroyPQExpBuffer(query);
9742 320 : }
9743 :
9744 : /*
9745 : * getTSConfigurations:
9746 : * get information about all text search configurations
9747 : */
9748 : void
9749 320 : getTSConfigurations(Archive *fout)
9750 : {
9751 : PGresult *res;
9752 : int ntups;
9753 : int i;
9754 : PQExpBuffer query;
9755 : TSConfigInfo *cfginfo;
9756 : int i_tableoid;
9757 : int i_oid;
9758 : int i_cfgname;
9759 : int i_cfgnamespace;
9760 : int i_cfgowner;
9761 : int i_cfgparser;
9762 :
9763 320 : query = createPQExpBuffer();
9764 :
9765 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9766 : "cfgnamespace, cfgowner, cfgparser "
9767 : "FROM pg_ts_config");
9768 :
9769 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9770 :
9771 320 : ntups = PQntuples(res);
9772 :
9773 320 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9774 :
9775 320 : i_tableoid = PQfnumber(res, "tableoid");
9776 320 : i_oid = PQfnumber(res, "oid");
9777 320 : i_cfgname = PQfnumber(res, "cfgname");
9778 320 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9779 320 : i_cfgowner = PQfnumber(res, "cfgowner");
9780 320 : i_cfgparser = PQfnumber(res, "cfgparser");
9781 :
9782 10070 : for (i = 0; i < ntups; i++)
9783 : {
9784 9750 : cfginfo[i].dobj.objType = DO_TSCONFIG;
9785 9750 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9786 9750 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9787 9750 : AssignDumpId(&cfginfo[i].dobj);
9788 9750 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9789 19500 : cfginfo[i].dobj.namespace =
9790 9750 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9791 9750 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9792 9750 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9793 :
9794 : /* Decide whether we want to dump it */
9795 9750 : selectDumpableObject(&(cfginfo[i].dobj), fout);
9796 : }
9797 :
9798 320 : PQclear(res);
9799 :
9800 320 : destroyPQExpBuffer(query);
9801 320 : }
9802 :
9803 : /*
9804 : * getForeignDataWrappers:
9805 : * get information about all foreign-data wrappers in the system catalogs
9806 : */
9807 : void
9808 320 : getForeignDataWrappers(Archive *fout)
9809 : {
9810 : PGresult *res;
9811 : int ntups;
9812 : int i;
9813 : PQExpBuffer query;
9814 : FdwInfo *fdwinfo;
9815 : int i_tableoid;
9816 : int i_oid;
9817 : int i_fdwname;
9818 : int i_fdwowner;
9819 : int i_fdwhandler;
9820 : int i_fdwvalidator;
9821 : int i_fdwacl;
9822 : int i_acldefault;
9823 : int i_fdwoptions;
9824 :
9825 320 : query = createPQExpBuffer();
9826 :
9827 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9828 : "fdwowner, "
9829 : "fdwhandler::pg_catalog.regproc, "
9830 : "fdwvalidator::pg_catalog.regproc, "
9831 : "fdwacl, "
9832 : "acldefault('F', fdwowner) AS acldefault, "
9833 : "array_to_string(ARRAY("
9834 : "SELECT quote_ident(option_name) || ' ' || "
9835 : "quote_literal(option_value) "
9836 : "FROM pg_options_to_table(fdwoptions) "
9837 : "ORDER BY option_name"
9838 : "), E',\n ') AS fdwoptions "
9839 : "FROM pg_foreign_data_wrapper");
9840 :
9841 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9842 :
9843 320 : ntups = PQntuples(res);
9844 :
9845 320 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9846 :
9847 320 : i_tableoid = PQfnumber(res, "tableoid");
9848 320 : i_oid = PQfnumber(res, "oid");
9849 320 : i_fdwname = PQfnumber(res, "fdwname");
9850 320 : i_fdwowner = PQfnumber(res, "fdwowner");
9851 320 : i_fdwhandler = PQfnumber(res, "fdwhandler");
9852 320 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9853 320 : i_fdwacl = PQfnumber(res, "fdwacl");
9854 320 : i_acldefault = PQfnumber(res, "acldefault");
9855 320 : i_fdwoptions = PQfnumber(res, "fdwoptions");
9856 :
9857 466 : for (i = 0; i < ntups; i++)
9858 : {
9859 146 : fdwinfo[i].dobj.objType = DO_FDW;
9860 146 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9861 146 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9862 146 : AssignDumpId(&fdwinfo[i].dobj);
9863 146 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9864 146 : fdwinfo[i].dobj.namespace = NULL;
9865 146 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9866 146 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9867 146 : fdwinfo[i].dacl.privtype = 0;
9868 146 : fdwinfo[i].dacl.initprivs = NULL;
9869 146 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9870 146 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9871 146 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9872 146 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9873 :
9874 : /* Decide whether we want to dump it */
9875 146 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
9876 :
9877 : /* Mark whether FDW has an ACL */
9878 146 : if (!PQgetisnull(res, i, i_fdwacl))
9879 94 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9880 : }
9881 :
9882 320 : PQclear(res);
9883 :
9884 320 : destroyPQExpBuffer(query);
9885 320 : }
9886 :
9887 : /*
9888 : * getForeignServers:
9889 : * get information about all foreign servers in the system catalogs
9890 : */
9891 : void
9892 320 : getForeignServers(Archive *fout)
9893 : {
9894 : PGresult *res;
9895 : int ntups;
9896 : int i;
9897 : PQExpBuffer query;
9898 : ForeignServerInfo *srvinfo;
9899 : int i_tableoid;
9900 : int i_oid;
9901 : int i_srvname;
9902 : int i_srvowner;
9903 : int i_srvfdw;
9904 : int i_srvtype;
9905 : int i_srvversion;
9906 : int i_srvacl;
9907 : int i_acldefault;
9908 : int i_srvoptions;
9909 :
9910 320 : query = createPQExpBuffer();
9911 :
9912 320 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9913 : "srvowner, "
9914 : "srvfdw, srvtype, srvversion, srvacl, "
9915 : "acldefault('S', srvowner) AS acldefault, "
9916 : "array_to_string(ARRAY("
9917 : "SELECT quote_ident(option_name) || ' ' || "
9918 : "quote_literal(option_value) "
9919 : "FROM pg_options_to_table(srvoptions) "
9920 : "ORDER BY option_name"
9921 : "), E',\n ') AS srvoptions "
9922 : "FROM pg_foreign_server");
9923 :
9924 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9925 :
9926 320 : ntups = PQntuples(res);
9927 :
9928 320 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9929 :
9930 320 : i_tableoid = PQfnumber(res, "tableoid");
9931 320 : i_oid = PQfnumber(res, "oid");
9932 320 : i_srvname = PQfnumber(res, "srvname");
9933 320 : i_srvowner = PQfnumber(res, "srvowner");
9934 320 : i_srvfdw = PQfnumber(res, "srvfdw");
9935 320 : i_srvtype = PQfnumber(res, "srvtype");
9936 320 : i_srvversion = PQfnumber(res, "srvversion");
9937 320 : i_srvacl = PQfnumber(res, "srvacl");
9938 320 : i_acldefault = PQfnumber(res, "acldefault");
9939 320 : i_srvoptions = PQfnumber(res, "srvoptions");
9940 :
9941 474 : for (i = 0; i < ntups; i++)
9942 : {
9943 154 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9944 154 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9945 154 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9946 154 : AssignDumpId(&srvinfo[i].dobj);
9947 154 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9948 154 : srvinfo[i].dobj.namespace = NULL;
9949 154 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9950 154 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9951 154 : srvinfo[i].dacl.privtype = 0;
9952 154 : srvinfo[i].dacl.initprivs = NULL;
9953 154 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9954 154 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9955 154 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9956 154 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9957 154 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9958 :
9959 : /* Decide whether we want to dump it */
9960 154 : selectDumpableObject(&(srvinfo[i].dobj), fout);
9961 :
9962 : /* Servers have user mappings */
9963 154 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9964 :
9965 : /* Mark whether server has an ACL */
9966 154 : if (!PQgetisnull(res, i, i_srvacl))
9967 94 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9968 : }
9969 :
9970 320 : PQclear(res);
9971 :
9972 320 : destroyPQExpBuffer(query);
9973 320 : }
9974 :
9975 : /*
9976 : * getDefaultACLs:
9977 : * get information about all default ACL information in the system catalogs
9978 : */
9979 : void
9980 320 : getDefaultACLs(Archive *fout)
9981 : {
9982 320 : DumpOptions *dopt = fout->dopt;
9983 : DefaultACLInfo *daclinfo;
9984 : PQExpBuffer query;
9985 : PGresult *res;
9986 : int i_oid;
9987 : int i_tableoid;
9988 : int i_defaclrole;
9989 : int i_defaclnamespace;
9990 : int i_defaclobjtype;
9991 : int i_defaclacl;
9992 : int i_acldefault;
9993 : int i,
9994 : ntups;
9995 :
9996 320 : query = createPQExpBuffer();
9997 :
9998 : /*
9999 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10000 : * ACL for their object type. We should dump them as deltas from the
10001 : * default ACL, since that will be used as a starting point for
10002 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10003 : * non-global entries can only add privileges not revoke them. We must
10004 : * dump those as-is (i.e., as deltas from an empty ACL).
10005 : *
10006 : * We can use defaclobjtype as the object type for acldefault(), except
10007 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10008 : * 's'.
10009 : */
10010 320 : appendPQExpBufferStr(query,
10011 : "SELECT oid, tableoid, "
10012 : "defaclrole, "
10013 : "defaclnamespace, "
10014 : "defaclobjtype, "
10015 : "defaclacl, "
10016 : "CASE WHEN defaclnamespace = 0 THEN "
10017 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10018 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10019 : "defaclrole) ELSE '{}' END AS acldefault "
10020 : "FROM pg_default_acl");
10021 :
10022 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10023 :
10024 320 : ntups = PQntuples(res);
10025 :
10026 320 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10027 :
10028 320 : i_oid = PQfnumber(res, "oid");
10029 320 : i_tableoid = PQfnumber(res, "tableoid");
10030 320 : i_defaclrole = PQfnumber(res, "defaclrole");
10031 320 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10032 320 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10033 320 : i_defaclacl = PQfnumber(res, "defaclacl");
10034 320 : i_acldefault = PQfnumber(res, "acldefault");
10035 :
10036 696 : for (i = 0; i < ntups; i++)
10037 : {
10038 376 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10039 :
10040 376 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10041 376 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10042 376 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10043 376 : AssignDumpId(&daclinfo[i].dobj);
10044 : /* cheesy ... is it worth coming up with a better object name? */
10045 376 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10046 :
10047 376 : if (nspid != InvalidOid)
10048 188 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10049 : else
10050 188 : daclinfo[i].dobj.namespace = NULL;
10051 :
10052 376 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10053 376 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10054 376 : daclinfo[i].dacl.privtype = 0;
10055 376 : daclinfo[i].dacl.initprivs = NULL;
10056 376 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10057 376 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10058 :
10059 : /* Default ACLs are ACLs, of course */
10060 376 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10061 :
10062 : /* Decide whether we want to dump it */
10063 376 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10064 : }
10065 :
10066 320 : PQclear(res);
10067 :
10068 320 : destroyPQExpBuffer(query);
10069 320 : }
10070 :
10071 : /*
10072 : * getRoleName -- look up the name of a role, given its OID
10073 : *
10074 : * In current usage, we don't expect failures, so error out for a bad OID.
10075 : */
10076 : static const char *
10077 1006672 : getRoleName(const char *roleoid_str)
10078 : {
10079 1006672 : Oid roleoid = atooid(roleoid_str);
10080 :
10081 : /*
10082 : * Do binary search to find the appropriate item.
10083 : */
10084 1006672 : if (nrolenames > 0)
10085 : {
10086 1006672 : RoleNameItem *low = &rolenames[0];
10087 1006672 : RoleNameItem *high = &rolenames[nrolenames - 1];
10088 :
10089 4027094 : while (low <= high)
10090 : {
10091 4027094 : RoleNameItem *middle = low + (high - low) / 2;
10092 :
10093 4027094 : if (roleoid < middle->roleoid)
10094 3017806 : high = middle - 1;
10095 1009288 : else if (roleoid > middle->roleoid)
10096 2616 : low = middle + 1;
10097 : else
10098 1006672 : return middle->rolename; /* found a match */
10099 : }
10100 : }
10101 :
10102 0 : pg_fatal("role with OID %u does not exist", roleoid);
10103 : return NULL; /* keep compiler quiet */
10104 : }
10105 :
10106 : /*
10107 : * collectRoleNames --
10108 : *
10109 : * Construct a table of all known roles.
10110 : * The table is sorted by OID for speed in lookup.
10111 : */
10112 : static void
10113 322 : collectRoleNames(Archive *fout)
10114 : {
10115 : PGresult *res;
10116 : const char *query;
10117 : int i;
10118 :
10119 322 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10120 :
10121 322 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10122 :
10123 322 : nrolenames = PQntuples(res);
10124 :
10125 322 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10126 :
10127 6212 : for (i = 0; i < nrolenames; i++)
10128 : {
10129 5890 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10130 5890 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10131 : }
10132 :
10133 322 : PQclear(res);
10134 322 : }
10135 :
10136 : /*
10137 : * getAdditionalACLs
10138 : *
10139 : * We have now created all the DumpableObjects, and collected the ACL data
10140 : * that appears in the directly-associated catalog entries. However, there's
10141 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10142 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10143 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10144 : * Also, in versions having the pg_init_privs catalog, read that and load the
10145 : * information into the relevant DumpableObjects.
10146 : */
10147 : static void
10148 316 : getAdditionalACLs(Archive *fout)
10149 : {
10150 316 : PQExpBuffer query = createPQExpBuffer();
10151 : PGresult *res;
10152 : int ntups,
10153 : i;
10154 :
10155 : /* Check for per-column ACLs */
10156 316 : appendPQExpBufferStr(query,
10157 : "SELECT DISTINCT attrelid FROM pg_attribute "
10158 : "WHERE attacl IS NOT NULL");
10159 :
10160 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10161 :
10162 316 : ntups = PQntuples(res);
10163 984 : for (i = 0; i < ntups; i++)
10164 : {
10165 668 : Oid relid = atooid(PQgetvalue(res, i, 0));
10166 : TableInfo *tblinfo;
10167 :
10168 668 : tblinfo = findTableByOid(relid);
10169 : /* OK to ignore tables we haven't got a DumpableObject for */
10170 668 : if (tblinfo)
10171 : {
10172 668 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10173 668 : tblinfo->hascolumnACLs = true;
10174 : }
10175 : }
10176 316 : PQclear(res);
10177 :
10178 : /* Fetch initial-privileges data */
10179 316 : if (fout->remoteVersion >= 90600)
10180 : {
10181 316 : printfPQExpBuffer(query,
10182 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10183 : "FROM pg_init_privs");
10184 :
10185 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10186 :
10187 316 : ntups = PQntuples(res);
10188 72032 : for (i = 0; i < ntups; i++)
10189 : {
10190 71716 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10191 71716 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10192 71716 : int objsubid = atoi(PQgetvalue(res, i, 2));
10193 71716 : char privtype = *(PQgetvalue(res, i, 3));
10194 71716 : char *initprivs = PQgetvalue(res, i, 4);
10195 : CatalogId objId;
10196 : DumpableObject *dobj;
10197 :
10198 71716 : objId.tableoid = classoid;
10199 71716 : objId.oid = objoid;
10200 71716 : dobj = findObjectByCatalogId(objId);
10201 : /* OK to ignore entries we haven't got a DumpableObject for */
10202 71716 : if (dobj)
10203 : {
10204 : /* Cope with sub-object initprivs */
10205 51584 : if (objsubid != 0)
10206 : {
10207 5420 : if (dobj->objType == DO_TABLE)
10208 : {
10209 : /* For a column initprivs, set the table's ACL flags */
10210 5420 : dobj->components |= DUMP_COMPONENT_ACL;
10211 5420 : ((TableInfo *) dobj)->hascolumnACLs = true;
10212 : }
10213 : else
10214 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10215 : classoid, objoid, objsubid);
10216 5728 : continue;
10217 : }
10218 :
10219 : /*
10220 : * We ignore any pg_init_privs.initprivs entry for the public
10221 : * schema, as explained in getNamespaces().
10222 : */
10223 46164 : if (dobj->objType == DO_NAMESPACE &&
10224 624 : strcmp(dobj->name, "public") == 0)
10225 308 : continue;
10226 :
10227 : /* Else it had better be of a type we think has ACLs */
10228 45856 : if (dobj->objType == DO_NAMESPACE ||
10229 45540 : dobj->objType == DO_TYPE ||
10230 45492 : dobj->objType == DO_FUNC ||
10231 45304 : dobj->objType == DO_AGG ||
10232 45256 : dobj->objType == DO_TABLE ||
10233 0 : dobj->objType == DO_PROCLANG ||
10234 0 : dobj->objType == DO_FDW ||
10235 0 : dobj->objType == DO_FOREIGN_SERVER)
10236 45856 : {
10237 45856 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10238 :
10239 45856 : daobj->dacl.privtype = privtype;
10240 45856 : daobj->dacl.initprivs = pstrdup(initprivs);
10241 : }
10242 : else
10243 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10244 : classoid, objoid, objsubid);
10245 : }
10246 : }
10247 316 : PQclear(res);
10248 : }
10249 :
10250 316 : destroyPQExpBuffer(query);
10251 316 : }
10252 :
10253 : /*
10254 : * dumpCommentExtended --
10255 : *
10256 : * This routine is used to dump any comments associated with the
10257 : * object handed to this routine. The routine takes the object type
10258 : * and object name (ready to print, except for schema decoration), plus
10259 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10260 : * plus catalog ID and subid which are the lookup key for pg_description,
10261 : * plus the dump ID for the object (for setting a dependency).
10262 : * If a matching pg_description entry is found, it is dumped.
10263 : *
10264 : * Note: in some cases, such as comments for triggers and rules, the "type"
10265 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10266 : * but it doesn't seem worth complicating the API for all callers to make
10267 : * it cleaner.
10268 : *
10269 : * Note: although this routine takes a dumpId for dependency purposes,
10270 : * that purpose is just to mark the dependency in the emitted dump file
10271 : * for possible future use by pg_restore. We do NOT use it for determining
10272 : * ordering of the comment in the dump file, because this routine is called
10273 : * after dependency sorting occurs. This routine should be called just after
10274 : * calling ArchiveEntry() for the specified object.
10275 : */
10276 : static void
10277 12598 : dumpCommentExtended(Archive *fout, const char *type,
10278 : const char *name, const char *namespace,
10279 : const char *owner, CatalogId catalogId,
10280 : int subid, DumpId dumpId,
10281 : const char *initdb_comment)
10282 : {
10283 12598 : DumpOptions *dopt = fout->dopt;
10284 : CommentItem *comments;
10285 : int ncomments;
10286 :
10287 : /* do nothing, if --no-comments is supplied */
10288 12598 : if (dopt->no_comments)
10289 0 : return;
10290 :
10291 : /* Comments are schema not data ... except LO comments are data */
10292 12598 : if (strcmp(type, "LARGE OBJECT") != 0)
10293 : {
10294 12496 : if (!dopt->dumpSchema)
10295 0 : return;
10296 : }
10297 : else
10298 : {
10299 : /* We do dump LO comments in binary-upgrade mode */
10300 102 : if (!dopt->dumpData && !dopt->binary_upgrade)
10301 0 : return;
10302 : }
10303 :
10304 : /* Search for comments associated with catalogId, using table */
10305 12598 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10306 : &comments);
10307 :
10308 : /* Is there one matching the subid? */
10309 12598 : while (ncomments > 0)
10310 : {
10311 12512 : if (comments->objsubid == subid)
10312 12512 : break;
10313 0 : comments++;
10314 0 : ncomments--;
10315 : }
10316 :
10317 12598 : if (initdb_comment != NULL)
10318 : {
10319 : static CommentItem empty_comment = {.descr = ""};
10320 :
10321 : /*
10322 : * initdb creates this object with a comment. Skip dumping the
10323 : * initdb-provided comment, which would complicate matters for
10324 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10325 : * comment, replicate that.
10326 : */
10327 222 : if (ncomments == 0)
10328 : {
10329 8 : comments = &empty_comment;
10330 8 : ncomments = 1;
10331 : }
10332 214 : else if (strcmp(comments->descr, initdb_comment) == 0)
10333 214 : ncomments = 0;
10334 : }
10335 :
10336 : /* If a comment exists, build COMMENT ON statement */
10337 12598 : if (ncomments > 0)
10338 : {
10339 12306 : PQExpBuffer query = createPQExpBuffer();
10340 12306 : PQExpBuffer tag = createPQExpBuffer();
10341 :
10342 12306 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10343 12306 : if (namespace && *namespace)
10344 12000 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10345 12306 : appendPQExpBuffer(query, "%s IS ", name);
10346 12306 : appendStringLiteralAH(query, comments->descr, fout);
10347 12306 : appendPQExpBufferStr(query, ";\n");
10348 :
10349 12306 : appendPQExpBuffer(tag, "%s %s", type, name);
10350 :
10351 : /*
10352 : * We mark comments as SECTION_NONE because they really belong in the
10353 : * same section as their parent, whether that is pre-data or
10354 : * post-data.
10355 : */
10356 12306 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10357 12306 : ARCHIVE_OPTS(.tag = tag->data,
10358 : .namespace = namespace,
10359 : .owner = owner,
10360 : .description = "COMMENT",
10361 : .section = SECTION_NONE,
10362 : .createStmt = query->data,
10363 : .deps = &dumpId,
10364 : .nDeps = 1));
10365 :
10366 12306 : destroyPQExpBuffer(query);
10367 12306 : destroyPQExpBuffer(tag);
10368 : }
10369 : }
10370 :
10371 : /*
10372 : * dumpComment --
10373 : *
10374 : * Typical simplification of the above function.
10375 : */
10376 : static inline void
10377 12338 : dumpComment(Archive *fout, const char *type,
10378 : const char *name, const char *namespace,
10379 : const char *owner, CatalogId catalogId,
10380 : int subid, DumpId dumpId)
10381 : {
10382 12338 : dumpCommentExtended(fout, type, name, namespace, owner,
10383 : catalogId, subid, dumpId, NULL);
10384 12338 : }
10385 :
10386 : /*
10387 : * Tabular description of the parameters to pg_restore_relation_stats()
10388 : * param_name, param_type
10389 : */
10390 : static const char *rel_stats_arginfo[][2] = {
10391 : {"relation", "regclass"},
10392 : {"version", "integer"},
10393 : {"relpages", "integer"},
10394 : {"reltuples", "real"},
10395 : {"relallvisible", "integer"},
10396 : };
10397 :
10398 : /*
10399 : * Tabular description of the parameters to pg_restore_attribute_stats()
10400 : * param_name, param_type
10401 : */
10402 : static const char *att_stats_arginfo[][2] = {
10403 : {"relation", "regclass"},
10404 : {"attname", "name"},
10405 : {"inherited", "boolean"},
10406 : {"version", "integer"},
10407 : {"null_frac", "float4"},
10408 : {"avg_width", "integer"},
10409 : {"n_distinct", "float4"},
10410 : {"most_common_vals", "text"},
10411 : {"most_common_freqs", "float4[]"},
10412 : {"histogram_bounds", "text"},
10413 : {"correlation", "float4"},
10414 : {"most_common_elems", "text"},
10415 : {"most_common_elem_freqs", "float4[]"},
10416 : {"elem_count_histogram", "float4[]"},
10417 : {"range_length_histogram", "text"},
10418 : {"range_empty_frac", "float4"},
10419 : {"range_bounds_histogram", "text"},
10420 : };
10421 :
10422 : /*
10423 : * getRelStatsExportQuery --
10424 : *
10425 : * Generate a query that will fetch all relation (e.g. pg_class)
10426 : * stats for a given relation.
10427 : */
10428 : static void
10429 11898 : getRelStatsExportQuery(PQExpBuffer query, Archive *fout,
10430 : const char *schemaname, const char *relname)
10431 : {
10432 11898 : resetPQExpBuffer(query);
10433 11898 : appendPQExpBufferStr(query,
10434 : "SELECT c.oid::regclass AS relation, "
10435 : "current_setting('server_version_num') AS version, "
10436 : "c.relpages, c.reltuples, c.relallvisible "
10437 : "FROM pg_class c "
10438 : "JOIN pg_namespace n "
10439 : "ON n.oid = c.relnamespace "
10440 : "WHERE n.nspname = ");
10441 11898 : appendStringLiteralAH(query, schemaname, fout);
10442 11898 : appendPQExpBufferStr(query, " AND c.relname = ");
10443 11898 : appendStringLiteralAH(query, relname, fout);
10444 11898 : }
10445 :
10446 : /*
10447 : * getAttStatsExportQuery --
10448 : *
10449 : * Generate a query that will fetch all attribute (e.g. pg_statistic)
10450 : * stats for a given relation.
10451 : */
10452 : static void
10453 11898 : getAttStatsExportQuery(PQExpBuffer query, Archive *fout,
10454 : const char *schemaname, const char *relname)
10455 : {
10456 11898 : resetPQExpBuffer(query);
10457 11898 : appendPQExpBufferStr(query,
10458 : "SELECT c.oid::regclass AS relation, "
10459 : "s.attname,"
10460 : "s.inherited,"
10461 : "current_setting('server_version_num') AS version, "
10462 : "s.null_frac,"
10463 : "s.avg_width,"
10464 : "s.n_distinct,"
10465 : "s.most_common_vals,"
10466 : "s.most_common_freqs,"
10467 : "s.histogram_bounds,"
10468 : "s.correlation,"
10469 : "s.most_common_elems,"
10470 : "s.most_common_elem_freqs,"
10471 : "s.elem_count_histogram,");
10472 :
10473 11898 : if (fout->remoteVersion >= 170000)
10474 11898 : appendPQExpBufferStr(query,
10475 : "s.range_length_histogram,"
10476 : "s.range_empty_frac,"
10477 : "s.range_bounds_histogram ");
10478 : else
10479 0 : appendPQExpBufferStr(query,
10480 : "NULL AS range_length_histogram,"
10481 : "NULL AS range_empty_frac,"
10482 : "NULL AS range_bounds_histogram ");
10483 :
10484 11898 : appendPQExpBufferStr(query,
10485 : "FROM pg_stats s "
10486 : "JOIN pg_namespace n "
10487 : "ON n.nspname = s.schemaname "
10488 : "JOIN pg_class c "
10489 : "ON c.relname = s.tablename "
10490 : "AND c.relnamespace = n.oid "
10491 : "WHERE s.schemaname = ");
10492 11898 : appendStringLiteralAH(query, schemaname, fout);
10493 11898 : appendPQExpBufferStr(query, " AND s.tablename = ");
10494 11898 : appendStringLiteralAH(query, relname, fout);
10495 11898 : appendPQExpBufferStr(query, " ORDER BY s.attname, s.inherited");
10496 11898 : }
10497 :
10498 :
10499 : /*
10500 : * appendNamedArgument --
10501 : *
10502 : * Convenience routine for constructing parameters of the form:
10503 : * 'paraname', 'value'::type
10504 : */
10505 : static void
10506 119394 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10507 : const char *argval, const char *argtype)
10508 : {
10509 119394 : appendPQExpBufferStr(out, "\t");
10510 :
10511 119394 : appendStringLiteralAH(out, argname, fout);
10512 119394 : appendPQExpBufferStr(out, ", ");
10513 :
10514 119394 : appendStringLiteralAH(out, argval, fout);
10515 119394 : appendPQExpBuffer(out, "::%s", argtype);
10516 119394 : }
10517 :
10518 : /*
10519 : * appendRelStatsImport --
10520 : *
10521 : * Append a formatted pg_restore_relation_stats statement.
10522 : */
10523 : static void
10524 11898 : appendRelStatsImport(PQExpBuffer out, Archive *fout, PGresult *res)
10525 : {
10526 11898 : const char *sep = "";
10527 :
10528 11898 : if (PQntuples(res) == 0)
10529 0 : return;
10530 :
10531 11898 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
10532 :
10533 71388 : for (int argno = 0; argno < lengthof(rel_stats_arginfo); argno++)
10534 : {
10535 59490 : const char *argname = rel_stats_arginfo[argno][0];
10536 59490 : const char *argtype = rel_stats_arginfo[argno][1];
10537 59490 : int fieldno = PQfnumber(res, argname);
10538 :
10539 59490 : if (fieldno < 0)
10540 0 : pg_fatal("relation stats export query missing field '%s'",
10541 : argname);
10542 :
10543 59490 : if (PQgetisnull(res, 0, fieldno))
10544 0 : continue;
10545 :
10546 59490 : appendPQExpBufferStr(out, sep);
10547 59490 : appendNamedArgument(out, fout, argname, PQgetvalue(res, 0, fieldno), argtype);
10548 :
10549 59490 : sep = ",\n";
10550 : }
10551 11898 : appendPQExpBufferStr(out, "\n);\n");
10552 : }
10553 :
10554 : /*
10555 : * appendAttStatsImport --
10556 : *
10557 : * Append a series of formatted pg_restore_attribute_stats statements.
10558 : */
10559 : static void
10560 11898 : appendAttStatsImport(PQExpBuffer out, Archive *fout, PGresult *res)
10561 : {
10562 18140 : for (int rownum = 0; rownum < PQntuples(res); rownum++)
10563 : {
10564 6242 : const char *sep = "";
10565 :
10566 6242 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
10567 112356 : for (int argno = 0; argno < lengthof(att_stats_arginfo); argno++)
10568 : {
10569 106114 : const char *argname = att_stats_arginfo[argno][0];
10570 106114 : const char *argtype = att_stats_arginfo[argno][1];
10571 106114 : int fieldno = PQfnumber(res, argname);
10572 :
10573 106114 : if (fieldno < 0)
10574 0 : pg_fatal("attribute stats export query missing field '%s'",
10575 : argname);
10576 :
10577 106114 : if (PQgetisnull(res, rownum, fieldno))
10578 46210 : continue;
10579 :
10580 59904 : appendPQExpBufferStr(out, sep);
10581 59904 : appendNamedArgument(out, fout, argname, PQgetvalue(res, rownum, fieldno), argtype);
10582 59904 : sep = ",\n";
10583 : }
10584 6242 : appendPQExpBufferStr(out, "\n);\n");
10585 : }
10586 11898 : }
10587 :
10588 : /*
10589 : * Decide which section to use based on the relkind of the parent object.
10590 : *
10591 : * NB: materialized views may be postponed from SECTION_PRE_DATA to
10592 : * SECTION_POST_DATA to resolve some kinds of dependency problems. If so, the
10593 : * matview stats will also be postponed to SECTION_POST_DATA. See
10594 : * repairMatViewBoundaryMultiLoop().
10595 : */
10596 : static teSection
10597 23726 : statisticsDumpSection(const RelStatsInfo *rsinfo)
10598 : {
10599 23726 : switch (rsinfo->relkind)
10600 : {
10601 15946 : case RELKIND_RELATION:
10602 : case RELKIND_PARTITIONED_TABLE:
10603 : case RELKIND_MATVIEW:
10604 15946 : return SECTION_DATA;
10605 7780 : case RELKIND_INDEX:
10606 : case RELKIND_PARTITIONED_INDEX:
10607 7780 : return SECTION_POST_DATA;
10608 0 : default:
10609 0 : pg_fatal("cannot dump statistics for relation kind '%c'",
10610 : rsinfo->relkind);
10611 : }
10612 :
10613 : return 0; /* keep compiler quiet */
10614 : }
10615 :
10616 : /*
10617 : * dumpRelationStats --
10618 : *
10619 : * Dump command to import stats into the relation on the new database.
10620 : */
10621 : static void
10622 11898 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
10623 : {
10624 : PGresult *res;
10625 : PQExpBuffer query;
10626 : PQExpBuffer out;
10627 : PQExpBuffer tag;
10628 11898 : DumpableObject *dobj = (DumpableObject *) &rsinfo->dobj;
10629 11898 : DumpId *deps = NULL;
10630 11898 : int ndeps = 0;
10631 :
10632 : /* nothing to do if we are not dumping statistics */
10633 11898 : if (!fout->dopt->dumpStatistics)
10634 0 : return;
10635 :
10636 : /* dependent on the relation definition, if doing schema */
10637 11898 : if (fout->dopt->dumpSchema)
10638 : {
10639 11520 : deps = dobj->dependencies;
10640 11520 : ndeps = dobj->nDeps;
10641 : }
10642 :
10643 11898 : tag = createPQExpBuffer();
10644 11898 : appendPQExpBufferStr(tag, fmtId(dobj->name));
10645 :
10646 11898 : query = createPQExpBuffer();
10647 11898 : out = createPQExpBuffer();
10648 :
10649 11898 : getRelStatsExportQuery(query, fout, dobj->namespace->dobj.name,
10650 11898 : dobj->name);
10651 11898 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10652 11898 : appendRelStatsImport(out, fout, res);
10653 11898 : PQclear(res);
10654 :
10655 11898 : getAttStatsExportQuery(query, fout, dobj->namespace->dobj.name,
10656 11898 : dobj->name);
10657 11898 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10658 11898 : appendAttStatsImport(out, fout, res);
10659 11898 : PQclear(res);
10660 :
10661 11898 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10662 11898 : ARCHIVE_OPTS(.tag = tag->data,
10663 : .namespace = dobj->namespace->dobj.name,
10664 : .description = "STATISTICS DATA",
10665 : .section = rsinfo->postponed_def ?
10666 : SECTION_POST_DATA : statisticsDumpSection(rsinfo),
10667 : .createStmt = out->data,
10668 : .deps = deps,
10669 : .nDeps = ndeps));
10670 :
10671 11898 : destroyPQExpBuffer(query);
10672 11898 : destroyPQExpBuffer(out);
10673 11898 : destroyPQExpBuffer(tag);
10674 : }
10675 :
10676 : /*
10677 : * dumpTableComment --
10678 : *
10679 : * As above, but dump comments for both the specified table (or view)
10680 : * and its columns.
10681 : */
10682 : static void
10683 156 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10684 : const char *reltypename)
10685 : {
10686 156 : DumpOptions *dopt = fout->dopt;
10687 : CommentItem *comments;
10688 : int ncomments;
10689 : PQExpBuffer query;
10690 : PQExpBuffer tag;
10691 :
10692 : /* do nothing, if --no-comments is supplied */
10693 156 : if (dopt->no_comments)
10694 0 : return;
10695 :
10696 : /* Comments are SCHEMA not data */
10697 156 : if (!dopt->dumpSchema)
10698 0 : return;
10699 :
10700 : /* Search for comments associated with relation, using table */
10701 156 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
10702 : tbinfo->dobj.catId.oid,
10703 : &comments);
10704 :
10705 : /* If comments exist, build COMMENT ON statements */
10706 156 : if (ncomments <= 0)
10707 0 : return;
10708 :
10709 156 : query = createPQExpBuffer();
10710 156 : tag = createPQExpBuffer();
10711 :
10712 448 : while (ncomments > 0)
10713 : {
10714 292 : const char *descr = comments->descr;
10715 292 : int objsubid = comments->objsubid;
10716 :
10717 292 : if (objsubid == 0)
10718 : {
10719 68 : resetPQExpBuffer(tag);
10720 68 : appendPQExpBuffer(tag, "%s %s", reltypename,
10721 68 : fmtId(tbinfo->dobj.name));
10722 :
10723 68 : resetPQExpBuffer(query);
10724 68 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10725 68 : fmtQualifiedDumpable(tbinfo));
10726 68 : appendStringLiteralAH(query, descr, fout);
10727 68 : appendPQExpBufferStr(query, ";\n");
10728 :
10729 68 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10730 68 : ARCHIVE_OPTS(.tag = tag->data,
10731 : .namespace = tbinfo->dobj.namespace->dobj.name,
10732 : .owner = tbinfo->rolname,
10733 : .description = "COMMENT",
10734 : .section = SECTION_NONE,
10735 : .createStmt = query->data,
10736 : .deps = &(tbinfo->dobj.dumpId),
10737 : .nDeps = 1));
10738 : }
10739 224 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10740 : {
10741 224 : resetPQExpBuffer(tag);
10742 224 : appendPQExpBuffer(tag, "COLUMN %s.",
10743 224 : fmtId(tbinfo->dobj.name));
10744 224 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10745 :
10746 224 : resetPQExpBuffer(query);
10747 224 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10748 224 : fmtQualifiedDumpable(tbinfo));
10749 224 : appendPQExpBuffer(query, "%s IS ",
10750 224 : fmtId(tbinfo->attnames[objsubid - 1]));
10751 224 : appendStringLiteralAH(query, descr, fout);
10752 224 : appendPQExpBufferStr(query, ";\n");
10753 :
10754 224 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10755 224 : ARCHIVE_OPTS(.tag = tag->data,
10756 : .namespace = tbinfo->dobj.namespace->dobj.name,
10757 : .owner = tbinfo->rolname,
10758 : .description = "COMMENT",
10759 : .section = SECTION_NONE,
10760 : .createStmt = query->data,
10761 : .deps = &(tbinfo->dobj.dumpId),
10762 : .nDeps = 1));
10763 : }
10764 :
10765 292 : comments++;
10766 292 : ncomments--;
10767 : }
10768 :
10769 156 : destroyPQExpBuffer(query);
10770 156 : destroyPQExpBuffer(tag);
10771 : }
10772 :
10773 : /*
10774 : * findComments --
10775 : *
10776 : * Find the comment(s), if any, associated with the given object. All the
10777 : * objsubid values associated with the given classoid/objoid are found with
10778 : * one search.
10779 : */
10780 : static int
10781 12822 : findComments(Oid classoid, Oid objoid, CommentItem **items)
10782 : {
10783 12822 : CommentItem *middle = NULL;
10784 : CommentItem *low;
10785 : CommentItem *high;
10786 : int nmatch;
10787 :
10788 : /*
10789 : * Do binary search to find some item matching the object.
10790 : */
10791 12822 : low = &comments[0];
10792 12822 : high = &comments[ncomments - 1];
10793 127300 : while (low <= high)
10794 : {
10795 127214 : middle = low + (high - low) / 2;
10796 :
10797 127214 : if (classoid < middle->classoid)
10798 13222 : high = middle - 1;
10799 113992 : else if (classoid > middle->classoid)
10800 14082 : low = middle + 1;
10801 99910 : else if (objoid < middle->objoid)
10802 42188 : high = middle - 1;
10803 57722 : else if (objoid > middle->objoid)
10804 44986 : low = middle + 1;
10805 : else
10806 12736 : break; /* found a match */
10807 : }
10808 :
10809 12822 : if (low > high) /* no matches */
10810 : {
10811 86 : *items = NULL;
10812 86 : return 0;
10813 : }
10814 :
10815 : /*
10816 : * Now determine how many items match the object. The search loop
10817 : * invariant still holds: only items between low and high inclusive could
10818 : * match.
10819 : */
10820 12736 : nmatch = 1;
10821 12736 : while (middle > low)
10822 : {
10823 6074 : if (classoid != middle[-1].classoid ||
10824 5846 : objoid != middle[-1].objoid)
10825 : break;
10826 0 : middle--;
10827 0 : nmatch++;
10828 : }
10829 :
10830 12736 : *items = middle;
10831 :
10832 12736 : middle += nmatch;
10833 12872 : while (middle <= high)
10834 : {
10835 6984 : if (classoid != middle->classoid ||
10836 6266 : objoid != middle->objoid)
10837 : break;
10838 136 : middle++;
10839 136 : nmatch++;
10840 : }
10841 :
10842 12736 : return nmatch;
10843 : }
10844 :
10845 : /*
10846 : * collectComments --
10847 : *
10848 : * Construct a table of all comments available for database objects;
10849 : * also set the has-comment component flag for each relevant object.
10850 : *
10851 : * We used to do per-object queries for the comments, but it's much faster
10852 : * to pull them all over at once, and on most databases the memory cost
10853 : * isn't high.
10854 : *
10855 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
10856 : */
10857 : static void
10858 320 : collectComments(Archive *fout)
10859 : {
10860 : PGresult *res;
10861 : PQExpBuffer query;
10862 : int i_description;
10863 : int i_classoid;
10864 : int i_objoid;
10865 : int i_objsubid;
10866 : int ntups;
10867 : int i;
10868 : DumpableObject *dobj;
10869 :
10870 320 : query = createPQExpBuffer();
10871 :
10872 320 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10873 : "FROM pg_catalog.pg_description "
10874 : "ORDER BY classoid, objoid, objsubid");
10875 :
10876 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10877 :
10878 : /* Construct lookup table containing OIDs in numeric form */
10879 :
10880 320 : i_description = PQfnumber(res, "description");
10881 320 : i_classoid = PQfnumber(res, "classoid");
10882 320 : i_objoid = PQfnumber(res, "objoid");
10883 320 : i_objsubid = PQfnumber(res, "objsubid");
10884 :
10885 320 : ntups = PQntuples(res);
10886 :
10887 320 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10888 320 : ncomments = 0;
10889 320 : dobj = NULL;
10890 :
10891 1687546 : for (i = 0; i < ntups; i++)
10892 : {
10893 : CatalogId objId;
10894 : int subid;
10895 :
10896 1687226 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10897 1687226 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10898 1687226 : subid = atoi(PQgetvalue(res, i, i_objsubid));
10899 :
10900 : /* We needn't remember comments that don't match any dumpable object */
10901 1687226 : if (dobj == NULL ||
10902 607296 : dobj->catId.tableoid != objId.tableoid ||
10903 603290 : dobj->catId.oid != objId.oid)
10904 1687038 : dobj = findObjectByCatalogId(objId);
10905 1687226 : if (dobj == NULL)
10906 1079622 : continue;
10907 :
10908 : /*
10909 : * Comments on columns of composite types are linked to the type's
10910 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10911 : * in the type's own DumpableObject.
10912 : */
10913 607604 : if (subid != 0 && dobj->objType == DO_TABLE &&
10914 404 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10915 94 : {
10916 : TypeInfo *cTypeInfo;
10917 :
10918 94 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10919 94 : if (cTypeInfo)
10920 94 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10921 : }
10922 : else
10923 607510 : dobj->components |= DUMP_COMPONENT_COMMENT;
10924 :
10925 607604 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10926 607604 : comments[ncomments].classoid = objId.tableoid;
10927 607604 : comments[ncomments].objoid = objId.oid;
10928 607604 : comments[ncomments].objsubid = subid;
10929 607604 : ncomments++;
10930 : }
10931 :
10932 320 : PQclear(res);
10933 320 : destroyPQExpBuffer(query);
10934 320 : }
10935 :
10936 : /*
10937 : * dumpDumpableObject
10938 : *
10939 : * This routine and its subsidiaries are responsible for creating
10940 : * ArchiveEntries (TOC objects) for each object to be dumped.
10941 : */
10942 : static void
10943 1187240 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10944 : {
10945 : /*
10946 : * Clear any dump-request bits for components that don't exist for this
10947 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10948 : * request for every kind of object.)
10949 : */
10950 1187240 : dobj->dump &= dobj->components;
10951 :
10952 : /* Now, short-circuit if there's nothing to be done here. */
10953 1187240 : if (dobj->dump == 0)
10954 1041708 : return;
10955 :
10956 145532 : switch (dobj->objType)
10957 : {
10958 834 : case DO_NAMESPACE:
10959 834 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
10960 834 : break;
10961 38 : case DO_EXTENSION:
10962 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
10963 38 : break;
10964 1758 : case DO_TYPE:
10965 1758 : dumpType(fout, (const TypeInfo *) dobj);
10966 1758 : break;
10967 150 : case DO_SHELL_TYPE:
10968 150 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
10969 150 : break;
10970 3696 : case DO_FUNC:
10971 3696 : dumpFunc(fout, (const FuncInfo *) dobj);
10972 3696 : break;
10973 588 : case DO_AGG:
10974 588 : dumpAgg(fout, (const AggInfo *) dobj);
10975 588 : break;
10976 5012 : case DO_OPERATOR:
10977 5012 : dumpOpr(fout, (const OprInfo *) dobj);
10978 5012 : break;
10979 168 : case DO_ACCESS_METHOD:
10980 168 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10981 168 : break;
10982 1332 : case DO_OPCLASS:
10983 1332 : dumpOpclass(fout, (const OpclassInfo *) dobj);
10984 1332 : break;
10985 1106 : case DO_OPFAMILY:
10986 1106 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10987 1106 : break;
10988 4944 : case DO_COLLATION:
10989 4944 : dumpCollation(fout, (const CollInfo *) dobj);
10990 4944 : break;
10991 848 : case DO_CONVERSION:
10992 848 : dumpConversion(fout, (const ConvInfo *) dobj);
10993 848 : break;
10994 52734 : case DO_TABLE:
10995 52734 : dumpTable(fout, (const TableInfo *) dobj);
10996 52734 : break;
10997 2582 : case DO_TABLE_ATTACH:
10998 2582 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10999 2582 : break;
11000 2002 : case DO_ATTRDEF:
11001 2002 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11002 2002 : break;
11003 5184 : case DO_INDEX:
11004 5184 : dumpIndex(fout, (const IndxInfo *) dobj);
11005 5184 : break;
11006 1160 : case DO_INDEX_ATTACH:
11007 1160 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11008 1160 : break;
11009 278 : case DO_STATSEXT:
11010 278 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11011 278 : break;
11012 784 : case DO_REFRESH_MATVIEW:
11013 784 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11014 784 : break;
11015 2330 : case DO_RULE:
11016 2330 : dumpRule(fout, (const RuleInfo *) dobj);
11017 2330 : break;
11018 1066 : case DO_TRIGGER:
11019 1066 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11020 1066 : break;
11021 88 : case DO_EVENT_TRIGGER:
11022 88 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11023 88 : break;
11024 4396 : case DO_CONSTRAINT:
11025 4396 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11026 4396 : break;
11027 360 : case DO_FK_CONSTRAINT:
11028 360 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11029 360 : break;
11030 172 : case DO_PROCLANG:
11031 172 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11032 172 : break;
11033 138 : case DO_CAST:
11034 138 : dumpCast(fout, (const CastInfo *) dobj);
11035 138 : break;
11036 88 : case DO_TRANSFORM:
11037 88 : dumpTransform(fout, (const TransformInfo *) dobj);
11038 88 : break;
11039 790 : case DO_SEQUENCE_SET:
11040 790 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11041 790 : break;
11042 7836 : case DO_TABLE_DATA:
11043 7836 : dumpTableData(fout, (const TableDataInfo *) dobj);
11044 7836 : break;
11045 26808 : case DO_DUMMY_TYPE:
11046 : /* table rowtypes and array types are never dumped separately */
11047 26808 : break;
11048 86 : case DO_TSPARSER:
11049 86 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11050 86 : break;
11051 350 : case DO_TSDICT:
11052 350 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11053 350 : break;
11054 110 : case DO_TSTEMPLATE:
11055 110 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11056 110 : break;
11057 300 : case DO_TSCONFIG:
11058 300 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11059 300 : break;
11060 108 : case DO_FDW:
11061 108 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11062 108 : break;
11063 116 : case DO_FOREIGN_SERVER:
11064 116 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11065 116 : break;
11066 316 : case DO_DEFAULT_ACL:
11067 316 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11068 316 : break;
11069 154 : case DO_LARGE_OBJECT:
11070 154 : dumpLO(fout, (const LoInfo *) dobj);
11071 154 : break;
11072 154 : case DO_LARGE_OBJECT_DATA:
11073 154 : if (dobj->dump & DUMP_COMPONENT_DATA)
11074 : {
11075 : LoInfo *loinfo;
11076 : TocEntry *te;
11077 :
11078 154 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11079 154 : if (loinfo == NULL)
11080 0 : pg_fatal("missing metadata for large objects \"%s\"",
11081 : dobj->name);
11082 :
11083 154 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11084 154 : ARCHIVE_OPTS(.tag = dobj->name,
11085 : .owner = loinfo->rolname,
11086 : .description = "BLOBS",
11087 : .section = SECTION_DATA,
11088 : .deps = dobj->dependencies,
11089 : .nDeps = dobj->nDeps,
11090 : .dumpFn = dumpLOs,
11091 : .dumpArg = loinfo));
11092 :
11093 : /*
11094 : * Set the TocEntry's dataLength in case we are doing a
11095 : * parallel dump and want to order dump jobs by table size.
11096 : * (We need some size estimate for every TocEntry with a
11097 : * DataDumper function.) We don't currently have any cheap
11098 : * way to estimate the size of LOs, but fortunately it doesn't
11099 : * matter too much as long as we get large batches of LOs
11100 : * processed reasonably early. Assume 8K per blob.
11101 : */
11102 154 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11103 : }
11104 154 : break;
11105 694 : case DO_POLICY:
11106 694 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11107 694 : break;
11108 392 : case DO_PUBLICATION:
11109 392 : dumpPublication(fout, (const PublicationInfo *) dobj);
11110 392 : break;
11111 546 : case DO_PUBLICATION_REL:
11112 546 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11113 546 : break;
11114 156 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11115 156 : dumpPublicationNamespace(fout,
11116 : (const PublicationSchemaInfo *) dobj);
11117 156 : break;
11118 238 : case DO_SUBSCRIPTION:
11119 238 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11120 238 : break;
11121 4 : case DO_SUBSCRIPTION_REL:
11122 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11123 4 : break;
11124 11898 : case DO_REL_STATS:
11125 11898 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11126 11898 : break;
11127 640 : case DO_PRE_DATA_BOUNDARY:
11128 : case DO_POST_DATA_BOUNDARY:
11129 : /* never dumped, nothing to do */
11130 640 : break;
11131 : }
11132 : }
11133 :
11134 : /*
11135 : * dumpNamespace
11136 : * writes out to fout the queries to recreate a user-defined namespace
11137 : */
11138 : static void
11139 834 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11140 : {
11141 834 : DumpOptions *dopt = fout->dopt;
11142 : PQExpBuffer q;
11143 : PQExpBuffer delq;
11144 : char *qnspname;
11145 :
11146 : /* Do nothing if not dumping schema */
11147 834 : if (!dopt->dumpSchema)
11148 56 : return;
11149 :
11150 778 : q = createPQExpBuffer();
11151 778 : delq = createPQExpBuffer();
11152 :
11153 778 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11154 :
11155 778 : if (nspinfo->create)
11156 : {
11157 528 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11158 528 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11159 : }
11160 : else
11161 : {
11162 : /* see selectDumpableNamespace() */
11163 250 : appendPQExpBufferStr(delq,
11164 : "-- *not* dropping schema, since initdb creates it\n");
11165 250 : appendPQExpBufferStr(q,
11166 : "-- *not* creating schema, since initdb creates it\n");
11167 : }
11168 :
11169 778 : if (dopt->binary_upgrade)
11170 92 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11171 : "SCHEMA", qnspname, NULL);
11172 :
11173 778 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11174 342 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11175 342 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11176 : .owner = nspinfo->rolname,
11177 : .description = "SCHEMA",
11178 : .section = SECTION_PRE_DATA,
11179 : .createStmt = q->data,
11180 : .dropStmt = delq->data));
11181 :
11182 : /* Dump Schema Comments and Security Labels */
11183 778 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11184 : {
11185 260 : const char *initdb_comment = NULL;
11186 :
11187 260 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11188 222 : initdb_comment = "standard public schema";
11189 260 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11190 : NULL, nspinfo->rolname,
11191 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11192 : initdb_comment);
11193 : }
11194 :
11195 778 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11196 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11197 : NULL, nspinfo->rolname,
11198 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11199 :
11200 778 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11201 610 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11202 : qnspname, NULL, NULL,
11203 : NULL, nspinfo->rolname, &nspinfo->dacl);
11204 :
11205 778 : free(qnspname);
11206 :
11207 778 : destroyPQExpBuffer(q);
11208 778 : destroyPQExpBuffer(delq);
11209 : }
11210 :
11211 : /*
11212 : * dumpExtension
11213 : * writes out to fout the queries to recreate an extension
11214 : */
11215 : static void
11216 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11217 : {
11218 38 : DumpOptions *dopt = fout->dopt;
11219 : PQExpBuffer q;
11220 : PQExpBuffer delq;
11221 : char *qextname;
11222 :
11223 : /* Do nothing if not dumping schema */
11224 38 : if (!dopt->dumpSchema)
11225 2 : return;
11226 :
11227 36 : q = createPQExpBuffer();
11228 36 : delq = createPQExpBuffer();
11229 :
11230 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11231 :
11232 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11233 :
11234 36 : if (!dopt->binary_upgrade)
11235 : {
11236 : /*
11237 : * In a regular dump, we simply create the extension, intentionally
11238 : * not specifying a version, so that the destination installation's
11239 : * default version is used.
11240 : *
11241 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11242 : * types; but there are various scenarios in which it's convenient to
11243 : * manually create the desired extension before restoring, so we
11244 : * prefer to allow it to exist already.
11245 : */
11246 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11247 34 : qextname, fmtId(extinfo->namespace));
11248 : }
11249 : else
11250 : {
11251 : /*
11252 : * In binary-upgrade mode, it's critical to reproduce the state of the
11253 : * database exactly, so our procedure is to create an empty extension,
11254 : * restore all the contained objects normally, and add them to the
11255 : * extension one by one. This function performs just the first of
11256 : * those steps. binary_upgrade_extension_member() takes care of
11257 : * adding member objects as they're created.
11258 : */
11259 : int i;
11260 : int n;
11261 :
11262 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11263 :
11264 : /*
11265 : * We unconditionally create the extension, so we must drop it if it
11266 : * exists. This could happen if the user deleted 'plpgsql' and then
11267 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11268 : */
11269 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11270 :
11271 2 : appendPQExpBufferStr(q,
11272 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11273 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11274 2 : appendPQExpBufferStr(q, ", ");
11275 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11276 2 : appendPQExpBufferStr(q, ", ");
11277 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11278 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11279 2 : appendPQExpBufferStr(q, ", ");
11280 :
11281 : /*
11282 : * Note that we're pushing extconfig (an OID array) back into
11283 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11284 : * preserved in binary upgrade.
11285 : */
11286 2 : if (strlen(extinfo->extconfig) > 2)
11287 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11288 : else
11289 0 : appendPQExpBufferStr(q, "NULL");
11290 2 : appendPQExpBufferStr(q, ", ");
11291 2 : if (strlen(extinfo->extcondition) > 2)
11292 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11293 : else
11294 0 : appendPQExpBufferStr(q, "NULL");
11295 2 : appendPQExpBufferStr(q, ", ");
11296 2 : appendPQExpBufferStr(q, "ARRAY[");
11297 2 : n = 0;
11298 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11299 : {
11300 : DumpableObject *extobj;
11301 :
11302 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11303 2 : if (extobj && extobj->objType == DO_EXTENSION)
11304 : {
11305 0 : if (n++ > 0)
11306 0 : appendPQExpBufferChar(q, ',');
11307 0 : appendStringLiteralAH(q, extobj->name, fout);
11308 : }
11309 : }
11310 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11311 2 : appendPQExpBufferStr(q, ");\n");
11312 : }
11313 :
11314 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11315 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11316 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11317 : .description = "EXTENSION",
11318 : .section = SECTION_PRE_DATA,
11319 : .createStmt = q->data,
11320 : .dropStmt = delq->data));
11321 :
11322 : /* Dump Extension Comments and Security Labels */
11323 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11324 36 : dumpComment(fout, "EXTENSION", qextname,
11325 : NULL, "",
11326 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11327 :
11328 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11329 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11330 : NULL, "",
11331 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11332 :
11333 36 : free(qextname);
11334 :
11335 36 : destroyPQExpBuffer(q);
11336 36 : destroyPQExpBuffer(delq);
11337 : }
11338 :
11339 : /*
11340 : * dumpType
11341 : * writes out to fout the queries to recreate a user-defined type
11342 : */
11343 : static void
11344 1758 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11345 : {
11346 1758 : DumpOptions *dopt = fout->dopt;
11347 :
11348 : /* Do nothing if not dumping schema */
11349 1758 : if (!dopt->dumpSchema)
11350 86 : return;
11351 :
11352 : /* Dump out in proper style */
11353 1672 : if (tyinfo->typtype == TYPTYPE_BASE)
11354 558 : dumpBaseType(fout, tyinfo);
11355 1114 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11356 278 : dumpDomain(fout, tyinfo);
11357 836 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11358 264 : dumpCompositeType(fout, tyinfo);
11359 572 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11360 112 : dumpEnumType(fout, tyinfo);
11361 460 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11362 232 : dumpRangeType(fout, tyinfo);
11363 228 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11364 78 : dumpUndefinedType(fout, tyinfo);
11365 : else
11366 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11367 : tyinfo->dobj.name);
11368 : }
11369 :
11370 : /*
11371 : * dumpEnumType
11372 : * writes out to fout the queries to recreate a user-defined enum type
11373 : */
11374 : static void
11375 112 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11376 : {
11377 112 : DumpOptions *dopt = fout->dopt;
11378 112 : PQExpBuffer q = createPQExpBuffer();
11379 112 : PQExpBuffer delq = createPQExpBuffer();
11380 112 : PQExpBuffer query = createPQExpBuffer();
11381 : PGresult *res;
11382 : int num,
11383 : i;
11384 : Oid enum_oid;
11385 : char *qtypname;
11386 : char *qualtypname;
11387 : char *label;
11388 : int i_enumlabel;
11389 : int i_oid;
11390 :
11391 112 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11392 : {
11393 : /* Set up query for enum-specific details */
11394 82 : appendPQExpBufferStr(query,
11395 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11396 : "SELECT oid, enumlabel "
11397 : "FROM pg_catalog.pg_enum "
11398 : "WHERE enumtypid = $1 "
11399 : "ORDER BY enumsortorder");
11400 :
11401 82 : ExecuteSqlStatement(fout, query->data);
11402 :
11403 82 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11404 : }
11405 :
11406 112 : printfPQExpBuffer(query,
11407 : "EXECUTE dumpEnumType('%u')",
11408 : tyinfo->dobj.catId.oid);
11409 :
11410 112 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11411 :
11412 112 : num = PQntuples(res);
11413 :
11414 112 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11415 112 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11416 :
11417 : /*
11418 : * CASCADE shouldn't be required here as for normal types since the I/O
11419 : * functions are generic and do not get dropped.
11420 : */
11421 112 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11422 :
11423 112 : if (dopt->binary_upgrade)
11424 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11425 : tyinfo->dobj.catId.oid,
11426 : false, false);
11427 :
11428 112 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11429 : qualtypname);
11430 :
11431 112 : if (!dopt->binary_upgrade)
11432 : {
11433 102 : i_enumlabel = PQfnumber(res, "enumlabel");
11434 :
11435 : /* Labels with server-assigned oids */
11436 740 : for (i = 0; i < num; i++)
11437 : {
11438 638 : label = PQgetvalue(res, i, i_enumlabel);
11439 638 : if (i > 0)
11440 536 : appendPQExpBufferChar(q, ',');
11441 638 : appendPQExpBufferStr(q, "\n ");
11442 638 : appendStringLiteralAH(q, label, fout);
11443 : }
11444 : }
11445 :
11446 112 : appendPQExpBufferStr(q, "\n);\n");
11447 :
11448 112 : if (dopt->binary_upgrade)
11449 : {
11450 10 : i_oid = PQfnumber(res, "oid");
11451 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11452 :
11453 : /* Labels with dump-assigned (preserved) oids */
11454 116 : for (i = 0; i < num; i++)
11455 : {
11456 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11457 106 : label = PQgetvalue(res, i, i_enumlabel);
11458 :
11459 106 : if (i == 0)
11460 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11461 106 : appendPQExpBuffer(q,
11462 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11463 : enum_oid);
11464 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11465 106 : appendStringLiteralAH(q, label, fout);
11466 106 : appendPQExpBufferStr(q, ";\n\n");
11467 : }
11468 : }
11469 :
11470 112 : if (dopt->binary_upgrade)
11471 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11472 : "TYPE", qtypname,
11473 10 : tyinfo->dobj.namespace->dobj.name);
11474 :
11475 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11476 112 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11477 112 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11478 : .namespace = tyinfo->dobj.namespace->dobj.name,
11479 : .owner = tyinfo->rolname,
11480 : .description = "TYPE",
11481 : .section = SECTION_PRE_DATA,
11482 : .createStmt = q->data,
11483 : .dropStmt = delq->data));
11484 :
11485 : /* Dump Type Comments and Security Labels */
11486 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11487 68 : dumpComment(fout, "TYPE", qtypname,
11488 68 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11489 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11490 :
11491 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11492 0 : dumpSecLabel(fout, "TYPE", qtypname,
11493 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11494 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11495 :
11496 112 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11497 68 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11498 : qtypname, NULL,
11499 68 : tyinfo->dobj.namespace->dobj.name,
11500 : NULL, tyinfo->rolname, &tyinfo->dacl);
11501 :
11502 112 : PQclear(res);
11503 112 : destroyPQExpBuffer(q);
11504 112 : destroyPQExpBuffer(delq);
11505 112 : destroyPQExpBuffer(query);
11506 112 : free(qtypname);
11507 112 : free(qualtypname);
11508 112 : }
11509 :
11510 : /*
11511 : * dumpRangeType
11512 : * writes out to fout the queries to recreate a user-defined range type
11513 : */
11514 : static void
11515 232 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11516 : {
11517 232 : DumpOptions *dopt = fout->dopt;
11518 232 : PQExpBuffer q = createPQExpBuffer();
11519 232 : PQExpBuffer delq = createPQExpBuffer();
11520 232 : PQExpBuffer query = createPQExpBuffer();
11521 : PGresult *res;
11522 : Oid collationOid;
11523 : char *qtypname;
11524 : char *qualtypname;
11525 : char *procname;
11526 :
11527 232 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11528 : {
11529 : /* Set up query for range-specific details */
11530 84 : appendPQExpBufferStr(query,
11531 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11532 :
11533 84 : appendPQExpBufferStr(query,
11534 : "SELECT ");
11535 :
11536 84 : if (fout->remoteVersion >= 140000)
11537 84 : appendPQExpBufferStr(query,
11538 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11539 : else
11540 0 : appendPQExpBufferStr(query,
11541 : "NULL AS rngmultitype, ");
11542 :
11543 84 : appendPQExpBufferStr(query,
11544 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11545 : "opc.opcname AS opcname, "
11546 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11547 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11548 : "opc.opcdefault, "
11549 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11550 : " ELSE rngcollation END AS collation, "
11551 : "rngcanonical, rngsubdiff "
11552 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11553 : " pg_catalog.pg_opclass opc "
11554 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11555 : "rngtypid = $1");
11556 :
11557 84 : ExecuteSqlStatement(fout, query->data);
11558 :
11559 84 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11560 : }
11561 :
11562 232 : printfPQExpBuffer(query,
11563 : "EXECUTE dumpRangeType('%u')",
11564 : tyinfo->dobj.catId.oid);
11565 :
11566 232 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11567 :
11568 232 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11569 232 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11570 :
11571 : /*
11572 : * CASCADE shouldn't be required here as for normal types since the I/O
11573 : * functions are generic and do not get dropped.
11574 : */
11575 232 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11576 :
11577 232 : if (dopt->binary_upgrade)
11578 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11579 : tyinfo->dobj.catId.oid,
11580 : false, true);
11581 :
11582 232 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11583 : qualtypname);
11584 :
11585 232 : appendPQExpBuffer(q, "\n subtype = %s",
11586 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11587 :
11588 232 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11589 232 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11590 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11591 :
11592 : /* print subtype_opclass only if not default for subtype */
11593 232 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11594 : {
11595 68 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11596 68 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11597 :
11598 68 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11599 : fmtId(nspname));
11600 68 : appendPQExpBufferStr(q, fmtId(opcname));
11601 : }
11602 :
11603 232 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11604 232 : if (OidIsValid(collationOid))
11605 : {
11606 78 : CollInfo *coll = findCollationByOid(collationOid);
11607 :
11608 78 : if (coll)
11609 78 : appendPQExpBuffer(q, ",\n collation = %s",
11610 78 : fmtQualifiedDumpable(coll));
11611 : }
11612 :
11613 232 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11614 232 : if (strcmp(procname, "-") != 0)
11615 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
11616 :
11617 232 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11618 232 : if (strcmp(procname, "-") != 0)
11619 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11620 :
11621 232 : appendPQExpBufferStr(q, "\n);\n");
11622 :
11623 232 : if (dopt->binary_upgrade)
11624 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11625 : "TYPE", qtypname,
11626 16 : tyinfo->dobj.namespace->dobj.name);
11627 :
11628 232 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11629 232 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11630 232 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11631 : .namespace = tyinfo->dobj.namespace->dobj.name,
11632 : .owner = tyinfo->rolname,
11633 : .description = "TYPE",
11634 : .section = SECTION_PRE_DATA,
11635 : .createStmt = q->data,
11636 : .dropStmt = delq->data));
11637 :
11638 : /* Dump Type Comments and Security Labels */
11639 232 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11640 104 : dumpComment(fout, "TYPE", qtypname,
11641 104 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11642 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11643 :
11644 232 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11645 0 : dumpSecLabel(fout, "TYPE", qtypname,
11646 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11647 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11648 :
11649 232 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11650 68 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11651 : qtypname, NULL,
11652 68 : tyinfo->dobj.namespace->dobj.name,
11653 : NULL, tyinfo->rolname, &tyinfo->dacl);
11654 :
11655 232 : PQclear(res);
11656 232 : destroyPQExpBuffer(q);
11657 232 : destroyPQExpBuffer(delq);
11658 232 : destroyPQExpBuffer(query);
11659 232 : free(qtypname);
11660 232 : free(qualtypname);
11661 232 : }
11662 :
11663 : /*
11664 : * dumpUndefinedType
11665 : * writes out to fout the queries to recreate a !typisdefined type
11666 : *
11667 : * This is a shell type, but we use different terminology to distinguish
11668 : * this case from where we have to emit a shell type definition to break
11669 : * circular dependencies. An undefined type shouldn't ever have anything
11670 : * depending on it.
11671 : */
11672 : static void
11673 78 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11674 : {
11675 78 : DumpOptions *dopt = fout->dopt;
11676 78 : PQExpBuffer q = createPQExpBuffer();
11677 78 : PQExpBuffer delq = createPQExpBuffer();
11678 : char *qtypname;
11679 : char *qualtypname;
11680 :
11681 78 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11682 78 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11683 :
11684 78 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11685 :
11686 78 : if (dopt->binary_upgrade)
11687 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11688 : tyinfo->dobj.catId.oid,
11689 : false, false);
11690 :
11691 78 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11692 : qualtypname);
11693 :
11694 78 : if (dopt->binary_upgrade)
11695 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11696 : "TYPE", qtypname,
11697 4 : tyinfo->dobj.namespace->dobj.name);
11698 :
11699 78 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11700 78 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11701 78 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11702 : .namespace = tyinfo->dobj.namespace->dobj.name,
11703 : .owner = tyinfo->rolname,
11704 : .description = "TYPE",
11705 : .section = SECTION_PRE_DATA,
11706 : .createStmt = q->data,
11707 : .dropStmt = delq->data));
11708 :
11709 : /* Dump Type Comments and Security Labels */
11710 78 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11711 68 : dumpComment(fout, "TYPE", qtypname,
11712 68 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11713 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11714 :
11715 78 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11716 0 : dumpSecLabel(fout, "TYPE", qtypname,
11717 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11718 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11719 :
11720 78 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11721 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11722 : qtypname, NULL,
11723 0 : tyinfo->dobj.namespace->dobj.name,
11724 : NULL, tyinfo->rolname, &tyinfo->dacl);
11725 :
11726 78 : destroyPQExpBuffer(q);
11727 78 : destroyPQExpBuffer(delq);
11728 78 : free(qtypname);
11729 78 : free(qualtypname);
11730 78 : }
11731 :
11732 : /*
11733 : * dumpBaseType
11734 : * writes out to fout the queries to recreate a user-defined base type
11735 : */
11736 : static void
11737 558 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11738 : {
11739 558 : DumpOptions *dopt = fout->dopt;
11740 558 : PQExpBuffer q = createPQExpBuffer();
11741 558 : PQExpBuffer delq = createPQExpBuffer();
11742 558 : PQExpBuffer query = createPQExpBuffer();
11743 : PGresult *res;
11744 : char *qtypname;
11745 : char *qualtypname;
11746 : char *typlen;
11747 : char *typinput;
11748 : char *typoutput;
11749 : char *typreceive;
11750 : char *typsend;
11751 : char *typmodin;
11752 : char *typmodout;
11753 : char *typanalyze;
11754 : char *typsubscript;
11755 : Oid typreceiveoid;
11756 : Oid typsendoid;
11757 : Oid typmodinoid;
11758 : Oid typmodoutoid;
11759 : Oid typanalyzeoid;
11760 : Oid typsubscriptoid;
11761 : char *typcategory;
11762 : char *typispreferred;
11763 : char *typdelim;
11764 : char *typbyval;
11765 : char *typalign;
11766 : char *typstorage;
11767 : char *typcollatable;
11768 : char *typdefault;
11769 558 : bool typdefault_is_literal = false;
11770 :
11771 558 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11772 : {
11773 : /* Set up query for type-specific details */
11774 84 : appendPQExpBufferStr(query,
11775 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11776 : "SELECT typlen, "
11777 : "typinput, typoutput, typreceive, typsend, "
11778 : "typreceive::pg_catalog.oid AS typreceiveoid, "
11779 : "typsend::pg_catalog.oid AS typsendoid, "
11780 : "typanalyze, "
11781 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11782 : "typdelim, typbyval, typalign, typstorage, "
11783 : "typmodin, typmodout, "
11784 : "typmodin::pg_catalog.oid AS typmodinoid, "
11785 : "typmodout::pg_catalog.oid AS typmodoutoid, "
11786 : "typcategory, typispreferred, "
11787 : "(typcollation <> 0) AS typcollatable, "
11788 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11789 :
11790 84 : if (fout->remoteVersion >= 140000)
11791 84 : appendPQExpBufferStr(query,
11792 : "typsubscript, "
11793 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11794 : else
11795 0 : appendPQExpBufferStr(query,
11796 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
11797 :
11798 84 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11799 : "WHERE oid = $1");
11800 :
11801 84 : ExecuteSqlStatement(fout, query->data);
11802 :
11803 84 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11804 : }
11805 :
11806 558 : printfPQExpBuffer(query,
11807 : "EXECUTE dumpBaseType('%u')",
11808 : tyinfo->dobj.catId.oid);
11809 :
11810 558 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11811 :
11812 558 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11813 558 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11814 558 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11815 558 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11816 558 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11817 558 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11818 558 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11819 558 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11820 558 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11821 558 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11822 558 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11823 558 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11824 558 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11825 558 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11826 558 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11827 558 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11828 558 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11829 558 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11830 558 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11831 558 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11832 558 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11833 558 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11834 558 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11835 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11836 558 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11837 : {
11838 88 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11839 88 : typdefault_is_literal = true; /* it needs quotes */
11840 : }
11841 : else
11842 470 : typdefault = NULL;
11843 :
11844 558 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11845 558 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11846 :
11847 : /*
11848 : * The reason we include CASCADE is that the circular dependency between
11849 : * the type and its I/O functions makes it impossible to drop the type any
11850 : * other way.
11851 : */
11852 558 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11853 :
11854 : /*
11855 : * We might already have a shell type, but setting pg_type_oid is
11856 : * harmless, and in any case we'd better set the array type OID.
11857 : */
11858 558 : if (dopt->binary_upgrade)
11859 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11860 : tyinfo->dobj.catId.oid,
11861 : false, false);
11862 :
11863 558 : appendPQExpBuffer(q,
11864 : "CREATE TYPE %s (\n"
11865 : " INTERNALLENGTH = %s",
11866 : qualtypname,
11867 558 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11868 :
11869 : /* regproc result is sufficiently quoted already */
11870 558 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11871 558 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11872 558 : if (OidIsValid(typreceiveoid))
11873 408 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11874 558 : if (OidIsValid(typsendoid))
11875 408 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11876 558 : if (OidIsValid(typmodinoid))
11877 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11878 558 : if (OidIsValid(typmodoutoid))
11879 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11880 558 : if (OidIsValid(typanalyzeoid))
11881 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11882 :
11883 558 : if (strcmp(typcollatable, "t") == 0)
11884 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11885 :
11886 558 : if (typdefault != NULL)
11887 : {
11888 88 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
11889 88 : if (typdefault_is_literal)
11890 88 : appendStringLiteralAH(q, typdefault, fout);
11891 : else
11892 0 : appendPQExpBufferStr(q, typdefault);
11893 : }
11894 :
11895 558 : if (OidIsValid(typsubscriptoid))
11896 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11897 :
11898 558 : if (OidIsValid(tyinfo->typelem))
11899 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
11900 : getFormattedTypeName(fout, tyinfo->typelem,
11901 : zeroIsError));
11902 :
11903 558 : if (strcmp(typcategory, "U") != 0)
11904 : {
11905 310 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
11906 310 : appendStringLiteralAH(q, typcategory, fout);
11907 : }
11908 :
11909 558 : if (strcmp(typispreferred, "t") == 0)
11910 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
11911 :
11912 558 : if (typdelim && strcmp(typdelim, ",") != 0)
11913 : {
11914 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
11915 6 : appendStringLiteralAH(q, typdelim, fout);
11916 : }
11917 :
11918 558 : if (*typalign == TYPALIGN_CHAR)
11919 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11920 534 : else if (*typalign == TYPALIGN_SHORT)
11921 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11922 522 : else if (*typalign == TYPALIGN_INT)
11923 372 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11924 150 : else if (*typalign == TYPALIGN_DOUBLE)
11925 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11926 :
11927 558 : if (*typstorage == TYPSTORAGE_PLAIN)
11928 408 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
11929 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
11930 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
11931 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
11932 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
11933 18 : else if (*typstorage == TYPSTORAGE_MAIN)
11934 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
11935 :
11936 558 : if (strcmp(typbyval, "t") == 0)
11937 266 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11938 :
11939 558 : appendPQExpBufferStr(q, "\n);\n");
11940 :
11941 558 : if (dopt->binary_upgrade)
11942 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11943 : "TYPE", qtypname,
11944 16 : tyinfo->dobj.namespace->dobj.name);
11945 :
11946 558 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11947 558 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11948 558 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11949 : .namespace = tyinfo->dobj.namespace->dobj.name,
11950 : .owner = tyinfo->rolname,
11951 : .description = "TYPE",
11952 : .section = SECTION_PRE_DATA,
11953 : .createStmt = q->data,
11954 : .dropStmt = delq->data));
11955 :
11956 : /* Dump Type Comments and Security Labels */
11957 558 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11958 488 : dumpComment(fout, "TYPE", qtypname,
11959 488 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11960 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11961 :
11962 558 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11963 0 : dumpSecLabel(fout, "TYPE", qtypname,
11964 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11965 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11966 :
11967 558 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11968 68 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11969 : qtypname, NULL,
11970 68 : tyinfo->dobj.namespace->dobj.name,
11971 : NULL, tyinfo->rolname, &tyinfo->dacl);
11972 :
11973 558 : PQclear(res);
11974 558 : destroyPQExpBuffer(q);
11975 558 : destroyPQExpBuffer(delq);
11976 558 : destroyPQExpBuffer(query);
11977 558 : free(qtypname);
11978 558 : free(qualtypname);
11979 558 : }
11980 :
11981 : /*
11982 : * dumpDomain
11983 : * writes out to fout the queries to recreate a user-defined domain
11984 : */
11985 : static void
11986 278 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11987 : {
11988 278 : DumpOptions *dopt = fout->dopt;
11989 278 : PQExpBuffer q = createPQExpBuffer();
11990 278 : PQExpBuffer delq = createPQExpBuffer();
11991 278 : PQExpBuffer query = createPQExpBuffer();
11992 : PGresult *res;
11993 : int i;
11994 : char *qtypname;
11995 : char *qualtypname;
11996 : char *typnotnull;
11997 : char *typdefn;
11998 : char *typdefault;
11999 : Oid typcollation;
12000 278 : bool typdefault_is_literal = false;
12001 :
12002 278 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12003 : {
12004 : /* Set up query for domain-specific details */
12005 78 : appendPQExpBufferStr(query,
12006 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12007 :
12008 78 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12009 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12010 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12011 : "t.typdefault, "
12012 : "CASE WHEN t.typcollation <> u.typcollation "
12013 : "THEN t.typcollation ELSE 0 END AS typcollation "
12014 : "FROM pg_catalog.pg_type t "
12015 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12016 : "WHERE t.oid = $1");
12017 :
12018 78 : ExecuteSqlStatement(fout, query->data);
12019 :
12020 78 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12021 : }
12022 :
12023 278 : printfPQExpBuffer(query,
12024 : "EXECUTE dumpDomain('%u')",
12025 : tyinfo->dobj.catId.oid);
12026 :
12027 278 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12028 :
12029 278 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12030 278 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12031 278 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12032 78 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12033 200 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12034 : {
12035 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12036 0 : typdefault_is_literal = true; /* it needs quotes */
12037 : }
12038 : else
12039 200 : typdefault = NULL;
12040 278 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12041 :
12042 278 : if (dopt->binary_upgrade)
12043 44 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12044 : tyinfo->dobj.catId.oid,
12045 : true, /* force array type */
12046 : false); /* force multirange type */
12047 :
12048 278 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12049 278 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12050 :
12051 278 : appendPQExpBuffer(q,
12052 : "CREATE DOMAIN %s AS %s",
12053 : qualtypname,
12054 : typdefn);
12055 :
12056 : /* Print collation only if different from base type's collation */
12057 278 : if (OidIsValid(typcollation))
12058 : {
12059 : CollInfo *coll;
12060 :
12061 68 : coll = findCollationByOid(typcollation);
12062 68 : if (coll)
12063 68 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12064 : }
12065 :
12066 278 : if (typnotnull[0] == 't')
12067 30 : appendPQExpBufferStr(q, " NOT NULL");
12068 :
12069 278 : if (typdefault != NULL)
12070 : {
12071 78 : appendPQExpBufferStr(q, " DEFAULT ");
12072 78 : if (typdefault_is_literal)
12073 0 : appendStringLiteralAH(q, typdefault, fout);
12074 : else
12075 78 : appendPQExpBufferStr(q, typdefault);
12076 : }
12077 :
12078 278 : PQclear(res);
12079 :
12080 : /*
12081 : * Add any CHECK constraints for the domain
12082 : */
12083 466 : for (i = 0; i < tyinfo->nDomChecks; i++)
12084 : {
12085 188 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12086 :
12087 188 : if (!domcheck->separate)
12088 188 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12089 188 : fmtId(domcheck->dobj.name), domcheck->condef);
12090 : }
12091 :
12092 278 : appendPQExpBufferStr(q, ";\n");
12093 :
12094 278 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12095 :
12096 278 : if (dopt->binary_upgrade)
12097 44 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12098 : "DOMAIN", qtypname,
12099 44 : tyinfo->dobj.namespace->dobj.name);
12100 :
12101 278 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12102 278 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12103 278 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12104 : .namespace = tyinfo->dobj.namespace->dobj.name,
12105 : .owner = tyinfo->rolname,
12106 : .description = "DOMAIN",
12107 : .section = SECTION_PRE_DATA,
12108 : .createStmt = q->data,
12109 : .dropStmt = delq->data));
12110 :
12111 : /* Dump Domain Comments and Security Labels */
12112 278 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12113 0 : dumpComment(fout, "DOMAIN", qtypname,
12114 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12115 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12116 :
12117 278 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12118 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12119 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12120 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12121 :
12122 278 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12123 68 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12124 : qtypname, NULL,
12125 68 : tyinfo->dobj.namespace->dobj.name,
12126 : NULL, tyinfo->rolname, &tyinfo->dacl);
12127 :
12128 : /* Dump any per-constraint comments */
12129 466 : for (i = 0; i < tyinfo->nDomChecks; i++)
12130 : {
12131 188 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12132 188 : PQExpBuffer conprefix = createPQExpBuffer();
12133 :
12134 188 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12135 188 : fmtId(domcheck->dobj.name));
12136 :
12137 188 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12138 68 : dumpComment(fout, conprefix->data, qtypname,
12139 68 : tyinfo->dobj.namespace->dobj.name,
12140 : tyinfo->rolname,
12141 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12142 :
12143 188 : destroyPQExpBuffer(conprefix);
12144 : }
12145 :
12146 278 : destroyPQExpBuffer(q);
12147 278 : destroyPQExpBuffer(delq);
12148 278 : destroyPQExpBuffer(query);
12149 278 : free(qtypname);
12150 278 : free(qualtypname);
12151 278 : }
12152 :
12153 : /*
12154 : * dumpCompositeType
12155 : * writes out to fout the queries to recreate a user-defined stand-alone
12156 : * composite type
12157 : */
12158 : static void
12159 264 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12160 : {
12161 264 : DumpOptions *dopt = fout->dopt;
12162 264 : PQExpBuffer q = createPQExpBuffer();
12163 264 : PQExpBuffer dropped = createPQExpBuffer();
12164 264 : PQExpBuffer delq = createPQExpBuffer();
12165 264 : PQExpBuffer query = createPQExpBuffer();
12166 : PGresult *res;
12167 : char *qtypname;
12168 : char *qualtypname;
12169 : int ntups;
12170 : int i_attname;
12171 : int i_atttypdefn;
12172 : int i_attlen;
12173 : int i_attalign;
12174 : int i_attisdropped;
12175 : int i_attcollation;
12176 : int i;
12177 : int actual_atts;
12178 :
12179 264 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12180 : {
12181 : /*
12182 : * Set up query for type-specific details.
12183 : *
12184 : * Since we only want to dump COLLATE clauses for attributes whose
12185 : * collation is different from their type's default, we use a CASE
12186 : * here to suppress uninteresting attcollations cheaply. atttypid
12187 : * will be 0 for dropped columns; collation does not matter for those.
12188 : */
12189 114 : appendPQExpBufferStr(query,
12190 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12191 : "SELECT a.attname, a.attnum, "
12192 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12193 : "a.attlen, a.attalign, a.attisdropped, "
12194 : "CASE WHEN a.attcollation <> at.typcollation "
12195 : "THEN a.attcollation ELSE 0 END AS attcollation "
12196 : "FROM pg_catalog.pg_type ct "
12197 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12198 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12199 : "WHERE ct.oid = $1 "
12200 : "ORDER BY a.attnum");
12201 :
12202 114 : ExecuteSqlStatement(fout, query->data);
12203 :
12204 114 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12205 : }
12206 :
12207 264 : printfPQExpBuffer(query,
12208 : "EXECUTE dumpCompositeType('%u')",
12209 : tyinfo->dobj.catId.oid);
12210 :
12211 264 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12212 :
12213 264 : ntups = PQntuples(res);
12214 :
12215 264 : i_attname = PQfnumber(res, "attname");
12216 264 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12217 264 : i_attlen = PQfnumber(res, "attlen");
12218 264 : i_attalign = PQfnumber(res, "attalign");
12219 264 : i_attisdropped = PQfnumber(res, "attisdropped");
12220 264 : i_attcollation = PQfnumber(res, "attcollation");
12221 :
12222 264 : if (dopt->binary_upgrade)
12223 : {
12224 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12225 : tyinfo->dobj.catId.oid,
12226 : false, false);
12227 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12228 : }
12229 :
12230 264 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12231 264 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12232 :
12233 264 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12234 : qualtypname);
12235 :
12236 264 : actual_atts = 0;
12237 836 : for (i = 0; i < ntups; i++)
12238 : {
12239 : char *attname;
12240 : char *atttypdefn;
12241 : char *attlen;
12242 : char *attalign;
12243 : bool attisdropped;
12244 : Oid attcollation;
12245 :
12246 572 : attname = PQgetvalue(res, i, i_attname);
12247 572 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12248 572 : attlen = PQgetvalue(res, i, i_attlen);
12249 572 : attalign = PQgetvalue(res, i, i_attalign);
12250 572 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12251 572 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12252 :
12253 572 : if (attisdropped && !dopt->binary_upgrade)
12254 16 : continue;
12255 :
12256 : /* Format properly if not first attr */
12257 556 : if (actual_atts++ > 0)
12258 292 : appendPQExpBufferChar(q, ',');
12259 556 : appendPQExpBufferStr(q, "\n\t");
12260 :
12261 556 : if (!attisdropped)
12262 : {
12263 552 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12264 :
12265 : /* Add collation if not default for the column type */
12266 552 : if (OidIsValid(attcollation))
12267 : {
12268 : CollInfo *coll;
12269 :
12270 0 : coll = findCollationByOid(attcollation);
12271 0 : if (coll)
12272 0 : appendPQExpBuffer(q, " COLLATE %s",
12273 0 : fmtQualifiedDumpable(coll));
12274 : }
12275 : }
12276 : else
12277 : {
12278 : /*
12279 : * This is a dropped attribute and we're in binary_upgrade mode.
12280 : * Insert a placeholder for it in the CREATE TYPE command, and set
12281 : * length and alignment with direct UPDATE to the catalogs
12282 : * afterwards. See similar code in dumpTableSchema().
12283 : */
12284 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12285 :
12286 : /* stash separately for insertion after the CREATE TYPE */
12287 4 : appendPQExpBufferStr(dropped,
12288 : "\n-- For binary upgrade, recreate dropped column.\n");
12289 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12290 : "SET attlen = %s, "
12291 : "attalign = '%s', attbyval = false\n"
12292 : "WHERE attname = ", attlen, attalign);
12293 4 : appendStringLiteralAH(dropped, attname, fout);
12294 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12295 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12296 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12297 :
12298 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12299 : qualtypname);
12300 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12301 : fmtId(attname));
12302 : }
12303 : }
12304 264 : appendPQExpBufferStr(q, "\n);\n");
12305 264 : appendPQExpBufferStr(q, dropped->data);
12306 :
12307 264 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12308 :
12309 264 : if (dopt->binary_upgrade)
12310 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12311 : "TYPE", qtypname,
12312 36 : tyinfo->dobj.namespace->dobj.name);
12313 :
12314 264 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12315 230 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12316 230 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12317 : .namespace = tyinfo->dobj.namespace->dobj.name,
12318 : .owner = tyinfo->rolname,
12319 : .description = "TYPE",
12320 : .section = SECTION_PRE_DATA,
12321 : .createStmt = q->data,
12322 : .dropStmt = delq->data));
12323 :
12324 :
12325 : /* Dump Type Comments and Security Labels */
12326 264 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12327 68 : dumpComment(fout, "TYPE", qtypname,
12328 68 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12329 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12330 :
12331 264 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12332 0 : dumpSecLabel(fout, "TYPE", qtypname,
12333 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12334 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12335 :
12336 264 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12337 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12338 : qtypname, NULL,
12339 36 : tyinfo->dobj.namespace->dobj.name,
12340 : NULL, tyinfo->rolname, &tyinfo->dacl);
12341 :
12342 : /* Dump any per-column comments */
12343 264 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12344 68 : dumpCompositeTypeColComments(fout, tyinfo, res);
12345 :
12346 264 : PQclear(res);
12347 264 : destroyPQExpBuffer(q);
12348 264 : destroyPQExpBuffer(dropped);
12349 264 : destroyPQExpBuffer(delq);
12350 264 : destroyPQExpBuffer(query);
12351 264 : free(qtypname);
12352 264 : free(qualtypname);
12353 264 : }
12354 :
12355 : /*
12356 : * dumpCompositeTypeColComments
12357 : * writes out to fout the queries to recreate comments on the columns of
12358 : * a user-defined stand-alone composite type.
12359 : *
12360 : * The caller has already made a query to collect the names and attnums
12361 : * of the type's columns, so we just pass that result into here rather
12362 : * than reading them again.
12363 : */
12364 : static void
12365 68 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12366 : PGresult *res)
12367 : {
12368 : CommentItem *comments;
12369 : int ncomments;
12370 : PQExpBuffer query;
12371 : PQExpBuffer target;
12372 : int i;
12373 : int ntups;
12374 : int i_attname;
12375 : int i_attnum;
12376 : int i_attisdropped;
12377 :
12378 : /* do nothing, if --no-comments is supplied */
12379 68 : if (fout->dopt->no_comments)
12380 0 : return;
12381 :
12382 : /* Search for comments associated with type's pg_class OID */
12383 68 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12384 : &comments);
12385 :
12386 : /* If no comments exist, we're done */
12387 68 : if (ncomments <= 0)
12388 0 : return;
12389 :
12390 : /* Build COMMENT ON statements */
12391 68 : query = createPQExpBuffer();
12392 68 : target = createPQExpBuffer();
12393 :
12394 68 : ntups = PQntuples(res);
12395 68 : i_attnum = PQfnumber(res, "attnum");
12396 68 : i_attname = PQfnumber(res, "attname");
12397 68 : i_attisdropped = PQfnumber(res, "attisdropped");
12398 136 : while (ncomments > 0)
12399 : {
12400 : const char *attname;
12401 :
12402 68 : attname = NULL;
12403 68 : for (i = 0; i < ntups; i++)
12404 : {
12405 68 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12406 68 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12407 : {
12408 68 : attname = PQgetvalue(res, i, i_attname);
12409 68 : break;
12410 : }
12411 : }
12412 68 : if (attname) /* just in case we don't find it */
12413 : {
12414 68 : const char *descr = comments->descr;
12415 :
12416 68 : resetPQExpBuffer(target);
12417 68 : appendPQExpBuffer(target, "COLUMN %s.",
12418 68 : fmtId(tyinfo->dobj.name));
12419 68 : appendPQExpBufferStr(target, fmtId(attname));
12420 :
12421 68 : resetPQExpBuffer(query);
12422 68 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12423 68 : fmtQualifiedDumpable(tyinfo));
12424 68 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12425 68 : appendStringLiteralAH(query, descr, fout);
12426 68 : appendPQExpBufferStr(query, ";\n");
12427 :
12428 68 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12429 68 : ARCHIVE_OPTS(.tag = target->data,
12430 : .namespace = tyinfo->dobj.namespace->dobj.name,
12431 : .owner = tyinfo->rolname,
12432 : .description = "COMMENT",
12433 : .section = SECTION_NONE,
12434 : .createStmt = query->data,
12435 : .deps = &(tyinfo->dobj.dumpId),
12436 : .nDeps = 1));
12437 : }
12438 :
12439 68 : comments++;
12440 68 : ncomments--;
12441 : }
12442 :
12443 68 : destroyPQExpBuffer(query);
12444 68 : destroyPQExpBuffer(target);
12445 : }
12446 :
12447 : /*
12448 : * dumpShellType
12449 : * writes out to fout the queries to create a shell type
12450 : *
12451 : * We dump a shell definition in advance of the I/O functions for the type.
12452 : */
12453 : static void
12454 150 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12455 : {
12456 150 : DumpOptions *dopt = fout->dopt;
12457 : PQExpBuffer q;
12458 :
12459 : /* Do nothing if not dumping schema */
12460 150 : if (!dopt->dumpSchema)
12461 12 : return;
12462 :
12463 138 : q = createPQExpBuffer();
12464 :
12465 : /*
12466 : * Note the lack of a DROP command for the shell type; any required DROP
12467 : * is driven off the base type entry, instead. This interacts with
12468 : * _printTocEntry()'s use of the presence of a DROP command to decide
12469 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12470 : * the shell type's owner immediately on creation; that should happen only
12471 : * after it's filled in, otherwise the backend complains.
12472 : */
12473 :
12474 138 : if (dopt->binary_upgrade)
12475 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12476 16 : stinfo->baseType->dobj.catId.oid,
12477 : false, false);
12478 :
12479 138 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12480 138 : fmtQualifiedDumpable(stinfo));
12481 :
12482 138 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12483 138 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12484 138 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12485 : .namespace = stinfo->dobj.namespace->dobj.name,
12486 : .owner = stinfo->baseType->rolname,
12487 : .description = "SHELL TYPE",
12488 : .section = SECTION_PRE_DATA,
12489 : .createStmt = q->data));
12490 :
12491 138 : destroyPQExpBuffer(q);
12492 : }
12493 :
12494 : /*
12495 : * dumpProcLang
12496 : * writes out to fout the queries to recreate a user-defined
12497 : * procedural language
12498 : */
12499 : static void
12500 172 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12501 : {
12502 172 : DumpOptions *dopt = fout->dopt;
12503 : PQExpBuffer defqry;
12504 : PQExpBuffer delqry;
12505 : bool useParams;
12506 : char *qlanname;
12507 : FuncInfo *funcInfo;
12508 172 : FuncInfo *inlineInfo = NULL;
12509 172 : FuncInfo *validatorInfo = NULL;
12510 :
12511 : /* Do nothing if not dumping schema */
12512 172 : if (!dopt->dumpSchema)
12513 26 : return;
12514 :
12515 : /*
12516 : * Try to find the support function(s). It is not an error if we don't
12517 : * find them --- if the functions are in the pg_catalog schema, as is
12518 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12519 : * we will emit a parameterless CREATE LANGUAGE command, which will
12520 : * require PL template knowledge in the backend to reload.)
12521 : */
12522 :
12523 146 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12524 146 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12525 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12526 :
12527 146 : if (OidIsValid(plang->laninline))
12528 : {
12529 80 : inlineInfo = findFuncByOid(plang->laninline);
12530 80 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12531 2 : inlineInfo = NULL;
12532 : }
12533 :
12534 146 : if (OidIsValid(plang->lanvalidator))
12535 : {
12536 80 : validatorInfo = findFuncByOid(plang->lanvalidator);
12537 80 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12538 2 : validatorInfo = NULL;
12539 : }
12540 :
12541 : /*
12542 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12543 : * parameters. Otherwise, we'll write a parameterless command, which will
12544 : * be interpreted as CREATE EXTENSION.
12545 : */
12546 64 : useParams = (funcInfo != NULL &&
12547 274 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12548 64 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12549 :
12550 146 : defqry = createPQExpBuffer();
12551 146 : delqry = createPQExpBuffer();
12552 :
12553 146 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12554 :
12555 146 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12556 : qlanname);
12557 :
12558 146 : if (useParams)
12559 : {
12560 64 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12561 64 : plang->lanpltrusted ? "TRUSTED " : "",
12562 : qlanname);
12563 64 : appendPQExpBuffer(defqry, " HANDLER %s",
12564 64 : fmtQualifiedDumpable(funcInfo));
12565 64 : if (OidIsValid(plang->laninline))
12566 0 : appendPQExpBuffer(defqry, " INLINE %s",
12567 0 : fmtQualifiedDumpable(inlineInfo));
12568 64 : if (OidIsValid(plang->lanvalidator))
12569 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
12570 0 : fmtQualifiedDumpable(validatorInfo));
12571 : }
12572 : else
12573 : {
12574 : /*
12575 : * If not dumping parameters, then use CREATE OR REPLACE so that the
12576 : * command will not fail if the language is preinstalled in the target
12577 : * database.
12578 : *
12579 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
12580 : * EXISTS; perhaps we should emit that instead? But it might just add
12581 : * confusion.
12582 : */
12583 82 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12584 : qlanname);
12585 : }
12586 146 : appendPQExpBufferStr(defqry, ";\n");
12587 :
12588 146 : if (dopt->binary_upgrade)
12589 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
12590 : "LANGUAGE", qlanname, NULL);
12591 :
12592 146 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12593 66 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12594 66 : ARCHIVE_OPTS(.tag = plang->dobj.name,
12595 : .owner = plang->lanowner,
12596 : .description = "PROCEDURAL LANGUAGE",
12597 : .section = SECTION_PRE_DATA,
12598 : .createStmt = defqry->data,
12599 : .dropStmt = delqry->data,
12600 : ));
12601 :
12602 : /* Dump Proc Lang Comments and Security Labels */
12603 146 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12604 0 : dumpComment(fout, "LANGUAGE", qlanname,
12605 : NULL, plang->lanowner,
12606 : plang->dobj.catId, 0, plang->dobj.dumpId);
12607 :
12608 146 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12609 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
12610 : NULL, plang->lanowner,
12611 : plang->dobj.catId, 0, plang->dobj.dumpId);
12612 :
12613 146 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12614 80 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12615 : qlanname, NULL, NULL,
12616 : NULL, plang->lanowner, &plang->dacl);
12617 :
12618 146 : free(qlanname);
12619 :
12620 146 : destroyPQExpBuffer(defqry);
12621 146 : destroyPQExpBuffer(delqry);
12622 : }
12623 :
12624 : /*
12625 : * format_function_arguments: generate function name and argument list
12626 : *
12627 : * This is used when we can rely on pg_get_function_arguments to format
12628 : * the argument list. Note, however, that pg_get_function_arguments
12629 : * does not special-case zero-argument aggregates.
12630 : */
12631 : static char *
12632 8292 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12633 : {
12634 : PQExpBufferData fn;
12635 :
12636 8292 : initPQExpBuffer(&fn);
12637 8292 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12638 8292 : if (is_agg && finfo->nargs == 0)
12639 160 : appendPQExpBufferStr(&fn, "(*)");
12640 : else
12641 8132 : appendPQExpBuffer(&fn, "(%s)", funcargs);
12642 8292 : return fn.data;
12643 : }
12644 :
12645 : /*
12646 : * format_function_signature: generate function name and argument list
12647 : *
12648 : * Only a minimal list of input argument types is generated; this is
12649 : * sufficient to reference the function, but not to define it.
12650 : *
12651 : * If honor_quotes is false then the function name is never quoted.
12652 : * This is appropriate for use in TOC tags, but not in SQL commands.
12653 : */
12654 : static char *
12655 4374 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12656 : {
12657 : PQExpBufferData fn;
12658 : int j;
12659 :
12660 4374 : initPQExpBuffer(&fn);
12661 4374 : if (honor_quotes)
12662 802 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12663 : else
12664 3572 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12665 7998 : for (j = 0; j < finfo->nargs; j++)
12666 : {
12667 3624 : if (j > 0)
12668 844 : appendPQExpBufferStr(&fn, ", ");
12669 :
12670 3624 : appendPQExpBufferStr(&fn,
12671 3624 : getFormattedTypeName(fout, finfo->argtypes[j],
12672 : zeroIsError));
12673 : }
12674 4374 : appendPQExpBufferChar(&fn, ')');
12675 4374 : return fn.data;
12676 : }
12677 :
12678 :
12679 : /*
12680 : * dumpFunc:
12681 : * dump out one function
12682 : */
12683 : static void
12684 3696 : dumpFunc(Archive *fout, const FuncInfo *finfo)
12685 : {
12686 3696 : DumpOptions *dopt = fout->dopt;
12687 : PQExpBuffer query;
12688 : PQExpBuffer q;
12689 : PQExpBuffer delqry;
12690 : PQExpBuffer asPart;
12691 : PGresult *res;
12692 : char *funcsig; /* identity signature */
12693 3696 : char *funcfullsig = NULL; /* full signature */
12694 : char *funcsig_tag;
12695 : char *qual_funcsig;
12696 : char *proretset;
12697 : char *prosrc;
12698 : char *probin;
12699 : char *prosqlbody;
12700 : char *funcargs;
12701 : char *funciargs;
12702 : char *funcresult;
12703 : char *protrftypes;
12704 : char *prokind;
12705 : char *provolatile;
12706 : char *proisstrict;
12707 : char *prosecdef;
12708 : char *proleakproof;
12709 : char *proconfig;
12710 : char *procost;
12711 : char *prorows;
12712 : char *prosupport;
12713 : char *proparallel;
12714 : char *lanname;
12715 3696 : char **configitems = NULL;
12716 3696 : int nconfigitems = 0;
12717 : const char *keyword;
12718 :
12719 : /* Do nothing if not dumping schema */
12720 3696 : if (!dopt->dumpSchema)
12721 124 : return;
12722 :
12723 3572 : query = createPQExpBuffer();
12724 3572 : q = createPQExpBuffer();
12725 3572 : delqry = createPQExpBuffer();
12726 3572 : asPart = createPQExpBuffer();
12727 :
12728 3572 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12729 : {
12730 : /* Set up query for function-specific details */
12731 126 : appendPQExpBufferStr(query,
12732 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12733 :
12734 126 : appendPQExpBufferStr(query,
12735 : "SELECT\n"
12736 : "proretset,\n"
12737 : "prosrc,\n"
12738 : "probin,\n"
12739 : "provolatile,\n"
12740 : "proisstrict,\n"
12741 : "prosecdef,\n"
12742 : "lanname,\n"
12743 : "proconfig,\n"
12744 : "procost,\n"
12745 : "prorows,\n"
12746 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12747 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12748 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12749 : "proleakproof,\n");
12750 :
12751 126 : if (fout->remoteVersion >= 90500)
12752 126 : appendPQExpBufferStr(query,
12753 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12754 : else
12755 0 : appendPQExpBufferStr(query,
12756 : "NULL AS protrftypes,\n");
12757 :
12758 126 : if (fout->remoteVersion >= 90600)
12759 126 : appendPQExpBufferStr(query,
12760 : "proparallel,\n");
12761 : else
12762 0 : appendPQExpBufferStr(query,
12763 : "'u' AS proparallel,\n");
12764 :
12765 126 : if (fout->remoteVersion >= 110000)
12766 126 : appendPQExpBufferStr(query,
12767 : "prokind,\n");
12768 : else
12769 0 : appendPQExpBufferStr(query,
12770 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12771 :
12772 126 : if (fout->remoteVersion >= 120000)
12773 126 : appendPQExpBufferStr(query,
12774 : "prosupport,\n");
12775 : else
12776 0 : appendPQExpBufferStr(query,
12777 : "'-' AS prosupport,\n");
12778 :
12779 126 : if (fout->remoteVersion >= 140000)
12780 126 : appendPQExpBufferStr(query,
12781 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12782 : else
12783 0 : appendPQExpBufferStr(query,
12784 : "NULL AS prosqlbody\n");
12785 :
12786 126 : appendPQExpBufferStr(query,
12787 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12788 : "WHERE p.oid = $1 "
12789 : "AND l.oid = p.prolang");
12790 :
12791 126 : ExecuteSqlStatement(fout, query->data);
12792 :
12793 126 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12794 : }
12795 :
12796 3572 : printfPQExpBuffer(query,
12797 : "EXECUTE dumpFunc('%u')",
12798 : finfo->dobj.catId.oid);
12799 :
12800 3572 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12801 :
12802 3572 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12803 3572 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12804 : {
12805 3472 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12806 3472 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12807 3472 : prosqlbody = NULL;
12808 : }
12809 : else
12810 : {
12811 100 : prosrc = NULL;
12812 100 : probin = NULL;
12813 100 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12814 : }
12815 3572 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12816 3572 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12817 3572 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12818 3572 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12819 3572 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12820 3572 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12821 3572 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12822 3572 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12823 3572 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12824 3572 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12825 3572 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12826 3572 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12827 3572 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12828 3572 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12829 3572 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12830 :
12831 : /*
12832 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
12833 : * is used.
12834 : */
12835 3572 : if (prosqlbody)
12836 : {
12837 100 : appendPQExpBufferStr(asPart, prosqlbody);
12838 : }
12839 3472 : else if (probin[0] != '\0')
12840 : {
12841 298 : appendPQExpBufferStr(asPart, "AS ");
12842 298 : appendStringLiteralAH(asPart, probin, fout);
12843 298 : if (prosrc[0] != '\0')
12844 : {
12845 298 : appendPQExpBufferStr(asPart, ", ");
12846 :
12847 : /*
12848 : * where we have bin, use dollar quoting if allowed and src
12849 : * contains quote or backslash; else use regular quoting.
12850 : */
12851 298 : if (dopt->disable_dollar_quoting ||
12852 298 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12853 298 : appendStringLiteralAH(asPart, prosrc, fout);
12854 : else
12855 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
12856 : }
12857 : }
12858 : else
12859 : {
12860 3174 : appendPQExpBufferStr(asPart, "AS ");
12861 : /* with no bin, dollar quote src unconditionally if allowed */
12862 3174 : if (dopt->disable_dollar_quoting)
12863 0 : appendStringLiteralAH(asPart, prosrc, fout);
12864 : else
12865 3174 : appendStringLiteralDQ(asPart, prosrc, NULL);
12866 : }
12867 :
12868 3572 : if (*proconfig)
12869 : {
12870 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12871 0 : pg_fatal("could not parse %s array", "proconfig");
12872 : }
12873 : else
12874 : {
12875 3542 : configitems = NULL;
12876 3542 : nconfigitems = 0;
12877 : }
12878 :
12879 3572 : funcfullsig = format_function_arguments(finfo, funcargs, false);
12880 3572 : funcsig = format_function_arguments(finfo, funciargs, false);
12881 :
12882 3572 : funcsig_tag = format_function_signature(fout, finfo, false);
12883 :
12884 3572 : qual_funcsig = psprintf("%s.%s",
12885 3572 : fmtId(finfo->dobj.namespace->dobj.name),
12886 : funcsig);
12887 :
12888 3572 : if (prokind[0] == PROKIND_PROCEDURE)
12889 188 : keyword = "PROCEDURE";
12890 : else
12891 3384 : keyword = "FUNCTION"; /* works for window functions too */
12892 :
12893 3572 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
12894 : keyword, qual_funcsig);
12895 :
12896 7144 : appendPQExpBuffer(q, "CREATE %s %s.%s",
12897 : keyword,
12898 3572 : fmtId(finfo->dobj.namespace->dobj.name),
12899 : funcfullsig ? funcfullsig :
12900 : funcsig);
12901 :
12902 3572 : if (prokind[0] == PROKIND_PROCEDURE)
12903 : /* no result type to output */ ;
12904 3384 : else if (funcresult)
12905 3384 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
12906 : else
12907 0 : appendPQExpBuffer(q, " RETURNS %s%s",
12908 0 : (proretset[0] == 't') ? "SETOF " : "",
12909 : getFormattedTypeName(fout, finfo->prorettype,
12910 : zeroIsError));
12911 :
12912 3572 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12913 :
12914 3572 : if (*protrftypes)
12915 : {
12916 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
12917 : int i;
12918 :
12919 0 : appendPQExpBufferStr(q, " TRANSFORM ");
12920 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12921 0 : for (i = 0; typeids[i]; i++)
12922 : {
12923 0 : if (i != 0)
12924 0 : appendPQExpBufferStr(q, ", ");
12925 0 : appendPQExpBuffer(q, "FOR TYPE %s",
12926 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
12927 : }
12928 :
12929 0 : free(typeids);
12930 : }
12931 :
12932 3572 : if (prokind[0] == PROKIND_WINDOW)
12933 10 : appendPQExpBufferStr(q, " WINDOW");
12934 :
12935 3572 : if (provolatile[0] != PROVOLATILE_VOLATILE)
12936 : {
12937 710 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12938 668 : appendPQExpBufferStr(q, " IMMUTABLE");
12939 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
12940 42 : appendPQExpBufferStr(q, " STABLE");
12941 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
12942 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
12943 : finfo->dobj.name);
12944 : }
12945 :
12946 3572 : if (proisstrict[0] == 't')
12947 718 : appendPQExpBufferStr(q, " STRICT");
12948 :
12949 3572 : if (prosecdef[0] == 't')
12950 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
12951 :
12952 3572 : if (proleakproof[0] == 't')
12953 20 : appendPQExpBufferStr(q, " LEAKPROOF");
12954 :
12955 : /*
12956 : * COST and ROWS are emitted only if present and not default, so as not to
12957 : * break backwards-compatibility of the dump without need. Keep this code
12958 : * in sync with the defaults in functioncmds.c.
12959 : */
12960 3572 : if (strcmp(procost, "0") != 0)
12961 : {
12962 3572 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12963 : {
12964 : /* default cost is 1 */
12965 772 : if (strcmp(procost, "1") != 0)
12966 0 : appendPQExpBuffer(q, " COST %s", procost);
12967 : }
12968 : else
12969 : {
12970 : /* default cost is 100 */
12971 2800 : if (strcmp(procost, "100") != 0)
12972 12 : appendPQExpBuffer(q, " COST %s", procost);
12973 : }
12974 : }
12975 3572 : if (proretset[0] == 't' &&
12976 378 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12977 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
12978 :
12979 3572 : if (strcmp(prosupport, "-") != 0)
12980 : {
12981 : /* We rely on regprocout to provide quoting and qualification */
12982 88 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12983 : }
12984 :
12985 3572 : if (proparallel[0] != PROPARALLEL_UNSAFE)
12986 : {
12987 240 : if (proparallel[0] == PROPARALLEL_SAFE)
12988 230 : appendPQExpBufferStr(q, " PARALLEL SAFE");
12989 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12990 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12991 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
12992 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
12993 : finfo->dobj.name);
12994 : }
12995 :
12996 3642 : for (int i = 0; i < nconfigitems; i++)
12997 : {
12998 : /* we feel free to scribble on configitems[] here */
12999 70 : char *configitem = configitems[i];
13000 : char *pos;
13001 :
13002 70 : pos = strchr(configitem, '=');
13003 70 : if (pos == NULL)
13004 0 : continue;
13005 70 : *pos++ = '\0';
13006 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13007 :
13008 : /*
13009 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13010 : * by flatten_set_variable_args() before they were put into the
13011 : * proconfig array. However, because the quoting rules used there
13012 : * aren't exactly like SQL's, we have to break the list value apart
13013 : * and then quote the elements as string literals. (The elements may
13014 : * be double-quoted as-is, but we can't just feed them to the SQL
13015 : * parser; it would do the wrong thing with elements that are
13016 : * zero-length or longer than NAMEDATALEN.)
13017 : *
13018 : * Variables that are not so marked should just be emitted as simple
13019 : * string literals. If the variable is not known to
13020 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13021 : * to use GUC_LIST_QUOTE for extension variables.
13022 : */
13023 70 : if (variable_is_guc_list_quote(configitem))
13024 : {
13025 : char **namelist;
13026 : char **nameptr;
13027 :
13028 : /* Parse string into list of identifiers */
13029 : /* this shouldn't fail really */
13030 20 : if (SplitGUCList(pos, ',', &namelist))
13031 : {
13032 70 : for (nameptr = namelist; *nameptr; nameptr++)
13033 : {
13034 50 : if (nameptr != namelist)
13035 30 : appendPQExpBufferStr(q, ", ");
13036 50 : appendStringLiteralAH(q, *nameptr, fout);
13037 : }
13038 : }
13039 20 : pg_free(namelist);
13040 : }
13041 : else
13042 50 : appendStringLiteralAH(q, pos, fout);
13043 : }
13044 :
13045 3572 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13046 :
13047 3572 : append_depends_on_extension(fout, q, &finfo->dobj,
13048 : "pg_catalog.pg_proc", keyword,
13049 : qual_funcsig);
13050 :
13051 3572 : if (dopt->binary_upgrade)
13052 582 : binary_upgrade_extension_member(q, &finfo->dobj,
13053 : keyword, funcsig,
13054 582 : finfo->dobj.namespace->dobj.name);
13055 :
13056 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13057 3372 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13058 3372 : ARCHIVE_OPTS(.tag = funcsig_tag,
13059 : .namespace = finfo->dobj.namespace->dobj.name,
13060 : .owner = finfo->rolname,
13061 : .description = keyword,
13062 : .section = finfo->postponed_def ?
13063 : SECTION_POST_DATA : SECTION_PRE_DATA,
13064 : .createStmt = q->data,
13065 : .dropStmt = delqry->data));
13066 :
13067 : /* Dump Function Comments and Security Labels */
13068 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13069 18 : dumpComment(fout, keyword, funcsig,
13070 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13071 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13072 :
13073 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13074 0 : dumpSecLabel(fout, keyword, funcsig,
13075 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13076 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13077 :
13078 3572 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13079 208 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13080 : funcsig, NULL,
13081 208 : finfo->dobj.namespace->dobj.name,
13082 : NULL, finfo->rolname, &finfo->dacl);
13083 :
13084 3572 : PQclear(res);
13085 :
13086 3572 : destroyPQExpBuffer(query);
13087 3572 : destroyPQExpBuffer(q);
13088 3572 : destroyPQExpBuffer(delqry);
13089 3572 : destroyPQExpBuffer(asPart);
13090 3572 : free(funcsig);
13091 3572 : free(funcfullsig);
13092 3572 : free(funcsig_tag);
13093 3572 : free(qual_funcsig);
13094 3572 : free(configitems);
13095 : }
13096 :
13097 :
13098 : /*
13099 : * Dump a user-defined cast
13100 : */
13101 : static void
13102 138 : dumpCast(Archive *fout, const CastInfo *cast)
13103 : {
13104 138 : DumpOptions *dopt = fout->dopt;
13105 : PQExpBuffer defqry;
13106 : PQExpBuffer delqry;
13107 : PQExpBuffer labelq;
13108 : PQExpBuffer castargs;
13109 138 : FuncInfo *funcInfo = NULL;
13110 : const char *sourceType;
13111 : const char *targetType;
13112 :
13113 : /* Do nothing if not dumping schema */
13114 138 : if (!dopt->dumpSchema)
13115 12 : return;
13116 :
13117 : /* Cannot dump if we don't have the cast function's info */
13118 126 : if (OidIsValid(cast->castfunc))
13119 : {
13120 76 : funcInfo = findFuncByOid(cast->castfunc);
13121 76 : if (funcInfo == NULL)
13122 0 : pg_fatal("could not find function definition for function with OID %u",
13123 : cast->castfunc);
13124 : }
13125 :
13126 126 : defqry = createPQExpBuffer();
13127 126 : delqry = createPQExpBuffer();
13128 126 : labelq = createPQExpBuffer();
13129 126 : castargs = createPQExpBuffer();
13130 :
13131 126 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13132 126 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13133 126 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13134 : sourceType, targetType);
13135 :
13136 126 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13137 : sourceType, targetType);
13138 :
13139 126 : switch (cast->castmethod)
13140 : {
13141 50 : case COERCION_METHOD_BINARY:
13142 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13143 50 : break;
13144 0 : case COERCION_METHOD_INOUT:
13145 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13146 0 : break;
13147 76 : case COERCION_METHOD_FUNCTION:
13148 76 : if (funcInfo)
13149 : {
13150 76 : char *fsig = format_function_signature(fout, funcInfo, true);
13151 :
13152 : /*
13153 : * Always qualify the function name (format_function_signature
13154 : * won't qualify it).
13155 : */
13156 76 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13157 76 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13158 76 : free(fsig);
13159 : }
13160 : else
13161 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13162 76 : break;
13163 0 : default:
13164 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13165 : }
13166 :
13167 126 : if (cast->castcontext == 'a')
13168 66 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13169 60 : else if (cast->castcontext == 'i')
13170 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13171 126 : appendPQExpBufferStr(defqry, ";\n");
13172 :
13173 126 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13174 : sourceType, targetType);
13175 :
13176 126 : appendPQExpBuffer(castargs, "(%s AS %s)",
13177 : sourceType, targetType);
13178 :
13179 126 : if (dopt->binary_upgrade)
13180 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13181 14 : "CAST", castargs->data, NULL);
13182 :
13183 126 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13184 126 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13185 126 : ARCHIVE_OPTS(.tag = labelq->data,
13186 : .description = "CAST",
13187 : .section = SECTION_PRE_DATA,
13188 : .createStmt = defqry->data,
13189 : .dropStmt = delqry->data));
13190 :
13191 : /* Dump Cast Comments */
13192 126 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13193 0 : dumpComment(fout, "CAST", castargs->data,
13194 : NULL, "",
13195 : cast->dobj.catId, 0, cast->dobj.dumpId);
13196 :
13197 126 : destroyPQExpBuffer(defqry);
13198 126 : destroyPQExpBuffer(delqry);
13199 126 : destroyPQExpBuffer(labelq);
13200 126 : destroyPQExpBuffer(castargs);
13201 : }
13202 :
13203 : /*
13204 : * Dump a transform
13205 : */
13206 : static void
13207 88 : dumpTransform(Archive *fout, const TransformInfo *transform)
13208 : {
13209 88 : DumpOptions *dopt = fout->dopt;
13210 : PQExpBuffer defqry;
13211 : PQExpBuffer delqry;
13212 : PQExpBuffer labelq;
13213 : PQExpBuffer transformargs;
13214 88 : FuncInfo *fromsqlFuncInfo = NULL;
13215 88 : FuncInfo *tosqlFuncInfo = NULL;
13216 : char *lanname;
13217 : const char *transformType;
13218 :
13219 : /* Do nothing if not dumping schema */
13220 88 : if (!dopt->dumpSchema)
13221 12 : return;
13222 :
13223 : /* Cannot dump if we don't have the transform functions' info */
13224 76 : if (OidIsValid(transform->trffromsql))
13225 : {
13226 76 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13227 76 : if (fromsqlFuncInfo == NULL)
13228 0 : pg_fatal("could not find function definition for function with OID %u",
13229 : transform->trffromsql);
13230 : }
13231 76 : if (OidIsValid(transform->trftosql))
13232 : {
13233 76 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13234 76 : if (tosqlFuncInfo == NULL)
13235 0 : pg_fatal("could not find function definition for function with OID %u",
13236 : transform->trftosql);
13237 : }
13238 :
13239 76 : defqry = createPQExpBuffer();
13240 76 : delqry = createPQExpBuffer();
13241 76 : labelq = createPQExpBuffer();
13242 76 : transformargs = createPQExpBuffer();
13243 :
13244 76 : lanname = get_language_name(fout, transform->trflang);
13245 76 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13246 :
13247 76 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13248 : transformType, lanname);
13249 :
13250 76 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13251 : transformType, lanname);
13252 :
13253 76 : if (!transform->trffromsql && !transform->trftosql)
13254 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13255 :
13256 76 : if (transform->trffromsql)
13257 : {
13258 76 : if (fromsqlFuncInfo)
13259 : {
13260 76 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13261 :
13262 : /*
13263 : * Always qualify the function name (format_function_signature
13264 : * won't qualify it).
13265 : */
13266 76 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13267 76 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13268 76 : free(fsig);
13269 : }
13270 : else
13271 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13272 : }
13273 :
13274 76 : if (transform->trftosql)
13275 : {
13276 76 : if (transform->trffromsql)
13277 76 : appendPQExpBufferStr(defqry, ", ");
13278 :
13279 76 : if (tosqlFuncInfo)
13280 : {
13281 76 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13282 :
13283 : /*
13284 : * Always qualify the function name (format_function_signature
13285 : * won't qualify it).
13286 : */
13287 76 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13288 76 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13289 76 : free(fsig);
13290 : }
13291 : else
13292 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13293 : }
13294 :
13295 76 : appendPQExpBufferStr(defqry, ");\n");
13296 :
13297 76 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13298 : transformType, lanname);
13299 :
13300 76 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13301 : transformType, lanname);
13302 :
13303 76 : if (dopt->binary_upgrade)
13304 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13305 4 : "TRANSFORM", transformargs->data, NULL);
13306 :
13307 76 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13308 76 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13309 76 : ARCHIVE_OPTS(.tag = labelq->data,
13310 : .description = "TRANSFORM",
13311 : .section = SECTION_PRE_DATA,
13312 : .createStmt = defqry->data,
13313 : .dropStmt = delqry->data,
13314 : .deps = transform->dobj.dependencies,
13315 : .nDeps = transform->dobj.nDeps));
13316 :
13317 : /* Dump Transform Comments */
13318 76 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13319 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13320 : NULL, "",
13321 : transform->dobj.catId, 0, transform->dobj.dumpId);
13322 :
13323 76 : free(lanname);
13324 76 : destroyPQExpBuffer(defqry);
13325 76 : destroyPQExpBuffer(delqry);
13326 76 : destroyPQExpBuffer(labelq);
13327 76 : destroyPQExpBuffer(transformargs);
13328 : }
13329 :
13330 :
13331 : /*
13332 : * dumpOpr
13333 : * write out a single operator definition
13334 : */
13335 : static void
13336 5012 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
13337 : {
13338 5012 : DumpOptions *dopt = fout->dopt;
13339 : PQExpBuffer query;
13340 : PQExpBuffer q;
13341 : PQExpBuffer delq;
13342 : PQExpBuffer oprid;
13343 : PQExpBuffer details;
13344 : PGresult *res;
13345 : int i_oprkind;
13346 : int i_oprcode;
13347 : int i_oprleft;
13348 : int i_oprright;
13349 : int i_oprcom;
13350 : int i_oprnegate;
13351 : int i_oprrest;
13352 : int i_oprjoin;
13353 : int i_oprcanmerge;
13354 : int i_oprcanhash;
13355 : char *oprkind;
13356 : char *oprcode;
13357 : char *oprleft;
13358 : char *oprright;
13359 : char *oprcom;
13360 : char *oprnegate;
13361 : char *oprrest;
13362 : char *oprjoin;
13363 : char *oprcanmerge;
13364 : char *oprcanhash;
13365 : char *oprregproc;
13366 : char *oprref;
13367 :
13368 : /* Do nothing if not dumping schema */
13369 5012 : if (!dopt->dumpSchema)
13370 12 : return;
13371 :
13372 : /*
13373 : * some operators are invalid because they were the result of user
13374 : * defining operators before commutators exist
13375 : */
13376 5000 : if (!OidIsValid(oprinfo->oprcode))
13377 28 : return;
13378 :
13379 4972 : query = createPQExpBuffer();
13380 4972 : q = createPQExpBuffer();
13381 4972 : delq = createPQExpBuffer();
13382 4972 : oprid = createPQExpBuffer();
13383 4972 : details = createPQExpBuffer();
13384 :
13385 4972 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13386 : {
13387 : /* Set up query for operator-specific details */
13388 84 : appendPQExpBufferStr(query,
13389 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13390 : "SELECT oprkind, "
13391 : "oprcode::pg_catalog.regprocedure, "
13392 : "oprleft::pg_catalog.regtype, "
13393 : "oprright::pg_catalog.regtype, "
13394 : "oprcom, "
13395 : "oprnegate, "
13396 : "oprrest::pg_catalog.regprocedure, "
13397 : "oprjoin::pg_catalog.regprocedure, "
13398 : "oprcanmerge, oprcanhash "
13399 : "FROM pg_catalog.pg_operator "
13400 : "WHERE oid = $1");
13401 :
13402 84 : ExecuteSqlStatement(fout, query->data);
13403 :
13404 84 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13405 : }
13406 :
13407 4972 : printfPQExpBuffer(query,
13408 : "EXECUTE dumpOpr('%u')",
13409 : oprinfo->dobj.catId.oid);
13410 :
13411 4972 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13412 :
13413 4972 : i_oprkind = PQfnumber(res, "oprkind");
13414 4972 : i_oprcode = PQfnumber(res, "oprcode");
13415 4972 : i_oprleft = PQfnumber(res, "oprleft");
13416 4972 : i_oprright = PQfnumber(res, "oprright");
13417 4972 : i_oprcom = PQfnumber(res, "oprcom");
13418 4972 : i_oprnegate = PQfnumber(res, "oprnegate");
13419 4972 : i_oprrest = PQfnumber(res, "oprrest");
13420 4972 : i_oprjoin = PQfnumber(res, "oprjoin");
13421 4972 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13422 4972 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13423 :
13424 4972 : oprkind = PQgetvalue(res, 0, i_oprkind);
13425 4972 : oprcode = PQgetvalue(res, 0, i_oprcode);
13426 4972 : oprleft = PQgetvalue(res, 0, i_oprleft);
13427 4972 : oprright = PQgetvalue(res, 0, i_oprright);
13428 4972 : oprcom = PQgetvalue(res, 0, i_oprcom);
13429 4972 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13430 4972 : oprrest = PQgetvalue(res, 0, i_oprrest);
13431 4972 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13432 4972 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13433 4972 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13434 :
13435 : /* In PG14 upwards postfix operator support does not exist anymore. */
13436 4972 : if (strcmp(oprkind, "r") == 0)
13437 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13438 : oprcode);
13439 :
13440 4972 : oprregproc = convertRegProcReference(oprcode);
13441 4972 : if (oprregproc)
13442 : {
13443 4972 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13444 4972 : free(oprregproc);
13445 : }
13446 :
13447 4972 : appendPQExpBuffer(oprid, "%s (",
13448 : oprinfo->dobj.name);
13449 :
13450 : /*
13451 : * right unary means there's a left arg and left unary means there's a
13452 : * right arg. (Although the "r" case is dead code for PG14 and later,
13453 : * continue to support it in case we're dumping from an old server.)
13454 : */
13455 4972 : if (strcmp(oprkind, "r") == 0 ||
13456 4972 : strcmp(oprkind, "b") == 0)
13457 : {
13458 4686 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13459 4686 : appendPQExpBufferStr(oprid, oprleft);
13460 : }
13461 : else
13462 286 : appendPQExpBufferStr(oprid, "NONE");
13463 :
13464 4972 : if (strcmp(oprkind, "l") == 0 ||
13465 4686 : strcmp(oprkind, "b") == 0)
13466 : {
13467 4972 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13468 4972 : appendPQExpBuffer(oprid, ", %s)", oprright);
13469 : }
13470 : else
13471 0 : appendPQExpBufferStr(oprid, ", NONE)");
13472 :
13473 4972 : oprref = getFormattedOperatorName(oprcom);
13474 4972 : if (oprref)
13475 : {
13476 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13477 3322 : free(oprref);
13478 : }
13479 :
13480 4972 : oprref = getFormattedOperatorName(oprnegate);
13481 4972 : if (oprref)
13482 : {
13483 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13484 2326 : free(oprref);
13485 : }
13486 :
13487 4972 : if (strcmp(oprcanmerge, "t") == 0)
13488 370 : appendPQExpBufferStr(details, ",\n MERGES");
13489 :
13490 4972 : if (strcmp(oprcanhash, "t") == 0)
13491 276 : appendPQExpBufferStr(details, ",\n HASHES");
13492 :
13493 4972 : oprregproc = convertRegProcReference(oprrest);
13494 4972 : if (oprregproc)
13495 : {
13496 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13497 3028 : free(oprregproc);
13498 : }
13499 :
13500 4972 : oprregproc = convertRegProcReference(oprjoin);
13501 4972 : if (oprregproc)
13502 : {
13503 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13504 3028 : free(oprregproc);
13505 : }
13506 :
13507 4972 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13508 4972 : fmtId(oprinfo->dobj.namespace->dobj.name),
13509 : oprid->data);
13510 :
13511 4972 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13512 4972 : fmtId(oprinfo->dobj.namespace->dobj.name),
13513 : oprinfo->dobj.name, details->data);
13514 :
13515 4972 : if (dopt->binary_upgrade)
13516 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13517 24 : "OPERATOR", oprid->data,
13518 24 : oprinfo->dobj.namespace->dobj.name);
13519 :
13520 4972 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13521 4972 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13522 4972 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13523 : .namespace = oprinfo->dobj.namespace->dobj.name,
13524 : .owner = oprinfo->rolname,
13525 : .description = "OPERATOR",
13526 : .section = SECTION_PRE_DATA,
13527 : .createStmt = q->data,
13528 : .dropStmt = delq->data));
13529 :
13530 : /* Dump Operator Comments */
13531 4972 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13532 4794 : dumpComment(fout, "OPERATOR", oprid->data,
13533 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13534 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13535 :
13536 4972 : PQclear(res);
13537 :
13538 4972 : destroyPQExpBuffer(query);
13539 4972 : destroyPQExpBuffer(q);
13540 4972 : destroyPQExpBuffer(delq);
13541 4972 : destroyPQExpBuffer(oprid);
13542 4972 : destroyPQExpBuffer(details);
13543 : }
13544 :
13545 : /*
13546 : * Convert a function reference obtained from pg_operator
13547 : *
13548 : * Returns allocated string of what to print, or NULL if function references
13549 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13550 : *
13551 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13552 : * part.
13553 : */
13554 : static char *
13555 14916 : convertRegProcReference(const char *proc)
13556 : {
13557 : char *name;
13558 : char *paren;
13559 : bool inquote;
13560 :
13561 : /* In all cases "-" means a null reference */
13562 14916 : if (strcmp(proc, "-") == 0)
13563 3888 : return NULL;
13564 :
13565 11028 : name = pg_strdup(proc);
13566 : /* find non-double-quoted left paren */
13567 11028 : inquote = false;
13568 132892 : for (paren = name; *paren; paren++)
13569 : {
13570 132892 : if (*paren == '(' && !inquote)
13571 : {
13572 11028 : *paren = '\0';
13573 11028 : break;
13574 : }
13575 121864 : if (*paren == '"')
13576 100 : inquote = !inquote;
13577 : }
13578 11028 : return name;
13579 : }
13580 :
13581 : /*
13582 : * getFormattedOperatorName - retrieve the operator name for the
13583 : * given operator OID (presented in string form).
13584 : *
13585 : * Returns an allocated string, or NULL if the given OID is invalid.
13586 : * Caller is responsible for free'ing result string.
13587 : *
13588 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
13589 : * useful in commands where the operator's argument types can be inferred from
13590 : * context. We always schema-qualify the name, though. The predecessor to
13591 : * this code tried to skip the schema qualification if possible, but that led
13592 : * to wrong results in corner cases, such as if an operator and its negator
13593 : * are in different schemas.
13594 : */
13595 : static char *
13596 10518 : getFormattedOperatorName(const char *oproid)
13597 : {
13598 : OprInfo *oprInfo;
13599 :
13600 : /* In all cases "0" means a null reference */
13601 10518 : if (strcmp(oproid, "0") == 0)
13602 4870 : return NULL;
13603 :
13604 5648 : oprInfo = findOprByOid(atooid(oproid));
13605 5648 : if (oprInfo == NULL)
13606 : {
13607 0 : pg_log_warning("could not find operator with OID %s",
13608 : oproid);
13609 0 : return NULL;
13610 : }
13611 :
13612 5648 : return psprintf("OPERATOR(%s.%s)",
13613 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
13614 : oprInfo->dobj.name);
13615 : }
13616 :
13617 : /*
13618 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13619 : *
13620 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13621 : * argument lists of these functions are predetermined. Note that the
13622 : * caller should ensure we are in the proper schema, because the results
13623 : * are search path dependent!
13624 : */
13625 : static char *
13626 430 : convertTSFunction(Archive *fout, Oid funcOid)
13627 : {
13628 : char *result;
13629 : char query[128];
13630 : PGresult *res;
13631 :
13632 430 : snprintf(query, sizeof(query),
13633 : "SELECT '%u'::pg_catalog.regproc", funcOid);
13634 430 : res = ExecuteSqlQueryForSingleRow(fout, query);
13635 :
13636 430 : result = pg_strdup(PQgetvalue(res, 0, 0));
13637 :
13638 430 : PQclear(res);
13639 :
13640 430 : return result;
13641 : }
13642 :
13643 : /*
13644 : * dumpAccessMethod
13645 : * write out a single access method definition
13646 : */
13647 : static void
13648 168 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13649 : {
13650 168 : DumpOptions *dopt = fout->dopt;
13651 : PQExpBuffer q;
13652 : PQExpBuffer delq;
13653 : char *qamname;
13654 :
13655 : /* Do nothing if not dumping schema */
13656 168 : if (!dopt->dumpSchema)
13657 24 : return;
13658 :
13659 144 : q = createPQExpBuffer();
13660 144 : delq = createPQExpBuffer();
13661 :
13662 144 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
13663 :
13664 144 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13665 :
13666 144 : switch (aminfo->amtype)
13667 : {
13668 68 : case AMTYPE_INDEX:
13669 68 : appendPQExpBufferStr(q, "TYPE INDEX ");
13670 68 : break;
13671 76 : case AMTYPE_TABLE:
13672 76 : appendPQExpBufferStr(q, "TYPE TABLE ");
13673 76 : break;
13674 0 : default:
13675 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13676 : aminfo->amtype, qamname);
13677 0 : destroyPQExpBuffer(q);
13678 0 : destroyPQExpBuffer(delq);
13679 0 : free(qamname);
13680 0 : return;
13681 : }
13682 :
13683 144 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13684 :
13685 144 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13686 : qamname);
13687 :
13688 144 : if (dopt->binary_upgrade)
13689 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
13690 : "ACCESS METHOD", qamname, NULL);
13691 :
13692 144 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13693 144 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13694 144 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13695 : .description = "ACCESS METHOD",
13696 : .section = SECTION_PRE_DATA,
13697 : .createStmt = q->data,
13698 : .dropStmt = delq->data));
13699 :
13700 : /* Dump Access Method Comments */
13701 144 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13702 0 : dumpComment(fout, "ACCESS METHOD", qamname,
13703 : NULL, "",
13704 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13705 :
13706 144 : destroyPQExpBuffer(q);
13707 144 : destroyPQExpBuffer(delq);
13708 144 : free(qamname);
13709 : }
13710 :
13711 : /*
13712 : * dumpOpclass
13713 : * write out a single operator class definition
13714 : */
13715 : static void
13716 1332 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13717 : {
13718 1332 : DumpOptions *dopt = fout->dopt;
13719 : PQExpBuffer query;
13720 : PQExpBuffer q;
13721 : PQExpBuffer delq;
13722 : PQExpBuffer nameusing;
13723 : PGresult *res;
13724 : int ntups;
13725 : int i_opcintype;
13726 : int i_opckeytype;
13727 : int i_opcdefault;
13728 : int i_opcfamily;
13729 : int i_opcfamilyname;
13730 : int i_opcfamilynsp;
13731 : int i_amname;
13732 : int i_amopstrategy;
13733 : int i_amopopr;
13734 : int i_sortfamily;
13735 : int i_sortfamilynsp;
13736 : int i_amprocnum;
13737 : int i_amproc;
13738 : int i_amproclefttype;
13739 : int i_amprocrighttype;
13740 : char *opcintype;
13741 : char *opckeytype;
13742 : char *opcdefault;
13743 : char *opcfamily;
13744 : char *opcfamilyname;
13745 : char *opcfamilynsp;
13746 : char *amname;
13747 : char *amopstrategy;
13748 : char *amopopr;
13749 : char *sortfamily;
13750 : char *sortfamilynsp;
13751 : char *amprocnum;
13752 : char *amproc;
13753 : char *amproclefttype;
13754 : char *amprocrighttype;
13755 : bool needComma;
13756 : int i;
13757 :
13758 : /* Do nothing if not dumping schema */
13759 1332 : if (!dopt->dumpSchema)
13760 36 : return;
13761 :
13762 1296 : query = createPQExpBuffer();
13763 1296 : q = createPQExpBuffer();
13764 1296 : delq = createPQExpBuffer();
13765 1296 : nameusing = createPQExpBuffer();
13766 :
13767 : /* Get additional fields from the pg_opclass row */
13768 1296 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13769 : "opckeytype::pg_catalog.regtype, "
13770 : "opcdefault, opcfamily, "
13771 : "opfname AS opcfamilyname, "
13772 : "nspname AS opcfamilynsp, "
13773 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13774 : "FROM pg_catalog.pg_opclass c "
13775 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13776 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13777 : "WHERE c.oid = '%u'::pg_catalog.oid",
13778 : opcinfo->dobj.catId.oid);
13779 :
13780 1296 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13781 :
13782 1296 : i_opcintype = PQfnumber(res, "opcintype");
13783 1296 : i_opckeytype = PQfnumber(res, "opckeytype");
13784 1296 : i_opcdefault = PQfnumber(res, "opcdefault");
13785 1296 : i_opcfamily = PQfnumber(res, "opcfamily");
13786 1296 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13787 1296 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13788 1296 : i_amname = PQfnumber(res, "amname");
13789 :
13790 : /* opcintype may still be needed after we PQclear res */
13791 1296 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13792 1296 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
13793 1296 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
13794 : /* opcfamily will still be needed after we PQclear res */
13795 1296 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13796 1296 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13797 1296 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13798 : /* amname will still be needed after we PQclear res */
13799 1296 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13800 :
13801 1296 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13802 1296 : fmtQualifiedDumpable(opcinfo));
13803 1296 : appendPQExpBuffer(delq, " USING %s;\n",
13804 : fmtId(amname));
13805 :
13806 : /* Build the fixed portion of the CREATE command */
13807 1296 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13808 1296 : fmtQualifiedDumpable(opcinfo));
13809 1296 : if (strcmp(opcdefault, "t") == 0)
13810 714 : appendPQExpBufferStr(q, "DEFAULT ");
13811 1296 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13812 : opcintype,
13813 : fmtId(amname));
13814 1296 : if (strlen(opcfamilyname) > 0)
13815 : {
13816 1296 : appendPQExpBufferStr(q, " FAMILY ");
13817 1296 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13818 1296 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
13819 : }
13820 1296 : appendPQExpBufferStr(q, " AS\n ");
13821 :
13822 1296 : needComma = false;
13823 :
13824 1296 : if (strcmp(opckeytype, "-") != 0)
13825 : {
13826 504 : appendPQExpBuffer(q, "STORAGE %s",
13827 : opckeytype);
13828 504 : needComma = true;
13829 : }
13830 :
13831 1296 : PQclear(res);
13832 :
13833 : /*
13834 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13835 : *
13836 : * Print only those opfamily members that are tied to the opclass by
13837 : * pg_depend entries.
13838 : */
13839 1296 : resetPQExpBuffer(query);
13840 1296 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13841 : "amopopr::pg_catalog.regoperator, "
13842 : "opfname AS sortfamily, "
13843 : "nspname AS sortfamilynsp "
13844 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13845 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13846 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13847 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13848 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13849 : "AND refobjid = '%u'::pg_catalog.oid "
13850 : "AND amopfamily = '%s'::pg_catalog.oid "
13851 : "ORDER BY amopstrategy",
13852 : opcinfo->dobj.catId.oid,
13853 : opcfamily);
13854 :
13855 1296 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13856 :
13857 1296 : ntups = PQntuples(res);
13858 :
13859 1296 : i_amopstrategy = PQfnumber(res, "amopstrategy");
13860 1296 : i_amopopr = PQfnumber(res, "amopopr");
13861 1296 : i_sortfamily = PQfnumber(res, "sortfamily");
13862 1296 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13863 :
13864 1724 : for (i = 0; i < ntups; i++)
13865 : {
13866 428 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13867 428 : amopopr = PQgetvalue(res, i, i_amopopr);
13868 428 : sortfamily = PQgetvalue(res, i, i_sortfamily);
13869 428 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13870 :
13871 428 : if (needComma)
13872 272 : appendPQExpBufferStr(q, " ,\n ");
13873 :
13874 428 : appendPQExpBuffer(q, "OPERATOR %s %s",
13875 : amopstrategy, amopopr);
13876 :
13877 428 : if (strlen(sortfamily) > 0)
13878 : {
13879 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13880 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13881 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13882 : }
13883 :
13884 428 : needComma = true;
13885 : }
13886 :
13887 1296 : PQclear(res);
13888 :
13889 : /*
13890 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13891 : *
13892 : * Print only those opfamily members that are tied to the opclass by
13893 : * pg_depend entries.
13894 : *
13895 : * We print the amproclefttype/amprocrighttype even though in most cases
13896 : * the backend could deduce the right values, because of the corner case
13897 : * of a btree sort support function for a cross-type comparison.
13898 : */
13899 1296 : resetPQExpBuffer(query);
13900 :
13901 1296 : appendPQExpBuffer(query, "SELECT amprocnum, "
13902 : "amproc::pg_catalog.regprocedure, "
13903 : "amproclefttype::pg_catalog.regtype, "
13904 : "amprocrighttype::pg_catalog.regtype "
13905 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13906 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13907 : "AND refobjid = '%u'::pg_catalog.oid "
13908 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13909 : "AND objid = ap.oid "
13910 : "ORDER BY amprocnum",
13911 : opcinfo->dobj.catId.oid);
13912 :
13913 1296 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13914 :
13915 1296 : ntups = PQntuples(res);
13916 :
13917 1296 : i_amprocnum = PQfnumber(res, "amprocnum");
13918 1296 : i_amproc = PQfnumber(res, "amproc");
13919 1296 : i_amproclefttype = PQfnumber(res, "amproclefttype");
13920 1296 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13921 :
13922 1364 : for (i = 0; i < ntups; i++)
13923 : {
13924 68 : amprocnum = PQgetvalue(res, i, i_amprocnum);
13925 68 : amproc = PQgetvalue(res, i, i_amproc);
13926 68 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13927 68 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13928 :
13929 68 : if (needComma)
13930 68 : appendPQExpBufferStr(q, " ,\n ");
13931 :
13932 68 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13933 :
13934 68 : if (*amproclefttype && *amprocrighttype)
13935 68 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13936 :
13937 68 : appendPQExpBuffer(q, " %s", amproc);
13938 :
13939 68 : needComma = true;
13940 : }
13941 :
13942 1296 : PQclear(res);
13943 :
13944 : /*
13945 : * If needComma is still false it means we haven't added anything after
13946 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13947 : * clause with the same datatype. This isn't sanctioned by the
13948 : * documentation, but actually DefineOpClass will treat it as a no-op.
13949 : */
13950 1296 : if (!needComma)
13951 636 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
13952 :
13953 1296 : appendPQExpBufferStr(q, ";\n");
13954 :
13955 1296 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13956 1296 : appendPQExpBuffer(nameusing, " USING %s",
13957 : fmtId(amname));
13958 :
13959 1296 : if (dopt->binary_upgrade)
13960 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
13961 12 : "OPERATOR CLASS", nameusing->data,
13962 12 : opcinfo->dobj.namespace->dobj.name);
13963 :
13964 1296 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13965 1296 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13966 1296 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13967 : .namespace = opcinfo->dobj.namespace->dobj.name,
13968 : .owner = opcinfo->rolname,
13969 : .description = "OPERATOR CLASS",
13970 : .section = SECTION_PRE_DATA,
13971 : .createStmt = q->data,
13972 : .dropStmt = delq->data));
13973 :
13974 : /* Dump Operator Class Comments */
13975 1296 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13976 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13977 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13978 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13979 :
13980 1296 : free(opcintype);
13981 1296 : free(opcfamily);
13982 1296 : free(amname);
13983 1296 : destroyPQExpBuffer(query);
13984 1296 : destroyPQExpBuffer(q);
13985 1296 : destroyPQExpBuffer(delq);
13986 1296 : destroyPQExpBuffer(nameusing);
13987 : }
13988 :
13989 : /*
13990 : * dumpOpfamily
13991 : * write out a single operator family definition
13992 : *
13993 : * Note: this also dumps any "loose" operator members that aren't bound to a
13994 : * specific opclass within the opfamily.
13995 : */
13996 : static void
13997 1106 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13998 : {
13999 1106 : DumpOptions *dopt = fout->dopt;
14000 : PQExpBuffer query;
14001 : PQExpBuffer q;
14002 : PQExpBuffer delq;
14003 : PQExpBuffer nameusing;
14004 : PGresult *res;
14005 : PGresult *res_ops;
14006 : PGresult *res_procs;
14007 : int ntups;
14008 : int i_amname;
14009 : int i_amopstrategy;
14010 : int i_amopopr;
14011 : int i_sortfamily;
14012 : int i_sortfamilynsp;
14013 : int i_amprocnum;
14014 : int i_amproc;
14015 : int i_amproclefttype;
14016 : int i_amprocrighttype;
14017 : char *amname;
14018 : char *amopstrategy;
14019 : char *amopopr;
14020 : char *sortfamily;
14021 : char *sortfamilynsp;
14022 : char *amprocnum;
14023 : char *amproc;
14024 : char *amproclefttype;
14025 : char *amprocrighttype;
14026 : bool needComma;
14027 : int i;
14028 :
14029 : /* Do nothing if not dumping schema */
14030 1106 : if (!dopt->dumpSchema)
14031 24 : return;
14032 :
14033 1082 : query = createPQExpBuffer();
14034 1082 : q = createPQExpBuffer();
14035 1082 : delq = createPQExpBuffer();
14036 1082 : nameusing = createPQExpBuffer();
14037 :
14038 : /*
14039 : * Fetch only those opfamily members that are tied directly to the
14040 : * opfamily by pg_depend entries.
14041 : */
14042 1082 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14043 : "amopopr::pg_catalog.regoperator, "
14044 : "opfname AS sortfamily, "
14045 : "nspname AS sortfamilynsp "
14046 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14047 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14048 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14049 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14050 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14051 : "AND refobjid = '%u'::pg_catalog.oid "
14052 : "AND amopfamily = '%u'::pg_catalog.oid "
14053 : "ORDER BY amopstrategy",
14054 : opfinfo->dobj.catId.oid,
14055 : opfinfo->dobj.catId.oid);
14056 :
14057 1082 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14058 :
14059 1082 : resetPQExpBuffer(query);
14060 :
14061 1082 : appendPQExpBuffer(query, "SELECT amprocnum, "
14062 : "amproc::pg_catalog.regprocedure, "
14063 : "amproclefttype::pg_catalog.regtype, "
14064 : "amprocrighttype::pg_catalog.regtype "
14065 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14066 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14067 : "AND refobjid = '%u'::pg_catalog.oid "
14068 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14069 : "AND objid = ap.oid "
14070 : "ORDER BY amprocnum",
14071 : opfinfo->dobj.catId.oid);
14072 :
14073 1082 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14074 :
14075 : /* Get additional fields from the pg_opfamily row */
14076 1082 : resetPQExpBuffer(query);
14077 :
14078 1082 : appendPQExpBuffer(query, "SELECT "
14079 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14080 : "FROM pg_catalog.pg_opfamily "
14081 : "WHERE oid = '%u'::pg_catalog.oid",
14082 : opfinfo->dobj.catId.oid);
14083 :
14084 1082 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14085 :
14086 1082 : i_amname = PQfnumber(res, "amname");
14087 :
14088 : /* amname will still be needed after we PQclear res */
14089 1082 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14090 :
14091 1082 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14092 1082 : fmtQualifiedDumpable(opfinfo));
14093 1082 : appendPQExpBuffer(delq, " USING %s;\n",
14094 : fmtId(amname));
14095 :
14096 : /* Build the fixed portion of the CREATE command */
14097 1082 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14098 1082 : fmtQualifiedDumpable(opfinfo));
14099 1082 : appendPQExpBuffer(q, " USING %s;\n",
14100 : fmtId(amname));
14101 :
14102 1082 : PQclear(res);
14103 :
14104 : /* Do we need an ALTER to add loose members? */
14105 1082 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14106 : {
14107 98 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14108 98 : fmtQualifiedDumpable(opfinfo));
14109 98 : appendPQExpBuffer(q, " USING %s ADD\n ",
14110 : fmtId(amname));
14111 :
14112 98 : needComma = false;
14113 :
14114 : /*
14115 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14116 : */
14117 98 : ntups = PQntuples(res_ops);
14118 :
14119 98 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14120 98 : i_amopopr = PQfnumber(res_ops, "amopopr");
14121 98 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14122 98 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14123 :
14124 438 : for (i = 0; i < ntups; i++)
14125 : {
14126 340 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14127 340 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14128 340 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14129 340 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14130 :
14131 340 : if (needComma)
14132 272 : appendPQExpBufferStr(q, " ,\n ");
14133 :
14134 340 : appendPQExpBuffer(q, "OPERATOR %s %s",
14135 : amopstrategy, amopopr);
14136 :
14137 340 : if (strlen(sortfamily) > 0)
14138 : {
14139 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14140 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14141 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14142 : }
14143 :
14144 340 : needComma = true;
14145 : }
14146 :
14147 : /*
14148 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14149 : */
14150 98 : ntups = PQntuples(res_procs);
14151 :
14152 98 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14153 98 : i_amproc = PQfnumber(res_procs, "amproc");
14154 98 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14155 98 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14156 :
14157 468 : for (i = 0; i < ntups; i++)
14158 : {
14159 370 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14160 370 : amproc = PQgetvalue(res_procs, i, i_amproc);
14161 370 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14162 370 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14163 :
14164 370 : if (needComma)
14165 340 : appendPQExpBufferStr(q, " ,\n ");
14166 :
14167 370 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14168 : amprocnum, amproclefttype, amprocrighttype,
14169 : amproc);
14170 :
14171 370 : needComma = true;
14172 : }
14173 :
14174 98 : appendPQExpBufferStr(q, ";\n");
14175 : }
14176 :
14177 1082 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14178 1082 : appendPQExpBuffer(nameusing, " USING %s",
14179 : fmtId(amname));
14180 :
14181 1082 : if (dopt->binary_upgrade)
14182 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14183 18 : "OPERATOR FAMILY", nameusing->data,
14184 18 : opfinfo->dobj.namespace->dobj.name);
14185 :
14186 1082 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14187 1082 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14188 1082 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14189 : .namespace = opfinfo->dobj.namespace->dobj.name,
14190 : .owner = opfinfo->rolname,
14191 : .description = "OPERATOR FAMILY",
14192 : .section = SECTION_PRE_DATA,
14193 : .createStmt = q->data,
14194 : .dropStmt = delq->data));
14195 :
14196 : /* Dump Operator Family Comments */
14197 1082 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14198 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14199 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14200 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14201 :
14202 1082 : free(amname);
14203 1082 : PQclear(res_ops);
14204 1082 : PQclear(res_procs);
14205 1082 : destroyPQExpBuffer(query);
14206 1082 : destroyPQExpBuffer(q);
14207 1082 : destroyPQExpBuffer(delq);
14208 1082 : destroyPQExpBuffer(nameusing);
14209 : }
14210 :
14211 : /*
14212 : * dumpCollation
14213 : * write out a single collation definition
14214 : */
14215 : static void
14216 4944 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14217 : {
14218 4944 : DumpOptions *dopt = fout->dopt;
14219 : PQExpBuffer query;
14220 : PQExpBuffer q;
14221 : PQExpBuffer delq;
14222 : char *qcollname;
14223 : PGresult *res;
14224 : int i_collprovider;
14225 : int i_collisdeterministic;
14226 : int i_collcollate;
14227 : int i_collctype;
14228 : int i_colllocale;
14229 : int i_collicurules;
14230 : const char *collprovider;
14231 : const char *collcollate;
14232 : const char *collctype;
14233 : const char *colllocale;
14234 : const char *collicurules;
14235 :
14236 : /* Do nothing if not dumping schema */
14237 4944 : if (!dopt->dumpSchema)
14238 24 : return;
14239 :
14240 4920 : query = createPQExpBuffer();
14241 4920 : q = createPQExpBuffer();
14242 4920 : delq = createPQExpBuffer();
14243 :
14244 4920 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14245 :
14246 : /* Get collation-specific details */
14247 4920 : appendPQExpBufferStr(query, "SELECT ");
14248 :
14249 4920 : if (fout->remoteVersion >= 100000)
14250 4920 : appendPQExpBufferStr(query,
14251 : "collprovider, "
14252 : "collversion, ");
14253 : else
14254 0 : appendPQExpBufferStr(query,
14255 : "'c' AS collprovider, "
14256 : "NULL AS collversion, ");
14257 :
14258 4920 : if (fout->remoteVersion >= 120000)
14259 4920 : appendPQExpBufferStr(query,
14260 : "collisdeterministic, ");
14261 : else
14262 0 : appendPQExpBufferStr(query,
14263 : "true AS collisdeterministic, ");
14264 :
14265 4920 : if (fout->remoteVersion >= 170000)
14266 4920 : appendPQExpBufferStr(query,
14267 : "colllocale, ");
14268 0 : else if (fout->remoteVersion >= 150000)
14269 0 : appendPQExpBufferStr(query,
14270 : "colliculocale AS colllocale, ");
14271 : else
14272 0 : appendPQExpBufferStr(query,
14273 : "NULL AS colllocale, ");
14274 :
14275 4920 : if (fout->remoteVersion >= 160000)
14276 4920 : appendPQExpBufferStr(query,
14277 : "collicurules, ");
14278 : else
14279 0 : appendPQExpBufferStr(query,
14280 : "NULL AS collicurules, ");
14281 :
14282 4920 : appendPQExpBuffer(query,
14283 : "collcollate, "
14284 : "collctype "
14285 : "FROM pg_catalog.pg_collation c "
14286 : "WHERE c.oid = '%u'::pg_catalog.oid",
14287 : collinfo->dobj.catId.oid);
14288 :
14289 4920 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14290 :
14291 4920 : i_collprovider = PQfnumber(res, "collprovider");
14292 4920 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14293 4920 : i_collcollate = PQfnumber(res, "collcollate");
14294 4920 : i_collctype = PQfnumber(res, "collctype");
14295 4920 : i_colllocale = PQfnumber(res, "colllocale");
14296 4920 : i_collicurules = PQfnumber(res, "collicurules");
14297 :
14298 4920 : collprovider = PQgetvalue(res, 0, i_collprovider);
14299 :
14300 4920 : if (!PQgetisnull(res, 0, i_collcollate))
14301 96 : collcollate = PQgetvalue(res, 0, i_collcollate);
14302 : else
14303 4824 : collcollate = NULL;
14304 :
14305 4920 : if (!PQgetisnull(res, 0, i_collctype))
14306 96 : collctype = PQgetvalue(res, 0, i_collctype);
14307 : else
14308 4824 : collctype = NULL;
14309 :
14310 : /*
14311 : * Before version 15, collcollate and collctype were of type NAME and
14312 : * non-nullable. Treat empty strings as NULL for consistency.
14313 : */
14314 4920 : if (fout->remoteVersion < 150000)
14315 : {
14316 0 : if (collcollate[0] == '\0')
14317 0 : collcollate = NULL;
14318 0 : if (collctype[0] == '\0')
14319 0 : collctype = NULL;
14320 : }
14321 :
14322 4920 : if (!PQgetisnull(res, 0, i_colllocale))
14323 4818 : colllocale = PQgetvalue(res, 0, i_colllocale);
14324 : else
14325 102 : colllocale = NULL;
14326 :
14327 4920 : if (!PQgetisnull(res, 0, i_collicurules))
14328 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
14329 : else
14330 4920 : collicurules = NULL;
14331 :
14332 4920 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
14333 4920 : fmtQualifiedDumpable(collinfo));
14334 :
14335 4920 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
14336 4920 : fmtQualifiedDumpable(collinfo));
14337 :
14338 4920 : appendPQExpBufferStr(q, "provider = ");
14339 4920 : if (collprovider[0] == 'b')
14340 38 : appendPQExpBufferStr(q, "builtin");
14341 4882 : else if (collprovider[0] == 'c')
14342 96 : appendPQExpBufferStr(q, "libc");
14343 4786 : else if (collprovider[0] == 'i')
14344 4780 : appendPQExpBufferStr(q, "icu");
14345 6 : else if (collprovider[0] == 'd')
14346 : /* to allow dumping pg_catalog; not accepted on input */
14347 6 : appendPQExpBufferStr(q, "default");
14348 : else
14349 0 : pg_fatal("unrecognized collation provider: %s",
14350 : collprovider);
14351 :
14352 4920 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14353 0 : appendPQExpBufferStr(q, ", deterministic = false");
14354 :
14355 4920 : if (collprovider[0] == 'd')
14356 : {
14357 6 : if (collcollate || collctype || colllocale || collicurules)
14358 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14359 :
14360 : /* no locale -- the default collation cannot be reloaded anyway */
14361 : }
14362 4914 : else if (collprovider[0] == 'b')
14363 : {
14364 38 : if (collcollate || collctype || !colllocale || collicurules)
14365 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14366 :
14367 38 : appendPQExpBufferStr(q, ", locale = ");
14368 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14369 : fout);
14370 : }
14371 4876 : else if (collprovider[0] == 'i')
14372 : {
14373 4780 : if (fout->remoteVersion >= 150000)
14374 : {
14375 4780 : if (collcollate || collctype || !colllocale)
14376 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14377 :
14378 4780 : appendPQExpBufferStr(q, ", locale = ");
14379 4780 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14380 : fout);
14381 : }
14382 : else
14383 : {
14384 0 : if (!collcollate || !collctype || colllocale ||
14385 0 : strcmp(collcollate, collctype) != 0)
14386 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14387 :
14388 0 : appendPQExpBufferStr(q, ", locale = ");
14389 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14390 : }
14391 :
14392 4780 : if (collicurules)
14393 : {
14394 0 : appendPQExpBufferStr(q, ", rules = ");
14395 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14396 : }
14397 : }
14398 96 : else if (collprovider[0] == 'c')
14399 : {
14400 96 : if (colllocale || collicurules || !collcollate || !collctype)
14401 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14402 :
14403 96 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14404 : {
14405 96 : appendPQExpBufferStr(q, ", locale = ");
14406 96 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14407 : }
14408 : else
14409 : {
14410 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14411 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14412 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14413 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14414 : }
14415 : }
14416 : else
14417 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14418 :
14419 : /*
14420 : * For binary upgrade, carry over the collation version. For normal
14421 : * dump/restore, omit the version, so that it is computed upon restore.
14422 : */
14423 4920 : if (dopt->binary_upgrade)
14424 : {
14425 : int i_collversion;
14426 :
14427 10 : i_collversion = PQfnumber(res, "collversion");
14428 10 : if (!PQgetisnull(res, 0, i_collversion))
14429 : {
14430 8 : appendPQExpBufferStr(q, ", version = ");
14431 8 : appendStringLiteralAH(q,
14432 : PQgetvalue(res, 0, i_collversion),
14433 : fout);
14434 : }
14435 : }
14436 :
14437 4920 : appendPQExpBufferStr(q, ");\n");
14438 :
14439 4920 : if (dopt->binary_upgrade)
14440 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
14441 : "COLLATION", qcollname,
14442 10 : collinfo->dobj.namespace->dobj.name);
14443 :
14444 4920 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14445 4920 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14446 4920 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14447 : .namespace = collinfo->dobj.namespace->dobj.name,
14448 : .owner = collinfo->rolname,
14449 : .description = "COLLATION",
14450 : .section = SECTION_PRE_DATA,
14451 : .createStmt = q->data,
14452 : .dropStmt = delq->data));
14453 :
14454 : /* Dump Collation Comments */
14455 4920 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14456 4740 : dumpComment(fout, "COLLATION", qcollname,
14457 4740 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14458 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14459 :
14460 4920 : PQclear(res);
14461 :
14462 4920 : destroyPQExpBuffer(query);
14463 4920 : destroyPQExpBuffer(q);
14464 4920 : destroyPQExpBuffer(delq);
14465 4920 : free(qcollname);
14466 : }
14467 :
14468 : /*
14469 : * dumpConversion
14470 : * write out a single conversion definition
14471 : */
14472 : static void
14473 848 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14474 : {
14475 848 : DumpOptions *dopt = fout->dopt;
14476 : PQExpBuffer query;
14477 : PQExpBuffer q;
14478 : PQExpBuffer delq;
14479 : char *qconvname;
14480 : PGresult *res;
14481 : int i_conforencoding;
14482 : int i_contoencoding;
14483 : int i_conproc;
14484 : int i_condefault;
14485 : const char *conforencoding;
14486 : const char *contoencoding;
14487 : const char *conproc;
14488 : bool condefault;
14489 :
14490 : /* Do nothing if not dumping schema */
14491 848 : if (!dopt->dumpSchema)
14492 12 : return;
14493 :
14494 836 : query = createPQExpBuffer();
14495 836 : q = createPQExpBuffer();
14496 836 : delq = createPQExpBuffer();
14497 :
14498 836 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14499 :
14500 : /* Get conversion-specific details */
14501 836 : appendPQExpBuffer(query, "SELECT "
14502 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14503 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14504 : "conproc, condefault "
14505 : "FROM pg_catalog.pg_conversion c "
14506 : "WHERE c.oid = '%u'::pg_catalog.oid",
14507 : convinfo->dobj.catId.oid);
14508 :
14509 836 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14510 :
14511 836 : i_conforencoding = PQfnumber(res, "conforencoding");
14512 836 : i_contoencoding = PQfnumber(res, "contoencoding");
14513 836 : i_conproc = PQfnumber(res, "conproc");
14514 836 : i_condefault = PQfnumber(res, "condefault");
14515 :
14516 836 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14517 836 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14518 836 : conproc = PQgetvalue(res, 0, i_conproc);
14519 836 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14520 :
14521 836 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14522 836 : fmtQualifiedDumpable(convinfo));
14523 :
14524 836 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14525 : (condefault) ? "DEFAULT " : "",
14526 836 : fmtQualifiedDumpable(convinfo));
14527 836 : appendStringLiteralAH(q, conforencoding, fout);
14528 836 : appendPQExpBufferStr(q, " TO ");
14529 836 : appendStringLiteralAH(q, contoencoding, fout);
14530 : /* regproc output is already sufficiently quoted */
14531 836 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14532 :
14533 836 : if (dopt->binary_upgrade)
14534 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14535 : "CONVERSION", qconvname,
14536 2 : convinfo->dobj.namespace->dobj.name);
14537 :
14538 836 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14539 836 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14540 836 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14541 : .namespace = convinfo->dobj.namespace->dobj.name,
14542 : .owner = convinfo->rolname,
14543 : .description = "CONVERSION",
14544 : .section = SECTION_PRE_DATA,
14545 : .createStmt = q->data,
14546 : .dropStmt = delq->data));
14547 :
14548 : /* Dump Conversion Comments */
14549 836 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14550 836 : dumpComment(fout, "CONVERSION", qconvname,
14551 836 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14552 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14553 :
14554 836 : PQclear(res);
14555 :
14556 836 : destroyPQExpBuffer(query);
14557 836 : destroyPQExpBuffer(q);
14558 836 : destroyPQExpBuffer(delq);
14559 836 : free(qconvname);
14560 : }
14561 :
14562 : /*
14563 : * format_aggregate_signature: generate aggregate name and argument list
14564 : *
14565 : * The argument type names are qualified if needed. The aggregate name
14566 : * is never qualified.
14567 : */
14568 : static char *
14569 574 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14570 : {
14571 : PQExpBufferData buf;
14572 : int j;
14573 :
14574 574 : initPQExpBuffer(&buf);
14575 574 : if (honor_quotes)
14576 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14577 : else
14578 574 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14579 :
14580 574 : if (agginfo->aggfn.nargs == 0)
14581 80 : appendPQExpBufferStr(&buf, "(*)");
14582 : else
14583 : {
14584 494 : appendPQExpBufferChar(&buf, '(');
14585 1078 : for (j = 0; j < agginfo->aggfn.nargs; j++)
14586 584 : appendPQExpBuffer(&buf, "%s%s",
14587 : (j > 0) ? ", " : "",
14588 : getFormattedTypeName(fout,
14589 584 : agginfo->aggfn.argtypes[j],
14590 : zeroIsError));
14591 494 : appendPQExpBufferChar(&buf, ')');
14592 : }
14593 574 : return buf.data;
14594 : }
14595 :
14596 : /*
14597 : * dumpAgg
14598 : * write out a single aggregate definition
14599 : */
14600 : static void
14601 588 : dumpAgg(Archive *fout, const AggInfo *agginfo)
14602 : {
14603 588 : DumpOptions *dopt = fout->dopt;
14604 : PQExpBuffer query;
14605 : PQExpBuffer q;
14606 : PQExpBuffer delq;
14607 : PQExpBuffer details;
14608 : char *aggsig; /* identity signature */
14609 588 : char *aggfullsig = NULL; /* full signature */
14610 : char *aggsig_tag;
14611 : PGresult *res;
14612 : int i_agginitval;
14613 : int i_aggminitval;
14614 : const char *aggtransfn;
14615 : const char *aggfinalfn;
14616 : const char *aggcombinefn;
14617 : const char *aggserialfn;
14618 : const char *aggdeserialfn;
14619 : const char *aggmtransfn;
14620 : const char *aggminvtransfn;
14621 : const char *aggmfinalfn;
14622 : bool aggfinalextra;
14623 : bool aggmfinalextra;
14624 : char aggfinalmodify;
14625 : char aggmfinalmodify;
14626 : const char *aggsortop;
14627 : char *aggsortconvop;
14628 : char aggkind;
14629 : const char *aggtranstype;
14630 : const char *aggtransspace;
14631 : const char *aggmtranstype;
14632 : const char *aggmtransspace;
14633 : const char *agginitval;
14634 : const char *aggminitval;
14635 : const char *proparallel;
14636 : char defaultfinalmodify;
14637 :
14638 : /* Do nothing if not dumping schema */
14639 588 : if (!dopt->dumpSchema)
14640 14 : return;
14641 :
14642 574 : query = createPQExpBuffer();
14643 574 : q = createPQExpBuffer();
14644 574 : delq = createPQExpBuffer();
14645 574 : details = createPQExpBuffer();
14646 :
14647 574 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14648 : {
14649 : /* Set up query for aggregate-specific details */
14650 114 : appendPQExpBufferStr(query,
14651 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14652 :
14653 114 : appendPQExpBufferStr(query,
14654 : "SELECT "
14655 : "aggtransfn,\n"
14656 : "aggfinalfn,\n"
14657 : "aggtranstype::pg_catalog.regtype,\n"
14658 : "agginitval,\n"
14659 : "aggsortop,\n"
14660 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14661 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14662 :
14663 114 : if (fout->remoteVersion >= 90400)
14664 114 : appendPQExpBufferStr(query,
14665 : "aggkind,\n"
14666 : "aggmtransfn,\n"
14667 : "aggminvtransfn,\n"
14668 : "aggmfinalfn,\n"
14669 : "aggmtranstype::pg_catalog.regtype,\n"
14670 : "aggfinalextra,\n"
14671 : "aggmfinalextra,\n"
14672 : "aggtransspace,\n"
14673 : "aggmtransspace,\n"
14674 : "aggminitval,\n");
14675 : else
14676 0 : appendPQExpBufferStr(query,
14677 : "'n' AS aggkind,\n"
14678 : "'-' AS aggmtransfn,\n"
14679 : "'-' AS aggminvtransfn,\n"
14680 : "'-' AS aggmfinalfn,\n"
14681 : "0 AS aggmtranstype,\n"
14682 : "false AS aggfinalextra,\n"
14683 : "false AS aggmfinalextra,\n"
14684 : "0 AS aggtransspace,\n"
14685 : "0 AS aggmtransspace,\n"
14686 : "NULL AS aggminitval,\n");
14687 :
14688 114 : if (fout->remoteVersion >= 90600)
14689 114 : appendPQExpBufferStr(query,
14690 : "aggcombinefn,\n"
14691 : "aggserialfn,\n"
14692 : "aggdeserialfn,\n"
14693 : "proparallel,\n");
14694 : else
14695 0 : appendPQExpBufferStr(query,
14696 : "'-' AS aggcombinefn,\n"
14697 : "'-' AS aggserialfn,\n"
14698 : "'-' AS aggdeserialfn,\n"
14699 : "'u' AS proparallel,\n");
14700 :
14701 114 : if (fout->remoteVersion >= 110000)
14702 114 : appendPQExpBufferStr(query,
14703 : "aggfinalmodify,\n"
14704 : "aggmfinalmodify\n");
14705 : else
14706 0 : appendPQExpBufferStr(query,
14707 : "'0' AS aggfinalmodify,\n"
14708 : "'0' AS aggmfinalmodify\n");
14709 :
14710 114 : appendPQExpBufferStr(query,
14711 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14712 : "WHERE a.aggfnoid = p.oid "
14713 : "AND p.oid = $1");
14714 :
14715 114 : ExecuteSqlStatement(fout, query->data);
14716 :
14717 114 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14718 : }
14719 :
14720 574 : printfPQExpBuffer(query,
14721 : "EXECUTE dumpAgg('%u')",
14722 : agginfo->aggfn.dobj.catId.oid);
14723 :
14724 574 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14725 :
14726 574 : i_agginitval = PQfnumber(res, "agginitval");
14727 574 : i_aggminitval = PQfnumber(res, "aggminitval");
14728 :
14729 574 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14730 574 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14731 574 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14732 574 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14733 574 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14734 574 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14735 574 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14736 574 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14737 574 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14738 574 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14739 574 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14740 574 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14741 574 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14742 574 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14743 574 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14744 574 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14745 574 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14746 574 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14747 574 : agginitval = PQgetvalue(res, 0, i_agginitval);
14748 574 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
14749 574 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14750 :
14751 : {
14752 : char *funcargs;
14753 : char *funciargs;
14754 :
14755 574 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14756 574 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14757 574 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14758 574 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14759 : }
14760 :
14761 574 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14762 :
14763 : /* identify default modify flag for aggkind (must match DefineAggregate) */
14764 574 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14765 : /* replace omitted flags for old versions */
14766 574 : if (aggfinalmodify == '0')
14767 0 : aggfinalmodify = defaultfinalmodify;
14768 574 : if (aggmfinalmodify == '0')
14769 0 : aggmfinalmodify = defaultfinalmodify;
14770 :
14771 : /* regproc and regtype output is already sufficiently quoted */
14772 574 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14773 : aggtransfn, aggtranstype);
14774 :
14775 574 : if (strcmp(aggtransspace, "0") != 0)
14776 : {
14777 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
14778 : aggtransspace);
14779 : }
14780 :
14781 574 : if (!PQgetisnull(res, 0, i_agginitval))
14782 : {
14783 418 : appendPQExpBufferStr(details, ",\n INITCOND = ");
14784 418 : appendStringLiteralAH(details, agginitval, fout);
14785 : }
14786 :
14787 574 : if (strcmp(aggfinalfn, "-") != 0)
14788 : {
14789 268 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14790 : aggfinalfn);
14791 268 : if (aggfinalextra)
14792 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14793 268 : if (aggfinalmodify != defaultfinalmodify)
14794 : {
14795 68 : switch (aggfinalmodify)
14796 : {
14797 0 : case AGGMODIFY_READ_ONLY:
14798 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14799 0 : break;
14800 68 : case AGGMODIFY_SHAREABLE:
14801 68 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14802 68 : break;
14803 0 : case AGGMODIFY_READ_WRITE:
14804 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14805 0 : break;
14806 0 : default:
14807 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14808 : agginfo->aggfn.dobj.name);
14809 : break;
14810 : }
14811 506 : }
14812 : }
14813 :
14814 574 : if (strcmp(aggcombinefn, "-") != 0)
14815 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14816 :
14817 574 : if (strcmp(aggserialfn, "-") != 0)
14818 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14819 :
14820 574 : if (strcmp(aggdeserialfn, "-") != 0)
14821 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14822 :
14823 574 : if (strcmp(aggmtransfn, "-") != 0)
14824 : {
14825 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14826 : aggmtransfn,
14827 : aggminvtransfn,
14828 : aggmtranstype);
14829 : }
14830 :
14831 574 : if (strcmp(aggmtransspace, "0") != 0)
14832 : {
14833 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
14834 : aggmtransspace);
14835 : }
14836 :
14837 574 : if (!PQgetisnull(res, 0, i_aggminitval))
14838 : {
14839 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
14840 20 : appendStringLiteralAH(details, aggminitval, fout);
14841 : }
14842 :
14843 574 : if (strcmp(aggmfinalfn, "-") != 0)
14844 : {
14845 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14846 : aggmfinalfn);
14847 0 : if (aggmfinalextra)
14848 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14849 0 : if (aggmfinalmodify != defaultfinalmodify)
14850 : {
14851 0 : switch (aggmfinalmodify)
14852 : {
14853 0 : case AGGMODIFY_READ_ONLY:
14854 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14855 0 : break;
14856 0 : case AGGMODIFY_SHAREABLE:
14857 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14858 0 : break;
14859 0 : case AGGMODIFY_READ_WRITE:
14860 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14861 0 : break;
14862 0 : default:
14863 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14864 : agginfo->aggfn.dobj.name);
14865 : break;
14866 : }
14867 574 : }
14868 : }
14869 :
14870 574 : aggsortconvop = getFormattedOperatorName(aggsortop);
14871 574 : if (aggsortconvop)
14872 : {
14873 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
14874 : aggsortconvop);
14875 0 : free(aggsortconvop);
14876 : }
14877 :
14878 574 : if (aggkind == AGGKIND_HYPOTHETICAL)
14879 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14880 :
14881 574 : if (proparallel[0] != PROPARALLEL_UNSAFE)
14882 : {
14883 10 : if (proparallel[0] == PROPARALLEL_SAFE)
14884 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14885 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14886 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14887 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
14888 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
14889 : agginfo->aggfn.dobj.name);
14890 : }
14891 :
14892 574 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14893 574 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14894 : aggsig);
14895 :
14896 1148 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14897 574 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14898 : aggfullsig ? aggfullsig : aggsig, details->data);
14899 :
14900 574 : if (dopt->binary_upgrade)
14901 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14902 : "AGGREGATE", aggsig,
14903 98 : agginfo->aggfn.dobj.namespace->dobj.name);
14904 :
14905 574 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14906 540 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14907 : agginfo->aggfn.dobj.dumpId,
14908 540 : ARCHIVE_OPTS(.tag = aggsig_tag,
14909 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14910 : .owner = agginfo->aggfn.rolname,
14911 : .description = "AGGREGATE",
14912 : .section = SECTION_PRE_DATA,
14913 : .createStmt = q->data,
14914 : .dropStmt = delq->data));
14915 :
14916 : /* Dump Aggregate Comments */
14917 574 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14918 20 : dumpComment(fout, "AGGREGATE", aggsig,
14919 20 : agginfo->aggfn.dobj.namespace->dobj.name,
14920 : agginfo->aggfn.rolname,
14921 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14922 :
14923 574 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14924 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
14925 0 : agginfo->aggfn.dobj.namespace->dobj.name,
14926 : agginfo->aggfn.rolname,
14927 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14928 :
14929 : /*
14930 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14931 : * command look like a function's GRANT; in particular this affects the
14932 : * syntax for zero-argument aggregates and ordered-set aggregates.
14933 : */
14934 574 : free(aggsig);
14935 :
14936 574 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14937 :
14938 574 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14939 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14940 : "FUNCTION", aggsig, NULL,
14941 36 : agginfo->aggfn.dobj.namespace->dobj.name,
14942 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14943 :
14944 574 : free(aggsig);
14945 574 : free(aggfullsig);
14946 574 : free(aggsig_tag);
14947 :
14948 574 : PQclear(res);
14949 :
14950 574 : destroyPQExpBuffer(query);
14951 574 : destroyPQExpBuffer(q);
14952 574 : destroyPQExpBuffer(delq);
14953 574 : destroyPQExpBuffer(details);
14954 : }
14955 :
14956 : /*
14957 : * dumpTSParser
14958 : * write out a single text search parser
14959 : */
14960 : static void
14961 86 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14962 : {
14963 86 : DumpOptions *dopt = fout->dopt;
14964 : PQExpBuffer q;
14965 : PQExpBuffer delq;
14966 : char *qprsname;
14967 :
14968 : /* Do nothing if not dumping schema */
14969 86 : if (!dopt->dumpSchema)
14970 12 : return;
14971 :
14972 74 : q = createPQExpBuffer();
14973 74 : delq = createPQExpBuffer();
14974 :
14975 74 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14976 :
14977 74 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14978 74 : fmtQualifiedDumpable(prsinfo));
14979 :
14980 74 : appendPQExpBuffer(q, " START = %s,\n",
14981 : convertTSFunction(fout, prsinfo->prsstart));
14982 74 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14983 : convertTSFunction(fout, prsinfo->prstoken));
14984 74 : appendPQExpBuffer(q, " END = %s,\n",
14985 : convertTSFunction(fout, prsinfo->prsend));
14986 74 : if (prsinfo->prsheadline != InvalidOid)
14987 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
14988 : convertTSFunction(fout, prsinfo->prsheadline));
14989 74 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14990 : convertTSFunction(fout, prsinfo->prslextype));
14991 :
14992 74 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14993 74 : fmtQualifiedDumpable(prsinfo));
14994 :
14995 74 : if (dopt->binary_upgrade)
14996 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
14997 : "TEXT SEARCH PARSER", qprsname,
14998 2 : prsinfo->dobj.namespace->dobj.name);
14999 :
15000 74 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15001 74 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15002 74 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15003 : .namespace = prsinfo->dobj.namespace->dobj.name,
15004 : .description = "TEXT SEARCH PARSER",
15005 : .section = SECTION_PRE_DATA,
15006 : .createStmt = q->data,
15007 : .dropStmt = delq->data));
15008 :
15009 : /* Dump Parser Comments */
15010 74 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15011 74 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15012 74 : prsinfo->dobj.namespace->dobj.name, "",
15013 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15014 :
15015 74 : destroyPQExpBuffer(q);
15016 74 : destroyPQExpBuffer(delq);
15017 74 : free(qprsname);
15018 : }
15019 :
15020 : /*
15021 : * dumpTSDictionary
15022 : * write out a single text search dictionary
15023 : */
15024 : static void
15025 350 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15026 : {
15027 350 : DumpOptions *dopt = fout->dopt;
15028 : PQExpBuffer q;
15029 : PQExpBuffer delq;
15030 : PQExpBuffer query;
15031 : char *qdictname;
15032 : PGresult *res;
15033 : char *nspname;
15034 : char *tmplname;
15035 :
15036 : /* Do nothing if not dumping schema */
15037 350 : if (!dopt->dumpSchema)
15038 12 : return;
15039 :
15040 338 : q = createPQExpBuffer();
15041 338 : delq = createPQExpBuffer();
15042 338 : query = createPQExpBuffer();
15043 :
15044 338 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15045 :
15046 : /* Fetch name and namespace of the dictionary's template */
15047 338 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15048 : "FROM pg_ts_template p, pg_namespace n "
15049 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15050 : dictinfo->dicttemplate);
15051 338 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15052 338 : nspname = PQgetvalue(res, 0, 0);
15053 338 : tmplname = PQgetvalue(res, 0, 1);
15054 :
15055 338 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15056 338 : fmtQualifiedDumpable(dictinfo));
15057 :
15058 338 : appendPQExpBufferStr(q, " TEMPLATE = ");
15059 338 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15060 338 : appendPQExpBufferStr(q, fmtId(tmplname));
15061 :
15062 338 : PQclear(res);
15063 :
15064 : /* the dictinitoption can be dumped straight into the command */
15065 338 : if (dictinfo->dictinitoption)
15066 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15067 :
15068 338 : appendPQExpBufferStr(q, " );\n");
15069 :
15070 338 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15071 338 : fmtQualifiedDumpable(dictinfo));
15072 :
15073 338 : if (dopt->binary_upgrade)
15074 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15075 : "TEXT SEARCH DICTIONARY", qdictname,
15076 20 : dictinfo->dobj.namespace->dobj.name);
15077 :
15078 338 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15079 338 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15080 338 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15081 : .namespace = dictinfo->dobj.namespace->dobj.name,
15082 : .owner = dictinfo->rolname,
15083 : .description = "TEXT SEARCH DICTIONARY",
15084 : .section = SECTION_PRE_DATA,
15085 : .createStmt = q->data,
15086 : .dropStmt = delq->data));
15087 :
15088 : /* Dump Dictionary Comments */
15089 338 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15090 248 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15091 248 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15092 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15093 :
15094 338 : destroyPQExpBuffer(q);
15095 338 : destroyPQExpBuffer(delq);
15096 338 : destroyPQExpBuffer(query);
15097 338 : free(qdictname);
15098 : }
15099 :
15100 : /*
15101 : * dumpTSTemplate
15102 : * write out a single text search template
15103 : */
15104 : static void
15105 110 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15106 : {
15107 110 : DumpOptions *dopt = fout->dopt;
15108 : PQExpBuffer q;
15109 : PQExpBuffer delq;
15110 : char *qtmplname;
15111 :
15112 : /* Do nothing if not dumping schema */
15113 110 : if (!dopt->dumpSchema)
15114 12 : return;
15115 :
15116 98 : q = createPQExpBuffer();
15117 98 : delq = createPQExpBuffer();
15118 :
15119 98 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15120 :
15121 98 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15122 98 : fmtQualifiedDumpable(tmplinfo));
15123 :
15124 98 : if (tmplinfo->tmplinit != InvalidOid)
15125 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15126 : convertTSFunction(fout, tmplinfo->tmplinit));
15127 98 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15128 : convertTSFunction(fout, tmplinfo->tmpllexize));
15129 :
15130 98 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15131 98 : fmtQualifiedDumpable(tmplinfo));
15132 :
15133 98 : if (dopt->binary_upgrade)
15134 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15135 : "TEXT SEARCH TEMPLATE", qtmplname,
15136 2 : tmplinfo->dobj.namespace->dobj.name);
15137 :
15138 98 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15139 98 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15140 98 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15141 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15142 : .description = "TEXT SEARCH TEMPLATE",
15143 : .section = SECTION_PRE_DATA,
15144 : .createStmt = q->data,
15145 : .dropStmt = delq->data));
15146 :
15147 : /* Dump Template Comments */
15148 98 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15149 98 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15150 98 : tmplinfo->dobj.namespace->dobj.name, "",
15151 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15152 :
15153 98 : destroyPQExpBuffer(q);
15154 98 : destroyPQExpBuffer(delq);
15155 98 : free(qtmplname);
15156 : }
15157 :
15158 : /*
15159 : * dumpTSConfig
15160 : * write out a single text search configuration
15161 : */
15162 : static void
15163 300 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15164 : {
15165 300 : DumpOptions *dopt = fout->dopt;
15166 : PQExpBuffer q;
15167 : PQExpBuffer delq;
15168 : PQExpBuffer query;
15169 : char *qcfgname;
15170 : PGresult *res;
15171 : char *nspname;
15172 : char *prsname;
15173 : int ntups,
15174 : i;
15175 : int i_tokenname;
15176 : int i_dictname;
15177 :
15178 : /* Do nothing if not dumping schema */
15179 300 : if (!dopt->dumpSchema)
15180 12 : return;
15181 :
15182 288 : q = createPQExpBuffer();
15183 288 : delq = createPQExpBuffer();
15184 288 : query = createPQExpBuffer();
15185 :
15186 288 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15187 :
15188 : /* Fetch name and namespace of the config's parser */
15189 288 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15190 : "FROM pg_ts_parser p, pg_namespace n "
15191 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15192 : cfginfo->cfgparser);
15193 288 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15194 288 : nspname = PQgetvalue(res, 0, 0);
15195 288 : prsname = PQgetvalue(res, 0, 1);
15196 :
15197 288 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15198 288 : fmtQualifiedDumpable(cfginfo));
15199 :
15200 288 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15201 288 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15202 :
15203 288 : PQclear(res);
15204 :
15205 288 : resetPQExpBuffer(query);
15206 288 : appendPQExpBuffer(query,
15207 : "SELECT\n"
15208 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15209 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15210 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15211 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15212 : "WHERE m.mapcfg = '%u'\n"
15213 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15214 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15215 :
15216 288 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15217 288 : ntups = PQntuples(res);
15218 :
15219 288 : i_tokenname = PQfnumber(res, "tokenname");
15220 288 : i_dictname = PQfnumber(res, "dictname");
15221 :
15222 6030 : for (i = 0; i < ntups; i++)
15223 : {
15224 5742 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15225 5742 : char *dictname = PQgetvalue(res, i, i_dictname);
15226 :
15227 5742 : if (i == 0 ||
15228 5454 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15229 : {
15230 : /* starting a new token type, so start a new command */
15231 5472 : if (i > 0)
15232 5184 : appendPQExpBufferStr(q, ";\n");
15233 5472 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15234 5472 : fmtQualifiedDumpable(cfginfo));
15235 : /* tokenname needs quoting, dictname does NOT */
15236 5472 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15237 : fmtId(tokenname), dictname);
15238 : }
15239 : else
15240 270 : appendPQExpBuffer(q, ", %s", dictname);
15241 : }
15242 :
15243 288 : if (ntups > 0)
15244 288 : appendPQExpBufferStr(q, ";\n");
15245 :
15246 288 : PQclear(res);
15247 :
15248 288 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15249 288 : fmtQualifiedDumpable(cfginfo));
15250 :
15251 288 : if (dopt->binary_upgrade)
15252 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15253 : "TEXT SEARCH CONFIGURATION", qcfgname,
15254 10 : cfginfo->dobj.namespace->dobj.name);
15255 :
15256 288 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15257 288 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15258 288 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15259 : .namespace = cfginfo->dobj.namespace->dobj.name,
15260 : .owner = cfginfo->rolname,
15261 : .description = "TEXT SEARCH CONFIGURATION",
15262 : .section = SECTION_PRE_DATA,
15263 : .createStmt = q->data,
15264 : .dropStmt = delq->data));
15265 :
15266 : /* Dump Configuration Comments */
15267 288 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15268 248 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15269 248 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15270 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15271 :
15272 288 : destroyPQExpBuffer(q);
15273 288 : destroyPQExpBuffer(delq);
15274 288 : destroyPQExpBuffer(query);
15275 288 : free(qcfgname);
15276 : }
15277 :
15278 : /*
15279 : * dumpForeignDataWrapper
15280 : * write out a single foreign-data wrapper definition
15281 : */
15282 : static void
15283 108 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15284 : {
15285 108 : DumpOptions *dopt = fout->dopt;
15286 : PQExpBuffer q;
15287 : PQExpBuffer delq;
15288 : char *qfdwname;
15289 :
15290 : /* Do nothing if not dumping schema */
15291 108 : if (!dopt->dumpSchema)
15292 14 : return;
15293 :
15294 94 : q = createPQExpBuffer();
15295 94 : delq = createPQExpBuffer();
15296 :
15297 94 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15298 :
15299 94 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15300 : qfdwname);
15301 :
15302 94 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15303 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15304 :
15305 94 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15306 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15307 :
15308 94 : if (strlen(fdwinfo->fdwoptions) > 0)
15309 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15310 :
15311 94 : appendPQExpBufferStr(q, ";\n");
15312 :
15313 94 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15314 : qfdwname);
15315 :
15316 94 : if (dopt->binary_upgrade)
15317 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15318 : "FOREIGN DATA WRAPPER", qfdwname,
15319 : NULL);
15320 :
15321 94 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15322 94 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15323 94 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15324 : .owner = fdwinfo->rolname,
15325 : .description = "FOREIGN DATA WRAPPER",
15326 : .section = SECTION_PRE_DATA,
15327 : .createStmt = q->data,
15328 : .dropStmt = delq->data));
15329 :
15330 : /* Dump Foreign Data Wrapper Comments */
15331 94 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15332 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
15333 : NULL, fdwinfo->rolname,
15334 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
15335 :
15336 : /* Handle the ACL */
15337 94 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
15338 66 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15339 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15340 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15341 :
15342 94 : free(qfdwname);
15343 :
15344 94 : destroyPQExpBuffer(q);
15345 94 : destroyPQExpBuffer(delq);
15346 : }
15347 :
15348 : /*
15349 : * dumpForeignServer
15350 : * write out a foreign server definition
15351 : */
15352 : static void
15353 116 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15354 : {
15355 116 : DumpOptions *dopt = fout->dopt;
15356 : PQExpBuffer q;
15357 : PQExpBuffer delq;
15358 : PQExpBuffer query;
15359 : PGresult *res;
15360 : char *qsrvname;
15361 : char *fdwname;
15362 :
15363 : /* Do nothing if not dumping schema */
15364 116 : if (!dopt->dumpSchema)
15365 18 : return;
15366 :
15367 98 : q = createPQExpBuffer();
15368 98 : delq = createPQExpBuffer();
15369 98 : query = createPQExpBuffer();
15370 :
15371 98 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15372 :
15373 : /* look up the foreign-data wrapper */
15374 98 : appendPQExpBuffer(query, "SELECT fdwname "
15375 : "FROM pg_foreign_data_wrapper w "
15376 : "WHERE w.oid = '%u'",
15377 : srvinfo->srvfdw);
15378 98 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15379 98 : fdwname = PQgetvalue(res, 0, 0);
15380 :
15381 98 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15382 98 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15383 : {
15384 0 : appendPQExpBufferStr(q, " TYPE ");
15385 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15386 : }
15387 98 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15388 : {
15389 0 : appendPQExpBufferStr(q, " VERSION ");
15390 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15391 : }
15392 :
15393 98 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15394 98 : appendPQExpBufferStr(q, fmtId(fdwname));
15395 :
15396 98 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15397 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15398 :
15399 98 : appendPQExpBufferStr(q, ";\n");
15400 :
15401 98 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15402 : qsrvname);
15403 :
15404 98 : if (dopt->binary_upgrade)
15405 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15406 : "SERVER", qsrvname, NULL);
15407 :
15408 98 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15409 98 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15410 98 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15411 : .owner = srvinfo->rolname,
15412 : .description = "SERVER",
15413 : .section = SECTION_PRE_DATA,
15414 : .createStmt = q->data,
15415 : .dropStmt = delq->data));
15416 :
15417 : /* Dump Foreign Server Comments */
15418 98 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15419 0 : dumpComment(fout, "SERVER", qsrvname,
15420 : NULL, srvinfo->rolname,
15421 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15422 :
15423 : /* Handle the ACL */
15424 98 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15425 66 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15426 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15427 : NULL, srvinfo->rolname, &srvinfo->dacl);
15428 :
15429 : /* Dump user mappings */
15430 98 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15431 98 : dumpUserMappings(fout,
15432 98 : srvinfo->dobj.name, NULL,
15433 : srvinfo->rolname,
15434 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15435 :
15436 98 : PQclear(res);
15437 :
15438 98 : free(qsrvname);
15439 :
15440 98 : destroyPQExpBuffer(q);
15441 98 : destroyPQExpBuffer(delq);
15442 98 : destroyPQExpBuffer(query);
15443 : }
15444 :
15445 : /*
15446 : * dumpUserMappings
15447 : *
15448 : * This routine is used to dump any user mappings associated with the
15449 : * server handed to this routine. Should be called after ArchiveEntry()
15450 : * for the server.
15451 : */
15452 : static void
15453 98 : dumpUserMappings(Archive *fout,
15454 : const char *servername, const char *namespace,
15455 : const char *owner,
15456 : CatalogId catalogId, DumpId dumpId)
15457 : {
15458 : PQExpBuffer q;
15459 : PQExpBuffer delq;
15460 : PQExpBuffer query;
15461 : PQExpBuffer tag;
15462 : PGresult *res;
15463 : int ntups;
15464 : int i_usename;
15465 : int i_umoptions;
15466 : int i;
15467 :
15468 98 : q = createPQExpBuffer();
15469 98 : tag = createPQExpBuffer();
15470 98 : delq = createPQExpBuffer();
15471 98 : query = createPQExpBuffer();
15472 :
15473 : /*
15474 : * We read from the publicly accessible view pg_user_mappings, so as not
15475 : * to fail if run by a non-superuser. Note that the view will show
15476 : * umoptions as null if the user hasn't got privileges for the associated
15477 : * server; this means that pg_dump will dump such a mapping, but with no
15478 : * OPTIONS clause. A possible alternative is to skip such mappings
15479 : * altogether, but it's not clear that that's an improvement.
15480 : */
15481 98 : appendPQExpBuffer(query,
15482 : "SELECT usename, "
15483 : "array_to_string(ARRAY("
15484 : "SELECT quote_ident(option_name) || ' ' || "
15485 : "quote_literal(option_value) "
15486 : "FROM pg_options_to_table(umoptions) "
15487 : "ORDER BY option_name"
15488 : "), E',\n ') AS umoptions "
15489 : "FROM pg_user_mappings "
15490 : "WHERE srvid = '%u' "
15491 : "ORDER BY usename",
15492 : catalogId.oid);
15493 :
15494 98 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15495 :
15496 98 : ntups = PQntuples(res);
15497 98 : i_usename = PQfnumber(res, "usename");
15498 98 : i_umoptions = PQfnumber(res, "umoptions");
15499 :
15500 164 : for (i = 0; i < ntups; i++)
15501 : {
15502 : char *usename;
15503 : char *umoptions;
15504 :
15505 66 : usename = PQgetvalue(res, i, i_usename);
15506 66 : umoptions = PQgetvalue(res, i, i_umoptions);
15507 :
15508 66 : resetPQExpBuffer(q);
15509 66 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15510 66 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15511 :
15512 66 : if (umoptions && strlen(umoptions) > 0)
15513 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15514 :
15515 66 : appendPQExpBufferStr(q, ";\n");
15516 :
15517 66 : resetPQExpBuffer(delq);
15518 66 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15519 66 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15520 :
15521 66 : resetPQExpBuffer(tag);
15522 66 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15523 : usename, servername);
15524 :
15525 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15526 66 : ARCHIVE_OPTS(.tag = tag->data,
15527 : .namespace = namespace,
15528 : .owner = owner,
15529 : .description = "USER MAPPING",
15530 : .section = SECTION_PRE_DATA,
15531 : .createStmt = q->data,
15532 : .dropStmt = delq->data));
15533 : }
15534 :
15535 98 : PQclear(res);
15536 :
15537 98 : destroyPQExpBuffer(query);
15538 98 : destroyPQExpBuffer(delq);
15539 98 : destroyPQExpBuffer(tag);
15540 98 : destroyPQExpBuffer(q);
15541 98 : }
15542 :
15543 : /*
15544 : * Write out default privileges information
15545 : */
15546 : static void
15547 316 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15548 : {
15549 316 : DumpOptions *dopt = fout->dopt;
15550 : PQExpBuffer q;
15551 : PQExpBuffer tag;
15552 : const char *type;
15553 :
15554 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
15555 316 : if (!dopt->dumpSchema || dopt->aclsSkip)
15556 56 : return;
15557 :
15558 260 : q = createPQExpBuffer();
15559 260 : tag = createPQExpBuffer();
15560 :
15561 260 : switch (daclinfo->defaclobjtype)
15562 : {
15563 130 : case DEFACLOBJ_RELATION:
15564 130 : type = "TABLES";
15565 130 : break;
15566 0 : case DEFACLOBJ_SEQUENCE:
15567 0 : type = "SEQUENCES";
15568 0 : break;
15569 130 : case DEFACLOBJ_FUNCTION:
15570 130 : type = "FUNCTIONS";
15571 130 : break;
15572 0 : case DEFACLOBJ_TYPE:
15573 0 : type = "TYPES";
15574 0 : break;
15575 0 : case DEFACLOBJ_NAMESPACE:
15576 0 : type = "SCHEMAS";
15577 0 : break;
15578 0 : default:
15579 : /* shouldn't get here */
15580 0 : pg_fatal("unrecognized object type in default privileges: %d",
15581 : (int) daclinfo->defaclobjtype);
15582 : type = ""; /* keep compiler quiet */
15583 : }
15584 :
15585 260 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15586 :
15587 : /* build the actual command(s) for this tuple */
15588 260 : if (!buildDefaultACLCommands(type,
15589 260 : daclinfo->dobj.namespace != NULL ?
15590 132 : daclinfo->dobj.namespace->dobj.name : NULL,
15591 260 : daclinfo->dacl.acl,
15592 260 : daclinfo->dacl.acldefault,
15593 : daclinfo->defaclrole,
15594 : fout->remoteVersion,
15595 : q))
15596 0 : pg_fatal("could not parse default ACL list (%s)",
15597 : daclinfo->dacl.acl);
15598 :
15599 260 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15600 260 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15601 260 : ARCHIVE_OPTS(.tag = tag->data,
15602 : .namespace = daclinfo->dobj.namespace ?
15603 : daclinfo->dobj.namespace->dobj.name : NULL,
15604 : .owner = daclinfo->defaclrole,
15605 : .description = "DEFAULT ACL",
15606 : .section = SECTION_POST_DATA,
15607 : .createStmt = q->data));
15608 :
15609 260 : destroyPQExpBuffer(tag);
15610 260 : destroyPQExpBuffer(q);
15611 : }
15612 :
15613 : /*----------
15614 : * Write out grant/revoke information
15615 : *
15616 : * 'objDumpId' is the dump ID of the underlying object.
15617 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15618 : * or InvalidDumpId if there is no need for a second dependency.
15619 : * 'type' must be one of
15620 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15621 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15622 : * 'name' is the formatted name of the object. Must be quoted etc. already.
15623 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15624 : * (Currently we assume that subname is only provided for table columns.)
15625 : * 'nspname' is the namespace the object is in (NULL if none).
15626 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
15627 : * to use the default for the object type.
15628 : * 'owner' is the owner, NULL if there is no owner (for languages).
15629 : * 'dacl' is the DumpableAcl struct for the object.
15630 : *
15631 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15632 : * no ACL entry was created.
15633 : *----------
15634 : */
15635 : static DumpId
15636 46918 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15637 : const char *type, const char *name, const char *subname,
15638 : const char *nspname, const char *tag, const char *owner,
15639 : const DumpableAcl *dacl)
15640 : {
15641 46918 : DumpId aclDumpId = InvalidDumpId;
15642 46918 : DumpOptions *dopt = fout->dopt;
15643 46918 : const char *acls = dacl->acl;
15644 46918 : const char *acldefault = dacl->acldefault;
15645 46918 : char privtype = dacl->privtype;
15646 46918 : const char *initprivs = dacl->initprivs;
15647 : const char *baseacls;
15648 : PQExpBuffer sql;
15649 :
15650 : /* Do nothing if ACL dump is not enabled */
15651 46918 : if (dopt->aclsSkip)
15652 636 : return InvalidDumpId;
15653 :
15654 : /* --data-only skips ACLs *except* large object ACLs */
15655 46282 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
15656 0 : return InvalidDumpId;
15657 :
15658 46282 : sql = createPQExpBuffer();
15659 :
15660 : /*
15661 : * In binary upgrade mode, we don't run an extension's script but instead
15662 : * dump out the objects independently and then recreate them. To preserve
15663 : * any initial privileges which were set on extension objects, we need to
15664 : * compute the set of GRANT and REVOKE commands necessary to get from the
15665 : * default privileges of an object to its initial privileges as recorded
15666 : * in pg_init_privs.
15667 : *
15668 : * At restore time, we apply these commands after having called
15669 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
15670 : * copy the results into pg_init_privs. This is how we preserve the
15671 : * contents of that catalog across binary upgrades.
15672 : */
15673 46282 : if (dopt->binary_upgrade && privtype == 'e' &&
15674 26 : initprivs && *initprivs != '\0')
15675 : {
15676 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15677 26 : if (!buildACLCommands(name, subname, nspname, type,
15678 : initprivs, acldefault, owner,
15679 : "", fout->remoteVersion, sql))
15680 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15681 : initprivs, acldefault, name, type);
15682 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15683 : }
15684 :
15685 : /*
15686 : * Now figure the GRANT and REVOKE commands needed to get to the object's
15687 : * actual current ACL, starting from the initprivs if given, else from the
15688 : * object-type-specific default. Also, while buildACLCommands will assume
15689 : * that a NULL/empty acls string means it needn't do anything, what that
15690 : * actually represents is the object-type-specific default; so we need to
15691 : * substitute the acldefault string to get the right results in that case.
15692 : */
15693 46282 : if (initprivs && *initprivs != '\0')
15694 : {
15695 42678 : baseacls = initprivs;
15696 42678 : if (acls == NULL || *acls == '\0')
15697 34 : acls = acldefault;
15698 : }
15699 : else
15700 3604 : baseacls = acldefault;
15701 :
15702 46282 : if (!buildACLCommands(name, subname, nspname, type,
15703 : acls, baseacls, owner,
15704 : "", fout->remoteVersion, sql))
15705 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15706 : acls, baseacls, name, type);
15707 :
15708 46282 : if (sql->len > 0)
15709 : {
15710 3764 : PQExpBuffer tagbuf = createPQExpBuffer();
15711 : DumpId aclDeps[2];
15712 3764 : int nDeps = 0;
15713 :
15714 3764 : if (tag)
15715 0 : appendPQExpBufferStr(tagbuf, tag);
15716 3764 : else if (subname)
15717 2222 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
15718 : else
15719 1542 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
15720 :
15721 3764 : aclDeps[nDeps++] = objDumpId;
15722 3764 : if (altDumpId != InvalidDumpId)
15723 2050 : aclDeps[nDeps++] = altDumpId;
15724 :
15725 3764 : aclDumpId = createDumpId();
15726 :
15727 3764 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
15728 3764 : ARCHIVE_OPTS(.tag = tagbuf->data,
15729 : .namespace = nspname,
15730 : .owner = owner,
15731 : .description = "ACL",
15732 : .section = SECTION_NONE,
15733 : .createStmt = sql->data,
15734 : .deps = aclDeps,
15735 : .nDeps = nDeps));
15736 :
15737 3764 : destroyPQExpBuffer(tagbuf);
15738 : }
15739 :
15740 46282 : destroyPQExpBuffer(sql);
15741 :
15742 46282 : return aclDumpId;
15743 : }
15744 :
15745 : /*
15746 : * dumpSecLabel
15747 : *
15748 : * This routine is used to dump any security labels associated with the
15749 : * object handed to this routine. The routine takes the object type
15750 : * and object name (ready to print, except for schema decoration), plus
15751 : * the namespace and owner of the object (for labeling the ArchiveEntry),
15752 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
15753 : * plus the dump ID for the object (for setting a dependency).
15754 : * If a matching pg_seclabel entry is found, it is dumped.
15755 : *
15756 : * Note: although this routine takes a dumpId for dependency purposes,
15757 : * that purpose is just to mark the dependency in the emitted dump file
15758 : * for possible future use by pg_restore. We do NOT use it for determining
15759 : * ordering of the label in the dump file, because this routine is called
15760 : * after dependency sorting occurs. This routine should be called just after
15761 : * calling ArchiveEntry() for the specified object.
15762 : */
15763 : static void
15764 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
15765 : const char *namespace, const char *owner,
15766 : CatalogId catalogId, int subid, DumpId dumpId)
15767 : {
15768 0 : DumpOptions *dopt = fout->dopt;
15769 : SecLabelItem *labels;
15770 : int nlabels;
15771 : int i;
15772 : PQExpBuffer query;
15773 :
15774 : /* do nothing, if --no-security-labels is supplied */
15775 0 : if (dopt->no_security_labels)
15776 0 : return;
15777 :
15778 : /*
15779 : * Security labels are schema not data ... except large object labels are
15780 : * data
15781 : */
15782 0 : if (strcmp(type, "LARGE OBJECT") != 0)
15783 : {
15784 0 : if (!dopt->dumpSchema)
15785 0 : return;
15786 : }
15787 : else
15788 : {
15789 : /* We do dump large object security labels in binary-upgrade mode */
15790 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
15791 0 : return;
15792 : }
15793 :
15794 : /* Search for security labels associated with catalogId, using table */
15795 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15796 :
15797 0 : query = createPQExpBuffer();
15798 :
15799 0 : for (i = 0; i < nlabels; i++)
15800 : {
15801 : /*
15802 : * Ignore label entries for which the subid doesn't match.
15803 : */
15804 0 : if (labels[i].objsubid != subid)
15805 0 : continue;
15806 :
15807 0 : appendPQExpBuffer(query,
15808 : "SECURITY LABEL FOR %s ON %s ",
15809 0 : fmtId(labels[i].provider), type);
15810 0 : if (namespace && *namespace)
15811 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
15812 0 : appendPQExpBuffer(query, "%s IS ", name);
15813 0 : appendStringLiteralAH(query, labels[i].label, fout);
15814 0 : appendPQExpBufferStr(query, ";\n");
15815 : }
15816 :
15817 0 : if (query->len > 0)
15818 : {
15819 0 : PQExpBuffer tag = createPQExpBuffer();
15820 :
15821 0 : appendPQExpBuffer(tag, "%s %s", type, name);
15822 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15823 0 : ARCHIVE_OPTS(.tag = tag->data,
15824 : .namespace = namespace,
15825 : .owner = owner,
15826 : .description = "SECURITY LABEL",
15827 : .section = SECTION_NONE,
15828 : .createStmt = query->data,
15829 : .deps = &dumpId,
15830 : .nDeps = 1));
15831 0 : destroyPQExpBuffer(tag);
15832 : }
15833 :
15834 0 : destroyPQExpBuffer(query);
15835 : }
15836 :
15837 : /*
15838 : * dumpTableSecLabel
15839 : *
15840 : * As above, but dump security label for both the specified table (or view)
15841 : * and its columns.
15842 : */
15843 : static void
15844 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15845 : {
15846 0 : DumpOptions *dopt = fout->dopt;
15847 : SecLabelItem *labels;
15848 : int nlabels;
15849 : int i;
15850 : PQExpBuffer query;
15851 : PQExpBuffer target;
15852 :
15853 : /* do nothing, if --no-security-labels is supplied */
15854 0 : if (dopt->no_security_labels)
15855 0 : return;
15856 :
15857 : /* SecLabel are SCHEMA not data */
15858 0 : if (!dopt->dumpSchema)
15859 0 : return;
15860 :
15861 : /* Search for comments associated with relation, using table */
15862 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15863 : tbinfo->dobj.catId.oid,
15864 : &labels);
15865 :
15866 : /* If security labels exist, build SECURITY LABEL statements */
15867 0 : if (nlabels <= 0)
15868 0 : return;
15869 :
15870 0 : query = createPQExpBuffer();
15871 0 : target = createPQExpBuffer();
15872 :
15873 0 : for (i = 0; i < nlabels; i++)
15874 : {
15875 : const char *colname;
15876 0 : const char *provider = labels[i].provider;
15877 0 : const char *label = labels[i].label;
15878 0 : int objsubid = labels[i].objsubid;
15879 :
15880 0 : resetPQExpBuffer(target);
15881 0 : if (objsubid == 0)
15882 : {
15883 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15884 0 : fmtQualifiedDumpable(tbinfo));
15885 : }
15886 : else
15887 : {
15888 0 : colname = getAttrName(objsubid, tbinfo);
15889 : /* first fmtXXX result must be consumed before calling again */
15890 0 : appendPQExpBuffer(target, "COLUMN %s",
15891 0 : fmtQualifiedDumpable(tbinfo));
15892 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
15893 : }
15894 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15895 : fmtId(provider), target->data);
15896 0 : appendStringLiteralAH(query, label, fout);
15897 0 : appendPQExpBufferStr(query, ";\n");
15898 : }
15899 0 : if (query->len > 0)
15900 : {
15901 0 : resetPQExpBuffer(target);
15902 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15903 0 : fmtId(tbinfo->dobj.name));
15904 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15905 0 : ARCHIVE_OPTS(.tag = target->data,
15906 : .namespace = tbinfo->dobj.namespace->dobj.name,
15907 : .owner = tbinfo->rolname,
15908 : .description = "SECURITY LABEL",
15909 : .section = SECTION_NONE,
15910 : .createStmt = query->data,
15911 : .deps = &(tbinfo->dobj.dumpId),
15912 : .nDeps = 1));
15913 : }
15914 0 : destroyPQExpBuffer(query);
15915 0 : destroyPQExpBuffer(target);
15916 : }
15917 :
15918 : /*
15919 : * findSecLabels
15920 : *
15921 : * Find the security label(s), if any, associated with the given object.
15922 : * All the objsubid values associated with the given classoid/objoid are
15923 : * found with one search.
15924 : */
15925 : static int
15926 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15927 : {
15928 0 : SecLabelItem *middle = NULL;
15929 : SecLabelItem *low;
15930 : SecLabelItem *high;
15931 : int nmatch;
15932 :
15933 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
15934 : {
15935 0 : *items = NULL;
15936 0 : return 0;
15937 : }
15938 :
15939 : /*
15940 : * Do binary search to find some item matching the object.
15941 : */
15942 0 : low = &seclabels[0];
15943 0 : high = &seclabels[nseclabels - 1];
15944 0 : while (low <= high)
15945 : {
15946 0 : middle = low + (high - low) / 2;
15947 :
15948 0 : if (classoid < middle->classoid)
15949 0 : high = middle - 1;
15950 0 : else if (classoid > middle->classoid)
15951 0 : low = middle + 1;
15952 0 : else if (objoid < middle->objoid)
15953 0 : high = middle - 1;
15954 0 : else if (objoid > middle->objoid)
15955 0 : low = middle + 1;
15956 : else
15957 0 : break; /* found a match */
15958 : }
15959 :
15960 0 : if (low > high) /* no matches */
15961 : {
15962 0 : *items = NULL;
15963 0 : return 0;
15964 : }
15965 :
15966 : /*
15967 : * Now determine how many items match the object. The search loop
15968 : * invariant still holds: only items between low and high inclusive could
15969 : * match.
15970 : */
15971 0 : nmatch = 1;
15972 0 : while (middle > low)
15973 : {
15974 0 : if (classoid != middle[-1].classoid ||
15975 0 : objoid != middle[-1].objoid)
15976 : break;
15977 0 : middle--;
15978 0 : nmatch++;
15979 : }
15980 :
15981 0 : *items = middle;
15982 :
15983 0 : middle += nmatch;
15984 0 : while (middle <= high)
15985 : {
15986 0 : if (classoid != middle->classoid ||
15987 0 : objoid != middle->objoid)
15988 : break;
15989 0 : middle++;
15990 0 : nmatch++;
15991 : }
15992 :
15993 0 : return nmatch;
15994 : }
15995 :
15996 : /*
15997 : * collectSecLabels
15998 : *
15999 : * Construct a table of all security labels available for database objects;
16000 : * also set the has-seclabel component flag for each relevant object.
16001 : *
16002 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16003 : */
16004 : static void
16005 320 : collectSecLabels(Archive *fout)
16006 : {
16007 : PGresult *res;
16008 : PQExpBuffer query;
16009 : int i_label;
16010 : int i_provider;
16011 : int i_classoid;
16012 : int i_objoid;
16013 : int i_objsubid;
16014 : int ntups;
16015 : int i;
16016 : DumpableObject *dobj;
16017 :
16018 320 : query = createPQExpBuffer();
16019 :
16020 320 : appendPQExpBufferStr(query,
16021 : "SELECT label, provider, classoid, objoid, objsubid "
16022 : "FROM pg_catalog.pg_seclabel "
16023 : "ORDER BY classoid, objoid, objsubid");
16024 :
16025 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16026 :
16027 : /* Construct lookup table containing OIDs in numeric form */
16028 320 : i_label = PQfnumber(res, "label");
16029 320 : i_provider = PQfnumber(res, "provider");
16030 320 : i_classoid = PQfnumber(res, "classoid");
16031 320 : i_objoid = PQfnumber(res, "objoid");
16032 320 : i_objsubid = PQfnumber(res, "objsubid");
16033 :
16034 320 : ntups = PQntuples(res);
16035 :
16036 320 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16037 320 : nseclabels = 0;
16038 320 : dobj = NULL;
16039 :
16040 320 : for (i = 0; i < ntups; i++)
16041 : {
16042 : CatalogId objId;
16043 : int subid;
16044 :
16045 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16046 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16047 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16048 :
16049 : /* We needn't remember labels that don't match any dumpable object */
16050 0 : if (dobj == NULL ||
16051 0 : dobj->catId.tableoid != objId.tableoid ||
16052 0 : dobj->catId.oid != objId.oid)
16053 0 : dobj = findObjectByCatalogId(objId);
16054 0 : if (dobj == NULL)
16055 0 : continue;
16056 :
16057 : /*
16058 : * Labels on columns of composite types are linked to the type's
16059 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16060 : * in the type's own DumpableObject.
16061 : */
16062 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16063 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16064 0 : {
16065 : TypeInfo *cTypeInfo;
16066 :
16067 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16068 0 : if (cTypeInfo)
16069 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16070 : }
16071 : else
16072 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16073 :
16074 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16075 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16076 0 : seclabels[nseclabels].classoid = objId.tableoid;
16077 0 : seclabels[nseclabels].objoid = objId.oid;
16078 0 : seclabels[nseclabels].objsubid = subid;
16079 0 : nseclabels++;
16080 : }
16081 :
16082 320 : PQclear(res);
16083 320 : destroyPQExpBuffer(query);
16084 320 : }
16085 :
16086 : /*
16087 : * dumpTable
16088 : * write out to fout the declarations (not data) of a user-defined table
16089 : */
16090 : static void
16091 52734 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16092 : {
16093 52734 : DumpOptions *dopt = fout->dopt;
16094 52734 : DumpId tableAclDumpId = InvalidDumpId;
16095 : char *namecopy;
16096 :
16097 : /* Do nothing if not dumping schema */
16098 52734 : if (!dopt->dumpSchema)
16099 2892 : return;
16100 :
16101 49842 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16102 : {
16103 12616 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16104 752 : dumpSequence(fout, tbinfo);
16105 : else
16106 11864 : dumpTableSchema(fout, tbinfo);
16107 : }
16108 :
16109 : /* Handle the ACL here */
16110 49842 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16111 49842 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16112 : {
16113 38672 : const char *objtype =
16114 38672 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16115 :
16116 : tableAclDumpId =
16117 38672 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16118 : objtype, namecopy, NULL,
16119 38672 : tbinfo->dobj.namespace->dobj.name,
16120 : NULL, tbinfo->rolname, &tbinfo->dacl);
16121 : }
16122 :
16123 : /*
16124 : * Handle column ACLs, if any. Note: we pull these with a separate query
16125 : * rather than trying to fetch them during getTableAttrs, so that we won't
16126 : * miss ACLs on system columns. Doing it this way also allows us to dump
16127 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16128 : */
16129 49842 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16130 : {
16131 522 : PQExpBuffer query = createPQExpBuffer();
16132 : PGresult *res;
16133 : int i;
16134 :
16135 522 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16136 : {
16137 : /* Set up query for column ACLs */
16138 268 : appendPQExpBufferStr(query,
16139 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16140 :
16141 268 : if (fout->remoteVersion >= 90600)
16142 : {
16143 : /*
16144 : * In principle we should call acldefault('c', relowner) to
16145 : * get the default ACL for a column. However, we don't
16146 : * currently store the numeric OID of the relowner in
16147 : * TableInfo. We could convert the owner name using regrole,
16148 : * but that creates a risk of failure due to concurrent role
16149 : * renames. Given that the default ACL for columns is empty
16150 : * and is likely to stay that way, it's not worth extra cycles
16151 : * and risk to avoid hard-wiring that knowledge here.
16152 : */
16153 268 : appendPQExpBufferStr(query,
16154 : "SELECT at.attname, "
16155 : "at.attacl, "
16156 : "'{}' AS acldefault, "
16157 : "pip.privtype, pip.initprivs "
16158 : "FROM pg_catalog.pg_attribute at "
16159 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16160 : "(at.attrelid = pip.objoid "
16161 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16162 : "AND at.attnum = pip.objsubid) "
16163 : "WHERE at.attrelid = $1 AND "
16164 : "NOT at.attisdropped "
16165 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16166 : "ORDER BY at.attnum");
16167 : }
16168 : else
16169 : {
16170 0 : appendPQExpBufferStr(query,
16171 : "SELECT attname, attacl, '{}' AS acldefault, "
16172 : "NULL AS privtype, NULL AS initprivs "
16173 : "FROM pg_catalog.pg_attribute "
16174 : "WHERE attrelid = $1 AND NOT attisdropped "
16175 : "AND attacl IS NOT NULL "
16176 : "ORDER BY attnum");
16177 : }
16178 :
16179 268 : ExecuteSqlStatement(fout, query->data);
16180 :
16181 268 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16182 : }
16183 :
16184 522 : printfPQExpBuffer(query,
16185 : "EXECUTE getColumnACLs('%u')",
16186 : tbinfo->dobj.catId.oid);
16187 :
16188 522 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16189 :
16190 7198 : for (i = 0; i < PQntuples(res); i++)
16191 : {
16192 6676 : char *attname = PQgetvalue(res, i, 0);
16193 6676 : char *attacl = PQgetvalue(res, i, 1);
16194 6676 : char *acldefault = PQgetvalue(res, i, 2);
16195 6676 : char privtype = *(PQgetvalue(res, i, 3));
16196 6676 : char *initprivs = PQgetvalue(res, i, 4);
16197 : DumpableAcl coldacl;
16198 : char *attnamecopy;
16199 :
16200 6676 : coldacl.acl = attacl;
16201 6676 : coldacl.acldefault = acldefault;
16202 6676 : coldacl.privtype = privtype;
16203 6676 : coldacl.initprivs = initprivs;
16204 6676 : attnamecopy = pg_strdup(fmtId(attname));
16205 :
16206 : /*
16207 : * Column's GRANT type is always TABLE. Each column ACL depends
16208 : * on the table-level ACL, since we can restore column ACLs in
16209 : * parallel but the table-level ACL has to be done first.
16210 : */
16211 6676 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16212 : "TABLE", namecopy, attnamecopy,
16213 6676 : tbinfo->dobj.namespace->dobj.name,
16214 : NULL, tbinfo->rolname, &coldacl);
16215 6676 : free(attnamecopy);
16216 : }
16217 522 : PQclear(res);
16218 522 : destroyPQExpBuffer(query);
16219 : }
16220 :
16221 49842 : free(namecopy);
16222 : }
16223 :
16224 : /*
16225 : * Create the AS clause for a view or materialized view. The semicolon is
16226 : * stripped because a materialized view must add a WITH NO DATA clause.
16227 : *
16228 : * This returns a new buffer which must be freed by the caller.
16229 : */
16230 : static PQExpBuffer
16231 1796 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16232 : {
16233 1796 : PQExpBuffer query = createPQExpBuffer();
16234 1796 : PQExpBuffer result = createPQExpBuffer();
16235 : PGresult *res;
16236 : int len;
16237 :
16238 : /* Fetch the view definition */
16239 1796 : appendPQExpBuffer(query,
16240 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16241 : tbinfo->dobj.catId.oid);
16242 :
16243 1796 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16244 :
16245 1796 : if (PQntuples(res) != 1)
16246 : {
16247 0 : if (PQntuples(res) < 1)
16248 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16249 : tbinfo->dobj.name);
16250 : else
16251 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16252 : tbinfo->dobj.name);
16253 : }
16254 :
16255 1796 : len = PQgetlength(res, 0, 0);
16256 :
16257 1796 : if (len == 0)
16258 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16259 : tbinfo->dobj.name);
16260 :
16261 : /* Strip off the trailing semicolon so that other things may follow. */
16262 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16263 1796 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16264 :
16265 1796 : PQclear(res);
16266 1796 : destroyPQExpBuffer(query);
16267 :
16268 1796 : return result;
16269 : }
16270 :
16271 : /*
16272 : * Create a dummy AS clause for a view. This is used when the real view
16273 : * definition has to be postponed because of circular dependencies.
16274 : * We must duplicate the view's external properties -- column names and types
16275 : * (including collation) -- so that it works for subsequent references.
16276 : *
16277 : * This returns a new buffer which must be freed by the caller.
16278 : */
16279 : static PQExpBuffer
16280 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16281 : {
16282 40 : PQExpBuffer result = createPQExpBuffer();
16283 : int j;
16284 :
16285 40 : appendPQExpBufferStr(result, "SELECT");
16286 :
16287 80 : for (j = 0; j < tbinfo->numatts; j++)
16288 : {
16289 40 : if (j > 0)
16290 20 : appendPQExpBufferChar(result, ',');
16291 40 : appendPQExpBufferStr(result, "\n ");
16292 :
16293 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16294 :
16295 : /*
16296 : * Must add collation if not default for the type, because CREATE OR
16297 : * REPLACE VIEW won't change it
16298 : */
16299 40 : if (OidIsValid(tbinfo->attcollation[j]))
16300 : {
16301 : CollInfo *coll;
16302 :
16303 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16304 0 : if (coll)
16305 0 : appendPQExpBuffer(result, " COLLATE %s",
16306 0 : fmtQualifiedDumpable(coll));
16307 : }
16308 :
16309 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16310 : }
16311 :
16312 40 : return result;
16313 : }
16314 :
16315 : /*
16316 : * dumpTableSchema
16317 : * write the declaration (not data) of one user-defined table or view
16318 : */
16319 : static void
16320 11864 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16321 : {
16322 11864 : DumpOptions *dopt = fout->dopt;
16323 11864 : PQExpBuffer q = createPQExpBuffer();
16324 11864 : PQExpBuffer delq = createPQExpBuffer();
16325 11864 : PQExpBuffer extra = createPQExpBuffer();
16326 : char *qrelname;
16327 : char *qualrelname;
16328 : int numParents;
16329 : TableInfo **parents;
16330 : int actual_atts; /* number of attrs in this CREATE statement */
16331 : const char *reltypename;
16332 : char *storage;
16333 : int j,
16334 : k;
16335 :
16336 : /* We had better have loaded per-column details about this table */
16337 : Assert(tbinfo->interesting);
16338 :
16339 11864 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16340 11864 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16341 :
16342 11864 : if (tbinfo->hasoids)
16343 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16344 : qrelname);
16345 :
16346 11864 : if (dopt->binary_upgrade)
16347 1620 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16348 :
16349 : /* Is it a table or a view? */
16350 11864 : if (tbinfo->relkind == RELKIND_VIEW)
16351 : {
16352 : PQExpBuffer result;
16353 :
16354 : /*
16355 : * Note: keep this code in sync with the is_view case in dumpRule()
16356 : */
16357 :
16358 1036 : reltypename = "VIEW";
16359 :
16360 1036 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16361 :
16362 1036 : if (dopt->binary_upgrade)
16363 102 : binary_upgrade_set_pg_class_oids(fout, q,
16364 : tbinfo->dobj.catId.oid);
16365 :
16366 1036 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16367 :
16368 1036 : if (tbinfo->dummy_view)
16369 20 : result = createDummyViewAsClause(fout, tbinfo);
16370 : else
16371 : {
16372 1016 : if (nonemptyReloptions(tbinfo->reloptions))
16373 : {
16374 126 : appendPQExpBufferStr(q, " WITH (");
16375 126 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16376 126 : appendPQExpBufferChar(q, ')');
16377 : }
16378 1016 : result = createViewAsClause(fout, tbinfo);
16379 : }
16380 1036 : appendPQExpBuffer(q, " AS\n%s", result->data);
16381 1036 : destroyPQExpBuffer(result);
16382 :
16383 1036 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16384 68 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16385 1036 : appendPQExpBufferStr(q, ";\n");
16386 : }
16387 : else
16388 : {
16389 10828 : char *partkeydef = NULL;
16390 10828 : char *ftoptions = NULL;
16391 10828 : char *srvname = NULL;
16392 10828 : const char *foreign = "";
16393 :
16394 : /*
16395 : * Set reltypename, and collect any relkind-specific data that we
16396 : * didn't fetch during getTables().
16397 : */
16398 10828 : switch (tbinfo->relkind)
16399 : {
16400 1052 : case RELKIND_PARTITIONED_TABLE:
16401 : {
16402 1052 : PQExpBuffer query = createPQExpBuffer();
16403 : PGresult *res;
16404 :
16405 1052 : reltypename = "TABLE";
16406 :
16407 : /* retrieve partition key definition */
16408 1052 : appendPQExpBuffer(query,
16409 : "SELECT pg_get_partkeydef('%u')",
16410 : tbinfo->dobj.catId.oid);
16411 1052 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16412 1052 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16413 1052 : PQclear(res);
16414 1052 : destroyPQExpBuffer(query);
16415 1052 : break;
16416 : }
16417 72 : case RELKIND_FOREIGN_TABLE:
16418 : {
16419 72 : PQExpBuffer query = createPQExpBuffer();
16420 : PGresult *res;
16421 : int i_srvname;
16422 : int i_ftoptions;
16423 :
16424 72 : reltypename = "FOREIGN TABLE";
16425 :
16426 : /* retrieve name of foreign server and generic options */
16427 72 : appendPQExpBuffer(query,
16428 : "SELECT fs.srvname, "
16429 : "pg_catalog.array_to_string(ARRAY("
16430 : "SELECT pg_catalog.quote_ident(option_name) || "
16431 : "' ' || pg_catalog.quote_literal(option_value) "
16432 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16433 : "ORDER BY option_name"
16434 : "), E',\n ') AS ftoptions "
16435 : "FROM pg_catalog.pg_foreign_table ft "
16436 : "JOIN pg_catalog.pg_foreign_server fs "
16437 : "ON (fs.oid = ft.ftserver) "
16438 : "WHERE ft.ftrelid = '%u'",
16439 : tbinfo->dobj.catId.oid);
16440 72 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16441 72 : i_srvname = PQfnumber(res, "srvname");
16442 72 : i_ftoptions = PQfnumber(res, "ftoptions");
16443 72 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16444 72 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16445 72 : PQclear(res);
16446 72 : destroyPQExpBuffer(query);
16447 :
16448 72 : foreign = "FOREIGN ";
16449 72 : break;
16450 : }
16451 760 : case RELKIND_MATVIEW:
16452 760 : reltypename = "MATERIALIZED VIEW";
16453 760 : break;
16454 8944 : default:
16455 8944 : reltypename = "TABLE";
16456 8944 : break;
16457 : }
16458 :
16459 10828 : numParents = tbinfo->numParents;
16460 10828 : parents = tbinfo->parents;
16461 :
16462 10828 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16463 :
16464 10828 : if (dopt->binary_upgrade)
16465 1518 : binary_upgrade_set_pg_class_oids(fout, q,
16466 : tbinfo->dobj.catId.oid);
16467 :
16468 : /*
16469 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16470 : * ignore it when dumping if it was set in this case.
16471 : */
16472 10828 : appendPQExpBuffer(q, "CREATE %s%s %s",
16473 10828 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16474 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16475 : "UNLOGGED " : "",
16476 : reltypename,
16477 : qualrelname);
16478 :
16479 : /*
16480 : * Attach to type, if reloftype; except in case of a binary upgrade,
16481 : * we dump the table normally and attach it to the type afterward.
16482 : */
16483 10828 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16484 48 : appendPQExpBuffer(q, " OF %s",
16485 : getFormattedTypeName(fout, tbinfo->reloftype,
16486 : zeroIsError));
16487 :
16488 10828 : if (tbinfo->relkind != RELKIND_MATVIEW)
16489 : {
16490 : /* Dump the attributes */
16491 10068 : actual_atts = 0;
16492 48920 : for (j = 0; j < tbinfo->numatts; j++)
16493 : {
16494 : /*
16495 : * Normally, dump if it's locally defined in this table, and
16496 : * not dropped. But for binary upgrade, we'll dump all the
16497 : * columns, and then fix up the dropped and nonlocal cases
16498 : * below.
16499 : */
16500 38852 : if (shouldPrintColumn(dopt, tbinfo, j))
16501 : {
16502 : bool print_default;
16503 : bool print_notnull;
16504 :
16505 : /*
16506 : * Default value --- suppress if to be printed separately
16507 : * or not at all.
16508 : */
16509 76076 : print_default = (tbinfo->attrdefs[j] != NULL &&
16510 38952 : tbinfo->attrdefs[j]->dobj.dump &&
16511 1926 : !tbinfo->attrdefs[j]->separate);
16512 :
16513 : /*
16514 : * Not Null constraint --- print it if it is locally
16515 : * defined, or if binary upgrade. (In the latter case, we
16516 : * reset conislocal below.)
16517 : */
16518 41256 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16519 4230 : (tbinfo->notnull_islocal[j] ||
16520 1130 : dopt->binary_upgrade ||
16521 974 : tbinfo->ispartition));
16522 :
16523 : /*
16524 : * Skip column if fully defined by reloftype, except in
16525 : * binary upgrade
16526 : */
16527 37026 : if (OidIsValid(tbinfo->reloftype) &&
16528 100 : !print_default && !print_notnull &&
16529 60 : !dopt->binary_upgrade)
16530 48 : continue;
16531 :
16532 : /* Format properly if not first attr */
16533 36978 : if (actual_atts == 0)
16534 9564 : appendPQExpBufferStr(q, " (");
16535 : else
16536 27414 : appendPQExpBufferChar(q, ',');
16537 36978 : appendPQExpBufferStr(q, "\n ");
16538 36978 : actual_atts++;
16539 :
16540 : /* Attribute name */
16541 36978 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16542 :
16543 36978 : if (tbinfo->attisdropped[j])
16544 : {
16545 : /*
16546 : * ALTER TABLE DROP COLUMN clears
16547 : * pg_attribute.atttypid, so we will not have gotten a
16548 : * valid type name; insert INTEGER as a stopgap. We'll
16549 : * clean things up later.
16550 : */
16551 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16552 : /* and skip to the next column */
16553 168 : continue;
16554 : }
16555 :
16556 : /*
16557 : * Attribute type; print it except when creating a typed
16558 : * table ('OF type_name'), but in binary-upgrade mode,
16559 : * print it in that case too.
16560 : */
16561 36810 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16562 : {
16563 36778 : appendPQExpBuffer(q, " %s",
16564 36778 : tbinfo->atttypnames[j]);
16565 : }
16566 :
16567 36810 : if (print_default)
16568 : {
16569 1658 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16570 556 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16571 556 : tbinfo->attrdefs[j]->adef_expr);
16572 1102 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
16573 370 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
16574 370 : tbinfo->attrdefs[j]->adef_expr);
16575 : else
16576 732 : appendPQExpBuffer(q, " DEFAULT %s",
16577 732 : tbinfo->attrdefs[j]->adef_expr);
16578 : }
16579 :
16580 41040 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16581 4230 : (tbinfo->notnull_islocal[j] ||
16582 1130 : dopt->binary_upgrade ||
16583 974 : tbinfo->ispartition));
16584 :
16585 36810 : if (print_notnull)
16586 : {
16587 4164 : if (tbinfo->notnull_constrs[j][0] == '\0')
16588 3028 : appendPQExpBufferStr(q, " NOT NULL");
16589 : else
16590 1136 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16591 1136 : fmtId(tbinfo->notnull_constrs[j]));
16592 :
16593 4164 : if (tbinfo->notnull_noinh[j])
16594 0 : appendPQExpBufferStr(q, " NO INHERIT");
16595 : }
16596 :
16597 : /* Add collation if not default for the type */
16598 36810 : if (OidIsValid(tbinfo->attcollation[j]))
16599 : {
16600 : CollInfo *coll;
16601 :
16602 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
16603 394 : if (coll)
16604 394 : appendPQExpBuffer(q, " COLLATE %s",
16605 394 : fmtQualifiedDumpable(coll));
16606 : }
16607 : }
16608 :
16609 : /*
16610 : * On the other hand, if we choose not to print a column
16611 : * (likely because it is created by inheritance), but the
16612 : * column has a locally-defined not-null constraint, we need
16613 : * to dump the constraint as a standalone object.
16614 : *
16615 : * This syntax isn't SQL-conforming, but if you wanted
16616 : * standard output you wouldn't be creating non-standard
16617 : * objects to begin with.
16618 : */
16619 38636 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
16620 1826 : !tbinfo->attisdropped[j] &&
16621 1096 : tbinfo->notnull_constrs[j] != NULL &&
16622 202 : tbinfo->notnull_islocal[j])
16623 : {
16624 : /* Format properly if not first attr */
16625 32 : if (actual_atts == 0)
16626 24 : appendPQExpBufferStr(q, " (");
16627 : else
16628 8 : appendPQExpBufferChar(q, ',');
16629 32 : appendPQExpBufferStr(q, "\n ");
16630 32 : actual_atts++;
16631 :
16632 32 : if (tbinfo->notnull_constrs[j][0] == '\0')
16633 8 : appendPQExpBuffer(q, "NOT NULL %s",
16634 8 : fmtId(tbinfo->attnames[j]));
16635 : else
16636 48 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
16637 24 : tbinfo->notnull_constrs[j],
16638 24 : fmtId(tbinfo->attnames[j]));
16639 : }
16640 : }
16641 :
16642 : /*
16643 : * Add non-inherited CHECK constraints, if any.
16644 : *
16645 : * For partitions, we need to include check constraints even if
16646 : * they're not defined locally, because the ALTER TABLE ATTACH
16647 : * PARTITION that we'll emit later expects the constraint to be
16648 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
16649 : */
16650 11254 : for (j = 0; j < tbinfo->ncheck; j++)
16651 : {
16652 1186 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16653 :
16654 1186 : if (constr->separate ||
16655 1046 : (!constr->conislocal && !tbinfo->ispartition))
16656 218 : continue;
16657 :
16658 968 : if (actual_atts == 0)
16659 32 : appendPQExpBufferStr(q, " (\n ");
16660 : else
16661 936 : appendPQExpBufferStr(q, ",\n ");
16662 :
16663 968 : appendPQExpBuffer(q, "CONSTRAINT %s ",
16664 968 : fmtId(constr->dobj.name));
16665 968 : appendPQExpBufferStr(q, constr->condef);
16666 :
16667 968 : actual_atts++;
16668 : }
16669 :
16670 10068 : if (actual_atts)
16671 9620 : appendPQExpBufferStr(q, "\n)");
16672 448 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16673 : {
16674 : /*
16675 : * No attributes? we must have a parenthesized attribute list,
16676 : * even though empty, when not using the OF TYPE syntax.
16677 : */
16678 424 : appendPQExpBufferStr(q, " (\n)");
16679 : }
16680 :
16681 : /*
16682 : * Emit the INHERITS clause (not for partitions), except in
16683 : * binary-upgrade mode.
16684 : */
16685 10068 : if (numParents > 0 && !tbinfo->ispartition &&
16686 738 : !dopt->binary_upgrade)
16687 : {
16688 628 : appendPQExpBufferStr(q, "\nINHERITS (");
16689 1320 : for (k = 0; k < numParents; k++)
16690 : {
16691 692 : TableInfo *parentRel = parents[k];
16692 :
16693 692 : if (k > 0)
16694 64 : appendPQExpBufferStr(q, ", ");
16695 692 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16696 : }
16697 628 : appendPQExpBufferChar(q, ')');
16698 : }
16699 :
16700 10068 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16701 1052 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16702 :
16703 10068 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16704 72 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16705 : }
16706 :
16707 21368 : if (nonemptyReloptions(tbinfo->reloptions) ||
16708 10540 : nonemptyReloptions(tbinfo->toast_reloptions))
16709 : {
16710 288 : bool addcomma = false;
16711 :
16712 288 : appendPQExpBufferStr(q, "\nWITH (");
16713 288 : if (nonemptyReloptions(tbinfo->reloptions))
16714 : {
16715 288 : addcomma = true;
16716 288 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16717 : }
16718 288 : if (nonemptyReloptions(tbinfo->toast_reloptions))
16719 : {
16720 10 : if (addcomma)
16721 10 : appendPQExpBufferStr(q, ", ");
16722 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16723 : fout);
16724 : }
16725 288 : appendPQExpBufferChar(q, ')');
16726 : }
16727 :
16728 : /* Dump generic options if any */
16729 10828 : if (ftoptions && ftoptions[0])
16730 68 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16731 :
16732 : /*
16733 : * For materialized views, create the AS clause just like a view. At
16734 : * this point, we always mark the view as not populated.
16735 : */
16736 10828 : if (tbinfo->relkind == RELKIND_MATVIEW)
16737 : {
16738 : PQExpBuffer result;
16739 :
16740 760 : result = createViewAsClause(fout, tbinfo);
16741 760 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16742 : result->data);
16743 760 : destroyPQExpBuffer(result);
16744 : }
16745 : else
16746 10068 : appendPQExpBufferStr(q, ";\n");
16747 :
16748 : /* Materialized views can depend on extensions */
16749 10828 : if (tbinfo->relkind == RELKIND_MATVIEW)
16750 760 : append_depends_on_extension(fout, q, &tbinfo->dobj,
16751 : "pg_catalog.pg_class",
16752 : "MATERIALIZED VIEW",
16753 : qualrelname);
16754 :
16755 : /*
16756 : * in binary upgrade mode, update the catalog with any missing values
16757 : * that might be present.
16758 : */
16759 10828 : if (dopt->binary_upgrade)
16760 : {
16761 7626 : for (j = 0; j < tbinfo->numatts; j++)
16762 : {
16763 6108 : if (tbinfo->attmissingval[j][0] != '\0')
16764 : {
16765 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
16766 4 : appendPQExpBufferStr(q,
16767 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16768 4 : appendStringLiteralAH(q, qualrelname, fout);
16769 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16770 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16771 4 : appendPQExpBufferChar(q, ',');
16772 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16773 4 : appendPQExpBufferStr(q, ");\n\n");
16774 : }
16775 : }
16776 : }
16777 :
16778 : /*
16779 : * To create binary-compatible heap files, we have to ensure the same
16780 : * physical column order, including dropped columns, as in the
16781 : * original. Therefore, we create dropped columns above and drop them
16782 : * here, also updating their attlen/attalign values so that the
16783 : * dropped column can be skipped properly. (We do not bother with
16784 : * restoring the original attbyval setting.) Also, inheritance
16785 : * relationships are set up by doing ALTER TABLE INHERIT rather than
16786 : * using an INHERITS clause --- the latter would possibly mess up the
16787 : * column order. That also means we have to take care about setting
16788 : * attislocal correctly, plus fix up any inherited CHECK constraints.
16789 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
16790 : *
16791 : * We process foreign and partitioned tables here, even though they
16792 : * lack heap storage, because they can participate in inheritance
16793 : * relationships and we want this stuff to be consistent across the
16794 : * inheritance tree. We can exclude indexes, toast tables, sequences
16795 : * and matviews, even though they have storage, because we don't
16796 : * support altering or dropping columns in them, nor can they be part
16797 : * of inheritance trees.
16798 : */
16799 10828 : if (dopt->binary_upgrade &&
16800 1518 : (tbinfo->relkind == RELKIND_RELATION ||
16801 212 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16802 210 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16803 : {
16804 : bool firstitem;
16805 : bool firstitem_extra;
16806 :
16807 : /*
16808 : * Drop any dropped columns. Merge the pg_attribute manipulations
16809 : * into a single SQL command, so that we don't cause repeated
16810 : * relcache flushes on the target table. Otherwise we risk O(N^2)
16811 : * relcache bloat while dropping N columns.
16812 : */
16813 1482 : resetPQExpBuffer(extra);
16814 1482 : firstitem = true;
16815 7546 : for (j = 0; j < tbinfo->numatts; j++)
16816 : {
16817 6064 : if (tbinfo->attisdropped[j])
16818 : {
16819 168 : if (firstitem)
16820 : {
16821 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
16822 : "UPDATE pg_catalog.pg_attribute\n"
16823 : "SET attlen = v.dlen, "
16824 : "attalign = v.dalign, "
16825 : "attbyval = false\n"
16826 : "FROM (VALUES ");
16827 76 : firstitem = false;
16828 : }
16829 : else
16830 92 : appendPQExpBufferStr(q, ",\n ");
16831 168 : appendPQExpBufferChar(q, '(');
16832 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16833 168 : appendPQExpBuffer(q, ", %d, '%c')",
16834 168 : tbinfo->attlen[j],
16835 168 : tbinfo->attalign[j]);
16836 : /* The ALTER ... DROP COLUMN commands must come after */
16837 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
16838 : foreign, qualrelname);
16839 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
16840 168 : fmtId(tbinfo->attnames[j]));
16841 : }
16842 : }
16843 1482 : if (!firstitem)
16844 : {
16845 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
16846 : "WHERE attrelid = ");
16847 76 : appendStringLiteralAH(q, qualrelname, fout);
16848 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16849 : " AND attname = v.dname;\n");
16850 : /* Now we can issue the actual DROP COLUMN commands */
16851 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16852 : }
16853 :
16854 : /*
16855 : * Fix up inherited columns. As above, do the pg_attribute
16856 : * manipulations in a single SQL command.
16857 : */
16858 1482 : firstitem = true;
16859 7546 : for (j = 0; j < tbinfo->numatts; j++)
16860 : {
16861 6064 : if (!tbinfo->attisdropped[j] &&
16862 5896 : !tbinfo->attislocal[j])
16863 : {
16864 1158 : if (firstitem)
16865 : {
16866 504 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
16867 504 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16868 : "SET attislocal = false\n"
16869 : "WHERE attrelid = ");
16870 504 : appendStringLiteralAH(q, qualrelname, fout);
16871 504 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16872 : " AND attname IN (");
16873 504 : firstitem = false;
16874 : }
16875 : else
16876 654 : appendPQExpBufferStr(q, ", ");
16877 1158 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16878 : }
16879 : }
16880 1482 : if (!firstitem)
16881 504 : appendPQExpBufferStr(q, ");\n");
16882 :
16883 : /*
16884 : * Fix up not-null constraints that come from inheritance. As
16885 : * above, do the pg_constraint manipulations in a single SQL
16886 : * command. (Actually, two in special cases, if we're doing an
16887 : * upgrade from < 18).
16888 : */
16889 1482 : firstitem = true;
16890 1482 : firstitem_extra = true;
16891 1482 : resetPQExpBuffer(extra);
16892 7546 : for (j = 0; j < tbinfo->numatts; j++)
16893 : {
16894 : /*
16895 : * If a not-null constraint comes from inheritance, reset
16896 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
16897 : * below. Special hack: in versions < 18, columns with no
16898 : * local definition need their constraint to be matched by
16899 : * column number in conkeys instead of by constraint name,
16900 : * because the latter is not available. (We distinguish the
16901 : * case because the constraint name is the empty string.)
16902 : */
16903 6064 : if (tbinfo->notnull_constrs[j] != NULL &&
16904 538 : !tbinfo->notnull_islocal[j])
16905 : {
16906 156 : if (tbinfo->notnull_constrs[j][0] != '\0')
16907 : {
16908 130 : if (firstitem)
16909 : {
16910 114 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16911 : "SET conislocal = false\n"
16912 : "WHERE contype = 'n' AND conrelid = ");
16913 114 : appendStringLiteralAH(q, qualrelname, fout);
16914 114 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16915 : "conname IN (");
16916 114 : firstitem = false;
16917 : }
16918 : else
16919 16 : appendPQExpBufferStr(q, ", ");
16920 130 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16921 : }
16922 : else
16923 : {
16924 26 : if (firstitem_extra)
16925 : {
16926 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16927 : "SET conislocal = false\n"
16928 : "WHERE contype = 'n' AND conrelid = ");
16929 26 : appendStringLiteralAH(extra, qualrelname, fout);
16930 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
16931 : "conkey IN (");
16932 26 : firstitem_extra = false;
16933 : }
16934 : else
16935 0 : appendPQExpBufferStr(extra, ", ");
16936 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
16937 : }
16938 : }
16939 : }
16940 1482 : if (!firstitem)
16941 114 : appendPQExpBufferStr(q, ");\n");
16942 1482 : if (!firstitem_extra)
16943 26 : appendPQExpBufferStr(extra, ");\n");
16944 :
16945 1482 : if (extra->len > 0)
16946 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16947 :
16948 : /*
16949 : * Add inherited CHECK constraints, if any.
16950 : *
16951 : * For partitions, they were already dumped, and conislocal
16952 : * doesn't need fixing.
16953 : *
16954 : * As above, issue only one direct manipulation of pg_constraint.
16955 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
16956 : * commands into one as well, refrain for now due to concern about
16957 : * possible backend memory bloat if there are many such
16958 : * constraints.
16959 : */
16960 1482 : resetPQExpBuffer(extra);
16961 1482 : firstitem = true;
16962 1606 : for (k = 0; k < tbinfo->ncheck; k++)
16963 : {
16964 124 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16965 :
16966 124 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
16967 120 : continue;
16968 :
16969 4 : if (firstitem)
16970 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
16971 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16972 : foreign, qualrelname,
16973 4 : fmtId(constr->dobj.name),
16974 : constr->condef);
16975 : /* Update pg_constraint after all the ALTER TABLEs */
16976 4 : if (firstitem)
16977 : {
16978 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16979 : "SET conislocal = false\n"
16980 : "WHERE contype = 'c' AND conrelid = ");
16981 4 : appendStringLiteralAH(extra, qualrelname, fout);
16982 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
16983 4 : appendPQExpBufferStr(extra, " AND conname IN (");
16984 4 : firstitem = false;
16985 : }
16986 : else
16987 0 : appendPQExpBufferStr(extra, ", ");
16988 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
16989 : }
16990 1482 : if (!firstitem)
16991 : {
16992 4 : appendPQExpBufferStr(extra, ");\n");
16993 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16994 : }
16995 :
16996 1482 : if (numParents > 0 && !tbinfo->ispartition)
16997 : {
16998 110 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16999 236 : for (k = 0; k < numParents; k++)
17000 : {
17001 126 : TableInfo *parentRel = parents[k];
17002 :
17003 126 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17004 : qualrelname,
17005 126 : fmtQualifiedDumpable(parentRel));
17006 : }
17007 : }
17008 :
17009 1482 : if (OidIsValid(tbinfo->reloftype))
17010 : {
17011 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17012 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17013 : qualrelname,
17014 : getFormattedTypeName(fout, tbinfo->reloftype,
17015 : zeroIsError));
17016 : }
17017 : }
17018 :
17019 : /*
17020 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17021 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17022 : * TOAST tables semi-independently, here we see them only as children
17023 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17024 : * child toast table is handled below.)
17025 : */
17026 10828 : if (dopt->binary_upgrade &&
17027 1518 : (tbinfo->relkind == RELKIND_RELATION ||
17028 212 : tbinfo->relkind == RELKIND_MATVIEW))
17029 : {
17030 1342 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17031 1342 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17032 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17033 : "WHERE oid = ",
17034 : tbinfo->frozenxid, tbinfo->minmxid);
17035 1342 : appendStringLiteralAH(q, qualrelname, fout);
17036 1342 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17037 :
17038 1342 : if (tbinfo->toast_oid)
17039 : {
17040 : /*
17041 : * The toast table will have the same OID at restore, so we
17042 : * can safely target it by OID.
17043 : */
17044 552 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17045 552 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17046 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17047 : "WHERE oid = '%u';\n",
17048 : tbinfo->toast_frozenxid,
17049 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17050 : }
17051 : }
17052 :
17053 : /*
17054 : * In binary_upgrade mode, restore matviews' populated status by
17055 : * poking pg_class directly. This is pretty ugly, but we can't use
17056 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17057 : * matview is not populated even though this matview is; in any case,
17058 : * we want to transfer the matview's heap storage, not run REFRESH.
17059 : */
17060 10828 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17061 36 : tbinfo->relispopulated)
17062 : {
17063 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17064 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17065 : "SET relispopulated = 't'\n"
17066 : "WHERE oid = ");
17067 32 : appendStringLiteralAH(q, qualrelname, fout);
17068 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17069 : }
17070 :
17071 : /*
17072 : * Dump additional per-column properties that we can't handle in the
17073 : * main CREATE TABLE command.
17074 : */
17075 50596 : for (j = 0; j < tbinfo->numatts; j++)
17076 : {
17077 : /* None of this applies to dropped columns */
17078 39768 : if (tbinfo->attisdropped[j])
17079 898 : continue;
17080 :
17081 : /*
17082 : * Dump per-column statistics information. We only issue an ALTER
17083 : * TABLE statement if the attstattarget entry for this column is
17084 : * not the default value.
17085 : */
17086 38870 : if (tbinfo->attstattarget[j] >= 0)
17087 68 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17088 : foreign, qualrelname,
17089 68 : fmtId(tbinfo->attnames[j]),
17090 68 : tbinfo->attstattarget[j]);
17091 :
17092 : /*
17093 : * Dump per-column storage information. The statement is only
17094 : * dumped if the storage has been changed from the type's default.
17095 : */
17096 38870 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17097 : {
17098 166 : switch (tbinfo->attstorage[j])
17099 : {
17100 20 : case TYPSTORAGE_PLAIN:
17101 20 : storage = "PLAIN";
17102 20 : break;
17103 78 : case TYPSTORAGE_EXTERNAL:
17104 78 : storage = "EXTERNAL";
17105 78 : break;
17106 0 : case TYPSTORAGE_EXTENDED:
17107 0 : storage = "EXTENDED";
17108 0 : break;
17109 68 : case TYPSTORAGE_MAIN:
17110 68 : storage = "MAIN";
17111 68 : break;
17112 0 : default:
17113 0 : storage = NULL;
17114 : }
17115 :
17116 : /*
17117 : * Only dump the statement if it's a storage type we recognize
17118 : */
17119 166 : if (storage != NULL)
17120 166 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17121 : foreign, qualrelname,
17122 166 : fmtId(tbinfo->attnames[j]),
17123 : storage);
17124 : }
17125 :
17126 : /*
17127 : * Dump per-column compression, if it's been set.
17128 : */
17129 38870 : if (!dopt->no_toast_compression)
17130 : {
17131 : const char *cmname;
17132 :
17133 38690 : switch (tbinfo->attcompression[j])
17134 : {
17135 116 : case 'p':
17136 116 : cmname = "pglz";
17137 116 : break;
17138 192 : case 'l':
17139 192 : cmname = "lz4";
17140 192 : break;
17141 38382 : default:
17142 38382 : cmname = NULL;
17143 38382 : break;
17144 : }
17145 :
17146 38690 : if (cmname != NULL)
17147 308 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17148 : foreign, qualrelname,
17149 308 : fmtId(tbinfo->attnames[j]),
17150 : cmname);
17151 : }
17152 :
17153 : /*
17154 : * Dump per-column attributes.
17155 : */
17156 38870 : if (tbinfo->attoptions[j][0] != '\0')
17157 68 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17158 : foreign, qualrelname,
17159 68 : fmtId(tbinfo->attnames[j]),
17160 68 : tbinfo->attoptions[j]);
17161 :
17162 : /*
17163 : * Dump per-column fdw options.
17164 : */
17165 38870 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17166 72 : tbinfo->attfdwoptions[j][0] != '\0')
17167 68 : appendPQExpBuffer(q,
17168 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17169 : " %s\n"
17170 : ");\n",
17171 : qualrelname,
17172 68 : fmtId(tbinfo->attnames[j]),
17173 68 : tbinfo->attfdwoptions[j]);
17174 : } /* end loop over columns */
17175 :
17176 10828 : free(partkeydef);
17177 10828 : free(ftoptions);
17178 10828 : free(srvname);
17179 : }
17180 :
17181 : /*
17182 : * dump properties we only have ALTER TABLE syntax for
17183 : */
17184 11864 : if ((tbinfo->relkind == RELKIND_RELATION ||
17185 2920 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17186 1868 : tbinfo->relkind == RELKIND_MATVIEW) &&
17187 10756 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17188 : {
17189 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17190 : {
17191 : /* nothing to do, will be set when the index is dumped */
17192 : }
17193 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17194 : {
17195 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17196 : qualrelname);
17197 : }
17198 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17199 : {
17200 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17201 : qualrelname);
17202 : }
17203 : }
17204 :
17205 11864 : if (tbinfo->forcerowsec)
17206 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17207 : qualrelname);
17208 :
17209 11864 : if (dopt->binary_upgrade)
17210 1620 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17211 : reltypename, qrelname,
17212 1620 : tbinfo->dobj.namespace->dobj.name);
17213 :
17214 11864 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17215 : {
17216 11864 : char *tablespace = NULL;
17217 11864 : char *tableam = NULL;
17218 :
17219 : /*
17220 : * _selectTablespace() relies on tablespace-enabled objects in the
17221 : * default tablespace to have a tablespace of "" (empty string) versus
17222 : * non-tablespace-enabled objects to have a tablespace of NULL.
17223 : * getTables() sets tbinfo->reltablespace to "" for the default
17224 : * tablespace (not NULL).
17225 : */
17226 11864 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17227 10756 : tablespace = tbinfo->reltablespace;
17228 :
17229 11864 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17230 2160 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17231 10756 : tableam = tbinfo->amname;
17232 :
17233 11864 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17234 11864 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17235 : .namespace = tbinfo->dobj.namespace->dobj.name,
17236 : .tablespace = tablespace,
17237 : .tableam = tableam,
17238 : .relkind = tbinfo->relkind,
17239 : .owner = tbinfo->rolname,
17240 : .description = reltypename,
17241 : .section = tbinfo->postponed_def ?
17242 : SECTION_POST_DATA : SECTION_PRE_DATA,
17243 : .createStmt = q->data,
17244 : .dropStmt = delq->data));
17245 : }
17246 :
17247 : /* Dump Table Comments */
17248 11864 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17249 156 : dumpTableComment(fout, tbinfo, reltypename);
17250 :
17251 : /* Dump Table Security Labels */
17252 11864 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17253 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17254 :
17255 : /* Dump comments on inlined table constraints */
17256 13050 : for (j = 0; j < tbinfo->ncheck; j++)
17257 : {
17258 1186 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17259 :
17260 1186 : if (constr->separate || !constr->conislocal)
17261 508 : continue;
17262 :
17263 678 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17264 78 : dumpTableConstraintComment(fout, constr);
17265 : }
17266 :
17267 11864 : destroyPQExpBuffer(q);
17268 11864 : destroyPQExpBuffer(delq);
17269 11864 : destroyPQExpBuffer(extra);
17270 11864 : free(qrelname);
17271 11864 : free(qualrelname);
17272 11864 : }
17273 :
17274 : /*
17275 : * dumpTableAttach
17276 : * write to fout the commands to attach a child partition
17277 : *
17278 : * Child partitions are always made by creating them separately
17279 : * and then using ATTACH PARTITION, rather than using
17280 : * CREATE TABLE ... PARTITION OF. This is important for preserving
17281 : * any possible discrepancy in column layout, to allow assigning the
17282 : * correct tablespace if different, and so that it's possible to restore
17283 : * a partition without restoring its parent. (You'll get an error from
17284 : * the ATTACH PARTITION command, but that can be ignored, or skipped
17285 : * using "pg_restore -L" if you prefer.) The last point motivates
17286 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
17287 : * rather than emitting it within the child partition's ArchiveEntry.
17288 : */
17289 : static void
17290 2582 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
17291 : {
17292 2582 : DumpOptions *dopt = fout->dopt;
17293 : PQExpBuffer q;
17294 : PGresult *res;
17295 : char *partbound;
17296 :
17297 : /* Do nothing if not dumping schema */
17298 2582 : if (!dopt->dumpSchema)
17299 84 : return;
17300 :
17301 2498 : q = createPQExpBuffer();
17302 :
17303 2498 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
17304 : {
17305 : /* Set up query for partbound details */
17306 90 : appendPQExpBufferStr(q,
17307 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
17308 :
17309 90 : appendPQExpBufferStr(q,
17310 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
17311 : "FROM pg_class c "
17312 : "WHERE c.oid = $1");
17313 :
17314 90 : ExecuteSqlStatement(fout, q->data);
17315 :
17316 90 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
17317 : }
17318 :
17319 2498 : printfPQExpBuffer(q,
17320 : "EXECUTE dumpTableAttach('%u')",
17321 2498 : attachinfo->partitionTbl->dobj.catId.oid);
17322 :
17323 2498 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
17324 2498 : partbound = PQgetvalue(res, 0, 0);
17325 :
17326 : /* Perform ALTER TABLE on the parent */
17327 2498 : printfPQExpBuffer(q,
17328 : "ALTER TABLE ONLY %s ",
17329 2498 : fmtQualifiedDumpable(attachinfo->parentTbl));
17330 2498 : appendPQExpBuffer(q,
17331 : "ATTACH PARTITION %s %s;\n",
17332 2498 : fmtQualifiedDumpable(attachinfo->partitionTbl),
17333 : partbound);
17334 :
17335 : /*
17336 : * There is no point in creating a drop query as the drop is done by table
17337 : * drop. (If you think to change this, see also _printTocEntry().)
17338 : * Although this object doesn't really have ownership as such, set the
17339 : * owner field anyway to ensure that the command is run by the correct
17340 : * role at restore time.
17341 : */
17342 2498 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17343 2498 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17344 : .namespace = attachinfo->dobj.namespace->dobj.name,
17345 : .owner = attachinfo->partitionTbl->rolname,
17346 : .description = "TABLE ATTACH",
17347 : .section = SECTION_PRE_DATA,
17348 : .createStmt = q->data));
17349 :
17350 2498 : PQclear(res);
17351 2498 : destroyPQExpBuffer(q);
17352 : }
17353 :
17354 : /*
17355 : * dumpAttrDef --- dump an attribute's default-value declaration
17356 : */
17357 : static void
17358 2002 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
17359 : {
17360 2002 : DumpOptions *dopt = fout->dopt;
17361 2002 : TableInfo *tbinfo = adinfo->adtable;
17362 2002 : int adnum = adinfo->adnum;
17363 : PQExpBuffer q;
17364 : PQExpBuffer delq;
17365 : char *qualrelname;
17366 : char *tag;
17367 : char *foreign;
17368 :
17369 : /* Do nothing if not dumping schema */
17370 2002 : if (!dopt->dumpSchema)
17371 0 : return;
17372 :
17373 : /* Skip if not "separate"; it was dumped in the table's definition */
17374 2002 : if (!adinfo->separate)
17375 1658 : return;
17376 :
17377 344 : q = createPQExpBuffer();
17378 344 : delq = createPQExpBuffer();
17379 :
17380 344 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17381 :
17382 344 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17383 :
17384 344 : appendPQExpBuffer(q,
17385 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17386 344 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17387 : adinfo->adef_expr);
17388 :
17389 344 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17390 : foreign, qualrelname,
17391 344 : fmtId(tbinfo->attnames[adnum - 1]));
17392 :
17393 344 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17394 :
17395 344 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17396 344 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17397 344 : ARCHIVE_OPTS(.tag = tag,
17398 : .namespace = tbinfo->dobj.namespace->dobj.name,
17399 : .owner = tbinfo->rolname,
17400 : .description = "DEFAULT",
17401 : .section = SECTION_PRE_DATA,
17402 : .createStmt = q->data,
17403 : .dropStmt = delq->data));
17404 :
17405 344 : free(tag);
17406 344 : destroyPQExpBuffer(q);
17407 344 : destroyPQExpBuffer(delq);
17408 344 : free(qualrelname);
17409 : }
17410 :
17411 : /*
17412 : * getAttrName: extract the correct name for an attribute
17413 : *
17414 : * The array tblInfo->attnames[] only provides names of user attributes;
17415 : * if a system attribute number is supplied, we have to fake it.
17416 : * We also do a little bit of bounds checking for safety's sake.
17417 : */
17418 : static const char *
17419 4124 : getAttrName(int attrnum, const TableInfo *tblInfo)
17420 : {
17421 4124 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
17422 4124 : return tblInfo->attnames[attrnum - 1];
17423 0 : switch (attrnum)
17424 : {
17425 0 : case SelfItemPointerAttributeNumber:
17426 0 : return "ctid";
17427 0 : case MinTransactionIdAttributeNumber:
17428 0 : return "xmin";
17429 0 : case MinCommandIdAttributeNumber:
17430 0 : return "cmin";
17431 0 : case MaxTransactionIdAttributeNumber:
17432 0 : return "xmax";
17433 0 : case MaxCommandIdAttributeNumber:
17434 0 : return "cmax";
17435 0 : case TableOidAttributeNumber:
17436 0 : return "tableoid";
17437 : }
17438 0 : pg_fatal("invalid column number %d for table \"%s\"",
17439 : attrnum, tblInfo->dobj.name);
17440 : return NULL; /* keep compiler quiet */
17441 : }
17442 :
17443 : /*
17444 : * dumpIndex
17445 : * write out to fout a user-defined index
17446 : */
17447 : static void
17448 5184 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17449 : {
17450 5184 : DumpOptions *dopt = fout->dopt;
17451 5184 : TableInfo *tbinfo = indxinfo->indextable;
17452 5184 : bool is_constraint = (indxinfo->indexconstraint != 0);
17453 : PQExpBuffer q;
17454 : PQExpBuffer delq;
17455 : char *qindxname;
17456 : char *qqindxname;
17457 :
17458 : /* Do nothing if not dumping schema */
17459 5184 : if (!dopt->dumpSchema)
17460 234 : return;
17461 :
17462 4950 : q = createPQExpBuffer();
17463 4950 : delq = createPQExpBuffer();
17464 :
17465 4950 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17466 4950 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17467 :
17468 : /*
17469 : * If there's an associated constraint, don't dump the index per se, but
17470 : * do dump any comment for it. (This is safe because dependency ordering
17471 : * will have ensured the constraint is emitted first.) Note that the
17472 : * emitted comment has to be shown as depending on the constraint, not the
17473 : * index, in such cases.
17474 : */
17475 4950 : if (!is_constraint)
17476 : {
17477 2088 : char *indstatcols = indxinfo->indstatcols;
17478 2088 : char *indstatvals = indxinfo->indstatvals;
17479 2088 : char **indstatcolsarray = NULL;
17480 2088 : char **indstatvalsarray = NULL;
17481 2088 : int nstatcols = 0;
17482 2088 : int nstatvals = 0;
17483 :
17484 2088 : if (dopt->binary_upgrade)
17485 310 : binary_upgrade_set_pg_class_oids(fout, q,
17486 : indxinfo->dobj.catId.oid);
17487 :
17488 : /* Plain secondary index */
17489 2088 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17490 :
17491 : /*
17492 : * Append ALTER TABLE commands as needed to set properties that we
17493 : * only have ALTER TABLE syntax for. Keep this in sync with the
17494 : * similar code in dumpConstraint!
17495 : */
17496 :
17497 : /* If the index is clustered, we need to record that. */
17498 2088 : if (indxinfo->indisclustered)
17499 : {
17500 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17501 0 : fmtQualifiedDumpable(tbinfo));
17502 : /* index name is not qualified in this syntax */
17503 0 : appendPQExpBuffer(q, " ON %s;\n",
17504 : qindxname);
17505 : }
17506 :
17507 : /*
17508 : * If the index has any statistics on some of its columns, generate
17509 : * the associated ALTER INDEX queries.
17510 : */
17511 2088 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17512 : {
17513 : int j;
17514 :
17515 68 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17516 0 : pg_fatal("could not parse index statistic columns");
17517 68 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17518 0 : pg_fatal("could not parse index statistic values");
17519 68 : if (nstatcols != nstatvals)
17520 0 : pg_fatal("mismatched number of columns and values for index statistics");
17521 :
17522 204 : for (j = 0; j < nstatcols; j++)
17523 : {
17524 136 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17525 :
17526 : /*
17527 : * Note that this is a column number, so no quotes should be
17528 : * used.
17529 : */
17530 136 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
17531 136 : indstatcolsarray[j]);
17532 136 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17533 136 : indstatvalsarray[j]);
17534 : }
17535 : }
17536 :
17537 : /* Indexes can depend on extensions */
17538 2088 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17539 : "pg_catalog.pg_class",
17540 : "INDEX", qqindxname);
17541 :
17542 : /* If the index defines identity, we need to record that. */
17543 2088 : if (indxinfo->indisreplident)
17544 : {
17545 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17546 0 : fmtQualifiedDumpable(tbinfo));
17547 : /* index name is not qualified in this syntax */
17548 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17549 : qindxname);
17550 : }
17551 :
17552 2088 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17553 :
17554 2088 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17555 2088 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17556 2088 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17557 : .namespace = tbinfo->dobj.namespace->dobj.name,
17558 : .tablespace = indxinfo->tablespace,
17559 : .owner = tbinfo->rolname,
17560 : .description = "INDEX",
17561 : .section = SECTION_POST_DATA,
17562 : .createStmt = q->data,
17563 : .dropStmt = delq->data));
17564 :
17565 2088 : free(indstatcolsarray);
17566 2088 : free(indstatvalsarray);
17567 : }
17568 :
17569 : /* Dump Index Comments */
17570 4950 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17571 30 : dumpComment(fout, "INDEX", qindxname,
17572 30 : tbinfo->dobj.namespace->dobj.name,
17573 : tbinfo->rolname,
17574 : indxinfo->dobj.catId, 0,
17575 : is_constraint ? indxinfo->indexconstraint :
17576 : indxinfo->dobj.dumpId);
17577 :
17578 4950 : destroyPQExpBuffer(q);
17579 4950 : destroyPQExpBuffer(delq);
17580 4950 : free(qindxname);
17581 4950 : free(qqindxname);
17582 : }
17583 :
17584 : /*
17585 : * dumpIndexAttach
17586 : * write out to fout a partitioned-index attachment clause
17587 : */
17588 : static void
17589 1160 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17590 : {
17591 : /* Do nothing if not dumping schema */
17592 1160 : if (!fout->dopt->dumpSchema)
17593 96 : return;
17594 :
17595 1064 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17596 : {
17597 1064 : PQExpBuffer q = createPQExpBuffer();
17598 :
17599 1064 : appendPQExpBuffer(q, "ALTER INDEX %s ",
17600 1064 : fmtQualifiedDumpable(attachinfo->parentIdx));
17601 1064 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17602 1064 : fmtQualifiedDumpable(attachinfo->partitionIdx));
17603 :
17604 : /*
17605 : * There is no point in creating a drop query as the drop is done by
17606 : * index drop. (If you think to change this, see also
17607 : * _printTocEntry().) Although this object doesn't really have
17608 : * ownership as such, set the owner field anyway to ensure that the
17609 : * command is run by the correct role at restore time.
17610 : */
17611 1064 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17612 1064 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17613 : .namespace = attachinfo->dobj.namespace->dobj.name,
17614 : .owner = attachinfo->parentIdx->indextable->rolname,
17615 : .description = "INDEX ATTACH",
17616 : .section = SECTION_POST_DATA,
17617 : .createStmt = q->data));
17618 :
17619 1064 : destroyPQExpBuffer(q);
17620 : }
17621 : }
17622 :
17623 : /*
17624 : * dumpStatisticsExt
17625 : * write out to fout an extended statistics object
17626 : */
17627 : static void
17628 278 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17629 : {
17630 278 : DumpOptions *dopt = fout->dopt;
17631 : PQExpBuffer q;
17632 : PQExpBuffer delq;
17633 : PQExpBuffer query;
17634 : char *qstatsextname;
17635 : PGresult *res;
17636 : char *stxdef;
17637 :
17638 : /* Do nothing if not dumping schema */
17639 278 : if (!dopt->dumpSchema)
17640 36 : return;
17641 :
17642 242 : q = createPQExpBuffer();
17643 242 : delq = createPQExpBuffer();
17644 242 : query = createPQExpBuffer();
17645 :
17646 242 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17647 :
17648 242 : appendPQExpBuffer(query, "SELECT "
17649 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17650 : statsextinfo->dobj.catId.oid);
17651 :
17652 242 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17653 :
17654 242 : stxdef = PQgetvalue(res, 0, 0);
17655 :
17656 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17657 242 : appendPQExpBuffer(q, "%s;\n", stxdef);
17658 :
17659 : /*
17660 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17661 : * for this statistics object is not the default value.
17662 : */
17663 242 : if (statsextinfo->stattarget >= 0)
17664 : {
17665 68 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17666 68 : fmtQualifiedDumpable(statsextinfo));
17667 68 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17668 : statsextinfo->stattarget);
17669 : }
17670 :
17671 242 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17672 242 : fmtQualifiedDumpable(statsextinfo));
17673 :
17674 242 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17675 242 : ArchiveEntry(fout, statsextinfo->dobj.catId,
17676 : statsextinfo->dobj.dumpId,
17677 242 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17678 : .namespace = statsextinfo->dobj.namespace->dobj.name,
17679 : .owner = statsextinfo->rolname,
17680 : .description = "STATISTICS",
17681 : .section = SECTION_POST_DATA,
17682 : .createStmt = q->data,
17683 : .dropStmt = delq->data));
17684 :
17685 : /* Dump Statistics Comments */
17686 242 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17687 0 : dumpComment(fout, "STATISTICS", qstatsextname,
17688 0 : statsextinfo->dobj.namespace->dobj.name,
17689 : statsextinfo->rolname,
17690 : statsextinfo->dobj.catId, 0,
17691 : statsextinfo->dobj.dumpId);
17692 :
17693 242 : PQclear(res);
17694 242 : destroyPQExpBuffer(q);
17695 242 : destroyPQExpBuffer(delq);
17696 242 : destroyPQExpBuffer(query);
17697 242 : free(qstatsextname);
17698 : }
17699 :
17700 : /*
17701 : * dumpConstraint
17702 : * write out to fout a user-defined constraint
17703 : */
17704 : static void
17705 4756 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17706 : {
17707 4756 : DumpOptions *dopt = fout->dopt;
17708 4756 : TableInfo *tbinfo = coninfo->contable;
17709 : PQExpBuffer q;
17710 : PQExpBuffer delq;
17711 4756 : char *tag = NULL;
17712 : char *foreign;
17713 :
17714 : /* Do nothing if not dumping schema */
17715 4756 : if (!dopt->dumpSchema)
17716 184 : return;
17717 :
17718 4572 : q = createPQExpBuffer();
17719 4572 : delq = createPQExpBuffer();
17720 :
17721 8956 : foreign = tbinfo &&
17722 4572 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17723 :
17724 4572 : if (coninfo->contype == 'p' ||
17725 2192 : coninfo->contype == 'u' ||
17726 1730 : coninfo->contype == 'x')
17727 2862 : {
17728 : /* Index-related constraint */
17729 : IndxInfo *indxinfo;
17730 : int k;
17731 :
17732 2862 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17733 :
17734 2862 : if (indxinfo == NULL)
17735 0 : pg_fatal("missing index for constraint \"%s\"",
17736 : coninfo->dobj.name);
17737 :
17738 2862 : if (dopt->binary_upgrade)
17739 286 : binary_upgrade_set_pg_class_oids(fout, q,
17740 : indxinfo->dobj.catId.oid);
17741 :
17742 2862 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17743 2862 : fmtQualifiedDumpable(tbinfo));
17744 2862 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17745 2862 : fmtId(coninfo->dobj.name));
17746 :
17747 2862 : if (coninfo->condef)
17748 : {
17749 : /* pg_get_constraintdef should have provided everything */
17750 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17751 : }
17752 : else
17753 : {
17754 2842 : appendPQExpBufferStr(q,
17755 2842 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17756 :
17757 : /*
17758 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17759 : * indexes. Being able to create this was fixed, but we need to
17760 : * make the index distinct in order to be able to restore the
17761 : * dump.
17762 : */
17763 2842 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17764 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17765 2842 : appendPQExpBufferStr(q, " (");
17766 6886 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
17767 : {
17768 4044 : int indkey = (int) indxinfo->indkeys[k];
17769 : const char *attname;
17770 :
17771 4044 : if (indkey == InvalidAttrNumber)
17772 0 : break;
17773 4044 : attname = getAttrName(indkey, tbinfo);
17774 :
17775 4044 : appendPQExpBuffer(q, "%s%s",
17776 : (k == 0) ? "" : ", ",
17777 : fmtId(attname));
17778 : }
17779 2842 : if (coninfo->conperiod)
17780 216 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17781 :
17782 2842 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17783 40 : appendPQExpBufferStr(q, ") INCLUDE (");
17784 :
17785 2922 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17786 : {
17787 80 : int indkey = (int) indxinfo->indkeys[k];
17788 : const char *attname;
17789 :
17790 80 : if (indkey == InvalidAttrNumber)
17791 0 : break;
17792 80 : attname = getAttrName(indkey, tbinfo);
17793 :
17794 160 : appendPQExpBuffer(q, "%s%s",
17795 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
17796 : fmtId(attname));
17797 : }
17798 :
17799 2842 : appendPQExpBufferChar(q, ')');
17800 :
17801 2842 : if (nonemptyReloptions(indxinfo->indreloptions))
17802 : {
17803 0 : appendPQExpBufferStr(q, " WITH (");
17804 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17805 0 : appendPQExpBufferChar(q, ')');
17806 : }
17807 :
17808 2842 : if (coninfo->condeferrable)
17809 : {
17810 50 : appendPQExpBufferStr(q, " DEFERRABLE");
17811 50 : if (coninfo->condeferred)
17812 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17813 : }
17814 :
17815 2842 : appendPQExpBufferStr(q, ";\n");
17816 : }
17817 :
17818 : /*
17819 : * Append ALTER TABLE commands as needed to set properties that we
17820 : * only have ALTER TABLE syntax for. Keep this in sync with the
17821 : * similar code in dumpIndex!
17822 : */
17823 :
17824 : /* If the index is clustered, we need to record that. */
17825 2862 : if (indxinfo->indisclustered)
17826 : {
17827 68 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17828 68 : fmtQualifiedDumpable(tbinfo));
17829 : /* index name is not qualified in this syntax */
17830 68 : appendPQExpBuffer(q, " ON %s;\n",
17831 68 : fmtId(indxinfo->dobj.name));
17832 : }
17833 :
17834 : /* If the index defines identity, we need to record that. */
17835 2862 : if (indxinfo->indisreplident)
17836 : {
17837 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17838 0 : fmtQualifiedDumpable(tbinfo));
17839 : /* index name is not qualified in this syntax */
17840 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17841 0 : fmtId(indxinfo->dobj.name));
17842 : }
17843 :
17844 : /* Indexes can depend on extensions */
17845 2862 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17846 : "pg_catalog.pg_class", "INDEX",
17847 2862 : fmtQualifiedDumpable(indxinfo));
17848 :
17849 2862 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17850 2862 : fmtQualifiedDumpable(tbinfo));
17851 2862 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17852 2862 : fmtId(coninfo->dobj.name));
17853 :
17854 2862 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17855 :
17856 2862 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17857 2862 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17858 2862 : ARCHIVE_OPTS(.tag = tag,
17859 : .namespace = tbinfo->dobj.namespace->dobj.name,
17860 : .tablespace = indxinfo->tablespace,
17861 : .owner = tbinfo->rolname,
17862 : .description = "CONSTRAINT",
17863 : .section = SECTION_POST_DATA,
17864 : .createStmt = q->data,
17865 : .dropStmt = delq->data));
17866 : }
17867 1710 : else if (coninfo->contype == 'f')
17868 : {
17869 : char *only;
17870 :
17871 : /*
17872 : * Foreign keys on partitioned tables are always declared as
17873 : * inheriting to partitions; for all other cases, emit them as
17874 : * applying ONLY directly to the named table, because that's how they
17875 : * work for regular inherited tables.
17876 : */
17877 336 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17878 :
17879 : /*
17880 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17881 : * current table data is not processed
17882 : */
17883 336 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17884 336 : only, fmtQualifiedDumpable(tbinfo));
17885 336 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17886 336 : fmtId(coninfo->dobj.name),
17887 : coninfo->condef);
17888 :
17889 336 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17890 336 : only, fmtQualifiedDumpable(tbinfo));
17891 336 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17892 336 : fmtId(coninfo->dobj.name));
17893 :
17894 336 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17895 :
17896 336 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17897 336 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17898 336 : ARCHIVE_OPTS(.tag = tag,
17899 : .namespace = tbinfo->dobj.namespace->dobj.name,
17900 : .owner = tbinfo->rolname,
17901 : .description = "FK CONSTRAINT",
17902 : .section = SECTION_POST_DATA,
17903 : .createStmt = q->data,
17904 : .dropStmt = delq->data));
17905 : }
17906 1374 : else if (coninfo->contype == 'c' && tbinfo)
17907 : {
17908 : /* CHECK constraint on a table */
17909 :
17910 : /* Ignore if not to be dumped separately, or if it was inherited */
17911 1186 : if (coninfo->separate && coninfo->conislocal)
17912 : {
17913 : /* not ONLY since we want it to propagate to children */
17914 90 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17915 90 : fmtQualifiedDumpable(tbinfo));
17916 90 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17917 90 : fmtId(coninfo->dobj.name),
17918 : coninfo->condef);
17919 :
17920 90 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17921 90 : fmtQualifiedDumpable(tbinfo));
17922 90 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17923 90 : fmtId(coninfo->dobj.name));
17924 :
17925 90 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17926 :
17927 90 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17928 90 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17929 90 : ARCHIVE_OPTS(.tag = tag,
17930 : .namespace = tbinfo->dobj.namespace->dobj.name,
17931 : .owner = tbinfo->rolname,
17932 : .description = "CHECK CONSTRAINT",
17933 : .section = SECTION_POST_DATA,
17934 : .createStmt = q->data,
17935 : .dropStmt = delq->data));
17936 : }
17937 : }
17938 188 : else if (coninfo->contype == 'c' && tbinfo == NULL)
17939 188 : {
17940 : /* CHECK constraint on a domain */
17941 188 : TypeInfo *tyinfo = coninfo->condomain;
17942 :
17943 : /* Ignore if not to be dumped separately */
17944 188 : if (coninfo->separate)
17945 : {
17946 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17947 0 : fmtQualifiedDumpable(tyinfo));
17948 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17949 0 : fmtId(coninfo->dobj.name),
17950 : coninfo->condef);
17951 :
17952 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17953 0 : fmtQualifiedDumpable(tyinfo));
17954 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17955 0 : fmtId(coninfo->dobj.name));
17956 :
17957 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17958 :
17959 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17960 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17961 0 : ARCHIVE_OPTS(.tag = tag,
17962 : .namespace = tyinfo->dobj.namespace->dobj.name,
17963 : .owner = tyinfo->rolname,
17964 : .description = "CHECK CONSTRAINT",
17965 : .section = SECTION_POST_DATA,
17966 : .createStmt = q->data,
17967 : .dropStmt = delq->data));
17968 : }
17969 : }
17970 : else
17971 : {
17972 0 : pg_fatal("unrecognized constraint type: %c",
17973 : coninfo->contype);
17974 : }
17975 :
17976 : /* Dump Constraint Comments --- only works for table constraints */
17977 4572 : if (tbinfo && coninfo->separate &&
17978 3338 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17979 20 : dumpTableConstraintComment(fout, coninfo);
17980 :
17981 4572 : free(tag);
17982 4572 : destroyPQExpBuffer(q);
17983 4572 : destroyPQExpBuffer(delq);
17984 : }
17985 :
17986 : /*
17987 : * dumpTableConstraintComment --- dump a constraint's comment if any
17988 : *
17989 : * This is split out because we need the function in two different places
17990 : * depending on whether the constraint is dumped as part of CREATE TABLE
17991 : * or as a separate ALTER command.
17992 : */
17993 : static void
17994 98 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17995 : {
17996 98 : TableInfo *tbinfo = coninfo->contable;
17997 98 : PQExpBuffer conprefix = createPQExpBuffer();
17998 : char *qtabname;
17999 :
18000 98 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18001 :
18002 98 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18003 98 : fmtId(coninfo->dobj.name));
18004 :
18005 98 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18006 98 : dumpComment(fout, conprefix->data, qtabname,
18007 98 : tbinfo->dobj.namespace->dobj.name,
18008 : tbinfo->rolname,
18009 : coninfo->dobj.catId, 0,
18010 98 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18011 :
18012 98 : destroyPQExpBuffer(conprefix);
18013 98 : free(qtabname);
18014 98 : }
18015 :
18016 : static inline SeqType
18017 1278 : parse_sequence_type(const char *name)
18018 : {
18019 2844 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18020 : {
18021 2844 : if (strcmp(SeqTypeNames[i], name) == 0)
18022 1278 : return (SeqType) i;
18023 : }
18024 :
18025 0 : pg_fatal("unrecognized sequence type: %s", name);
18026 : return (SeqType) 0; /* keep compiler quiet */
18027 : }
18028 :
18029 : /*
18030 : * bsearch() comparator for SequenceItem
18031 : */
18032 : static int
18033 5930 : SequenceItemCmp(const void *p1, const void *p2)
18034 : {
18035 5930 : SequenceItem v1 = *((const SequenceItem *) p1);
18036 5930 : SequenceItem v2 = *((const SequenceItem *) p2);
18037 :
18038 5930 : return pg_cmp_u32(v1.oid, v2.oid);
18039 : }
18040 :
18041 : /*
18042 : * collectSequences
18043 : *
18044 : * Construct a table of sequence information. This table is sorted by OID for
18045 : * speed in lookup.
18046 : */
18047 : static void
18048 320 : collectSequences(Archive *fout)
18049 : {
18050 : PGresult *res;
18051 : const char *query;
18052 :
18053 : /*
18054 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18055 : * some extra effort, we might be able to use the sorted table for those
18056 : * versions, but for now it seems unlikely to be worth it.
18057 : *
18058 : * Since version 18, we can gather the sequence data in this query with
18059 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18060 : */
18061 320 : if (fout->remoteVersion < 100000)
18062 0 : return;
18063 320 : else if (fout->remoteVersion < 180000 ||
18064 320 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18065 8 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18066 : "seqstart, seqincrement, "
18067 : "seqmax, seqmin, "
18068 : "seqcache, seqcycle, "
18069 : "NULL, 'f' "
18070 : "FROM pg_catalog.pg_sequence "
18071 : "ORDER BY seqrelid";
18072 : else
18073 312 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18074 : "seqstart, seqincrement, "
18075 : "seqmax, seqmin, "
18076 : "seqcache, seqcycle, "
18077 : "last_value, is_called "
18078 : "FROM pg_catalog.pg_sequence, "
18079 : "pg_get_sequence_data(seqrelid) "
18080 : "ORDER BY seqrelid;";
18081 :
18082 320 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18083 :
18084 320 : nsequences = PQntuples(res);
18085 320 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18086 :
18087 1598 : for (int i = 0; i < nsequences; i++)
18088 : {
18089 1278 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18090 1278 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18091 1278 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18092 1278 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18093 1278 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18094 1278 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18095 1278 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18096 1278 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18097 1278 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18098 1278 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18099 : }
18100 :
18101 320 : PQclear(res);
18102 : }
18103 :
18104 : /*
18105 : * dumpSequence
18106 : * write the declaration (not data) of one user-defined sequence
18107 : */
18108 : static void
18109 752 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18110 : {
18111 752 : DumpOptions *dopt = fout->dopt;
18112 : SequenceItem *seq;
18113 : bool is_ascending;
18114 : int64 default_minv,
18115 : default_maxv;
18116 752 : PQExpBuffer query = createPQExpBuffer();
18117 752 : PQExpBuffer delqry = createPQExpBuffer();
18118 : char *qseqname;
18119 752 : TableInfo *owning_tab = NULL;
18120 :
18121 752 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18122 :
18123 : /*
18124 : * For versions >= 10, the sequence information is gathered in a sorted
18125 : * table before any calls to dumpSequence(). See collectSequences() for
18126 : * more information.
18127 : */
18128 752 : if (fout->remoteVersion >= 100000)
18129 : {
18130 752 : SequenceItem key = {0};
18131 :
18132 : Assert(sequences);
18133 :
18134 752 : key.oid = tbinfo->dobj.catId.oid;
18135 752 : seq = bsearch(&key, sequences, nsequences,
18136 : sizeof(SequenceItem), SequenceItemCmp);
18137 : }
18138 : else
18139 : {
18140 : PGresult *res;
18141 :
18142 : /*
18143 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18144 : *
18145 : * Note: it might seem that 'bigint' potentially needs to be
18146 : * schema-qualified, but actually that's a keyword.
18147 : */
18148 0 : appendPQExpBuffer(query,
18149 : "SELECT 'bigint' AS sequence_type, "
18150 : "start_value, increment_by, max_value, min_value, "
18151 : "cache_value, is_cycled FROM %s",
18152 0 : fmtQualifiedDumpable(tbinfo));
18153 :
18154 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18155 :
18156 0 : if (PQntuples(res) != 1)
18157 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18158 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18159 : PQntuples(res)),
18160 : tbinfo->dobj.name, PQntuples(res));
18161 :
18162 0 : seq = pg_malloc0(sizeof(SequenceItem));
18163 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18164 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18165 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18166 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18167 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18168 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18169 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18170 :
18171 0 : PQclear(res);
18172 : }
18173 :
18174 : /* Calculate default limits for a sequence of this type */
18175 752 : is_ascending = (seq->incby >= 0);
18176 752 : if (seq->seqtype == SEQTYPE_SMALLINT)
18177 : {
18178 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18179 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18180 : }
18181 702 : else if (seq->seqtype == SEQTYPE_INTEGER)
18182 : {
18183 580 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18184 580 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18185 : }
18186 122 : else if (seq->seqtype == SEQTYPE_BIGINT)
18187 : {
18188 122 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18189 122 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18190 : }
18191 : else
18192 : {
18193 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18194 : default_minv = default_maxv = 0; /* keep compiler quiet */
18195 : }
18196 :
18197 : /*
18198 : * Identity sequences are not to be dropped separately.
18199 : */
18200 752 : if (!tbinfo->is_identity_sequence)
18201 : {
18202 464 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18203 464 : fmtQualifiedDumpable(tbinfo));
18204 : }
18205 :
18206 752 : resetPQExpBuffer(query);
18207 :
18208 752 : if (dopt->binary_upgrade)
18209 : {
18210 122 : binary_upgrade_set_pg_class_oids(fout, query,
18211 : tbinfo->dobj.catId.oid);
18212 :
18213 : /*
18214 : * In older PG versions a sequence will have a pg_type entry, but v14
18215 : * and up don't use that, so don't attempt to preserve the type OID.
18216 : */
18217 : }
18218 :
18219 752 : if (tbinfo->is_identity_sequence)
18220 : {
18221 288 : owning_tab = findTableByOid(tbinfo->owning_tab);
18222 :
18223 288 : appendPQExpBuffer(query,
18224 : "ALTER TABLE %s ",
18225 288 : fmtQualifiedDumpable(owning_tab));
18226 288 : appendPQExpBuffer(query,
18227 : "ALTER COLUMN %s ADD GENERATED ",
18228 288 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18229 288 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18230 208 : appendPQExpBufferStr(query, "ALWAYS");
18231 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18232 80 : appendPQExpBufferStr(query, "BY DEFAULT");
18233 288 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
18234 288 : fmtQualifiedDumpable(tbinfo));
18235 :
18236 : /*
18237 : * Emit persistence option only if it's different from the owning
18238 : * table's. This avoids using this new syntax unnecessarily.
18239 : */
18240 288 : if (tbinfo->relpersistence != owning_tab->relpersistence)
18241 20 : appendPQExpBuffer(query, " %s\n",
18242 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18243 : "UNLOGGED" : "LOGGED");
18244 : }
18245 : else
18246 : {
18247 464 : appendPQExpBuffer(query,
18248 : "CREATE %sSEQUENCE %s\n",
18249 464 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18250 : "UNLOGGED " : "",
18251 464 : fmtQualifiedDumpable(tbinfo));
18252 :
18253 464 : if (seq->seqtype != SEQTYPE_BIGINT)
18254 372 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
18255 : }
18256 :
18257 752 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
18258 :
18259 752 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
18260 :
18261 752 : if (seq->minv != default_minv)
18262 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
18263 : else
18264 722 : appendPQExpBufferStr(query, " NO MINVALUE\n");
18265 :
18266 752 : if (seq->maxv != default_maxv)
18267 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
18268 : else
18269 722 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
18270 :
18271 752 : appendPQExpBuffer(query,
18272 : " CACHE " INT64_FORMAT "%s",
18273 752 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
18274 :
18275 752 : if (tbinfo->is_identity_sequence)
18276 288 : appendPQExpBufferStr(query, "\n);\n");
18277 : else
18278 464 : appendPQExpBufferStr(query, ";\n");
18279 :
18280 : /* binary_upgrade: no need to clear TOAST table oid */
18281 :
18282 752 : if (dopt->binary_upgrade)
18283 122 : binary_upgrade_extension_member(query, &tbinfo->dobj,
18284 : "SEQUENCE", qseqname,
18285 122 : tbinfo->dobj.namespace->dobj.name);
18286 :
18287 752 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18288 752 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18289 752 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18290 : .namespace = tbinfo->dobj.namespace->dobj.name,
18291 : .owner = tbinfo->rolname,
18292 : .description = "SEQUENCE",
18293 : .section = SECTION_PRE_DATA,
18294 : .createStmt = query->data,
18295 : .dropStmt = delqry->data));
18296 :
18297 : /*
18298 : * If the sequence is owned by a table column, emit the ALTER for it as a
18299 : * separate TOC entry immediately following the sequence's own entry. It's
18300 : * OK to do this rather than using full sorting logic, because the
18301 : * dependency that tells us it's owned will have forced the table to be
18302 : * created first. We can't just include the ALTER in the TOC entry
18303 : * because it will fail if we haven't reassigned the sequence owner to
18304 : * match the table's owner.
18305 : *
18306 : * We need not schema-qualify the table reference because both sequence
18307 : * and table must be in the same schema.
18308 : */
18309 752 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
18310 : {
18311 282 : owning_tab = findTableByOid(tbinfo->owning_tab);
18312 :
18313 282 : if (owning_tab == NULL)
18314 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
18315 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
18316 :
18317 282 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
18318 : {
18319 278 : resetPQExpBuffer(query);
18320 278 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
18321 278 : fmtQualifiedDumpable(tbinfo));
18322 278 : appendPQExpBuffer(query, " OWNED BY %s",
18323 278 : fmtQualifiedDumpable(owning_tab));
18324 278 : appendPQExpBuffer(query, ".%s;\n",
18325 278 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18326 :
18327 278 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18328 278 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18329 278 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18330 : .namespace = tbinfo->dobj.namespace->dobj.name,
18331 : .owner = tbinfo->rolname,
18332 : .description = "SEQUENCE OWNED BY",
18333 : .section = SECTION_PRE_DATA,
18334 : .createStmt = query->data,
18335 : .deps = &(tbinfo->dobj.dumpId),
18336 : .nDeps = 1));
18337 : }
18338 : }
18339 :
18340 : /* Dump Sequence Comments and Security Labels */
18341 752 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18342 0 : dumpComment(fout, "SEQUENCE", qseqname,
18343 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18344 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18345 :
18346 752 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18347 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
18348 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18349 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
18350 :
18351 752 : if (fout->remoteVersion < 100000)
18352 0 : pg_free(seq);
18353 752 : destroyPQExpBuffer(query);
18354 752 : destroyPQExpBuffer(delqry);
18355 752 : free(qseqname);
18356 752 : }
18357 :
18358 : /*
18359 : * dumpSequenceData
18360 : * write the data of one user-defined sequence
18361 : */
18362 : static void
18363 790 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
18364 : {
18365 790 : TableInfo *tbinfo = tdinfo->tdtable;
18366 : int64 last;
18367 : bool called;
18368 790 : PQExpBuffer query = createPQExpBuffer();
18369 :
18370 : /*
18371 : * For versions >= 18, the sequence information is gathered in the sorted
18372 : * array before any calls to dumpSequenceData(). See collectSequences()
18373 : * for more information.
18374 : *
18375 : * For older versions, we have to query the sequence relations
18376 : * individually.
18377 : */
18378 790 : if (fout->remoteVersion < 180000)
18379 : {
18380 : PGresult *res;
18381 :
18382 0 : appendPQExpBuffer(query,
18383 : "SELECT last_value, is_called FROM %s",
18384 0 : fmtQualifiedDumpable(tbinfo));
18385 :
18386 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18387 :
18388 0 : if (PQntuples(res) != 1)
18389 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18390 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18391 : PQntuples(res)),
18392 : tbinfo->dobj.name, PQntuples(res));
18393 :
18394 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18395 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18396 :
18397 0 : PQclear(res);
18398 : }
18399 : else
18400 : {
18401 790 : SequenceItem key = {0};
18402 : SequenceItem *entry;
18403 :
18404 : Assert(sequences);
18405 : Assert(tbinfo->dobj.catId.oid);
18406 :
18407 790 : key.oid = tbinfo->dobj.catId.oid;
18408 790 : entry = bsearch(&key, sequences, nsequences,
18409 : sizeof(SequenceItem), SequenceItemCmp);
18410 :
18411 790 : last = entry->last_value;
18412 790 : called = entry->is_called;
18413 : }
18414 :
18415 790 : resetPQExpBuffer(query);
18416 790 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18417 790 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18418 790 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18419 : last, (called ? "true" : "false"));
18420 :
18421 790 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18422 790 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18423 790 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18424 : .namespace = tbinfo->dobj.namespace->dobj.name,
18425 : .owner = tbinfo->rolname,
18426 : .description = "SEQUENCE SET",
18427 : .section = SECTION_DATA,
18428 : .createStmt = query->data,
18429 : .deps = &(tbinfo->dobj.dumpId),
18430 : .nDeps = 1));
18431 :
18432 790 : destroyPQExpBuffer(query);
18433 790 : }
18434 :
18435 : /*
18436 : * dumpTrigger
18437 : * write the declaration of one user-defined table trigger
18438 : */
18439 : static void
18440 1066 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18441 : {
18442 1066 : DumpOptions *dopt = fout->dopt;
18443 1066 : TableInfo *tbinfo = tginfo->tgtable;
18444 : PQExpBuffer query;
18445 : PQExpBuffer delqry;
18446 : PQExpBuffer trigprefix;
18447 : PQExpBuffer trigidentity;
18448 : char *qtabname;
18449 : char *tag;
18450 :
18451 : /* Do nothing if not dumping schema */
18452 1066 : if (!dopt->dumpSchema)
18453 62 : return;
18454 :
18455 1004 : query = createPQExpBuffer();
18456 1004 : delqry = createPQExpBuffer();
18457 1004 : trigprefix = createPQExpBuffer();
18458 1004 : trigidentity = createPQExpBuffer();
18459 :
18460 1004 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18461 :
18462 1004 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18463 1004 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18464 :
18465 1004 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18466 1004 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18467 :
18468 : /* Triggers can depend on extensions */
18469 1004 : append_depends_on_extension(fout, query, &tginfo->dobj,
18470 : "pg_catalog.pg_trigger", "TRIGGER",
18471 1004 : trigidentity->data);
18472 :
18473 1004 : if (tginfo->tgispartition)
18474 : {
18475 : Assert(tbinfo->ispartition);
18476 :
18477 : /*
18478 : * Partition triggers only appear here because their 'tgenabled' flag
18479 : * differs from its parent's. The trigger is created already, so
18480 : * remove the CREATE and replace it with an ALTER. (Clear out the
18481 : * DROP query too, so that pg_dump --create does not cause errors.)
18482 : */
18483 230 : resetPQExpBuffer(query);
18484 230 : resetPQExpBuffer(delqry);
18485 230 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18486 230 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18487 230 : fmtQualifiedDumpable(tbinfo));
18488 230 : switch (tginfo->tgenabled)
18489 : {
18490 80 : case 'f':
18491 : case 'D':
18492 80 : appendPQExpBufferStr(query, "DISABLE");
18493 80 : break;
18494 0 : case 't':
18495 : case 'O':
18496 0 : appendPQExpBufferStr(query, "ENABLE");
18497 0 : break;
18498 70 : case 'R':
18499 70 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18500 70 : break;
18501 80 : case 'A':
18502 80 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18503 80 : break;
18504 : }
18505 230 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18506 230 : fmtId(tginfo->dobj.name));
18507 : }
18508 774 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
18509 : {
18510 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18511 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18512 0 : fmtQualifiedDumpable(tbinfo));
18513 0 : switch (tginfo->tgenabled)
18514 : {
18515 0 : case 'D':
18516 : case 'f':
18517 0 : appendPQExpBufferStr(query, "DISABLE");
18518 0 : break;
18519 0 : case 'A':
18520 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18521 0 : break;
18522 0 : case 'R':
18523 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18524 0 : break;
18525 0 : default:
18526 0 : appendPQExpBufferStr(query, "ENABLE");
18527 0 : break;
18528 : }
18529 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18530 0 : fmtId(tginfo->dobj.name));
18531 : }
18532 :
18533 1004 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
18534 1004 : fmtId(tginfo->dobj.name));
18535 :
18536 1004 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
18537 :
18538 1004 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18539 1004 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
18540 1004 : ARCHIVE_OPTS(.tag = tag,
18541 : .namespace = tbinfo->dobj.namespace->dobj.name,
18542 : .owner = tbinfo->rolname,
18543 : .description = "TRIGGER",
18544 : .section = SECTION_POST_DATA,
18545 : .createStmt = query->data,
18546 : .dropStmt = delqry->data));
18547 :
18548 1004 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18549 0 : dumpComment(fout, trigprefix->data, qtabname,
18550 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18551 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
18552 :
18553 1004 : free(tag);
18554 1004 : destroyPQExpBuffer(query);
18555 1004 : destroyPQExpBuffer(delqry);
18556 1004 : destroyPQExpBuffer(trigprefix);
18557 1004 : destroyPQExpBuffer(trigidentity);
18558 1004 : free(qtabname);
18559 : }
18560 :
18561 : /*
18562 : * dumpEventTrigger
18563 : * write the declaration of one user-defined event trigger
18564 : */
18565 : static void
18566 88 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
18567 : {
18568 88 : DumpOptions *dopt = fout->dopt;
18569 : PQExpBuffer query;
18570 : PQExpBuffer delqry;
18571 : char *qevtname;
18572 :
18573 : /* Do nothing if not dumping schema */
18574 88 : if (!dopt->dumpSchema)
18575 12 : return;
18576 :
18577 76 : query = createPQExpBuffer();
18578 76 : delqry = createPQExpBuffer();
18579 :
18580 76 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
18581 :
18582 76 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
18583 76 : appendPQExpBufferStr(query, qevtname);
18584 76 : appendPQExpBufferStr(query, " ON ");
18585 76 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
18586 :
18587 76 : if (strcmp("", evtinfo->evttags) != 0)
18588 : {
18589 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
18590 10 : appendPQExpBufferStr(query, evtinfo->evttags);
18591 10 : appendPQExpBufferChar(query, ')');
18592 : }
18593 :
18594 76 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
18595 76 : appendPQExpBufferStr(query, evtinfo->evtfname);
18596 76 : appendPQExpBufferStr(query, "();\n");
18597 :
18598 76 : if (evtinfo->evtenabled != 'O')
18599 : {
18600 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
18601 : qevtname);
18602 0 : switch (evtinfo->evtenabled)
18603 : {
18604 0 : case 'D':
18605 0 : appendPQExpBufferStr(query, "DISABLE");
18606 0 : break;
18607 0 : case 'A':
18608 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18609 0 : break;
18610 0 : case 'R':
18611 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18612 0 : break;
18613 0 : default:
18614 0 : appendPQExpBufferStr(query, "ENABLE");
18615 0 : break;
18616 : }
18617 0 : appendPQExpBufferStr(query, ";\n");
18618 : }
18619 :
18620 76 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
18621 : qevtname);
18622 :
18623 76 : if (dopt->binary_upgrade)
18624 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
18625 : "EVENT TRIGGER", qevtname, NULL);
18626 :
18627 76 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18628 76 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
18629 76 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
18630 : .owner = evtinfo->evtowner,
18631 : .description = "EVENT TRIGGER",
18632 : .section = SECTION_POST_DATA,
18633 : .createStmt = query->data,
18634 : .dropStmt = delqry->data));
18635 :
18636 76 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18637 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
18638 : NULL, evtinfo->evtowner,
18639 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
18640 :
18641 76 : destroyPQExpBuffer(query);
18642 76 : destroyPQExpBuffer(delqry);
18643 76 : free(qevtname);
18644 : }
18645 :
18646 : /*
18647 : * dumpRule
18648 : * Dump a rule
18649 : */
18650 : static void
18651 2330 : dumpRule(Archive *fout, const RuleInfo *rinfo)
18652 : {
18653 2330 : DumpOptions *dopt = fout->dopt;
18654 2330 : TableInfo *tbinfo = rinfo->ruletable;
18655 : bool is_view;
18656 : PQExpBuffer query;
18657 : PQExpBuffer cmd;
18658 : PQExpBuffer delcmd;
18659 : PQExpBuffer ruleprefix;
18660 : char *qtabname;
18661 : PGresult *res;
18662 : char *tag;
18663 :
18664 : /* Do nothing if not dumping schema */
18665 2330 : if (!dopt->dumpSchema)
18666 132 : return;
18667 :
18668 : /*
18669 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18670 : * we do not want to dump it as a separate object.
18671 : */
18672 2198 : if (!rinfo->separate)
18673 1776 : return;
18674 :
18675 : /*
18676 : * If it's an ON SELECT rule, we want to print it as a view definition,
18677 : * instead of a rule.
18678 : */
18679 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18680 :
18681 422 : query = createPQExpBuffer();
18682 422 : cmd = createPQExpBuffer();
18683 422 : delcmd = createPQExpBuffer();
18684 422 : ruleprefix = createPQExpBuffer();
18685 :
18686 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18687 :
18688 422 : if (is_view)
18689 : {
18690 : PQExpBuffer result;
18691 :
18692 : /*
18693 : * We need OR REPLACE here because we'll be replacing a dummy view.
18694 : * Otherwise this should look largely like the regular view dump code.
18695 : */
18696 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18697 20 : fmtQualifiedDumpable(tbinfo));
18698 20 : if (nonemptyReloptions(tbinfo->reloptions))
18699 : {
18700 0 : appendPQExpBufferStr(cmd, " WITH (");
18701 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18702 0 : appendPQExpBufferChar(cmd, ')');
18703 : }
18704 20 : result = createViewAsClause(fout, tbinfo);
18705 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
18706 20 : destroyPQExpBuffer(result);
18707 20 : if (tbinfo->checkoption != NULL)
18708 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18709 : tbinfo->checkoption);
18710 20 : appendPQExpBufferStr(cmd, ";\n");
18711 : }
18712 : else
18713 : {
18714 : /* In the rule case, just print pg_get_ruledef's result verbatim */
18715 402 : appendPQExpBuffer(query,
18716 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18717 : rinfo->dobj.catId.oid);
18718 :
18719 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18720 :
18721 402 : if (PQntuples(res) != 1)
18722 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18723 : rinfo->dobj.name, tbinfo->dobj.name);
18724 :
18725 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18726 :
18727 402 : PQclear(res);
18728 : }
18729 :
18730 : /*
18731 : * Add the command to alter the rules replication firing semantics if it
18732 : * differs from the default.
18733 : */
18734 422 : if (rinfo->ev_enabled != 'O')
18735 : {
18736 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18737 30 : switch (rinfo->ev_enabled)
18738 : {
18739 0 : case 'A':
18740 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18741 0 : fmtId(rinfo->dobj.name));
18742 0 : break;
18743 0 : case 'R':
18744 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18745 0 : fmtId(rinfo->dobj.name));
18746 0 : break;
18747 30 : case 'D':
18748 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18749 30 : fmtId(rinfo->dobj.name));
18750 30 : break;
18751 : }
18752 392 : }
18753 :
18754 422 : if (is_view)
18755 : {
18756 : /*
18757 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18758 : * REPLACE VIEW to replace the rule with something with minimal
18759 : * dependencies.
18760 : */
18761 : PQExpBuffer result;
18762 :
18763 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18764 20 : fmtQualifiedDumpable(tbinfo));
18765 20 : result = createDummyViewAsClause(fout, tbinfo);
18766 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18767 20 : destroyPQExpBuffer(result);
18768 : }
18769 : else
18770 : {
18771 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
18772 402 : fmtId(rinfo->dobj.name));
18773 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
18774 402 : fmtQualifiedDumpable(tbinfo));
18775 : }
18776 :
18777 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
18778 422 : fmtId(rinfo->dobj.name));
18779 :
18780 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18781 :
18782 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18783 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18784 422 : ARCHIVE_OPTS(.tag = tag,
18785 : .namespace = tbinfo->dobj.namespace->dobj.name,
18786 : .owner = tbinfo->rolname,
18787 : .description = "RULE",
18788 : .section = SECTION_POST_DATA,
18789 : .createStmt = cmd->data,
18790 : .dropStmt = delcmd->data));
18791 :
18792 : /* Dump rule comments */
18793 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18794 0 : dumpComment(fout, ruleprefix->data, qtabname,
18795 0 : tbinfo->dobj.namespace->dobj.name,
18796 : tbinfo->rolname,
18797 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18798 :
18799 422 : free(tag);
18800 422 : destroyPQExpBuffer(query);
18801 422 : destroyPQExpBuffer(cmd);
18802 422 : destroyPQExpBuffer(delcmd);
18803 422 : destroyPQExpBuffer(ruleprefix);
18804 422 : free(qtabname);
18805 : }
18806 :
18807 : /*
18808 : * getExtensionMembership --- obtain extension membership data
18809 : *
18810 : * We need to identify objects that are extension members as soon as they're
18811 : * loaded, so that we can correctly determine whether they need to be dumped.
18812 : * Generally speaking, extension member objects will get marked as *not* to
18813 : * be dumped, as they will be recreated by the single CREATE EXTENSION
18814 : * command. However, in binary upgrade mode we still need to dump the members
18815 : * individually.
18816 : */
18817 : void
18818 322 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18819 : int numExtensions)
18820 : {
18821 : PQExpBuffer query;
18822 : PGresult *res;
18823 : int ntups,
18824 : i;
18825 : int i_classid,
18826 : i_objid,
18827 : i_refobjid;
18828 : ExtensionInfo *ext;
18829 :
18830 : /* Nothing to do if no extensions */
18831 322 : if (numExtensions == 0)
18832 0 : return;
18833 :
18834 322 : query = createPQExpBuffer();
18835 :
18836 : /* refclassid constraint is redundant but may speed the search */
18837 322 : appendPQExpBufferStr(query, "SELECT "
18838 : "classid, objid, refobjid "
18839 : "FROM pg_depend "
18840 : "WHERE refclassid = 'pg_extension'::regclass "
18841 : "AND deptype = 'e' "
18842 : "ORDER BY 3");
18843 :
18844 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18845 :
18846 322 : ntups = PQntuples(res);
18847 :
18848 322 : i_classid = PQfnumber(res, "classid");
18849 322 : i_objid = PQfnumber(res, "objid");
18850 322 : i_refobjid = PQfnumber(res, "refobjid");
18851 :
18852 : /*
18853 : * Since we ordered the SELECT by referenced ID, we can expect that
18854 : * multiple entries for the same extension will appear together; this
18855 : * saves on searches.
18856 : */
18857 322 : ext = NULL;
18858 :
18859 2810 : for (i = 0; i < ntups; i++)
18860 : {
18861 : CatalogId objId;
18862 : Oid extId;
18863 :
18864 2488 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18865 2488 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18866 2488 : extId = atooid(PQgetvalue(res, i, i_refobjid));
18867 :
18868 2488 : if (ext == NULL ||
18869 2166 : ext->dobj.catId.oid != extId)
18870 372 : ext = findExtensionByOid(extId);
18871 :
18872 2488 : if (ext == NULL)
18873 : {
18874 : /* shouldn't happen */
18875 0 : pg_log_warning("could not find referenced extension %u", extId);
18876 0 : continue;
18877 : }
18878 :
18879 2488 : recordExtensionMembership(objId, ext);
18880 : }
18881 :
18882 322 : PQclear(res);
18883 :
18884 322 : destroyPQExpBuffer(query);
18885 : }
18886 :
18887 : /*
18888 : * processExtensionTables --- deal with extension configuration tables
18889 : *
18890 : * There are two parts to this process:
18891 : *
18892 : * 1. Identify and create dump records for extension configuration tables.
18893 : *
18894 : * Extensions can mark tables as "configuration", which means that the user
18895 : * is able and expected to modify those tables after the extension has been
18896 : * loaded. For these tables, we dump out only the data- the structure is
18897 : * expected to be handled at CREATE EXTENSION time, including any indexes or
18898 : * foreign keys, which brings us to-
18899 : *
18900 : * 2. Record FK dependencies between configuration tables.
18901 : *
18902 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
18903 : * the data is loaded, we have to work out what the best order for reloading
18904 : * the data is, to avoid FK violations when the tables are restored. This is
18905 : * not perfect- we can't handle circular dependencies and if any exist they
18906 : * will cause an invalid dump to be produced (though at least all of the data
18907 : * is included for a user to manually restore). This is currently documented
18908 : * but perhaps we can provide a better solution in the future.
18909 : */
18910 : void
18911 320 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18912 : int numExtensions)
18913 : {
18914 320 : DumpOptions *dopt = fout->dopt;
18915 : PQExpBuffer query;
18916 : PGresult *res;
18917 : int ntups,
18918 : i;
18919 : int i_conrelid,
18920 : i_confrelid;
18921 :
18922 : /* Nothing to do if no extensions */
18923 320 : if (numExtensions == 0)
18924 0 : return;
18925 :
18926 : /*
18927 : * Identify extension configuration tables and create TableDataInfo
18928 : * objects for them, ensuring their data will be dumped even though the
18929 : * tables themselves won't be.
18930 : *
18931 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
18932 : * user data in a configuration table is treated like schema data. This
18933 : * seems appropriate since system data in a config table would get
18934 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
18935 : * list of extensions to be included, none of its data is dumped.
18936 : */
18937 690 : for (i = 0; i < numExtensions; i++)
18938 : {
18939 370 : ExtensionInfo *curext = &(extinfo[i]);
18940 370 : char *extconfig = curext->extconfig;
18941 370 : char *extcondition = curext->extcondition;
18942 370 : char **extconfigarray = NULL;
18943 370 : char **extconditionarray = NULL;
18944 370 : int nconfigitems = 0;
18945 370 : int nconditionitems = 0;
18946 :
18947 : /*
18948 : * Check if this extension is listed as to include in the dump. If
18949 : * not, any table data associated with it is discarded.
18950 : */
18951 370 : if (extension_include_oids.head != NULL &&
18952 16 : !simple_oid_list_member(&extension_include_oids,
18953 : curext->dobj.catId.oid))
18954 12 : continue;
18955 :
18956 : /*
18957 : * Check if this extension is listed as to exclude in the dump. If
18958 : * yes, any table data associated with it is discarded.
18959 : */
18960 370 : if (extension_exclude_oids.head != NULL &&
18961 8 : simple_oid_list_member(&extension_exclude_oids,
18962 : curext->dobj.catId.oid))
18963 4 : continue;
18964 :
18965 358 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18966 : {
18967 : int j;
18968 :
18969 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18970 0 : pg_fatal("could not parse %s array", "extconfig");
18971 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18972 0 : pg_fatal("could not parse %s array", "extcondition");
18973 40 : if (nconfigitems != nconditionitems)
18974 0 : pg_fatal("mismatched number of configurations and conditions for extension");
18975 :
18976 120 : for (j = 0; j < nconfigitems; j++)
18977 : {
18978 : TableInfo *configtbl;
18979 80 : Oid configtbloid = atooid(extconfigarray[j]);
18980 80 : bool dumpobj =
18981 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18982 :
18983 80 : configtbl = findTableByOid(configtbloid);
18984 80 : if (configtbl == NULL)
18985 0 : continue;
18986 :
18987 : /*
18988 : * Tables of not-to-be-dumped extensions shouldn't be dumped
18989 : * unless the table or its schema is explicitly included
18990 : */
18991 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18992 : {
18993 : /* check table explicitly requested */
18994 4 : if (table_include_oids.head != NULL &&
18995 0 : simple_oid_list_member(&table_include_oids,
18996 : configtbloid))
18997 0 : dumpobj = true;
18998 :
18999 : /* check table's schema explicitly requested */
19000 4 : if (configtbl->dobj.namespace->dobj.dump &
19001 : DUMP_COMPONENT_DATA)
19002 4 : dumpobj = true;
19003 : }
19004 :
19005 : /* check table excluded by an exclusion switch */
19006 88 : if (table_exclude_oids.head != NULL &&
19007 8 : simple_oid_list_member(&table_exclude_oids,
19008 : configtbloid))
19009 2 : dumpobj = false;
19010 :
19011 : /* check schema excluded by an exclusion switch */
19012 80 : if (simple_oid_list_member(&schema_exclude_oids,
19013 80 : configtbl->dobj.namespace->dobj.catId.oid))
19014 0 : dumpobj = false;
19015 :
19016 80 : if (dumpobj)
19017 : {
19018 78 : makeTableDataInfo(dopt, configtbl);
19019 78 : if (configtbl->dataObj != NULL)
19020 : {
19021 78 : if (strlen(extconditionarray[j]) > 0)
19022 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19023 : }
19024 : }
19025 : }
19026 : }
19027 358 : if (extconfigarray)
19028 40 : free(extconfigarray);
19029 358 : if (extconditionarray)
19030 40 : free(extconditionarray);
19031 : }
19032 :
19033 : /*
19034 : * Now that all the TableDataInfo objects have been created for all the
19035 : * extensions, check their FK dependencies and register them to try and
19036 : * dump the data out in an order that they can be restored in.
19037 : *
19038 : * Note that this is not a problem for user tables as their FKs are
19039 : * recreated after the data has been loaded.
19040 : */
19041 :
19042 320 : query = createPQExpBuffer();
19043 :
19044 320 : printfPQExpBuffer(query,
19045 : "SELECT conrelid, confrelid "
19046 : "FROM pg_constraint "
19047 : "JOIN pg_depend ON (objid = confrelid) "
19048 : "WHERE contype = 'f' "
19049 : "AND refclassid = 'pg_extension'::regclass "
19050 : "AND classid = 'pg_class'::regclass;");
19051 :
19052 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19053 320 : ntups = PQntuples(res);
19054 :
19055 320 : i_conrelid = PQfnumber(res, "conrelid");
19056 320 : i_confrelid = PQfnumber(res, "confrelid");
19057 :
19058 : /* Now get the dependencies and register them */
19059 320 : for (i = 0; i < ntups; i++)
19060 : {
19061 : Oid conrelid,
19062 : confrelid;
19063 : TableInfo *reftable,
19064 : *contable;
19065 :
19066 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19067 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19068 0 : contable = findTableByOid(conrelid);
19069 0 : reftable = findTableByOid(confrelid);
19070 :
19071 0 : if (reftable == NULL ||
19072 0 : reftable->dataObj == NULL ||
19073 0 : contable == NULL ||
19074 0 : contable->dataObj == NULL)
19075 0 : continue;
19076 :
19077 : /*
19078 : * Make referencing TABLE_DATA object depend on the referenced table's
19079 : * TABLE_DATA object.
19080 : */
19081 0 : addObjectDependency(&contable->dataObj->dobj,
19082 0 : reftable->dataObj->dobj.dumpId);
19083 : }
19084 320 : PQclear(res);
19085 320 : destroyPQExpBuffer(query);
19086 : }
19087 :
19088 : /*
19089 : * getDependencies --- obtain available dependency data
19090 : */
19091 : static void
19092 320 : getDependencies(Archive *fout)
19093 : {
19094 : PQExpBuffer query;
19095 : PGresult *res;
19096 : int ntups,
19097 : i;
19098 : int i_classid,
19099 : i_objid,
19100 : i_refclassid,
19101 : i_refobjid,
19102 : i_deptype;
19103 : DumpableObject *dobj,
19104 : *refdobj;
19105 :
19106 320 : pg_log_info("reading dependency data");
19107 :
19108 320 : query = createPQExpBuffer();
19109 :
19110 : /*
19111 : * Messy query to collect the dependency data we need. Note that we
19112 : * ignore the sub-object column, so that dependencies of or on a column
19113 : * look the same as dependencies of or on a whole table.
19114 : *
19115 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19116 : * already processed by getExtensionMembership.
19117 : */
19118 320 : appendPQExpBufferStr(query, "SELECT "
19119 : "classid, objid, refclassid, refobjid, deptype "
19120 : "FROM pg_depend "
19121 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19122 :
19123 : /*
19124 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19125 : * have to translate their dependencies into dependencies of their parent
19126 : * opfamily. Ignore internal dependencies though, as those will point to
19127 : * their parent opclass, which we needn't consider here (and if we did,
19128 : * it'd just result in circular dependencies). Also, "loose" opfamily
19129 : * entries will have dependencies on their parent opfamily, which we
19130 : * should drop since they'd likewise become useless self-dependencies.
19131 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19132 : */
19133 320 : appendPQExpBufferStr(query, "UNION ALL\n"
19134 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19135 : "FROM pg_depend d, pg_amop o "
19136 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19137 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19138 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19139 :
19140 : /* Likewise for pg_amproc entries */
19141 320 : appendPQExpBufferStr(query, "UNION ALL\n"
19142 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19143 : "FROM pg_depend d, pg_amproc p "
19144 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19145 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19146 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19147 :
19148 : /* Sort the output for efficiency below */
19149 320 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19150 :
19151 320 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19152 :
19153 320 : ntups = PQntuples(res);
19154 :
19155 320 : i_classid = PQfnumber(res, "classid");
19156 320 : i_objid = PQfnumber(res, "objid");
19157 320 : i_refclassid = PQfnumber(res, "refclassid");
19158 320 : i_refobjid = PQfnumber(res, "refobjid");
19159 320 : i_deptype = PQfnumber(res, "deptype");
19160 :
19161 : /*
19162 : * Since we ordered the SELECT by referencing ID, we can expect that
19163 : * multiple entries for the same object will appear together; this saves
19164 : * on searches.
19165 : */
19166 320 : dobj = NULL;
19167 :
19168 703938 : for (i = 0; i < ntups; i++)
19169 : {
19170 : CatalogId objId;
19171 : CatalogId refobjId;
19172 : char deptype;
19173 :
19174 703618 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19175 703618 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19176 703618 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19177 703618 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19178 703618 : deptype = *(PQgetvalue(res, i, i_deptype));
19179 :
19180 703618 : if (dobj == NULL ||
19181 655450 : dobj->catId.tableoid != objId.tableoid ||
19182 651568 : dobj->catId.oid != objId.oid)
19183 312302 : dobj = findObjectByCatalogId(objId);
19184 :
19185 : /*
19186 : * Failure to find objects mentioned in pg_depend is not unexpected,
19187 : * since for example we don't collect info about TOAST tables.
19188 : */
19189 703618 : if (dobj == NULL)
19190 : {
19191 : #ifdef NOT_USED
19192 : pg_log_warning("no referencing object %u %u",
19193 : objId.tableoid, objId.oid);
19194 : #endif
19195 49416 : continue;
19196 : }
19197 :
19198 655770 : refdobj = findObjectByCatalogId(refobjId);
19199 :
19200 655770 : if (refdobj == NULL)
19201 : {
19202 : #ifdef NOT_USED
19203 : pg_log_warning("no referenced object %u %u",
19204 : refobjId.tableoid, refobjId.oid);
19205 : #endif
19206 1568 : continue;
19207 : }
19208 :
19209 : /*
19210 : * For 'x' dependencies, mark the object for later; we still add the
19211 : * normal dependency, for possible ordering purposes. Currently
19212 : * pg_dump_sort.c knows to put extensions ahead of all object types
19213 : * that could possibly depend on them, but this is safer.
19214 : */
19215 654202 : if (deptype == 'x')
19216 88 : dobj->depends_on_ext = true;
19217 :
19218 : /*
19219 : * Ordinarily, table rowtypes have implicit dependencies on their
19220 : * tables. However, for a composite type the implicit dependency goes
19221 : * the other way in pg_depend; which is the right thing for DROP but
19222 : * it doesn't produce the dependency ordering we need. So in that one
19223 : * case, we reverse the direction of the dependency.
19224 : */
19225 654202 : if (deptype == 'i' &&
19226 182342 : dobj->objType == DO_TABLE &&
19227 2376 : refdobj->objType == DO_TYPE)
19228 368 : addObjectDependency(refdobj, dobj->dumpId);
19229 : else
19230 : /* normal case */
19231 653834 : addObjectDependency(dobj, refdobj->dumpId);
19232 : }
19233 :
19234 320 : PQclear(res);
19235 :
19236 320 : destroyPQExpBuffer(query);
19237 320 : }
19238 :
19239 :
19240 : /*
19241 : * createBoundaryObjects - create dummy DumpableObjects to represent
19242 : * dump section boundaries.
19243 : */
19244 : static DumpableObject *
19245 320 : createBoundaryObjects(void)
19246 : {
19247 : DumpableObject *dobjs;
19248 :
19249 320 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
19250 :
19251 320 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
19252 320 : dobjs[0].catId = nilCatalogId;
19253 320 : AssignDumpId(dobjs + 0);
19254 320 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
19255 :
19256 320 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
19257 320 : dobjs[1].catId = nilCatalogId;
19258 320 : AssignDumpId(dobjs + 1);
19259 320 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
19260 :
19261 320 : return dobjs;
19262 : }
19263 :
19264 : /*
19265 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
19266 : * section boundaries.
19267 : */
19268 : static void
19269 320 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
19270 : DumpableObject *boundaryObjs)
19271 : {
19272 320 : DumpableObject *preDataBound = boundaryObjs + 0;
19273 320 : DumpableObject *postDataBound = boundaryObjs + 1;
19274 : int i;
19275 :
19276 1187560 : for (i = 0; i < numObjs; i++)
19277 : {
19278 1187240 : DumpableObject *dobj = dobjs[i];
19279 :
19280 : /*
19281 : * The classification of object types here must match the SECTION_xxx
19282 : * values assigned during subsequent ArchiveEntry calls!
19283 : */
19284 1187240 : switch (dobj->objType)
19285 : {
19286 1100808 : case DO_NAMESPACE:
19287 : case DO_EXTENSION:
19288 : case DO_TYPE:
19289 : case DO_SHELL_TYPE:
19290 : case DO_FUNC:
19291 : case DO_AGG:
19292 : case DO_OPERATOR:
19293 : case DO_ACCESS_METHOD:
19294 : case DO_OPCLASS:
19295 : case DO_OPFAMILY:
19296 : case DO_COLLATION:
19297 : case DO_CONVERSION:
19298 : case DO_TABLE:
19299 : case DO_TABLE_ATTACH:
19300 : case DO_ATTRDEF:
19301 : case DO_PROCLANG:
19302 : case DO_CAST:
19303 : case DO_DUMMY_TYPE:
19304 : case DO_TSPARSER:
19305 : case DO_TSDICT:
19306 : case DO_TSTEMPLATE:
19307 : case DO_TSCONFIG:
19308 : case DO_FDW:
19309 : case DO_FOREIGN_SERVER:
19310 : case DO_TRANSFORM:
19311 : /* Pre-data objects: must come before the pre-data boundary */
19312 1100808 : addObjectDependency(preDataBound, dobj->dumpId);
19313 1100808 : break;
19314 8934 : case DO_TABLE_DATA:
19315 : case DO_SEQUENCE_SET:
19316 : case DO_LARGE_OBJECT:
19317 : case DO_LARGE_OBJECT_DATA:
19318 : /* Data objects: must come between the boundaries */
19319 8934 : addObjectDependency(dobj, preDataBound->dumpId);
19320 8934 : addObjectDependency(postDataBound, dobj->dumpId);
19321 8934 : break;
19322 11324 : case DO_INDEX:
19323 : case DO_INDEX_ATTACH:
19324 : case DO_STATSEXT:
19325 : case DO_REFRESH_MATVIEW:
19326 : case DO_TRIGGER:
19327 : case DO_EVENT_TRIGGER:
19328 : case DO_DEFAULT_ACL:
19329 : case DO_POLICY:
19330 : case DO_PUBLICATION:
19331 : case DO_PUBLICATION_REL:
19332 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
19333 : case DO_SUBSCRIPTION:
19334 : case DO_SUBSCRIPTION_REL:
19335 : /* Post-data objects: must come after the post-data boundary */
19336 11324 : addObjectDependency(dobj, postDataBound->dumpId);
19337 11324 : break;
19338 48792 : case DO_RULE:
19339 : /* Rules are post-data, but only if dumped separately */
19340 48792 : if (((RuleInfo *) dobj)->separate)
19341 1186 : addObjectDependency(dobj, postDataBound->dumpId);
19342 48792 : break;
19343 4844 : case DO_CONSTRAINT:
19344 : case DO_FK_CONSTRAINT:
19345 : /* Constraints are post-data, but only if dumped separately */
19346 4844 : if (((ConstraintInfo *) dobj)->separate)
19347 3510 : addObjectDependency(dobj, postDataBound->dumpId);
19348 4844 : break;
19349 320 : case DO_PRE_DATA_BOUNDARY:
19350 : /* nothing to do */
19351 320 : break;
19352 320 : case DO_POST_DATA_BOUNDARY:
19353 : /* must come after the pre-data boundary */
19354 320 : addObjectDependency(dobj, preDataBound->dumpId);
19355 320 : break;
19356 11898 : case DO_REL_STATS:
19357 : /* stats section varies by parent object type, DATA or POST */
19358 11898 : if (statisticsDumpSection((RelStatsInfo *) dobj) == SECTION_DATA)
19359 : {
19360 8008 : addObjectDependency(dobj, preDataBound->dumpId);
19361 8008 : addObjectDependency(postDataBound, dobj->dumpId);
19362 : }
19363 : else
19364 3890 : addObjectDependency(dobj, postDataBound->dumpId);
19365 11898 : break;
19366 : }
19367 1187240 : }
19368 320 : }
19369 :
19370 :
19371 : /*
19372 : * BuildArchiveDependencies - create dependency data for archive TOC entries
19373 : *
19374 : * The raw dependency data obtained by getDependencies() is not terribly
19375 : * useful in an archive dump, because in many cases there are dependency
19376 : * chains linking through objects that don't appear explicitly in the dump.
19377 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
19378 : * will depend on other objects --- but the rule will not appear as a separate
19379 : * object in the dump. We need to adjust the view's dependencies to include
19380 : * whatever the rule depends on that is included in the dump.
19381 : *
19382 : * Just to make things more complicated, there are also "special" dependencies
19383 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
19384 : * not rearrange because pg_restore knows that TABLE DATA only depends on
19385 : * its table. In these cases we must leave the dependencies strictly as-is
19386 : * even if they refer to not-to-be-dumped objects.
19387 : *
19388 : * To handle this, the convention is that "special" dependencies are created
19389 : * during ArchiveEntry calls, and an archive TOC item that has any such
19390 : * entries will not be touched here. Otherwise, we recursively search the
19391 : * DumpableObject data structures to build the correct dependencies for each
19392 : * archive TOC item.
19393 : */
19394 : static void
19395 66 : BuildArchiveDependencies(Archive *fout)
19396 : {
19397 66 : ArchiveHandle *AH = (ArchiveHandle *) fout;
19398 : TocEntry *te;
19399 :
19400 : /* Scan all TOC entries in the archive */
19401 13322 : for (te = AH->toc->next; te != AH->toc; te = te->next)
19402 : {
19403 : DumpableObject *dobj;
19404 : DumpId *dependencies;
19405 : int nDeps;
19406 : int allocDeps;
19407 :
19408 : /* No need to process entries that will not be dumped */
19409 13256 : if (te->reqs == 0)
19410 6310 : continue;
19411 : /* Ignore entries that already have "special" dependencies */
19412 13250 : if (te->nDeps > 0)
19413 5750 : continue;
19414 : /* Otherwise, look up the item's original DumpableObject, if any */
19415 7500 : dobj = findObjectByDumpId(te->dumpId);
19416 7500 : if (dobj == NULL)
19417 342 : continue;
19418 : /* No work if it has no dependencies */
19419 7158 : if (dobj->nDeps <= 0)
19420 212 : continue;
19421 : /* Set up work array */
19422 6946 : allocDeps = 64;
19423 6946 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19424 6946 : nDeps = 0;
19425 : /* Recursively find all dumpable dependencies */
19426 6946 : findDumpableDependencies(AH, dobj,
19427 : &dependencies, &nDeps, &allocDeps);
19428 : /* And save 'em ... */
19429 6946 : if (nDeps > 0)
19430 : {
19431 5320 : dependencies = (DumpId *) pg_realloc(dependencies,
19432 : nDeps * sizeof(DumpId));
19433 5320 : te->dependencies = dependencies;
19434 5320 : te->nDeps = nDeps;
19435 : }
19436 : else
19437 1626 : free(dependencies);
19438 : }
19439 66 : }
19440 :
19441 : /* Recursive search subroutine for BuildArchiveDependencies */
19442 : static void
19443 17018 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19444 : DumpId **dependencies, int *nDeps, int *allocDeps)
19445 : {
19446 : int i;
19447 :
19448 : /*
19449 : * Ignore section boundary objects: if we search through them, we'll
19450 : * report lots of bogus dependencies.
19451 : */
19452 17018 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19453 16980 : dobj->objType == DO_POST_DATA_BOUNDARY)
19454 2996 : return;
19455 :
19456 35428 : for (i = 0; i < dobj->nDeps; i++)
19457 : {
19458 21406 : DumpId depid = dobj->dependencies[i];
19459 :
19460 21406 : if (TocIDRequired(AH, depid) != 0)
19461 : {
19462 : /* Object will be dumped, so just reference it as a dependency */
19463 11334 : if (*nDeps >= *allocDeps)
19464 : {
19465 0 : *allocDeps *= 2;
19466 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
19467 0 : *allocDeps * sizeof(DumpId));
19468 : }
19469 11334 : (*dependencies)[*nDeps] = depid;
19470 11334 : (*nDeps)++;
19471 : }
19472 : else
19473 : {
19474 : /*
19475 : * Object will not be dumped, so recursively consider its deps. We
19476 : * rely on the assumption that sortDumpableObjects already broke
19477 : * any dependency loops, else we might recurse infinitely.
19478 : */
19479 10072 : DumpableObject *otherdobj = findObjectByDumpId(depid);
19480 :
19481 10072 : if (otherdobj)
19482 10072 : findDumpableDependencies(AH, otherdobj,
19483 : dependencies, nDeps, allocDeps);
19484 : }
19485 : }
19486 : }
19487 :
19488 :
19489 : /*
19490 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
19491 : * given type OID.
19492 : *
19493 : * This does not guarantee to schema-qualify the output, so it should not
19494 : * be used to create the target object name for CREATE or ALTER commands.
19495 : *
19496 : * Note that the result is cached and must not be freed by the caller.
19497 : */
19498 : static const char *
19499 4648 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
19500 : {
19501 : TypeInfo *typeInfo;
19502 : char *result;
19503 : PQExpBuffer query;
19504 : PGresult *res;
19505 :
19506 4648 : if (oid == 0)
19507 : {
19508 0 : if ((opts & zeroAsStar) != 0)
19509 0 : return "*";
19510 0 : else if ((opts & zeroAsNone) != 0)
19511 0 : return "NONE";
19512 : }
19513 :
19514 : /* see if we have the result cached in the type's TypeInfo record */
19515 4648 : typeInfo = findTypeByOid(oid);
19516 4648 : if (typeInfo && typeInfo->ftypname)
19517 3686 : return typeInfo->ftypname;
19518 :
19519 962 : query = createPQExpBuffer();
19520 962 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
19521 : oid);
19522 :
19523 962 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
19524 :
19525 : /* result of format_type is already quoted */
19526 962 : result = pg_strdup(PQgetvalue(res, 0, 0));
19527 :
19528 962 : PQclear(res);
19529 962 : destroyPQExpBuffer(query);
19530 :
19531 : /*
19532 : * Cache the result for re-use in later requests, if possible. If we
19533 : * don't have a TypeInfo for the type, the string will be leaked once the
19534 : * caller is done with it ... but that case really should not happen, so
19535 : * leaking if it does seems acceptable.
19536 : */
19537 962 : if (typeInfo)
19538 962 : typeInfo->ftypname = result;
19539 :
19540 962 : return result;
19541 : }
19542 :
19543 : /*
19544 : * Return a column list clause for the given relation.
19545 : *
19546 : * Special case: if there are no undropped columns in the relation, return
19547 : * "", not an invalid "()" column list.
19548 : */
19549 : static const char *
19550 15248 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
19551 : {
19552 15248 : int numatts = ti->numatts;
19553 15248 : char **attnames = ti->attnames;
19554 15248 : bool *attisdropped = ti->attisdropped;
19555 15248 : char *attgenerated = ti->attgenerated;
19556 : bool needComma;
19557 : int i;
19558 :
19559 15248 : appendPQExpBufferChar(buffer, '(');
19560 15248 : needComma = false;
19561 76492 : for (i = 0; i < numatts; i++)
19562 : {
19563 61244 : if (attisdropped[i])
19564 1204 : continue;
19565 60040 : if (attgenerated[i])
19566 2160 : continue;
19567 57880 : if (needComma)
19568 43104 : appendPQExpBufferStr(buffer, ", ");
19569 57880 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
19570 57880 : needComma = true;
19571 : }
19572 :
19573 15248 : if (!needComma)
19574 472 : return ""; /* no undropped columns */
19575 :
19576 14776 : appendPQExpBufferChar(buffer, ')');
19577 14776 : return buffer->data;
19578 : }
19579 :
19580 : /*
19581 : * Check if a reloptions array is nonempty.
19582 : */
19583 : static bool
19584 25822 : nonemptyReloptions(const char *reloptions)
19585 : {
19586 : /* Don't want to print it if it's just "{}" */
19587 25822 : return (reloptions != NULL && strlen(reloptions) > 2);
19588 : }
19589 :
19590 : /*
19591 : * Format a reloptions array and append it to the given buffer.
19592 : *
19593 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
19594 : */
19595 : static void
19596 424 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
19597 : const char *prefix, Archive *fout)
19598 : {
19599 : bool res;
19600 :
19601 424 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
19602 424 : fout->std_strings);
19603 424 : if (!res)
19604 0 : pg_log_warning("could not parse %s array", "reloptions");
19605 424 : }
19606 :
19607 : /*
19608 : * read_dump_filters - retrieve object identifier patterns from file
19609 : *
19610 : * Parse the specified filter file for include and exclude patterns, and add
19611 : * them to the relevant lists. If the filename is "-" then filters will be
19612 : * read from STDIN rather than a file.
19613 : */
19614 : static void
19615 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
19616 : {
19617 : FilterStateData fstate;
19618 : char *objname;
19619 : FilterCommandType comtype;
19620 : FilterObjectType objtype;
19621 :
19622 52 : filter_init(&fstate, filename, exit_nicely);
19623 :
19624 116 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
19625 : {
19626 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
19627 : {
19628 34 : switch (objtype)
19629 : {
19630 0 : case FILTER_OBJECT_TYPE_NONE:
19631 0 : break;
19632 0 : case FILTER_OBJECT_TYPE_DATABASE:
19633 : case FILTER_OBJECT_TYPE_FUNCTION:
19634 : case FILTER_OBJECT_TYPE_INDEX:
19635 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19636 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19637 : case FILTER_OBJECT_TYPE_TRIGGER:
19638 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19639 : "include",
19640 : filter_object_type_name(objtype));
19641 0 : exit_nicely(1);
19642 : break; /* unreachable */
19643 :
19644 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19645 2 : simple_string_list_append(&extension_include_patterns, objname);
19646 2 : break;
19647 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19648 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
19649 2 : break;
19650 2 : case FILTER_OBJECT_TYPE_SCHEMA:
19651 2 : simple_string_list_append(&schema_include_patterns, objname);
19652 2 : dopt->include_everything = false;
19653 2 : break;
19654 26 : case FILTER_OBJECT_TYPE_TABLE:
19655 26 : simple_string_list_append(&table_include_patterns, objname);
19656 26 : dopt->include_everything = false;
19657 26 : break;
19658 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19659 2 : simple_string_list_append(&table_include_patterns_and_children,
19660 : objname);
19661 2 : dopt->include_everything = false;
19662 2 : break;
19663 : }
19664 34 : }
19665 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
19666 : {
19667 18 : switch (objtype)
19668 : {
19669 0 : case FILTER_OBJECT_TYPE_NONE:
19670 0 : break;
19671 2 : case FILTER_OBJECT_TYPE_DATABASE:
19672 : case FILTER_OBJECT_TYPE_FUNCTION:
19673 : case FILTER_OBJECT_TYPE_INDEX:
19674 : case FILTER_OBJECT_TYPE_TRIGGER:
19675 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19676 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19677 : "exclude",
19678 : filter_object_type_name(objtype));
19679 2 : exit_nicely(1);
19680 : break;
19681 :
19682 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19683 2 : simple_string_list_append(&extension_exclude_patterns, objname);
19684 2 : break;
19685 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19686 2 : simple_string_list_append(&tabledata_exclude_patterns,
19687 : objname);
19688 2 : break;
19689 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19690 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
19691 : objname);
19692 2 : break;
19693 4 : case FILTER_OBJECT_TYPE_SCHEMA:
19694 4 : simple_string_list_append(&schema_exclude_patterns, objname);
19695 4 : break;
19696 4 : case FILTER_OBJECT_TYPE_TABLE:
19697 4 : simple_string_list_append(&table_exclude_patterns, objname);
19698 4 : break;
19699 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19700 2 : simple_string_list_append(&table_exclude_patterns_and_children,
19701 : objname);
19702 2 : break;
19703 : }
19704 16 : }
19705 : else
19706 : {
19707 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19708 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19709 : }
19710 :
19711 64 : if (objname)
19712 50 : free(objname);
19713 : }
19714 :
19715 44 : filter_free(&fstate);
19716 44 : }
|