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_constraint_d.h"
51 : #include "catalog/pg_default_acl_d.h"
52 : #include "catalog/pg_largeobject_d.h"
53 : #include "catalog/pg_largeobject_metadata_d.h"
54 : #include "catalog/pg_proc_d.h"
55 : #include "catalog/pg_publication_d.h"
56 : #include "catalog/pg_shdepend_d.h"
57 : #include "catalog/pg_subscription_d.h"
58 : #include "catalog/pg_type_d.h"
59 : #include "common/connect.h"
60 : #include "common/int.h"
61 : #include "common/relpath.h"
62 : #include "common/shortest_dec.h"
63 : #include "compress_io.h"
64 : #include "dumputils.h"
65 : #include "fe_utils/option_utils.h"
66 : #include "fe_utils/string_utils.h"
67 : #include "filter.h"
68 : #include "getopt_long.h"
69 : #include "libpq/libpq-fs.h"
70 : #include "parallel.h"
71 : #include "pg_backup_db.h"
72 : #include "pg_backup_utils.h"
73 : #include "pg_dump.h"
74 : #include "storage/block.h"
75 :
76 : typedef struct
77 : {
78 : Oid roleoid; /* role's OID */
79 : const char *rolename; /* role's name */
80 : } RoleNameItem;
81 :
82 : typedef struct
83 : {
84 : const char *descr; /* comment for an object */
85 : Oid classoid; /* object class (catalog OID) */
86 : Oid objoid; /* object OID */
87 : int objsubid; /* subobject (table column #) */
88 : } CommentItem;
89 :
90 : typedef struct
91 : {
92 : const char *provider; /* label provider of this security label */
93 : const char *label; /* security label for an object */
94 : Oid classoid; /* object class (catalog OID) */
95 : Oid objoid; /* object OID */
96 : int objsubid; /* subobject (table column #) */
97 : } SecLabelItem;
98 :
99 : typedef struct
100 : {
101 : Oid oid; /* object OID */
102 : char relkind; /* object kind */
103 : RelFileNumber relfilenumber; /* object filenode */
104 : Oid toast_oid; /* toast table OID */
105 : RelFileNumber toast_relfilenumber; /* toast table filenode */
106 : Oid toast_index_oid; /* toast table index OID */
107 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
108 : } BinaryUpgradeClassOidItem;
109 :
110 : /* sequence types */
111 : typedef enum SeqType
112 : {
113 : SEQTYPE_SMALLINT,
114 : SEQTYPE_INTEGER,
115 : SEQTYPE_BIGINT,
116 : } SeqType;
117 :
118 : static const char *const SeqTypeNames[] =
119 : {
120 : [SEQTYPE_SMALLINT] = "smallint",
121 : [SEQTYPE_INTEGER] = "integer",
122 : [SEQTYPE_BIGINT] = "bigint",
123 : };
124 :
125 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
126 : "array length mismatch");
127 :
128 : typedef struct
129 : {
130 : Oid oid; /* sequence OID */
131 : SeqType seqtype; /* data type of sequence */
132 : bool cycled; /* whether sequence cycles */
133 : int64 minv; /* minimum value */
134 : int64 maxv; /* maximum value */
135 : int64 startv; /* start value */
136 : int64 incby; /* increment value */
137 : int64 cache; /* cache size */
138 : int64 last_value; /* last value of sequence */
139 : bool is_called; /* whether nextval advances before returning */
140 : } SequenceItem;
141 :
142 : typedef enum OidOptions
143 : {
144 : zeroIsError = 1,
145 : zeroAsStar = 2,
146 : zeroAsNone = 4,
147 : } OidOptions;
148 :
149 : /* global decls */
150 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
151 :
152 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
153 :
154 : /* The specified names/patterns should to match at least one entity */
155 : static int strict_names = 0;
156 :
157 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
158 :
159 : /*
160 : * Object inclusion/exclusion lists
161 : *
162 : * The string lists record the patterns given by command-line switches,
163 : * which we then convert to lists of OIDs of matching objects.
164 : */
165 : static SimpleStringList schema_include_patterns = {NULL, NULL};
166 : static SimpleOidList schema_include_oids = {NULL, NULL};
167 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
168 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
169 :
170 : static SimpleStringList table_include_patterns = {NULL, NULL};
171 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
172 : static SimpleOidList table_include_oids = {NULL, NULL};
173 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
174 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
175 : static SimpleOidList table_exclude_oids = {NULL, NULL};
176 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
177 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
178 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
179 :
180 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
181 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
182 :
183 : static SimpleStringList extension_include_patterns = {NULL, NULL};
184 : static SimpleOidList extension_include_oids = {NULL, NULL};
185 :
186 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
187 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
188 :
189 : static const CatalogId nilCatalogId = {0, 0};
190 :
191 : /* override for standard extra_float_digits setting */
192 : static bool have_extra_float_digits = false;
193 : static int extra_float_digits;
194 :
195 : /* sorted table of role names */
196 : static RoleNameItem *rolenames = NULL;
197 : static int nrolenames = 0;
198 :
199 : /* sorted table of comments */
200 : static CommentItem *comments = NULL;
201 : static int ncomments = 0;
202 :
203 : /* sorted table of security labels */
204 : static SecLabelItem *seclabels = NULL;
205 : static int nseclabels = 0;
206 :
207 : /* sorted table of pg_class information for binary upgrade */
208 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
209 : static int nbinaryUpgradeClassOids = 0;
210 :
211 : /* sorted table of sequences */
212 : static SequenceItem *sequences = NULL;
213 : static int nsequences = 0;
214 :
215 : /*
216 : * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
217 : * as a dependency for pg_shdepend and any large object comments/seclabels.
218 : */
219 : static DumpId lo_metadata_dumpId;
220 :
221 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
222 : #define MAX_ATTR_STATS_RELS 64
223 :
224 : /*
225 : * The default number of rows per INSERT when
226 : * --inserts is specified without --rows-per-insert
227 : */
228 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
229 :
230 : /*
231 : * Maximum number of large objects to group into a single ArchiveEntry.
232 : * At some point we might want to make this user-controllable, but for now
233 : * a hard-wired setting will suffice.
234 : */
235 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
236 :
237 : /*
238 : * Macro for producing quoted, schema-qualified name of a dumpable object.
239 : */
240 : #define fmtQualifiedDumpable(obj) \
241 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
242 : (obj)->dobj.name)
243 :
244 : static void help(const char *progname);
245 : static void setup_connection(Archive *AH,
246 : const char *dumpencoding, const char *dumpsnapshot,
247 : char *use_role);
248 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
249 : static void expand_schema_name_patterns(Archive *fout,
250 : SimpleStringList *patterns,
251 : SimpleOidList *oids,
252 : bool strict_names);
253 : static void expand_extension_name_patterns(Archive *fout,
254 : SimpleStringList *patterns,
255 : SimpleOidList *oids,
256 : bool strict_names);
257 : static void expand_foreign_server_name_patterns(Archive *fout,
258 : SimpleStringList *patterns,
259 : SimpleOidList *oids);
260 : static void expand_table_name_patterns(Archive *fout,
261 : SimpleStringList *patterns,
262 : SimpleOidList *oids,
263 : bool strict_names,
264 : bool with_child_tables);
265 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
266 : const char *pattern);
267 :
268 : static NamespaceInfo *findNamespace(Oid nsoid);
269 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
270 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
271 : static const char *getRoleName(const char *roleoid_str);
272 : static void collectRoleNames(Archive *fout);
273 : static void getAdditionalACLs(Archive *fout);
274 : static void dumpCommentExtended(Archive *fout, const char *type,
275 : const char *name, const char *namespace,
276 : const char *owner, CatalogId catalogId,
277 : int subid, DumpId dumpId,
278 : const char *initdb_comment);
279 : static inline void dumpComment(Archive *fout, const char *type,
280 : const char *name, const char *namespace,
281 : const char *owner, CatalogId catalogId,
282 : int subid, DumpId dumpId);
283 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
284 : static void collectComments(Archive *fout);
285 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
286 : const char *namespace, const char *owner,
287 : CatalogId catalogId, int subid, DumpId dumpId);
288 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
289 : static void collectSecLabels(Archive *fout);
290 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
291 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
292 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
293 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
294 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
295 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
296 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
297 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
298 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
299 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
300 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
301 : PGresult *res);
302 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
303 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
304 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
305 : static void dumpCast(Archive *fout, const CastInfo *cast);
306 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
307 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
308 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
309 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
310 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
311 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
312 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
313 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
314 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
315 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
316 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
317 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
318 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
319 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
320 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
321 : static void collectSequences(Archive *fout);
322 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
323 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
324 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
325 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
326 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
327 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
328 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
329 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
330 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
331 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
332 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
333 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
334 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
335 : static void dumpUserMappings(Archive *fout,
336 : const char *servername, const char *namespace,
337 : const char *owner, CatalogId catalogId, DumpId dumpId);
338 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
339 :
340 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
341 : const char *type, const char *name, const char *subname,
342 : const char *nspname, const char *tag, const char *owner,
343 : const DumpableAcl *dacl);
344 :
345 : static void getDependencies(Archive *fout);
346 : static void BuildArchiveDependencies(Archive *fout);
347 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
348 : DumpId **dependencies, int *nDeps, int *allocDeps);
349 :
350 : static DumpableObject *createBoundaryObjects(void);
351 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
352 : DumpableObject *boundaryObjs);
353 :
354 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
355 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
356 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
357 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
358 : static void buildMatViewRefreshDependencies(Archive *fout);
359 : static void getTableDataFKConstraints(void);
360 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
361 : TableInfo *tbinfo, int j,
362 : int i_notnull_name,
363 : int i_notnull_comment,
364 : int i_notnull_invalidoid,
365 : int i_notnull_noinherit,
366 : int i_notnull_islocal,
367 : PQExpBuffer *invalidnotnulloids);
368 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
369 : bool is_agg);
370 : static char *format_function_signature(Archive *fout,
371 : const FuncInfo *finfo, bool honor_quotes);
372 : static char *convertRegProcReference(const char *proc);
373 : static char *getFormattedOperatorName(const char *oproid);
374 : static char *convertTSFunction(Archive *fout, Oid funcOid);
375 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
376 : static void getLOs(Archive *fout);
377 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
378 : static int dumpLOs(Archive *fout, const void *arg);
379 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
380 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
381 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
382 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
383 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
384 : static void dumpDatabase(Archive *fout);
385 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
386 : const char *dbname, Oid dboid);
387 : static void dumpEncoding(Archive *AH);
388 : static void dumpStdStrings(Archive *AH);
389 : static void dumpSearchPath(Archive *AH);
390 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
391 : PQExpBuffer upgrade_buffer,
392 : Oid pg_type_oid,
393 : bool force_array_type,
394 : bool include_multirange_type);
395 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
396 : PQExpBuffer upgrade_buffer,
397 : const TableInfo *tbinfo);
398 : static void collectBinaryUpgradeClassOids(Archive *fout);
399 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
400 : PQExpBuffer upgrade_buffer,
401 : Oid pg_class_oid);
402 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
403 : const DumpableObject *dobj,
404 : const char *objtype,
405 : const char *objname,
406 : const char *objnamespace);
407 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
408 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
409 : static bool nonemptyReloptions(const char *reloptions);
410 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
411 : const char *prefix, Archive *fout);
412 : static char *get_synchronized_snapshot(Archive *fout);
413 : static void set_restrict_relation_kind(Archive *AH, const char *value);
414 : static void setupDumpWorker(Archive *AH);
415 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
416 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
417 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
418 :
419 :
420 : int
421 580 : main(int argc, char **argv)
422 : {
423 : int c;
424 580 : const char *filename = NULL;
425 580 : const char *format = "p";
426 : TableInfo *tblinfo;
427 : int numTables;
428 : DumpableObject **dobjs;
429 : int numObjs;
430 : DumpableObject *boundaryObjs;
431 : int i;
432 : int optindex;
433 : RestoreOptions *ropt;
434 : Archive *fout; /* the script file */
435 580 : bool g_verbose = false;
436 580 : const char *dumpencoding = NULL;
437 580 : const char *dumpsnapshot = NULL;
438 580 : char *use_role = NULL;
439 580 : int numWorkers = 1;
440 580 : int plainText = 0;
441 580 : ArchiveFormat archiveFormat = archUnknown;
442 : ArchiveMode archiveMode;
443 580 : pg_compress_specification compression_spec = {0};
444 580 : char *compression_detail = NULL;
445 580 : char *compression_algorithm_str = "none";
446 580 : char *error_detail = NULL;
447 580 : bool user_compression_defined = false;
448 580 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
449 580 : bool data_only = false;
450 580 : bool schema_only = false;
451 580 : bool statistics_only = false;
452 580 : bool with_statistics = false;
453 580 : bool no_data = false;
454 580 : bool no_schema = false;
455 580 : bool no_statistics = false;
456 :
457 : static DumpOptions dopt;
458 :
459 : static struct option long_options[] = {
460 : {"data-only", no_argument, NULL, 'a'},
461 : {"blobs", no_argument, NULL, 'b'},
462 : {"large-objects", no_argument, NULL, 'b'},
463 : {"no-blobs", no_argument, NULL, 'B'},
464 : {"no-large-objects", no_argument, NULL, 'B'},
465 : {"clean", no_argument, NULL, 'c'},
466 : {"create", no_argument, NULL, 'C'},
467 : {"dbname", required_argument, NULL, 'd'},
468 : {"extension", required_argument, NULL, 'e'},
469 : {"file", required_argument, NULL, 'f'},
470 : {"format", required_argument, NULL, 'F'},
471 : {"host", required_argument, NULL, 'h'},
472 : {"jobs", 1, NULL, 'j'},
473 : {"no-reconnect", no_argument, NULL, 'R'},
474 : {"no-owner", no_argument, NULL, 'O'},
475 : {"port", required_argument, NULL, 'p'},
476 : {"schema", required_argument, NULL, 'n'},
477 : {"exclude-schema", required_argument, NULL, 'N'},
478 : {"schema-only", no_argument, NULL, 's'},
479 : {"superuser", required_argument, NULL, 'S'},
480 : {"table", required_argument, NULL, 't'},
481 : {"exclude-table", required_argument, NULL, 'T'},
482 : {"no-password", no_argument, NULL, 'w'},
483 : {"password", no_argument, NULL, 'W'},
484 : {"username", required_argument, NULL, 'U'},
485 : {"verbose", no_argument, NULL, 'v'},
486 : {"no-privileges", no_argument, NULL, 'x'},
487 : {"no-acl", no_argument, NULL, 'x'},
488 : {"compress", required_argument, NULL, 'Z'},
489 : {"encoding", required_argument, NULL, 'E'},
490 : {"help", no_argument, NULL, '?'},
491 : {"version", no_argument, NULL, 'V'},
492 :
493 : /*
494 : * the following options don't have an equivalent short option letter
495 : */
496 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
497 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
498 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
499 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
500 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
501 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
502 : {"exclude-table-data", required_argument, NULL, 4},
503 : {"extra-float-digits", required_argument, NULL, 8},
504 : {"if-exists", no_argument, &dopt.if_exists, 1},
505 : {"inserts", no_argument, NULL, 9},
506 : {"lock-wait-timeout", required_argument, NULL, 2},
507 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
508 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
509 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
510 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
511 : {"role", required_argument, NULL, 3},
512 : {"section", required_argument, NULL, 5},
513 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
514 : {"snapshot", required_argument, NULL, 6},
515 : {"statistics", no_argument, NULL, 22},
516 : {"statistics-only", no_argument, NULL, 18},
517 : {"strict-names", no_argument, &strict_names, 1},
518 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
519 : {"no-comments", no_argument, &dopt.no_comments, 1},
520 : {"no-data", no_argument, NULL, 19},
521 : {"no-policies", no_argument, &dopt.no_policies, 1},
522 : {"no-publications", no_argument, &dopt.no_publications, 1},
523 : {"no-schema", no_argument, NULL, 20},
524 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
525 : {"no-statistics", no_argument, NULL, 21},
526 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
527 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
528 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
529 : {"no-sync", no_argument, NULL, 7},
530 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
531 : {"rows-per-insert", required_argument, NULL, 10},
532 : {"include-foreign-data", required_argument, NULL, 11},
533 : {"table-and-children", required_argument, NULL, 12},
534 : {"exclude-table-and-children", required_argument, NULL, 13},
535 : {"exclude-table-data-and-children", required_argument, NULL, 14},
536 : {"sync-method", required_argument, NULL, 15},
537 : {"filter", required_argument, NULL, 16},
538 : {"exclude-extension", required_argument, NULL, 17},
539 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
540 : {"restrict-key", required_argument, NULL, 25},
541 :
542 : {NULL, 0, NULL, 0}
543 : };
544 :
545 580 : pg_logging_init(argv[0]);
546 580 : pg_logging_set_level(PG_LOG_WARNING);
547 580 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
548 :
549 : /*
550 : * Initialize what we need for parallel execution, especially for thread
551 : * support on Windows.
552 : */
553 580 : init_parallel_dump_utils();
554 :
555 580 : progname = get_progname(argv[0]);
556 :
557 580 : if (argc > 1)
558 : {
559 580 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 : {
561 2 : help(progname);
562 2 : exit_nicely(0);
563 : }
564 578 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 : {
566 124 : puts("pg_dump (PostgreSQL) " PG_VERSION);
567 124 : exit_nicely(0);
568 : }
569 : }
570 :
571 454 : InitDumpOptions(&dopt);
572 :
573 2520 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 2520 : long_options, &optindex)) != -1)
575 : {
576 2082 : switch (c)
577 : {
578 18 : case 'a': /* Dump data only */
579 18 : data_only = true;
580 18 : break;
581 :
582 2 : case 'b': /* Dump LOs */
583 2 : dopt.outputLOs = true;
584 2 : break;
585 :
586 4 : case 'B': /* Don't dump LOs */
587 4 : dopt.dontOutputLOs = true;
588 4 : break;
589 :
590 12 : case 'c': /* clean (i.e., drop) schema prior to create */
591 12 : dopt.outputClean = 1;
592 12 : break;
593 :
594 58 : case 'C': /* Create DB */
595 58 : dopt.outputCreateDB = 1;
596 58 : break;
597 :
598 10 : case 'd': /* database name */
599 10 : dopt.cparams.dbname = pg_strdup(optarg);
600 10 : break;
601 :
602 8 : case 'e': /* include extension(s) */
603 8 : simple_string_list_append(&extension_include_patterns, optarg);
604 8 : dopt.include_everything = false;
605 8 : break;
606 :
607 4 : case 'E': /* Dump encoding */
608 4 : dumpencoding = pg_strdup(optarg);
609 4 : break;
610 :
611 360 : case 'f':
612 360 : filename = pg_strdup(optarg);
613 360 : break;
614 :
615 216 : case 'F':
616 216 : format = pg_strdup(optarg);
617 216 : break;
618 :
619 68 : case 'h': /* server host */
620 68 : dopt.cparams.pghost = pg_strdup(optarg);
621 68 : break;
622 :
623 22 : case 'j': /* number of dump jobs */
624 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
625 : PG_MAX_JOBS,
626 : &numWorkers))
627 2 : exit_nicely(1);
628 20 : break;
629 :
630 34 : case 'n': /* include schema(s) */
631 34 : simple_string_list_append(&schema_include_patterns, optarg);
632 34 : dopt.include_everything = false;
633 34 : break;
634 :
635 2 : case 'N': /* exclude schema(s) */
636 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
637 2 : break;
638 :
639 4 : case 'O': /* Don't reconnect to match owner */
640 4 : dopt.outputNoOwner = 1;
641 4 : break;
642 :
643 146 : case 'p': /* server port */
644 146 : dopt.cparams.pgport = pg_strdup(optarg);
645 146 : break;
646 :
647 4 : case 'R':
648 : /* no-op, still accepted for backwards compatibility */
649 4 : break;
650 :
651 14 : case 's': /* dump schema only */
652 14 : schema_only = true;
653 14 : break;
654 :
655 2 : case 'S': /* Username for superuser in plain text output */
656 2 : dopt.outputSuperuser = pg_strdup(optarg);
657 2 : break;
658 :
659 16 : case 't': /* include table(s) */
660 16 : simple_string_list_append(&table_include_patterns, optarg);
661 16 : dopt.include_everything = false;
662 16 : break;
663 :
664 8 : case 'T': /* exclude table(s) */
665 8 : simple_string_list_append(&table_exclude_patterns, optarg);
666 8 : break;
667 :
668 72 : case 'U':
669 72 : dopt.cparams.username = pg_strdup(optarg);
670 72 : break;
671 :
672 12 : case 'v': /* verbose */
673 12 : g_verbose = true;
674 12 : pg_logging_increase_verbosity();
675 12 : break;
676 :
677 2 : case 'w':
678 2 : dopt.cparams.promptPassword = TRI_NO;
679 2 : break;
680 :
681 0 : case 'W':
682 0 : dopt.cparams.promptPassword = TRI_YES;
683 0 : break;
684 :
685 4 : case 'x': /* skip ACL dump */
686 4 : dopt.aclsSkip = true;
687 4 : break;
688 :
689 24 : case 'Z': /* Compression */
690 24 : parse_compress_options(optarg, &compression_algorithm_str,
691 : &compression_detail);
692 24 : user_compression_defined = true;
693 24 : break;
694 :
695 256 : case 0:
696 : /* This covers the long options. */
697 256 : break;
698 :
699 4 : case 2: /* lock-wait-timeout */
700 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
701 4 : break;
702 :
703 6 : case 3: /* SET ROLE */
704 6 : use_role = pg_strdup(optarg);
705 6 : break;
706 :
707 2 : case 4: /* exclude table(s) data */
708 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
709 2 : break;
710 :
711 12 : case 5: /* section */
712 12 : set_dump_section(optarg, &dopt.dumpSections);
713 12 : break;
714 :
715 0 : case 6: /* snapshot */
716 0 : dumpsnapshot = pg_strdup(optarg);
717 0 : break;
718 :
719 276 : case 7: /* no-sync */
720 276 : dosync = false;
721 276 : break;
722 :
723 2 : case 8:
724 2 : have_extra_float_digits = true;
725 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
726 : &extra_float_digits))
727 2 : exit_nicely(1);
728 0 : break;
729 :
730 4 : case 9: /* inserts */
731 :
732 : /*
733 : * dump_inserts also stores --rows-per-insert, careful not to
734 : * overwrite that.
735 : */
736 4 : if (dopt.dump_inserts == 0)
737 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
738 4 : break;
739 :
740 4 : case 10: /* rows per insert */
741 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
742 : &dopt.dump_inserts))
743 2 : exit_nicely(1);
744 2 : break;
745 :
746 8 : case 11: /* include foreign data */
747 8 : simple_string_list_append(&foreign_servers_include_patterns,
748 : optarg);
749 8 : break;
750 :
751 2 : case 12: /* include table(s) and their children */
752 2 : simple_string_list_append(&table_include_patterns_and_children,
753 : optarg);
754 2 : dopt.include_everything = false;
755 2 : break;
756 :
757 2 : case 13: /* exclude table(s) and their children */
758 2 : simple_string_list_append(&table_exclude_patterns_and_children,
759 : optarg);
760 2 : break;
761 :
762 2 : case 14: /* exclude data of table(s) and children */
763 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
764 : optarg);
765 2 : break;
766 :
767 0 : case 15:
768 0 : if (!parse_sync_method(optarg, &sync_method))
769 0 : exit_nicely(1);
770 0 : break;
771 :
772 52 : case 16: /* read object filters from file */
773 52 : read_dump_filters(optarg, &dopt);
774 44 : break;
775 :
776 2 : case 17: /* exclude extension(s) */
777 2 : simple_string_list_append(&extension_exclude_patterns,
778 : optarg);
779 2 : break;
780 :
781 8 : case 18:
782 8 : statistics_only = true;
783 8 : break;
784 :
785 72 : case 19:
786 72 : no_data = true;
787 72 : break;
788 :
789 4 : case 20:
790 4 : no_schema = true;
791 4 : break;
792 :
793 16 : case 21:
794 16 : no_statistics = true;
795 16 : break;
796 :
797 168 : case 22:
798 168 : with_statistics = true;
799 168 : break;
800 :
801 52 : case 25:
802 52 : dopt.restrict_key = pg_strdup(optarg);
803 52 : break;
804 :
805 2 : default:
806 : /* getopt_long already emitted a complaint */
807 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
808 2 : exit_nicely(1);
809 : }
810 : }
811 :
812 : /*
813 : * Non-option argument specifies database name as long as it wasn't
814 : * already specified with -d / --dbname
815 : */
816 438 : if (optind < argc && dopt.cparams.dbname == NULL)
817 366 : dopt.cparams.dbname = argv[optind++];
818 :
819 : /* Complain if any arguments remain */
820 438 : if (optind < argc)
821 : {
822 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
823 : argv[optind]);
824 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
825 2 : exit_nicely(1);
826 : }
827 :
828 : /* --column-inserts implies --inserts */
829 436 : if (dopt.column_inserts && dopt.dump_inserts == 0)
830 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
831 :
832 : /* reject conflicting "-only" options */
833 436 : if (data_only && schema_only)
834 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
835 434 : if (schema_only && statistics_only)
836 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
837 432 : if (data_only && statistics_only)
838 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
839 :
840 : /* reject conflicting "-only" and "no-" options */
841 430 : if (data_only && no_data)
842 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
843 430 : if (schema_only && no_schema)
844 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
845 430 : if (statistics_only && no_statistics)
846 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
847 :
848 : /* reject conflicting "no-" options */
849 428 : if (with_statistics && no_statistics)
850 0 : pg_fatal("options --statistics and --no-statistics cannot be used together");
851 :
852 : /* reject conflicting "-only" options */
853 428 : if (data_only && with_statistics)
854 0 : pg_fatal("options %s and %s cannot be used together",
855 : "-a/--data-only", "--statistics");
856 428 : if (schema_only && with_statistics)
857 2 : pg_fatal("options %s and %s cannot be used together",
858 : "-s/--schema-only", "--statistics");
859 :
860 426 : if (schema_only && foreign_servers_include_patterns.head != NULL)
861 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
862 :
863 424 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
864 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
865 :
866 422 : if (data_only && dopt.outputClean)
867 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
868 :
869 420 : if (dopt.if_exists && !dopt.outputClean)
870 2 : pg_fatal("option --if-exists requires option -c/--clean");
871 :
872 : /*
873 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
874 : * "--schema-only --no-schema", will have already caused an error in one
875 : * of the checks above.
876 : */
877 418 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
878 836 : data_only) && !no_data;
879 418 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
880 836 : schema_only) && !no_schema;
881 418 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
882 836 : (statistics_only || with_statistics)) && !no_statistics;
883 :
884 :
885 : /*
886 : * --inserts are already implied above if --column-inserts or
887 : * --rows-per-insert were specified.
888 : */
889 418 : if (dopt.do_nothing && dopt.dump_inserts == 0)
890 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
891 :
892 : /* Identify archive format to emit */
893 416 : archiveFormat = parseArchiveFormat(format, &archiveMode);
894 :
895 : /* archiveFormat specific setup */
896 414 : if (archiveFormat == archNull)
897 : {
898 306 : plainText = 1;
899 :
900 : /*
901 : * If you don't provide a restrict key, one will be appointed for you.
902 : */
903 306 : if (!dopt.restrict_key)
904 254 : dopt.restrict_key = generate_restrict_key();
905 306 : if (!dopt.restrict_key)
906 0 : pg_fatal("could not generate restrict key");
907 306 : if (!valid_restrict_key(dopt.restrict_key))
908 0 : pg_fatal("invalid restrict key");
909 : }
910 108 : else if (dopt.restrict_key)
911 0 : pg_fatal("option --restrict-key can only be used with --format=plain");
912 :
913 : /*
914 : * Custom and directory formats are compressed by default with gzip when
915 : * available, not the others. If gzip is not available, no compression is
916 : * done by default.
917 : */
918 414 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
919 102 : !user_compression_defined)
920 : {
921 : #ifdef HAVE_LIBZ
922 92 : compression_algorithm_str = "gzip";
923 : #else
924 : compression_algorithm_str = "none";
925 : #endif
926 : }
927 :
928 : /*
929 : * Compression options
930 : */
931 414 : if (!parse_compress_algorithm(compression_algorithm_str,
932 : &compression_algorithm))
933 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
934 : compression_algorithm_str);
935 :
936 412 : parse_compress_specification(compression_algorithm, compression_detail,
937 : &compression_spec);
938 412 : error_detail = validate_compress_specification(&compression_spec);
939 412 : if (error_detail != NULL)
940 6 : pg_fatal("invalid compression specification: %s",
941 : error_detail);
942 :
943 406 : error_detail = supports_compression(compression_spec);
944 406 : if (error_detail != NULL)
945 0 : pg_fatal("%s", error_detail);
946 :
947 : /*
948 : * Disable support for zstd workers for now - these are based on
949 : * threading, and it's unclear how it interacts with parallel dumps on
950 : * platforms where that relies on threads too (e.g. Windows).
951 : */
952 406 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
953 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
954 : "workers");
955 :
956 : /*
957 : * If emitting an archive format, we always want to emit a DATABASE item,
958 : * in case --create is specified at pg_restore time.
959 : */
960 406 : if (!plainText)
961 108 : dopt.outputCreateDB = 1;
962 :
963 : /* Parallel backup only in the directory archive format so far */
964 406 : if (archiveFormat != archDirectory && numWorkers > 1)
965 2 : pg_fatal("parallel backup only supported by the directory format");
966 :
967 : /* Open the output file */
968 404 : fout = CreateArchive(filename, archiveFormat, compression_spec,
969 : dosync, archiveMode, setupDumpWorker, sync_method);
970 :
971 : /* Make dump options accessible right away */
972 402 : SetArchiveOptions(fout, &dopt, NULL);
973 :
974 : /* Register the cleanup hook */
975 402 : on_exit_close_archive(fout);
976 :
977 : /* Let the archiver know how noisy to be */
978 402 : fout->verbose = g_verbose;
979 :
980 :
981 : /*
982 : * We allow the server to be back to 9.2, and up to any minor release of
983 : * our own major version. (See also version check in pg_dumpall.c.)
984 : */
985 402 : fout->minRemoteVersion = 90200;
986 402 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
987 :
988 402 : fout->numWorkers = numWorkers;
989 :
990 : /*
991 : * Open the database using the Archiver, so it knows about it. Errors mean
992 : * death.
993 : */
994 402 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
995 398 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
996 :
997 : /*
998 : * On hot standbys, never try to dump unlogged table data, since it will
999 : * just throw an error.
1000 : */
1001 398 : if (fout->isStandby)
1002 8 : dopt.no_unlogged_table_data = true;
1003 :
1004 : /*
1005 : * Find the last built-in OID, if needed (prior to 8.1)
1006 : *
1007 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1008 : */
1009 398 : g_last_builtin_oid = FirstNormalObjectId - 1;
1010 :
1011 398 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1012 :
1013 : /* Expand schema selection patterns into OID lists */
1014 398 : if (schema_include_patterns.head != NULL)
1015 : {
1016 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1017 : &schema_include_oids,
1018 : strict_names);
1019 24 : if (schema_include_oids.head == NULL)
1020 2 : pg_fatal("no matching schemas were found");
1021 : }
1022 384 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1023 : &schema_exclude_oids,
1024 : false);
1025 : /* non-matching exclusion patterns aren't an error */
1026 :
1027 : /* Expand table selection patterns into OID lists */
1028 384 : expand_table_name_patterns(fout, &table_include_patterns,
1029 : &table_include_oids,
1030 : strict_names, false);
1031 374 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1032 : &table_include_oids,
1033 : strict_names, true);
1034 374 : if ((table_include_patterns.head != NULL ||
1035 352 : table_include_patterns_and_children.head != NULL) &&
1036 26 : table_include_oids.head == NULL)
1037 4 : pg_fatal("no matching tables were found");
1038 :
1039 370 : expand_table_name_patterns(fout, &table_exclude_patterns,
1040 : &table_exclude_oids,
1041 : false, false);
1042 370 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1043 : &table_exclude_oids,
1044 : false, true);
1045 :
1046 370 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1047 : &tabledata_exclude_oids,
1048 : false, false);
1049 370 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1050 : &tabledata_exclude_oids,
1051 : false, true);
1052 :
1053 370 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1054 : &foreign_servers_include_oids);
1055 :
1056 : /* non-matching exclusion patterns aren't an error */
1057 :
1058 : /* Expand extension selection patterns into OID lists */
1059 368 : if (extension_include_patterns.head != NULL)
1060 : {
1061 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1062 : &extension_include_oids,
1063 : strict_names);
1064 10 : if (extension_include_oids.head == NULL)
1065 2 : pg_fatal("no matching extensions were found");
1066 : }
1067 366 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1068 : &extension_exclude_oids,
1069 : false);
1070 : /* non-matching exclusion patterns aren't an error */
1071 :
1072 : /*
1073 : * Dumping LOs is the default for dumps where an inclusion switch is not
1074 : * used (an "include everything" dump). -B can be used to exclude LOs
1075 : * from those dumps. -b can be used to include LOs even when an inclusion
1076 : * switch is used.
1077 : *
1078 : * -s means "schema only" and LOs are data, not schema, so we never
1079 : * include LOs when -s is used.
1080 : */
1081 366 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1082 236 : dopt.outputLOs = true;
1083 :
1084 : /*
1085 : * Collect role names so we can map object owner OIDs to names.
1086 : */
1087 366 : collectRoleNames(fout);
1088 :
1089 : /*
1090 : * Now scan the database and create DumpableObject structs for all the
1091 : * objects we intend to dump.
1092 : */
1093 366 : tblinfo = getSchemaData(fout, &numTables);
1094 :
1095 364 : if (dopt.dumpData)
1096 : {
1097 284 : getTableData(&dopt, tblinfo, numTables, 0);
1098 284 : buildMatViewRefreshDependencies(fout);
1099 284 : if (!dopt.dumpSchema)
1100 14 : getTableDataFKConstraints();
1101 : }
1102 :
1103 364 : if (!dopt.dumpData && dopt.sequence_data)
1104 64 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1105 :
1106 : /*
1107 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1108 : * associated pg_shdepend rows. This is faster to restore than the
1109 : * equivalent set of large object commands. We can only do this for
1110 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1111 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1112 : */
1113 364 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1114 : {
1115 72 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1116 72 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1117 :
1118 72 : makeTableDataInfo(&dopt, lo_metadata);
1119 72 : makeTableDataInfo(&dopt, shdepend);
1120 :
1121 : /*
1122 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1123 : * pg_shdepend and any large object comments/seclabels.
1124 : */
1125 72 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1126 72 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1127 :
1128 : /*
1129 : * Only dump large object shdepend rows for this database.
1130 : */
1131 72 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1132 : "AND dbid = (SELECT oid FROM pg_database "
1133 : " WHERE datname = current_database())";
1134 : }
1135 :
1136 : /*
1137 : * In binary-upgrade mode, we do not have to worry about the actual LO
1138 : * data or the associated metadata that resides in the pg_largeobject and
1139 : * pg_largeobject_metadata tables, respectively.
1140 : *
1141 : * However, we do need to collect LO information as there may be comments
1142 : * or other information on LOs that we do need to dump out.
1143 : */
1144 364 : if (dopt.outputLOs || dopt.binary_upgrade)
1145 308 : getLOs(fout);
1146 :
1147 : /*
1148 : * Collect dependency data to assist in ordering the objects.
1149 : */
1150 364 : getDependencies(fout);
1151 :
1152 : /*
1153 : * Collect ACLs, comments, and security labels, if wanted.
1154 : */
1155 364 : if (!dopt.aclsSkip)
1156 360 : getAdditionalACLs(fout);
1157 364 : if (!dopt.no_comments)
1158 364 : collectComments(fout);
1159 364 : if (!dopt.no_security_labels)
1160 364 : collectSecLabels(fout);
1161 :
1162 : /* For binary upgrade mode, collect required pg_class information. */
1163 364 : if (dopt.binary_upgrade)
1164 72 : collectBinaryUpgradeClassOids(fout);
1165 :
1166 : /* Collect sequence information. */
1167 364 : collectSequences(fout);
1168 :
1169 : /* Lastly, create dummy objects to represent the section boundaries */
1170 364 : boundaryObjs = createBoundaryObjects();
1171 :
1172 : /* Get pointers to all the known DumpableObjects */
1173 364 : getDumpableObjects(&dobjs, &numObjs);
1174 :
1175 : /*
1176 : * Add dummy dependencies to enforce the dump section ordering.
1177 : */
1178 364 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1179 :
1180 : /*
1181 : * Sort the objects into a safe dump order (no forward references).
1182 : *
1183 : * We rely on dependency information to help us determine a safe order, so
1184 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1185 : * ensure that logically identical schemas will dump identically.
1186 : */
1187 364 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1188 :
1189 364 : sortDumpableObjects(dobjs, numObjs,
1190 364 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1191 :
1192 : /*
1193 : * Create archive TOC entries for all the objects to be dumped, in a safe
1194 : * order.
1195 : */
1196 :
1197 : /*
1198 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1199 : */
1200 364 : dumpEncoding(fout);
1201 364 : dumpStdStrings(fout);
1202 364 : dumpSearchPath(fout);
1203 :
1204 : /* The database items are always next, unless we don't want them at all */
1205 364 : if (dopt.outputCreateDB)
1206 164 : dumpDatabase(fout);
1207 :
1208 : /* Now the rearrangeable objects. */
1209 1355506 : for (i = 0; i < numObjs; i++)
1210 1355142 : dumpDumpableObject(fout, dobjs[i]);
1211 :
1212 : /*
1213 : * Set up options info to ensure we dump what we want.
1214 : */
1215 364 : ropt = NewRestoreOptions();
1216 364 : ropt->filename = filename;
1217 :
1218 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1219 364 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1220 364 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1221 364 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1222 364 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1223 364 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1224 364 : ropt->dropSchema = dopt.outputClean;
1225 364 : ropt->dumpData = dopt.dumpData;
1226 364 : ropt->dumpSchema = dopt.dumpSchema;
1227 364 : ropt->dumpStatistics = dopt.dumpStatistics;
1228 364 : ropt->if_exists = dopt.if_exists;
1229 364 : ropt->column_inserts = dopt.column_inserts;
1230 364 : ropt->dumpSections = dopt.dumpSections;
1231 364 : ropt->aclsSkip = dopt.aclsSkip;
1232 364 : ropt->superuser = dopt.outputSuperuser;
1233 364 : ropt->createDB = dopt.outputCreateDB;
1234 364 : ropt->noOwner = dopt.outputNoOwner;
1235 364 : ropt->noTableAm = dopt.outputNoTableAm;
1236 364 : ropt->noTablespace = dopt.outputNoTablespaces;
1237 364 : ropt->disable_triggers = dopt.disable_triggers;
1238 364 : ropt->use_setsessauth = dopt.use_setsessauth;
1239 364 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1240 364 : ropt->dump_inserts = dopt.dump_inserts;
1241 364 : ropt->no_comments = dopt.no_comments;
1242 364 : ropt->no_policies = dopt.no_policies;
1243 364 : ropt->no_publications = dopt.no_publications;
1244 364 : ropt->no_security_labels = dopt.no_security_labels;
1245 364 : ropt->no_subscriptions = dopt.no_subscriptions;
1246 364 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1247 364 : ropt->include_everything = dopt.include_everything;
1248 364 : ropt->enable_row_security = dopt.enable_row_security;
1249 364 : ropt->sequence_data = dopt.sequence_data;
1250 364 : ropt->binary_upgrade = dopt.binary_upgrade;
1251 364 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1252 :
1253 364 : ropt->compression_spec = compression_spec;
1254 :
1255 364 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1256 :
1257 364 : SetArchiveOptions(fout, &dopt, ropt);
1258 :
1259 : /* Mark which entries should be output */
1260 364 : ProcessArchiveRestoreOptions(fout);
1261 :
1262 : /*
1263 : * The archive's TOC entries are now marked as to which ones will actually
1264 : * be output, so we can set up their dependency lists properly. This isn't
1265 : * necessary for plain-text output, though.
1266 : */
1267 364 : if (!plainText)
1268 106 : BuildArchiveDependencies(fout);
1269 :
1270 : /*
1271 : * And finally we can do the actual output.
1272 : *
1273 : * Note: for non-plain-text output formats, the output file is written
1274 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1275 : * right now.
1276 : */
1277 364 : if (plainText)
1278 258 : RestoreArchive(fout);
1279 :
1280 362 : CloseArchive(fout);
1281 :
1282 362 : exit_nicely(0);
1283 : }
1284 :
1285 :
1286 : static void
1287 2 : help(const char *progname)
1288 : {
1289 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1290 2 : printf(_("Usage:\n"));
1291 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1292 :
1293 2 : printf(_("\nGeneral options:\n"));
1294 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1295 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1296 : " plain text (default))\n"));
1297 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1298 2 : printf(_(" -v, --verbose verbose mode\n"));
1299 2 : printf(_(" -V, --version output version information, then exit\n"));
1300 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1301 : " compress as specified\n"));
1302 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1303 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1304 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1305 2 : printf(_(" -?, --help show this help, then exit\n"));
1306 :
1307 2 : printf(_("\nOptions controlling the output content:\n"));
1308 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1309 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1310 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1311 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1312 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1313 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1314 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1315 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1316 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1317 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1318 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1319 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1320 : " plain-text format\n"));
1321 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1322 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1323 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1324 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1325 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1326 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1327 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1328 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1329 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1330 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1331 : " access to)\n"));
1332 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1333 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1334 : " do NOT dump the specified table(s), including\n"
1335 : " child and partition tables\n"));
1336 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1337 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1338 : " do NOT dump data for the specified table(s),\n"
1339 : " including child and partition tables\n"));
1340 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1341 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1342 : " based on expressions in FILENAME\n"));
1343 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1344 2 : printf(_(" --include-foreign-data=PATTERN\n"
1345 : " include data of foreign tables on foreign\n"
1346 : " servers matching PATTERN\n"));
1347 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1348 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1349 2 : printf(_(" --no-comments do not dump comment commands\n"));
1350 2 : printf(_(" --no-data do not dump data\n"));
1351 2 : printf(_(" --no-policies do not dump row security policies\n"));
1352 2 : printf(_(" --no-publications do not dump publications\n"));
1353 2 : printf(_(" --no-schema do not dump schema\n"));
1354 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1355 2 : printf(_(" --no-statistics do not dump statistics\n"));
1356 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1357 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1358 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1359 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1360 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1361 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1362 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1363 2 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1364 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1365 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1366 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1367 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1368 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1369 2 : printf(_(" --statistics dump the statistics\n"));
1370 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1371 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1372 : " match at least one entity each\n"));
1373 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1374 : " child and partition tables\n"));
1375 2 : printf(_(" --use-set-session-authorization\n"
1376 : " use SET SESSION AUTHORIZATION commands instead of\n"
1377 : " ALTER OWNER commands to set ownership\n"));
1378 :
1379 2 : printf(_("\nConnection options:\n"));
1380 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1381 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1382 2 : printf(_(" -p, --port=PORT database server port number\n"));
1383 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1384 2 : printf(_(" -w, --no-password never prompt for password\n"));
1385 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1386 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1387 :
1388 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1389 : "variable value is used.\n\n"));
1390 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1391 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1392 2 : }
1393 :
1394 : static void
1395 430 : setup_connection(Archive *AH, const char *dumpencoding,
1396 : const char *dumpsnapshot, char *use_role)
1397 : {
1398 430 : DumpOptions *dopt = AH->dopt;
1399 430 : PGconn *conn = GetConnection(AH);
1400 : const char *std_strings;
1401 :
1402 430 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1403 :
1404 : /*
1405 : * Set the client encoding if requested.
1406 : */
1407 430 : if (dumpencoding)
1408 : {
1409 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1410 0 : pg_fatal("invalid client encoding \"%s\" specified",
1411 : dumpencoding);
1412 : }
1413 :
1414 : /*
1415 : * Get the active encoding and the standard_conforming_strings setting, so
1416 : * we know how to escape strings.
1417 : */
1418 430 : AH->encoding = PQclientEncoding(conn);
1419 430 : setFmtEncoding(AH->encoding);
1420 :
1421 430 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1422 430 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1423 :
1424 : /*
1425 : * Set the role if requested. In a parallel dump worker, we'll be passed
1426 : * use_role == NULL, but AH->use_role is already set (if user specified it
1427 : * originally) and we should use that.
1428 : */
1429 430 : if (!use_role && AH->use_role)
1430 4 : use_role = AH->use_role;
1431 :
1432 : /* Set the role if requested */
1433 430 : if (use_role)
1434 : {
1435 10 : PQExpBuffer query = createPQExpBuffer();
1436 :
1437 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1438 10 : ExecuteSqlStatement(AH, query->data);
1439 10 : destroyPQExpBuffer(query);
1440 :
1441 : /* save it for possible later use by parallel workers */
1442 10 : if (!AH->use_role)
1443 6 : AH->use_role = pg_strdup(use_role);
1444 : }
1445 :
1446 : /* Set the datestyle to ISO to ensure the dump's portability */
1447 430 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1448 :
1449 : /* Likewise, avoid using sql_standard intervalstyle */
1450 430 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1451 :
1452 : /*
1453 : * Use an explicitly specified extra_float_digits if it has been provided.
1454 : * Otherwise, set extra_float_digits so that we can dump float data
1455 : * exactly (given correctly implemented float I/O code, anyway).
1456 : */
1457 430 : if (have_extra_float_digits)
1458 : {
1459 0 : PQExpBuffer q = createPQExpBuffer();
1460 :
1461 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1462 : extra_float_digits);
1463 0 : ExecuteSqlStatement(AH, q->data);
1464 0 : destroyPQExpBuffer(q);
1465 : }
1466 : else
1467 430 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1468 :
1469 : /*
1470 : * Disable synchronized scanning, to prevent unpredictable changes in row
1471 : * ordering across a dump and reload.
1472 : */
1473 430 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1474 :
1475 : /*
1476 : * Disable timeouts if supported.
1477 : */
1478 430 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1479 430 : if (AH->remoteVersion >= 90300)
1480 430 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1481 430 : if (AH->remoteVersion >= 90600)
1482 430 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1483 430 : if (AH->remoteVersion >= 170000)
1484 430 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1485 :
1486 : /*
1487 : * Quote all identifiers, if requested.
1488 : */
1489 430 : if (quote_all_identifiers)
1490 68 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1491 :
1492 : /*
1493 : * Adjust row-security mode, if supported.
1494 : */
1495 430 : if (AH->remoteVersion >= 90500)
1496 : {
1497 430 : if (dopt->enable_row_security)
1498 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1499 : else
1500 430 : ExecuteSqlStatement(AH, "SET row_security = off");
1501 : }
1502 :
1503 : /*
1504 : * For security reasons, we restrict the expansion of non-system views and
1505 : * access to foreign tables during the pg_dump process. This restriction
1506 : * is adjusted when dumping foreign table data.
1507 : */
1508 430 : set_restrict_relation_kind(AH, "view, foreign-table");
1509 :
1510 : /*
1511 : * Initialize prepared-query state to "nothing prepared". We do this here
1512 : * so that a parallel dump worker will have its own state.
1513 : */
1514 430 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1515 :
1516 : /*
1517 : * Start transaction-snapshot mode transaction to dump consistent data.
1518 : */
1519 430 : ExecuteSqlStatement(AH, "BEGIN");
1520 :
1521 : /*
1522 : * To support the combination of serializable_deferrable with the jobs
1523 : * option we use REPEATABLE READ for the worker connections that are
1524 : * passed a snapshot. As long as the snapshot is acquired in a
1525 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1526 : * REPEATABLE READ transaction provides the appropriate integrity
1527 : * guarantees. This is a kluge, but safe for back-patching.
1528 : */
1529 430 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1530 0 : ExecuteSqlStatement(AH,
1531 : "SET TRANSACTION ISOLATION LEVEL "
1532 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1533 : else
1534 430 : ExecuteSqlStatement(AH,
1535 : "SET TRANSACTION ISOLATION LEVEL "
1536 : "REPEATABLE READ, READ ONLY");
1537 :
1538 : /*
1539 : * If user specified a snapshot to use, select that. In a parallel dump
1540 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1541 : * is already set (if the server can handle it) and we should use that.
1542 : */
1543 430 : if (dumpsnapshot)
1544 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1545 :
1546 430 : if (AH->sync_snapshot_id)
1547 : {
1548 32 : PQExpBuffer query = createPQExpBuffer();
1549 :
1550 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1551 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1552 32 : ExecuteSqlStatement(AH, query->data);
1553 32 : destroyPQExpBuffer(query);
1554 : }
1555 398 : else if (AH->numWorkers > 1)
1556 : {
1557 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1558 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1559 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1560 : }
1561 430 : }
1562 :
1563 : /* Set up connection for a parallel worker process */
1564 : static void
1565 32 : setupDumpWorker(Archive *AH)
1566 : {
1567 : /*
1568 : * We want to re-select all the same values the leader connection is
1569 : * using. We'll have inherited directly-usable values in
1570 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1571 : * inherited encoding value back to a string to pass to setup_connection.
1572 : */
1573 32 : setup_connection(AH,
1574 : pg_encoding_to_char(AH->encoding),
1575 : NULL,
1576 : NULL);
1577 32 : }
1578 :
1579 : static char *
1580 16 : get_synchronized_snapshot(Archive *fout)
1581 : {
1582 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1583 : char *result;
1584 : PGresult *res;
1585 :
1586 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1587 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1588 16 : PQclear(res);
1589 :
1590 16 : return result;
1591 : }
1592 :
1593 : static ArchiveFormat
1594 416 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1595 : {
1596 : ArchiveFormat archiveFormat;
1597 :
1598 416 : *mode = archModeWrite;
1599 :
1600 416 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1601 : {
1602 : /* This is used by pg_dumpall, and is not documented */
1603 86 : archiveFormat = archNull;
1604 86 : *mode = archModeAppend;
1605 : }
1606 330 : else if (pg_strcasecmp(format, "c") == 0)
1607 0 : archiveFormat = archCustom;
1608 330 : else if (pg_strcasecmp(format, "custom") == 0)
1609 82 : archiveFormat = archCustom;
1610 248 : else if (pg_strcasecmp(format, "d") == 0)
1611 0 : archiveFormat = archDirectory;
1612 248 : else if (pg_strcasecmp(format, "directory") == 0)
1613 20 : archiveFormat = archDirectory;
1614 228 : else if (pg_strcasecmp(format, "p") == 0)
1615 214 : archiveFormat = archNull;
1616 14 : else if (pg_strcasecmp(format, "plain") == 0)
1617 6 : archiveFormat = archNull;
1618 8 : else if (pg_strcasecmp(format, "t") == 0)
1619 0 : archiveFormat = archTar;
1620 8 : else if (pg_strcasecmp(format, "tar") == 0)
1621 6 : archiveFormat = archTar;
1622 : else
1623 2 : pg_fatal("invalid output format \"%s\" specified", format);
1624 414 : return archiveFormat;
1625 : }
1626 :
1627 : /*
1628 : * Find the OIDs of all schemas matching the given list of patterns,
1629 : * and append them to the given OID list.
1630 : */
1631 : static void
1632 420 : expand_schema_name_patterns(Archive *fout,
1633 : SimpleStringList *patterns,
1634 : SimpleOidList *oids,
1635 : bool strict_names)
1636 : {
1637 : PQExpBuffer query;
1638 : PGresult *res;
1639 : SimpleStringListCell *cell;
1640 : int i;
1641 :
1642 420 : if (patterns->head == NULL)
1643 378 : return; /* nothing to do */
1644 :
1645 42 : query = createPQExpBuffer();
1646 :
1647 : /*
1648 : * The loop below runs multiple SELECTs might sometimes result in
1649 : * duplicate entries in the OID list, but we don't care.
1650 : */
1651 :
1652 72 : for (cell = patterns->head; cell; cell = cell->next)
1653 : {
1654 : PQExpBufferData dbbuf;
1655 : int dotcnt;
1656 :
1657 42 : appendPQExpBufferStr(query,
1658 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1659 42 : initPQExpBuffer(&dbbuf);
1660 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1661 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1662 : &dotcnt);
1663 42 : if (dotcnt > 1)
1664 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1665 : cell->val);
1666 38 : else if (dotcnt == 1)
1667 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1668 32 : termPQExpBuffer(&dbbuf);
1669 :
1670 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1671 32 : if (strict_names && PQntuples(res) == 0)
1672 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1673 :
1674 58 : for (i = 0; i < PQntuples(res); i++)
1675 : {
1676 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1677 : }
1678 :
1679 30 : PQclear(res);
1680 30 : resetPQExpBuffer(query);
1681 : }
1682 :
1683 30 : destroyPQExpBuffer(query);
1684 : }
1685 :
1686 : /*
1687 : * Find the OIDs of all extensions matching the given list of patterns,
1688 : * and append them to the given OID list.
1689 : */
1690 : static void
1691 376 : expand_extension_name_patterns(Archive *fout,
1692 : SimpleStringList *patterns,
1693 : SimpleOidList *oids,
1694 : bool strict_names)
1695 : {
1696 : PQExpBuffer query;
1697 : PGresult *res;
1698 : SimpleStringListCell *cell;
1699 : int i;
1700 :
1701 376 : if (patterns->head == NULL)
1702 362 : return; /* nothing to do */
1703 :
1704 14 : query = createPQExpBuffer();
1705 :
1706 : /*
1707 : * The loop below runs multiple SELECTs might sometimes result in
1708 : * duplicate entries in the OID list, but we don't care.
1709 : */
1710 28 : for (cell = patterns->head; cell; cell = cell->next)
1711 : {
1712 : int dotcnt;
1713 :
1714 14 : appendPQExpBufferStr(query,
1715 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1716 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1717 : false, NULL, "e.extname", NULL, NULL, NULL,
1718 : &dotcnt);
1719 14 : if (dotcnt > 0)
1720 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1721 : cell->val);
1722 :
1723 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1724 14 : if (strict_names && PQntuples(res) == 0)
1725 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1726 :
1727 26 : for (i = 0; i < PQntuples(res); i++)
1728 : {
1729 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1730 : }
1731 :
1732 14 : PQclear(res);
1733 14 : resetPQExpBuffer(query);
1734 : }
1735 :
1736 14 : destroyPQExpBuffer(query);
1737 : }
1738 :
1739 : /*
1740 : * Find the OIDs of all foreign servers matching the given list of patterns,
1741 : * and append them to the given OID list.
1742 : */
1743 : static void
1744 370 : expand_foreign_server_name_patterns(Archive *fout,
1745 : SimpleStringList *patterns,
1746 : SimpleOidList *oids)
1747 : {
1748 : PQExpBuffer query;
1749 : PGresult *res;
1750 : SimpleStringListCell *cell;
1751 : int i;
1752 :
1753 370 : if (patterns->head == NULL)
1754 364 : return; /* nothing to do */
1755 :
1756 6 : query = createPQExpBuffer();
1757 :
1758 : /*
1759 : * The loop below runs multiple SELECTs might sometimes result in
1760 : * duplicate entries in the OID list, but we don't care.
1761 : */
1762 :
1763 10 : for (cell = patterns->head; cell; cell = cell->next)
1764 : {
1765 : int dotcnt;
1766 :
1767 6 : appendPQExpBufferStr(query,
1768 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1769 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1770 : false, NULL, "s.srvname", NULL, NULL, NULL,
1771 : &dotcnt);
1772 6 : if (dotcnt > 0)
1773 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1774 : cell->val);
1775 :
1776 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1777 6 : if (PQntuples(res) == 0)
1778 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1779 :
1780 8 : for (i = 0; i < PQntuples(res); i++)
1781 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1782 :
1783 4 : PQclear(res);
1784 4 : resetPQExpBuffer(query);
1785 : }
1786 :
1787 4 : destroyPQExpBuffer(query);
1788 : }
1789 :
1790 : /*
1791 : * Find the OIDs of all tables matching the given list of patterns,
1792 : * and append them to the given OID list. See also expand_dbname_patterns()
1793 : * in pg_dumpall.c
1794 : */
1795 : static void
1796 2238 : expand_table_name_patterns(Archive *fout,
1797 : SimpleStringList *patterns, SimpleOidList *oids,
1798 : bool strict_names, bool with_child_tables)
1799 : {
1800 : PQExpBuffer query;
1801 : PGresult *res;
1802 : SimpleStringListCell *cell;
1803 : int i;
1804 :
1805 2238 : if (patterns->head == NULL)
1806 2180 : return; /* nothing to do */
1807 :
1808 58 : query = createPQExpBuffer();
1809 :
1810 : /*
1811 : * this might sometimes result in duplicate entries in the OID list, but
1812 : * we don't care.
1813 : */
1814 :
1815 118 : for (cell = patterns->head; cell; cell = cell->next)
1816 : {
1817 : PQExpBufferData dbbuf;
1818 : int dotcnt;
1819 :
1820 : /*
1821 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1822 : * would be unnecessary given a pg_table_is_visible() variant taking a
1823 : * search_path argument.
1824 : *
1825 : * For with_child_tables, we start with the basic query's results and
1826 : * recursively search the inheritance tree to add child tables.
1827 : */
1828 70 : if (with_child_tables)
1829 : {
1830 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1831 : }
1832 :
1833 70 : appendPQExpBuffer(query,
1834 : "SELECT c.oid"
1835 : "\nFROM pg_catalog.pg_class c"
1836 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1837 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1838 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1839 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1840 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1841 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1842 : RELKIND_PARTITIONED_TABLE);
1843 70 : initPQExpBuffer(&dbbuf);
1844 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1845 : false, "n.nspname", "c.relname", NULL,
1846 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1847 : &dotcnt);
1848 70 : if (dotcnt > 2)
1849 2 : pg_fatal("improper relation name (too many dotted names): %s",
1850 : cell->val);
1851 68 : else if (dotcnt == 2)
1852 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1853 64 : termPQExpBuffer(&dbbuf);
1854 :
1855 64 : if (with_child_tables)
1856 : {
1857 12 : appendPQExpBufferStr(query, "UNION"
1858 : "\nSELECT i.inhrelid"
1859 : "\nFROM partition_tree p"
1860 : "\n JOIN pg_catalog.pg_inherits i"
1861 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1862 : "\n)"
1863 : "\nSELECT relid FROM partition_tree");
1864 : }
1865 :
1866 64 : ExecuteSqlStatement(fout, "RESET search_path");
1867 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1868 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1869 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1870 64 : if (strict_names && PQntuples(res) == 0)
1871 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1872 :
1873 148 : for (i = 0; i < PQntuples(res); i++)
1874 : {
1875 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1876 : }
1877 :
1878 60 : PQclear(res);
1879 60 : resetPQExpBuffer(query);
1880 : }
1881 :
1882 48 : destroyPQExpBuffer(query);
1883 : }
1884 :
1885 : /*
1886 : * Verifies that the connected database name matches the given database name,
1887 : * and if not, dies with an error about the given pattern.
1888 : *
1889 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1890 : */
1891 : static void
1892 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1893 : {
1894 : const char *db;
1895 :
1896 10 : db = PQdb(conn);
1897 10 : if (db == NULL)
1898 0 : pg_fatal("You are currently not connected to a database.");
1899 :
1900 10 : if (strcmp(db, dbname) != 0)
1901 10 : pg_fatal("cross-database references are not implemented: %s",
1902 : pattern);
1903 0 : }
1904 :
1905 : /*
1906 : * checkExtensionMembership
1907 : * Determine whether object is an extension member, and if so,
1908 : * record an appropriate dependency and set the object's dump flag.
1909 : *
1910 : * It's important to call this for each object that could be an extension
1911 : * member. Generally, we integrate this with determining the object's
1912 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1913 : *
1914 : * Returns true if object is an extension member, else false.
1915 : */
1916 : static bool
1917 1149880 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1918 : {
1919 1149880 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1920 :
1921 1149880 : if (ext == NULL)
1922 1148300 : return false;
1923 :
1924 1580 : dobj->ext_member = true;
1925 :
1926 : /* Record dependency so that getDependencies needn't deal with that */
1927 1580 : addObjectDependency(dobj, ext->dobj.dumpId);
1928 :
1929 : /*
1930 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1931 : * dumped. (Any initial ACLs will be removed later, using data from
1932 : * pg_init_privs, so that we'll dump only the delta from the extension's
1933 : * initial setup.)
1934 : *
1935 : * Prior to 9.6, we do not include any extension member components.
1936 : *
1937 : * In binary upgrades, we still dump all components of the members
1938 : * individually, since the idea is to exactly reproduce the database
1939 : * contents rather than replace the extension contents with something
1940 : * different.
1941 : *
1942 : * Note: it might be interesting someday to implement storage and delta
1943 : * dumping of extension members' RLS policies and/or security labels.
1944 : * However there is a pitfall for RLS policies: trying to dump them
1945 : * requires getting a lock on their tables, and the calling user might not
1946 : * have privileges for that. We need no lock to examine a table's ACLs,
1947 : * so the current feature doesn't have a problem of that sort.
1948 : */
1949 1580 : if (fout->dopt->binary_upgrade)
1950 328 : dobj->dump = ext->dobj.dump;
1951 : else
1952 : {
1953 1252 : if (fout->remoteVersion < 90600)
1954 0 : dobj->dump = DUMP_COMPONENT_NONE;
1955 : else
1956 1252 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1957 : }
1958 :
1959 1580 : return true;
1960 : }
1961 :
1962 : /*
1963 : * selectDumpableNamespace: policy-setting subroutine
1964 : * Mark a namespace as to be dumped or not
1965 : */
1966 : static void
1967 2856 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1968 : {
1969 : /*
1970 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1971 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1972 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1973 : */
1974 2856 : nsinfo->create = true;
1975 :
1976 : /*
1977 : * If specific tables are being dumped, do not dump any complete
1978 : * namespaces. If specific namespaces are being dumped, dump just those
1979 : * namespaces. Otherwise, dump all non-system namespaces.
1980 : */
1981 2856 : if (table_include_oids.head != NULL)
1982 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1983 2756 : else if (schema_include_oids.head != NULL)
1984 374 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1985 374 : simple_oid_list_member(&schema_include_oids,
1986 : nsinfo->dobj.catId.oid) ?
1987 374 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1988 2382 : else if (fout->remoteVersion >= 90600 &&
1989 2382 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1990 : {
1991 : /*
1992 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1993 : * they are interesting (and not the original ACLs which were set at
1994 : * initdb time, see pg_init_privs).
1995 : */
1996 322 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1997 : }
1998 2060 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1999 1002 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2000 : {
2001 : /* Other system schemas don't get dumped */
2002 1380 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2003 : }
2004 680 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2005 : {
2006 : /*
2007 : * The public schema is a strange beast that sits in a sort of
2008 : * no-mans-land between being a system object and a user object.
2009 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2010 : * a comment and an indication of ownership. If the owner is the
2011 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2012 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2013 : */
2014 314 : nsinfo->create = false;
2015 314 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2016 314 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2017 224 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2018 314 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2019 :
2020 : /*
2021 : * Also, make like it has a comment even if it doesn't; this is so
2022 : * that we'll emit a command to drop the comment, if appropriate.
2023 : * (Without this, we'd not call dumpCommentExtended for it.)
2024 : */
2025 314 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2026 : }
2027 : else
2028 366 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2029 :
2030 : /*
2031 : * In any case, a namespace can be excluded by an exclusion switch
2032 : */
2033 3880 : if (nsinfo->dobj.dump_contains &&
2034 1024 : simple_oid_list_member(&schema_exclude_oids,
2035 : nsinfo->dobj.catId.oid))
2036 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2037 :
2038 : /*
2039 : * If the schema belongs to an extension, allow extension membership to
2040 : * override the dump decision for the schema itself. However, this does
2041 : * not change dump_contains, so this won't change what we do with objects
2042 : * within the schema. (If they belong to the extension, they'll get
2043 : * suppressed by it, otherwise not.)
2044 : */
2045 2856 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2046 2856 : }
2047 :
2048 : /*
2049 : * selectDumpableTable: policy-setting subroutine
2050 : * Mark a table as to be dumped or not
2051 : */
2052 : static void
2053 96626 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2054 : {
2055 96626 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2056 450 : return; /* extension membership overrides all else */
2057 :
2058 : /*
2059 : * If specific tables are being dumped, dump just those tables; else, dump
2060 : * according to the parent namespace's dump flag.
2061 : */
2062 96176 : if (table_include_oids.head != NULL)
2063 10356 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2064 : tbinfo->dobj.catId.oid) ?
2065 5178 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2066 : else
2067 90998 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2068 :
2069 : /*
2070 : * In any case, a table can be excluded by an exclusion switch
2071 : */
2072 157314 : if (tbinfo->dobj.dump &&
2073 61138 : simple_oid_list_member(&table_exclude_oids,
2074 : tbinfo->dobj.catId.oid))
2075 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2076 : }
2077 :
2078 : /*
2079 : * selectDumpableType: policy-setting subroutine
2080 : * Mark a type as to be dumped or not
2081 : *
2082 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2083 : * special type code to facilitate sorting into the desired order. (We don't
2084 : * want to consider those to be ordinary types because that would bring tables
2085 : * up into the datatype part of the dump order.) We still set the object's
2086 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2087 : * need it so that casts involving such types will be dumped correctly -- see
2088 : * dumpCast. This means the flag should be set the same as for the underlying
2089 : * object (the table or base type).
2090 : */
2091 : static void
2092 264998 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2093 : {
2094 : /* skip complex types, except for standalone composite types */
2095 264998 : if (OidIsValid(tyinfo->typrelid) &&
2096 95160 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2097 : {
2098 94790 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2099 :
2100 94790 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2101 94790 : if (tytable != NULL)
2102 94790 : tyinfo->dobj.dump = tytable->dobj.dump;
2103 : else
2104 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2105 94790 : return;
2106 : }
2107 :
2108 : /* skip auto-generated array and multirange types */
2109 170208 : if (tyinfo->isArray || tyinfo->isMultirange)
2110 : {
2111 129626 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2112 :
2113 : /*
2114 : * Fall through to set the dump flag; we assume that the subsequent
2115 : * rules will do the same thing as they would for the array's base
2116 : * type or multirange's range type. (We cannot reliably look up the
2117 : * base type here, since getTypes may not have processed it yet.)
2118 : */
2119 : }
2120 :
2121 170208 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2122 300 : return; /* extension membership overrides all else */
2123 :
2124 : /* Dump based on if the contents of the namespace are being dumped */
2125 169908 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2126 : }
2127 :
2128 : /*
2129 : * selectDumpableDefaultACL: policy-setting subroutine
2130 : * Mark a default ACL as to be dumped or not
2131 : *
2132 : * For per-schema default ACLs, dump if the schema is to be dumped.
2133 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2134 : * and aclsSkip are checked separately.
2135 : */
2136 : static void
2137 412 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2138 : {
2139 : /* Default ACLs can't be extension members */
2140 :
2141 412 : if (dinfo->dobj.namespace)
2142 : /* default ACLs are considered part of the namespace */
2143 192 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2144 : else
2145 220 : dinfo->dobj.dump = dopt->include_everything ?
2146 220 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2147 412 : }
2148 :
2149 : /*
2150 : * selectDumpableCast: policy-setting subroutine
2151 : * Mark a cast as to be dumped or not
2152 : *
2153 : * Casts do not belong to any particular namespace (since they haven't got
2154 : * names), nor do they have identifiable owners. To distinguish user-defined
2155 : * casts from built-in ones, we must resort to checking whether the cast's
2156 : * OID is in the range reserved for initdb.
2157 : */
2158 : static void
2159 86084 : selectDumpableCast(CastInfo *cast, Archive *fout)
2160 : {
2161 86084 : if (checkExtensionMembership(&cast->dobj, fout))
2162 0 : return; /* extension membership overrides all else */
2163 :
2164 : /*
2165 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2166 : * support ACLs currently.
2167 : */
2168 86084 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2169 85904 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2170 : else
2171 180 : cast->dobj.dump = fout->dopt->include_everything ?
2172 180 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2173 : }
2174 :
2175 : /*
2176 : * selectDumpableProcLang: policy-setting subroutine
2177 : * Mark a procedural language as to be dumped or not
2178 : *
2179 : * Procedural languages do not belong to any particular namespace. To
2180 : * identify built-in languages, we must resort to checking whether the
2181 : * language's OID is in the range reserved for initdb.
2182 : */
2183 : static void
2184 460 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2185 : {
2186 460 : if (checkExtensionMembership(&plang->dobj, fout))
2187 364 : return; /* extension membership overrides all else */
2188 :
2189 : /*
2190 : * Only include procedural languages when we are dumping everything.
2191 : *
2192 : * For from-initdb procedural languages, only include ACLs, as we do for
2193 : * the pg_catalog namespace. We need this because procedural languages do
2194 : * not live in any namespace.
2195 : */
2196 96 : if (!fout->dopt->include_everything)
2197 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2198 : else
2199 : {
2200 80 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2201 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2202 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2203 : else
2204 80 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2205 : }
2206 : }
2207 :
2208 : /*
2209 : * selectDumpableAccessMethod: policy-setting subroutine
2210 : * Mark an access method as to be dumped or not
2211 : *
2212 : * Access methods do not belong to any particular namespace. To identify
2213 : * built-in access methods, we must resort to checking whether the
2214 : * method's OID is in the range reserved for initdb.
2215 : */
2216 : static void
2217 2804 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2218 : {
2219 : /* see getAccessMethods() comment about v9.6. */
2220 2804 : if (fout->remoteVersion < 90600)
2221 : {
2222 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2223 0 : return;
2224 : }
2225 :
2226 2804 : if (checkExtensionMembership(&method->dobj, fout))
2227 50 : return; /* extension membership overrides all else */
2228 :
2229 : /*
2230 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2231 : * they do not support ACLs currently.
2232 : */
2233 2754 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2234 2548 : method->dobj.dump = DUMP_COMPONENT_NONE;
2235 : else
2236 206 : method->dobj.dump = fout->dopt->include_everything ?
2237 206 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2238 : }
2239 :
2240 : /*
2241 : * selectDumpableExtension: policy-setting subroutine
2242 : * Mark an extension as to be dumped or not
2243 : *
2244 : * Built-in extensions should be skipped except for checking ACLs, since we
2245 : * assume those will already be installed in the target database. We identify
2246 : * such extensions by their having OIDs in the range reserved for initdb.
2247 : * We dump all user-added extensions by default. No extensions are dumped
2248 : * if include_everything is false (i.e., a --schema or --table switch was
2249 : * given), except if --extension specifies a list of extensions to dump.
2250 : */
2251 : static void
2252 416 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2253 : {
2254 : /*
2255 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2256 : * change permissions on their member objects, if they wish to, and have
2257 : * those changes preserved.
2258 : */
2259 416 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2260 366 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2261 : else
2262 : {
2263 : /* check if there is a list of extensions to dump */
2264 50 : if (extension_include_oids.head != NULL)
2265 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2266 8 : simple_oid_list_member(&extension_include_oids,
2267 : extinfo->dobj.catId.oid) ?
2268 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2269 : else
2270 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2271 42 : dopt->include_everything ?
2272 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2273 :
2274 : /* check that the extension is not explicitly excluded */
2275 92 : if (extinfo->dobj.dump &&
2276 42 : simple_oid_list_member(&extension_exclude_oids,
2277 : extinfo->dobj.catId.oid))
2278 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2279 : }
2280 416 : }
2281 :
2282 : /*
2283 : * selectDumpablePublicationObject: policy-setting subroutine
2284 : * Mark a publication object as to be dumped or not
2285 : *
2286 : * A publication can have schemas and tables which have schemas, but those are
2287 : * ignored in decision making, because publications are only dumped when we are
2288 : * dumping everything.
2289 : */
2290 : static void
2291 1004 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2292 : {
2293 1004 : if (checkExtensionMembership(dobj, fout))
2294 0 : return; /* extension membership overrides all else */
2295 :
2296 1004 : dobj->dump = fout->dopt->include_everything ?
2297 1004 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2298 : }
2299 :
2300 : /*
2301 : * selectDumpableStatisticsObject: policy-setting subroutine
2302 : * Mark an extended statistics object as to be dumped or not
2303 : *
2304 : * We dump an extended statistics object if the schema it's in and the table
2305 : * it's for are being dumped. (This'll need more thought if statistics
2306 : * objects ever support cross-table stats.)
2307 : */
2308 : static void
2309 344 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2310 : {
2311 344 : if (checkExtensionMembership(&sobj->dobj, fout))
2312 0 : return; /* extension membership overrides all else */
2313 :
2314 344 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2315 344 : if (sobj->stattable == NULL ||
2316 344 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2317 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2318 : }
2319 :
2320 : /*
2321 : * selectDumpableObject: policy-setting subroutine
2322 : * Mark a generic dumpable object as to be dumped or not
2323 : *
2324 : * Use this only for object types without a special-case routine above.
2325 : */
2326 : static void
2327 789494 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2328 : {
2329 789494 : if (checkExtensionMembership(dobj, fout))
2330 366 : return; /* extension membership overrides all else */
2331 :
2332 : /*
2333 : * Default policy is to dump if parent namespace is dumpable, or for
2334 : * non-namespace-associated items, dump if we're dumping "everything".
2335 : */
2336 789128 : if (dobj->namespace)
2337 787768 : dobj->dump = dobj->namespace->dobj.dump_contains;
2338 : else
2339 1360 : dobj->dump = fout->dopt->include_everything ?
2340 1360 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2341 : }
2342 :
2343 : /*
2344 : * Dump a table's contents for loading using the COPY command
2345 : * - this routine is called by the Archiver when it wants the table
2346 : * to be dumped.
2347 : */
2348 : static int
2349 8426 : dumpTableData_copy(Archive *fout, const void *dcontext)
2350 : {
2351 8426 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2352 8426 : TableInfo *tbinfo = tdinfo->tdtable;
2353 8426 : const char *classname = tbinfo->dobj.name;
2354 8426 : PQExpBuffer q = createPQExpBuffer();
2355 :
2356 : /*
2357 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2358 : * which uses it already.
2359 : */
2360 8426 : PQExpBuffer clistBuf = createPQExpBuffer();
2361 8426 : PGconn *conn = GetConnection(fout);
2362 : PGresult *res;
2363 : int ret;
2364 : char *copybuf;
2365 : const char *column_list;
2366 :
2367 8426 : pg_log_info("dumping contents of table \"%s.%s\"",
2368 : tbinfo->dobj.namespace->dobj.name, classname);
2369 :
2370 : /*
2371 : * Specify the column list explicitly so that we have no possibility of
2372 : * retrieving data in the wrong column order. (The default column
2373 : * ordering of COPY will not be what we want in certain corner cases
2374 : * involving ADD COLUMN and inheritance.)
2375 : */
2376 8426 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2377 :
2378 : /*
2379 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2380 : * a filter condition was specified. For other cases a simple COPY
2381 : * suffices.
2382 : */
2383 8426 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2384 : {
2385 : /* Temporary allows to access to foreign tables to dump data */
2386 74 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2387 2 : set_restrict_relation_kind(fout, "view");
2388 :
2389 74 : appendPQExpBufferStr(q, "COPY (SELECT ");
2390 : /* klugery to get rid of parens in column list */
2391 74 : if (strlen(column_list) > 2)
2392 : {
2393 74 : appendPQExpBufferStr(q, column_list + 1);
2394 74 : q->data[q->len - 1] = ' ';
2395 : }
2396 : else
2397 0 : appendPQExpBufferStr(q, "* ");
2398 :
2399 148 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2400 74 : fmtQualifiedDumpable(tbinfo),
2401 74 : tdinfo->filtercond ? tdinfo->filtercond : "");
2402 : }
2403 : else
2404 : {
2405 8352 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2406 8352 : fmtQualifiedDumpable(tbinfo),
2407 : column_list);
2408 : }
2409 8426 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2410 8424 : PQclear(res);
2411 8424 : destroyPQExpBuffer(clistBuf);
2412 :
2413 : for (;;)
2414 : {
2415 3620934 : ret = PQgetCopyData(conn, ©buf, 0);
2416 :
2417 3620934 : if (ret < 0)
2418 8424 : break; /* done or error */
2419 :
2420 3612510 : if (copybuf)
2421 : {
2422 3612510 : WriteData(fout, copybuf, ret);
2423 3612510 : PQfreemem(copybuf);
2424 : }
2425 :
2426 : /* ----------
2427 : * THROTTLE:
2428 : *
2429 : * There was considerable discussion in late July, 2000 regarding
2430 : * slowing down pg_dump when backing up large tables. Users with both
2431 : * slow & fast (multi-processor) machines experienced performance
2432 : * degradation when doing a backup.
2433 : *
2434 : * Initial attempts based on sleeping for a number of ms for each ms
2435 : * of work were deemed too complex, then a simple 'sleep in each loop'
2436 : * implementation was suggested. The latter failed because the loop
2437 : * was too tight. Finally, the following was implemented:
2438 : *
2439 : * If throttle is non-zero, then
2440 : * See how long since the last sleep.
2441 : * Work out how long to sleep (based on ratio).
2442 : * If sleep is more than 100ms, then
2443 : * sleep
2444 : * reset timer
2445 : * EndIf
2446 : * EndIf
2447 : *
2448 : * where the throttle value was the number of ms to sleep per ms of
2449 : * work. The calculation was done in each loop.
2450 : *
2451 : * Most of the hard work is done in the backend, and this solution
2452 : * still did not work particularly well: on slow machines, the ratio
2453 : * was 50:1, and on medium paced machines, 1:1, and on fast
2454 : * multi-processor machines, it had little or no effect, for reasons
2455 : * that were unclear.
2456 : *
2457 : * Further discussion ensued, and the proposal was dropped.
2458 : *
2459 : * For those people who want this feature, it can be implemented using
2460 : * gettimeofday in each loop, calculating the time since last sleep,
2461 : * multiplying that by the sleep ratio, then if the result is more
2462 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2463 : * function to sleep for a subsecond period ie.
2464 : *
2465 : * select(0, NULL, NULL, NULL, &tvi);
2466 : *
2467 : * This will return after the interval specified in the structure tvi.
2468 : * Finally, call gettimeofday again to save the 'last sleep time'.
2469 : * ----------
2470 : */
2471 : }
2472 8424 : archprintf(fout, "\\.\n\n\n");
2473 :
2474 8424 : if (ret == -2)
2475 : {
2476 : /* copy data transfer failed */
2477 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2478 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2479 0 : pg_log_error_detail("Command was: %s", q->data);
2480 0 : exit_nicely(1);
2481 : }
2482 :
2483 : /* Check command status and return to normal libpq state */
2484 8424 : res = PQgetResult(conn);
2485 8424 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2486 : {
2487 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2488 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2489 0 : pg_log_error_detail("Command was: %s", q->data);
2490 0 : exit_nicely(1);
2491 : }
2492 8424 : PQclear(res);
2493 :
2494 : /* Do this to ensure we've pumped libpq back to idle state */
2495 8424 : if (PQgetResult(conn) != NULL)
2496 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2497 : classname);
2498 :
2499 8424 : destroyPQExpBuffer(q);
2500 :
2501 : /* Revert back the setting */
2502 8424 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2503 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2504 :
2505 8424 : return 1;
2506 : }
2507 :
2508 : /*
2509 : * Dump table data using INSERT commands.
2510 : *
2511 : * Caution: when we restore from an archive file direct to database, the
2512 : * INSERT commands emitted by this function have to be parsed by
2513 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2514 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2515 : */
2516 : static int
2517 170 : dumpTableData_insert(Archive *fout, const void *dcontext)
2518 : {
2519 170 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2520 170 : TableInfo *tbinfo = tdinfo->tdtable;
2521 170 : DumpOptions *dopt = fout->dopt;
2522 170 : PQExpBuffer q = createPQExpBuffer();
2523 170 : PQExpBuffer insertStmt = NULL;
2524 : char *attgenerated;
2525 : PGresult *res;
2526 : int nfields,
2527 : i;
2528 170 : int rows_per_statement = dopt->dump_inserts;
2529 170 : int rows_this_statement = 0;
2530 :
2531 : /* Temporary allows to access to foreign tables to dump data */
2532 170 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2533 0 : set_restrict_relation_kind(fout, "view");
2534 :
2535 : /*
2536 : * If we're going to emit INSERTs with column names, the most efficient
2537 : * way to deal with generated columns is to exclude them entirely. For
2538 : * INSERTs without column names, we have to emit DEFAULT rather than the
2539 : * actual column value --- but we can save a few cycles by fetching nulls
2540 : * rather than the uninteresting-to-us value.
2541 : */
2542 170 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2543 170 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2544 170 : nfields = 0;
2545 522 : for (i = 0; i < tbinfo->numatts; i++)
2546 : {
2547 352 : if (tbinfo->attisdropped[i])
2548 4 : continue;
2549 348 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2550 16 : continue;
2551 332 : if (nfields > 0)
2552 176 : appendPQExpBufferStr(q, ", ");
2553 332 : if (tbinfo->attgenerated[i])
2554 16 : appendPQExpBufferStr(q, "NULL");
2555 : else
2556 316 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2557 332 : attgenerated[nfields] = tbinfo->attgenerated[i];
2558 332 : nfields++;
2559 : }
2560 : /* Servers before 9.4 will complain about zero-column SELECT */
2561 170 : if (nfields == 0)
2562 14 : appendPQExpBufferStr(q, "NULL");
2563 170 : appendPQExpBuffer(q, " FROM ONLY %s",
2564 170 : fmtQualifiedDumpable(tbinfo));
2565 170 : if (tdinfo->filtercond)
2566 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2567 :
2568 170 : ExecuteSqlStatement(fout, q->data);
2569 :
2570 : while (1)
2571 : {
2572 274 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2573 : PGRES_TUPLES_OK);
2574 :
2575 : /* cross-check field count, allowing for dummy NULL if any */
2576 274 : if (nfields != PQnfields(res) &&
2577 20 : !(nfields == 0 && PQnfields(res) == 1))
2578 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2579 : tbinfo->dobj.name);
2580 :
2581 : /*
2582 : * First time through, we build as much of the INSERT statement as
2583 : * possible in "insertStmt", which we can then just print for each
2584 : * statement. If the table happens to have zero dumpable columns then
2585 : * this will be a complete statement, otherwise it will end in
2586 : * "VALUES" and be ready to have the row's column values printed.
2587 : */
2588 274 : if (insertStmt == NULL)
2589 : {
2590 : TableInfo *targettab;
2591 :
2592 170 : insertStmt = createPQExpBuffer();
2593 :
2594 : /*
2595 : * When load-via-partition-root is set or forced, get the root
2596 : * table name for the partition table, so that we can reload data
2597 : * through the root table.
2598 : */
2599 170 : if (tbinfo->ispartition &&
2600 96 : (dopt->load_via_partition_root ||
2601 48 : forcePartitionRootLoad(tbinfo)))
2602 14 : targettab = getRootTableInfo(tbinfo);
2603 : else
2604 156 : targettab = tbinfo;
2605 :
2606 170 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2607 170 : fmtQualifiedDumpable(targettab));
2608 :
2609 : /* corner case for zero-column table */
2610 170 : if (nfields == 0)
2611 : {
2612 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2613 : }
2614 : else
2615 : {
2616 : /* append the list of column names if required */
2617 156 : if (dopt->column_inserts)
2618 : {
2619 70 : appendPQExpBufferChar(insertStmt, '(');
2620 210 : for (int field = 0; field < nfields; field++)
2621 : {
2622 140 : if (field > 0)
2623 70 : appendPQExpBufferStr(insertStmt, ", ");
2624 140 : appendPQExpBufferStr(insertStmt,
2625 140 : fmtId(PQfname(res, field)));
2626 : }
2627 70 : appendPQExpBufferStr(insertStmt, ") ");
2628 : }
2629 :
2630 156 : if (tbinfo->needs_override)
2631 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2632 :
2633 156 : appendPQExpBufferStr(insertStmt, "VALUES");
2634 : }
2635 : }
2636 :
2637 6816 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2638 : {
2639 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2640 6542 : if (rows_this_statement == 0)
2641 6530 : archputs(insertStmt->data, fout);
2642 :
2643 : /*
2644 : * If it is zero-column table then we've already written the
2645 : * complete statement, which will mean we've disobeyed
2646 : * --rows-per-insert when it's set greater than 1. We do support
2647 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2648 : * UNION ALL ... but that's non-standard so we should avoid it
2649 : * given that using INSERTs is mostly only ever needed for
2650 : * cross-database exports.
2651 : */
2652 6542 : if (nfields == 0)
2653 12 : continue;
2654 :
2655 : /* Emit a row heading */
2656 6530 : if (rows_per_statement == 1)
2657 6512 : archputs(" (", fout);
2658 18 : else if (rows_this_statement > 0)
2659 12 : archputs(",\n\t(", fout);
2660 : else
2661 6 : archputs("\n\t(", fout);
2662 :
2663 19698 : for (int field = 0; field < nfields; field++)
2664 : {
2665 13168 : if (field > 0)
2666 6638 : archputs(", ", fout);
2667 13168 : if (attgenerated[field])
2668 : {
2669 4 : archputs("DEFAULT", fout);
2670 4 : continue;
2671 : }
2672 13164 : if (PQgetisnull(res, tuple, field))
2673 : {
2674 166 : archputs("NULL", fout);
2675 166 : continue;
2676 : }
2677 :
2678 : /* XXX This code is partially duplicated in ruleutils.c */
2679 12998 : switch (PQftype(res, field))
2680 : {
2681 8938 : case INT2OID:
2682 : case INT4OID:
2683 : case INT8OID:
2684 : case OIDOID:
2685 : case FLOAT4OID:
2686 : case FLOAT8OID:
2687 : case NUMERICOID:
2688 : {
2689 : /*
2690 : * These types are printed without quotes unless
2691 : * they contain values that aren't accepted by the
2692 : * scanner unquoted (e.g., 'NaN'). Note that
2693 : * strtod() and friends might accept NaN, so we
2694 : * can't use that to test.
2695 : *
2696 : * In reality we only need to defend against
2697 : * infinity and NaN, so we need not get too crazy
2698 : * about pattern matching here.
2699 : */
2700 8938 : const char *s = PQgetvalue(res, tuple, field);
2701 :
2702 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2703 8934 : archputs(s, fout);
2704 : else
2705 4 : archprintf(fout, "'%s'", s);
2706 : }
2707 8938 : break;
2708 :
2709 4 : case BITOID:
2710 : case VARBITOID:
2711 4 : archprintf(fout, "B'%s'",
2712 : PQgetvalue(res, tuple, field));
2713 4 : break;
2714 :
2715 8 : case BOOLOID:
2716 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2717 4 : archputs("true", fout);
2718 : else
2719 4 : archputs("false", fout);
2720 8 : break;
2721 :
2722 4048 : default:
2723 : /* All other types are printed as string literals. */
2724 4048 : resetPQExpBuffer(q);
2725 4048 : appendStringLiteralAH(q,
2726 : PQgetvalue(res, tuple, field),
2727 : fout);
2728 4048 : archputs(q->data, fout);
2729 4048 : break;
2730 : }
2731 : }
2732 :
2733 : /* Terminate the row ... */
2734 6530 : archputs(")", fout);
2735 :
2736 : /* ... and the statement, if the target no. of rows is reached */
2737 6530 : if (++rows_this_statement >= rows_per_statement)
2738 : {
2739 6516 : if (dopt->do_nothing)
2740 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2741 : else
2742 6516 : archputs(";\n", fout);
2743 : /* Reset the row counter */
2744 6516 : rows_this_statement = 0;
2745 : }
2746 : }
2747 :
2748 274 : if (PQntuples(res) <= 0)
2749 : {
2750 170 : PQclear(res);
2751 170 : break;
2752 : }
2753 104 : PQclear(res);
2754 : }
2755 :
2756 : /* Terminate any statements that didn't make the row count. */
2757 170 : if (rows_this_statement > 0)
2758 : {
2759 2 : if (dopt->do_nothing)
2760 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2761 : else
2762 2 : archputs(";\n", fout);
2763 : }
2764 :
2765 170 : archputs("\n\n", fout);
2766 :
2767 170 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2768 :
2769 170 : destroyPQExpBuffer(q);
2770 170 : if (insertStmt != NULL)
2771 170 : destroyPQExpBuffer(insertStmt);
2772 170 : free(attgenerated);
2773 :
2774 : /* Revert back the setting */
2775 170 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2776 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2777 :
2778 170 : return 1;
2779 : }
2780 :
2781 : /*
2782 : * getRootTableInfo:
2783 : * get the root TableInfo for the given partition table.
2784 : */
2785 : static TableInfo *
2786 170 : getRootTableInfo(const TableInfo *tbinfo)
2787 : {
2788 : TableInfo *parentTbinfo;
2789 :
2790 : Assert(tbinfo->ispartition);
2791 : Assert(tbinfo->numParents == 1);
2792 :
2793 170 : parentTbinfo = tbinfo->parents[0];
2794 170 : while (parentTbinfo->ispartition)
2795 : {
2796 : Assert(parentTbinfo->numParents == 1);
2797 0 : parentTbinfo = parentTbinfo->parents[0];
2798 : }
2799 :
2800 170 : return parentTbinfo;
2801 : }
2802 :
2803 : /*
2804 : * forcePartitionRootLoad
2805 : * Check if we must force load_via_partition_root for this partition.
2806 : *
2807 : * This is required if any level of ancestral partitioned table has an
2808 : * unsafe partitioning scheme.
2809 : */
2810 : static bool
2811 2150 : forcePartitionRootLoad(const TableInfo *tbinfo)
2812 : {
2813 : TableInfo *parentTbinfo;
2814 :
2815 : Assert(tbinfo->ispartition);
2816 : Assert(tbinfo->numParents == 1);
2817 :
2818 2150 : parentTbinfo = tbinfo->parents[0];
2819 2150 : if (parentTbinfo->unsafe_partitions)
2820 170 : return true;
2821 2412 : while (parentTbinfo->ispartition)
2822 : {
2823 : Assert(parentTbinfo->numParents == 1);
2824 432 : parentTbinfo = parentTbinfo->parents[0];
2825 432 : if (parentTbinfo->unsafe_partitions)
2826 0 : return true;
2827 : }
2828 :
2829 1980 : return false;
2830 : }
2831 :
2832 : /*
2833 : * dumpTableData -
2834 : * dump the contents of a single table
2835 : *
2836 : * Actually, this just makes an ArchiveEntry for the table contents.
2837 : */
2838 : static void
2839 8764 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2840 : {
2841 8764 : DumpOptions *dopt = fout->dopt;
2842 8764 : TableInfo *tbinfo = tdinfo->tdtable;
2843 8764 : PQExpBuffer copyBuf = createPQExpBuffer();
2844 8764 : PQExpBuffer clistBuf = createPQExpBuffer();
2845 : DataDumperPtr dumpFn;
2846 8764 : char *tdDefn = NULL;
2847 : char *copyStmt;
2848 : const char *copyFrom;
2849 :
2850 : /* We had better have loaded per-column details about this table */
2851 : Assert(tbinfo->interesting);
2852 :
2853 : /*
2854 : * When load-via-partition-root is set or forced, get the root table name
2855 : * for the partition table, so that we can reload data through the root
2856 : * table. Then construct a comment to be inserted into the TOC entry's
2857 : * defn field, so that such cases can be identified reliably.
2858 : */
2859 8764 : if (tbinfo->ispartition &&
2860 4204 : (dopt->load_via_partition_root ||
2861 2102 : forcePartitionRootLoad(tbinfo)))
2862 156 : {
2863 : TableInfo *parentTbinfo;
2864 : char *sanitized;
2865 :
2866 156 : parentTbinfo = getRootTableInfo(tbinfo);
2867 156 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2868 156 : sanitized = sanitize_line(copyFrom, true);
2869 156 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2870 : sanitized);
2871 156 : free(sanitized);
2872 156 : tdDefn = pg_strdup(copyBuf->data);
2873 : }
2874 : else
2875 8608 : copyFrom = fmtQualifiedDumpable(tbinfo);
2876 :
2877 8764 : if (dopt->dump_inserts == 0)
2878 : {
2879 : /* Dump/restore using COPY */
2880 8594 : dumpFn = dumpTableData_copy;
2881 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2882 8594 : printfPQExpBuffer(copyBuf, "COPY %s ",
2883 : copyFrom);
2884 8594 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2885 : fmtCopyColumnList(tbinfo, clistBuf));
2886 8594 : copyStmt = copyBuf->data;
2887 : }
2888 : else
2889 : {
2890 : /* Restore using INSERT */
2891 170 : dumpFn = dumpTableData_insert;
2892 170 : copyStmt = NULL;
2893 : }
2894 :
2895 : /*
2896 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2897 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2898 : * See comments for BuildArchiveDependencies.
2899 : */
2900 8764 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2901 : {
2902 : TocEntry *te;
2903 :
2904 8764 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2905 8764 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2906 : .namespace = tbinfo->dobj.namespace->dobj.name,
2907 : .owner = tbinfo->rolname,
2908 : .description = "TABLE DATA",
2909 : .section = SECTION_DATA,
2910 : .createStmt = tdDefn,
2911 : .copyStmt = copyStmt,
2912 : .deps = &(tbinfo->dobj.dumpId),
2913 : .nDeps = 1,
2914 : .dumpFn = dumpFn,
2915 : .dumpArg = tdinfo));
2916 :
2917 : /*
2918 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2919 : * and want to order dump jobs by table size. We choose to measure
2920 : * dataLength in table pages (including TOAST pages) during dump, so
2921 : * no scaling is needed.
2922 : *
2923 : * However, relpages is declared as "integer" in pg_class, and hence
2924 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2925 : * Cast so that we get the right interpretation of table sizes
2926 : * exceeding INT_MAX pages.
2927 : */
2928 8764 : te->dataLength = (BlockNumber) tbinfo->relpages;
2929 8764 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2930 :
2931 : /*
2932 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2933 : * and instead we'd better worry about integer overflow. Clamp to
2934 : * INT_MAX if the correct result exceeds that.
2935 : */
2936 : if (sizeof(te->dataLength) == 4 &&
2937 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2938 : te->dataLength < 0))
2939 : te->dataLength = INT_MAX;
2940 : }
2941 :
2942 8764 : destroyPQExpBuffer(copyBuf);
2943 8764 : destroyPQExpBuffer(clistBuf);
2944 8764 : }
2945 :
2946 : /*
2947 : * refreshMatViewData -
2948 : * load or refresh the contents of a single materialized view
2949 : *
2950 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2951 : * statement.
2952 : */
2953 : static void
2954 804 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2955 : {
2956 804 : TableInfo *tbinfo = tdinfo->tdtable;
2957 : PQExpBuffer q;
2958 :
2959 : /* If the materialized view is not flagged as populated, skip this. */
2960 804 : if (!tbinfo->relispopulated)
2961 148 : return;
2962 :
2963 656 : q = createPQExpBuffer();
2964 :
2965 656 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2966 656 : fmtQualifiedDumpable(tbinfo));
2967 :
2968 656 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2969 656 : ArchiveEntry(fout,
2970 : tdinfo->dobj.catId, /* catalog ID */
2971 656 : tdinfo->dobj.dumpId, /* dump ID */
2972 656 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2973 : .namespace = tbinfo->dobj.namespace->dobj.name,
2974 : .owner = tbinfo->rolname,
2975 : .description = "MATERIALIZED VIEW DATA",
2976 : .section = SECTION_POST_DATA,
2977 : .createStmt = q->data,
2978 : .deps = tdinfo->dobj.dependencies,
2979 : .nDeps = tdinfo->dobj.nDeps));
2980 :
2981 656 : destroyPQExpBuffer(q);
2982 : }
2983 :
2984 : /*
2985 : * getTableData -
2986 : * set up dumpable objects representing the contents of tables
2987 : */
2988 : static void
2989 348 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2990 : {
2991 : int i;
2992 :
2993 92996 : for (i = 0; i < numTables; i++)
2994 : {
2995 92648 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2996 1858 : (!relkind || tblinfo[i].relkind == relkind))
2997 12250 : makeTableDataInfo(dopt, &(tblinfo[i]));
2998 : }
2999 348 : }
3000 :
3001 : /*
3002 : * Make a dumpable object for the data of this specific table
3003 : *
3004 : * Note: we make a TableDataInfo if and only if we are going to dump the
3005 : * table data; the "dump" field in such objects isn't very interesting.
3006 : */
3007 : static void
3008 12472 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3009 : {
3010 : TableDataInfo *tdinfo;
3011 :
3012 : /*
3013 : * Nothing to do if we already decided to dump the table. This will
3014 : * happen for "config" tables.
3015 : */
3016 12472 : if (tbinfo->dataObj != NULL)
3017 2 : return;
3018 :
3019 : /* Skip VIEWs (no data to dump) */
3020 12470 : if (tbinfo->relkind == RELKIND_VIEW)
3021 968 : return;
3022 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3023 11502 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3024 82 : (foreign_servers_include_oids.head == NULL ||
3025 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3026 : tbinfo->foreign_server)))
3027 80 : return;
3028 : /* Skip partitioned tables (data in partitions) */
3029 11422 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3030 998 : return;
3031 :
3032 : /* Don't dump data in unlogged tables, if so requested */
3033 10424 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3034 82 : dopt->no_unlogged_table_data)
3035 36 : return;
3036 :
3037 : /* Check that the data is not explicitly excluded */
3038 10388 : if (simple_oid_list_member(&tabledata_exclude_oids,
3039 : tbinfo->dobj.catId.oid))
3040 16 : return;
3041 :
3042 : /* OK, let's dump it */
3043 10372 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3044 :
3045 10372 : if (tbinfo->relkind == RELKIND_MATVIEW)
3046 804 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3047 9568 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3048 804 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3049 : else
3050 8764 : tdinfo->dobj.objType = DO_TABLE_DATA;
3051 :
3052 : /*
3053 : * Note: use tableoid 0 so that this object won't be mistaken for
3054 : * something that pg_depend entries apply to.
3055 : */
3056 10372 : tdinfo->dobj.catId.tableoid = 0;
3057 10372 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3058 10372 : AssignDumpId(&tdinfo->dobj);
3059 10372 : tdinfo->dobj.name = tbinfo->dobj.name;
3060 10372 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3061 10372 : tdinfo->tdtable = tbinfo;
3062 10372 : tdinfo->filtercond = NULL; /* might get set later */
3063 10372 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3064 :
3065 : /* A TableDataInfo contains data, of course */
3066 10372 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3067 :
3068 10372 : tbinfo->dataObj = tdinfo;
3069 :
3070 : /*
3071 : * Materialized view statistics must be restored after the data, because
3072 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3073 : *
3074 : * The dependency is added here because the statistics objects are created
3075 : * first.
3076 : */
3077 10372 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3078 : {
3079 640 : tbinfo->stats->section = SECTION_POST_DATA;
3080 640 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3081 : }
3082 :
3083 : /* Make sure that we'll collect per-column info for this table. */
3084 10372 : tbinfo->interesting = true;
3085 : }
3086 :
3087 : /*
3088 : * The refresh for a materialized view must be dependent on the refresh for
3089 : * any materialized view that this one is dependent on.
3090 : *
3091 : * This must be called after all the objects are created, but before they are
3092 : * sorted.
3093 : */
3094 : static void
3095 284 : buildMatViewRefreshDependencies(Archive *fout)
3096 : {
3097 : PQExpBuffer query;
3098 : PGresult *res;
3099 : int ntups,
3100 : i;
3101 : int i_classid,
3102 : i_objid,
3103 : i_refobjid;
3104 :
3105 : /* No Mat Views before 9.3. */
3106 284 : if (fout->remoteVersion < 90300)
3107 0 : return;
3108 :
3109 284 : query = createPQExpBuffer();
3110 :
3111 284 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3112 : "( "
3113 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3114 : "FROM pg_depend d1 "
3115 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3116 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3117 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3118 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3119 : "AND d2.objid = r1.oid "
3120 : "AND d2.refobjid <> d1.objid "
3121 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3122 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3123 : CppAsString2(RELKIND_VIEW) ") "
3124 : "WHERE d1.classid = 'pg_class'::regclass "
3125 : "UNION "
3126 : "SELECT w.objid, d3.refobjid, c3.relkind "
3127 : "FROM w "
3128 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3129 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3130 : "AND d3.objid = r3.oid "
3131 : "AND d3.refobjid <> w.refobjid "
3132 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3133 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3134 : CppAsString2(RELKIND_VIEW) ") "
3135 : ") "
3136 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3137 : "FROM w "
3138 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3139 :
3140 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3141 :
3142 284 : ntups = PQntuples(res);
3143 :
3144 284 : i_classid = PQfnumber(res, "classid");
3145 284 : i_objid = PQfnumber(res, "objid");
3146 284 : i_refobjid = PQfnumber(res, "refobjid");
3147 :
3148 848 : for (i = 0; i < ntups; i++)
3149 : {
3150 : CatalogId objId;
3151 : CatalogId refobjId;
3152 : DumpableObject *dobj;
3153 : DumpableObject *refdobj;
3154 : TableInfo *tbinfo;
3155 : TableInfo *reftbinfo;
3156 :
3157 564 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3158 564 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3159 564 : refobjId.tableoid = objId.tableoid;
3160 564 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3161 :
3162 564 : dobj = findObjectByCatalogId(objId);
3163 564 : if (dobj == NULL)
3164 96 : continue;
3165 :
3166 : Assert(dobj->objType == DO_TABLE);
3167 564 : tbinfo = (TableInfo *) dobj;
3168 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3169 564 : dobj = (DumpableObject *) tbinfo->dataObj;
3170 564 : if (dobj == NULL)
3171 96 : continue;
3172 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3173 :
3174 468 : refdobj = findObjectByCatalogId(refobjId);
3175 468 : if (refdobj == NULL)
3176 0 : continue;
3177 :
3178 : Assert(refdobj->objType == DO_TABLE);
3179 468 : reftbinfo = (TableInfo *) refdobj;
3180 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3181 468 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3182 468 : if (refdobj == NULL)
3183 0 : continue;
3184 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3185 :
3186 468 : addObjectDependency(dobj, refdobj->dumpId);
3187 :
3188 468 : if (!reftbinfo->relispopulated)
3189 74 : tbinfo->relispopulated = false;
3190 : }
3191 :
3192 284 : PQclear(res);
3193 :
3194 284 : destroyPQExpBuffer(query);
3195 : }
3196 :
3197 : /*
3198 : * getTableDataFKConstraints -
3199 : * add dump-order dependencies reflecting foreign key constraints
3200 : *
3201 : * This code is executed only in a data-only dump --- in schema+data dumps
3202 : * we handle foreign key issues by not creating the FK constraints until
3203 : * after the data is loaded. In a data-only dump, however, we want to
3204 : * order the table data objects in such a way that a table's referenced
3205 : * tables are restored first. (In the presence of circular references or
3206 : * self-references this may be impossible; we'll detect and complain about
3207 : * that during the dependency sorting step.)
3208 : */
3209 : static void
3210 14 : getTableDataFKConstraints(void)
3211 : {
3212 : DumpableObject **dobjs;
3213 : int numObjs;
3214 : int i;
3215 :
3216 : /* Search through all the dumpable objects for FK constraints */
3217 14 : getDumpableObjects(&dobjs, &numObjs);
3218 51614 : for (i = 0; i < numObjs; i++)
3219 : {
3220 51600 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3221 : {
3222 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3223 : TableInfo *ftable;
3224 :
3225 : /* Not interesting unless both tables are to be dumped */
3226 16 : if (cinfo->contable == NULL ||
3227 16 : cinfo->contable->dataObj == NULL)
3228 8 : continue;
3229 8 : ftable = findTableByOid(cinfo->confrelid);
3230 8 : if (ftable == NULL ||
3231 8 : ftable->dataObj == NULL)
3232 0 : continue;
3233 :
3234 : /*
3235 : * Okay, make referencing table's TABLE_DATA object depend on the
3236 : * referenced table's TABLE_DATA object.
3237 : */
3238 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3239 8 : ftable->dataObj->dobj.dumpId);
3240 : }
3241 : }
3242 14 : free(dobjs);
3243 14 : }
3244 :
3245 :
3246 : /*
3247 : * dumpDatabase:
3248 : * dump the database definition
3249 : */
3250 : static void
3251 164 : dumpDatabase(Archive *fout)
3252 : {
3253 164 : DumpOptions *dopt = fout->dopt;
3254 164 : PQExpBuffer dbQry = createPQExpBuffer();
3255 164 : PQExpBuffer delQry = createPQExpBuffer();
3256 164 : PQExpBuffer creaQry = createPQExpBuffer();
3257 164 : PQExpBuffer labelq = createPQExpBuffer();
3258 164 : PGconn *conn = GetConnection(fout);
3259 : PGresult *res;
3260 : int i_tableoid,
3261 : i_oid,
3262 : i_datname,
3263 : i_datdba,
3264 : i_encoding,
3265 : i_datlocprovider,
3266 : i_collate,
3267 : i_ctype,
3268 : i_datlocale,
3269 : i_daticurules,
3270 : i_frozenxid,
3271 : i_minmxid,
3272 : i_datacl,
3273 : i_acldefault,
3274 : i_datistemplate,
3275 : i_datconnlimit,
3276 : i_datcollversion,
3277 : i_tablespace;
3278 : CatalogId dbCatId;
3279 : DumpId dbDumpId;
3280 : DumpableAcl dbdacl;
3281 : const char *datname,
3282 : *dba,
3283 : *encoding,
3284 : *datlocprovider,
3285 : *collate,
3286 : *ctype,
3287 : *locale,
3288 : *icurules,
3289 : *datistemplate,
3290 : *datconnlimit,
3291 : *tablespace;
3292 : uint32 frozenxid,
3293 : minmxid;
3294 : char *qdatname;
3295 :
3296 164 : pg_log_info("saving database definition");
3297 :
3298 : /*
3299 : * Fetch the database-level properties for this database.
3300 : */
3301 164 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3302 : "datdba, "
3303 : "pg_encoding_to_char(encoding) AS encoding, "
3304 : "datcollate, datctype, datfrozenxid, "
3305 : "datacl, acldefault('d', datdba) AS acldefault, "
3306 : "datistemplate, datconnlimit, ");
3307 164 : if (fout->remoteVersion >= 90300)
3308 164 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3309 : else
3310 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3311 164 : if (fout->remoteVersion >= 170000)
3312 164 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3313 0 : else if (fout->remoteVersion >= 150000)
3314 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3315 : else
3316 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3317 164 : if (fout->remoteVersion >= 160000)
3318 164 : appendPQExpBufferStr(dbQry, "daticurules, ");
3319 : else
3320 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3321 164 : appendPQExpBufferStr(dbQry,
3322 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3323 : "shobj_description(oid, 'pg_database') AS description "
3324 : "FROM pg_database "
3325 : "WHERE datname = current_database()");
3326 :
3327 164 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3328 :
3329 164 : i_tableoid = PQfnumber(res, "tableoid");
3330 164 : i_oid = PQfnumber(res, "oid");
3331 164 : i_datname = PQfnumber(res, "datname");
3332 164 : i_datdba = PQfnumber(res, "datdba");
3333 164 : i_encoding = PQfnumber(res, "encoding");
3334 164 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3335 164 : i_collate = PQfnumber(res, "datcollate");
3336 164 : i_ctype = PQfnumber(res, "datctype");
3337 164 : i_datlocale = PQfnumber(res, "datlocale");
3338 164 : i_daticurules = PQfnumber(res, "daticurules");
3339 164 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3340 164 : i_minmxid = PQfnumber(res, "datminmxid");
3341 164 : i_datacl = PQfnumber(res, "datacl");
3342 164 : i_acldefault = PQfnumber(res, "acldefault");
3343 164 : i_datistemplate = PQfnumber(res, "datistemplate");
3344 164 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3345 164 : i_datcollversion = PQfnumber(res, "datcollversion");
3346 164 : i_tablespace = PQfnumber(res, "tablespace");
3347 :
3348 164 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3349 164 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3350 164 : datname = PQgetvalue(res, 0, i_datname);
3351 164 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3352 164 : encoding = PQgetvalue(res, 0, i_encoding);
3353 164 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3354 164 : collate = PQgetvalue(res, 0, i_collate);
3355 164 : ctype = PQgetvalue(res, 0, i_ctype);
3356 164 : if (!PQgetisnull(res, 0, i_datlocale))
3357 28 : locale = PQgetvalue(res, 0, i_datlocale);
3358 : else
3359 136 : locale = NULL;
3360 164 : if (!PQgetisnull(res, 0, i_daticurules))
3361 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3362 : else
3363 164 : icurules = NULL;
3364 164 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3365 164 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3366 164 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3367 164 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3368 164 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3369 164 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3370 164 : tablespace = PQgetvalue(res, 0, i_tablespace);
3371 :
3372 164 : qdatname = pg_strdup(fmtId(datname));
3373 :
3374 : /*
3375 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3376 : * to preserve that), as well as the encoding, locale, and tablespace
3377 : * since those can't be altered later. Other DB properties are left to
3378 : * the DATABASE PROPERTIES entry, so that they can be applied after
3379 : * reconnecting to the target DB.
3380 : *
3381 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3382 : * shown it to be faster. When the server is in binary upgrade mode, it
3383 : * will also skip the checkpoints this strategy ordinarily performs.
3384 : */
3385 164 : if (dopt->binary_upgrade)
3386 : {
3387 70 : appendPQExpBuffer(creaQry,
3388 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3389 : "OID = %u STRATEGY = FILE_COPY",
3390 : qdatname, dbCatId.oid);
3391 : }
3392 : else
3393 : {
3394 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3395 : qdatname);
3396 : }
3397 164 : if (strlen(encoding) > 0)
3398 : {
3399 164 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3400 164 : appendStringLiteralAH(creaQry, encoding, fout);
3401 : }
3402 :
3403 164 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3404 164 : if (datlocprovider[0] == 'b')
3405 28 : appendPQExpBufferStr(creaQry, "builtin");
3406 136 : else if (datlocprovider[0] == 'c')
3407 136 : appendPQExpBufferStr(creaQry, "libc");
3408 0 : else if (datlocprovider[0] == 'i')
3409 0 : appendPQExpBufferStr(creaQry, "icu");
3410 : else
3411 0 : pg_fatal("unrecognized locale provider: %s",
3412 : datlocprovider);
3413 :
3414 164 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3415 : {
3416 164 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3417 164 : appendStringLiteralAH(creaQry, collate, fout);
3418 : }
3419 : else
3420 : {
3421 0 : if (strlen(collate) > 0)
3422 : {
3423 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3424 0 : appendStringLiteralAH(creaQry, collate, fout);
3425 : }
3426 0 : if (strlen(ctype) > 0)
3427 : {
3428 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3429 0 : appendStringLiteralAH(creaQry, ctype, fout);
3430 : }
3431 : }
3432 164 : if (locale)
3433 : {
3434 28 : if (datlocprovider[0] == 'b')
3435 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3436 : else
3437 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3438 :
3439 28 : appendStringLiteralAH(creaQry, locale, fout);
3440 : }
3441 :
3442 164 : if (icurules)
3443 : {
3444 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3445 0 : appendStringLiteralAH(creaQry, icurules, fout);
3446 : }
3447 :
3448 : /*
3449 : * For binary upgrade, carry over the collation version. For normal
3450 : * dump/restore, omit the version, so that it is computed upon restore.
3451 : */
3452 164 : if (dopt->binary_upgrade)
3453 : {
3454 70 : if (!PQgetisnull(res, 0, i_datcollversion))
3455 : {
3456 70 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3457 70 : appendStringLiteralAH(creaQry,
3458 : PQgetvalue(res, 0, i_datcollversion),
3459 : fout);
3460 : }
3461 : }
3462 :
3463 : /*
3464 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3465 : * thing; the decision whether to specify a tablespace should be left till
3466 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3467 : * label the DATABASE entry with the tablespace and let the normal
3468 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3469 : * attention to default_tablespace, so that won't work.
3470 : */
3471 164 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3472 10 : !dopt->outputNoTablespaces)
3473 10 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3474 : fmtId(tablespace));
3475 164 : appendPQExpBufferStr(creaQry, ";\n");
3476 :
3477 164 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3478 : qdatname);
3479 :
3480 164 : dbDumpId = createDumpId();
3481 :
3482 164 : ArchiveEntry(fout,
3483 : dbCatId, /* catalog ID */
3484 : dbDumpId, /* dump ID */
3485 164 : ARCHIVE_OPTS(.tag = datname,
3486 : .owner = dba,
3487 : .description = "DATABASE",
3488 : .section = SECTION_PRE_DATA,
3489 : .createStmt = creaQry->data,
3490 : .dropStmt = delQry->data));
3491 :
3492 : /* Compute correct tag for archive entry */
3493 164 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3494 :
3495 : /* Dump DB comment if any */
3496 : {
3497 : /*
3498 : * 8.2 and up keep comments on shared objects in a shared table, so we
3499 : * cannot use the dumpComment() code used for other database objects.
3500 : * Be careful that the ArchiveEntry parameters match that function.
3501 : */
3502 164 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3503 :
3504 164 : if (comment && *comment && !dopt->no_comments)
3505 : {
3506 74 : resetPQExpBuffer(dbQry);
3507 :
3508 : /*
3509 : * Generates warning when loaded into a differently-named
3510 : * database.
3511 : */
3512 74 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3513 74 : appendStringLiteralAH(dbQry, comment, fout);
3514 74 : appendPQExpBufferStr(dbQry, ";\n");
3515 :
3516 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3517 74 : ARCHIVE_OPTS(.tag = labelq->data,
3518 : .owner = dba,
3519 : .description = "COMMENT",
3520 : .section = SECTION_NONE,
3521 : .createStmt = dbQry->data,
3522 : .deps = &dbDumpId,
3523 : .nDeps = 1));
3524 : }
3525 : }
3526 :
3527 : /* Dump DB security label, if enabled */
3528 164 : if (!dopt->no_security_labels)
3529 : {
3530 : PGresult *shres;
3531 : PQExpBuffer seclabelQry;
3532 :
3533 164 : seclabelQry = createPQExpBuffer();
3534 :
3535 164 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3536 164 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3537 164 : resetPQExpBuffer(seclabelQry);
3538 164 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3539 164 : if (seclabelQry->len > 0)
3540 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3541 0 : ARCHIVE_OPTS(.tag = labelq->data,
3542 : .owner = dba,
3543 : .description = "SECURITY LABEL",
3544 : .section = SECTION_NONE,
3545 : .createStmt = seclabelQry->data,
3546 : .deps = &dbDumpId,
3547 : .nDeps = 1));
3548 164 : destroyPQExpBuffer(seclabelQry);
3549 164 : PQclear(shres);
3550 : }
3551 :
3552 : /*
3553 : * Dump ACL if any. Note that we do not support initial privileges
3554 : * (pg_init_privs) on databases.
3555 : */
3556 164 : dbdacl.privtype = 0;
3557 164 : dbdacl.initprivs = NULL;
3558 :
3559 164 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3560 : qdatname, NULL, NULL,
3561 : NULL, dba, &dbdacl);
3562 :
3563 : /*
3564 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3565 : * non-default database-level properties. (The reason this must be
3566 : * separate is that we cannot put any additional commands into the TOC
3567 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3568 : * in an implicit transaction block, and the backend won't allow CREATE
3569 : * DATABASE in that context.)
3570 : */
3571 164 : resetPQExpBuffer(creaQry);
3572 164 : resetPQExpBuffer(delQry);
3573 :
3574 164 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3575 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3576 : qdatname, datconnlimit);
3577 :
3578 164 : if (strcmp(datistemplate, "t") == 0)
3579 : {
3580 20 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3581 : qdatname);
3582 :
3583 : /*
3584 : * The backend won't accept DROP DATABASE on a template database. We
3585 : * can deal with that by removing the template marking before the DROP
3586 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3587 : * since no such command is currently supported, fake it with a direct
3588 : * UPDATE on pg_database.
3589 : */
3590 20 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3591 : "SET datistemplate = false WHERE datname = ");
3592 20 : appendStringLiteralAH(delQry, datname, fout);
3593 20 : appendPQExpBufferStr(delQry, ";\n");
3594 : }
3595 :
3596 : /*
3597 : * We do not restore pg_database.dathasloginevt because it is set
3598 : * automatically on login event trigger creation.
3599 : */
3600 :
3601 : /* Add database-specific SET options */
3602 164 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3603 :
3604 : /*
3605 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3606 : * entry, too, for lack of a better place.
3607 : */
3608 164 : if (dopt->binary_upgrade)
3609 : {
3610 70 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3611 70 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3612 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3613 : "WHERE datname = ",
3614 : frozenxid, minmxid);
3615 70 : appendStringLiteralAH(creaQry, datname, fout);
3616 70 : appendPQExpBufferStr(creaQry, ";\n");
3617 : }
3618 :
3619 164 : if (creaQry->len > 0)
3620 78 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3621 78 : ARCHIVE_OPTS(.tag = datname,
3622 : .owner = dba,
3623 : .description = "DATABASE PROPERTIES",
3624 : .section = SECTION_PRE_DATA,
3625 : .createStmt = creaQry->data,
3626 : .dropStmt = delQry->data,
3627 : .deps = &dbDumpId));
3628 :
3629 : /*
3630 : * pg_largeobject comes from the old system intact, so set its
3631 : * relfrozenxids, relminmxids and relfilenode.
3632 : */
3633 164 : if (dopt->binary_upgrade)
3634 : {
3635 : PGresult *lo_res;
3636 70 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3637 70 : PQExpBuffer loOutQry = createPQExpBuffer();
3638 70 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3639 : int ii_relfrozenxid,
3640 : ii_relfilenode,
3641 : ii_oid,
3642 : ii_relminmxid;
3643 :
3644 : /*
3645 : * pg_largeobject
3646 : */
3647 70 : if (fout->remoteVersion >= 90300)
3648 70 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3649 : "FROM pg_catalog.pg_class\n"
3650 : "WHERE oid IN (%u, %u);\n",
3651 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3652 : else
3653 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3654 : "FROM pg_catalog.pg_class\n"
3655 : "WHERE oid IN (%u, %u);\n",
3656 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3657 :
3658 70 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3659 :
3660 70 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3661 70 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3662 70 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3663 70 : ii_oid = PQfnumber(lo_res, "oid");
3664 :
3665 70 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3666 70 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3667 210 : for (int i = 0; i < PQntuples(lo_res); ++i)
3668 : {
3669 : Oid oid;
3670 : RelFileNumber relfilenumber;
3671 :
3672 140 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3673 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3674 : "WHERE oid = %u;\n",
3675 140 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3676 140 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3677 140 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3678 :
3679 140 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3680 140 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3681 :
3682 140 : if (oid == LargeObjectRelationId)
3683 70 : appendPQExpBuffer(loOutQry,
3684 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3685 : relfilenumber);
3686 70 : else if (oid == LargeObjectLOidPNIndexId)
3687 70 : appendPQExpBuffer(loOutQry,
3688 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3689 : relfilenumber);
3690 : }
3691 :
3692 70 : appendPQExpBufferStr(loOutQry,
3693 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3694 70 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3695 :
3696 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3697 70 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3698 : .description = "pg_largeobject",
3699 : .section = SECTION_PRE_DATA,
3700 : .createStmt = loOutQry->data));
3701 :
3702 70 : PQclear(lo_res);
3703 :
3704 70 : destroyPQExpBuffer(loFrozenQry);
3705 70 : destroyPQExpBuffer(loHorizonQry);
3706 70 : destroyPQExpBuffer(loOutQry);
3707 : }
3708 :
3709 164 : PQclear(res);
3710 :
3711 164 : free(qdatname);
3712 164 : destroyPQExpBuffer(dbQry);
3713 164 : destroyPQExpBuffer(delQry);
3714 164 : destroyPQExpBuffer(creaQry);
3715 164 : destroyPQExpBuffer(labelq);
3716 164 : }
3717 :
3718 : /*
3719 : * Collect any database-specific or role-and-database-specific SET options
3720 : * for this database, and append them to outbuf.
3721 : */
3722 : static void
3723 164 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3724 : const char *dbname, Oid dboid)
3725 : {
3726 164 : PGconn *conn = GetConnection(AH);
3727 164 : PQExpBuffer buf = createPQExpBuffer();
3728 : PGresult *res;
3729 :
3730 : /* First collect database-specific options */
3731 164 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3732 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3733 : dboid);
3734 :
3735 164 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3736 :
3737 224 : for (int i = 0; i < PQntuples(res); i++)
3738 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3739 : "DATABASE", dbname, NULL, NULL,
3740 : outbuf);
3741 :
3742 164 : PQclear(res);
3743 :
3744 : /* Now look for role-and-database-specific options */
3745 164 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3746 : "FROM pg_db_role_setting s, pg_roles r "
3747 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3748 : dboid);
3749 :
3750 164 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3751 :
3752 164 : for (int i = 0; i < PQntuples(res); i++)
3753 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3754 0 : "ROLE", PQgetvalue(res, i, 0),
3755 : "DATABASE", dbname,
3756 : outbuf);
3757 :
3758 164 : PQclear(res);
3759 :
3760 164 : destroyPQExpBuffer(buf);
3761 164 : }
3762 :
3763 : /*
3764 : * dumpEncoding: put the correct encoding into the archive
3765 : */
3766 : static void
3767 364 : dumpEncoding(Archive *AH)
3768 : {
3769 364 : const char *encname = pg_encoding_to_char(AH->encoding);
3770 364 : PQExpBuffer qry = createPQExpBuffer();
3771 :
3772 364 : pg_log_info("saving encoding = %s", encname);
3773 :
3774 364 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3775 364 : appendStringLiteralAH(qry, encname, AH);
3776 364 : appendPQExpBufferStr(qry, ";\n");
3777 :
3778 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3779 364 : ARCHIVE_OPTS(.tag = "ENCODING",
3780 : .description = "ENCODING",
3781 : .section = SECTION_PRE_DATA,
3782 : .createStmt = qry->data));
3783 :
3784 364 : destroyPQExpBuffer(qry);
3785 364 : }
3786 :
3787 :
3788 : /*
3789 : * dumpStdStrings: put the correct escape string behavior into the archive
3790 : */
3791 : static void
3792 364 : dumpStdStrings(Archive *AH)
3793 : {
3794 364 : const char *stdstrings = AH->std_strings ? "on" : "off";
3795 364 : PQExpBuffer qry = createPQExpBuffer();
3796 :
3797 364 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3798 : stdstrings);
3799 :
3800 364 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3801 : stdstrings);
3802 :
3803 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3804 364 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3805 : .description = "STDSTRINGS",
3806 : .section = SECTION_PRE_DATA,
3807 : .createStmt = qry->data));
3808 :
3809 364 : destroyPQExpBuffer(qry);
3810 364 : }
3811 :
3812 : /*
3813 : * dumpSearchPath: record the active search_path in the archive
3814 : */
3815 : static void
3816 364 : dumpSearchPath(Archive *AH)
3817 : {
3818 364 : PQExpBuffer qry = createPQExpBuffer();
3819 364 : PQExpBuffer path = createPQExpBuffer();
3820 : PGresult *res;
3821 364 : char **schemanames = NULL;
3822 364 : int nschemanames = 0;
3823 : int i;
3824 :
3825 : /*
3826 : * We use the result of current_schemas(), not the search_path GUC,
3827 : * because that might contain wildcards such as "$user", which won't
3828 : * necessarily have the same value during restore. Also, this way avoids
3829 : * listing schemas that may appear in search_path but not actually exist,
3830 : * which seems like a prudent exclusion.
3831 : */
3832 364 : res = ExecuteSqlQueryForSingleRow(AH,
3833 : "SELECT pg_catalog.current_schemas(false)");
3834 :
3835 364 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3836 0 : pg_fatal("could not parse result of current_schemas()");
3837 :
3838 : /*
3839 : * We use set_config(), not a simple "SET search_path" command, because
3840 : * the latter has less-clean behavior if the search path is empty. While
3841 : * that's likely to get fixed at some point, it seems like a good idea to
3842 : * be as backwards-compatible as possible in what we put into archives.
3843 : */
3844 364 : for (i = 0; i < nschemanames; i++)
3845 : {
3846 0 : if (i > 0)
3847 0 : appendPQExpBufferStr(path, ", ");
3848 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3849 : }
3850 :
3851 364 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3852 364 : appendStringLiteralAH(qry, path->data, AH);
3853 364 : appendPQExpBufferStr(qry, ", false);\n");
3854 :
3855 364 : pg_log_info("saving \"search_path = %s\"", path->data);
3856 :
3857 364 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3858 364 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3859 : .description = "SEARCHPATH",
3860 : .section = SECTION_PRE_DATA,
3861 : .createStmt = qry->data));
3862 :
3863 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3864 364 : AH->searchpath = pg_strdup(qry->data);
3865 :
3866 364 : free(schemanames);
3867 364 : PQclear(res);
3868 364 : destroyPQExpBuffer(qry);
3869 364 : destroyPQExpBuffer(path);
3870 364 : }
3871 :
3872 :
3873 : /*
3874 : * getLOs:
3875 : * Collect schema-level data about large objects
3876 : */
3877 : static void
3878 308 : getLOs(Archive *fout)
3879 : {
3880 308 : DumpOptions *dopt = fout->dopt;
3881 308 : PQExpBuffer loQry = createPQExpBuffer();
3882 : PGresult *res;
3883 : int ntups;
3884 : int i;
3885 : int n;
3886 : int i_oid;
3887 : int i_lomowner;
3888 : int i_lomacl;
3889 : int i_acldefault;
3890 :
3891 308 : pg_log_info("reading large objects");
3892 :
3893 : /*
3894 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3895 : * with the same owner/ACL appear together.
3896 : */
3897 308 : appendPQExpBufferStr(loQry,
3898 : "SELECT oid, lomowner, lomacl, "
3899 : "acldefault('L', lomowner) AS acldefault "
3900 : "FROM pg_largeobject_metadata "
3901 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3902 :
3903 308 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3904 :
3905 308 : i_oid = PQfnumber(res, "oid");
3906 308 : i_lomowner = PQfnumber(res, "lomowner");
3907 308 : i_lomacl = PQfnumber(res, "lomacl");
3908 308 : i_acldefault = PQfnumber(res, "acldefault");
3909 :
3910 308 : ntups = PQntuples(res);
3911 :
3912 : /*
3913 : * Group the blobs into suitably-sized groups that have the same owner and
3914 : * ACL setting, and build a metadata and a data DumpableObject for each
3915 : * group. (If we supported initprivs for blobs, we'd have to insist that
3916 : * groups also share initprivs settings, since the DumpableObject only has
3917 : * room for one.) i is the index of the first tuple in the current group,
3918 : * and n is the number of tuples we include in the group.
3919 : */
3920 466 : for (i = 0; i < ntups; i += n)
3921 : {
3922 158 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3923 158 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3924 158 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3925 : LoInfo *loinfo;
3926 : DumpableObject *lodata;
3927 : char namebuf[64];
3928 :
3929 : /* Scan to find first tuple not to be included in group */
3930 158 : n = 1;
3931 178 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3932 : {
3933 94 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3934 94 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3935 : break;
3936 20 : n++;
3937 : }
3938 :
3939 : /* Build the metadata DumpableObject */
3940 158 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3941 :
3942 158 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3943 158 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3944 158 : loinfo->dobj.catId.oid = thisoid;
3945 158 : AssignDumpId(&loinfo->dobj);
3946 :
3947 158 : if (n > 1)
3948 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3949 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3950 : else
3951 148 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3952 158 : loinfo->dobj.name = pg_strdup(namebuf);
3953 158 : loinfo->dacl.acl = pg_strdup(thisacl);
3954 158 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3955 158 : loinfo->dacl.privtype = 0;
3956 158 : loinfo->dacl.initprivs = NULL;
3957 158 : loinfo->rolname = getRoleName(thisowner);
3958 158 : loinfo->numlos = n;
3959 158 : loinfo->looids[0] = thisoid;
3960 : /* Collect OIDs of the remaining blobs in this group */
3961 178 : for (int k = 1; k < n; k++)
3962 : {
3963 : CatalogId extraID;
3964 :
3965 20 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3966 :
3967 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3968 20 : extraID.tableoid = LargeObjectRelationId;
3969 20 : extraID.oid = loinfo->looids[k];
3970 20 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3971 : }
3972 :
3973 : /* LOs have data */
3974 158 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3975 :
3976 : /* Mark whether LO group has a non-empty ACL */
3977 158 : if (!PQgetisnull(res, i, i_lomacl))
3978 74 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3979 :
3980 : /*
3981 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3982 : * as it will be copied by pg_upgrade, which simply copies the
3983 : * pg_largeobject table. We *do* however dump out anything but the
3984 : * data, as pg_upgrade copies just pg_largeobject, but not
3985 : * pg_largeobject_metadata, after the dump is restored. In versions
3986 : * before v12, this is done via proper large object commands. In
3987 : * newer versions, we dump the content of pg_largeobject_metadata and
3988 : * any associated pg_shdepend rows, which is faster to restore. (On
3989 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
3990 : * column is hidden and won't be dumped.)
3991 : */
3992 158 : if (dopt->binary_upgrade)
3993 : {
3994 6 : if (fout->remoteVersion >= 120000)
3995 : {
3996 : /*
3997 : * We should've saved pg_largeobject_metadata's dump ID before
3998 : * this point.
3999 : */
4000 : Assert(lo_metadata_dumpId);
4001 :
4002 6 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4003 :
4004 : /*
4005 : * Mark the large object as dependent on
4006 : * pg_largeobject_metadata so that any large object
4007 : * comments/seclables are dumped after it.
4008 : */
4009 6 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4010 6 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4011 6 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4012 : }
4013 : else
4014 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4015 : }
4016 :
4017 : /*
4018 : * Create a "BLOBS" data item for the group, too. This is just a
4019 : * placeholder for sorting; it carries no data now.
4020 : */
4021 158 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4022 158 : lodata->objType = DO_LARGE_OBJECT_DATA;
4023 158 : lodata->catId = nilCatalogId;
4024 158 : AssignDumpId(lodata);
4025 158 : lodata->name = pg_strdup(namebuf);
4026 158 : lodata->components |= DUMP_COMPONENT_DATA;
4027 : /* Set up explicit dependency from data to metadata */
4028 158 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4029 158 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4030 158 : lodata->nDeps = lodata->allocDeps = 1;
4031 : }
4032 :
4033 308 : PQclear(res);
4034 308 : destroyPQExpBuffer(loQry);
4035 308 : }
4036 :
4037 : /*
4038 : * dumpLO
4039 : *
4040 : * dump the definition (metadata) of the given large object group
4041 : */
4042 : static void
4043 156 : dumpLO(Archive *fout, const LoInfo *loinfo)
4044 : {
4045 156 : PQExpBuffer cquery = createPQExpBuffer();
4046 :
4047 : /*
4048 : * The "definition" is just a newline-separated list of OIDs. We need to
4049 : * put something into the dropStmt too, but it can just be a comment.
4050 : */
4051 332 : for (int i = 0; i < loinfo->numlos; i++)
4052 176 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4053 :
4054 156 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4055 152 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4056 152 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4057 : .owner = loinfo->rolname,
4058 : .description = "BLOB METADATA",
4059 : .section = SECTION_DATA,
4060 : .createStmt = cquery->data,
4061 : .dropStmt = "-- dummy"));
4062 :
4063 : /*
4064 : * Dump per-blob comments and seclabels if any. We assume these are rare
4065 : * enough that it's okay to generate retail TOC entries for them.
4066 : */
4067 156 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4068 : DUMP_COMPONENT_SECLABEL))
4069 : {
4070 188 : for (int i = 0; i < loinfo->numlos; i++)
4071 : {
4072 : CatalogId catId;
4073 : char namebuf[32];
4074 :
4075 : /* Build identifying info for this blob */
4076 104 : catId.tableoid = loinfo->dobj.catId.tableoid;
4077 104 : catId.oid = loinfo->looids[i];
4078 104 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4079 :
4080 104 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4081 104 : dumpComment(fout, "LARGE OBJECT", namebuf,
4082 104 : NULL, loinfo->rolname,
4083 104 : catId, 0, loinfo->dobj.dumpId);
4084 :
4085 104 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4086 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4087 0 : NULL, loinfo->rolname,
4088 0 : catId, 0, loinfo->dobj.dumpId);
4089 : }
4090 : }
4091 :
4092 : /*
4093 : * Dump the ACLs if any (remember that all blobs in the group will have
4094 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4095 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4096 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4097 : * string to emit a mutated version for each blob.
4098 : */
4099 156 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4100 : {
4101 : char namebuf[32];
4102 :
4103 : /* Build identifying info for the first blob */
4104 72 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4105 :
4106 72 : if (loinfo->numlos > 1)
4107 : {
4108 : char tagbuf[64];
4109 :
4110 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4111 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4112 :
4113 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4114 : "LARGE OBJECT", namebuf, NULL, NULL,
4115 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4116 : }
4117 : else
4118 : {
4119 72 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4120 : "LARGE OBJECT", namebuf, NULL, NULL,
4121 72 : NULL, loinfo->rolname, &loinfo->dacl);
4122 : }
4123 : }
4124 :
4125 156 : destroyPQExpBuffer(cquery);
4126 156 : }
4127 :
4128 : /*
4129 : * dumpLOs:
4130 : * dump the data contents of the large objects in the given group
4131 : */
4132 : static int
4133 144 : dumpLOs(Archive *fout, const void *arg)
4134 : {
4135 144 : const LoInfo *loinfo = (const LoInfo *) arg;
4136 144 : PGconn *conn = GetConnection(fout);
4137 : char buf[LOBBUFSIZE];
4138 :
4139 144 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4140 :
4141 304 : for (int i = 0; i < loinfo->numlos; i++)
4142 : {
4143 160 : Oid loOid = loinfo->looids[i];
4144 : int loFd;
4145 : int cnt;
4146 :
4147 : /* Open the LO */
4148 160 : loFd = lo_open(conn, loOid, INV_READ);
4149 160 : if (loFd == -1)
4150 0 : pg_fatal("could not open large object %u: %s",
4151 : loOid, PQerrorMessage(conn));
4152 :
4153 160 : StartLO(fout, loOid);
4154 :
4155 : /* Now read it in chunks, sending data to archive */
4156 : do
4157 : {
4158 244 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4159 244 : if (cnt < 0)
4160 0 : pg_fatal("error reading large object %u: %s",
4161 : loOid, PQerrorMessage(conn));
4162 :
4163 244 : WriteData(fout, buf, cnt);
4164 244 : } while (cnt > 0);
4165 :
4166 160 : lo_close(conn, loFd);
4167 :
4168 160 : EndLO(fout, loOid);
4169 : }
4170 :
4171 144 : return 1;
4172 : }
4173 :
4174 : /*
4175 : * getPolicies
4176 : * get information about all RLS policies on dumpable tables.
4177 : */
4178 : void
4179 364 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4180 : {
4181 364 : DumpOptions *dopt = fout->dopt;
4182 : PQExpBuffer query;
4183 : PQExpBuffer tbloids;
4184 : PGresult *res;
4185 : PolicyInfo *polinfo;
4186 : int i_oid;
4187 : int i_tableoid;
4188 : int i_polrelid;
4189 : int i_polname;
4190 : int i_polcmd;
4191 : int i_polpermissive;
4192 : int i_polroles;
4193 : int i_polqual;
4194 : int i_polwithcheck;
4195 : int i,
4196 : j,
4197 : ntups;
4198 :
4199 : /* No policies before 9.5 */
4200 364 : if (fout->remoteVersion < 90500)
4201 0 : return;
4202 :
4203 : /* Skip if --no-policies was specified */
4204 364 : if (dopt->no_policies)
4205 2 : return;
4206 :
4207 362 : query = createPQExpBuffer();
4208 362 : tbloids = createPQExpBuffer();
4209 :
4210 : /*
4211 : * Identify tables of interest, and check which ones have RLS enabled.
4212 : */
4213 362 : appendPQExpBufferChar(tbloids, '{');
4214 96272 : for (i = 0; i < numTables; i++)
4215 : {
4216 95910 : TableInfo *tbinfo = &tblinfo[i];
4217 :
4218 : /* Ignore row security on tables not to be dumped */
4219 95910 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4220 81690 : continue;
4221 :
4222 : /* It can't have RLS or policies if it's not a table */
4223 14220 : if (tbinfo->relkind != RELKIND_RELATION &&
4224 4040 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4225 2844 : continue;
4226 :
4227 : /* Add it to the list of table OIDs to be probed below */
4228 11376 : if (tbloids->len > 1) /* do we have more than the '{'? */
4229 11142 : appendPQExpBufferChar(tbloids, ',');
4230 11376 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4231 :
4232 : /* Is RLS enabled? (That's separate from whether it has policies) */
4233 11376 : if (tbinfo->rowsec)
4234 : {
4235 112 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4236 :
4237 : /*
4238 : * We represent RLS being enabled on a table by creating a
4239 : * PolicyInfo object with null polname.
4240 : *
4241 : * Note: use tableoid 0 so that this object won't be mistaken for
4242 : * something that pg_depend entries apply to.
4243 : */
4244 112 : polinfo = pg_malloc(sizeof(PolicyInfo));
4245 112 : polinfo->dobj.objType = DO_POLICY;
4246 112 : polinfo->dobj.catId.tableoid = 0;
4247 112 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4248 112 : AssignDumpId(&polinfo->dobj);
4249 112 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4250 112 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4251 112 : polinfo->poltable = tbinfo;
4252 112 : polinfo->polname = NULL;
4253 112 : polinfo->polcmd = '\0';
4254 112 : polinfo->polpermissive = 0;
4255 112 : polinfo->polroles = NULL;
4256 112 : polinfo->polqual = NULL;
4257 112 : polinfo->polwithcheck = NULL;
4258 : }
4259 : }
4260 362 : appendPQExpBufferChar(tbloids, '}');
4261 :
4262 : /*
4263 : * Now, read all RLS policies belonging to the tables of interest, and
4264 : * create PolicyInfo objects for them. (Note that we must filter the
4265 : * results server-side not locally, because we dare not apply pg_get_expr
4266 : * to tables we don't have lock on.)
4267 : */
4268 362 : pg_log_info("reading row-level security policies");
4269 :
4270 362 : printfPQExpBuffer(query,
4271 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4272 362 : if (fout->remoteVersion >= 100000)
4273 362 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4274 : else
4275 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4276 362 : appendPQExpBuffer(query,
4277 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4278 : " 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, "
4279 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4280 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4281 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4282 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4283 : tbloids->data);
4284 :
4285 362 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4286 :
4287 362 : ntups = PQntuples(res);
4288 362 : if (ntups > 0)
4289 : {
4290 92 : i_oid = PQfnumber(res, "oid");
4291 92 : i_tableoid = PQfnumber(res, "tableoid");
4292 92 : i_polrelid = PQfnumber(res, "polrelid");
4293 92 : i_polname = PQfnumber(res, "polname");
4294 92 : i_polcmd = PQfnumber(res, "polcmd");
4295 92 : i_polpermissive = PQfnumber(res, "polpermissive");
4296 92 : i_polroles = PQfnumber(res, "polroles");
4297 92 : i_polqual = PQfnumber(res, "polqual");
4298 92 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4299 :
4300 92 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4301 :
4302 674 : for (j = 0; j < ntups; j++)
4303 : {
4304 582 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4305 582 : TableInfo *tbinfo = findTableByOid(polrelid);
4306 :
4307 582 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4308 :
4309 582 : polinfo[j].dobj.objType = DO_POLICY;
4310 582 : polinfo[j].dobj.catId.tableoid =
4311 582 : atooid(PQgetvalue(res, j, i_tableoid));
4312 582 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4313 582 : AssignDumpId(&polinfo[j].dobj);
4314 582 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4315 582 : polinfo[j].poltable = tbinfo;
4316 582 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4317 582 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4318 :
4319 582 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4320 582 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4321 :
4322 582 : if (PQgetisnull(res, j, i_polroles))
4323 254 : polinfo[j].polroles = NULL;
4324 : else
4325 328 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4326 :
4327 582 : if (PQgetisnull(res, j, i_polqual))
4328 82 : polinfo[j].polqual = NULL;
4329 : else
4330 500 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4331 :
4332 582 : if (PQgetisnull(res, j, i_polwithcheck))
4333 306 : polinfo[j].polwithcheck = NULL;
4334 : else
4335 276 : polinfo[j].polwithcheck
4336 276 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4337 : }
4338 : }
4339 :
4340 362 : PQclear(res);
4341 :
4342 362 : destroyPQExpBuffer(query);
4343 362 : destroyPQExpBuffer(tbloids);
4344 : }
4345 :
4346 : /*
4347 : * dumpPolicy
4348 : * dump the definition of the given policy
4349 : */
4350 : static void
4351 694 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4352 : {
4353 694 : DumpOptions *dopt = fout->dopt;
4354 694 : TableInfo *tbinfo = polinfo->poltable;
4355 : PQExpBuffer query;
4356 : PQExpBuffer delqry;
4357 : PQExpBuffer polprefix;
4358 : char *qtabname;
4359 : const char *cmd;
4360 : char *tag;
4361 :
4362 : /* Do nothing if not dumping schema */
4363 694 : if (!dopt->dumpSchema)
4364 98 : return;
4365 :
4366 : /*
4367 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4368 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4369 : * ROW LEVEL SECURITY.
4370 : */
4371 596 : if (polinfo->polname == NULL)
4372 : {
4373 98 : query = createPQExpBuffer();
4374 :
4375 98 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4376 98 : fmtQualifiedDumpable(tbinfo));
4377 :
4378 : /*
4379 : * We must emit the ROW SECURITY object's dependency on its table
4380 : * explicitly, because it will not match anything in pg_depend (unlike
4381 : * the case for other PolicyInfo objects).
4382 : */
4383 98 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4384 98 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4385 98 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4386 : .namespace = polinfo->dobj.namespace->dobj.name,
4387 : .owner = tbinfo->rolname,
4388 : .description = "ROW SECURITY",
4389 : .section = SECTION_POST_DATA,
4390 : .createStmt = query->data,
4391 : .deps = &(tbinfo->dobj.dumpId),
4392 : .nDeps = 1));
4393 :
4394 98 : destroyPQExpBuffer(query);
4395 98 : return;
4396 : }
4397 :
4398 498 : if (polinfo->polcmd == '*')
4399 166 : cmd = "";
4400 332 : else if (polinfo->polcmd == 'r')
4401 88 : cmd = " FOR SELECT";
4402 244 : else if (polinfo->polcmd == 'a')
4403 68 : cmd = " FOR INSERT";
4404 176 : else if (polinfo->polcmd == 'w')
4405 88 : cmd = " FOR UPDATE";
4406 88 : else if (polinfo->polcmd == 'd')
4407 88 : cmd = " FOR DELETE";
4408 : else
4409 0 : pg_fatal("unexpected policy command type: %c",
4410 : polinfo->polcmd);
4411 :
4412 498 : query = createPQExpBuffer();
4413 498 : delqry = createPQExpBuffer();
4414 498 : polprefix = createPQExpBuffer();
4415 :
4416 498 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4417 :
4418 498 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4419 :
4420 498 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4421 498 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4422 :
4423 498 : if (polinfo->polroles != NULL)
4424 272 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4425 :
4426 498 : if (polinfo->polqual != NULL)
4427 430 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4428 :
4429 498 : if (polinfo->polwithcheck != NULL)
4430 234 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4431 :
4432 498 : appendPQExpBufferStr(query, ";\n");
4433 :
4434 498 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4435 498 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4436 :
4437 498 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4438 498 : fmtId(polinfo->polname));
4439 :
4440 498 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4441 :
4442 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4443 498 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4444 498 : ARCHIVE_OPTS(.tag = tag,
4445 : .namespace = polinfo->dobj.namespace->dobj.name,
4446 : .owner = tbinfo->rolname,
4447 : .description = "POLICY",
4448 : .section = SECTION_POST_DATA,
4449 : .createStmt = query->data,
4450 : .dropStmt = delqry->data));
4451 :
4452 498 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4453 0 : dumpComment(fout, polprefix->data, qtabname,
4454 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4455 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4456 :
4457 498 : free(tag);
4458 498 : destroyPQExpBuffer(query);
4459 498 : destroyPQExpBuffer(delqry);
4460 498 : destroyPQExpBuffer(polprefix);
4461 498 : free(qtabname);
4462 : }
4463 :
4464 : /*
4465 : * getPublications
4466 : * get information about publications
4467 : */
4468 : void
4469 364 : getPublications(Archive *fout)
4470 : {
4471 364 : DumpOptions *dopt = fout->dopt;
4472 : PQExpBuffer query;
4473 : PGresult *res;
4474 : PublicationInfo *pubinfo;
4475 : int i_tableoid;
4476 : int i_oid;
4477 : int i_pubname;
4478 : int i_pubowner;
4479 : int i_puballtables;
4480 : int i_pubinsert;
4481 : int i_pubupdate;
4482 : int i_pubdelete;
4483 : int i_pubtruncate;
4484 : int i_pubviaroot;
4485 : int i_pubgencols;
4486 : int i,
4487 : ntups;
4488 :
4489 364 : if (dopt->no_publications || fout->remoteVersion < 100000)
4490 0 : return;
4491 :
4492 364 : query = createPQExpBuffer();
4493 :
4494 : /* Get the publications. */
4495 364 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4496 : "p.pubowner, p.puballtables, p.pubinsert, "
4497 : "p.pubupdate, p.pubdelete, ");
4498 :
4499 364 : if (fout->remoteVersion >= 110000)
4500 364 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4501 : else
4502 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4503 :
4504 364 : if (fout->remoteVersion >= 130000)
4505 364 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4506 : else
4507 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4508 :
4509 364 : if (fout->remoteVersion >= 180000)
4510 364 : appendPQExpBufferStr(query, "p.pubgencols ");
4511 : else
4512 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4513 :
4514 364 : appendPQExpBufferStr(query, "FROM pg_publication p");
4515 :
4516 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4517 :
4518 364 : ntups = PQntuples(res);
4519 :
4520 364 : if (ntups == 0)
4521 252 : goto cleanup;
4522 :
4523 112 : i_tableoid = PQfnumber(res, "tableoid");
4524 112 : i_oid = PQfnumber(res, "oid");
4525 112 : i_pubname = PQfnumber(res, "pubname");
4526 112 : i_pubowner = PQfnumber(res, "pubowner");
4527 112 : i_puballtables = PQfnumber(res, "puballtables");
4528 112 : i_pubinsert = PQfnumber(res, "pubinsert");
4529 112 : i_pubupdate = PQfnumber(res, "pubupdate");
4530 112 : i_pubdelete = PQfnumber(res, "pubdelete");
4531 112 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4532 112 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4533 112 : i_pubgencols = PQfnumber(res, "pubgencols");
4534 :
4535 112 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4536 :
4537 664 : for (i = 0; i < ntups; i++)
4538 : {
4539 552 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4540 552 : pubinfo[i].dobj.catId.tableoid =
4541 552 : atooid(PQgetvalue(res, i, i_tableoid));
4542 552 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4543 552 : AssignDumpId(&pubinfo[i].dobj);
4544 552 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4545 552 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4546 552 : pubinfo[i].puballtables =
4547 552 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4548 552 : pubinfo[i].pubinsert =
4549 552 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4550 552 : pubinfo[i].pubupdate =
4551 552 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4552 552 : pubinfo[i].pubdelete =
4553 552 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4554 552 : pubinfo[i].pubtruncate =
4555 552 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4556 552 : pubinfo[i].pubviaroot =
4557 552 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4558 552 : pubinfo[i].pubgencols_type =
4559 552 : *(PQgetvalue(res, i, i_pubgencols));
4560 :
4561 : /* Decide whether we want to dump it */
4562 552 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4563 : }
4564 :
4565 112 : cleanup:
4566 364 : PQclear(res);
4567 :
4568 364 : destroyPQExpBuffer(query);
4569 : }
4570 :
4571 : /*
4572 : * dumpPublication
4573 : * dump the definition of the given publication
4574 : */
4575 : static void
4576 452 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4577 : {
4578 452 : DumpOptions *dopt = fout->dopt;
4579 : PQExpBuffer delq;
4580 : PQExpBuffer query;
4581 : char *qpubname;
4582 452 : bool first = true;
4583 :
4584 : /* Do nothing if not dumping schema */
4585 452 : if (!dopt->dumpSchema)
4586 60 : return;
4587 :
4588 392 : delq = createPQExpBuffer();
4589 392 : query = createPQExpBuffer();
4590 :
4591 392 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4592 :
4593 392 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4594 : qpubname);
4595 :
4596 392 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4597 : qpubname);
4598 :
4599 392 : if (pubinfo->puballtables)
4600 70 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4601 :
4602 392 : appendPQExpBufferStr(query, " WITH (publish = '");
4603 392 : if (pubinfo->pubinsert)
4604 : {
4605 324 : appendPQExpBufferStr(query, "insert");
4606 324 : first = false;
4607 : }
4608 :
4609 392 : if (pubinfo->pubupdate)
4610 : {
4611 324 : if (!first)
4612 324 : appendPQExpBufferStr(query, ", ");
4613 :
4614 324 : appendPQExpBufferStr(query, "update");
4615 324 : first = false;
4616 : }
4617 :
4618 392 : if (pubinfo->pubdelete)
4619 : {
4620 324 : if (!first)
4621 324 : appendPQExpBufferStr(query, ", ");
4622 :
4623 324 : appendPQExpBufferStr(query, "delete");
4624 324 : first = false;
4625 : }
4626 :
4627 392 : if (pubinfo->pubtruncate)
4628 : {
4629 324 : if (!first)
4630 324 : appendPQExpBufferStr(query, ", ");
4631 :
4632 324 : appendPQExpBufferStr(query, "truncate");
4633 324 : first = false;
4634 : }
4635 :
4636 392 : appendPQExpBufferChar(query, '\'');
4637 :
4638 392 : if (pubinfo->pubviaroot)
4639 10 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4640 :
4641 392 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4642 68 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4643 :
4644 392 : appendPQExpBufferStr(query, ");\n");
4645 :
4646 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4647 392 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4648 392 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4649 : .owner = pubinfo->rolname,
4650 : .description = "PUBLICATION",
4651 : .section = SECTION_POST_DATA,
4652 : .createStmt = query->data,
4653 : .dropStmt = delq->data));
4654 :
4655 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4656 68 : dumpComment(fout, "PUBLICATION", qpubname,
4657 68 : NULL, pubinfo->rolname,
4658 68 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4659 :
4660 392 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4661 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4662 0 : NULL, pubinfo->rolname,
4663 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4664 :
4665 392 : destroyPQExpBuffer(delq);
4666 392 : destroyPQExpBuffer(query);
4667 392 : free(qpubname);
4668 : }
4669 :
4670 : /*
4671 : * getPublicationNamespaces
4672 : * get information about publication membership for dumpable schemas.
4673 : */
4674 : void
4675 364 : getPublicationNamespaces(Archive *fout)
4676 : {
4677 : PQExpBuffer query;
4678 : PGresult *res;
4679 : PublicationSchemaInfo *pubsinfo;
4680 364 : DumpOptions *dopt = fout->dopt;
4681 : int i_tableoid;
4682 : int i_oid;
4683 : int i_pnpubid;
4684 : int i_pnnspid;
4685 : int i,
4686 : j,
4687 : ntups;
4688 :
4689 364 : if (dopt->no_publications || fout->remoteVersion < 150000)
4690 0 : return;
4691 :
4692 364 : query = createPQExpBuffer();
4693 :
4694 : /* Collect all publication membership info. */
4695 364 : appendPQExpBufferStr(query,
4696 : "SELECT tableoid, oid, pnpubid, pnnspid "
4697 : "FROM pg_catalog.pg_publication_namespace");
4698 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4699 :
4700 364 : ntups = PQntuples(res);
4701 :
4702 364 : i_tableoid = PQfnumber(res, "tableoid");
4703 364 : i_oid = PQfnumber(res, "oid");
4704 364 : i_pnpubid = PQfnumber(res, "pnpubid");
4705 364 : i_pnnspid = PQfnumber(res, "pnnspid");
4706 :
4707 : /* this allocation may be more than we need */
4708 364 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4709 364 : j = 0;
4710 :
4711 626 : for (i = 0; i < ntups; i++)
4712 : {
4713 262 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4714 262 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4715 : PublicationInfo *pubinfo;
4716 : NamespaceInfo *nspinfo;
4717 :
4718 : /*
4719 : * Ignore any entries for which we aren't interested in either the
4720 : * publication or the rel.
4721 : */
4722 262 : pubinfo = findPublicationByOid(pnpubid);
4723 262 : if (pubinfo == NULL)
4724 0 : continue;
4725 262 : nspinfo = findNamespaceByOid(pnnspid);
4726 262 : if (nspinfo == NULL)
4727 0 : continue;
4728 :
4729 : /* OK, make a DumpableObject for this relationship */
4730 262 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4731 262 : pubsinfo[j].dobj.catId.tableoid =
4732 262 : atooid(PQgetvalue(res, i, i_tableoid));
4733 262 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4734 262 : AssignDumpId(&pubsinfo[j].dobj);
4735 262 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4736 262 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4737 262 : pubsinfo[j].publication = pubinfo;
4738 262 : pubsinfo[j].pubschema = nspinfo;
4739 :
4740 : /* Decide whether we want to dump it */
4741 262 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4742 :
4743 262 : j++;
4744 : }
4745 :
4746 364 : PQclear(res);
4747 364 : destroyPQExpBuffer(query);
4748 : }
4749 :
4750 : /*
4751 : * getPublicationTables
4752 : * get information about publication membership for dumpable tables.
4753 : */
4754 : void
4755 364 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4756 : {
4757 : PQExpBuffer query;
4758 : PGresult *res;
4759 : PublicationRelInfo *pubrinfo;
4760 364 : DumpOptions *dopt = fout->dopt;
4761 : int i_tableoid;
4762 : int i_oid;
4763 : int i_prpubid;
4764 : int i_prrelid;
4765 : int i_prrelqual;
4766 : int i_prattrs;
4767 : int i,
4768 : j,
4769 : ntups;
4770 :
4771 364 : if (dopt->no_publications || fout->remoteVersion < 100000)
4772 0 : return;
4773 :
4774 364 : query = createPQExpBuffer();
4775 :
4776 : /* Collect all publication membership info. */
4777 364 : if (fout->remoteVersion >= 150000)
4778 364 : appendPQExpBufferStr(query,
4779 : "SELECT tableoid, oid, prpubid, prrelid, "
4780 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4781 : "(CASE\n"
4782 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4783 : " (SELECT array_agg(attname)\n"
4784 : " FROM\n"
4785 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4786 : " pg_catalog.pg_attribute\n"
4787 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4788 : " ELSE NULL END) prattrs "
4789 : "FROM pg_catalog.pg_publication_rel pr");
4790 : else
4791 0 : appendPQExpBufferStr(query,
4792 : "SELECT tableoid, oid, prpubid, prrelid, "
4793 : "NULL AS prrelqual, NULL AS prattrs "
4794 : "FROM pg_catalog.pg_publication_rel");
4795 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4796 :
4797 364 : ntups = PQntuples(res);
4798 :
4799 364 : i_tableoid = PQfnumber(res, "tableoid");
4800 364 : i_oid = PQfnumber(res, "oid");
4801 364 : i_prpubid = PQfnumber(res, "prpubid");
4802 364 : i_prrelid = PQfnumber(res, "prrelid");
4803 364 : i_prrelqual = PQfnumber(res, "prrelqual");
4804 364 : i_prattrs = PQfnumber(res, "prattrs");
4805 :
4806 : /* this allocation may be more than we need */
4807 364 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4808 364 : j = 0;
4809 :
4810 1106 : for (i = 0; i < ntups; i++)
4811 : {
4812 742 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4813 742 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4814 : PublicationInfo *pubinfo;
4815 : TableInfo *tbinfo;
4816 :
4817 : /*
4818 : * Ignore any entries for which we aren't interested in either the
4819 : * publication or the rel.
4820 : */
4821 742 : pubinfo = findPublicationByOid(prpubid);
4822 742 : if (pubinfo == NULL)
4823 0 : continue;
4824 742 : tbinfo = findTableByOid(prrelid);
4825 742 : if (tbinfo == NULL)
4826 0 : continue;
4827 :
4828 : /* OK, make a DumpableObject for this relationship */
4829 742 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4830 742 : pubrinfo[j].dobj.catId.tableoid =
4831 742 : atooid(PQgetvalue(res, i, i_tableoid));
4832 742 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4833 742 : AssignDumpId(&pubrinfo[j].dobj);
4834 742 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4835 742 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4836 742 : pubrinfo[j].publication = pubinfo;
4837 742 : pubrinfo[j].pubtable = tbinfo;
4838 742 : if (PQgetisnull(res, i, i_prrelqual))
4839 412 : pubrinfo[j].pubrelqual = NULL;
4840 : else
4841 330 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4842 :
4843 742 : if (!PQgetisnull(res, i, i_prattrs))
4844 : {
4845 : char **attnames;
4846 : int nattnames;
4847 : PQExpBuffer attribs;
4848 :
4849 234 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4850 : &attnames, &nattnames))
4851 0 : pg_fatal("could not parse %s array", "prattrs");
4852 234 : attribs = createPQExpBuffer();
4853 674 : for (int k = 0; k < nattnames; k++)
4854 : {
4855 440 : if (k > 0)
4856 206 : appendPQExpBufferStr(attribs, ", ");
4857 :
4858 440 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4859 : }
4860 234 : pubrinfo[j].pubrattrs = attribs->data;
4861 234 : free(attribs); /* but not attribs->data */
4862 234 : free(attnames);
4863 : }
4864 : else
4865 508 : pubrinfo[j].pubrattrs = NULL;
4866 :
4867 : /* Decide whether we want to dump it */
4868 742 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4869 :
4870 742 : j++;
4871 : }
4872 :
4873 364 : PQclear(res);
4874 364 : destroyPQExpBuffer(query);
4875 : }
4876 :
4877 : /*
4878 : * dumpPublicationNamespace
4879 : * dump the definition of the given publication schema mapping.
4880 : */
4881 : static void
4882 210 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4883 : {
4884 210 : DumpOptions *dopt = fout->dopt;
4885 210 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4886 210 : PublicationInfo *pubinfo = pubsinfo->publication;
4887 : PQExpBuffer query;
4888 : char *tag;
4889 :
4890 : /* Do nothing if not dumping schema */
4891 210 : if (!dopt->dumpSchema)
4892 24 : return;
4893 :
4894 186 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4895 :
4896 186 : query = createPQExpBuffer();
4897 :
4898 186 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4899 186 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4900 :
4901 : /*
4902 : * There is no point in creating drop query as the drop is done by schema
4903 : * drop.
4904 : */
4905 186 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4906 186 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4907 186 : ARCHIVE_OPTS(.tag = tag,
4908 : .namespace = schemainfo->dobj.name,
4909 : .owner = pubinfo->rolname,
4910 : .description = "PUBLICATION TABLES IN SCHEMA",
4911 : .section = SECTION_POST_DATA,
4912 : .createStmt = query->data));
4913 :
4914 : /* These objects can't currently have comments or seclabels */
4915 :
4916 186 : free(tag);
4917 186 : destroyPQExpBuffer(query);
4918 : }
4919 :
4920 : /*
4921 : * dumpPublicationTable
4922 : * dump the definition of the given publication table mapping
4923 : */
4924 : static void
4925 610 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4926 : {
4927 610 : DumpOptions *dopt = fout->dopt;
4928 610 : PublicationInfo *pubinfo = pubrinfo->publication;
4929 610 : TableInfo *tbinfo = pubrinfo->pubtable;
4930 : PQExpBuffer query;
4931 : char *tag;
4932 :
4933 : /* Do nothing if not dumping schema */
4934 610 : if (!dopt->dumpSchema)
4935 84 : return;
4936 :
4937 526 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4938 :
4939 526 : query = createPQExpBuffer();
4940 :
4941 526 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4942 526 : fmtId(pubinfo->dobj.name));
4943 526 : appendPQExpBuffer(query, " %s",
4944 526 : fmtQualifiedDumpable(tbinfo));
4945 :
4946 526 : if (pubrinfo->pubrattrs)
4947 166 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4948 :
4949 526 : if (pubrinfo->pubrelqual)
4950 : {
4951 : /*
4952 : * It's necessary to add parentheses around the expression because
4953 : * pg_get_expr won't supply the parentheses for things like WHERE
4954 : * TRUE.
4955 : */
4956 234 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4957 : }
4958 526 : appendPQExpBufferStr(query, ";\n");
4959 :
4960 : /*
4961 : * There is no point in creating a drop query as the drop is done by table
4962 : * drop. (If you think to change this, see also _printTocEntry().)
4963 : * Although this object doesn't really have ownership as such, set the
4964 : * owner field anyway to ensure that the command is run by the correct
4965 : * role at restore time.
4966 : */
4967 526 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4968 526 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4969 526 : ARCHIVE_OPTS(.tag = tag,
4970 : .namespace = tbinfo->dobj.namespace->dobj.name,
4971 : .owner = pubinfo->rolname,
4972 : .description = "PUBLICATION TABLE",
4973 : .section = SECTION_POST_DATA,
4974 : .createStmt = query->data));
4975 :
4976 : /* These objects can't currently have comments or seclabels */
4977 :
4978 526 : free(tag);
4979 526 : destroyPQExpBuffer(query);
4980 : }
4981 :
4982 : /*
4983 : * Is the currently connected user a superuser?
4984 : */
4985 : static bool
4986 364 : is_superuser(Archive *fout)
4987 : {
4988 364 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4989 : const char *val;
4990 :
4991 364 : val = PQparameterStatus(AH->connection, "is_superuser");
4992 :
4993 364 : if (val && strcmp(val, "on") == 0)
4994 358 : return true;
4995 :
4996 6 : return false;
4997 : }
4998 :
4999 : /*
5000 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5001 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5002 : * the setting query is effective only where available.
5003 : */
5004 : static void
5005 432 : set_restrict_relation_kind(Archive *AH, const char *value)
5006 : {
5007 432 : PQExpBuffer query = createPQExpBuffer();
5008 : PGresult *res;
5009 :
5010 432 : appendPQExpBuffer(query,
5011 : "SELECT set_config(name, '%s', false) "
5012 : "FROM pg_settings "
5013 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5014 : value);
5015 432 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5016 :
5017 432 : PQclear(res);
5018 432 : destroyPQExpBuffer(query);
5019 432 : }
5020 :
5021 : /*
5022 : * getSubscriptions
5023 : * get information about subscriptions
5024 : */
5025 : void
5026 364 : getSubscriptions(Archive *fout)
5027 : {
5028 364 : DumpOptions *dopt = fout->dopt;
5029 : PQExpBuffer query;
5030 : PGresult *res;
5031 : SubscriptionInfo *subinfo;
5032 : int i_tableoid;
5033 : int i_oid;
5034 : int i_subname;
5035 : int i_subowner;
5036 : int i_subbinary;
5037 : int i_substream;
5038 : int i_subtwophasestate;
5039 : int i_subdisableonerr;
5040 : int i_subpasswordrequired;
5041 : int i_subrunasowner;
5042 : int i_subconninfo;
5043 : int i_subslotname;
5044 : int i_subsynccommit;
5045 : int i_subpublications;
5046 : int i_suborigin;
5047 : int i_suboriginremotelsn;
5048 : int i_subenabled;
5049 : int i_subfailover;
5050 : int i_subretaindeadtuples;
5051 : int i,
5052 : ntups;
5053 :
5054 364 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5055 0 : return;
5056 :
5057 364 : if (!is_superuser(fout))
5058 : {
5059 : int n;
5060 :
5061 6 : res = ExecuteSqlQuery(fout,
5062 : "SELECT count(*) FROM pg_subscription "
5063 : "WHERE subdbid = (SELECT oid FROM pg_database"
5064 : " WHERE datname = current_database())",
5065 : PGRES_TUPLES_OK);
5066 6 : n = atoi(PQgetvalue(res, 0, 0));
5067 6 : if (n > 0)
5068 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5069 6 : PQclear(res);
5070 6 : return;
5071 : }
5072 :
5073 358 : query = createPQExpBuffer();
5074 :
5075 : /* Get the subscriptions in current database. */
5076 358 : appendPQExpBufferStr(query,
5077 : "SELECT s.tableoid, s.oid, s.subname,\n"
5078 : " s.subowner,\n"
5079 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5080 : " s.subpublications,\n");
5081 :
5082 358 : if (fout->remoteVersion >= 140000)
5083 358 : appendPQExpBufferStr(query, " s.subbinary,\n");
5084 : else
5085 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5086 :
5087 358 : if (fout->remoteVersion >= 140000)
5088 358 : appendPQExpBufferStr(query, " s.substream,\n");
5089 : else
5090 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5091 :
5092 358 : if (fout->remoteVersion >= 150000)
5093 358 : appendPQExpBufferStr(query,
5094 : " s.subtwophasestate,\n"
5095 : " s.subdisableonerr,\n");
5096 : else
5097 0 : appendPQExpBuffer(query,
5098 : " '%c' AS subtwophasestate,\n"
5099 : " false AS subdisableonerr,\n",
5100 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5101 :
5102 358 : if (fout->remoteVersion >= 160000)
5103 358 : appendPQExpBufferStr(query,
5104 : " s.subpasswordrequired,\n"
5105 : " s.subrunasowner,\n"
5106 : " s.suborigin,\n");
5107 : else
5108 0 : appendPQExpBuffer(query,
5109 : " 't' AS subpasswordrequired,\n"
5110 : " 't' AS subrunasowner,\n"
5111 : " '%s' AS suborigin,\n",
5112 : LOGICALREP_ORIGIN_ANY);
5113 :
5114 358 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5115 72 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5116 : " s.subenabled,\n");
5117 : else
5118 286 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5119 : " false AS subenabled,\n");
5120 :
5121 358 : if (fout->remoteVersion >= 170000)
5122 358 : appendPQExpBufferStr(query,
5123 : " s.subfailover,\n");
5124 : else
5125 0 : appendPQExpBufferStr(query,
5126 : " false AS subfailover,\n");
5127 :
5128 358 : if (fout->remoteVersion >= 190000)
5129 358 : appendPQExpBufferStr(query,
5130 : " s.subretaindeadtuples\n");
5131 : else
5132 0 : appendPQExpBufferStr(query,
5133 : " false AS subretaindeadtuples\n");
5134 :
5135 358 : appendPQExpBufferStr(query,
5136 : "FROM pg_subscription s\n");
5137 :
5138 358 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5139 72 : appendPQExpBufferStr(query,
5140 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5141 : " ON o.external_id = 'pg_' || s.oid::text \n");
5142 :
5143 358 : appendPQExpBufferStr(query,
5144 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5145 : " WHERE datname = current_database())");
5146 :
5147 358 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5148 :
5149 358 : ntups = PQntuples(res);
5150 :
5151 : /*
5152 : * Get subscription fields. We don't include subskiplsn in the dump as
5153 : * after restoring the dump this value may no longer be relevant.
5154 : */
5155 358 : i_tableoid = PQfnumber(res, "tableoid");
5156 358 : i_oid = PQfnumber(res, "oid");
5157 358 : i_subname = PQfnumber(res, "subname");
5158 358 : i_subowner = PQfnumber(res, "subowner");
5159 358 : i_subenabled = PQfnumber(res, "subenabled");
5160 358 : i_subbinary = PQfnumber(res, "subbinary");
5161 358 : i_substream = PQfnumber(res, "substream");
5162 358 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5163 358 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5164 358 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5165 358 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5166 358 : i_subfailover = PQfnumber(res, "subfailover");
5167 358 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5168 358 : i_subconninfo = PQfnumber(res, "subconninfo");
5169 358 : i_subslotname = PQfnumber(res, "subslotname");
5170 358 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5171 358 : i_subpublications = PQfnumber(res, "subpublications");
5172 358 : i_suborigin = PQfnumber(res, "suborigin");
5173 358 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5174 :
5175 358 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5176 :
5177 638 : for (i = 0; i < ntups; i++)
5178 : {
5179 280 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5180 280 : subinfo[i].dobj.catId.tableoid =
5181 280 : atooid(PQgetvalue(res, i, i_tableoid));
5182 280 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5183 280 : AssignDumpId(&subinfo[i].dobj);
5184 280 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5185 280 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5186 :
5187 280 : subinfo[i].subenabled =
5188 280 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5189 280 : subinfo[i].subbinary =
5190 280 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5191 280 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5192 280 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5193 280 : subinfo[i].subdisableonerr =
5194 280 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5195 280 : subinfo[i].subpasswordrequired =
5196 280 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5197 280 : subinfo[i].subrunasowner =
5198 280 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5199 280 : subinfo[i].subfailover =
5200 280 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5201 280 : subinfo[i].subretaindeadtuples =
5202 280 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5203 560 : subinfo[i].subconninfo =
5204 280 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5205 280 : if (PQgetisnull(res, i, i_subslotname))
5206 0 : subinfo[i].subslotname = NULL;
5207 : else
5208 280 : subinfo[i].subslotname =
5209 280 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5210 560 : subinfo[i].subsynccommit =
5211 280 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5212 560 : subinfo[i].subpublications =
5213 280 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5214 280 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5215 280 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5216 278 : subinfo[i].suboriginremotelsn = NULL;
5217 : else
5218 2 : subinfo[i].suboriginremotelsn =
5219 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5220 :
5221 : /* Decide whether we want to dump it */
5222 280 : selectDumpableObject(&(subinfo[i].dobj), fout);
5223 : }
5224 358 : PQclear(res);
5225 :
5226 358 : destroyPQExpBuffer(query);
5227 : }
5228 :
5229 : /*
5230 : * getSubscriptionTables
5231 : * Get information about subscription membership for dumpable tables. This
5232 : * will be used only in binary-upgrade mode for PG17 or later versions.
5233 : */
5234 : void
5235 364 : getSubscriptionTables(Archive *fout)
5236 : {
5237 364 : DumpOptions *dopt = fout->dopt;
5238 364 : SubscriptionInfo *subinfo = NULL;
5239 : SubRelInfo *subrinfo;
5240 : PGresult *res;
5241 : int i_srsubid;
5242 : int i_srrelid;
5243 : int i_srsubstate;
5244 : int i_srsublsn;
5245 : int ntups;
5246 364 : Oid last_srsubid = InvalidOid;
5247 :
5248 364 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5249 72 : fout->remoteVersion < 170000)
5250 292 : return;
5251 :
5252 72 : res = ExecuteSqlQuery(fout,
5253 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5254 : "FROM pg_catalog.pg_subscription_rel "
5255 : "ORDER BY srsubid",
5256 : PGRES_TUPLES_OK);
5257 72 : ntups = PQntuples(res);
5258 72 : if (ntups == 0)
5259 70 : goto cleanup;
5260 :
5261 : /* Get pg_subscription_rel attributes */
5262 2 : i_srsubid = PQfnumber(res, "srsubid");
5263 2 : i_srrelid = PQfnumber(res, "srrelid");
5264 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5265 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5266 :
5267 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5268 6 : for (int i = 0; i < ntups; i++)
5269 : {
5270 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5271 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5272 : TableInfo *tblinfo;
5273 :
5274 : /*
5275 : * If we switched to a new subscription, check if the subscription
5276 : * exists.
5277 : */
5278 4 : if (cur_srsubid != last_srsubid)
5279 : {
5280 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5281 4 : if (subinfo == NULL)
5282 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5283 :
5284 4 : last_srsubid = cur_srsubid;
5285 : }
5286 :
5287 4 : tblinfo = findTableByOid(relid);
5288 4 : if (tblinfo == NULL)
5289 0 : pg_fatal("failed sanity check, table with OID %u not found",
5290 : relid);
5291 :
5292 : /* OK, make a DumpableObject for this relationship */
5293 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5294 4 : subrinfo[i].dobj.catId.tableoid = relid;
5295 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5296 4 : AssignDumpId(&subrinfo[i].dobj);
5297 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5298 4 : subrinfo[i].tblinfo = tblinfo;
5299 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5300 4 : if (PQgetisnull(res, i, i_srsublsn))
5301 2 : subrinfo[i].srsublsn = NULL;
5302 : else
5303 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5304 :
5305 4 : subrinfo[i].subinfo = subinfo;
5306 :
5307 : /* Decide whether we want to dump it */
5308 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5309 : }
5310 :
5311 2 : cleanup:
5312 72 : PQclear(res);
5313 : }
5314 :
5315 : /*
5316 : * dumpSubscriptionTable
5317 : * Dump the definition of the given subscription table mapping. This will be
5318 : * used only in binary-upgrade mode for PG17 or later versions.
5319 : */
5320 : static void
5321 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5322 : {
5323 4 : DumpOptions *dopt = fout->dopt;
5324 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5325 : PQExpBuffer query;
5326 : char *tag;
5327 :
5328 : /* Do nothing if not dumping schema */
5329 4 : if (!dopt->dumpSchema)
5330 0 : return;
5331 :
5332 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5333 :
5334 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5335 :
5336 4 : query = createPQExpBuffer();
5337 :
5338 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5339 : {
5340 : /*
5341 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5342 : * to pg_subscription_rel table. This will be used only in
5343 : * binary-upgrade mode.
5344 : */
5345 4 : appendPQExpBufferStr(query,
5346 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5347 4 : appendPQExpBufferStr(query,
5348 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5349 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5350 4 : appendPQExpBuffer(query,
5351 : ", %u, '%c'",
5352 4 : subrinfo->tblinfo->dobj.catId.oid,
5353 4 : subrinfo->srsubstate);
5354 :
5355 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5356 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5357 : else
5358 2 : appendPQExpBufferStr(query, ", NULL");
5359 :
5360 4 : appendPQExpBufferStr(query, ");\n");
5361 : }
5362 :
5363 : /*
5364 : * There is no point in creating a drop query as the drop is done by table
5365 : * drop. (If you think to change this, see also _printTocEntry().)
5366 : * Although this object doesn't really have ownership as such, set the
5367 : * owner field anyway to ensure that the command is run by the correct
5368 : * role at restore time.
5369 : */
5370 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5371 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5372 4 : ARCHIVE_OPTS(.tag = tag,
5373 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5374 : .owner = subinfo->rolname,
5375 : .description = "SUBSCRIPTION TABLE",
5376 : .section = SECTION_POST_DATA,
5377 : .createStmt = query->data));
5378 :
5379 : /* These objects can't currently have comments or seclabels */
5380 :
5381 4 : free(tag);
5382 4 : destroyPQExpBuffer(query);
5383 : }
5384 :
5385 : /*
5386 : * dumpSubscription
5387 : * dump the definition of the given subscription
5388 : */
5389 : static void
5390 244 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5391 : {
5392 244 : DumpOptions *dopt = fout->dopt;
5393 : PQExpBuffer delq;
5394 : PQExpBuffer query;
5395 : PQExpBuffer publications;
5396 : char *qsubname;
5397 244 : char **pubnames = NULL;
5398 244 : int npubnames = 0;
5399 : int i;
5400 :
5401 : /* Do nothing if not dumping schema */
5402 244 : if (!dopt->dumpSchema)
5403 36 : return;
5404 :
5405 208 : delq = createPQExpBuffer();
5406 208 : query = createPQExpBuffer();
5407 :
5408 208 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5409 :
5410 208 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5411 : qsubname);
5412 :
5413 208 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5414 : qsubname);
5415 208 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5416 :
5417 : /* Build list of quoted publications and append them to query. */
5418 208 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5419 0 : pg_fatal("could not parse %s array", "subpublications");
5420 :
5421 208 : publications = createPQExpBuffer();
5422 416 : for (i = 0; i < npubnames; i++)
5423 : {
5424 208 : if (i > 0)
5425 0 : appendPQExpBufferStr(publications, ", ");
5426 :
5427 208 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5428 : }
5429 :
5430 208 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5431 208 : if (subinfo->subslotname)
5432 208 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5433 : else
5434 0 : appendPQExpBufferStr(query, "NONE");
5435 :
5436 208 : if (subinfo->subbinary)
5437 0 : appendPQExpBufferStr(query, ", binary = true");
5438 :
5439 208 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5440 68 : appendPQExpBufferStr(query, ", streaming = on");
5441 140 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5442 72 : appendPQExpBufferStr(query, ", streaming = parallel");
5443 : else
5444 68 : appendPQExpBufferStr(query, ", streaming = off");
5445 :
5446 208 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5447 0 : appendPQExpBufferStr(query, ", two_phase = on");
5448 :
5449 208 : if (subinfo->subdisableonerr)
5450 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5451 :
5452 208 : if (!subinfo->subpasswordrequired)
5453 0 : appendPQExpBufferStr(query, ", password_required = false");
5454 :
5455 208 : if (subinfo->subrunasowner)
5456 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5457 :
5458 208 : if (subinfo->subfailover)
5459 2 : appendPQExpBufferStr(query, ", failover = true");
5460 :
5461 208 : if (subinfo->subretaindeadtuples)
5462 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5463 :
5464 208 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5465 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5466 :
5467 208 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5468 68 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5469 :
5470 208 : appendPQExpBufferStr(query, ");\n");
5471 :
5472 : /*
5473 : * In binary-upgrade mode, we allow the replication to continue after the
5474 : * upgrade.
5475 : */
5476 208 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5477 : {
5478 10 : if (subinfo->suboriginremotelsn)
5479 : {
5480 : /*
5481 : * Preserve the remote_lsn for the subscriber's replication
5482 : * origin. This value is required to start the replication from
5483 : * the position before the upgrade. This value will be stale if
5484 : * the publisher gets upgraded before the subscriber node.
5485 : * However, this shouldn't be a problem as the upgrade of the
5486 : * publisher ensures that all the transactions were replicated
5487 : * before upgrading it.
5488 : */
5489 2 : appendPQExpBufferStr(query,
5490 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5491 2 : appendPQExpBufferStr(query,
5492 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5493 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5494 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5495 : }
5496 :
5497 10 : if (subinfo->subenabled)
5498 : {
5499 : /*
5500 : * Enable the subscription to allow the replication to continue
5501 : * after the upgrade.
5502 : */
5503 2 : appendPQExpBufferStr(query,
5504 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5505 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5506 : }
5507 : }
5508 :
5509 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5510 208 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5511 208 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5512 : .owner = subinfo->rolname,
5513 : .description = "SUBSCRIPTION",
5514 : .section = SECTION_POST_DATA,
5515 : .createStmt = query->data,
5516 : .dropStmt = delq->data));
5517 :
5518 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5519 68 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5520 68 : NULL, subinfo->rolname,
5521 68 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5522 :
5523 208 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5524 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5525 0 : NULL, subinfo->rolname,
5526 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5527 :
5528 208 : destroyPQExpBuffer(publications);
5529 208 : free(pubnames);
5530 :
5531 208 : destroyPQExpBuffer(delq);
5532 208 : destroyPQExpBuffer(query);
5533 208 : free(qsubname);
5534 : }
5535 :
5536 : /*
5537 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5538 : * the object needs.
5539 : */
5540 : static void
5541 10384 : append_depends_on_extension(Archive *fout,
5542 : PQExpBuffer create,
5543 : const DumpableObject *dobj,
5544 : const char *catalog,
5545 : const char *keyword,
5546 : const char *objname)
5547 : {
5548 10384 : if (dobj->depends_on_ext)
5549 : {
5550 : char *nm;
5551 : PGresult *res;
5552 : PQExpBuffer query;
5553 : int ntups;
5554 : int i_extname;
5555 : int i;
5556 :
5557 : /* dodge fmtId() non-reentrancy */
5558 84 : nm = pg_strdup(objname);
5559 :
5560 84 : query = createPQExpBuffer();
5561 84 : appendPQExpBuffer(query,
5562 : "SELECT e.extname "
5563 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5564 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5565 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5566 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5567 : catalog,
5568 84 : dobj->catId.oid);
5569 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5570 84 : ntups = PQntuples(res);
5571 84 : i_extname = PQfnumber(res, "extname");
5572 168 : for (i = 0; i < ntups; i++)
5573 : {
5574 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5575 : keyword, nm,
5576 84 : fmtId(PQgetvalue(res, i, i_extname)));
5577 : }
5578 :
5579 84 : PQclear(res);
5580 84 : destroyPQExpBuffer(query);
5581 84 : pg_free(nm);
5582 : }
5583 10384 : }
5584 :
5585 : static Oid
5586 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5587 : {
5588 : /*
5589 : * If the old version didn't assign an array type, but the new version
5590 : * does, we must select an unused type OID to assign. This currently only
5591 : * happens for domains, when upgrading pre-v11 to v11 and up.
5592 : *
5593 : * Note: local state here is kind of ugly, but we must have some, since we
5594 : * mustn't choose the same unused OID more than once.
5595 : */
5596 : static Oid next_possible_free_oid = FirstNormalObjectId;
5597 : PGresult *res;
5598 : bool is_dup;
5599 :
5600 : do
5601 : {
5602 0 : ++next_possible_free_oid;
5603 0 : printfPQExpBuffer(upgrade_query,
5604 : "SELECT EXISTS(SELECT 1 "
5605 : "FROM pg_catalog.pg_type "
5606 : "WHERE oid = '%u'::pg_catalog.oid);",
5607 : next_possible_free_oid);
5608 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5609 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5610 0 : PQclear(res);
5611 0 : } while (is_dup);
5612 :
5613 0 : return next_possible_free_oid;
5614 : }
5615 :
5616 : static void
5617 1886 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5618 : PQExpBuffer upgrade_buffer,
5619 : Oid pg_type_oid,
5620 : bool force_array_type,
5621 : bool include_multirange_type)
5622 : {
5623 1886 : PQExpBuffer upgrade_query = createPQExpBuffer();
5624 : PGresult *res;
5625 : Oid pg_type_array_oid;
5626 : Oid pg_type_multirange_oid;
5627 : Oid pg_type_multirange_array_oid;
5628 : TypeInfo *tinfo;
5629 :
5630 1886 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5631 1886 : appendPQExpBuffer(upgrade_buffer,
5632 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5633 : pg_type_oid);
5634 :
5635 1886 : tinfo = findTypeByOid(pg_type_oid);
5636 1886 : if (tinfo)
5637 1886 : pg_type_array_oid = tinfo->typarray;
5638 : else
5639 0 : pg_type_array_oid = InvalidOid;
5640 :
5641 1886 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5642 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5643 :
5644 1886 : if (OidIsValid(pg_type_array_oid))
5645 : {
5646 1882 : appendPQExpBufferStr(upgrade_buffer,
5647 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5648 1882 : appendPQExpBuffer(upgrade_buffer,
5649 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5650 : pg_type_array_oid);
5651 : }
5652 :
5653 : /*
5654 : * Pre-set the multirange type oid and its own array type oid.
5655 : */
5656 1886 : if (include_multirange_type)
5657 : {
5658 16 : if (fout->remoteVersion >= 140000)
5659 : {
5660 16 : printfPQExpBuffer(upgrade_query,
5661 : "SELECT t.oid, t.typarray "
5662 : "FROM pg_catalog.pg_type t "
5663 : "JOIN pg_catalog.pg_range r "
5664 : "ON t.oid = r.rngmultitypid "
5665 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5666 : pg_type_oid);
5667 :
5668 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5669 :
5670 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5671 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5672 :
5673 16 : PQclear(res);
5674 : }
5675 : else
5676 : {
5677 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5678 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5679 : }
5680 :
5681 16 : appendPQExpBufferStr(upgrade_buffer,
5682 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5683 16 : appendPQExpBuffer(upgrade_buffer,
5684 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5685 : pg_type_multirange_oid);
5686 16 : appendPQExpBufferStr(upgrade_buffer,
5687 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5688 16 : appendPQExpBuffer(upgrade_buffer,
5689 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5690 : pg_type_multirange_array_oid);
5691 : }
5692 :
5693 1886 : destroyPQExpBuffer(upgrade_query);
5694 1886 : }
5695 :
5696 : static void
5697 1736 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5698 : PQExpBuffer upgrade_buffer,
5699 : const TableInfo *tbinfo)
5700 : {
5701 1736 : Oid pg_type_oid = tbinfo->reltype;
5702 :
5703 1736 : if (OidIsValid(pg_type_oid))
5704 1736 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5705 : pg_type_oid, false, false);
5706 1736 : }
5707 :
5708 : /*
5709 : * bsearch() comparator for BinaryUpgradeClassOidItem
5710 : */
5711 : static int
5712 24720 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5713 : {
5714 24720 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5715 24720 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5716 :
5717 24720 : return pg_cmp_u32(v1.oid, v2.oid);
5718 : }
5719 :
5720 : /*
5721 : * collectBinaryUpgradeClassOids
5722 : *
5723 : * Construct a table of pg_class information required for
5724 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5725 : * lookup.
5726 : */
5727 : static void
5728 72 : collectBinaryUpgradeClassOids(Archive *fout)
5729 : {
5730 : PGresult *res;
5731 : const char *query;
5732 :
5733 72 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5734 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5735 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5736 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5737 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5738 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5739 : "ORDER BY c.oid;";
5740 :
5741 72 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5742 :
5743 72 : nbinaryUpgradeClassOids = PQntuples(res);
5744 72 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5745 72 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5746 :
5747 33652 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5748 : {
5749 33580 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5750 33580 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5751 33580 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5752 33580 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5753 33580 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5754 33580 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5755 33580 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5756 : }
5757 :
5758 72 : PQclear(res);
5759 72 : }
5760 :
5761 : static void
5762 2508 : binary_upgrade_set_pg_class_oids(Archive *fout,
5763 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5764 : {
5765 2508 : BinaryUpgradeClassOidItem key = {0};
5766 : BinaryUpgradeClassOidItem *entry;
5767 :
5768 : Assert(binaryUpgradeClassOids);
5769 :
5770 : /*
5771 : * Preserve the OID and relfilenumber of the table, table's index, table's
5772 : * toast table and toast table's index if any.
5773 : *
5774 : * One complexity is that the current table definition might not require
5775 : * the creation of a TOAST table, but the old database might have a TOAST
5776 : * table that was created earlier, before some wide columns were dropped.
5777 : * By setting the TOAST oid we force creation of the TOAST heap and index
5778 : * by the new backend, so we can copy the files during binary upgrade
5779 : * without worrying about this case.
5780 : */
5781 2508 : key.oid = pg_class_oid;
5782 2508 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5783 : sizeof(BinaryUpgradeClassOidItem),
5784 : BinaryUpgradeClassOidItemCmp);
5785 :
5786 2508 : appendPQExpBufferStr(upgrade_buffer,
5787 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5788 :
5789 2508 : if (entry->relkind != RELKIND_INDEX &&
5790 1954 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5791 : {
5792 1904 : appendPQExpBuffer(upgrade_buffer,
5793 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5794 : pg_class_oid);
5795 :
5796 : /*
5797 : * Not every relation has storage. Also, in a pre-v12 database,
5798 : * partitioned tables have a relfilenumber, which should not be
5799 : * preserved when upgrading.
5800 : */
5801 1904 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5802 1580 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5803 1580 : appendPQExpBuffer(upgrade_buffer,
5804 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5805 : entry->relfilenumber);
5806 :
5807 : /*
5808 : * In a pre-v12 database, partitioned tables might be marked as having
5809 : * toast tables, but we should ignore them if so.
5810 : */
5811 1904 : if (OidIsValid(entry->toast_oid) &&
5812 560 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5813 : {
5814 560 : appendPQExpBuffer(upgrade_buffer,
5815 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5816 : entry->toast_oid);
5817 560 : appendPQExpBuffer(upgrade_buffer,
5818 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5819 : entry->toast_relfilenumber);
5820 :
5821 : /* every toast table has an index */
5822 560 : appendPQExpBuffer(upgrade_buffer,
5823 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5824 : entry->toast_index_oid);
5825 560 : appendPQExpBuffer(upgrade_buffer,
5826 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5827 : entry->toast_index_relfilenumber);
5828 : }
5829 : }
5830 : else
5831 : {
5832 : /* Preserve the OID and relfilenumber of the index */
5833 604 : appendPQExpBuffer(upgrade_buffer,
5834 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5835 : pg_class_oid);
5836 604 : appendPQExpBuffer(upgrade_buffer,
5837 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5838 : entry->relfilenumber);
5839 : }
5840 :
5841 2508 : appendPQExpBufferChar(upgrade_buffer, '\n');
5842 2508 : }
5843 :
5844 : /*
5845 : * If the DumpableObject is a member of an extension, add a suitable
5846 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5847 : *
5848 : * For somewhat historical reasons, objname should already be quoted,
5849 : * but not objnamespace (if any).
5850 : */
5851 : static void
5852 2998 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5853 : const DumpableObject *dobj,
5854 : const char *objtype,
5855 : const char *objname,
5856 : const char *objnamespace)
5857 : {
5858 2998 : DumpableObject *extobj = NULL;
5859 : int i;
5860 :
5861 2998 : if (!dobj->ext_member)
5862 2966 : return;
5863 :
5864 : /*
5865 : * Find the parent extension. We could avoid this search if we wanted to
5866 : * add a link field to DumpableObject, but the space costs of that would
5867 : * be considerable. We assume that member objects could only have a
5868 : * direct dependency on their own extension, not any others.
5869 : */
5870 32 : for (i = 0; i < dobj->nDeps; i++)
5871 : {
5872 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5873 32 : if (extobj && extobj->objType == DO_EXTENSION)
5874 32 : break;
5875 0 : extobj = NULL;
5876 : }
5877 32 : if (extobj == NULL)
5878 0 : pg_fatal("could not find parent extension for %s %s",
5879 : objtype, objname);
5880 :
5881 32 : appendPQExpBufferStr(upgrade_buffer,
5882 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5883 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5884 32 : fmtId(extobj->name),
5885 : objtype);
5886 32 : if (objnamespace && *objnamespace)
5887 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5888 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5889 : }
5890 :
5891 : /*
5892 : * getNamespaces:
5893 : * get information about all namespaces in the system catalogs
5894 : */
5895 : void
5896 366 : getNamespaces(Archive *fout)
5897 : {
5898 : PGresult *res;
5899 : int ntups;
5900 : int i;
5901 : PQExpBuffer query;
5902 : NamespaceInfo *nsinfo;
5903 : int i_tableoid;
5904 : int i_oid;
5905 : int i_nspname;
5906 : int i_nspowner;
5907 : int i_nspacl;
5908 : int i_acldefault;
5909 :
5910 366 : query = createPQExpBuffer();
5911 :
5912 : /*
5913 : * we fetch all namespaces including system ones, so that every object we
5914 : * read in can be linked to a containing namespace.
5915 : */
5916 366 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5917 : "n.nspowner, "
5918 : "n.nspacl, "
5919 : "acldefault('n', n.nspowner) AS acldefault "
5920 : "FROM pg_namespace n");
5921 :
5922 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5923 :
5924 366 : ntups = PQntuples(res);
5925 :
5926 366 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5927 :
5928 366 : i_tableoid = PQfnumber(res, "tableoid");
5929 366 : i_oid = PQfnumber(res, "oid");
5930 366 : i_nspname = PQfnumber(res, "nspname");
5931 366 : i_nspowner = PQfnumber(res, "nspowner");
5932 366 : i_nspacl = PQfnumber(res, "nspacl");
5933 366 : i_acldefault = PQfnumber(res, "acldefault");
5934 :
5935 3222 : for (i = 0; i < ntups; i++)
5936 : {
5937 : const char *nspowner;
5938 :
5939 2856 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5940 2856 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5941 2856 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5942 2856 : AssignDumpId(&nsinfo[i].dobj);
5943 2856 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5944 2856 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5945 2856 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5946 2856 : nsinfo[i].dacl.privtype = 0;
5947 2856 : nsinfo[i].dacl.initprivs = NULL;
5948 2856 : nspowner = PQgetvalue(res, i, i_nspowner);
5949 2856 : nsinfo[i].nspowner = atooid(nspowner);
5950 2856 : nsinfo[i].rolname = getRoleName(nspowner);
5951 :
5952 : /* Decide whether to dump this namespace */
5953 2856 : selectDumpableNamespace(&nsinfo[i], fout);
5954 :
5955 : /* Mark whether namespace has an ACL */
5956 2856 : if (!PQgetisnull(res, i, i_nspacl))
5957 1228 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5958 :
5959 : /*
5960 : * We ignore any pg_init_privs.initprivs entry for the public schema
5961 : * and assume a predetermined default, for several reasons. First,
5962 : * dropping and recreating the schema removes its pg_init_privs entry,
5963 : * but an empty destination database starts with this ACL nonetheless.
5964 : * Second, we support dump/reload of public schema ownership changes.
5965 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5966 : * initprivs continues to reflect the initial owner. Hence,
5967 : * synthesize the value that nspacl will have after the restore's
5968 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5969 : * match the source's ACL, even if the latter was an initdb-default
5970 : * ACL, which changed in v15. An upgrade pulls in changes to most
5971 : * system object ACLs that the DBA had not customized. We've made the
5972 : * public schema depart from that, because changing its ACL so easily
5973 : * breaks applications.
5974 : */
5975 2856 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5976 : {
5977 358 : PQExpBuffer aclarray = createPQExpBuffer();
5978 358 : PQExpBuffer aclitem = createPQExpBuffer();
5979 :
5980 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5981 358 : appendPQExpBufferChar(aclarray, '{');
5982 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5983 358 : appendPQExpBufferStr(aclitem, "=UC/");
5984 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5985 358 : appendPGArray(aclarray, aclitem->data);
5986 358 : resetPQExpBuffer(aclitem);
5987 358 : appendPQExpBufferStr(aclitem, "=U/");
5988 358 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5989 358 : appendPGArray(aclarray, aclitem->data);
5990 358 : appendPQExpBufferChar(aclarray, '}');
5991 :
5992 358 : nsinfo[i].dacl.privtype = 'i';
5993 358 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5994 358 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5995 :
5996 358 : destroyPQExpBuffer(aclarray);
5997 358 : destroyPQExpBuffer(aclitem);
5998 : }
5999 : }
6000 :
6001 366 : PQclear(res);
6002 366 : destroyPQExpBuffer(query);
6003 366 : }
6004 :
6005 : /*
6006 : * findNamespace:
6007 : * given a namespace OID, look up the info read by getNamespaces
6008 : */
6009 : static NamespaceInfo *
6010 1150666 : findNamespace(Oid nsoid)
6011 : {
6012 : NamespaceInfo *nsinfo;
6013 :
6014 1150666 : nsinfo = findNamespaceByOid(nsoid);
6015 1150666 : if (nsinfo == NULL)
6016 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6017 1150666 : return nsinfo;
6018 : }
6019 :
6020 : /*
6021 : * getExtensions:
6022 : * read all extensions in the system catalogs and return them in the
6023 : * ExtensionInfo* structure
6024 : *
6025 : * numExtensions is set to the number of extensions read in
6026 : */
6027 : ExtensionInfo *
6028 366 : getExtensions(Archive *fout, int *numExtensions)
6029 : {
6030 366 : DumpOptions *dopt = fout->dopt;
6031 : PGresult *res;
6032 : int ntups;
6033 : int i;
6034 : PQExpBuffer query;
6035 366 : ExtensionInfo *extinfo = NULL;
6036 : int i_tableoid;
6037 : int i_oid;
6038 : int i_extname;
6039 : int i_nspname;
6040 : int i_extrelocatable;
6041 : int i_extversion;
6042 : int i_extconfig;
6043 : int i_extcondition;
6044 :
6045 366 : query = createPQExpBuffer();
6046 :
6047 366 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6048 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6049 : "FROM pg_extension x "
6050 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6051 :
6052 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6053 :
6054 366 : ntups = PQntuples(res);
6055 366 : if (ntups == 0)
6056 0 : goto cleanup;
6057 :
6058 366 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6059 :
6060 366 : i_tableoid = PQfnumber(res, "tableoid");
6061 366 : i_oid = PQfnumber(res, "oid");
6062 366 : i_extname = PQfnumber(res, "extname");
6063 366 : i_nspname = PQfnumber(res, "nspname");
6064 366 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6065 366 : i_extversion = PQfnumber(res, "extversion");
6066 366 : i_extconfig = PQfnumber(res, "extconfig");
6067 366 : i_extcondition = PQfnumber(res, "extcondition");
6068 :
6069 782 : for (i = 0; i < ntups; i++)
6070 : {
6071 416 : extinfo[i].dobj.objType = DO_EXTENSION;
6072 416 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6073 416 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6074 416 : AssignDumpId(&extinfo[i].dobj);
6075 416 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6076 416 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6077 416 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6078 416 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6079 416 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6080 416 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6081 :
6082 : /* Decide whether we want to dump it */
6083 416 : selectDumpableExtension(&(extinfo[i]), dopt);
6084 : }
6085 :
6086 366 : cleanup:
6087 366 : PQclear(res);
6088 366 : destroyPQExpBuffer(query);
6089 :
6090 366 : *numExtensions = ntups;
6091 :
6092 366 : return extinfo;
6093 : }
6094 :
6095 : /*
6096 : * getTypes:
6097 : * get information about all types in the system catalogs
6098 : *
6099 : * NB: this must run after getFuncs() because we assume we can do
6100 : * findFuncByOid().
6101 : */
6102 : void
6103 364 : getTypes(Archive *fout)
6104 : {
6105 : PGresult *res;
6106 : int ntups;
6107 : int i;
6108 364 : PQExpBuffer query = createPQExpBuffer();
6109 : TypeInfo *tyinfo;
6110 : ShellTypeInfo *stinfo;
6111 : int i_tableoid;
6112 : int i_oid;
6113 : int i_typname;
6114 : int i_typnamespace;
6115 : int i_typacl;
6116 : int i_acldefault;
6117 : int i_typowner;
6118 : int i_typelem;
6119 : int i_typrelid;
6120 : int i_typrelkind;
6121 : int i_typtype;
6122 : int i_typisdefined;
6123 : int i_isarray;
6124 : int i_typarray;
6125 :
6126 : /*
6127 : * we include even the built-in types because those may be used as array
6128 : * elements by user-defined types
6129 : *
6130 : * we filter out the built-in types when we dump out the types
6131 : *
6132 : * same approach for undefined (shell) types and array types
6133 : *
6134 : * Note: as of 8.3 we can reliably detect whether a type is an
6135 : * auto-generated array type by checking the element type's typarray.
6136 : * (Before that the test is capable of generating false positives.) We
6137 : * still check for name beginning with '_', though, so as to avoid the
6138 : * cost of the subselect probe for all standard types. This would have to
6139 : * be revisited if the backend ever allows renaming of array types.
6140 : */
6141 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6142 : "typnamespace, typacl, "
6143 : "acldefault('T', typowner) AS acldefault, "
6144 : "typowner, "
6145 : "typelem, typrelid, typarray, "
6146 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6147 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6148 : "typtype, typisdefined, "
6149 : "typname[0] = '_' AND typelem != 0 AND "
6150 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6151 : "FROM pg_type");
6152 :
6153 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6154 :
6155 364 : ntups = PQntuples(res);
6156 :
6157 364 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6158 :
6159 364 : i_tableoid = PQfnumber(res, "tableoid");
6160 364 : i_oid = PQfnumber(res, "oid");
6161 364 : i_typname = PQfnumber(res, "typname");
6162 364 : i_typnamespace = PQfnumber(res, "typnamespace");
6163 364 : i_typacl = PQfnumber(res, "typacl");
6164 364 : i_acldefault = PQfnumber(res, "acldefault");
6165 364 : i_typowner = PQfnumber(res, "typowner");
6166 364 : i_typelem = PQfnumber(res, "typelem");
6167 364 : i_typrelid = PQfnumber(res, "typrelid");
6168 364 : i_typrelkind = PQfnumber(res, "typrelkind");
6169 364 : i_typtype = PQfnumber(res, "typtype");
6170 364 : i_typisdefined = PQfnumber(res, "typisdefined");
6171 364 : i_isarray = PQfnumber(res, "isarray");
6172 364 : i_typarray = PQfnumber(res, "typarray");
6173 :
6174 265362 : for (i = 0; i < ntups; i++)
6175 : {
6176 264998 : tyinfo[i].dobj.objType = DO_TYPE;
6177 264998 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6178 264998 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6179 264998 : AssignDumpId(&tyinfo[i].dobj);
6180 264998 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6181 529996 : tyinfo[i].dobj.namespace =
6182 264998 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6183 264998 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6184 264998 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6185 264998 : tyinfo[i].dacl.privtype = 0;
6186 264998 : tyinfo[i].dacl.initprivs = NULL;
6187 264998 : tyinfo[i].ftypname = NULL; /* may get filled later */
6188 264998 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6189 264998 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6190 264998 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6191 264998 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6192 264998 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6193 264998 : tyinfo[i].shellType = NULL;
6194 :
6195 264998 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6196 264888 : tyinfo[i].isDefined = true;
6197 : else
6198 110 : tyinfo[i].isDefined = false;
6199 :
6200 264998 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6201 127166 : tyinfo[i].isArray = true;
6202 : else
6203 137832 : tyinfo[i].isArray = false;
6204 :
6205 264998 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6206 :
6207 264998 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6208 2460 : tyinfo[i].isMultirange = true;
6209 : else
6210 262538 : tyinfo[i].isMultirange = false;
6211 :
6212 : /* Decide whether we want to dump it */
6213 264998 : selectDumpableType(&tyinfo[i], fout);
6214 :
6215 : /* Mark whether type has an ACL */
6216 264998 : if (!PQgetisnull(res, i, i_typacl))
6217 434 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6218 :
6219 : /*
6220 : * If it's a domain, fetch info about its constraints, if any
6221 : */
6222 264998 : tyinfo[i].nDomChecks = 0;
6223 264998 : tyinfo[i].domChecks = NULL;
6224 264998 : tyinfo[i].notnull = NULL;
6225 264998 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6226 30996 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6227 322 : getDomainConstraints(fout, &(tyinfo[i]));
6228 :
6229 : /*
6230 : * If it's a base type, make a DumpableObject representing a shell
6231 : * definition of the type. We will need to dump that ahead of the I/O
6232 : * functions for the type. Similarly, range types need a shell
6233 : * definition in case they have a canonicalize function.
6234 : *
6235 : * Note: the shell type doesn't have a catId. You might think it
6236 : * should copy the base type's catId, but then it might capture the
6237 : * pg_depend entries for the type, which we don't want.
6238 : */
6239 264998 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6240 30996 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6241 15068 : tyinfo[i].typtype == TYPTYPE_RANGE))
6242 : {
6243 16188 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6244 16188 : stinfo->dobj.objType = DO_SHELL_TYPE;
6245 16188 : stinfo->dobj.catId = nilCatalogId;
6246 16188 : AssignDumpId(&stinfo->dobj);
6247 16188 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6248 16188 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6249 16188 : stinfo->baseType = &(tyinfo[i]);
6250 16188 : tyinfo[i].shellType = stinfo;
6251 :
6252 : /*
6253 : * Initially mark the shell type as not to be dumped. We'll only
6254 : * dump it if the I/O or canonicalize functions need to be dumped;
6255 : * this is taken care of while sorting dependencies.
6256 : */
6257 16188 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6258 : }
6259 : }
6260 :
6261 364 : PQclear(res);
6262 :
6263 364 : destroyPQExpBuffer(query);
6264 364 : }
6265 :
6266 : /*
6267 : * getOperators:
6268 : * get information about all operators in the system catalogs
6269 : */
6270 : void
6271 364 : getOperators(Archive *fout)
6272 : {
6273 : PGresult *res;
6274 : int ntups;
6275 : int i;
6276 364 : PQExpBuffer query = createPQExpBuffer();
6277 : OprInfo *oprinfo;
6278 : int i_tableoid;
6279 : int i_oid;
6280 : int i_oprname;
6281 : int i_oprnamespace;
6282 : int i_oprowner;
6283 : int i_oprkind;
6284 : int i_oprleft;
6285 : int i_oprright;
6286 : int i_oprcode;
6287 :
6288 : /*
6289 : * find all operators, including builtin operators; we filter out
6290 : * system-defined operators at dump-out time.
6291 : */
6292 :
6293 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6294 : "oprnamespace, "
6295 : "oprowner, "
6296 : "oprkind, "
6297 : "oprleft, "
6298 : "oprright, "
6299 : "oprcode::oid AS oprcode "
6300 : "FROM pg_operator");
6301 :
6302 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6303 :
6304 364 : ntups = PQntuples(res);
6305 :
6306 364 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6307 :
6308 364 : i_tableoid = PQfnumber(res, "tableoid");
6309 364 : i_oid = PQfnumber(res, "oid");
6310 364 : i_oprname = PQfnumber(res, "oprname");
6311 364 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6312 364 : i_oprowner = PQfnumber(res, "oprowner");
6313 364 : i_oprkind = PQfnumber(res, "oprkind");
6314 364 : i_oprleft = PQfnumber(res, "oprleft");
6315 364 : i_oprright = PQfnumber(res, "oprright");
6316 364 : i_oprcode = PQfnumber(res, "oprcode");
6317 :
6318 291490 : for (i = 0; i < ntups; i++)
6319 : {
6320 291126 : oprinfo[i].dobj.objType = DO_OPERATOR;
6321 291126 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6322 291126 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6323 291126 : AssignDumpId(&oprinfo[i].dobj);
6324 291126 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6325 582252 : oprinfo[i].dobj.namespace =
6326 291126 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6327 291126 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6328 291126 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6329 291126 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6330 291126 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6331 291126 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6332 :
6333 : /* Decide whether we want to dump it */
6334 291126 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6335 : }
6336 :
6337 364 : PQclear(res);
6338 :
6339 364 : destroyPQExpBuffer(query);
6340 364 : }
6341 :
6342 : /*
6343 : * getCollations:
6344 : * get information about all collations in the system catalogs
6345 : */
6346 : void
6347 364 : getCollations(Archive *fout)
6348 : {
6349 : PGresult *res;
6350 : int ntups;
6351 : int i;
6352 : PQExpBuffer query;
6353 : CollInfo *collinfo;
6354 : int i_tableoid;
6355 : int i_oid;
6356 : int i_collname;
6357 : int i_collnamespace;
6358 : int i_collowner;
6359 : int i_collencoding;
6360 :
6361 364 : query = createPQExpBuffer();
6362 :
6363 : /*
6364 : * find all collations, including builtin collations; we filter out
6365 : * system-defined collations at dump-out time.
6366 : */
6367 :
6368 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6369 : "collnamespace, "
6370 : "collowner, "
6371 : "collencoding "
6372 : "FROM pg_collation");
6373 :
6374 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6375 :
6376 364 : ntups = PQntuples(res);
6377 :
6378 364 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6379 :
6380 364 : i_tableoid = PQfnumber(res, "tableoid");
6381 364 : i_oid = PQfnumber(res, "oid");
6382 364 : i_collname = PQfnumber(res, "collname");
6383 364 : i_collnamespace = PQfnumber(res, "collnamespace");
6384 364 : i_collowner = PQfnumber(res, "collowner");
6385 364 : i_collencoding = PQfnumber(res, "collencoding");
6386 :
6387 297622 : for (i = 0; i < ntups; i++)
6388 : {
6389 297258 : collinfo[i].dobj.objType = DO_COLLATION;
6390 297258 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6391 297258 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6392 297258 : AssignDumpId(&collinfo[i].dobj);
6393 297258 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6394 594516 : collinfo[i].dobj.namespace =
6395 297258 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6396 297258 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6397 297258 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6398 :
6399 : /* Decide whether we want to dump it */
6400 297258 : selectDumpableObject(&(collinfo[i].dobj), fout);
6401 : }
6402 :
6403 364 : PQclear(res);
6404 :
6405 364 : destroyPQExpBuffer(query);
6406 364 : }
6407 :
6408 : /*
6409 : * getConversions:
6410 : * get information about all conversions in the system catalogs
6411 : */
6412 : void
6413 364 : getConversions(Archive *fout)
6414 : {
6415 : PGresult *res;
6416 : int ntups;
6417 : int i;
6418 : PQExpBuffer query;
6419 : ConvInfo *convinfo;
6420 : int i_tableoid;
6421 : int i_oid;
6422 : int i_conname;
6423 : int i_connamespace;
6424 : int i_conowner;
6425 :
6426 364 : query = createPQExpBuffer();
6427 :
6428 : /*
6429 : * find all conversions, including builtin conversions; we filter out
6430 : * system-defined conversions at dump-out time.
6431 : */
6432 :
6433 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6434 : "connamespace, "
6435 : "conowner "
6436 : "FROM pg_conversion");
6437 :
6438 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6439 :
6440 364 : ntups = PQntuples(res);
6441 :
6442 364 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6443 :
6444 364 : i_tableoid = PQfnumber(res, "tableoid");
6445 364 : i_oid = PQfnumber(res, "oid");
6446 364 : i_conname = PQfnumber(res, "conname");
6447 364 : i_connamespace = PQfnumber(res, "connamespace");
6448 364 : i_conowner = PQfnumber(res, "conowner");
6449 :
6450 47052 : for (i = 0; i < ntups; i++)
6451 : {
6452 46688 : convinfo[i].dobj.objType = DO_CONVERSION;
6453 46688 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6454 46688 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6455 46688 : AssignDumpId(&convinfo[i].dobj);
6456 46688 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6457 93376 : convinfo[i].dobj.namespace =
6458 46688 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6459 46688 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6460 :
6461 : /* Decide whether we want to dump it */
6462 46688 : selectDumpableObject(&(convinfo[i].dobj), fout);
6463 : }
6464 :
6465 364 : PQclear(res);
6466 :
6467 364 : destroyPQExpBuffer(query);
6468 364 : }
6469 :
6470 : /*
6471 : * getAccessMethods:
6472 : * get information about all user-defined access methods
6473 : */
6474 : void
6475 364 : getAccessMethods(Archive *fout)
6476 : {
6477 : PGresult *res;
6478 : int ntups;
6479 : int i;
6480 : PQExpBuffer query;
6481 : AccessMethodInfo *aminfo;
6482 : int i_tableoid;
6483 : int i_oid;
6484 : int i_amname;
6485 : int i_amhandler;
6486 : int i_amtype;
6487 :
6488 364 : query = createPQExpBuffer();
6489 :
6490 : /*
6491 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6492 : * ACCESS METHOD, so earlier versions usually have only built-in access
6493 : * methods. v9.6 also changed the access method API, replacing dozens of
6494 : * pg_am columns with amhandler. Even if a user created an access method
6495 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6496 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6497 : * pg_am just to facilitate findAccessMethodByOid() providing the
6498 : * OID-to-name mapping.
6499 : */
6500 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6501 364 : if (fout->remoteVersion >= 90600)
6502 364 : appendPQExpBufferStr(query,
6503 : "amtype, "
6504 : "amhandler::pg_catalog.regproc AS amhandler ");
6505 : else
6506 0 : appendPQExpBufferStr(query,
6507 : "'i'::pg_catalog.\"char\" AS amtype, "
6508 : "'-'::pg_catalog.regproc AS amhandler ");
6509 364 : appendPQExpBufferStr(query, "FROM pg_am");
6510 :
6511 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6512 :
6513 364 : ntups = PQntuples(res);
6514 :
6515 364 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6516 :
6517 364 : i_tableoid = PQfnumber(res, "tableoid");
6518 364 : i_oid = PQfnumber(res, "oid");
6519 364 : i_amname = PQfnumber(res, "amname");
6520 364 : i_amhandler = PQfnumber(res, "amhandler");
6521 364 : i_amtype = PQfnumber(res, "amtype");
6522 :
6523 3168 : for (i = 0; i < ntups; i++)
6524 : {
6525 2804 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6526 2804 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6527 2804 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6528 2804 : AssignDumpId(&aminfo[i].dobj);
6529 2804 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6530 2804 : aminfo[i].dobj.namespace = NULL;
6531 2804 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6532 2804 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6533 :
6534 : /* Decide whether we want to dump it */
6535 2804 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6536 : }
6537 :
6538 364 : PQclear(res);
6539 :
6540 364 : destroyPQExpBuffer(query);
6541 364 : }
6542 :
6543 :
6544 : /*
6545 : * getOpclasses:
6546 : * get information about all opclasses in the system catalogs
6547 : */
6548 : void
6549 364 : getOpclasses(Archive *fout)
6550 : {
6551 : PGresult *res;
6552 : int ntups;
6553 : int i;
6554 364 : PQExpBuffer query = createPQExpBuffer();
6555 : OpclassInfo *opcinfo;
6556 : int i_tableoid;
6557 : int i_oid;
6558 : int i_opcmethod;
6559 : int i_opcname;
6560 : int i_opcnamespace;
6561 : int i_opcowner;
6562 :
6563 : /*
6564 : * find all opclasses, including builtin opclasses; we filter out
6565 : * system-defined opclasses at dump-out time.
6566 : */
6567 :
6568 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6569 : "opcnamespace, "
6570 : "opcowner "
6571 : "FROM pg_opclass");
6572 :
6573 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6574 :
6575 364 : ntups = PQntuples(res);
6576 :
6577 364 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6578 :
6579 364 : i_tableoid = PQfnumber(res, "tableoid");
6580 364 : i_oid = PQfnumber(res, "oid");
6581 364 : i_opcmethod = PQfnumber(res, "opcmethod");
6582 364 : i_opcname = PQfnumber(res, "opcname");
6583 364 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6584 364 : i_opcowner = PQfnumber(res, "opcowner");
6585 :
6586 65122 : for (i = 0; i < ntups; i++)
6587 : {
6588 64758 : opcinfo[i].dobj.objType = DO_OPCLASS;
6589 64758 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6590 64758 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6591 64758 : AssignDumpId(&opcinfo[i].dobj);
6592 64758 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6593 129516 : opcinfo[i].dobj.namespace =
6594 64758 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6595 64758 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6596 64758 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6597 :
6598 : /* Decide whether we want to dump it */
6599 64758 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6600 : }
6601 :
6602 364 : PQclear(res);
6603 :
6604 364 : destroyPQExpBuffer(query);
6605 364 : }
6606 :
6607 : /*
6608 : * getOpfamilies:
6609 : * get information about all opfamilies in the system catalogs
6610 : */
6611 : void
6612 364 : getOpfamilies(Archive *fout)
6613 : {
6614 : PGresult *res;
6615 : int ntups;
6616 : int i;
6617 : PQExpBuffer query;
6618 : OpfamilyInfo *opfinfo;
6619 : int i_tableoid;
6620 : int i_oid;
6621 : int i_opfmethod;
6622 : int i_opfname;
6623 : int i_opfnamespace;
6624 : int i_opfowner;
6625 :
6626 364 : query = createPQExpBuffer();
6627 :
6628 : /*
6629 : * find all opfamilies, including builtin opfamilies; we filter out
6630 : * system-defined opfamilies at dump-out time.
6631 : */
6632 :
6633 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6634 : "opfnamespace, "
6635 : "opfowner "
6636 : "FROM pg_opfamily");
6637 :
6638 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6639 :
6640 364 : ntups = PQntuples(res);
6641 :
6642 364 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6643 :
6644 364 : i_tableoid = PQfnumber(res, "tableoid");
6645 364 : i_oid = PQfnumber(res, "oid");
6646 364 : i_opfname = PQfnumber(res, "opfname");
6647 364 : i_opfmethod = PQfnumber(res, "opfmethod");
6648 364 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6649 364 : i_opfowner = PQfnumber(res, "opfowner");
6650 :
6651 53798 : for (i = 0; i < ntups; i++)
6652 : {
6653 53434 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6654 53434 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6655 53434 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6656 53434 : AssignDumpId(&opfinfo[i].dobj);
6657 53434 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6658 106868 : opfinfo[i].dobj.namespace =
6659 53434 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6660 53434 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6661 53434 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6662 :
6663 : /* Decide whether we want to dump it */
6664 53434 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6665 : }
6666 :
6667 364 : PQclear(res);
6668 :
6669 364 : destroyPQExpBuffer(query);
6670 364 : }
6671 :
6672 : /*
6673 : * getAggregates:
6674 : * get information about all user-defined aggregates in the system catalogs
6675 : */
6676 : void
6677 364 : getAggregates(Archive *fout)
6678 : {
6679 364 : DumpOptions *dopt = fout->dopt;
6680 : PGresult *res;
6681 : int ntups;
6682 : int i;
6683 364 : PQExpBuffer query = createPQExpBuffer();
6684 : AggInfo *agginfo;
6685 : int i_tableoid;
6686 : int i_oid;
6687 : int i_aggname;
6688 : int i_aggnamespace;
6689 : int i_pronargs;
6690 : int i_proargtypes;
6691 : int i_proowner;
6692 : int i_aggacl;
6693 : int i_acldefault;
6694 :
6695 : /*
6696 : * Find all interesting aggregates. See comment in getFuncs() for the
6697 : * rationale behind the filtering logic.
6698 : */
6699 364 : if (fout->remoteVersion >= 90600)
6700 : {
6701 : const char *agg_check;
6702 :
6703 728 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6704 364 : : "p.proisagg");
6705 :
6706 364 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6707 : "p.proname AS aggname, "
6708 : "p.pronamespace AS aggnamespace, "
6709 : "p.pronargs, p.proargtypes, "
6710 : "p.proowner, "
6711 : "p.proacl AS aggacl, "
6712 : "acldefault('f', p.proowner) AS acldefault "
6713 : "FROM pg_proc p "
6714 : "LEFT JOIN pg_init_privs pip ON "
6715 : "(p.oid = pip.objoid "
6716 : "AND pip.classoid = 'pg_proc'::regclass "
6717 : "AND pip.objsubid = 0) "
6718 : "WHERE %s AND ("
6719 : "p.pronamespace != "
6720 : "(SELECT oid FROM pg_namespace "
6721 : "WHERE nspname = 'pg_catalog') OR "
6722 : "p.proacl IS DISTINCT FROM pip.initprivs",
6723 : agg_check);
6724 364 : if (dopt->binary_upgrade)
6725 72 : appendPQExpBufferStr(query,
6726 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6727 : "classid = 'pg_proc'::regclass AND "
6728 : "objid = p.oid AND "
6729 : "refclassid = 'pg_extension'::regclass AND "
6730 : "deptype = 'e')");
6731 364 : appendPQExpBufferChar(query, ')');
6732 : }
6733 : else
6734 : {
6735 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6736 : "pronamespace AS aggnamespace, "
6737 : "pronargs, proargtypes, "
6738 : "proowner, "
6739 : "proacl AS aggacl, "
6740 : "acldefault('f', proowner) AS acldefault "
6741 : "FROM pg_proc p "
6742 : "WHERE proisagg AND ("
6743 : "pronamespace != "
6744 : "(SELECT oid FROM pg_namespace "
6745 : "WHERE nspname = 'pg_catalog')");
6746 0 : if (dopt->binary_upgrade)
6747 0 : appendPQExpBufferStr(query,
6748 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6749 : "classid = 'pg_proc'::regclass AND "
6750 : "objid = p.oid AND "
6751 : "refclassid = 'pg_extension'::regclass AND "
6752 : "deptype = 'e')");
6753 0 : appendPQExpBufferChar(query, ')');
6754 : }
6755 :
6756 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6757 :
6758 364 : ntups = PQntuples(res);
6759 :
6760 364 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6761 :
6762 364 : i_tableoid = PQfnumber(res, "tableoid");
6763 364 : i_oid = PQfnumber(res, "oid");
6764 364 : i_aggname = PQfnumber(res, "aggname");
6765 364 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6766 364 : i_pronargs = PQfnumber(res, "pronargs");
6767 364 : i_proargtypes = PQfnumber(res, "proargtypes");
6768 364 : i_proowner = PQfnumber(res, "proowner");
6769 364 : i_aggacl = PQfnumber(res, "aggacl");
6770 364 : i_acldefault = PQfnumber(res, "acldefault");
6771 :
6772 1168 : for (i = 0; i < ntups; i++)
6773 : {
6774 804 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6775 804 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6776 804 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6777 804 : AssignDumpId(&agginfo[i].aggfn.dobj);
6778 804 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6779 1608 : agginfo[i].aggfn.dobj.namespace =
6780 804 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6781 804 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6782 804 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6783 804 : agginfo[i].aggfn.dacl.privtype = 0;
6784 804 : agginfo[i].aggfn.dacl.initprivs = NULL;
6785 804 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6786 804 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6787 804 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6788 804 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6789 804 : if (agginfo[i].aggfn.nargs == 0)
6790 112 : agginfo[i].aggfn.argtypes = NULL;
6791 : else
6792 : {
6793 692 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6794 692 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6795 692 : agginfo[i].aggfn.argtypes,
6796 692 : agginfo[i].aggfn.nargs);
6797 : }
6798 804 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6799 :
6800 : /* Decide whether we want to dump it */
6801 804 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6802 :
6803 : /* Mark whether aggregate has an ACL */
6804 804 : if (!PQgetisnull(res, i, i_aggacl))
6805 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6806 : }
6807 :
6808 364 : PQclear(res);
6809 :
6810 364 : destroyPQExpBuffer(query);
6811 364 : }
6812 :
6813 : /*
6814 : * getFuncs:
6815 : * get information about all user-defined functions in the system catalogs
6816 : */
6817 : void
6818 364 : getFuncs(Archive *fout)
6819 : {
6820 364 : DumpOptions *dopt = fout->dopt;
6821 : PGresult *res;
6822 : int ntups;
6823 : int i;
6824 364 : PQExpBuffer query = createPQExpBuffer();
6825 : FuncInfo *finfo;
6826 : int i_tableoid;
6827 : int i_oid;
6828 : int i_proname;
6829 : int i_pronamespace;
6830 : int i_proowner;
6831 : int i_prolang;
6832 : int i_pronargs;
6833 : int i_proargtypes;
6834 : int i_prorettype;
6835 : int i_proacl;
6836 : int i_acldefault;
6837 :
6838 : /*
6839 : * Find all interesting functions. This is a bit complicated:
6840 : *
6841 : * 1. Always exclude aggregates; those are handled elsewhere.
6842 : *
6843 : * 2. Always exclude functions that are internally dependent on something
6844 : * else, since presumably those will be created as a result of creating
6845 : * the something else. This currently acts only to suppress constructor
6846 : * functions for range types. Note this is OK only because the
6847 : * constructors don't have any dependencies the range type doesn't have;
6848 : * otherwise we might not get creation ordering correct.
6849 : *
6850 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6851 : * they're members of extensions and we are in binary-upgrade mode then
6852 : * include them, since we want to dump extension members individually in
6853 : * that mode. Also, if they are used by casts or transforms then we need
6854 : * to gather the information about them, though they won't be dumped if
6855 : * they are built-in. Also, in 9.6 and up, include functions in
6856 : * pg_catalog if they have an ACL different from what's shown in
6857 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6858 : */
6859 364 : if (fout->remoteVersion >= 90600)
6860 : {
6861 : const char *not_agg_check;
6862 :
6863 728 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6864 364 : : "NOT p.proisagg");
6865 :
6866 364 : appendPQExpBuffer(query,
6867 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6868 : "p.pronargs, p.proargtypes, p.prorettype, "
6869 : "p.proacl, "
6870 : "acldefault('f', p.proowner) AS acldefault, "
6871 : "p.pronamespace, "
6872 : "p.proowner "
6873 : "FROM pg_proc p "
6874 : "LEFT JOIN pg_init_privs pip ON "
6875 : "(p.oid = pip.objoid "
6876 : "AND pip.classoid = 'pg_proc'::regclass "
6877 : "AND pip.objsubid = 0) "
6878 : "WHERE %s"
6879 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6880 : "WHERE classid = 'pg_proc'::regclass AND "
6881 : "objid = p.oid AND deptype = 'i')"
6882 : "\n AND ("
6883 : "\n pronamespace != "
6884 : "(SELECT oid FROM pg_namespace "
6885 : "WHERE nspname = 'pg_catalog')"
6886 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6887 : "\n WHERE pg_cast.oid > %u "
6888 : "\n AND p.oid = pg_cast.castfunc)"
6889 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6890 : "\n WHERE pg_transform.oid > %u AND "
6891 : "\n (p.oid = pg_transform.trffromsql"
6892 : "\n OR p.oid = pg_transform.trftosql))",
6893 : not_agg_check,
6894 : g_last_builtin_oid,
6895 : g_last_builtin_oid);
6896 364 : if (dopt->binary_upgrade)
6897 72 : appendPQExpBufferStr(query,
6898 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6899 : "classid = 'pg_proc'::regclass AND "
6900 : "objid = p.oid AND "
6901 : "refclassid = 'pg_extension'::regclass AND "
6902 : "deptype = 'e')");
6903 364 : appendPQExpBufferStr(query,
6904 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6905 364 : appendPQExpBufferChar(query, ')');
6906 : }
6907 : else
6908 : {
6909 0 : appendPQExpBuffer(query,
6910 : "SELECT tableoid, oid, proname, prolang, "
6911 : "pronargs, proargtypes, prorettype, proacl, "
6912 : "acldefault('f', proowner) AS acldefault, "
6913 : "pronamespace, "
6914 : "proowner "
6915 : "FROM pg_proc p "
6916 : "WHERE NOT proisagg"
6917 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6918 : "WHERE classid = 'pg_proc'::regclass AND "
6919 : "objid = p.oid AND deptype = 'i')"
6920 : "\n AND ("
6921 : "\n pronamespace != "
6922 : "(SELECT oid FROM pg_namespace "
6923 : "WHERE nspname = 'pg_catalog')"
6924 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6925 : "\n WHERE pg_cast.oid > '%u'::oid"
6926 : "\n AND p.oid = pg_cast.castfunc)",
6927 : g_last_builtin_oid);
6928 :
6929 0 : if (fout->remoteVersion >= 90500)
6930 0 : appendPQExpBuffer(query,
6931 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6932 : "\n WHERE pg_transform.oid > '%u'::oid"
6933 : "\n AND (p.oid = pg_transform.trffromsql"
6934 : "\n OR p.oid = pg_transform.trftosql))",
6935 : g_last_builtin_oid);
6936 :
6937 0 : if (dopt->binary_upgrade)
6938 0 : appendPQExpBufferStr(query,
6939 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6940 : "classid = 'pg_proc'::regclass AND "
6941 : "objid = p.oid AND "
6942 : "refclassid = 'pg_extension'::regclass AND "
6943 : "deptype = 'e')");
6944 0 : appendPQExpBufferChar(query, ')');
6945 : }
6946 :
6947 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6948 :
6949 364 : ntups = PQntuples(res);
6950 :
6951 364 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6952 :
6953 364 : i_tableoid = PQfnumber(res, "tableoid");
6954 364 : i_oid = PQfnumber(res, "oid");
6955 364 : i_proname = PQfnumber(res, "proname");
6956 364 : i_pronamespace = PQfnumber(res, "pronamespace");
6957 364 : i_proowner = PQfnumber(res, "proowner");
6958 364 : i_prolang = PQfnumber(res, "prolang");
6959 364 : i_pronargs = PQfnumber(res, "pronargs");
6960 364 : i_proargtypes = PQfnumber(res, "proargtypes");
6961 364 : i_prorettype = PQfnumber(res, "prorettype");
6962 364 : i_proacl = PQfnumber(res, "proacl");
6963 364 : i_acldefault = PQfnumber(res, "acldefault");
6964 :
6965 9840 : for (i = 0; i < ntups; i++)
6966 : {
6967 9476 : finfo[i].dobj.objType = DO_FUNC;
6968 9476 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6969 9476 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6970 9476 : AssignDumpId(&finfo[i].dobj);
6971 9476 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6972 18952 : finfo[i].dobj.namespace =
6973 9476 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6974 9476 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6975 9476 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6976 9476 : finfo[i].dacl.privtype = 0;
6977 9476 : finfo[i].dacl.initprivs = NULL;
6978 9476 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6979 9476 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6980 9476 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6981 9476 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6982 9476 : if (finfo[i].nargs == 0)
6983 2152 : finfo[i].argtypes = NULL;
6984 : else
6985 : {
6986 7324 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6987 7324 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6988 7324 : finfo[i].argtypes, finfo[i].nargs);
6989 : }
6990 9476 : finfo[i].postponed_def = false; /* might get set during sort */
6991 :
6992 : /* Decide whether we want to dump it */
6993 9476 : selectDumpableObject(&(finfo[i].dobj), fout);
6994 :
6995 : /* Mark whether function has an ACL */
6996 9476 : if (!PQgetisnull(res, i, i_proacl))
6997 292 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6998 : }
6999 :
7000 364 : PQclear(res);
7001 :
7002 364 : destroyPQExpBuffer(query);
7003 364 : }
7004 :
7005 : /*
7006 : * getRelationStatistics
7007 : * register the statistics object as a dependent of the relation.
7008 : *
7009 : * reltuples is passed as a string to avoid complexities in converting from/to
7010 : * floating point.
7011 : */
7012 : static RelStatsInfo *
7013 19722 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7014 : char *reltuples, int32 relallvisible,
7015 : int32 relallfrozen, char relkind,
7016 : char **indAttNames, int nindAttNames)
7017 : {
7018 19722 : if (!fout->dopt->dumpStatistics)
7019 12072 : return NULL;
7020 :
7021 7650 : if ((relkind == RELKIND_RELATION) ||
7022 3210 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7023 1956 : (relkind == RELKIND_INDEX) ||
7024 1284 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7025 568 : (relkind == RELKIND_MATVIEW ||
7026 : relkind == RELKIND_FOREIGN_TABLE))
7027 : {
7028 7152 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7029 7152 : DumpableObject *dobj = &info->dobj;
7030 :
7031 7152 : dobj->objType = DO_REL_STATS;
7032 7152 : dobj->catId.tableoid = 0;
7033 7152 : dobj->catId.oid = 0;
7034 7152 : AssignDumpId(dobj);
7035 7152 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7036 7152 : dobj->dependencies[0] = rel->dumpId;
7037 7152 : dobj->nDeps = 1;
7038 7152 : dobj->allocDeps = 1;
7039 7152 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7040 7152 : dobj->name = pg_strdup(rel->name);
7041 7152 : dobj->namespace = rel->namespace;
7042 7152 : info->relpages = relpages;
7043 7152 : info->reltuples = pstrdup(reltuples);
7044 7152 : info->relallvisible = relallvisible;
7045 7152 : info->relallfrozen = relallfrozen;
7046 7152 : info->relkind = relkind;
7047 7152 : info->indAttNames = indAttNames;
7048 7152 : info->nindAttNames = nindAttNames;
7049 :
7050 : /*
7051 : * Ordinarily, stats go in SECTION_DATA for tables and
7052 : * SECTION_POST_DATA for indexes.
7053 : *
7054 : * However, the section may be updated later for materialized view
7055 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7056 : * the stats, so the stats must be restored after the data. Also, the
7057 : * materialized view definition may be postponed to SECTION_POST_DATA
7058 : * (see repairMatViewBoundaryMultiLoop()).
7059 : */
7060 7152 : switch (info->relkind)
7061 : {
7062 5226 : case RELKIND_RELATION:
7063 : case RELKIND_PARTITIONED_TABLE:
7064 : case RELKIND_MATVIEW:
7065 : case RELKIND_FOREIGN_TABLE:
7066 5226 : info->section = SECTION_DATA;
7067 5226 : break;
7068 1926 : case RELKIND_INDEX:
7069 : case RELKIND_PARTITIONED_INDEX:
7070 1926 : info->section = SECTION_POST_DATA;
7071 1926 : break;
7072 0 : default:
7073 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7074 : info->relkind);
7075 : }
7076 :
7077 7152 : return info;
7078 : }
7079 498 : return NULL;
7080 : }
7081 :
7082 : /*
7083 : * getTables
7084 : * read all the tables (no indexes) in the system catalogs,
7085 : * and return them as an array of TableInfo structures
7086 : *
7087 : * *numTables is set to the number of tables read in
7088 : */
7089 : TableInfo *
7090 366 : getTables(Archive *fout, int *numTables)
7091 : {
7092 366 : DumpOptions *dopt = fout->dopt;
7093 : PGresult *res;
7094 : int ntups;
7095 : int i;
7096 366 : PQExpBuffer query = createPQExpBuffer();
7097 : TableInfo *tblinfo;
7098 : int i_reltableoid;
7099 : int i_reloid;
7100 : int i_relname;
7101 : int i_relnamespace;
7102 : int i_relkind;
7103 : int i_reltype;
7104 : int i_relowner;
7105 : int i_relchecks;
7106 : int i_relhasindex;
7107 : int i_relhasrules;
7108 : int i_relpages;
7109 : int i_reltuples;
7110 : int i_relallvisible;
7111 : int i_relallfrozen;
7112 : int i_toastpages;
7113 : int i_owning_tab;
7114 : int i_owning_col;
7115 : int i_reltablespace;
7116 : int i_relhasoids;
7117 : int i_relhastriggers;
7118 : int i_relpersistence;
7119 : int i_relispopulated;
7120 : int i_relreplident;
7121 : int i_relrowsec;
7122 : int i_relforcerowsec;
7123 : int i_relfrozenxid;
7124 : int i_toastfrozenxid;
7125 : int i_toastoid;
7126 : int i_relminmxid;
7127 : int i_toastminmxid;
7128 : int i_reloptions;
7129 : int i_checkoption;
7130 : int i_toastreloptions;
7131 : int i_reloftype;
7132 : int i_foreignserver;
7133 : int i_amname;
7134 : int i_is_identity_sequence;
7135 : int i_relacl;
7136 : int i_acldefault;
7137 : int i_ispartition;
7138 :
7139 : /*
7140 : * Find all the tables and table-like objects.
7141 : *
7142 : * We must fetch all tables in this phase because otherwise we cannot
7143 : * correctly identify inherited columns, owned sequences, etc.
7144 : *
7145 : * We include system catalogs, so that we can work if a user table is
7146 : * defined to inherit from a system catalog (pretty weird, but...)
7147 : *
7148 : * Note: in this phase we should collect only a minimal amount of
7149 : * information about each table, basically just enough to decide if it is
7150 : * interesting. In particular, since we do not yet have lock on any user
7151 : * table, we MUST NOT invoke any server-side data collection functions
7152 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7153 : * wrong answers if any concurrent DDL is happening.
7154 : */
7155 :
7156 366 : appendPQExpBufferStr(query,
7157 : "SELECT c.tableoid, c.oid, c.relname, "
7158 : "c.relnamespace, c.relkind, c.reltype, "
7159 : "c.relowner, "
7160 : "c.relchecks, "
7161 : "c.relhasindex, c.relhasrules, c.relpages, "
7162 : "c.reltuples, c.relallvisible, ");
7163 :
7164 366 : if (fout->remoteVersion >= 180000)
7165 366 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7166 : else
7167 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7168 :
7169 366 : appendPQExpBufferStr(query,
7170 : "c.relhastriggers, c.relpersistence, "
7171 : "c.reloftype, "
7172 : "c.relacl, "
7173 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7174 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7175 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7176 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7177 : "ELSE 0 END AS foreignserver, "
7178 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7179 : "tc.oid AS toid, "
7180 : "tc.relpages AS toastpages, "
7181 : "tc.reloptions AS toast_reloptions, "
7182 : "d.refobjid AS owning_tab, "
7183 : "d.refobjsubid AS owning_col, "
7184 : "tsp.spcname AS reltablespace, ");
7185 :
7186 366 : if (fout->remoteVersion >= 120000)
7187 366 : appendPQExpBufferStr(query,
7188 : "false AS relhasoids, ");
7189 : else
7190 0 : appendPQExpBufferStr(query,
7191 : "c.relhasoids, ");
7192 :
7193 366 : if (fout->remoteVersion >= 90300)
7194 366 : appendPQExpBufferStr(query,
7195 : "c.relispopulated, ");
7196 : else
7197 0 : appendPQExpBufferStr(query,
7198 : "'t' as relispopulated, ");
7199 :
7200 366 : if (fout->remoteVersion >= 90400)
7201 366 : appendPQExpBufferStr(query,
7202 : "c.relreplident, ");
7203 : else
7204 0 : appendPQExpBufferStr(query,
7205 : "'d' AS relreplident, ");
7206 :
7207 366 : if (fout->remoteVersion >= 90500)
7208 366 : appendPQExpBufferStr(query,
7209 : "c.relrowsecurity, c.relforcerowsecurity, ");
7210 : else
7211 0 : appendPQExpBufferStr(query,
7212 : "false AS relrowsecurity, "
7213 : "false AS relforcerowsecurity, ");
7214 :
7215 366 : if (fout->remoteVersion >= 90300)
7216 366 : appendPQExpBufferStr(query,
7217 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7218 : else
7219 0 : appendPQExpBufferStr(query,
7220 : "0 AS relminmxid, 0 AS tminmxid, ");
7221 :
7222 366 : if (fout->remoteVersion >= 90300)
7223 366 : appendPQExpBufferStr(query,
7224 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7225 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7226 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7227 : else
7228 0 : appendPQExpBufferStr(query,
7229 : "c.reloptions, NULL AS checkoption, ");
7230 :
7231 366 : if (fout->remoteVersion >= 90600)
7232 366 : appendPQExpBufferStr(query,
7233 : "am.amname, ");
7234 : else
7235 0 : appendPQExpBufferStr(query,
7236 : "NULL AS amname, ");
7237 :
7238 366 : if (fout->remoteVersion >= 90600)
7239 366 : appendPQExpBufferStr(query,
7240 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7241 : else
7242 0 : appendPQExpBufferStr(query,
7243 : "false AS is_identity_sequence, ");
7244 :
7245 366 : if (fout->remoteVersion >= 100000)
7246 366 : appendPQExpBufferStr(query,
7247 : "c.relispartition AS ispartition ");
7248 : else
7249 0 : appendPQExpBufferStr(query,
7250 : "false AS ispartition ");
7251 :
7252 : /*
7253 : * Left join to pg_depend to pick up dependency info linking sequences to
7254 : * their owning column, if any (note this dependency is AUTO except for
7255 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7256 : * collect the spcname.
7257 : */
7258 366 : appendPQExpBufferStr(query,
7259 : "\nFROM pg_class c\n"
7260 : "LEFT JOIN pg_depend d ON "
7261 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7262 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7263 : "d.objsubid = 0 AND "
7264 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7265 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7266 :
7267 : /*
7268 : * In 9.6 and up, left join to pg_am to pick up the amname.
7269 : */
7270 366 : if (fout->remoteVersion >= 90600)
7271 366 : appendPQExpBufferStr(query,
7272 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7273 :
7274 : /*
7275 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7276 : * that versions 10 and 11 have them, but later versions do not, so
7277 : * emitting them causes the upgrade to fail.
7278 : */
7279 366 : appendPQExpBufferStr(query,
7280 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7281 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7282 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7283 :
7284 : /*
7285 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7286 : * relkinds are possible in older servers, but it's not worth the trouble
7287 : * to emit a version-dependent list.
7288 : *
7289 : * Composite-type table entries won't be dumped as such, but we have to
7290 : * make a DumpableObject for them so that we can track dependencies of the
7291 : * composite type (pg_depend entries for columns of the composite type
7292 : * link to the pg_class entry not the pg_type entry).
7293 : */
7294 366 : appendPQExpBufferStr(query,
7295 : "WHERE c.relkind IN ("
7296 : CppAsString2(RELKIND_RELATION) ", "
7297 : CppAsString2(RELKIND_SEQUENCE) ", "
7298 : CppAsString2(RELKIND_VIEW) ", "
7299 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7300 : CppAsString2(RELKIND_MATVIEW) ", "
7301 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7302 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7303 : "ORDER BY c.oid");
7304 :
7305 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7306 :
7307 366 : ntups = PQntuples(res);
7308 :
7309 366 : *numTables = ntups;
7310 :
7311 : /*
7312 : * Extract data from result and lock dumpable tables. We do the locking
7313 : * before anything else, to minimize the window wherein a table could
7314 : * disappear under us.
7315 : *
7316 : * Note that we have to save info about all tables here, even when dumping
7317 : * only one, because we don't yet know which tables might be inheritance
7318 : * ancestors of the target table.
7319 : */
7320 366 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7321 :
7322 366 : i_reltableoid = PQfnumber(res, "tableoid");
7323 366 : i_reloid = PQfnumber(res, "oid");
7324 366 : i_relname = PQfnumber(res, "relname");
7325 366 : i_relnamespace = PQfnumber(res, "relnamespace");
7326 366 : i_relkind = PQfnumber(res, "relkind");
7327 366 : i_reltype = PQfnumber(res, "reltype");
7328 366 : i_relowner = PQfnumber(res, "relowner");
7329 366 : i_relchecks = PQfnumber(res, "relchecks");
7330 366 : i_relhasindex = PQfnumber(res, "relhasindex");
7331 366 : i_relhasrules = PQfnumber(res, "relhasrules");
7332 366 : i_relpages = PQfnumber(res, "relpages");
7333 366 : i_reltuples = PQfnumber(res, "reltuples");
7334 366 : i_relallvisible = PQfnumber(res, "relallvisible");
7335 366 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7336 366 : i_toastpages = PQfnumber(res, "toastpages");
7337 366 : i_owning_tab = PQfnumber(res, "owning_tab");
7338 366 : i_owning_col = PQfnumber(res, "owning_col");
7339 366 : i_reltablespace = PQfnumber(res, "reltablespace");
7340 366 : i_relhasoids = PQfnumber(res, "relhasoids");
7341 366 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7342 366 : i_relpersistence = PQfnumber(res, "relpersistence");
7343 366 : i_relispopulated = PQfnumber(res, "relispopulated");
7344 366 : i_relreplident = PQfnumber(res, "relreplident");
7345 366 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7346 366 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7347 366 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7348 366 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7349 366 : i_toastoid = PQfnumber(res, "toid");
7350 366 : i_relminmxid = PQfnumber(res, "relminmxid");
7351 366 : i_toastminmxid = PQfnumber(res, "tminmxid");
7352 366 : i_reloptions = PQfnumber(res, "reloptions");
7353 366 : i_checkoption = PQfnumber(res, "checkoption");
7354 366 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7355 366 : i_reloftype = PQfnumber(res, "reloftype");
7356 366 : i_foreignserver = PQfnumber(res, "foreignserver");
7357 366 : i_amname = PQfnumber(res, "amname");
7358 366 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7359 366 : i_relacl = PQfnumber(res, "relacl");
7360 366 : i_acldefault = PQfnumber(res, "acldefault");
7361 366 : i_ispartition = PQfnumber(res, "ispartition");
7362 :
7363 366 : if (dopt->lockWaitTimeout)
7364 : {
7365 : /*
7366 : * Arrange to fail instead of waiting forever for a table lock.
7367 : *
7368 : * NB: this coding assumes that the only queries issued within the
7369 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7370 : * applied to other things too.
7371 : */
7372 4 : resetPQExpBuffer(query);
7373 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7374 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7375 4 : ExecuteSqlStatement(fout, query->data);
7376 : }
7377 :
7378 366 : resetPQExpBuffer(query);
7379 :
7380 97364 : for (i = 0; i < ntups; i++)
7381 : {
7382 96998 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7383 96998 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7384 :
7385 96998 : tblinfo[i].dobj.objType = DO_TABLE;
7386 96998 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7387 96998 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7388 96998 : AssignDumpId(&tblinfo[i].dobj);
7389 96998 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7390 193996 : tblinfo[i].dobj.namespace =
7391 96998 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7392 96998 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7393 96998 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7394 96998 : tblinfo[i].dacl.privtype = 0;
7395 96998 : tblinfo[i].dacl.initprivs = NULL;
7396 96998 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7397 96998 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7398 96998 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7399 96998 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7400 96998 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7401 96998 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7402 96998 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7403 96998 : if (PQgetisnull(res, i, i_toastpages))
7404 77690 : tblinfo[i].toastpages = 0;
7405 : else
7406 19308 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7407 96998 : if (PQgetisnull(res, i, i_owning_tab))
7408 : {
7409 96150 : tblinfo[i].owning_tab = InvalidOid;
7410 96150 : tblinfo[i].owning_col = 0;
7411 : }
7412 : else
7413 : {
7414 848 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7415 848 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7416 : }
7417 96998 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7418 96998 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7419 96998 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7420 96998 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7421 96998 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7422 96998 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7423 96998 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7424 96998 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7425 96998 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7426 96998 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7427 96998 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7428 96998 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7429 96998 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7430 96998 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7431 96998 : if (PQgetisnull(res, i, i_checkoption))
7432 96900 : tblinfo[i].checkoption = NULL;
7433 : else
7434 98 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7435 96998 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7436 96998 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7437 96998 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7438 96998 : if (PQgetisnull(res, i, i_amname))
7439 57546 : tblinfo[i].amname = NULL;
7440 : else
7441 39452 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7442 96998 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7443 96998 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7444 :
7445 : /* other fields were zeroed above */
7446 :
7447 : /*
7448 : * Decide whether we want to dump this table.
7449 : */
7450 96998 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7451 372 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7452 : else
7453 96626 : selectDumpableTable(&tblinfo[i], fout);
7454 :
7455 : /*
7456 : * Now, consider the table "interesting" if we need to dump its
7457 : * definition, data or its statistics. Later on, we'll skip a lot of
7458 : * data collection for uninteresting tables.
7459 : *
7460 : * Note: the "interesting" flag will also be set by flagInhTables for
7461 : * parents of interesting tables, so that we collect necessary
7462 : * inheritance info even when the parents are not themselves being
7463 : * dumped. This is the main reason why we need an "interesting" flag
7464 : * that's separate from the components-to-dump bitmask.
7465 : */
7466 96998 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7467 : (DUMP_COMPONENT_DEFINITION |
7468 : DUMP_COMPONENT_DATA |
7469 96998 : DUMP_COMPONENT_STATISTICS)) != 0;
7470 :
7471 96998 : tblinfo[i].dummy_view = false; /* might get set during sort */
7472 96998 : tblinfo[i].postponed_def = false; /* might get set during sort */
7473 :
7474 : /* Tables have data */
7475 96998 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7476 :
7477 : /* Mark whether table has an ACL */
7478 96998 : if (!PQgetisnull(res, i, i_relacl))
7479 76934 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7480 96998 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7481 :
7482 : /* Add statistics */
7483 96998 : if (tblinfo[i].interesting)
7484 : {
7485 : RelStatsInfo *stats;
7486 :
7487 28884 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7488 14442 : tblinfo[i].relpages,
7489 : PQgetvalue(res, i, i_reltuples),
7490 : relallvisible, relallfrozen,
7491 14442 : tblinfo[i].relkind, NULL, 0);
7492 14442 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7493 920 : tblinfo[i].stats = stats;
7494 : }
7495 :
7496 : /*
7497 : * Read-lock target tables to make sure they aren't DROPPED or altered
7498 : * in schema before we get around to dumping them.
7499 : *
7500 : * Note that we don't explicitly lock parents of the target tables; we
7501 : * assume our lock on the child is enough to prevent schema
7502 : * alterations to parent tables.
7503 : *
7504 : * NOTE: it'd be kinda nice to lock other relations too, not only
7505 : * plain or partitioned tables, but the backend doesn't presently
7506 : * allow that.
7507 : *
7508 : * We only need to lock the table for certain components; see
7509 : * pg_dump.h
7510 : */
7511 96998 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7512 14442 : (tblinfo[i].relkind == RELKIND_RELATION ||
7513 4110 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7514 : {
7515 : /*
7516 : * Tables are locked in batches. When dumping from a remote
7517 : * server this can save a significant amount of time by reducing
7518 : * the number of round trips.
7519 : */
7520 11544 : if (query->len == 0)
7521 238 : appendPQExpBuffer(query, "LOCK TABLE %s",
7522 238 : fmtQualifiedDumpable(&tblinfo[i]));
7523 : else
7524 : {
7525 11306 : appendPQExpBuffer(query, ", %s",
7526 11306 : fmtQualifiedDumpable(&tblinfo[i]));
7527 :
7528 : /* Arbitrarily end a batch when query length reaches 100K. */
7529 11306 : if (query->len >= 100000)
7530 : {
7531 : /* Lock another batch of tables. */
7532 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7533 0 : ExecuteSqlStatement(fout, query->data);
7534 0 : resetPQExpBuffer(query);
7535 : }
7536 : }
7537 : }
7538 : }
7539 :
7540 366 : if (query->len != 0)
7541 : {
7542 : /* Lock the tables in the last batch. */
7543 238 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7544 238 : ExecuteSqlStatement(fout, query->data);
7545 : }
7546 :
7547 364 : if (dopt->lockWaitTimeout)
7548 : {
7549 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7550 : }
7551 :
7552 364 : PQclear(res);
7553 :
7554 364 : destroyPQExpBuffer(query);
7555 :
7556 364 : return tblinfo;
7557 : }
7558 :
7559 : /*
7560 : * getOwnedSeqs
7561 : * identify owned sequences and mark them as dumpable if owning table is
7562 : *
7563 : * We used to do this in getTables(), but it's better to do it after the
7564 : * index used by findTableByOid() has been set up.
7565 : */
7566 : void
7567 364 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7568 : {
7569 : int i;
7570 :
7571 : /*
7572 : * Force sequences that are "owned" by table columns to be dumped whenever
7573 : * their owning table is being dumped.
7574 : */
7575 96818 : for (i = 0; i < numTables; i++)
7576 : {
7577 96454 : TableInfo *seqinfo = &tblinfo[i];
7578 : TableInfo *owning_tab;
7579 :
7580 96454 : if (!OidIsValid(seqinfo->owning_tab))
7581 95612 : continue; /* not an owned sequence */
7582 :
7583 842 : owning_tab = findTableByOid(seqinfo->owning_tab);
7584 842 : if (owning_tab == NULL)
7585 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7586 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7587 :
7588 : /*
7589 : * For an identity sequence, dump exactly the same components for the
7590 : * sequence as for the owning table. This is important because we
7591 : * treat the identity sequence as an integral part of the table. For
7592 : * example, there is not any DDL command that allows creation of such
7593 : * a sequence independently of the table.
7594 : *
7595 : * For other owned sequences such as serial sequences, we need to dump
7596 : * the components that are being dumped for the table and any
7597 : * components that the sequence is explicitly marked with.
7598 : *
7599 : * We can't simply use the set of components which are being dumped
7600 : * for the table as the table might be in an extension (and only the
7601 : * non-extension components, eg: ACLs if changed, security labels, and
7602 : * policies, are being dumped) while the sequence is not (and
7603 : * therefore the definition and other components should also be
7604 : * dumped).
7605 : *
7606 : * If the sequence is part of the extension then it should be properly
7607 : * marked by checkExtensionMembership() and this will be a no-op as
7608 : * the table will be equivalently marked.
7609 : */
7610 842 : if (seqinfo->is_identity_sequence)
7611 404 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7612 : else
7613 438 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7614 :
7615 : /* Make sure that necessary data is available if we're dumping it */
7616 842 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7617 : {
7618 650 : seqinfo->interesting = true;
7619 650 : owning_tab->interesting = true;
7620 : }
7621 : }
7622 364 : }
7623 :
7624 : /*
7625 : * getInherits
7626 : * read all the inheritance information
7627 : * from the system catalogs return them in the InhInfo* structure
7628 : *
7629 : * numInherits is set to the number of pairs read in
7630 : */
7631 : InhInfo *
7632 364 : getInherits(Archive *fout, int *numInherits)
7633 : {
7634 : PGresult *res;
7635 : int ntups;
7636 : int i;
7637 364 : PQExpBuffer query = createPQExpBuffer();
7638 : InhInfo *inhinfo;
7639 :
7640 : int i_inhrelid;
7641 : int i_inhparent;
7642 :
7643 : /* find all the inheritance information */
7644 364 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7645 :
7646 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7647 :
7648 364 : ntups = PQntuples(res);
7649 :
7650 364 : *numInherits = ntups;
7651 :
7652 364 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7653 :
7654 364 : i_inhrelid = PQfnumber(res, "inhrelid");
7655 364 : i_inhparent = PQfnumber(res, "inhparent");
7656 :
7657 7230 : for (i = 0; i < ntups; i++)
7658 : {
7659 6866 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7660 6866 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7661 : }
7662 :
7663 364 : PQclear(res);
7664 :
7665 364 : destroyPQExpBuffer(query);
7666 :
7667 364 : return inhinfo;
7668 : }
7669 :
7670 : /*
7671 : * getPartitioningInfo
7672 : * get information about partitioning
7673 : *
7674 : * For the most part, we only collect partitioning info about tables we
7675 : * intend to dump. However, this function has to consider all partitioned
7676 : * tables in the database, because we need to know about parents of partitions
7677 : * we are going to dump even if the parents themselves won't be dumped.
7678 : *
7679 : * Specifically, what we need to know is whether each partitioned table
7680 : * has an "unsafe" partitioning scheme that requires us to force
7681 : * load-via-partition-root mode for its children. Currently the only case
7682 : * for which we force that is hash partitioning on enum columns, since the
7683 : * hash codes depend on enum value OIDs which won't be replicated across
7684 : * dump-and-reload. There are other cases in which load-via-partition-root
7685 : * might be necessary, but we expect users to cope with them.
7686 : */
7687 : void
7688 364 : getPartitioningInfo(Archive *fout)
7689 : {
7690 : PQExpBuffer query;
7691 : PGresult *res;
7692 : int ntups;
7693 :
7694 : /* hash partitioning didn't exist before v11 */
7695 364 : if (fout->remoteVersion < 110000)
7696 0 : return;
7697 : /* needn't bother if not dumping data */
7698 364 : if (!fout->dopt->dumpData)
7699 80 : return;
7700 :
7701 284 : query = createPQExpBuffer();
7702 :
7703 : /*
7704 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7705 : * appears among the partition opclasses. We needn't check partstrat.
7706 : *
7707 : * Note that this query may well retrieve info about tables we aren't
7708 : * going to dump and hence have no lock on. That's okay since we need not
7709 : * invoke any unsafe server-side functions.
7710 : */
7711 284 : appendPQExpBufferStr(query,
7712 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7713 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7714 : "ON c.opcmethod = a.oid\n"
7715 : "WHERE opcname = 'enum_ops' "
7716 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7717 : "AND amname = 'hash') = ANY(partclass)");
7718 :
7719 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7720 :
7721 284 : ntups = PQntuples(res);
7722 :
7723 376 : for (int i = 0; i < ntups; i++)
7724 : {
7725 92 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7726 : TableInfo *tbinfo;
7727 :
7728 92 : tbinfo = findTableByOid(tabrelid);
7729 92 : if (tbinfo == NULL)
7730 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7731 : tabrelid);
7732 92 : tbinfo->unsafe_partitions = true;
7733 : }
7734 :
7735 284 : PQclear(res);
7736 :
7737 284 : destroyPQExpBuffer(query);
7738 : }
7739 :
7740 : /*
7741 : * getIndexes
7742 : * get information about every index on a dumpable table
7743 : *
7744 : * Note: index data is not returned directly to the caller, but it
7745 : * does get entered into the DumpableObject tables.
7746 : */
7747 : void
7748 364 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7749 : {
7750 364 : PQExpBuffer query = createPQExpBuffer();
7751 364 : PQExpBuffer tbloids = createPQExpBuffer();
7752 : PGresult *res;
7753 : int ntups;
7754 : int curtblindx;
7755 : IndxInfo *indxinfo;
7756 : int i_tableoid,
7757 : i_oid,
7758 : i_indrelid,
7759 : i_indexname,
7760 : i_relpages,
7761 : i_reltuples,
7762 : i_relallvisible,
7763 : i_relallfrozen,
7764 : i_parentidx,
7765 : i_indexdef,
7766 : i_indnkeyatts,
7767 : i_indnatts,
7768 : i_indkey,
7769 : i_indisclustered,
7770 : i_indisreplident,
7771 : i_indnullsnotdistinct,
7772 : i_contype,
7773 : i_conname,
7774 : i_condeferrable,
7775 : i_condeferred,
7776 : i_conperiod,
7777 : i_contableoid,
7778 : i_conoid,
7779 : i_condef,
7780 : i_indattnames,
7781 : i_tablespace,
7782 : i_indreloptions,
7783 : i_indstatcols,
7784 : i_indstatvals;
7785 :
7786 : /*
7787 : * We want to perform just one query against pg_index. However, we
7788 : * mustn't try to select every row of the catalog and then sort it out on
7789 : * the client side, because some of the server-side functions we need
7790 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7791 : * build an array of the OIDs of tables we care about (and now have lock
7792 : * on!), and use a WHERE clause to constrain which rows are selected.
7793 : */
7794 364 : appendPQExpBufferChar(tbloids, '{');
7795 96818 : for (int i = 0; i < numTables; i++)
7796 : {
7797 96454 : TableInfo *tbinfo = &tblinfo[i];
7798 :
7799 96454 : if (!tbinfo->hasindex)
7800 68326 : continue;
7801 :
7802 : /*
7803 : * We can ignore indexes of uninteresting tables.
7804 : */
7805 28128 : if (!tbinfo->interesting)
7806 24070 : continue;
7807 :
7808 : /* OK, we need info for this table */
7809 4058 : if (tbloids->len > 1) /* do we have more than the '{'? */
7810 3896 : appendPQExpBufferChar(tbloids, ',');
7811 4058 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7812 : }
7813 364 : appendPQExpBufferChar(tbloids, '}');
7814 :
7815 364 : appendPQExpBufferStr(query,
7816 : "SELECT t.tableoid, t.oid, i.indrelid, "
7817 : "t.relname AS indexname, "
7818 : "t.relpages, t.reltuples, t.relallvisible, ");
7819 :
7820 364 : if (fout->remoteVersion >= 180000)
7821 364 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7822 : else
7823 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7824 :
7825 364 : appendPQExpBufferStr(query,
7826 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7827 : "i.indkey, i.indisclustered, "
7828 : "c.contype, c.conname, "
7829 : "c.condeferrable, c.condeferred, "
7830 : "c.tableoid AS contableoid, "
7831 : "c.oid AS conoid, "
7832 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7833 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7834 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7835 : " FROM pg_catalog.pg_attribute "
7836 : " WHERE attrelid = i.indexrelid) "
7837 : "ELSE NULL END AS indattnames, "
7838 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7839 : "t.reloptions AS indreloptions, ");
7840 :
7841 :
7842 364 : if (fout->remoteVersion >= 90400)
7843 364 : appendPQExpBufferStr(query,
7844 : "i.indisreplident, ");
7845 : else
7846 0 : appendPQExpBufferStr(query,
7847 : "false AS indisreplident, ");
7848 :
7849 364 : if (fout->remoteVersion >= 110000)
7850 364 : appendPQExpBufferStr(query,
7851 : "inh.inhparent AS parentidx, "
7852 : "i.indnkeyatts AS indnkeyatts, "
7853 : "i.indnatts AS indnatts, "
7854 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7855 : " FROM pg_catalog.pg_attribute "
7856 : " WHERE attrelid = i.indexrelid AND "
7857 : " attstattarget >= 0) AS indstatcols, "
7858 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7859 : " FROM pg_catalog.pg_attribute "
7860 : " WHERE attrelid = i.indexrelid AND "
7861 : " attstattarget >= 0) AS indstatvals, ");
7862 : else
7863 0 : appendPQExpBufferStr(query,
7864 : "0 AS parentidx, "
7865 : "i.indnatts AS indnkeyatts, "
7866 : "i.indnatts AS indnatts, "
7867 : "'' AS indstatcols, "
7868 : "'' AS indstatvals, ");
7869 :
7870 364 : if (fout->remoteVersion >= 150000)
7871 364 : appendPQExpBufferStr(query,
7872 : "i.indnullsnotdistinct, ");
7873 : else
7874 0 : appendPQExpBufferStr(query,
7875 : "false AS indnullsnotdistinct, ");
7876 :
7877 364 : if (fout->remoteVersion >= 180000)
7878 364 : appendPQExpBufferStr(query,
7879 : "c.conperiod ");
7880 : else
7881 0 : appendPQExpBufferStr(query,
7882 : "NULL AS conperiod ");
7883 :
7884 : /*
7885 : * The point of the messy-looking outer join is to find a constraint that
7886 : * is related by an internal dependency link to the index. If we find one,
7887 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7888 : * index won't have more than one internal dependency.
7889 : *
7890 : * Note: the check on conrelid is redundant, but useful because that
7891 : * column is indexed while conindid is not.
7892 : */
7893 364 : if (fout->remoteVersion >= 110000)
7894 : {
7895 364 : appendPQExpBuffer(query,
7896 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7897 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7898 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7899 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7900 : "LEFT JOIN pg_catalog.pg_constraint c "
7901 : "ON (i.indrelid = c.conrelid AND "
7902 : "i.indexrelid = c.conindid AND "
7903 : "c.contype IN ('p','u','x')) "
7904 : "LEFT JOIN pg_catalog.pg_inherits inh "
7905 : "ON (inh.inhrelid = indexrelid) "
7906 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7907 : "AND i.indisready "
7908 : "ORDER BY i.indrelid, indexname",
7909 : tbloids->data);
7910 : }
7911 : else
7912 : {
7913 : /*
7914 : * the test on indisready is necessary in 9.2, and harmless in
7915 : * earlier/later versions
7916 : */
7917 0 : appendPQExpBuffer(query,
7918 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7919 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7920 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7921 : "LEFT JOIN pg_catalog.pg_constraint c "
7922 : "ON (i.indrelid = c.conrelid AND "
7923 : "i.indexrelid = c.conindid AND "
7924 : "c.contype IN ('p','u','x')) "
7925 : "WHERE i.indisvalid AND i.indisready "
7926 : "ORDER BY i.indrelid, indexname",
7927 : tbloids->data);
7928 : }
7929 :
7930 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7931 :
7932 364 : ntups = PQntuples(res);
7933 :
7934 364 : i_tableoid = PQfnumber(res, "tableoid");
7935 364 : i_oid = PQfnumber(res, "oid");
7936 364 : i_indrelid = PQfnumber(res, "indrelid");
7937 364 : i_indexname = PQfnumber(res, "indexname");
7938 364 : i_relpages = PQfnumber(res, "relpages");
7939 364 : i_reltuples = PQfnumber(res, "reltuples");
7940 364 : i_relallvisible = PQfnumber(res, "relallvisible");
7941 364 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7942 364 : i_parentidx = PQfnumber(res, "parentidx");
7943 364 : i_indexdef = PQfnumber(res, "indexdef");
7944 364 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7945 364 : i_indnatts = PQfnumber(res, "indnatts");
7946 364 : i_indkey = PQfnumber(res, "indkey");
7947 364 : i_indisclustered = PQfnumber(res, "indisclustered");
7948 364 : i_indisreplident = PQfnumber(res, "indisreplident");
7949 364 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7950 364 : i_contype = PQfnumber(res, "contype");
7951 364 : i_conname = PQfnumber(res, "conname");
7952 364 : i_condeferrable = PQfnumber(res, "condeferrable");
7953 364 : i_condeferred = PQfnumber(res, "condeferred");
7954 364 : i_conperiod = PQfnumber(res, "conperiod");
7955 364 : i_contableoid = PQfnumber(res, "contableoid");
7956 364 : i_conoid = PQfnumber(res, "conoid");
7957 364 : i_condef = PQfnumber(res, "condef");
7958 364 : i_indattnames = PQfnumber(res, "indattnames");
7959 364 : i_tablespace = PQfnumber(res, "tablespace");
7960 364 : i_indreloptions = PQfnumber(res, "indreloptions");
7961 364 : i_indstatcols = PQfnumber(res, "indstatcols");
7962 364 : i_indstatvals = PQfnumber(res, "indstatvals");
7963 :
7964 364 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7965 :
7966 : /*
7967 : * Outer loop iterates once per table, not once per row. Incrementing of
7968 : * j is handled by the inner loop.
7969 : */
7970 364 : curtblindx = -1;
7971 4390 : for (int j = 0; j < ntups;)
7972 : {
7973 4026 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7974 4026 : TableInfo *tbinfo = NULL;
7975 4026 : char **indAttNames = NULL;
7976 4026 : int nindAttNames = 0;
7977 : int numinds;
7978 :
7979 : /* Count rows for this table */
7980 5280 : for (numinds = 1; numinds < ntups - j; numinds++)
7981 5118 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7982 3864 : break;
7983 :
7984 : /*
7985 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7986 : * order.
7987 : */
7988 47834 : while (++curtblindx < numTables)
7989 : {
7990 47834 : tbinfo = &tblinfo[curtblindx];
7991 47834 : if (tbinfo->dobj.catId.oid == indrelid)
7992 4026 : break;
7993 : }
7994 4026 : if (curtblindx >= numTables)
7995 0 : pg_fatal("unrecognized table OID %u", indrelid);
7996 : /* cross-check that we only got requested tables */
7997 4026 : if (!tbinfo->hasindex ||
7998 4026 : !tbinfo->interesting)
7999 0 : pg_fatal("unexpected index data for table \"%s\"",
8000 : tbinfo->dobj.name);
8001 :
8002 : /* Save data for this table */
8003 4026 : tbinfo->indexes = indxinfo + j;
8004 4026 : tbinfo->numIndexes = numinds;
8005 :
8006 9306 : for (int c = 0; c < numinds; c++, j++)
8007 : {
8008 : char contype;
8009 : char indexkind;
8010 : RelStatsInfo *relstats;
8011 5280 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8012 5280 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8013 5280 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8014 :
8015 5280 : indxinfo[j].dobj.objType = DO_INDEX;
8016 5280 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8017 5280 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8018 5280 : AssignDumpId(&indxinfo[j].dobj);
8019 5280 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8020 5280 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8021 5280 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8022 5280 : indxinfo[j].indextable = tbinfo;
8023 5280 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8024 5280 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8025 5280 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8026 5280 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8027 5280 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8028 5280 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8029 5280 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8030 5280 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8031 5280 : parseOidArray(PQgetvalue(res, j, i_indkey),
8032 5280 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8033 5280 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8034 5280 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8035 5280 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8036 5280 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8037 5280 : indxinfo[j].partattaches = (SimplePtrList)
8038 : {
8039 : NULL, NULL
8040 : };
8041 :
8042 5280 : if (indxinfo[j].parentidx == 0)
8043 4104 : indexkind = RELKIND_INDEX;
8044 : else
8045 1176 : indexkind = RELKIND_PARTITIONED_INDEX;
8046 :
8047 5280 : if (!PQgetisnull(res, j, i_indattnames))
8048 : {
8049 304 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8050 : &indAttNames, &nindAttNames))
8051 0 : pg_fatal("could not parse %s array", "indattnames");
8052 : }
8053 :
8054 5280 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8055 : PQgetvalue(res, j, i_reltuples),
8056 : relallvisible, relallfrozen, indexkind,
8057 : indAttNames, nindAttNames);
8058 :
8059 5280 : contype = *(PQgetvalue(res, j, i_contype));
8060 5280 : if (contype == 'p' || contype == 'u' || contype == 'x')
8061 3074 : {
8062 : /*
8063 : * If we found a constraint matching the index, create an
8064 : * entry for it.
8065 : */
8066 : ConstraintInfo *constrinfo;
8067 :
8068 3074 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8069 3074 : constrinfo->dobj.objType = DO_CONSTRAINT;
8070 3074 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8071 3074 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8072 3074 : AssignDumpId(&constrinfo->dobj);
8073 3074 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8074 3074 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8075 3074 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8076 3074 : constrinfo->contable = tbinfo;
8077 3074 : constrinfo->condomain = NULL;
8078 3074 : constrinfo->contype = contype;
8079 3074 : if (contype == 'x')
8080 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8081 : else
8082 3054 : constrinfo->condef = NULL;
8083 3074 : constrinfo->confrelid = InvalidOid;
8084 3074 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8085 3074 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8086 3074 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8087 3074 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8088 3074 : constrinfo->conislocal = true;
8089 3074 : constrinfo->separate = true;
8090 :
8091 3074 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8092 3074 : if (relstats != NULL)
8093 1124 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8094 : }
8095 : else
8096 : {
8097 : /* Plain secondary index */
8098 2206 : indxinfo[j].indexconstraint = 0;
8099 : }
8100 : }
8101 : }
8102 :
8103 364 : PQclear(res);
8104 :
8105 364 : destroyPQExpBuffer(query);
8106 364 : destroyPQExpBuffer(tbloids);
8107 364 : }
8108 :
8109 : /*
8110 : * getExtendedStatistics
8111 : * get information about extended-statistics objects.
8112 : *
8113 : * Note: extended statistics data is not returned directly to the caller, but
8114 : * it does get entered into the DumpableObject tables.
8115 : */
8116 : void
8117 364 : getExtendedStatistics(Archive *fout)
8118 : {
8119 : PQExpBuffer query;
8120 : PGresult *res;
8121 : StatsExtInfo *statsextinfo;
8122 : int ntups;
8123 : int i_tableoid;
8124 : int i_oid;
8125 : int i_stxname;
8126 : int i_stxnamespace;
8127 : int i_stxowner;
8128 : int i_stxrelid;
8129 : int i_stattarget;
8130 : int i;
8131 :
8132 : /* Extended statistics were new in v10 */
8133 364 : if (fout->remoteVersion < 100000)
8134 0 : return;
8135 :
8136 364 : query = createPQExpBuffer();
8137 :
8138 364 : if (fout->remoteVersion < 130000)
8139 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8140 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8141 : "FROM pg_catalog.pg_statistic_ext");
8142 : else
8143 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8144 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8145 : "FROM pg_catalog.pg_statistic_ext");
8146 :
8147 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8148 :
8149 364 : ntups = PQntuples(res);
8150 :
8151 364 : i_tableoid = PQfnumber(res, "tableoid");
8152 364 : i_oid = PQfnumber(res, "oid");
8153 364 : i_stxname = PQfnumber(res, "stxname");
8154 364 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8155 364 : i_stxowner = PQfnumber(res, "stxowner");
8156 364 : i_stxrelid = PQfnumber(res, "stxrelid");
8157 364 : i_stattarget = PQfnumber(res, "stxstattarget");
8158 :
8159 364 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8160 :
8161 708 : for (i = 0; i < ntups; i++)
8162 : {
8163 344 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8164 344 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8165 344 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8166 344 : AssignDumpId(&statsextinfo[i].dobj);
8167 344 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8168 688 : statsextinfo[i].dobj.namespace =
8169 344 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8170 344 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8171 688 : statsextinfo[i].stattable =
8172 344 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8173 344 : if (PQgetisnull(res, i, i_stattarget))
8174 248 : statsextinfo[i].stattarget = -1;
8175 : else
8176 96 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8177 :
8178 : /* Decide whether we want to dump it */
8179 344 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8180 : }
8181 :
8182 364 : PQclear(res);
8183 364 : destroyPQExpBuffer(query);
8184 : }
8185 :
8186 : /*
8187 : * getConstraints
8188 : *
8189 : * Get info about constraints on dumpable tables.
8190 : *
8191 : * Currently handles foreign keys only.
8192 : * Unique and primary key constraints are handled with indexes,
8193 : * while check constraints are processed in getTableAttrs().
8194 : */
8195 : void
8196 364 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8197 : {
8198 364 : PQExpBuffer query = createPQExpBuffer();
8199 364 : PQExpBuffer tbloids = createPQExpBuffer();
8200 : PGresult *res;
8201 : int ntups;
8202 : int curtblindx;
8203 364 : TableInfo *tbinfo = NULL;
8204 : ConstraintInfo *constrinfo;
8205 : int i_contableoid,
8206 : i_conoid,
8207 : i_conrelid,
8208 : i_conname,
8209 : i_confrelid,
8210 : i_conindid,
8211 : i_condef;
8212 :
8213 : /*
8214 : * We want to perform just one query against pg_constraint. However, we
8215 : * mustn't try to select every row of the catalog and then sort it out on
8216 : * the client side, because some of the server-side functions we need
8217 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8218 : * build an array of the OIDs of tables we care about (and now have lock
8219 : * on!), and use a WHERE clause to constrain which rows are selected.
8220 : */
8221 364 : appendPQExpBufferChar(tbloids, '{');
8222 96818 : for (int i = 0; i < numTables; i++)
8223 : {
8224 96454 : TableInfo *tinfo = &tblinfo[i];
8225 :
8226 96454 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8227 82120 : continue;
8228 :
8229 : /* OK, we need info for this table */
8230 14334 : if (tbloids->len > 1) /* do we have more than the '{'? */
8231 14094 : appendPQExpBufferChar(tbloids, ',');
8232 14334 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8233 : }
8234 364 : appendPQExpBufferChar(tbloids, '}');
8235 :
8236 364 : appendPQExpBufferStr(query,
8237 : "SELECT c.tableoid, c.oid, "
8238 : "conrelid, conname, confrelid, ");
8239 364 : if (fout->remoteVersion >= 110000)
8240 364 : appendPQExpBufferStr(query, "conindid, ");
8241 : else
8242 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8243 364 : appendPQExpBuffer(query,
8244 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8245 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8246 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8247 : "WHERE contype = 'f' ",
8248 : tbloids->data);
8249 364 : if (fout->remoteVersion >= 110000)
8250 364 : appendPQExpBufferStr(query,
8251 : "AND conparentid = 0 ");
8252 364 : appendPQExpBufferStr(query,
8253 : "ORDER BY conrelid, conname");
8254 :
8255 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8256 :
8257 364 : ntups = PQntuples(res);
8258 :
8259 364 : i_contableoid = PQfnumber(res, "tableoid");
8260 364 : i_conoid = PQfnumber(res, "oid");
8261 364 : i_conrelid = PQfnumber(res, "conrelid");
8262 364 : i_conname = PQfnumber(res, "conname");
8263 364 : i_confrelid = PQfnumber(res, "confrelid");
8264 364 : i_conindid = PQfnumber(res, "conindid");
8265 364 : i_condef = PQfnumber(res, "condef");
8266 :
8267 364 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8268 :
8269 364 : curtblindx = -1;
8270 718 : for (int j = 0; j < ntups; j++)
8271 : {
8272 354 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8273 : TableInfo *reftable;
8274 :
8275 : /*
8276 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8277 : * order.
8278 : */
8279 354 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8280 : {
8281 28482 : while (++curtblindx < numTables)
8282 : {
8283 28482 : tbinfo = &tblinfo[curtblindx];
8284 28482 : if (tbinfo->dobj.catId.oid == conrelid)
8285 334 : break;
8286 : }
8287 334 : if (curtblindx >= numTables)
8288 0 : pg_fatal("unrecognized table OID %u", conrelid);
8289 : }
8290 :
8291 354 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8292 354 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8293 354 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8294 354 : AssignDumpId(&constrinfo[j].dobj);
8295 354 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8296 354 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8297 354 : constrinfo[j].contable = tbinfo;
8298 354 : constrinfo[j].condomain = NULL;
8299 354 : constrinfo[j].contype = 'f';
8300 354 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8301 354 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8302 354 : constrinfo[j].conindex = 0;
8303 354 : constrinfo[j].condeferrable = false;
8304 354 : constrinfo[j].condeferred = false;
8305 354 : constrinfo[j].conislocal = true;
8306 354 : constrinfo[j].separate = true;
8307 :
8308 : /*
8309 : * Restoring an FK that points to a partitioned table requires that
8310 : * all partition indexes have been attached beforehand. Ensure that
8311 : * happens by making the constraint depend on each index partition
8312 : * attach object.
8313 : */
8314 354 : reftable = findTableByOid(constrinfo[j].confrelid);
8315 354 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8316 : {
8317 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8318 :
8319 40 : if (indexOid != InvalidOid)
8320 : {
8321 40 : for (int k = 0; k < reftable->numIndexes; k++)
8322 : {
8323 : IndxInfo *refidx;
8324 :
8325 : /* not our index? */
8326 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8327 0 : continue;
8328 :
8329 40 : refidx = &reftable->indexes[k];
8330 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8331 40 : break;
8332 : }
8333 : }
8334 : }
8335 : }
8336 :
8337 364 : PQclear(res);
8338 :
8339 364 : destroyPQExpBuffer(query);
8340 364 : destroyPQExpBuffer(tbloids);
8341 364 : }
8342 :
8343 : /*
8344 : * addConstrChildIdxDeps
8345 : *
8346 : * Recursive subroutine for getConstraints
8347 : *
8348 : * Given an object representing a foreign key constraint and an index on the
8349 : * partitioned table it references, mark the constraint object as dependent
8350 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8351 : * drilling down to their partitions if any. This ensures that the FK is not
8352 : * restored until the index is fully marked valid.
8353 : */
8354 : static void
8355 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8356 : {
8357 : SimplePtrListCell *cell;
8358 :
8359 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8360 :
8361 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8362 : {
8363 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8364 :
8365 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8366 :
8367 220 : if (attach->partitionIdx->partattaches.head != NULL)
8368 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8369 : }
8370 90 : }
8371 :
8372 : /*
8373 : * getDomainConstraints
8374 : *
8375 : * Get info about constraints on a domain.
8376 : */
8377 : static void
8378 322 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8379 : {
8380 : ConstraintInfo *constrinfo;
8381 322 : PQExpBuffer query = createPQExpBuffer();
8382 : PGresult *res;
8383 : int i_tableoid,
8384 : i_oid,
8385 : i_conname,
8386 : i_consrc,
8387 : i_convalidated,
8388 : i_contype;
8389 : int ntups;
8390 :
8391 322 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8392 : {
8393 : /*
8394 : * Set up query for constraint-specific details. For servers 17 and
8395 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8396 : * just the latter.
8397 : */
8398 92 : appendPQExpBuffer(query,
8399 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8400 : "SELECT tableoid, oid, conname, "
8401 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8402 : "convalidated, contype "
8403 : "FROM pg_catalog.pg_constraint "
8404 : "WHERE contypid = $1 AND contype IN (%s) "
8405 : "ORDER BY conname",
8406 92 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8407 :
8408 92 : ExecuteSqlStatement(fout, query->data);
8409 :
8410 92 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8411 : }
8412 :
8413 322 : printfPQExpBuffer(query,
8414 : "EXECUTE getDomainConstraints('%u')",
8415 : tyinfo->dobj.catId.oid);
8416 :
8417 322 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8418 :
8419 322 : ntups = PQntuples(res);
8420 :
8421 322 : i_tableoid = PQfnumber(res, "tableoid");
8422 322 : i_oid = PQfnumber(res, "oid");
8423 322 : i_conname = PQfnumber(res, "conname");
8424 322 : i_consrc = PQfnumber(res, "consrc");
8425 322 : i_convalidated = PQfnumber(res, "convalidated");
8426 322 : i_contype = PQfnumber(res, "contype");
8427 :
8428 322 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8429 322 : tyinfo->domChecks = constrinfo;
8430 :
8431 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8432 666 : for (int i = 0, j = 0; i < ntups; i++)
8433 : {
8434 344 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8435 344 : char contype = (PQgetvalue(res, i, i_contype))[0];
8436 : ConstraintInfo *constraint;
8437 :
8438 344 : if (contype == CONSTRAINT_CHECK)
8439 : {
8440 232 : constraint = &constrinfo[j++];
8441 232 : tyinfo->nDomChecks++;
8442 : }
8443 : else
8444 : {
8445 : Assert(contype == CONSTRAINT_NOTNULL);
8446 : Assert(tyinfo->notnull == NULL);
8447 : /* use last item in array for the not-null constraint */
8448 112 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8449 112 : constraint = tyinfo->notnull;
8450 : }
8451 :
8452 344 : constraint->dobj.objType = DO_CONSTRAINT;
8453 344 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8454 344 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8455 344 : AssignDumpId(&(constraint->dobj));
8456 344 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8457 344 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8458 344 : constraint->contable = NULL;
8459 344 : constraint->condomain = tyinfo;
8460 344 : constraint->contype = contype;
8461 344 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8462 344 : constraint->confrelid = InvalidOid;
8463 344 : constraint->conindex = 0;
8464 344 : constraint->condeferrable = false;
8465 344 : constraint->condeferred = false;
8466 344 : constraint->conislocal = true;
8467 :
8468 344 : constraint->separate = !validated;
8469 :
8470 : /*
8471 : * Make the domain depend on the constraint, ensuring it won't be
8472 : * output till any constraint dependencies are OK. If the constraint
8473 : * has not been validated, it's going to be dumped after the domain
8474 : * anyway, so this doesn't matter.
8475 : */
8476 344 : if (validated)
8477 334 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8478 : }
8479 :
8480 322 : PQclear(res);
8481 :
8482 322 : destroyPQExpBuffer(query);
8483 322 : }
8484 :
8485 : /*
8486 : * getRules
8487 : * get basic information about every rule in the system
8488 : */
8489 : void
8490 364 : getRules(Archive *fout)
8491 : {
8492 : PGresult *res;
8493 : int ntups;
8494 : int i;
8495 364 : PQExpBuffer query = createPQExpBuffer();
8496 : RuleInfo *ruleinfo;
8497 : int i_tableoid;
8498 : int i_oid;
8499 : int i_rulename;
8500 : int i_ruletable;
8501 : int i_ev_type;
8502 : int i_is_instead;
8503 : int i_ev_enabled;
8504 :
8505 364 : appendPQExpBufferStr(query, "SELECT "
8506 : "tableoid, oid, rulename, "
8507 : "ev_class AS ruletable, ev_type, is_instead, "
8508 : "ev_enabled "
8509 : "FROM pg_rewrite "
8510 : "ORDER BY oid");
8511 :
8512 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8513 :
8514 364 : ntups = PQntuples(res);
8515 :
8516 364 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8517 :
8518 364 : i_tableoid = PQfnumber(res, "tableoid");
8519 364 : i_oid = PQfnumber(res, "oid");
8520 364 : i_rulename = PQfnumber(res, "rulename");
8521 364 : i_ruletable = PQfnumber(res, "ruletable");
8522 364 : i_ev_type = PQfnumber(res, "ev_type");
8523 364 : i_is_instead = PQfnumber(res, "is_instead");
8524 364 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8525 :
8526 56664 : for (i = 0; i < ntups; i++)
8527 : {
8528 : Oid ruletableoid;
8529 :
8530 56300 : ruleinfo[i].dobj.objType = DO_RULE;
8531 56300 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8532 56300 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8533 56300 : AssignDumpId(&ruleinfo[i].dobj);
8534 56300 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8535 56300 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8536 56300 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8537 56300 : if (ruleinfo[i].ruletable == NULL)
8538 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8539 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8540 56300 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8541 56300 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8542 56300 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8543 56300 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8544 56300 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8545 56300 : if (ruleinfo[i].ruletable)
8546 : {
8547 : /*
8548 : * If the table is a view or materialized view, force its ON
8549 : * SELECT rule to be sorted before the view itself --- this
8550 : * ensures that any dependencies for the rule affect the table's
8551 : * positioning. Other rules are forced to appear after their
8552 : * table.
8553 : */
8554 56300 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8555 1534 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8556 55838 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8557 : {
8558 55026 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8559 55026 : ruleinfo[i].dobj.dumpId);
8560 : /* We'll merge the rule into CREATE VIEW, if possible */
8561 55026 : ruleinfo[i].separate = false;
8562 : }
8563 : else
8564 : {
8565 1274 : addObjectDependency(&ruleinfo[i].dobj,
8566 1274 : ruleinfo[i].ruletable->dobj.dumpId);
8567 1274 : ruleinfo[i].separate = true;
8568 : }
8569 : }
8570 : else
8571 0 : ruleinfo[i].separate = true;
8572 : }
8573 :
8574 364 : PQclear(res);
8575 :
8576 364 : destroyPQExpBuffer(query);
8577 364 : }
8578 :
8579 : /*
8580 : * getTriggers
8581 : * get information about every trigger on a dumpable table
8582 : *
8583 : * Note: trigger data is not returned directly to the caller, but it
8584 : * does get entered into the DumpableObject tables.
8585 : */
8586 : void
8587 364 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8588 : {
8589 364 : PQExpBuffer query = createPQExpBuffer();
8590 364 : PQExpBuffer tbloids = createPQExpBuffer();
8591 : PGresult *res;
8592 : int ntups;
8593 : int curtblindx;
8594 : TriggerInfo *tginfo;
8595 : int i_tableoid,
8596 : i_oid,
8597 : i_tgrelid,
8598 : i_tgname,
8599 : i_tgenabled,
8600 : i_tgispartition,
8601 : i_tgdef;
8602 :
8603 : /*
8604 : * We want to perform just one query against pg_trigger. However, we
8605 : * mustn't try to select every row of the catalog and then sort it out on
8606 : * the client side, because some of the server-side functions we need
8607 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8608 : * build an array of the OIDs of tables we care about (and now have lock
8609 : * on!), and use a WHERE clause to constrain which rows are selected.
8610 : */
8611 364 : appendPQExpBufferChar(tbloids, '{');
8612 96818 : for (int i = 0; i < numTables; i++)
8613 : {
8614 96454 : TableInfo *tbinfo = &tblinfo[i];
8615 :
8616 96454 : if (!tbinfo->hastriggers ||
8617 2282 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8618 94696 : continue;
8619 :
8620 : /* OK, we need info for this table */
8621 1758 : if (tbloids->len > 1) /* do we have more than the '{'? */
8622 1650 : appendPQExpBufferChar(tbloids, ',');
8623 1758 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8624 : }
8625 364 : appendPQExpBufferChar(tbloids, '}');
8626 :
8627 364 : if (fout->remoteVersion >= 150000)
8628 : {
8629 : /*
8630 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8631 : * result in non-forward-compatible dumps of WHEN clauses due to
8632 : * under-parenthesization.
8633 : *
8634 : * NB: We need to see partition triggers in case the tgenabled flag
8635 : * has been changed from the parent.
8636 : */
8637 364 : appendPQExpBuffer(query,
8638 : "SELECT t.tgrelid, t.tgname, "
8639 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8640 : "t.tgenabled, t.tableoid, t.oid, "
8641 : "t.tgparentid <> 0 AS tgispartition\n"
8642 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8643 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8644 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8645 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8646 : "OR t.tgenabled != u.tgenabled) "
8647 : "ORDER BY t.tgrelid, t.tgname",
8648 : tbloids->data);
8649 : }
8650 0 : else if (fout->remoteVersion >= 130000)
8651 : {
8652 : /*
8653 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8654 : * result in non-forward-compatible dumps of WHEN clauses due to
8655 : * under-parenthesization.
8656 : *
8657 : * NB: We need to see tgisinternal triggers in partitions, in case the
8658 : * tgenabled flag has been changed from the parent.
8659 : */
8660 0 : appendPQExpBuffer(query,
8661 : "SELECT t.tgrelid, t.tgname, "
8662 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8663 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8664 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8665 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8666 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8667 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8668 : "ORDER BY t.tgrelid, t.tgname",
8669 : tbloids->data);
8670 : }
8671 0 : else if (fout->remoteVersion >= 110000)
8672 : {
8673 : /*
8674 : * NB: We need to see tgisinternal triggers in partitions, in case the
8675 : * tgenabled flag has been changed from the parent. No tgparentid in
8676 : * version 11-12, so we have to match them via pg_depend.
8677 : *
8678 : * See above about pretty=true in pg_get_triggerdef.
8679 : */
8680 0 : appendPQExpBuffer(query,
8681 : "SELECT t.tgrelid, t.tgname, "
8682 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8683 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8684 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8685 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8686 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8687 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8688 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8689 : " d.objid = t.oid "
8690 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8691 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8692 : "ORDER BY t.tgrelid, t.tgname",
8693 : tbloids->data);
8694 : }
8695 : else
8696 : {
8697 : /* See above about pretty=true in pg_get_triggerdef */
8698 0 : appendPQExpBuffer(query,
8699 : "SELECT t.tgrelid, t.tgname, "
8700 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8701 : "t.tgenabled, false as tgispartition, "
8702 : "t.tableoid, t.oid "
8703 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8704 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8705 : "WHERE NOT tgisinternal "
8706 : "ORDER BY t.tgrelid, t.tgname",
8707 : tbloids->data);
8708 : }
8709 :
8710 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8711 :
8712 364 : ntups = PQntuples(res);
8713 :
8714 364 : i_tableoid = PQfnumber(res, "tableoid");
8715 364 : i_oid = PQfnumber(res, "oid");
8716 364 : i_tgrelid = PQfnumber(res, "tgrelid");
8717 364 : i_tgname = PQfnumber(res, "tgname");
8718 364 : i_tgenabled = PQfnumber(res, "tgenabled");
8719 364 : i_tgispartition = PQfnumber(res, "tgispartition");
8720 364 : i_tgdef = PQfnumber(res, "tgdef");
8721 :
8722 364 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8723 :
8724 : /*
8725 : * Outer loop iterates once per table, not once per row. Incrementing of
8726 : * j is handled by the inner loop.
8727 : */
8728 364 : curtblindx = -1;
8729 1006 : for (int j = 0; j < ntups;)
8730 : {
8731 642 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8732 642 : TableInfo *tbinfo = NULL;
8733 : int numtrigs;
8734 :
8735 : /* Count rows for this table */
8736 1076 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8737 968 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8738 534 : break;
8739 :
8740 : /*
8741 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8742 : * order.
8743 : */
8744 33686 : while (++curtblindx < numTables)
8745 : {
8746 33686 : tbinfo = &tblinfo[curtblindx];
8747 33686 : if (tbinfo->dobj.catId.oid == tgrelid)
8748 642 : break;
8749 : }
8750 642 : if (curtblindx >= numTables)
8751 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8752 :
8753 : /* Save data for this table */
8754 642 : tbinfo->triggers = tginfo + j;
8755 642 : tbinfo->numTriggers = numtrigs;
8756 :
8757 1718 : for (int c = 0; c < numtrigs; c++, j++)
8758 : {
8759 1076 : tginfo[j].dobj.objType = DO_TRIGGER;
8760 1076 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8761 1076 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8762 1076 : AssignDumpId(&tginfo[j].dobj);
8763 1076 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8764 1076 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8765 1076 : tginfo[j].tgtable = tbinfo;
8766 1076 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8767 1076 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8768 1076 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8769 : }
8770 : }
8771 :
8772 364 : PQclear(res);
8773 :
8774 364 : destroyPQExpBuffer(query);
8775 364 : destroyPQExpBuffer(tbloids);
8776 364 : }
8777 :
8778 : /*
8779 : * getEventTriggers
8780 : * get information about event triggers
8781 : */
8782 : void
8783 364 : getEventTriggers(Archive *fout)
8784 : {
8785 : int i;
8786 : PQExpBuffer query;
8787 : PGresult *res;
8788 : EventTriggerInfo *evtinfo;
8789 : int i_tableoid,
8790 : i_oid,
8791 : i_evtname,
8792 : i_evtevent,
8793 : i_evtowner,
8794 : i_evttags,
8795 : i_evtfname,
8796 : i_evtenabled;
8797 : int ntups;
8798 :
8799 : /* Before 9.3, there are no event triggers */
8800 364 : if (fout->remoteVersion < 90300)
8801 0 : return;
8802 :
8803 364 : query = createPQExpBuffer();
8804 :
8805 364 : appendPQExpBufferStr(query,
8806 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8807 : "evtevent, evtowner, "
8808 : "array_to_string(array("
8809 : "select quote_literal(x) "
8810 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8811 : "e.evtfoid::regproc as evtfname "
8812 : "FROM pg_event_trigger e "
8813 : "ORDER BY e.oid");
8814 :
8815 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8816 :
8817 364 : ntups = PQntuples(res);
8818 :
8819 364 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8820 :
8821 364 : i_tableoid = PQfnumber(res, "tableoid");
8822 364 : i_oid = PQfnumber(res, "oid");
8823 364 : i_evtname = PQfnumber(res, "evtname");
8824 364 : i_evtevent = PQfnumber(res, "evtevent");
8825 364 : i_evtowner = PQfnumber(res, "evtowner");
8826 364 : i_evttags = PQfnumber(res, "evttags");
8827 364 : i_evtfname = PQfnumber(res, "evtfname");
8828 364 : i_evtenabled = PQfnumber(res, "evtenabled");
8829 :
8830 474 : for (i = 0; i < ntups; i++)
8831 : {
8832 110 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8833 110 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8834 110 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8835 110 : AssignDumpId(&evtinfo[i].dobj);
8836 110 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8837 110 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8838 110 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8839 110 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8840 110 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8841 110 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8842 110 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8843 :
8844 : /* Decide whether we want to dump it */
8845 110 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8846 : }
8847 :
8848 364 : PQclear(res);
8849 :
8850 364 : destroyPQExpBuffer(query);
8851 : }
8852 :
8853 : /*
8854 : * getProcLangs
8855 : * get basic information about every procedural language in the system
8856 : *
8857 : * NB: this must run after getFuncs() because we assume we can do
8858 : * findFuncByOid().
8859 : */
8860 : void
8861 364 : getProcLangs(Archive *fout)
8862 : {
8863 : PGresult *res;
8864 : int ntups;
8865 : int i;
8866 364 : PQExpBuffer query = createPQExpBuffer();
8867 : ProcLangInfo *planginfo;
8868 : int i_tableoid;
8869 : int i_oid;
8870 : int i_lanname;
8871 : int i_lanpltrusted;
8872 : int i_lanplcallfoid;
8873 : int i_laninline;
8874 : int i_lanvalidator;
8875 : int i_lanacl;
8876 : int i_acldefault;
8877 : int i_lanowner;
8878 :
8879 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8880 : "lanname, lanpltrusted, lanplcallfoid, "
8881 : "laninline, lanvalidator, "
8882 : "lanacl, "
8883 : "acldefault('l', lanowner) AS acldefault, "
8884 : "lanowner "
8885 : "FROM pg_language "
8886 : "WHERE lanispl "
8887 : "ORDER BY oid");
8888 :
8889 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8890 :
8891 364 : ntups = PQntuples(res);
8892 :
8893 364 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8894 :
8895 364 : i_tableoid = PQfnumber(res, "tableoid");
8896 364 : i_oid = PQfnumber(res, "oid");
8897 364 : i_lanname = PQfnumber(res, "lanname");
8898 364 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8899 364 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8900 364 : i_laninline = PQfnumber(res, "laninline");
8901 364 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8902 364 : i_lanacl = PQfnumber(res, "lanacl");
8903 364 : i_acldefault = PQfnumber(res, "acldefault");
8904 364 : i_lanowner = PQfnumber(res, "lanowner");
8905 :
8906 824 : for (i = 0; i < ntups; i++)
8907 : {
8908 460 : planginfo[i].dobj.objType = DO_PROCLANG;
8909 460 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8910 460 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8911 460 : AssignDumpId(&planginfo[i].dobj);
8912 :
8913 460 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8914 460 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8915 460 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8916 460 : planginfo[i].dacl.privtype = 0;
8917 460 : planginfo[i].dacl.initprivs = NULL;
8918 460 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8919 460 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8920 460 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8921 460 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8922 460 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8923 :
8924 : /* Decide whether we want to dump it */
8925 460 : selectDumpableProcLang(&(planginfo[i]), fout);
8926 :
8927 : /* Mark whether language has an ACL */
8928 460 : if (!PQgetisnull(res, i, i_lanacl))
8929 96 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8930 : }
8931 :
8932 364 : PQclear(res);
8933 :
8934 364 : destroyPQExpBuffer(query);
8935 364 : }
8936 :
8937 : /*
8938 : * getCasts
8939 : * get basic information about most casts in the system
8940 : *
8941 : * Skip casts from a range to its multirange, since we'll create those
8942 : * automatically.
8943 : */
8944 : void
8945 364 : getCasts(Archive *fout)
8946 : {
8947 : PGresult *res;
8948 : int ntups;
8949 : int i;
8950 364 : PQExpBuffer query = createPQExpBuffer();
8951 : CastInfo *castinfo;
8952 : int i_tableoid;
8953 : int i_oid;
8954 : int i_castsource;
8955 : int i_casttarget;
8956 : int i_castfunc;
8957 : int i_castcontext;
8958 : int i_castmethod;
8959 :
8960 364 : if (fout->remoteVersion >= 140000)
8961 : {
8962 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8963 : "castsource, casttarget, castfunc, castcontext, "
8964 : "castmethod "
8965 : "FROM pg_cast c "
8966 : "WHERE NOT EXISTS ( "
8967 : "SELECT 1 FROM pg_range r "
8968 : "WHERE c.castsource = r.rngtypid "
8969 : "AND c.casttarget = r.rngmultitypid "
8970 : ") "
8971 : "ORDER BY 3,4");
8972 : }
8973 : else
8974 : {
8975 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8976 : "castsource, casttarget, castfunc, castcontext, "
8977 : "castmethod "
8978 : "FROM pg_cast ORDER BY 3,4");
8979 : }
8980 :
8981 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8982 :
8983 364 : ntups = PQntuples(res);
8984 :
8985 364 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8986 :
8987 364 : i_tableoid = PQfnumber(res, "tableoid");
8988 364 : i_oid = PQfnumber(res, "oid");
8989 364 : i_castsource = PQfnumber(res, "castsource");
8990 364 : i_casttarget = PQfnumber(res, "casttarget");
8991 364 : i_castfunc = PQfnumber(res, "castfunc");
8992 364 : i_castcontext = PQfnumber(res, "castcontext");
8993 364 : i_castmethod = PQfnumber(res, "castmethod");
8994 :
8995 86448 : for (i = 0; i < ntups; i++)
8996 : {
8997 : PQExpBufferData namebuf;
8998 : TypeInfo *sTypeInfo;
8999 : TypeInfo *tTypeInfo;
9000 :
9001 86084 : castinfo[i].dobj.objType = DO_CAST;
9002 86084 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9003 86084 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9004 86084 : AssignDumpId(&castinfo[i].dobj);
9005 86084 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9006 86084 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9007 86084 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9008 86084 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9009 86084 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9010 :
9011 : /*
9012 : * Try to name cast as concatenation of typnames. This is only used
9013 : * for purposes of sorting. If we fail to find either type, the name
9014 : * will be an empty string.
9015 : */
9016 86084 : initPQExpBuffer(&namebuf);
9017 86084 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9018 86084 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9019 86084 : if (sTypeInfo && tTypeInfo)
9020 86084 : appendPQExpBuffer(&namebuf, "%s %s",
9021 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9022 86084 : castinfo[i].dobj.name = namebuf.data;
9023 :
9024 : /* Decide whether we want to dump it */
9025 86084 : selectDumpableCast(&(castinfo[i]), fout);
9026 : }
9027 :
9028 364 : PQclear(res);
9029 :
9030 364 : destroyPQExpBuffer(query);
9031 364 : }
9032 :
9033 : static char *
9034 188 : get_language_name(Archive *fout, Oid langid)
9035 : {
9036 : PQExpBuffer query;
9037 : PGresult *res;
9038 : char *lanname;
9039 :
9040 188 : query = createPQExpBuffer();
9041 188 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9042 188 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9043 188 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9044 188 : destroyPQExpBuffer(query);
9045 188 : PQclear(res);
9046 :
9047 188 : return lanname;
9048 : }
9049 :
9050 : /*
9051 : * getTransforms
9052 : * get basic information about every transform in the system
9053 : */
9054 : void
9055 364 : getTransforms(Archive *fout)
9056 : {
9057 : PGresult *res;
9058 : int ntups;
9059 : int i;
9060 : PQExpBuffer query;
9061 : TransformInfo *transforminfo;
9062 : int i_tableoid;
9063 : int i_oid;
9064 : int i_trftype;
9065 : int i_trflang;
9066 : int i_trffromsql;
9067 : int i_trftosql;
9068 :
9069 : /* Transforms didn't exist pre-9.5 */
9070 364 : if (fout->remoteVersion < 90500)
9071 0 : return;
9072 :
9073 364 : query = createPQExpBuffer();
9074 :
9075 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9076 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9077 : "FROM pg_transform "
9078 : "ORDER BY 3,4");
9079 :
9080 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9081 :
9082 364 : ntups = PQntuples(res);
9083 :
9084 364 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9085 :
9086 364 : i_tableoid = PQfnumber(res, "tableoid");
9087 364 : i_oid = PQfnumber(res, "oid");
9088 364 : i_trftype = PQfnumber(res, "trftype");
9089 364 : i_trflang = PQfnumber(res, "trflang");
9090 364 : i_trffromsql = PQfnumber(res, "trffromsql");
9091 364 : i_trftosql = PQfnumber(res, "trftosql");
9092 :
9093 474 : for (i = 0; i < ntups; i++)
9094 : {
9095 : PQExpBufferData namebuf;
9096 : TypeInfo *typeInfo;
9097 : char *lanname;
9098 :
9099 110 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9100 110 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9101 110 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9102 110 : AssignDumpId(&transforminfo[i].dobj);
9103 110 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9104 110 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9105 110 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9106 110 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9107 :
9108 : /*
9109 : * Try to name transform as concatenation of type and language name.
9110 : * This is only used for purposes of sorting. If we fail to find
9111 : * either, the name will be an empty string.
9112 : */
9113 110 : initPQExpBuffer(&namebuf);
9114 110 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9115 110 : lanname = get_language_name(fout, transforminfo[i].trflang);
9116 110 : if (typeInfo && lanname)
9117 110 : appendPQExpBuffer(&namebuf, "%s %s",
9118 : typeInfo->dobj.name, lanname);
9119 110 : transforminfo[i].dobj.name = namebuf.data;
9120 110 : free(lanname);
9121 :
9122 : /* Decide whether we want to dump it */
9123 110 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9124 : }
9125 :
9126 364 : PQclear(res);
9127 :
9128 364 : destroyPQExpBuffer(query);
9129 : }
9130 :
9131 : /*
9132 : * getTableAttrs -
9133 : * for each interesting table, read info about its attributes
9134 : * (names, types, default values, CHECK constraints, etc)
9135 : *
9136 : * modifies tblinfo
9137 : */
9138 : void
9139 364 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9140 : {
9141 364 : DumpOptions *dopt = fout->dopt;
9142 364 : PQExpBuffer q = createPQExpBuffer();
9143 364 : PQExpBuffer tbloids = createPQExpBuffer();
9144 364 : PQExpBuffer checkoids = createPQExpBuffer();
9145 364 : PQExpBuffer invalidnotnulloids = NULL;
9146 : PGresult *res;
9147 : int ntups;
9148 : int curtblindx;
9149 : int i_attrelid;
9150 : int i_attnum;
9151 : int i_attname;
9152 : int i_atttypname;
9153 : int i_attstattarget;
9154 : int i_attstorage;
9155 : int i_typstorage;
9156 : int i_attidentity;
9157 : int i_attgenerated;
9158 : int i_attisdropped;
9159 : int i_attlen;
9160 : int i_attalign;
9161 : int i_attislocal;
9162 : int i_notnull_name;
9163 : int i_notnull_comment;
9164 : int i_notnull_noinherit;
9165 : int i_notnull_islocal;
9166 : int i_notnull_invalidoid;
9167 : int i_attoptions;
9168 : int i_attcollation;
9169 : int i_attcompression;
9170 : int i_attfdwoptions;
9171 : int i_attmissingval;
9172 : int i_atthasdef;
9173 :
9174 : /*
9175 : * We want to perform just one query against pg_attribute, and then just
9176 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9177 : * (for CHECK constraints and for NOT NULL constraints). However, we
9178 : * mustn't try to select every row of those catalogs and then sort it out
9179 : * on the client side, because some of the server-side functions we need
9180 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9181 : * build an array of the OIDs of tables we care about (and now have lock
9182 : * on!), and use a WHERE clause to constrain which rows are selected.
9183 : */
9184 364 : appendPQExpBufferChar(tbloids, '{');
9185 364 : appendPQExpBufferChar(checkoids, '{');
9186 96818 : for (int i = 0; i < numTables; i++)
9187 : {
9188 96454 : TableInfo *tbinfo = &tblinfo[i];
9189 :
9190 : /* Don't bother to collect info for sequences */
9191 96454 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9192 1294 : continue;
9193 :
9194 : /*
9195 : * Don't bother with uninteresting tables, either. For binary
9196 : * upgrades, this is bypassed for pg_largeobject_metadata and
9197 : * pg_shdepend so that the columns names are collected for the
9198 : * corresponding COPY commands. Restoring the data for those catalogs
9199 : * is faster than restoring the equivalent set of large object
9200 : * commands. We can only do this for upgrades from v12 and newer; in
9201 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9202 : * the OID column is hidden and won't be dumped.
9203 : */
9204 95160 : if (!tbinfo->interesting &&
9205 81550 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9206 15444 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9207 15372 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9208 81406 : continue;
9209 :
9210 : /* OK, we need info for this table */
9211 13754 : if (tbloids->len > 1) /* do we have more than the '{'? */
9212 13478 : appendPQExpBufferChar(tbloids, ',');
9213 13754 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9214 :
9215 13754 : if (tbinfo->ncheck > 0)
9216 : {
9217 : /* Also make a list of the ones with check constraints */
9218 1124 : if (checkoids->len > 1) /* do we have more than the '{'? */
9219 980 : appendPQExpBufferChar(checkoids, ',');
9220 1124 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9221 : }
9222 : }
9223 364 : appendPQExpBufferChar(tbloids, '}');
9224 364 : appendPQExpBufferChar(checkoids, '}');
9225 :
9226 : /*
9227 : * Find all the user attributes and their types.
9228 : *
9229 : * Since we only want to dump COLLATE clauses for attributes whose
9230 : * collation is different from their type's default, we use a CASE here to
9231 : * suppress uninteresting attcollations cheaply.
9232 : */
9233 364 : appendPQExpBufferStr(q,
9234 : "SELECT\n"
9235 : "a.attrelid,\n"
9236 : "a.attnum,\n"
9237 : "a.attname,\n"
9238 : "a.attstattarget,\n"
9239 : "a.attstorage,\n"
9240 : "t.typstorage,\n"
9241 : "a.atthasdef,\n"
9242 : "a.attisdropped,\n"
9243 : "a.attlen,\n"
9244 : "a.attalign,\n"
9245 : "a.attislocal,\n"
9246 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9247 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9248 : "CASE WHEN a.attcollation <> t.typcollation "
9249 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9250 : "pg_catalog.array_to_string(ARRAY("
9251 : "SELECT pg_catalog.quote_ident(option_name) || "
9252 : "' ' || pg_catalog.quote_literal(option_value) "
9253 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9254 : "ORDER BY option_name"
9255 : "), E',\n ') AS attfdwoptions,\n");
9256 :
9257 : /*
9258 : * Find out any NOT NULL markings for each column. In 18 and up we read
9259 : * pg_constraint to obtain the constraint name, and for valid constraints
9260 : * also pg_description to obtain its comment. notnull_noinherit is set
9261 : * according to the NO INHERIT property. For versions prior to 18, we
9262 : * store an empty string as the name when a constraint is marked as
9263 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9264 : * without a name); also, such cases are never NO INHERIT.
9265 : *
9266 : * For invalid constraints, we need to store their OIDs for processing
9267 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9268 : * is invalid, and NULL otherwise. Their comments are handled not here
9269 : * but by collectComments, because they're their own dumpable object.
9270 : *
9271 : * We track in notnull_islocal whether the constraint was defined directly
9272 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9273 : * might modify this later; that routine is also in charge of determining
9274 : * the correct inhcount.
9275 : */
9276 364 : if (fout->remoteVersion >= 180000)
9277 364 : appendPQExpBufferStr(q,
9278 : "co.conname AS notnull_name,\n"
9279 : "CASE WHEN co.convalidated THEN pt.description"
9280 : " ELSE NULL END AS notnull_comment,\n"
9281 : "CASE WHEN NOT co.convalidated THEN co.oid "
9282 : "ELSE NULL END AS notnull_invalidoid,\n"
9283 : "co.connoinherit AS notnull_noinherit,\n"
9284 : "co.conislocal AS notnull_islocal,\n");
9285 : else
9286 0 : appendPQExpBufferStr(q,
9287 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9288 : "NULL AS notnull_comment,\n"
9289 : "NULL AS notnull_invalidoid,\n"
9290 : "false AS notnull_noinherit,\n"
9291 : "a.attislocal AS notnull_islocal,\n");
9292 :
9293 364 : if (fout->remoteVersion >= 140000)
9294 364 : appendPQExpBufferStr(q,
9295 : "a.attcompression AS attcompression,\n");
9296 : else
9297 0 : appendPQExpBufferStr(q,
9298 : "'' AS attcompression,\n");
9299 :
9300 364 : if (fout->remoteVersion >= 100000)
9301 364 : appendPQExpBufferStr(q,
9302 : "a.attidentity,\n");
9303 : else
9304 0 : appendPQExpBufferStr(q,
9305 : "'' AS attidentity,\n");
9306 :
9307 364 : if (fout->remoteVersion >= 110000)
9308 364 : appendPQExpBufferStr(q,
9309 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9310 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9311 : else
9312 0 : appendPQExpBufferStr(q,
9313 : "NULL AS attmissingval,\n");
9314 :
9315 364 : if (fout->remoteVersion >= 120000)
9316 364 : appendPQExpBufferStr(q,
9317 : "a.attgenerated\n");
9318 : else
9319 0 : appendPQExpBufferStr(q,
9320 : "'' AS attgenerated\n");
9321 :
9322 : /* need left join to pg_type to not fail on dropped columns ... */
9323 364 : appendPQExpBuffer(q,
9324 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9325 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9326 : "LEFT JOIN pg_catalog.pg_type t "
9327 : "ON (a.atttypid = t.oid)\n",
9328 : tbloids->data);
9329 :
9330 : /*
9331 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9332 : * entries and pg_description to get their comments.
9333 : */
9334 364 : if (fout->remoteVersion >= 180000)
9335 364 : appendPQExpBufferStr(q,
9336 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9337 : "(a.attrelid = co.conrelid\n"
9338 : " AND co.contype = 'n' AND "
9339 : "co.conkey = array[a.attnum])\n"
9340 : " LEFT JOIN pg_catalog.pg_description pt ON "
9341 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9342 :
9343 364 : appendPQExpBufferStr(q,
9344 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9345 : "ORDER BY a.attrelid, a.attnum");
9346 :
9347 364 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9348 :
9349 364 : ntups = PQntuples(res);
9350 :
9351 364 : i_attrelid = PQfnumber(res, "attrelid");
9352 364 : i_attnum = PQfnumber(res, "attnum");
9353 364 : i_attname = PQfnumber(res, "attname");
9354 364 : i_atttypname = PQfnumber(res, "atttypname");
9355 364 : i_attstattarget = PQfnumber(res, "attstattarget");
9356 364 : i_attstorage = PQfnumber(res, "attstorage");
9357 364 : i_typstorage = PQfnumber(res, "typstorage");
9358 364 : i_attidentity = PQfnumber(res, "attidentity");
9359 364 : i_attgenerated = PQfnumber(res, "attgenerated");
9360 364 : i_attisdropped = PQfnumber(res, "attisdropped");
9361 364 : i_attlen = PQfnumber(res, "attlen");
9362 364 : i_attalign = PQfnumber(res, "attalign");
9363 364 : i_attislocal = PQfnumber(res, "attislocal");
9364 364 : i_notnull_name = PQfnumber(res, "notnull_name");
9365 364 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9366 364 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9367 364 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9368 364 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9369 364 : i_attoptions = PQfnumber(res, "attoptions");
9370 364 : i_attcollation = PQfnumber(res, "attcollation");
9371 364 : i_attcompression = PQfnumber(res, "attcompression");
9372 364 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9373 364 : i_attmissingval = PQfnumber(res, "attmissingval");
9374 364 : i_atthasdef = PQfnumber(res, "atthasdef");
9375 :
9376 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9377 364 : resetPQExpBuffer(tbloids);
9378 364 : appendPQExpBufferChar(tbloids, '{');
9379 :
9380 : /*
9381 : * Outer loop iterates once per table, not once per row. Incrementing of
9382 : * r is handled by the inner loop.
9383 : */
9384 364 : curtblindx = -1;
9385 13824 : for (int r = 0; r < ntups;)
9386 : {
9387 13460 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9388 13460 : TableInfo *tbinfo = NULL;
9389 : int numatts;
9390 : bool hasdefaults;
9391 :
9392 : /* Count rows for this table */
9393 49884 : for (numatts = 1; numatts < ntups - r; numatts++)
9394 49614 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9395 13190 : break;
9396 :
9397 : /*
9398 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9399 : * order.
9400 : */
9401 66300 : while (++curtblindx < numTables)
9402 : {
9403 66300 : tbinfo = &tblinfo[curtblindx];
9404 66300 : if (tbinfo->dobj.catId.oid == attrelid)
9405 13460 : break;
9406 : }
9407 13460 : if (curtblindx >= numTables)
9408 0 : pg_fatal("unrecognized table OID %u", attrelid);
9409 : /* cross-check that we only got requested tables */
9410 13460 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9411 13460 : (!tbinfo->interesting &&
9412 144 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9413 144 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9414 72 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9415 0 : pg_fatal("unexpected column data for table \"%s\"",
9416 : tbinfo->dobj.name);
9417 :
9418 : /* Save data for this table */
9419 13460 : tbinfo->numatts = numatts;
9420 13460 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9421 13460 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9422 13460 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9423 13460 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9424 13460 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9425 13460 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9426 13460 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9427 13460 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9428 13460 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9429 13460 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9430 13460 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9431 13460 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9432 13460 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9433 13460 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9434 13460 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9435 13460 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9436 13460 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9437 13460 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9438 13460 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9439 13460 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9440 13460 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9441 13460 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9442 13460 : hasdefaults = false;
9443 :
9444 63344 : for (int j = 0; j < numatts; j++, r++)
9445 : {
9446 49884 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9447 0 : pg_fatal("invalid column numbering in table \"%s\"",
9448 : tbinfo->dobj.name);
9449 49884 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9450 49884 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9451 49884 : if (PQgetisnull(res, r, i_attstattarget))
9452 49798 : tbinfo->attstattarget[j] = -1;
9453 : else
9454 86 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9455 49884 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9456 49884 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9457 49884 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9458 49884 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9459 49884 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9460 49884 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9461 49884 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9462 49884 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9463 49884 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9464 :
9465 : /* Handle not-null constraint name and flags */
9466 49884 : determineNotNullFlags(fout, res, r,
9467 : tbinfo, j,
9468 : i_notnull_name,
9469 : i_notnull_comment,
9470 : i_notnull_invalidoid,
9471 : i_notnull_noinherit,
9472 : i_notnull_islocal,
9473 : &invalidnotnulloids);
9474 :
9475 49884 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9476 49884 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9477 49884 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9478 49884 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9479 49884 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9480 49884 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9481 49884 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9482 49884 : tbinfo->attrdefs[j] = NULL; /* fix below */
9483 49884 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9484 2632 : hasdefaults = true;
9485 : }
9486 :
9487 13460 : if (hasdefaults)
9488 : {
9489 : /* Collect OIDs of interesting tables that have defaults */
9490 1970 : if (tbloids->len > 1) /* do we have more than the '{'? */
9491 1828 : appendPQExpBufferChar(tbloids, ',');
9492 1970 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9493 : }
9494 : }
9495 :
9496 : /* If invalidnotnulloids has any data, finalize it */
9497 364 : if (invalidnotnulloids != NULL)
9498 92 : appendPQExpBufferChar(invalidnotnulloids, '}');
9499 :
9500 364 : PQclear(res);
9501 :
9502 : /*
9503 : * Now get info about column defaults. This is skipped for a data-only
9504 : * dump, as it is only needed for table schemas.
9505 : */
9506 364 : if (dopt->dumpSchema && tbloids->len > 1)
9507 : {
9508 : AttrDefInfo *attrdefs;
9509 : int numDefaults;
9510 126 : TableInfo *tbinfo = NULL;
9511 :
9512 126 : pg_log_info("finding table default expressions");
9513 :
9514 126 : appendPQExpBufferChar(tbloids, '}');
9515 :
9516 126 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9517 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9518 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9519 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9520 : "ORDER BY a.adrelid, a.adnum",
9521 : tbloids->data);
9522 :
9523 126 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9524 :
9525 126 : numDefaults = PQntuples(res);
9526 126 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9527 :
9528 126 : curtblindx = -1;
9529 2562 : for (int j = 0; j < numDefaults; j++)
9530 : {
9531 2436 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9532 2436 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9533 2436 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9534 2436 : int adnum = atoi(PQgetvalue(res, j, 3));
9535 2436 : char *adsrc = PQgetvalue(res, j, 4);
9536 :
9537 : /*
9538 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9539 : * OID order.
9540 : */
9541 2436 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9542 : {
9543 38982 : while (++curtblindx < numTables)
9544 : {
9545 38982 : tbinfo = &tblinfo[curtblindx];
9546 38982 : if (tbinfo->dobj.catId.oid == adrelid)
9547 1834 : break;
9548 : }
9549 1834 : if (curtblindx >= numTables)
9550 0 : pg_fatal("unrecognized table OID %u", adrelid);
9551 : }
9552 :
9553 2436 : if (adnum <= 0 || adnum > tbinfo->numatts)
9554 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9555 : adnum, tbinfo->dobj.name);
9556 :
9557 : /*
9558 : * dropped columns shouldn't have defaults, but just in case,
9559 : * ignore 'em
9560 : */
9561 2436 : if (tbinfo->attisdropped[adnum - 1])
9562 0 : continue;
9563 :
9564 2436 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9565 2436 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9566 2436 : attrdefs[j].dobj.catId.oid = adoid;
9567 2436 : AssignDumpId(&attrdefs[j].dobj);
9568 2436 : attrdefs[j].adtable = tbinfo;
9569 2436 : attrdefs[j].adnum = adnum;
9570 2436 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9571 :
9572 2436 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9573 2436 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9574 :
9575 2436 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9576 :
9577 : /*
9578 : * Figure out whether the default/generation expression should be
9579 : * dumped as part of the main CREATE TABLE (or similar) command or
9580 : * as a separate ALTER TABLE (or similar) command. The preference
9581 : * is to put it into the CREATE command, but in some cases that's
9582 : * not possible.
9583 : */
9584 2436 : if (tbinfo->attgenerated[adnum - 1])
9585 : {
9586 : /*
9587 : * Column generation expressions cannot be dumped separately,
9588 : * because there is no syntax for it. By setting separate to
9589 : * false here we prevent the "default" from being processed as
9590 : * its own dumpable object. Later, flagInhAttrs() will mark
9591 : * it as not to be dumped at all, if possible (that is, if it
9592 : * can be inherited from a parent).
9593 : */
9594 1360 : attrdefs[j].separate = false;
9595 : }
9596 1076 : else if (tbinfo->relkind == RELKIND_VIEW)
9597 : {
9598 : /*
9599 : * Defaults on a VIEW must always be dumped as separate ALTER
9600 : * TABLE commands.
9601 : */
9602 70 : attrdefs[j].separate = true;
9603 : }
9604 1006 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9605 : {
9606 : /* column will be suppressed, print default separately */
9607 8 : attrdefs[j].separate = true;
9608 : }
9609 : else
9610 : {
9611 998 : attrdefs[j].separate = false;
9612 : }
9613 :
9614 2436 : if (!attrdefs[j].separate)
9615 : {
9616 : /*
9617 : * Mark the default as needing to appear before the table, so
9618 : * that any dependencies it has must be emitted before the
9619 : * CREATE TABLE. If this is not possible, we'll change to
9620 : * "separate" mode while sorting dependencies.
9621 : */
9622 2358 : addObjectDependency(&tbinfo->dobj,
9623 2358 : attrdefs[j].dobj.dumpId);
9624 : }
9625 :
9626 2436 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9627 : }
9628 :
9629 126 : PQclear(res);
9630 : }
9631 :
9632 : /*
9633 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9634 : * data-only dump, as it is only needed for table schemas.
9635 : */
9636 364 : if (dopt->dumpSchema && invalidnotnulloids)
9637 : {
9638 : ConstraintInfo *constrs;
9639 : int numConstrs;
9640 : int i_tableoid;
9641 : int i_oid;
9642 : int i_conrelid;
9643 : int i_conname;
9644 : int i_consrc;
9645 : int i_conislocal;
9646 :
9647 80 : pg_log_info("finding invalid not-null constraints");
9648 :
9649 80 : resetPQExpBuffer(q);
9650 80 : appendPQExpBuffer(q,
9651 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9652 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9653 : "conislocal, convalidated "
9654 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9655 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9656 : "ORDER BY c.conrelid, c.conname",
9657 80 : invalidnotnulloids->data);
9658 :
9659 80 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9660 :
9661 80 : numConstrs = PQntuples(res);
9662 80 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9663 :
9664 80 : i_tableoid = PQfnumber(res, "tableoid");
9665 80 : i_oid = PQfnumber(res, "oid");
9666 80 : i_conrelid = PQfnumber(res, "conrelid");
9667 80 : i_conname = PQfnumber(res, "conname");
9668 80 : i_consrc = PQfnumber(res, "consrc");
9669 80 : i_conislocal = PQfnumber(res, "conislocal");
9670 :
9671 : /* As above, this loop iterates once per table, not once per row */
9672 80 : curtblindx = -1;
9673 220 : for (int j = 0; j < numConstrs;)
9674 : {
9675 140 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9676 140 : TableInfo *tbinfo = NULL;
9677 : int numcons;
9678 :
9679 : /* Count rows for this table */
9680 140 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9681 60 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9682 60 : break;
9683 :
9684 : /*
9685 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9686 : * OID order.
9687 : */
9688 26890 : while (++curtblindx < numTables)
9689 : {
9690 26890 : tbinfo = &tblinfo[curtblindx];
9691 26890 : if (tbinfo->dobj.catId.oid == conrelid)
9692 140 : break;
9693 : }
9694 140 : if (curtblindx >= numTables)
9695 0 : pg_fatal("unrecognized table OID %u", conrelid);
9696 :
9697 280 : for (int c = 0; c < numcons; c++, j++)
9698 : {
9699 140 : constrs[j].dobj.objType = DO_CONSTRAINT;
9700 140 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9701 140 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9702 140 : AssignDumpId(&constrs[j].dobj);
9703 140 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9704 140 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9705 140 : constrs[j].contable = tbinfo;
9706 140 : constrs[j].condomain = NULL;
9707 140 : constrs[j].contype = 'n';
9708 140 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9709 140 : constrs[j].confrelid = InvalidOid;
9710 140 : constrs[j].conindex = 0;
9711 140 : constrs[j].condeferrable = false;
9712 140 : constrs[j].condeferred = false;
9713 140 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9714 :
9715 : /*
9716 : * All invalid not-null constraints must be dumped separately,
9717 : * because CREATE TABLE would not create them as invalid, and
9718 : * also because they must be created after potentially
9719 : * violating data has been loaded.
9720 : */
9721 140 : constrs[j].separate = true;
9722 :
9723 140 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9724 : }
9725 : }
9726 80 : PQclear(res);
9727 : }
9728 :
9729 : /*
9730 : * Get info about table CHECK constraints. This is skipped for a
9731 : * data-only dump, as it is only needed for table schemas.
9732 : */
9733 364 : if (dopt->dumpSchema && checkoids->len > 2)
9734 : {
9735 : ConstraintInfo *constrs;
9736 : int numConstrs;
9737 : int i_tableoid;
9738 : int i_oid;
9739 : int i_conrelid;
9740 : int i_conname;
9741 : int i_consrc;
9742 : int i_conislocal;
9743 : int i_convalidated;
9744 :
9745 128 : pg_log_info("finding table check constraints");
9746 :
9747 128 : resetPQExpBuffer(q);
9748 128 : appendPQExpBuffer(q,
9749 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9750 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9751 : "conislocal, convalidated "
9752 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9753 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9754 : "WHERE contype = 'c' "
9755 : "ORDER BY c.conrelid, c.conname",
9756 : checkoids->data);
9757 :
9758 128 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9759 :
9760 128 : numConstrs = PQntuples(res);
9761 128 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9762 :
9763 128 : i_tableoid = PQfnumber(res, "tableoid");
9764 128 : i_oid = PQfnumber(res, "oid");
9765 128 : i_conrelid = PQfnumber(res, "conrelid");
9766 128 : i_conname = PQfnumber(res, "conname");
9767 128 : i_consrc = PQfnumber(res, "consrc");
9768 128 : i_conislocal = PQfnumber(res, "conislocal");
9769 128 : i_convalidated = PQfnumber(res, "convalidated");
9770 :
9771 : /* As above, this loop iterates once per table, not once per row */
9772 128 : curtblindx = -1;
9773 1150 : for (int j = 0; j < numConstrs;)
9774 : {
9775 1022 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9776 1022 : TableInfo *tbinfo = NULL;
9777 : int numcons;
9778 :
9779 : /* Count rows for this table */
9780 1304 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9781 1176 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9782 894 : break;
9783 :
9784 : /*
9785 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9786 : * OID order.
9787 : */
9788 37638 : while (++curtblindx < numTables)
9789 : {
9790 37638 : tbinfo = &tblinfo[curtblindx];
9791 37638 : if (tbinfo->dobj.catId.oid == conrelid)
9792 1022 : break;
9793 : }
9794 1022 : if (curtblindx >= numTables)
9795 0 : pg_fatal("unrecognized table OID %u", conrelid);
9796 :
9797 1022 : if (numcons != tbinfo->ncheck)
9798 : {
9799 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9800 : "expected %d check constraints on table \"%s\" but found %d",
9801 : tbinfo->ncheck),
9802 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9803 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9804 0 : exit_nicely(1);
9805 : }
9806 :
9807 1022 : tbinfo->checkexprs = constrs + j;
9808 :
9809 2326 : for (int c = 0; c < numcons; c++, j++)
9810 : {
9811 1304 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9812 :
9813 1304 : constrs[j].dobj.objType = DO_CONSTRAINT;
9814 1304 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9815 1304 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9816 1304 : AssignDumpId(&constrs[j].dobj);
9817 1304 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9818 1304 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9819 1304 : constrs[j].contable = tbinfo;
9820 1304 : constrs[j].condomain = NULL;
9821 1304 : constrs[j].contype = 'c';
9822 1304 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9823 1304 : constrs[j].confrelid = InvalidOid;
9824 1304 : constrs[j].conindex = 0;
9825 1304 : constrs[j].condeferrable = false;
9826 1304 : constrs[j].condeferred = false;
9827 1304 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9828 :
9829 : /*
9830 : * An unvalidated constraint needs to be dumped separately, so
9831 : * that potentially-violating existing data is loaded before
9832 : * the constraint.
9833 : */
9834 1304 : constrs[j].separate = !validated;
9835 :
9836 1304 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9837 :
9838 : /*
9839 : * Mark the constraint as needing to appear before the table
9840 : * --- this is so that any other dependencies of the
9841 : * constraint will be emitted before we try to create the
9842 : * table. If the constraint is to be dumped separately, it
9843 : * will be dumped after data is loaded anyway, so don't do it.
9844 : * (There's an automatic dependency in the opposite direction
9845 : * anyway, so don't need to add one manually here.)
9846 : */
9847 1304 : if (!constrs[j].separate)
9848 1174 : addObjectDependency(&tbinfo->dobj,
9849 1174 : constrs[j].dobj.dumpId);
9850 :
9851 : /*
9852 : * We will detect later whether the constraint must be split
9853 : * out from the table definition.
9854 : */
9855 : }
9856 : }
9857 :
9858 128 : PQclear(res);
9859 : }
9860 :
9861 364 : destroyPQExpBuffer(q);
9862 364 : destroyPQExpBuffer(tbloids);
9863 364 : destroyPQExpBuffer(checkoids);
9864 364 : }
9865 :
9866 : /*
9867 : * Based on the getTableAttrs query's row corresponding to one column, set
9868 : * the name and flags to handle a not-null constraint for that column in
9869 : * the tbinfo struct.
9870 : *
9871 : * Result row 'r' is for tbinfo's attribute 'j'.
9872 : *
9873 : * There are four possibilities:
9874 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9875 : * (the constraint name) remains NULL.
9876 : * 2) The column has a constraint with no name (this is the case when
9877 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9878 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9879 : * 3) The column has an invalid not-null constraint. This must be treated
9880 : * as a separate object (because it must be created after the table data
9881 : * is loaded). So we add its OID to invalidnotnulloids for processing
9882 : * elsewhere and do nothing further with it here. We distinguish this
9883 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9884 : * value, which is the constraint OID. Valid constraints have a null OID.
9885 : * 4) The column has a constraint with a known name; in that case
9886 : * notnull_constrs carries that name and dumpTableSchema will print
9887 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9888 : * (table_column_not_null) and there's no comment on the constraint,
9889 : * there's no need to print that name in the dump, so notnull_constrs
9890 : * is set to the empty string and it behaves as case 2.
9891 : *
9892 : * In a child table that inherits from a parent already containing NOT NULL
9893 : * constraints and the columns in the child don't have their own NOT NULL
9894 : * declarations, we suppress printing constraints in the child: the
9895 : * constraints are acquired at the point where the child is attached to the
9896 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9897 : * set not here but in flagInhAttrs. That flag is also used when the
9898 : * constraint was validated in a child but all its parent have it as NOT
9899 : * VALID.
9900 : *
9901 : * Any of these constraints might have the NO INHERIT bit. If so we set
9902 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9903 : *
9904 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9905 : * to do the right thing in all but the trivial case. However, the downside
9906 : * of getting it wrong is simply that the name is printed rather than
9907 : * suppressed, so it's not a big deal.
9908 : *
9909 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9910 : * constraints are found, it is initialized and filled with the array of
9911 : * OIDs of such constraints, for later processing.
9912 : */
9913 : static void
9914 49884 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9915 : TableInfo *tbinfo, int j,
9916 : int i_notnull_name,
9917 : int i_notnull_comment,
9918 : int i_notnull_invalidoid,
9919 : int i_notnull_noinherit,
9920 : int i_notnull_islocal,
9921 : PQExpBuffer *invalidnotnulloids)
9922 : {
9923 49884 : DumpOptions *dopt = fout->dopt;
9924 :
9925 : /*
9926 : * If this not-null constraint is not valid, list its OID in
9927 : * invalidnotnulloids and do nothing further. It'll be processed
9928 : * elsewhere later.
9929 : *
9930 : * Because invalid not-null constraints are rare, we don't want to malloc
9931 : * invalidnotnulloids until we're sure we're going it need it, which
9932 : * happens here.
9933 : */
9934 49884 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
9935 : {
9936 152 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9937 :
9938 152 : if (*invalidnotnulloids == NULL)
9939 : {
9940 92 : *invalidnotnulloids = createPQExpBuffer();
9941 92 : appendPQExpBufferChar(*invalidnotnulloids, '{');
9942 92 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
9943 : }
9944 : else
9945 60 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
9946 :
9947 : /*
9948 : * Track when a parent constraint is invalid for the cases where a
9949 : * child constraint has been validated independenly.
9950 : */
9951 152 : tbinfo->notnull_invalid[j] = true;
9952 :
9953 : /* nothing else to do */
9954 152 : tbinfo->notnull_constrs[j] = NULL;
9955 152 : return;
9956 : }
9957 :
9958 : /*
9959 : * notnull_noinh is straight from the query result. notnull_islocal also,
9960 : * though flagInhAttrs may change that one later.
9961 : */
9962 49732 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9963 49732 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9964 49732 : tbinfo->notnull_invalid[j] = false;
9965 :
9966 : /*
9967 : * Determine a constraint name to use. If the column is not marked not-
9968 : * null, we set NULL which cues ... to do nothing. An empty string says
9969 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9970 : * use.
9971 : */
9972 49732 : if (fout->remoteVersion < 180000)
9973 : {
9974 : /*
9975 : * < 18 doesn't have not-null names, so an unnamed constraint is
9976 : * sufficient.
9977 : */
9978 0 : if (PQgetisnull(res, r, i_notnull_name))
9979 0 : tbinfo->notnull_constrs[j] = NULL;
9980 : else
9981 0 : tbinfo->notnull_constrs[j] = "";
9982 : }
9983 : else
9984 : {
9985 49732 : if (PQgetisnull(res, r, i_notnull_name))
9986 44378 : tbinfo->notnull_constrs[j] = NULL;
9987 : else
9988 : {
9989 : /*
9990 : * In binary upgrade of inheritance child tables, must have a
9991 : * constraint name that we can UPDATE later; same if there's a
9992 : * comment on the constraint.
9993 : */
9994 5354 : if ((dopt->binary_upgrade &&
9995 652 : !tbinfo->ispartition &&
9996 5850 : !tbinfo->notnull_islocal) ||
9997 5354 : !PQgetisnull(res, r, i_notnull_comment))
9998 : {
9999 102 : tbinfo->notnull_constrs[j] =
10000 102 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10001 : }
10002 : else
10003 : {
10004 : char *default_name;
10005 :
10006 : /* XXX should match ChooseConstraintName better */
10007 5252 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10008 5252 : tbinfo->attnames[j]);
10009 5252 : if (strcmp(default_name,
10010 5252 : PQgetvalue(res, r, i_notnull_name)) == 0)
10011 3452 : tbinfo->notnull_constrs[j] = "";
10012 : else
10013 : {
10014 1800 : tbinfo->notnull_constrs[j] =
10015 1800 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10016 : }
10017 5252 : free(default_name);
10018 : }
10019 : }
10020 : }
10021 : }
10022 :
10023 : /*
10024 : * Test whether a column should be printed as part of table's CREATE TABLE.
10025 : * Column number is zero-based.
10026 : *
10027 : * Normally this is always true, but it's false for dropped columns, as well
10028 : * as those that were inherited without any local definition. (If we print
10029 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10030 : * For partitions, it's always true, because we want the partitions to be
10031 : * created independently and ATTACH PARTITION used afterwards.
10032 : *
10033 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10034 : * attisdropped state later, so as to keep control of the physical column
10035 : * order.
10036 : *
10037 : * This function exists because there are scattered nonobvious places that
10038 : * must be kept in sync with this decision.
10039 : */
10040 : bool
10041 81182 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10042 : {
10043 81182 : if (dopt->binary_upgrade)
10044 12412 : return true;
10045 68770 : if (tbinfo->attisdropped[colno])
10046 1464 : return false;
10047 67306 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10048 : }
10049 :
10050 :
10051 : /*
10052 : * getTSParsers:
10053 : * get information about all text search parsers in the system catalogs
10054 : */
10055 : void
10056 364 : getTSParsers(Archive *fout)
10057 : {
10058 : PGresult *res;
10059 : int ntups;
10060 : int i;
10061 : PQExpBuffer query;
10062 : TSParserInfo *prsinfo;
10063 : int i_tableoid;
10064 : int i_oid;
10065 : int i_prsname;
10066 : int i_prsnamespace;
10067 : int i_prsstart;
10068 : int i_prstoken;
10069 : int i_prsend;
10070 : int i_prsheadline;
10071 : int i_prslextype;
10072 :
10073 364 : query = createPQExpBuffer();
10074 :
10075 : /*
10076 : * find all text search objects, including builtin ones; we filter out
10077 : * system-defined objects at dump-out time.
10078 : */
10079 :
10080 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10081 : "prsstart::oid, prstoken::oid, "
10082 : "prsend::oid, prsheadline::oid, prslextype::oid "
10083 : "FROM pg_ts_parser");
10084 :
10085 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10086 :
10087 364 : ntups = PQntuples(res);
10088 :
10089 364 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10090 :
10091 364 : i_tableoid = PQfnumber(res, "tableoid");
10092 364 : i_oid = PQfnumber(res, "oid");
10093 364 : i_prsname = PQfnumber(res, "prsname");
10094 364 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10095 364 : i_prsstart = PQfnumber(res, "prsstart");
10096 364 : i_prstoken = PQfnumber(res, "prstoken");
10097 364 : i_prsend = PQfnumber(res, "prsend");
10098 364 : i_prsheadline = PQfnumber(res, "prsheadline");
10099 364 : i_prslextype = PQfnumber(res, "prslextype");
10100 :
10101 824 : for (i = 0; i < ntups; i++)
10102 : {
10103 460 : prsinfo[i].dobj.objType = DO_TSPARSER;
10104 460 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10105 460 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10106 460 : AssignDumpId(&prsinfo[i].dobj);
10107 460 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10108 920 : prsinfo[i].dobj.namespace =
10109 460 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10110 460 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10111 460 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10112 460 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10113 460 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10114 460 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10115 :
10116 : /* Decide whether we want to dump it */
10117 460 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10118 : }
10119 :
10120 364 : PQclear(res);
10121 :
10122 364 : destroyPQExpBuffer(query);
10123 364 : }
10124 :
10125 : /*
10126 : * getTSDictionaries:
10127 : * get information about all text search dictionaries in the system catalogs
10128 : */
10129 : void
10130 364 : getTSDictionaries(Archive *fout)
10131 : {
10132 : PGresult *res;
10133 : int ntups;
10134 : int i;
10135 : PQExpBuffer query;
10136 : TSDictInfo *dictinfo;
10137 : int i_tableoid;
10138 : int i_oid;
10139 : int i_dictname;
10140 : int i_dictnamespace;
10141 : int i_dictowner;
10142 : int i_dicttemplate;
10143 : int i_dictinitoption;
10144 :
10145 364 : query = createPQExpBuffer();
10146 :
10147 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10148 : "dictnamespace, dictowner, "
10149 : "dicttemplate, dictinitoption "
10150 : "FROM pg_ts_dict");
10151 :
10152 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10153 :
10154 364 : ntups = PQntuples(res);
10155 :
10156 364 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10157 :
10158 364 : i_tableoid = PQfnumber(res, "tableoid");
10159 364 : i_oid = PQfnumber(res, "oid");
10160 364 : i_dictname = PQfnumber(res, "dictname");
10161 364 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10162 364 : i_dictowner = PQfnumber(res, "dictowner");
10163 364 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10164 364 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10165 :
10166 11506 : for (i = 0; i < ntups; i++)
10167 : {
10168 11142 : dictinfo[i].dobj.objType = DO_TSDICT;
10169 11142 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10170 11142 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10171 11142 : AssignDumpId(&dictinfo[i].dobj);
10172 11142 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10173 22284 : dictinfo[i].dobj.namespace =
10174 11142 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10175 11142 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10176 11142 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10177 11142 : if (PQgetisnull(res, i, i_dictinitoption))
10178 460 : dictinfo[i].dictinitoption = NULL;
10179 : else
10180 10682 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10181 :
10182 : /* Decide whether we want to dump it */
10183 11142 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10184 : }
10185 :
10186 364 : PQclear(res);
10187 :
10188 364 : destroyPQExpBuffer(query);
10189 364 : }
10190 :
10191 : /*
10192 : * getTSTemplates:
10193 : * get information about all text search templates in the system catalogs
10194 : */
10195 : void
10196 364 : getTSTemplates(Archive *fout)
10197 : {
10198 : PGresult *res;
10199 : int ntups;
10200 : int i;
10201 : PQExpBuffer query;
10202 : TSTemplateInfo *tmplinfo;
10203 : int i_tableoid;
10204 : int i_oid;
10205 : int i_tmplname;
10206 : int i_tmplnamespace;
10207 : int i_tmplinit;
10208 : int i_tmpllexize;
10209 :
10210 364 : query = createPQExpBuffer();
10211 :
10212 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10213 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10214 : "FROM pg_ts_template");
10215 :
10216 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10217 :
10218 364 : ntups = PQntuples(res);
10219 :
10220 364 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10221 :
10222 364 : i_tableoid = PQfnumber(res, "tableoid");
10223 364 : i_oid = PQfnumber(res, "oid");
10224 364 : i_tmplname = PQfnumber(res, "tmplname");
10225 364 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10226 364 : i_tmplinit = PQfnumber(res, "tmplinit");
10227 364 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10228 :
10229 2280 : for (i = 0; i < ntups; i++)
10230 : {
10231 1916 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10232 1916 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10233 1916 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10234 1916 : AssignDumpId(&tmplinfo[i].dobj);
10235 1916 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10236 3832 : tmplinfo[i].dobj.namespace =
10237 1916 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10238 1916 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10239 1916 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10240 :
10241 : /* Decide whether we want to dump it */
10242 1916 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10243 : }
10244 :
10245 364 : PQclear(res);
10246 :
10247 364 : destroyPQExpBuffer(query);
10248 364 : }
10249 :
10250 : /*
10251 : * getTSConfigurations:
10252 : * get information about all text search configurations
10253 : */
10254 : void
10255 364 : getTSConfigurations(Archive *fout)
10256 : {
10257 : PGresult *res;
10258 : int ntups;
10259 : int i;
10260 : PQExpBuffer query;
10261 : TSConfigInfo *cfginfo;
10262 : int i_tableoid;
10263 : int i_oid;
10264 : int i_cfgname;
10265 : int i_cfgnamespace;
10266 : int i_cfgowner;
10267 : int i_cfgparser;
10268 :
10269 364 : query = createPQExpBuffer();
10270 :
10271 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10272 : "cfgnamespace, cfgowner, cfgparser "
10273 : "FROM pg_ts_config");
10274 :
10275 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10276 :
10277 364 : ntups = PQntuples(res);
10278 :
10279 364 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10280 :
10281 364 : i_tableoid = PQfnumber(res, "tableoid");
10282 364 : i_oid = PQfnumber(res, "oid");
10283 364 : i_cfgname = PQfnumber(res, "cfgname");
10284 364 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10285 364 : i_cfgowner = PQfnumber(res, "cfgowner");
10286 364 : i_cfgparser = PQfnumber(res, "cfgparser");
10287 :
10288 11436 : for (i = 0; i < ntups; i++)
10289 : {
10290 11072 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10291 11072 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10292 11072 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10293 11072 : AssignDumpId(&cfginfo[i].dobj);
10294 11072 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10295 22144 : cfginfo[i].dobj.namespace =
10296 11072 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10297 11072 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10298 11072 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10299 :
10300 : /* Decide whether we want to dump it */
10301 11072 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10302 : }
10303 :
10304 364 : PQclear(res);
10305 :
10306 364 : destroyPQExpBuffer(query);
10307 364 : }
10308 :
10309 : /*
10310 : * getForeignDataWrappers:
10311 : * get information about all foreign-data wrappers in the system catalogs
10312 : */
10313 : void
10314 364 : getForeignDataWrappers(Archive *fout)
10315 : {
10316 : PGresult *res;
10317 : int ntups;
10318 : int i;
10319 : PQExpBuffer query;
10320 : FdwInfo *fdwinfo;
10321 : int i_tableoid;
10322 : int i_oid;
10323 : int i_fdwname;
10324 : int i_fdwowner;
10325 : int i_fdwhandler;
10326 : int i_fdwvalidator;
10327 : int i_fdwacl;
10328 : int i_acldefault;
10329 : int i_fdwoptions;
10330 :
10331 364 : query = createPQExpBuffer();
10332 :
10333 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10334 : "fdwowner, "
10335 : "fdwhandler::pg_catalog.regproc, "
10336 : "fdwvalidator::pg_catalog.regproc, "
10337 : "fdwacl, "
10338 : "acldefault('F', fdwowner) AS acldefault, "
10339 : "array_to_string(ARRAY("
10340 : "SELECT quote_ident(option_name) || ' ' || "
10341 : "quote_literal(option_value) "
10342 : "FROM pg_options_to_table(fdwoptions) "
10343 : "ORDER BY option_name"
10344 : "), E',\n ') AS fdwoptions "
10345 : "FROM pg_foreign_data_wrapper");
10346 :
10347 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10348 :
10349 364 : ntups = PQntuples(res);
10350 :
10351 364 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10352 :
10353 364 : i_tableoid = PQfnumber(res, "tableoid");
10354 364 : i_oid = PQfnumber(res, "oid");
10355 364 : i_fdwname = PQfnumber(res, "fdwname");
10356 364 : i_fdwowner = PQfnumber(res, "fdwowner");
10357 364 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10358 364 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10359 364 : i_fdwacl = PQfnumber(res, "fdwacl");
10360 364 : i_acldefault = PQfnumber(res, "acldefault");
10361 364 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10362 :
10363 512 : for (i = 0; i < ntups; i++)
10364 : {
10365 148 : fdwinfo[i].dobj.objType = DO_FDW;
10366 148 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10367 148 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10368 148 : AssignDumpId(&fdwinfo[i].dobj);
10369 148 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10370 148 : fdwinfo[i].dobj.namespace = NULL;
10371 148 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10372 148 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10373 148 : fdwinfo[i].dacl.privtype = 0;
10374 148 : fdwinfo[i].dacl.initprivs = NULL;
10375 148 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10376 148 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10377 148 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10378 148 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10379 :
10380 : /* Decide whether we want to dump it */
10381 148 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10382 :
10383 : /* Mark whether FDW has an ACL */
10384 148 : if (!PQgetisnull(res, i, i_fdwacl))
10385 96 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10386 : }
10387 :
10388 364 : PQclear(res);
10389 :
10390 364 : destroyPQExpBuffer(query);
10391 364 : }
10392 :
10393 : /*
10394 : * getForeignServers:
10395 : * get information about all foreign servers in the system catalogs
10396 : */
10397 : void
10398 364 : getForeignServers(Archive *fout)
10399 : {
10400 : PGresult *res;
10401 : int ntups;
10402 : int i;
10403 : PQExpBuffer query;
10404 : ForeignServerInfo *srvinfo;
10405 : int i_tableoid;
10406 : int i_oid;
10407 : int i_srvname;
10408 : int i_srvowner;
10409 : int i_srvfdw;
10410 : int i_srvtype;
10411 : int i_srvversion;
10412 : int i_srvacl;
10413 : int i_acldefault;
10414 : int i_srvoptions;
10415 :
10416 364 : query = createPQExpBuffer();
10417 :
10418 364 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10419 : "srvowner, "
10420 : "srvfdw, srvtype, srvversion, srvacl, "
10421 : "acldefault('S', srvowner) AS acldefault, "
10422 : "array_to_string(ARRAY("
10423 : "SELECT quote_ident(option_name) || ' ' || "
10424 : "quote_literal(option_value) "
10425 : "FROM pg_options_to_table(srvoptions) "
10426 : "ORDER BY option_name"
10427 : "), E',\n ') AS srvoptions "
10428 : "FROM pg_foreign_server");
10429 :
10430 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10431 :
10432 364 : ntups = PQntuples(res);
10433 :
10434 364 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10435 :
10436 364 : i_tableoid = PQfnumber(res, "tableoid");
10437 364 : i_oid = PQfnumber(res, "oid");
10438 364 : i_srvname = PQfnumber(res, "srvname");
10439 364 : i_srvowner = PQfnumber(res, "srvowner");
10440 364 : i_srvfdw = PQfnumber(res, "srvfdw");
10441 364 : i_srvtype = PQfnumber(res, "srvtype");
10442 364 : i_srvversion = PQfnumber(res, "srvversion");
10443 364 : i_srvacl = PQfnumber(res, "srvacl");
10444 364 : i_acldefault = PQfnumber(res, "acldefault");
10445 364 : i_srvoptions = PQfnumber(res, "srvoptions");
10446 :
10447 520 : for (i = 0; i < ntups; i++)
10448 : {
10449 156 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10450 156 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10451 156 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10452 156 : AssignDumpId(&srvinfo[i].dobj);
10453 156 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10454 156 : srvinfo[i].dobj.namespace = NULL;
10455 156 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10456 156 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10457 156 : srvinfo[i].dacl.privtype = 0;
10458 156 : srvinfo[i].dacl.initprivs = NULL;
10459 156 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10460 156 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10461 156 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10462 156 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10463 156 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10464 :
10465 : /* Decide whether we want to dump it */
10466 156 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10467 :
10468 : /* Servers have user mappings */
10469 156 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10470 :
10471 : /* Mark whether server has an ACL */
10472 156 : if (!PQgetisnull(res, i, i_srvacl))
10473 96 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10474 : }
10475 :
10476 364 : PQclear(res);
10477 :
10478 364 : destroyPQExpBuffer(query);
10479 364 : }
10480 :
10481 : /*
10482 : * getDefaultACLs:
10483 : * get information about all default ACL information in the system catalogs
10484 : */
10485 : void
10486 364 : getDefaultACLs(Archive *fout)
10487 : {
10488 364 : DumpOptions *dopt = fout->dopt;
10489 : DefaultACLInfo *daclinfo;
10490 : PQExpBuffer query;
10491 : PGresult *res;
10492 : int i_oid;
10493 : int i_tableoid;
10494 : int i_defaclrole;
10495 : int i_defaclnamespace;
10496 : int i_defaclobjtype;
10497 : int i_defaclacl;
10498 : int i_acldefault;
10499 : int i,
10500 : ntups;
10501 :
10502 364 : query = createPQExpBuffer();
10503 :
10504 : /*
10505 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10506 : * ACL for their object type. We should dump them as deltas from the
10507 : * default ACL, since that will be used as a starting point for
10508 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10509 : * non-global entries can only add privileges not revoke them. We must
10510 : * dump those as-is (i.e., as deltas from an empty ACL).
10511 : *
10512 : * We can use defaclobjtype as the object type for acldefault(), except
10513 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10514 : * 's'.
10515 : */
10516 364 : appendPQExpBufferStr(query,
10517 : "SELECT oid, tableoid, "
10518 : "defaclrole, "
10519 : "defaclnamespace, "
10520 : "defaclobjtype, "
10521 : "defaclacl, "
10522 : "CASE WHEN defaclnamespace = 0 THEN "
10523 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10524 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10525 : "defaclrole) ELSE '{}' END AS acldefault "
10526 : "FROM pg_default_acl");
10527 :
10528 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10529 :
10530 364 : ntups = PQntuples(res);
10531 :
10532 364 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10533 :
10534 364 : i_oid = PQfnumber(res, "oid");
10535 364 : i_tableoid = PQfnumber(res, "tableoid");
10536 364 : i_defaclrole = PQfnumber(res, "defaclrole");
10537 364 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10538 364 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10539 364 : i_defaclacl = PQfnumber(res, "defaclacl");
10540 364 : i_acldefault = PQfnumber(res, "acldefault");
10541 :
10542 776 : for (i = 0; i < ntups; i++)
10543 : {
10544 412 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10545 :
10546 412 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10547 412 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10548 412 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10549 412 : AssignDumpId(&daclinfo[i].dobj);
10550 : /* cheesy ... is it worth coming up with a better object name? */
10551 412 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10552 :
10553 412 : if (nspid != InvalidOid)
10554 192 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10555 : else
10556 220 : daclinfo[i].dobj.namespace = NULL;
10557 :
10558 412 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10559 412 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10560 412 : daclinfo[i].dacl.privtype = 0;
10561 412 : daclinfo[i].dacl.initprivs = NULL;
10562 412 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10563 412 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10564 :
10565 : /* Default ACLs are ACLs, of course */
10566 412 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10567 :
10568 : /* Decide whether we want to dump it */
10569 412 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10570 : }
10571 :
10572 364 : PQclear(res);
10573 :
10574 364 : destroyPQExpBuffer(query);
10575 364 : }
10576 :
10577 : /*
10578 : * getRoleName -- look up the name of a role, given its OID
10579 : *
10580 : * In current usage, we don't expect failures, so error out for a bad OID.
10581 : */
10582 : static const char *
10583 1153394 : getRoleName(const char *roleoid_str)
10584 : {
10585 1153394 : Oid roleoid = atooid(roleoid_str);
10586 :
10587 : /*
10588 : * Do binary search to find the appropriate item.
10589 : */
10590 1153394 : if (nrolenames > 0)
10591 : {
10592 1153394 : RoleNameItem *low = &rolenames[0];
10593 1153394 : RoleNameItem *high = &rolenames[nrolenames - 1];
10594 :
10595 4613392 : while (low <= high)
10596 : {
10597 4613392 : RoleNameItem *middle = low + (high - low) / 2;
10598 :
10599 4613392 : if (roleoid < middle->roleoid)
10600 3457810 : high = middle - 1;
10601 1155582 : else if (roleoid > middle->roleoid)
10602 2188 : low = middle + 1;
10603 : else
10604 1153394 : return middle->rolename; /* found a match */
10605 : }
10606 : }
10607 :
10608 0 : pg_fatal("role with OID %u does not exist", roleoid);
10609 : return NULL; /* keep compiler quiet */
10610 : }
10611 :
10612 : /*
10613 : * collectRoleNames --
10614 : *
10615 : * Construct a table of all known roles.
10616 : * The table is sorted by OID for speed in lookup.
10617 : */
10618 : static void
10619 366 : collectRoleNames(Archive *fout)
10620 : {
10621 : PGresult *res;
10622 : const char *query;
10623 : int i;
10624 :
10625 366 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10626 :
10627 366 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10628 :
10629 366 : nrolenames = PQntuples(res);
10630 :
10631 366 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10632 :
10633 7136 : for (i = 0; i < nrolenames; i++)
10634 : {
10635 6770 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10636 6770 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10637 : }
10638 :
10639 366 : PQclear(res);
10640 366 : }
10641 :
10642 : /*
10643 : * getAdditionalACLs
10644 : *
10645 : * We have now created all the DumpableObjects, and collected the ACL data
10646 : * that appears in the directly-associated catalog entries. However, there's
10647 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10648 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10649 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10650 : * Also, in versions having the pg_init_privs catalog, read that and load the
10651 : * information into the relevant DumpableObjects.
10652 : */
10653 : static void
10654 360 : getAdditionalACLs(Archive *fout)
10655 : {
10656 360 : PQExpBuffer query = createPQExpBuffer();
10657 : PGresult *res;
10658 : int ntups,
10659 : i;
10660 :
10661 : /* Check for per-column ACLs */
10662 360 : appendPQExpBufferStr(query,
10663 : "SELECT DISTINCT attrelid FROM pg_attribute "
10664 : "WHERE attacl IS NOT NULL");
10665 :
10666 360 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10667 :
10668 360 : ntups = PQntuples(res);
10669 1078 : for (i = 0; i < ntups; i++)
10670 : {
10671 718 : Oid relid = atooid(PQgetvalue(res, i, 0));
10672 : TableInfo *tblinfo;
10673 :
10674 718 : tblinfo = findTableByOid(relid);
10675 : /* OK to ignore tables we haven't got a DumpableObject for */
10676 718 : if (tblinfo)
10677 : {
10678 718 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10679 718 : tblinfo->hascolumnACLs = true;
10680 : }
10681 : }
10682 360 : PQclear(res);
10683 :
10684 : /* Fetch initial-privileges data */
10685 360 : if (fout->remoteVersion >= 90600)
10686 : {
10687 360 : printfPQExpBuffer(query,
10688 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10689 : "FROM pg_init_privs");
10690 :
10691 360 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10692 :
10693 360 : ntups = PQntuples(res);
10694 84496 : for (i = 0; i < ntups; i++)
10695 : {
10696 84136 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10697 84136 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10698 84136 : int objsubid = atoi(PQgetvalue(res, i, 2));
10699 84136 : char privtype = *(PQgetvalue(res, i, 3));
10700 84136 : char *initprivs = PQgetvalue(res, i, 4);
10701 : CatalogId objId;
10702 : DumpableObject *dobj;
10703 :
10704 84136 : objId.tableoid = classoid;
10705 84136 : objId.oid = objoid;
10706 84136 : dobj = findObjectByCatalogId(objId);
10707 : /* OK to ignore entries we haven't got a DumpableObject for */
10708 84136 : if (dobj)
10709 : {
10710 : /* Cope with sub-object initprivs */
10711 60110 : if (objsubid != 0)
10712 : {
10713 6528 : if (dobj->objType == DO_TABLE)
10714 : {
10715 : /* For a column initprivs, set the table's ACL flags */
10716 6528 : dobj->components |= DUMP_COMPONENT_ACL;
10717 6528 : ((TableInfo *) dobj)->hascolumnACLs = true;
10718 : }
10719 : else
10720 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10721 : classoid, objoid, objsubid);
10722 6880 : continue;
10723 : }
10724 :
10725 : /*
10726 : * We ignore any pg_init_privs.initprivs entry for the public
10727 : * schema, as explained in getNamespaces().
10728 : */
10729 53582 : if (dobj->objType == DO_NAMESPACE &&
10730 712 : strcmp(dobj->name, "public") == 0)
10731 352 : continue;
10732 :
10733 : /* Else it had better be of a type we think has ACLs */
10734 53230 : if (dobj->objType == DO_NAMESPACE ||
10735 52870 : dobj->objType == DO_TYPE ||
10736 52822 : dobj->objType == DO_FUNC ||
10737 52632 : dobj->objType == DO_AGG ||
10738 52584 : dobj->objType == DO_TABLE ||
10739 0 : dobj->objType == DO_PROCLANG ||
10740 0 : dobj->objType == DO_FDW ||
10741 0 : dobj->objType == DO_FOREIGN_SERVER)
10742 53230 : {
10743 53230 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10744 :
10745 53230 : daobj->dacl.privtype = privtype;
10746 53230 : daobj->dacl.initprivs = pstrdup(initprivs);
10747 : }
10748 : else
10749 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10750 : classoid, objoid, objsubid);
10751 : }
10752 : }
10753 360 : PQclear(res);
10754 : }
10755 :
10756 360 : destroyPQExpBuffer(query);
10757 360 : }
10758 :
10759 : /*
10760 : * dumpCommentExtended --
10761 : *
10762 : * This routine is used to dump any comments associated with the
10763 : * object handed to this routine. The routine takes the object type
10764 : * and object name (ready to print, except for schema decoration), plus
10765 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10766 : * plus catalog ID and subid which are the lookup key for pg_description,
10767 : * plus the dump ID for the object (for setting a dependency).
10768 : * If a matching pg_description entry is found, it is dumped.
10769 : *
10770 : * Note: in some cases, such as comments for triggers and rules, the "type"
10771 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10772 : * but it doesn't seem worth complicating the API for all callers to make
10773 : * it cleaner.
10774 : *
10775 : * Note: although this routine takes a dumpId for dependency purposes,
10776 : * that purpose is just to mark the dependency in the emitted dump file
10777 : * for possible future use by pg_restore. We do NOT use it for determining
10778 : * ordering of the comment in the dump file, because this routine is called
10779 : * after dependency sorting occurs. This routine should be called just after
10780 : * calling ArchiveEntry() for the specified object.
10781 : */
10782 : static void
10783 12966 : dumpCommentExtended(Archive *fout, const char *type,
10784 : const char *name, const char *namespace,
10785 : const char *owner, CatalogId catalogId,
10786 : int subid, DumpId dumpId,
10787 : const char *initdb_comment)
10788 : {
10789 12966 : DumpOptions *dopt = fout->dopt;
10790 : CommentItem *comments;
10791 : int ncomments;
10792 :
10793 : /* do nothing, if --no-comments is supplied */
10794 12966 : if (dopt->no_comments)
10795 0 : return;
10796 :
10797 : /* Comments are schema not data ... except LO comments are data */
10798 12966 : if (strcmp(type, "LARGE OBJECT") != 0)
10799 : {
10800 12862 : if (!dopt->dumpSchema)
10801 0 : return;
10802 : }
10803 : else
10804 : {
10805 : /* We do dump LO comments in binary-upgrade mode */
10806 104 : if (!dopt->dumpData && !dopt->binary_upgrade)
10807 0 : return;
10808 : }
10809 :
10810 : /* Search for comments associated with catalogId, using table */
10811 12966 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10812 : &comments);
10813 :
10814 : /* Is there one matching the subid? */
10815 12966 : while (ncomments > 0)
10816 : {
10817 12878 : if (comments->objsubid == subid)
10818 12878 : break;
10819 0 : comments++;
10820 0 : ncomments--;
10821 : }
10822 :
10823 12966 : if (initdb_comment != NULL)
10824 : {
10825 : static CommentItem empty_comment = {.descr = ""};
10826 :
10827 : /*
10828 : * initdb creates this object with a comment. Skip dumping the
10829 : * initdb-provided comment, which would complicate matters for
10830 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10831 : * comment, replicate that.
10832 : */
10833 226 : if (ncomments == 0)
10834 : {
10835 8 : comments = &empty_comment;
10836 8 : ncomments = 1;
10837 : }
10838 218 : else if (strcmp(comments->descr, initdb_comment) == 0)
10839 218 : ncomments = 0;
10840 : }
10841 :
10842 : /* If a comment exists, build COMMENT ON statement */
10843 12966 : if (ncomments > 0)
10844 : {
10845 12668 : PQExpBuffer query = createPQExpBuffer();
10846 12668 : PQExpBuffer tag = createPQExpBuffer();
10847 :
10848 12668 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10849 12668 : if (namespace && *namespace)
10850 12316 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10851 12668 : appendPQExpBuffer(query, "%s IS ", name);
10852 12668 : appendStringLiteralAH(query, comments->descr, fout);
10853 12668 : appendPQExpBufferStr(query, ";\n");
10854 :
10855 12668 : appendPQExpBuffer(tag, "%s %s", type, name);
10856 :
10857 : /*
10858 : * We mark comments as SECTION_NONE because they really belong in the
10859 : * same section as their parent, whether that is pre-data or
10860 : * post-data.
10861 : */
10862 12668 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10863 12668 : ARCHIVE_OPTS(.tag = tag->data,
10864 : .namespace = namespace,
10865 : .owner = owner,
10866 : .description = "COMMENT",
10867 : .section = SECTION_NONE,
10868 : .createStmt = query->data,
10869 : .deps = &dumpId,
10870 : .nDeps = 1));
10871 :
10872 12668 : destroyPQExpBuffer(query);
10873 12668 : destroyPQExpBuffer(tag);
10874 : }
10875 : }
10876 :
10877 : /*
10878 : * dumpComment --
10879 : *
10880 : * Typical simplification of the above function.
10881 : */
10882 : static inline void
10883 12662 : dumpComment(Archive *fout, const char *type,
10884 : const char *name, const char *namespace,
10885 : const char *owner, CatalogId catalogId,
10886 : int subid, DumpId dumpId)
10887 : {
10888 12662 : dumpCommentExtended(fout, type, name, namespace, owner,
10889 : catalogId, subid, dumpId, NULL);
10890 12662 : }
10891 :
10892 : /*
10893 : * appendNamedArgument --
10894 : *
10895 : * Convenience routine for constructing parameters of the form:
10896 : * 'paraname', 'value'::type
10897 : */
10898 : static void
10899 10082 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10900 : const char *argtype, const char *argval)
10901 : {
10902 10082 : appendPQExpBufferStr(out, ",\n\t");
10903 :
10904 10082 : appendStringLiteralAH(out, argname, fout);
10905 10082 : appendPQExpBufferStr(out, ", ");
10906 :
10907 10082 : appendStringLiteralAH(out, argval, fout);
10908 10082 : appendPQExpBuffer(out, "::%s", argtype);
10909 10082 : }
10910 :
10911 : /*
10912 : * fetchAttributeStats --
10913 : *
10914 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10915 : */
10916 : static PGresult *
10917 2234 : fetchAttributeStats(Archive *fout)
10918 : {
10919 2234 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10920 2234 : PQExpBuffer nspnames = createPQExpBuffer();
10921 2234 : PQExpBuffer relnames = createPQExpBuffer();
10922 2234 : int count = 0;
10923 2234 : PGresult *res = NULL;
10924 : static TocEntry *te;
10925 : static bool restarted;
10926 2234 : int max_rels = MAX_ATTR_STATS_RELS;
10927 :
10928 : /*
10929 : * Our query for retrieving statistics for multiple relations uses WITH
10930 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10931 : * in v9.4. For older versions, we resort to gathering statistics for a
10932 : * single relation at a time.
10933 : */
10934 2234 : if (fout->remoteVersion < 90400)
10935 0 : max_rels = 1;
10936 :
10937 : /* If we're just starting, set our TOC pointer. */
10938 2234 : if (!te)
10939 114 : te = AH->toc->next;
10940 :
10941 : /*
10942 : * We can't easily avoid a second TOC scan for the tar format because it
10943 : * writes restore.sql separately, which means we must execute the queries
10944 : * twice. This feels risky, but there is no known reason it should
10945 : * generate different output than the first pass. Even if it does, the
10946 : * worst-case scenario is that restore.sql might have different statistics
10947 : * data than the archive.
10948 : */
10949 2234 : if (!restarted && te == AH->toc && AH->format == archTar)
10950 : {
10951 2 : te = AH->toc->next;
10952 2 : restarted = true;
10953 : }
10954 :
10955 2234 : appendPQExpBufferChar(nspnames, '{');
10956 2234 : appendPQExpBufferChar(relnames, '{');
10957 :
10958 : /*
10959 : * Scan the TOC for the next set of relevant stats entries. We assume
10960 : * that statistics are dumped in the order they are listed in the TOC.
10961 : * This is perhaps not the sturdiest assumption, so we verify it matches
10962 : * reality in dumpRelationStats_dumper().
10963 : */
10964 33756 : for (; te != AH->toc && count < max_rels; te = te->next)
10965 : {
10966 31522 : if ((te->reqs & REQ_STATS) != 0 &&
10967 7008 : strcmp(te->desc, "STATISTICS DATA") == 0)
10968 : {
10969 7008 : appendPGArray(nspnames, te->namespace);
10970 7008 : appendPGArray(relnames, te->tag);
10971 7008 : count++;
10972 : }
10973 : }
10974 :
10975 2234 : appendPQExpBufferChar(nspnames, '}');
10976 2234 : appendPQExpBufferChar(relnames, '}');
10977 :
10978 : /* Execute the query for the next batch of relations. */
10979 2234 : if (count > 0)
10980 : {
10981 206 : PQExpBuffer query = createPQExpBuffer();
10982 :
10983 206 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
10984 206 : appendStringLiteralAH(query, nspnames->data, fout);
10985 206 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
10986 206 : appendStringLiteralAH(query, relnames->data, fout);
10987 206 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
10988 206 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10989 206 : destroyPQExpBuffer(query);
10990 : }
10991 :
10992 2234 : destroyPQExpBuffer(nspnames);
10993 2234 : destroyPQExpBuffer(relnames);
10994 2234 : return res;
10995 : }
10996 :
10997 : /*
10998 : * dumpRelationStats_dumper --
10999 : *
11000 : * Generate command to import stats into the relation on the new database.
11001 : * This routine is called by the Archiver when it wants the statistics to be
11002 : * dumped.
11003 : */
11004 : static char *
11005 7008 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11006 : {
11007 7008 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11008 : static PGresult *res;
11009 : static int rownum;
11010 : PQExpBuffer query;
11011 : PQExpBufferData out_data;
11012 7008 : PQExpBuffer out = &out_data;
11013 : int i_schemaname;
11014 : int i_tablename;
11015 : int i_attname;
11016 : int i_inherited;
11017 : int i_null_frac;
11018 : int i_avg_width;
11019 : int i_n_distinct;
11020 : int i_most_common_vals;
11021 : int i_most_common_freqs;
11022 : int i_histogram_bounds;
11023 : int i_correlation;
11024 : int i_most_common_elems;
11025 : int i_most_common_elem_freqs;
11026 : int i_elem_count_histogram;
11027 : int i_range_length_histogram;
11028 : int i_range_empty_frac;
11029 : int i_range_bounds_histogram;
11030 : static TocEntry *expected_te;
11031 :
11032 : /*
11033 : * fetchAttributeStats() assumes that the statistics are dumped in the
11034 : * order they are listed in the TOC. We verify that here for safety.
11035 : */
11036 7008 : if (!expected_te)
11037 114 : expected_te = ((ArchiveHandle *) fout)->toc;
11038 :
11039 7008 : expected_te = expected_te->next;
11040 27830 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11041 7008 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11042 20822 : expected_te = expected_te->next;
11043 :
11044 7008 : if (te != expected_te)
11045 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11046 : te->dumpId, te->desc, te->tag,
11047 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11048 :
11049 7008 : query = createPQExpBuffer();
11050 7008 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11051 : {
11052 114 : appendPQExpBufferStr(query,
11053 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11054 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11055 : "s.null_frac, s.avg_width, s.n_distinct, "
11056 : "s.most_common_vals, s.most_common_freqs, "
11057 : "s.histogram_bounds, s.correlation, "
11058 : "s.most_common_elems, s.most_common_elem_freqs, "
11059 : "s.elem_count_histogram, ");
11060 :
11061 114 : if (fout->remoteVersion >= 170000)
11062 114 : appendPQExpBufferStr(query,
11063 : "s.range_length_histogram, "
11064 : "s.range_empty_frac, "
11065 : "s.range_bounds_histogram ");
11066 : else
11067 0 : appendPQExpBufferStr(query,
11068 : "NULL AS range_length_histogram,"
11069 : "NULL AS range_empty_frac,"
11070 : "NULL AS range_bounds_histogram ");
11071 :
11072 : /*
11073 : * The results must be in the order of the relations supplied in the
11074 : * parameters to ensure we remain in sync as we walk through the TOC.
11075 : * The redundant filter clause on s.tablename = ANY(...) seems
11076 : * sufficient to convince the planner to use
11077 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11078 : * This may not work for all versions.
11079 : *
11080 : * Our query for retrieving statistics for multiple relations uses
11081 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11082 : * introduced in v9.4. For older versions, we resort to gathering
11083 : * statistics for a single relation at a time.
11084 : */
11085 114 : if (fout->remoteVersion >= 90400)
11086 114 : appendPQExpBufferStr(query,
11087 : "FROM pg_catalog.pg_stats s "
11088 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11089 : "ON s.schemaname = u.schemaname "
11090 : "AND s.tablename = u.tablename "
11091 : "WHERE s.tablename = ANY($2) "
11092 : "ORDER BY u.ord, s.attname, s.inherited");
11093 : else
11094 0 : appendPQExpBufferStr(query,
11095 : "FROM pg_catalog.pg_stats s "
11096 : "WHERE s.schemaname = $1[1] "
11097 : "AND s.tablename = $2[1] "
11098 : "ORDER BY s.attname, s.inherited");
11099 :
11100 114 : ExecuteSqlStatement(fout, query->data);
11101 :
11102 114 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11103 114 : resetPQExpBuffer(query);
11104 : }
11105 :
11106 7008 : initPQExpBuffer(out);
11107 :
11108 : /* restore relation stats */
11109 7008 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11110 7008 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11111 : fout->remoteVersion);
11112 7008 : appendPQExpBufferStr(out, "\t'schemaname', ");
11113 7008 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11114 7008 : appendPQExpBufferStr(out, ",\n");
11115 7008 : appendPQExpBufferStr(out, "\t'relname', ");
11116 7008 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11117 7008 : appendPQExpBufferStr(out, ",\n");
11118 7008 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11119 :
11120 : /*
11121 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11122 : * the relation is empty, or it could mean that it hadn't yet been
11123 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11124 : * This ambiguity allegedly can cause the planner to choose inefficient
11125 : * plans after restoring to v18 or newer. To deal with this, let's just
11126 : * set reltuples to -1 in that case.
11127 : */
11128 7008 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11129 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11130 : else
11131 7008 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11132 :
11133 7008 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11134 7008 : rsinfo->relallvisible);
11135 :
11136 7008 : if (fout->remoteVersion >= 180000)
11137 7008 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11138 :
11139 7008 : appendPQExpBufferStr(out, "\n);\n");
11140 :
11141 : /* Fetch the next batch of attribute statistics if needed. */
11142 7008 : if (rownum >= PQntuples(res))
11143 : {
11144 2234 : PQclear(res);
11145 2234 : res = fetchAttributeStats(fout);
11146 2234 : rownum = 0;
11147 : }
11148 :
11149 7008 : i_schemaname = PQfnumber(res, "schemaname");
11150 7008 : i_tablename = PQfnumber(res, "tablename");
11151 7008 : i_attname = PQfnumber(res, "attname");
11152 7008 : i_inherited = PQfnumber(res, "inherited");
11153 7008 : i_null_frac = PQfnumber(res, "null_frac");
11154 7008 : i_avg_width = PQfnumber(res, "avg_width");
11155 7008 : i_n_distinct = PQfnumber(res, "n_distinct");
11156 7008 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11157 7008 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11158 7008 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11159 7008 : i_correlation = PQfnumber(res, "correlation");
11160 7008 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11161 7008 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11162 7008 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11163 7008 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11164 7008 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11165 7008 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11166 :
11167 : /* restore attribute stats */
11168 8534 : for (; rownum < PQntuples(res); rownum++)
11169 : {
11170 : const char *attname;
11171 :
11172 : /* Stop if the next stat row in our cache isn't for this relation. */
11173 6300 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11174 1526 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11175 : break;
11176 :
11177 1526 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11178 1526 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11179 : fout->remoteVersion);
11180 1526 : appendPQExpBufferStr(out, "\t'schemaname', ");
11181 1526 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11182 1526 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11183 1526 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11184 :
11185 1526 : if (PQgetisnull(res, rownum, i_attname))
11186 0 : pg_fatal("unexpected null attname");
11187 1526 : attname = PQgetvalue(res, rownum, i_attname);
11188 :
11189 : /*
11190 : * Indexes look up attname in indAttNames to derive attnum, all others
11191 : * use attname directly. We must specify attnum for indexes, since
11192 : * their attnames are not necessarily stable across dump/reload.
11193 : */
11194 1526 : if (rsinfo->nindAttNames == 0)
11195 : {
11196 1450 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11197 1450 : appendStringLiteralAH(out, attname, fout);
11198 : }
11199 : else
11200 : {
11201 76 : bool found = false;
11202 :
11203 144 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11204 : {
11205 144 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11206 : {
11207 76 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11208 : i + 1);
11209 76 : found = true;
11210 76 : break;
11211 : }
11212 : }
11213 :
11214 76 : if (!found)
11215 0 : pg_fatal("could not find index attname \"%s\"", attname);
11216 : }
11217 :
11218 1526 : if (!PQgetisnull(res, rownum, i_inherited))
11219 1526 : appendNamedArgument(out, fout, "inherited", "boolean",
11220 1526 : PQgetvalue(res, rownum, i_inherited));
11221 1526 : if (!PQgetisnull(res, rownum, i_null_frac))
11222 1526 : appendNamedArgument(out, fout, "null_frac", "real",
11223 1526 : PQgetvalue(res, rownum, i_null_frac));
11224 1526 : if (!PQgetisnull(res, rownum, i_avg_width))
11225 1526 : appendNamedArgument(out, fout, "avg_width", "integer",
11226 1526 : PQgetvalue(res, rownum, i_avg_width));
11227 1526 : if (!PQgetisnull(res, rownum, i_n_distinct))
11228 1526 : appendNamedArgument(out, fout, "n_distinct", "real",
11229 1526 : PQgetvalue(res, rownum, i_n_distinct));
11230 1526 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11231 764 : appendNamedArgument(out, fout, "most_common_vals", "text",
11232 764 : PQgetvalue(res, rownum, i_most_common_vals));
11233 1526 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11234 764 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11235 764 : PQgetvalue(res, rownum, i_most_common_freqs));
11236 1526 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11237 922 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11238 922 : PQgetvalue(res, rownum, i_histogram_bounds));
11239 1526 : if (!PQgetisnull(res, rownum, i_correlation))
11240 1464 : appendNamedArgument(out, fout, "correlation", "real",
11241 1464 : PQgetvalue(res, rownum, i_correlation));
11242 1526 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11243 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11244 16 : PQgetvalue(res, rownum, i_most_common_elems));
11245 1526 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11246 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11247 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11248 1526 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11249 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11250 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11251 1526 : if (fout->remoteVersion >= 170000)
11252 : {
11253 1526 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11254 6 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11255 6 : PQgetvalue(res, rownum, i_range_length_histogram));
11256 1526 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11257 6 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11258 6 : PQgetvalue(res, rownum, i_range_empty_frac));
11259 1526 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11260 6 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11261 6 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11262 : }
11263 1526 : appendPQExpBufferStr(out, "\n);\n");
11264 : }
11265 :
11266 7008 : destroyPQExpBuffer(query);
11267 7008 : return out->data;
11268 : }
11269 :
11270 : /*
11271 : * dumpRelationStats --
11272 : *
11273 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11274 : * care of gathering the statistics and generating the restore commands when
11275 : * they are needed.
11276 : */
11277 : static void
11278 7152 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11279 : {
11280 7152 : const DumpableObject *dobj = &rsinfo->dobj;
11281 :
11282 : /* nothing to do if we are not dumping statistics */
11283 7152 : if (!fout->dopt->dumpStatistics)
11284 0 : return;
11285 :
11286 7152 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11287 7152 : ARCHIVE_OPTS(.tag = dobj->name,
11288 : .namespace = dobj->namespace->dobj.name,
11289 : .description = "STATISTICS DATA",
11290 : .section = rsinfo->section,
11291 : .defnFn = dumpRelationStats_dumper,
11292 : .defnArg = rsinfo,
11293 : .deps = dobj->dependencies,
11294 : .nDeps = dobj->nDeps));
11295 : }
11296 :
11297 : /*
11298 : * dumpTableComment --
11299 : *
11300 : * As above, but dump comments for both the specified table (or view)
11301 : * and its columns.
11302 : */
11303 : static void
11304 160 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11305 : const char *reltypename)
11306 : {
11307 160 : DumpOptions *dopt = fout->dopt;
11308 : CommentItem *comments;
11309 : int ncomments;
11310 : PQExpBuffer query;
11311 : PQExpBuffer tag;
11312 :
11313 : /* do nothing, if --no-comments is supplied */
11314 160 : if (dopt->no_comments)
11315 0 : return;
11316 :
11317 : /* Comments are SCHEMA not data */
11318 160 : if (!dopt->dumpSchema)
11319 0 : return;
11320 :
11321 : /* Search for comments associated with relation, using table */
11322 160 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11323 160 : tbinfo->dobj.catId.oid,
11324 : &comments);
11325 :
11326 : /* If comments exist, build COMMENT ON statements */
11327 160 : if (ncomments <= 0)
11328 0 : return;
11329 :
11330 160 : query = createPQExpBuffer();
11331 160 : tag = createPQExpBuffer();
11332 :
11333 460 : while (ncomments > 0)
11334 : {
11335 300 : const char *descr = comments->descr;
11336 300 : int objsubid = comments->objsubid;
11337 :
11338 300 : if (objsubid == 0)
11339 : {
11340 70 : resetPQExpBuffer(tag);
11341 70 : appendPQExpBuffer(tag, "%s %s", reltypename,
11342 70 : fmtId(tbinfo->dobj.name));
11343 :
11344 70 : resetPQExpBuffer(query);
11345 70 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11346 70 : fmtQualifiedDumpable(tbinfo));
11347 70 : appendStringLiteralAH(query, descr, fout);
11348 70 : appendPQExpBufferStr(query, ";\n");
11349 :
11350 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11351 70 : ARCHIVE_OPTS(.tag = tag->data,
11352 : .namespace = tbinfo->dobj.namespace->dobj.name,
11353 : .owner = tbinfo->rolname,
11354 : .description = "COMMENT",
11355 : .section = SECTION_NONE,
11356 : .createStmt = query->data,
11357 : .deps = &(tbinfo->dobj.dumpId),
11358 : .nDeps = 1));
11359 : }
11360 230 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11361 : {
11362 230 : resetPQExpBuffer(tag);
11363 230 : appendPQExpBuffer(tag, "COLUMN %s.",
11364 230 : fmtId(tbinfo->dobj.name));
11365 230 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11366 :
11367 230 : resetPQExpBuffer(query);
11368 230 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11369 230 : fmtQualifiedDumpable(tbinfo));
11370 230 : appendPQExpBuffer(query, "%s IS ",
11371 230 : fmtId(tbinfo->attnames[objsubid - 1]));
11372 230 : appendStringLiteralAH(query, descr, fout);
11373 230 : appendPQExpBufferStr(query, ";\n");
11374 :
11375 230 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11376 230 : ARCHIVE_OPTS(.tag = tag->data,
11377 : .namespace = tbinfo->dobj.namespace->dobj.name,
11378 : .owner = tbinfo->rolname,
11379 : .description = "COMMENT",
11380 : .section = SECTION_NONE,
11381 : .createStmt = query->data,
11382 : .deps = &(tbinfo->dobj.dumpId),
11383 : .nDeps = 1));
11384 : }
11385 :
11386 300 : comments++;
11387 300 : ncomments--;
11388 : }
11389 :
11390 160 : destroyPQExpBuffer(query);
11391 160 : destroyPQExpBuffer(tag);
11392 : }
11393 :
11394 : /*
11395 : * findComments --
11396 : *
11397 : * Find the comment(s), if any, associated with the given object. All the
11398 : * objsubid values associated with the given classoid/objoid are found with
11399 : * one search.
11400 : */
11401 : static int
11402 13196 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11403 : {
11404 13196 : CommentItem *middle = NULL;
11405 : CommentItem *low;
11406 : CommentItem *high;
11407 : int nmatch;
11408 :
11409 : /*
11410 : * Do binary search to find some item matching the object.
11411 : */
11412 13196 : low = &comments[0];
11413 13196 : high = &comments[ncomments - 1];
11414 131426 : while (low <= high)
11415 : {
11416 131338 : middle = low + (high - low) / 2;
11417 :
11418 131338 : if (classoid < middle->classoid)
11419 14870 : high = middle - 1;
11420 116468 : else if (classoid > middle->classoid)
11421 14356 : low = middle + 1;
11422 102112 : else if (objoid < middle->objoid)
11423 43126 : high = middle - 1;
11424 58986 : else if (objoid > middle->objoid)
11425 45878 : low = middle + 1;
11426 : else
11427 13108 : break; /* found a match */
11428 : }
11429 :
11430 13196 : if (low > high) /* no matches */
11431 : {
11432 88 : *items = NULL;
11433 88 : return 0;
11434 : }
11435 :
11436 : /*
11437 : * Now determine how many items match the object. The search loop
11438 : * invariant still holds: only items between low and high inclusive could
11439 : * match.
11440 : */
11441 13108 : nmatch = 1;
11442 13248 : while (middle > low)
11443 : {
11444 6134 : if (classoid != middle[-1].classoid ||
11445 5876 : objoid != middle[-1].objoid)
11446 : break;
11447 140 : middle--;
11448 140 : nmatch++;
11449 : }
11450 :
11451 13108 : *items = middle;
11452 :
11453 13108 : middle += nmatch;
11454 13108 : while (middle <= high)
11455 : {
11456 7046 : if (classoid != middle->classoid ||
11457 6428 : objoid != middle->objoid)
11458 : break;
11459 0 : middle++;
11460 0 : nmatch++;
11461 : }
11462 :
11463 13108 : return nmatch;
11464 : }
11465 :
11466 : /*
11467 : * collectComments --
11468 : *
11469 : * Construct a table of all comments available for database objects;
11470 : * also set the has-comment component flag for each relevant object.
11471 : *
11472 : * We used to do per-object queries for the comments, but it's much faster
11473 : * to pull them all over at once, and on most databases the memory cost
11474 : * isn't high.
11475 : *
11476 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11477 : */
11478 : static void
11479 364 : collectComments(Archive *fout)
11480 : {
11481 : PGresult *res;
11482 : PQExpBuffer query;
11483 : int i_description;
11484 : int i_classoid;
11485 : int i_objoid;
11486 : int i_objsubid;
11487 : int ntups;
11488 : int i;
11489 : DumpableObject *dobj;
11490 :
11491 364 : query = createPQExpBuffer();
11492 :
11493 364 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11494 : "FROM pg_catalog.pg_description "
11495 : "ORDER BY classoid, objoid, objsubid");
11496 :
11497 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11498 :
11499 : /* Construct lookup table containing OIDs in numeric form */
11500 :
11501 364 : i_description = PQfnumber(res, "description");
11502 364 : i_classoid = PQfnumber(res, "classoid");
11503 364 : i_objoid = PQfnumber(res, "objoid");
11504 364 : i_objsubid = PQfnumber(res, "objsubid");
11505 :
11506 364 : ntups = PQntuples(res);
11507 :
11508 364 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11509 364 : ncomments = 0;
11510 364 : dobj = NULL;
11511 :
11512 1939322 : for (i = 0; i < ntups; i++)
11513 : {
11514 : CatalogId objId;
11515 : int subid;
11516 :
11517 1938958 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11518 1938958 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11519 1938958 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11520 :
11521 : /* We needn't remember comments that don't match any dumpable object */
11522 1938958 : if (dobj == NULL ||
11523 698686 : dobj->catId.tableoid != objId.tableoid ||
11524 694268 : dobj->catId.oid != objId.oid)
11525 1938766 : dobj = findObjectByCatalogId(objId);
11526 1938958 : if (dobj == NULL)
11527 1239920 : continue;
11528 :
11529 : /*
11530 : * Comments on columns of composite types are linked to the type's
11531 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11532 : * in the type's own DumpableObject.
11533 : */
11534 699038 : if (subid != 0 && dobj->objType == DO_TABLE &&
11535 412 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11536 96 : {
11537 : TypeInfo *cTypeInfo;
11538 :
11539 96 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11540 96 : if (cTypeInfo)
11541 96 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11542 : }
11543 : else
11544 698942 : dobj->components |= DUMP_COMPONENT_COMMENT;
11545 :
11546 699038 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11547 699038 : comments[ncomments].classoid = objId.tableoid;
11548 699038 : comments[ncomments].objoid = objId.oid;
11549 699038 : comments[ncomments].objsubid = subid;
11550 699038 : ncomments++;
11551 : }
11552 :
11553 364 : PQclear(res);
11554 364 : destroyPQExpBuffer(query);
11555 364 : }
11556 :
11557 : /*
11558 : * dumpDumpableObject
11559 : *
11560 : * This routine and its subsidiaries are responsible for creating
11561 : * ArchiveEntries (TOC objects) for each object to be dumped.
11562 : */
11563 : static void
11564 1355142 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11565 : {
11566 : /*
11567 : * Clear any dump-request bits for components that don't exist for this
11568 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11569 : * request for every kind of object.)
11570 : */
11571 1355142 : dobj->dump &= dobj->components;
11572 :
11573 : /* Now, short-circuit if there's nothing to be done here. */
11574 1355142 : if (dobj->dump == 0)
11575 1201090 : return;
11576 :
11577 154052 : switch (dobj->objType)
11578 : {
11579 966 : case DO_NAMESPACE:
11580 966 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11581 966 : break;
11582 38 : case DO_EXTENSION:
11583 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11584 38 : break;
11585 1888 : case DO_TYPE:
11586 1888 : dumpType(fout, (const TypeInfo *) dobj);
11587 1888 : break;
11588 152 : case DO_SHELL_TYPE:
11589 152 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11590 152 : break;
11591 3686 : case DO_FUNC:
11592 3686 : dumpFunc(fout, (const FuncInfo *) dobj);
11593 3686 : break;
11594 590 : case DO_AGG:
11595 590 : dumpAgg(fout, (const AggInfo *) dobj);
11596 590 : break;
11597 5014 : case DO_OPERATOR:
11598 5014 : dumpOpr(fout, (const OprInfo *) dobj);
11599 5014 : break;
11600 172 : case DO_ACCESS_METHOD:
11601 172 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11602 172 : break;
11603 1338 : case DO_OPCLASS:
11604 1338 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11605 1338 : break;
11606 1110 : case DO_OPFAMILY:
11607 1110 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11608 1110 : break;
11609 5086 : case DO_COLLATION:
11610 5086 : dumpCollation(fout, (const CollInfo *) dobj);
11611 5086 : break;
11612 850 : case DO_CONVERSION:
11613 850 : dumpConversion(fout, (const ConvInfo *) dobj);
11614 850 : break;
11615 61058 : case DO_TABLE:
11616 61058 : dumpTable(fout, (const TableInfo *) dobj);
11617 61058 : break;
11618 2816 : case DO_TABLE_ATTACH:
11619 2816 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11620 2816 : break;
11621 2136 : case DO_ATTRDEF:
11622 2136 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11623 2136 : break;
11624 5262 : case DO_INDEX:
11625 5262 : dumpIndex(fout, (const IndxInfo *) dobj);
11626 5262 : break;
11627 1176 : case DO_INDEX_ATTACH:
11628 1176 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11629 1176 : break;
11630 284 : case DO_STATSEXT:
11631 284 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11632 284 : break;
11633 804 : case DO_REFRESH_MATVIEW:
11634 804 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11635 804 : break;
11636 2380 : case DO_RULE:
11637 2380 : dumpRule(fout, (const RuleInfo *) dobj);
11638 2380 : break;
11639 1076 : case DO_TRIGGER:
11640 1076 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11641 1076 : break;
11642 90 : case DO_EVENT_TRIGGER:
11643 90 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11644 90 : break;
11645 4774 : case DO_CONSTRAINT:
11646 4774 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11647 4774 : break;
11648 354 : case DO_FK_CONSTRAINT:
11649 354 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11650 354 : break;
11651 176 : case DO_PROCLANG:
11652 176 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11653 176 : break;
11654 140 : case DO_CAST:
11655 140 : dumpCast(fout, (const CastInfo *) dobj);
11656 140 : break;
11657 90 : case DO_TRANSFORM:
11658 90 : dumpTransform(fout, (const TransformInfo *) dobj);
11659 90 : break;
11660 804 : case DO_SEQUENCE_SET:
11661 804 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11662 804 : break;
11663 8764 : case DO_TABLE_DATA:
11664 8764 : dumpTableData(fout, (const TableDataInfo *) dobj);
11665 8764 : break;
11666 29144 : case DO_DUMMY_TYPE:
11667 : /* table rowtypes and array types are never dumped separately */
11668 29144 : break;
11669 88 : case DO_TSPARSER:
11670 88 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11671 88 : break;
11672 352 : case DO_TSDICT:
11673 352 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11674 352 : break;
11675 112 : case DO_TSTEMPLATE:
11676 112 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11677 112 : break;
11678 302 : case DO_TSCONFIG:
11679 302 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11680 302 : break;
11681 110 : case DO_FDW:
11682 110 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11683 110 : break;
11684 118 : case DO_FOREIGN_SERVER:
11685 118 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11686 118 : break;
11687 344 : case DO_DEFAULT_ACL:
11688 344 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11689 344 : break;
11690 156 : case DO_LARGE_OBJECT:
11691 156 : dumpLO(fout, (const LoInfo *) dobj);
11692 156 : break;
11693 158 : case DO_LARGE_OBJECT_DATA:
11694 158 : if (dobj->dump & DUMP_COMPONENT_DATA)
11695 : {
11696 : LoInfo *loinfo;
11697 : TocEntry *te;
11698 :
11699 158 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11700 158 : if (loinfo == NULL)
11701 0 : pg_fatal("missing metadata for large objects \"%s\"",
11702 : dobj->name);
11703 :
11704 158 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11705 158 : ARCHIVE_OPTS(.tag = dobj->name,
11706 : .owner = loinfo->rolname,
11707 : .description = "BLOBS",
11708 : .section = SECTION_DATA,
11709 : .deps = dobj->dependencies,
11710 : .nDeps = dobj->nDeps,
11711 : .dumpFn = dumpLOs,
11712 : .dumpArg = loinfo));
11713 :
11714 : /*
11715 : * Set the TocEntry's dataLength in case we are doing a
11716 : * parallel dump and want to order dump jobs by table size.
11717 : * (We need some size estimate for every TocEntry with a
11718 : * DataDumper function.) We don't currently have any cheap
11719 : * way to estimate the size of LOs, but fortunately it doesn't
11720 : * matter too much as long as we get large batches of LOs
11721 : * processed reasonably early. Assume 8K per blob.
11722 : */
11723 158 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11724 : }
11725 158 : break;
11726 694 : case DO_POLICY:
11727 694 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11728 694 : break;
11729 452 : case DO_PUBLICATION:
11730 452 : dumpPublication(fout, (const PublicationInfo *) dobj);
11731 452 : break;
11732 610 : case DO_PUBLICATION_REL:
11733 610 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11734 610 : break;
11735 210 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11736 210 : dumpPublicationNamespace(fout,
11737 : (const PublicationSchemaInfo *) dobj);
11738 210 : break;
11739 244 : case DO_SUBSCRIPTION:
11740 244 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11741 244 : break;
11742 4 : case DO_SUBSCRIPTION_REL:
11743 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11744 4 : break;
11745 7152 : case DO_REL_STATS:
11746 7152 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11747 7152 : break;
11748 728 : case DO_PRE_DATA_BOUNDARY:
11749 : case DO_POST_DATA_BOUNDARY:
11750 : /* never dumped, nothing to do */
11751 728 : break;
11752 : }
11753 : }
11754 :
11755 : /*
11756 : * dumpNamespace
11757 : * writes out to fout the queries to recreate a user-defined namespace
11758 : */
11759 : static void
11760 966 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11761 : {
11762 966 : DumpOptions *dopt = fout->dopt;
11763 : PQExpBuffer q;
11764 : PQExpBuffer delq;
11765 : char *qnspname;
11766 :
11767 : /* Do nothing if not dumping schema */
11768 966 : if (!dopt->dumpSchema)
11769 56 : return;
11770 :
11771 910 : q = createPQExpBuffer();
11772 910 : delq = createPQExpBuffer();
11773 :
11774 910 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11775 :
11776 910 : if (nspinfo->create)
11777 : {
11778 616 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11779 616 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11780 : }
11781 : else
11782 : {
11783 : /* see selectDumpableNamespace() */
11784 294 : appendPQExpBufferStr(delq,
11785 : "-- *not* dropping schema, since initdb creates it\n");
11786 294 : appendPQExpBufferStr(q,
11787 : "-- *not* creating schema, since initdb creates it\n");
11788 : }
11789 :
11790 910 : if (dopt->binary_upgrade)
11791 180 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11792 : "SCHEMA", qnspname, NULL);
11793 :
11794 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11795 388 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11796 388 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11797 : .owner = nspinfo->rolname,
11798 : .description = "SCHEMA",
11799 : .section = SECTION_PRE_DATA,
11800 : .createStmt = q->data,
11801 : .dropStmt = delq->data));
11802 :
11803 : /* Dump Schema Comments and Security Labels */
11804 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11805 : {
11806 304 : const char *initdb_comment = NULL;
11807 :
11808 304 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11809 226 : initdb_comment = "standard public schema";
11810 304 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11811 304 : NULL, nspinfo->rolname,
11812 304 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11813 : initdb_comment);
11814 : }
11815 :
11816 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11817 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11818 0 : NULL, nspinfo->rolname,
11819 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11820 :
11821 910 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11822 700 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11823 : qnspname, NULL, NULL,
11824 700 : NULL, nspinfo->rolname, &nspinfo->dacl);
11825 :
11826 910 : free(qnspname);
11827 :
11828 910 : destroyPQExpBuffer(q);
11829 910 : destroyPQExpBuffer(delq);
11830 : }
11831 :
11832 : /*
11833 : * dumpExtension
11834 : * writes out to fout the queries to recreate an extension
11835 : */
11836 : static void
11837 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11838 : {
11839 38 : DumpOptions *dopt = fout->dopt;
11840 : PQExpBuffer q;
11841 : PQExpBuffer delq;
11842 : char *qextname;
11843 :
11844 : /* Do nothing if not dumping schema */
11845 38 : if (!dopt->dumpSchema)
11846 2 : return;
11847 :
11848 36 : q = createPQExpBuffer();
11849 36 : delq = createPQExpBuffer();
11850 :
11851 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11852 :
11853 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11854 :
11855 36 : if (!dopt->binary_upgrade)
11856 : {
11857 : /*
11858 : * In a regular dump, we simply create the extension, intentionally
11859 : * not specifying a version, so that the destination installation's
11860 : * default version is used.
11861 : *
11862 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11863 : * types; but there are various scenarios in which it's convenient to
11864 : * manually create the desired extension before restoring, so we
11865 : * prefer to allow it to exist already.
11866 : */
11867 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11868 34 : qextname, fmtId(extinfo->namespace));
11869 : }
11870 : else
11871 : {
11872 : /*
11873 : * In binary-upgrade mode, it's critical to reproduce the state of the
11874 : * database exactly, so our procedure is to create an empty extension,
11875 : * restore all the contained objects normally, and add them to the
11876 : * extension one by one. This function performs just the first of
11877 : * those steps. binary_upgrade_extension_member() takes care of
11878 : * adding member objects as they're created.
11879 : */
11880 : int i;
11881 : int n;
11882 :
11883 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11884 :
11885 : /*
11886 : * We unconditionally create the extension, so we must drop it if it
11887 : * exists. This could happen if the user deleted 'plpgsql' and then
11888 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11889 : */
11890 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11891 :
11892 2 : appendPQExpBufferStr(q,
11893 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11894 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11895 2 : appendPQExpBufferStr(q, ", ");
11896 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11897 2 : appendPQExpBufferStr(q, ", ");
11898 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11899 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11900 2 : appendPQExpBufferStr(q, ", ");
11901 :
11902 : /*
11903 : * Note that we're pushing extconfig (an OID array) back into
11904 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11905 : * preserved in binary upgrade.
11906 : */
11907 2 : if (strlen(extinfo->extconfig) > 2)
11908 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11909 : else
11910 0 : appendPQExpBufferStr(q, "NULL");
11911 2 : appendPQExpBufferStr(q, ", ");
11912 2 : if (strlen(extinfo->extcondition) > 2)
11913 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11914 : else
11915 0 : appendPQExpBufferStr(q, "NULL");
11916 2 : appendPQExpBufferStr(q, ", ");
11917 2 : appendPQExpBufferStr(q, "ARRAY[");
11918 2 : n = 0;
11919 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11920 : {
11921 : DumpableObject *extobj;
11922 :
11923 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11924 2 : if (extobj && extobj->objType == DO_EXTENSION)
11925 : {
11926 0 : if (n++ > 0)
11927 0 : appendPQExpBufferChar(q, ',');
11928 0 : appendStringLiteralAH(q, extobj->name, fout);
11929 : }
11930 : }
11931 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11932 2 : appendPQExpBufferStr(q, ");\n");
11933 : }
11934 :
11935 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11936 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11937 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11938 : .description = "EXTENSION",
11939 : .section = SECTION_PRE_DATA,
11940 : .createStmt = q->data,
11941 : .dropStmt = delq->data));
11942 :
11943 : /* Dump Extension Comments and Security Labels */
11944 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11945 36 : dumpComment(fout, "EXTENSION", qextname,
11946 : NULL, "",
11947 36 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11948 :
11949 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11950 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11951 : NULL, "",
11952 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11953 :
11954 36 : free(qextname);
11955 :
11956 36 : destroyPQExpBuffer(q);
11957 36 : destroyPQExpBuffer(delq);
11958 : }
11959 :
11960 : /*
11961 : * dumpType
11962 : * writes out to fout the queries to recreate a user-defined type
11963 : */
11964 : static void
11965 1888 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11966 : {
11967 1888 : DumpOptions *dopt = fout->dopt;
11968 :
11969 : /* Do nothing if not dumping schema */
11970 1888 : if (!dopt->dumpSchema)
11971 98 : return;
11972 :
11973 : /* Dump out in proper style */
11974 1790 : if (tyinfo->typtype == TYPTYPE_BASE)
11975 566 : dumpBaseType(fout, tyinfo);
11976 1224 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11977 310 : dumpDomain(fout, tyinfo);
11978 914 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11979 266 : dumpCompositeType(fout, tyinfo);
11980 648 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11981 182 : dumpEnumType(fout, tyinfo);
11982 466 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11983 236 : dumpRangeType(fout, tyinfo);
11984 230 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11985 80 : dumpUndefinedType(fout, tyinfo);
11986 : else
11987 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11988 : tyinfo->dobj.name);
11989 : }
11990 :
11991 : /*
11992 : * dumpEnumType
11993 : * writes out to fout the queries to recreate a user-defined enum type
11994 : */
11995 : static void
11996 182 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11997 : {
11998 182 : DumpOptions *dopt = fout->dopt;
11999 182 : PQExpBuffer q = createPQExpBuffer();
12000 182 : PQExpBuffer delq = createPQExpBuffer();
12001 182 : PQExpBuffer query = createPQExpBuffer();
12002 : PGresult *res;
12003 : int num,
12004 : i;
12005 : Oid enum_oid;
12006 : char *qtypname;
12007 : char *qualtypname;
12008 : char *label;
12009 : int i_enumlabel;
12010 : int i_oid;
12011 :
12012 182 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12013 : {
12014 : /* Set up query for enum-specific details */
12015 86 : appendPQExpBufferStr(query,
12016 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12017 : "SELECT oid, enumlabel "
12018 : "FROM pg_catalog.pg_enum "
12019 : "WHERE enumtypid = $1 "
12020 : "ORDER BY enumsortorder");
12021 :
12022 86 : ExecuteSqlStatement(fout, query->data);
12023 :
12024 86 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12025 : }
12026 :
12027 182 : printfPQExpBuffer(query,
12028 : "EXECUTE dumpEnumType('%u')",
12029 182 : tyinfo->dobj.catId.oid);
12030 :
12031 182 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12032 :
12033 182 : num = PQntuples(res);
12034 :
12035 182 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12036 182 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12037 :
12038 : /*
12039 : * CASCADE shouldn't be required here as for normal types since the I/O
12040 : * functions are generic and do not get dropped.
12041 : */
12042 182 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12043 :
12044 182 : if (dopt->binary_upgrade)
12045 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12046 12 : tyinfo->dobj.catId.oid,
12047 : false, false);
12048 :
12049 182 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12050 : qualtypname);
12051 :
12052 182 : if (!dopt->binary_upgrade)
12053 : {
12054 170 : i_enumlabel = PQfnumber(res, "enumlabel");
12055 :
12056 : /* Labels with server-assigned oids */
12057 1012 : for (i = 0; i < num; i++)
12058 : {
12059 842 : label = PQgetvalue(res, i, i_enumlabel);
12060 842 : if (i > 0)
12061 672 : appendPQExpBufferChar(q, ',');
12062 842 : appendPQExpBufferStr(q, "\n ");
12063 842 : appendStringLiteralAH(q, label, fout);
12064 : }
12065 : }
12066 :
12067 182 : appendPQExpBufferStr(q, "\n);\n");
12068 :
12069 182 : if (dopt->binary_upgrade)
12070 : {
12071 12 : i_oid = PQfnumber(res, "oid");
12072 12 : i_enumlabel = PQfnumber(res, "enumlabel");
12073 :
12074 : /* Labels with dump-assigned (preserved) oids */
12075 124 : for (i = 0; i < num; i++)
12076 : {
12077 112 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12078 112 : label = PQgetvalue(res, i, i_enumlabel);
12079 :
12080 112 : if (i == 0)
12081 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12082 112 : appendPQExpBuffer(q,
12083 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12084 : enum_oid);
12085 112 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12086 112 : appendStringLiteralAH(q, label, fout);
12087 112 : appendPQExpBufferStr(q, ";\n\n");
12088 : }
12089 : }
12090 :
12091 182 : if (dopt->binary_upgrade)
12092 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12093 : "TYPE", qtypname,
12094 12 : tyinfo->dobj.namespace->dobj.name);
12095 :
12096 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12097 182 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12098 182 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12099 : .namespace = tyinfo->dobj.namespace->dobj.name,
12100 : .owner = tyinfo->rolname,
12101 : .description = "TYPE",
12102 : .section = SECTION_PRE_DATA,
12103 : .createStmt = q->data,
12104 : .dropStmt = delq->data));
12105 :
12106 : /* Dump Type Comments and Security Labels */
12107 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12108 70 : dumpComment(fout, "TYPE", qtypname,
12109 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12110 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12111 :
12112 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12113 0 : dumpSecLabel(fout, "TYPE", qtypname,
12114 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12115 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12116 :
12117 182 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12118 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12119 : qtypname, NULL,
12120 70 : tyinfo->dobj.namespace->dobj.name,
12121 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12122 :
12123 182 : PQclear(res);
12124 182 : destroyPQExpBuffer(q);
12125 182 : destroyPQExpBuffer(delq);
12126 182 : destroyPQExpBuffer(query);
12127 182 : free(qtypname);
12128 182 : free(qualtypname);
12129 182 : }
12130 :
12131 : /*
12132 : * dumpRangeType
12133 : * writes out to fout the queries to recreate a user-defined range type
12134 : */
12135 : static void
12136 236 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12137 : {
12138 236 : DumpOptions *dopt = fout->dopt;
12139 236 : PQExpBuffer q = createPQExpBuffer();
12140 236 : PQExpBuffer delq = createPQExpBuffer();
12141 236 : PQExpBuffer query = createPQExpBuffer();
12142 : PGresult *res;
12143 : Oid collationOid;
12144 : char *qtypname;
12145 : char *qualtypname;
12146 : char *procname;
12147 :
12148 236 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12149 : {
12150 : /* Set up query for range-specific details */
12151 86 : appendPQExpBufferStr(query,
12152 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12153 :
12154 86 : appendPQExpBufferStr(query,
12155 : "SELECT ");
12156 :
12157 86 : if (fout->remoteVersion >= 140000)
12158 86 : appendPQExpBufferStr(query,
12159 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12160 : else
12161 0 : appendPQExpBufferStr(query,
12162 : "NULL AS rngmultitype, ");
12163 :
12164 86 : appendPQExpBufferStr(query,
12165 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12166 : "opc.opcname AS opcname, "
12167 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12168 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12169 : "opc.opcdefault, "
12170 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12171 : " ELSE rngcollation END AS collation, "
12172 : "rngcanonical, rngsubdiff "
12173 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12174 : " pg_catalog.pg_opclass opc "
12175 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12176 : "rngtypid = $1");
12177 :
12178 86 : ExecuteSqlStatement(fout, query->data);
12179 :
12180 86 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12181 : }
12182 :
12183 236 : printfPQExpBuffer(query,
12184 : "EXECUTE dumpRangeType('%u')",
12185 236 : tyinfo->dobj.catId.oid);
12186 :
12187 236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12188 :
12189 236 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12190 236 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12191 :
12192 : /*
12193 : * CASCADE shouldn't be required here as for normal types since the I/O
12194 : * functions are generic and do not get dropped.
12195 : */
12196 236 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12197 :
12198 236 : if (dopt->binary_upgrade)
12199 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12200 16 : tyinfo->dobj.catId.oid,
12201 : false, true);
12202 :
12203 236 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12204 : qualtypname);
12205 :
12206 236 : appendPQExpBuffer(q, "\n subtype = %s",
12207 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12208 :
12209 236 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12210 236 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12211 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12212 :
12213 : /* print subtype_opclass only if not default for subtype */
12214 236 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12215 : {
12216 70 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12217 70 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12218 :
12219 70 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12220 : fmtId(nspname));
12221 70 : appendPQExpBufferStr(q, fmtId(opcname));
12222 : }
12223 :
12224 236 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12225 236 : if (OidIsValid(collationOid))
12226 : {
12227 80 : CollInfo *coll = findCollationByOid(collationOid);
12228 :
12229 80 : if (coll)
12230 80 : appendPQExpBuffer(q, ",\n collation = %s",
12231 80 : fmtQualifiedDumpable(coll));
12232 : }
12233 :
12234 236 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12235 236 : if (strcmp(procname, "-") != 0)
12236 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12237 :
12238 236 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12239 236 : if (strcmp(procname, "-") != 0)
12240 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12241 :
12242 236 : appendPQExpBufferStr(q, "\n);\n");
12243 :
12244 236 : if (dopt->binary_upgrade)
12245 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12246 : "TYPE", qtypname,
12247 16 : tyinfo->dobj.namespace->dobj.name);
12248 :
12249 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12250 236 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12251 236 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12252 : .namespace = tyinfo->dobj.namespace->dobj.name,
12253 : .owner = tyinfo->rolname,
12254 : .description = "TYPE",
12255 : .section = SECTION_PRE_DATA,
12256 : .createStmt = q->data,
12257 : .dropStmt = delq->data));
12258 :
12259 : /* Dump Type Comments and Security Labels */
12260 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12261 106 : dumpComment(fout, "TYPE", qtypname,
12262 106 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12263 106 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12264 :
12265 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12266 0 : dumpSecLabel(fout, "TYPE", qtypname,
12267 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12268 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12269 :
12270 236 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12271 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12272 : qtypname, NULL,
12273 70 : tyinfo->dobj.namespace->dobj.name,
12274 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12275 :
12276 236 : PQclear(res);
12277 236 : destroyPQExpBuffer(q);
12278 236 : destroyPQExpBuffer(delq);
12279 236 : destroyPQExpBuffer(query);
12280 236 : free(qtypname);
12281 236 : free(qualtypname);
12282 236 : }
12283 :
12284 : /*
12285 : * dumpUndefinedType
12286 : * writes out to fout the queries to recreate a !typisdefined type
12287 : *
12288 : * This is a shell type, but we use different terminology to distinguish
12289 : * this case from where we have to emit a shell type definition to break
12290 : * circular dependencies. An undefined type shouldn't ever have anything
12291 : * depending on it.
12292 : */
12293 : static void
12294 80 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12295 : {
12296 80 : DumpOptions *dopt = fout->dopt;
12297 80 : PQExpBuffer q = createPQExpBuffer();
12298 80 : PQExpBuffer delq = createPQExpBuffer();
12299 : char *qtypname;
12300 : char *qualtypname;
12301 :
12302 80 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12303 80 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12304 :
12305 80 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12306 :
12307 80 : if (dopt->binary_upgrade)
12308 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12309 4 : tyinfo->dobj.catId.oid,
12310 : false, false);
12311 :
12312 80 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12313 : qualtypname);
12314 :
12315 80 : if (dopt->binary_upgrade)
12316 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12317 : "TYPE", qtypname,
12318 4 : tyinfo->dobj.namespace->dobj.name);
12319 :
12320 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12321 80 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12322 80 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12323 : .namespace = tyinfo->dobj.namespace->dobj.name,
12324 : .owner = tyinfo->rolname,
12325 : .description = "TYPE",
12326 : .section = SECTION_PRE_DATA,
12327 : .createStmt = q->data,
12328 : .dropStmt = delq->data));
12329 :
12330 : /* Dump Type Comments and Security Labels */
12331 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12332 70 : dumpComment(fout, "TYPE", qtypname,
12333 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12334 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12335 :
12336 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12337 0 : dumpSecLabel(fout, "TYPE", qtypname,
12338 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12339 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12340 :
12341 80 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12342 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12343 : qtypname, NULL,
12344 0 : tyinfo->dobj.namespace->dobj.name,
12345 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12346 :
12347 80 : destroyPQExpBuffer(q);
12348 80 : destroyPQExpBuffer(delq);
12349 80 : free(qtypname);
12350 80 : free(qualtypname);
12351 80 : }
12352 :
12353 : /*
12354 : * dumpBaseType
12355 : * writes out to fout the queries to recreate a user-defined base type
12356 : */
12357 : static void
12358 566 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12359 : {
12360 566 : DumpOptions *dopt = fout->dopt;
12361 566 : PQExpBuffer q = createPQExpBuffer();
12362 566 : PQExpBuffer delq = createPQExpBuffer();
12363 566 : PQExpBuffer query = createPQExpBuffer();
12364 : PGresult *res;
12365 : char *qtypname;
12366 : char *qualtypname;
12367 : char *typlen;
12368 : char *typinput;
12369 : char *typoutput;
12370 : char *typreceive;
12371 : char *typsend;
12372 : char *typmodin;
12373 : char *typmodout;
12374 : char *typanalyze;
12375 : char *typsubscript;
12376 : Oid typreceiveoid;
12377 : Oid typsendoid;
12378 : Oid typmodinoid;
12379 : Oid typmodoutoid;
12380 : Oid typanalyzeoid;
12381 : Oid typsubscriptoid;
12382 : char *typcategory;
12383 : char *typispreferred;
12384 : char *typdelim;
12385 : char *typbyval;
12386 : char *typalign;
12387 : char *typstorage;
12388 : char *typcollatable;
12389 : char *typdefault;
12390 566 : bool typdefault_is_literal = false;
12391 :
12392 566 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12393 : {
12394 : /* Set up query for type-specific details */
12395 86 : appendPQExpBufferStr(query,
12396 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12397 : "SELECT typlen, "
12398 : "typinput, typoutput, typreceive, typsend, "
12399 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12400 : "typsend::pg_catalog.oid AS typsendoid, "
12401 : "typanalyze, "
12402 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12403 : "typdelim, typbyval, typalign, typstorage, "
12404 : "typmodin, typmodout, "
12405 : "typmodin::pg_catalog.oid AS typmodinoid, "
12406 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12407 : "typcategory, typispreferred, "
12408 : "(typcollation <> 0) AS typcollatable, "
12409 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12410 :
12411 86 : if (fout->remoteVersion >= 140000)
12412 86 : appendPQExpBufferStr(query,
12413 : "typsubscript, "
12414 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12415 : else
12416 0 : appendPQExpBufferStr(query,
12417 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12418 :
12419 86 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12420 : "WHERE oid = $1");
12421 :
12422 86 : ExecuteSqlStatement(fout, query->data);
12423 :
12424 86 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12425 : }
12426 :
12427 566 : printfPQExpBuffer(query,
12428 : "EXECUTE dumpBaseType('%u')",
12429 566 : tyinfo->dobj.catId.oid);
12430 :
12431 566 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12432 :
12433 566 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12434 566 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12435 566 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12436 566 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12437 566 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12438 566 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12439 566 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12440 566 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12441 566 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12442 566 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12443 566 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12444 566 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12445 566 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12446 566 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12447 566 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12448 566 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12449 566 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12450 566 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12451 566 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12452 566 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12453 566 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12454 566 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12455 566 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12456 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12457 566 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12458 : {
12459 90 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12460 90 : typdefault_is_literal = true; /* it needs quotes */
12461 : }
12462 : else
12463 476 : typdefault = NULL;
12464 :
12465 566 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12466 566 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12467 :
12468 : /*
12469 : * The reason we include CASCADE is that the circular dependency between
12470 : * the type and its I/O functions makes it impossible to drop the type any
12471 : * other way.
12472 : */
12473 566 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12474 :
12475 : /*
12476 : * We might already have a shell type, but setting pg_type_oid is
12477 : * harmless, and in any case we'd better set the array type OID.
12478 : */
12479 566 : if (dopt->binary_upgrade)
12480 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12481 16 : tyinfo->dobj.catId.oid,
12482 : false, false);
12483 :
12484 566 : appendPQExpBuffer(q,
12485 : "CREATE TYPE %s (\n"
12486 : " INTERNALLENGTH = %s",
12487 : qualtypname,
12488 566 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12489 :
12490 : /* regproc result is sufficiently quoted already */
12491 566 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12492 566 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12493 566 : if (OidIsValid(typreceiveoid))
12494 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12495 566 : if (OidIsValid(typsendoid))
12496 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12497 566 : if (OidIsValid(typmodinoid))
12498 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12499 566 : if (OidIsValid(typmodoutoid))
12500 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12501 566 : if (OidIsValid(typanalyzeoid))
12502 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12503 :
12504 566 : if (strcmp(typcollatable, "t") == 0)
12505 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12506 :
12507 566 : if (typdefault != NULL)
12508 : {
12509 90 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12510 90 : if (typdefault_is_literal)
12511 90 : appendStringLiteralAH(q, typdefault, fout);
12512 : else
12513 0 : appendPQExpBufferStr(q, typdefault);
12514 : }
12515 :
12516 566 : if (OidIsValid(typsubscriptoid))
12517 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12518 :
12519 566 : if (OidIsValid(tyinfo->typelem))
12520 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12521 52 : getFormattedTypeName(fout, tyinfo->typelem,
12522 : zeroIsError));
12523 :
12524 566 : if (strcmp(typcategory, "U") != 0)
12525 : {
12526 316 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12527 316 : appendStringLiteralAH(q, typcategory, fout);
12528 : }
12529 :
12530 566 : if (strcmp(typispreferred, "t") == 0)
12531 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12532 :
12533 566 : if (typdelim && strcmp(typdelim, ",") != 0)
12534 : {
12535 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12536 6 : appendStringLiteralAH(q, typdelim, fout);
12537 : }
12538 :
12539 566 : if (*typalign == TYPALIGN_CHAR)
12540 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12541 542 : else if (*typalign == TYPALIGN_SHORT)
12542 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12543 530 : else if (*typalign == TYPALIGN_INT)
12544 380 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12545 150 : else if (*typalign == TYPALIGN_DOUBLE)
12546 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12547 :
12548 566 : if (*typstorage == TYPSTORAGE_PLAIN)
12549 416 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12550 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12551 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12552 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12553 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12554 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12555 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12556 :
12557 566 : if (strcmp(typbyval, "t") == 0)
12558 274 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12559 :
12560 566 : appendPQExpBufferStr(q, "\n);\n");
12561 :
12562 566 : if (dopt->binary_upgrade)
12563 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12564 : "TYPE", qtypname,
12565 16 : tyinfo->dobj.namespace->dobj.name);
12566 :
12567 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12568 566 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12569 566 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12570 : .namespace = tyinfo->dobj.namespace->dobj.name,
12571 : .owner = tyinfo->rolname,
12572 : .description = "TYPE",
12573 : .section = SECTION_PRE_DATA,
12574 : .createStmt = q->data,
12575 : .dropStmt = delq->data));
12576 :
12577 : /* Dump Type Comments and Security Labels */
12578 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12579 496 : dumpComment(fout, "TYPE", qtypname,
12580 496 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12581 496 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12582 :
12583 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12584 0 : dumpSecLabel(fout, "TYPE", qtypname,
12585 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12586 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12587 :
12588 566 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12589 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12590 : qtypname, NULL,
12591 70 : tyinfo->dobj.namespace->dobj.name,
12592 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12593 :
12594 566 : PQclear(res);
12595 566 : destroyPQExpBuffer(q);
12596 566 : destroyPQExpBuffer(delq);
12597 566 : destroyPQExpBuffer(query);
12598 566 : free(qtypname);
12599 566 : free(qualtypname);
12600 566 : }
12601 :
12602 : /*
12603 : * dumpDomain
12604 : * writes out to fout the queries to recreate a user-defined domain
12605 : */
12606 : static void
12607 310 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12608 : {
12609 310 : DumpOptions *dopt = fout->dopt;
12610 310 : PQExpBuffer q = createPQExpBuffer();
12611 310 : PQExpBuffer delq = createPQExpBuffer();
12612 310 : PQExpBuffer query = createPQExpBuffer();
12613 : PGresult *res;
12614 : int i;
12615 : char *qtypname;
12616 : char *qualtypname;
12617 : char *typnotnull;
12618 : char *typdefn;
12619 : char *typdefault;
12620 : Oid typcollation;
12621 310 : bool typdefault_is_literal = false;
12622 :
12623 310 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12624 : {
12625 : /* Set up query for domain-specific details */
12626 80 : appendPQExpBufferStr(query,
12627 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12628 :
12629 80 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12630 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12631 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12632 : "t.typdefault, "
12633 : "CASE WHEN t.typcollation <> u.typcollation "
12634 : "THEN t.typcollation ELSE 0 END AS typcollation "
12635 : "FROM pg_catalog.pg_type t "
12636 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12637 : "WHERE t.oid = $1");
12638 :
12639 80 : ExecuteSqlStatement(fout, query->data);
12640 :
12641 80 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12642 : }
12643 :
12644 310 : printfPQExpBuffer(query,
12645 : "EXECUTE dumpDomain('%u')",
12646 310 : tyinfo->dobj.catId.oid);
12647 :
12648 310 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12649 :
12650 310 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12651 310 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12652 310 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12653 80 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12654 230 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12655 : {
12656 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12657 0 : typdefault_is_literal = true; /* it needs quotes */
12658 : }
12659 : else
12660 230 : typdefault = NULL;
12661 310 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12662 :
12663 310 : if (dopt->binary_upgrade)
12664 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12665 50 : tyinfo->dobj.catId.oid,
12666 : true, /* force array type */
12667 : false); /* force multirange type */
12668 :
12669 310 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12670 310 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12671 :
12672 310 : appendPQExpBuffer(q,
12673 : "CREATE DOMAIN %s AS %s",
12674 : qualtypname,
12675 : typdefn);
12676 :
12677 : /* Print collation only if different from base type's collation */
12678 310 : if (OidIsValid(typcollation))
12679 : {
12680 : CollInfo *coll;
12681 :
12682 70 : coll = findCollationByOid(typcollation);
12683 70 : if (coll)
12684 70 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12685 : }
12686 :
12687 : /*
12688 : * Print a not-null constraint if there's one. In servers older than 17
12689 : * these don't have names, so just print it unadorned; in newer ones they
12690 : * do, but most of the time it's going to be the standard generated one,
12691 : * so omit the name in that case also.
12692 : */
12693 310 : if (typnotnull[0] == 't')
12694 : {
12695 100 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12696 0 : appendPQExpBufferStr(q, " NOT NULL");
12697 : else
12698 : {
12699 100 : ConstraintInfo *notnull = tyinfo->notnull;
12700 :
12701 100 : if (!notnull->separate)
12702 : {
12703 : char *default_name;
12704 :
12705 : /* XXX should match ChooseConstraintName better */
12706 100 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12707 :
12708 100 : if (strcmp(default_name, notnull->dobj.name) == 0)
12709 30 : appendPQExpBufferStr(q, " NOT NULL");
12710 : else
12711 70 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12712 70 : fmtId(notnull->dobj.name), notnull->condef);
12713 100 : free(default_name);
12714 : }
12715 : }
12716 : }
12717 :
12718 310 : if (typdefault != NULL)
12719 : {
12720 80 : appendPQExpBufferStr(q, " DEFAULT ");
12721 80 : if (typdefault_is_literal)
12722 0 : appendStringLiteralAH(q, typdefault, fout);
12723 : else
12724 80 : appendPQExpBufferStr(q, typdefault);
12725 : }
12726 :
12727 310 : PQclear(res);
12728 :
12729 : /*
12730 : * Add any CHECK constraints for the domain
12731 : */
12732 530 : for (i = 0; i < tyinfo->nDomChecks; i++)
12733 : {
12734 220 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12735 :
12736 220 : if (!domcheck->separate && domcheck->contype == 'c')
12737 210 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12738 210 : fmtId(domcheck->dobj.name), domcheck->condef);
12739 : }
12740 :
12741 310 : appendPQExpBufferStr(q, ";\n");
12742 :
12743 310 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12744 :
12745 310 : if (dopt->binary_upgrade)
12746 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12747 : "DOMAIN", qtypname,
12748 50 : tyinfo->dobj.namespace->dobj.name);
12749 :
12750 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12751 310 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12752 310 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12753 : .namespace = tyinfo->dobj.namespace->dobj.name,
12754 : .owner = tyinfo->rolname,
12755 : .description = "DOMAIN",
12756 : .section = SECTION_PRE_DATA,
12757 : .createStmt = q->data,
12758 : .dropStmt = delq->data));
12759 :
12760 : /* Dump Domain Comments and Security Labels */
12761 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12762 0 : dumpComment(fout, "DOMAIN", qtypname,
12763 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12764 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12765 :
12766 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12767 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12768 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12769 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12770 :
12771 310 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12772 70 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12773 : qtypname, NULL,
12774 70 : tyinfo->dobj.namespace->dobj.name,
12775 70 : NULL, tyinfo->rolname, &tyinfo->dacl);
12776 :
12777 : /* Dump any per-constraint comments */
12778 530 : for (i = 0; i < tyinfo->nDomChecks; i++)
12779 : {
12780 220 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12781 : PQExpBuffer conprefix;
12782 :
12783 : /* but only if the constraint itself was dumped here */
12784 220 : if (domcheck->separate)
12785 10 : continue;
12786 :
12787 210 : conprefix = createPQExpBuffer();
12788 210 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12789 210 : fmtId(domcheck->dobj.name));
12790 :
12791 210 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12792 70 : dumpComment(fout, conprefix->data, qtypname,
12793 70 : tyinfo->dobj.namespace->dobj.name,
12794 70 : tyinfo->rolname,
12795 70 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12796 :
12797 210 : destroyPQExpBuffer(conprefix);
12798 : }
12799 :
12800 : /*
12801 : * And a comment on the not-null constraint, if there's one -- but only if
12802 : * the constraint itself was dumped here
12803 : */
12804 310 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12805 : {
12806 100 : PQExpBuffer conprefix = createPQExpBuffer();
12807 :
12808 100 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12809 100 : fmtId(tyinfo->notnull->dobj.name));
12810 :
12811 100 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12812 70 : dumpComment(fout, conprefix->data, qtypname,
12813 70 : tyinfo->dobj.namespace->dobj.name,
12814 70 : tyinfo->rolname,
12815 70 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12816 100 : destroyPQExpBuffer(conprefix);
12817 : }
12818 :
12819 310 : destroyPQExpBuffer(q);
12820 310 : destroyPQExpBuffer(delq);
12821 310 : destroyPQExpBuffer(query);
12822 310 : free(qtypname);
12823 310 : free(qualtypname);
12824 310 : }
12825 :
12826 : /*
12827 : * dumpCompositeType
12828 : * writes out to fout the queries to recreate a user-defined stand-alone
12829 : * composite type
12830 : */
12831 : static void
12832 266 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12833 : {
12834 266 : DumpOptions *dopt = fout->dopt;
12835 266 : PQExpBuffer q = createPQExpBuffer();
12836 266 : PQExpBuffer dropped = createPQExpBuffer();
12837 266 : PQExpBuffer delq = createPQExpBuffer();
12838 266 : PQExpBuffer query = createPQExpBuffer();
12839 : PGresult *res;
12840 : char *qtypname;
12841 : char *qualtypname;
12842 : int ntups;
12843 : int i_attname;
12844 : int i_atttypdefn;
12845 : int i_attlen;
12846 : int i_attalign;
12847 : int i_attisdropped;
12848 : int i_attcollation;
12849 : int i;
12850 : int actual_atts;
12851 :
12852 266 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12853 : {
12854 : /*
12855 : * Set up query for type-specific details.
12856 : *
12857 : * Since we only want to dump COLLATE clauses for attributes whose
12858 : * collation is different from their type's default, we use a CASE
12859 : * here to suppress uninteresting attcollations cheaply. atttypid
12860 : * will be 0 for dropped columns; collation does not matter for those.
12861 : */
12862 116 : appendPQExpBufferStr(query,
12863 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12864 : "SELECT a.attname, a.attnum, "
12865 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12866 : "a.attlen, a.attalign, a.attisdropped, "
12867 : "CASE WHEN a.attcollation <> at.typcollation "
12868 : "THEN a.attcollation ELSE 0 END AS attcollation "
12869 : "FROM pg_catalog.pg_type ct "
12870 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12871 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12872 : "WHERE ct.oid = $1 "
12873 : "ORDER BY a.attnum");
12874 :
12875 116 : ExecuteSqlStatement(fout, query->data);
12876 :
12877 116 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12878 : }
12879 :
12880 266 : printfPQExpBuffer(query,
12881 : "EXECUTE dumpCompositeType('%u')",
12882 266 : tyinfo->dobj.catId.oid);
12883 :
12884 266 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12885 :
12886 266 : ntups = PQntuples(res);
12887 :
12888 266 : i_attname = PQfnumber(res, "attname");
12889 266 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12890 266 : i_attlen = PQfnumber(res, "attlen");
12891 266 : i_attalign = PQfnumber(res, "attalign");
12892 266 : i_attisdropped = PQfnumber(res, "attisdropped");
12893 266 : i_attcollation = PQfnumber(res, "attcollation");
12894 :
12895 266 : if (dopt->binary_upgrade)
12896 : {
12897 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12898 36 : tyinfo->dobj.catId.oid,
12899 : false, false);
12900 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12901 : }
12902 :
12903 266 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12904 266 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12905 :
12906 266 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12907 : qualtypname);
12908 :
12909 266 : actual_atts = 0;
12910 842 : for (i = 0; i < ntups; i++)
12911 : {
12912 : char *attname;
12913 : char *atttypdefn;
12914 : char *attlen;
12915 : char *attalign;
12916 : bool attisdropped;
12917 : Oid attcollation;
12918 :
12919 576 : attname = PQgetvalue(res, i, i_attname);
12920 576 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12921 576 : attlen = PQgetvalue(res, i, i_attlen);
12922 576 : attalign = PQgetvalue(res, i, i_attalign);
12923 576 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12924 576 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12925 :
12926 576 : if (attisdropped && !dopt->binary_upgrade)
12927 16 : continue;
12928 :
12929 : /* Format properly if not first attr */
12930 560 : if (actual_atts++ > 0)
12931 294 : appendPQExpBufferChar(q, ',');
12932 560 : appendPQExpBufferStr(q, "\n\t");
12933 :
12934 560 : if (!attisdropped)
12935 : {
12936 556 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12937 :
12938 : /* Add collation if not default for the column type */
12939 556 : if (OidIsValid(attcollation))
12940 : {
12941 : CollInfo *coll;
12942 :
12943 0 : coll = findCollationByOid(attcollation);
12944 0 : if (coll)
12945 0 : appendPQExpBuffer(q, " COLLATE %s",
12946 0 : fmtQualifiedDumpable(coll));
12947 : }
12948 : }
12949 : else
12950 : {
12951 : /*
12952 : * This is a dropped attribute and we're in binary_upgrade mode.
12953 : * Insert a placeholder for it in the CREATE TYPE command, and set
12954 : * length and alignment with direct UPDATE to the catalogs
12955 : * afterwards. See similar code in dumpTableSchema().
12956 : */
12957 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12958 :
12959 : /* stash separately for insertion after the CREATE TYPE */
12960 4 : appendPQExpBufferStr(dropped,
12961 : "\n-- For binary upgrade, recreate dropped column.\n");
12962 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12963 : "SET attlen = %s, "
12964 : "attalign = '%s', attbyval = false\n"
12965 : "WHERE attname = ", attlen, attalign);
12966 4 : appendStringLiteralAH(dropped, attname, fout);
12967 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12968 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12969 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12970 :
12971 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12972 : qualtypname);
12973 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12974 : fmtId(attname));
12975 : }
12976 : }
12977 266 : appendPQExpBufferStr(q, "\n);\n");
12978 266 : appendPQExpBufferStr(q, dropped->data);
12979 :
12980 266 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12981 :
12982 266 : if (dopt->binary_upgrade)
12983 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12984 : "TYPE", qtypname,
12985 36 : tyinfo->dobj.namespace->dobj.name);
12986 :
12987 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12988 232 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12989 232 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12990 : .namespace = tyinfo->dobj.namespace->dobj.name,
12991 : .owner = tyinfo->rolname,
12992 : .description = "TYPE",
12993 : .section = SECTION_PRE_DATA,
12994 : .createStmt = q->data,
12995 : .dropStmt = delq->data));
12996 :
12997 :
12998 : /* Dump Type Comments and Security Labels */
12999 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13000 70 : dumpComment(fout, "TYPE", qtypname,
13001 70 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13002 70 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13003 :
13004 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13005 0 : dumpSecLabel(fout, "TYPE", qtypname,
13006 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13007 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13008 :
13009 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13010 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13011 : qtypname, NULL,
13012 36 : tyinfo->dobj.namespace->dobj.name,
13013 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
13014 :
13015 : /* Dump any per-column comments */
13016 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13017 70 : dumpCompositeTypeColComments(fout, tyinfo, res);
13018 :
13019 266 : PQclear(res);
13020 266 : destroyPQExpBuffer(q);
13021 266 : destroyPQExpBuffer(dropped);
13022 266 : destroyPQExpBuffer(delq);
13023 266 : destroyPQExpBuffer(query);
13024 266 : free(qtypname);
13025 266 : free(qualtypname);
13026 266 : }
13027 :
13028 : /*
13029 : * dumpCompositeTypeColComments
13030 : * writes out to fout the queries to recreate comments on the columns of
13031 : * a user-defined stand-alone composite type.
13032 : *
13033 : * The caller has already made a query to collect the names and attnums
13034 : * of the type's columns, so we just pass that result into here rather
13035 : * than reading them again.
13036 : */
13037 : static void
13038 70 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13039 : PGresult *res)
13040 : {
13041 : CommentItem *comments;
13042 : int ncomments;
13043 : PQExpBuffer query;
13044 : PQExpBuffer target;
13045 : int i;
13046 : int ntups;
13047 : int i_attname;
13048 : int i_attnum;
13049 : int i_attisdropped;
13050 :
13051 : /* do nothing, if --no-comments is supplied */
13052 70 : if (fout->dopt->no_comments)
13053 0 : return;
13054 :
13055 : /* Search for comments associated with type's pg_class OID */
13056 70 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13057 : &comments);
13058 :
13059 : /* If no comments exist, we're done */
13060 70 : if (ncomments <= 0)
13061 0 : return;
13062 :
13063 : /* Build COMMENT ON statements */
13064 70 : query = createPQExpBuffer();
13065 70 : target = createPQExpBuffer();
13066 :
13067 70 : ntups = PQntuples(res);
13068 70 : i_attnum = PQfnumber(res, "attnum");
13069 70 : i_attname = PQfnumber(res, "attname");
13070 70 : i_attisdropped = PQfnumber(res, "attisdropped");
13071 140 : while (ncomments > 0)
13072 : {
13073 : const char *attname;
13074 :
13075 70 : attname = NULL;
13076 70 : for (i = 0; i < ntups; i++)
13077 : {
13078 70 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13079 70 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13080 : {
13081 70 : attname = PQgetvalue(res, i, i_attname);
13082 70 : break;
13083 : }
13084 : }
13085 70 : if (attname) /* just in case we don't find it */
13086 : {
13087 70 : const char *descr = comments->descr;
13088 :
13089 70 : resetPQExpBuffer(target);
13090 70 : appendPQExpBuffer(target, "COLUMN %s.",
13091 70 : fmtId(tyinfo->dobj.name));
13092 70 : appendPQExpBufferStr(target, fmtId(attname));
13093 :
13094 70 : resetPQExpBuffer(query);
13095 70 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13096 70 : fmtQualifiedDumpable(tyinfo));
13097 70 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13098 70 : appendStringLiteralAH(query, descr, fout);
13099 70 : appendPQExpBufferStr(query, ";\n");
13100 :
13101 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13102 70 : ARCHIVE_OPTS(.tag = target->data,
13103 : .namespace = tyinfo->dobj.namespace->dobj.name,
13104 : .owner = tyinfo->rolname,
13105 : .description = "COMMENT",
13106 : .section = SECTION_NONE,
13107 : .createStmt = query->data,
13108 : .deps = &(tyinfo->dobj.dumpId),
13109 : .nDeps = 1));
13110 : }
13111 :
13112 70 : comments++;
13113 70 : ncomments--;
13114 : }
13115 :
13116 70 : destroyPQExpBuffer(query);
13117 70 : destroyPQExpBuffer(target);
13118 : }
13119 :
13120 : /*
13121 : * dumpShellType
13122 : * writes out to fout the queries to create a shell type
13123 : *
13124 : * We dump a shell definition in advance of the I/O functions for the type.
13125 : */
13126 : static void
13127 152 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13128 : {
13129 152 : DumpOptions *dopt = fout->dopt;
13130 : PQExpBuffer q;
13131 :
13132 : /* Do nothing if not dumping schema */
13133 152 : if (!dopt->dumpSchema)
13134 12 : return;
13135 :
13136 140 : q = createPQExpBuffer();
13137 :
13138 : /*
13139 : * Note the lack of a DROP command for the shell type; any required DROP
13140 : * is driven off the base type entry, instead. This interacts with
13141 : * _printTocEntry()'s use of the presence of a DROP command to decide
13142 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13143 : * the shell type's owner immediately on creation; that should happen only
13144 : * after it's filled in, otherwise the backend complains.
13145 : */
13146 :
13147 140 : if (dopt->binary_upgrade)
13148 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13149 16 : stinfo->baseType->dobj.catId.oid,
13150 : false, false);
13151 :
13152 140 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13153 140 : fmtQualifiedDumpable(stinfo));
13154 :
13155 140 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13156 140 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13157 140 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13158 : .namespace = stinfo->dobj.namespace->dobj.name,
13159 : .owner = stinfo->baseType->rolname,
13160 : .description = "SHELL TYPE",
13161 : .section = SECTION_PRE_DATA,
13162 : .createStmt = q->data));
13163 :
13164 140 : destroyPQExpBuffer(q);
13165 : }
13166 :
13167 : /*
13168 : * dumpProcLang
13169 : * writes out to fout the queries to recreate a user-defined
13170 : * procedural language
13171 : */
13172 : static void
13173 176 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13174 : {
13175 176 : DumpOptions *dopt = fout->dopt;
13176 : PQExpBuffer defqry;
13177 : PQExpBuffer delqry;
13178 : bool useParams;
13179 : char *qlanname;
13180 : FuncInfo *funcInfo;
13181 176 : FuncInfo *inlineInfo = NULL;
13182 176 : FuncInfo *validatorInfo = NULL;
13183 :
13184 : /* Do nothing if not dumping schema */
13185 176 : if (!dopt->dumpSchema)
13186 26 : return;
13187 :
13188 : /*
13189 : * Try to find the support function(s). It is not an error if we don't
13190 : * find them --- if the functions are in the pg_catalog schema, as is
13191 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13192 : * we will emit a parameterless CREATE LANGUAGE command, which will
13193 : * require PL template knowledge in the backend to reload.)
13194 : */
13195 :
13196 150 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13197 150 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13198 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13199 :
13200 150 : if (OidIsValid(plang->laninline))
13201 : {
13202 82 : inlineInfo = findFuncByOid(plang->laninline);
13203 82 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13204 2 : inlineInfo = NULL;
13205 : }
13206 :
13207 150 : if (OidIsValid(plang->lanvalidator))
13208 : {
13209 82 : validatorInfo = findFuncByOid(plang->lanvalidator);
13210 82 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13211 2 : validatorInfo = NULL;
13212 : }
13213 :
13214 : /*
13215 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13216 : * parameters. Otherwise, we'll write a parameterless command, which will
13217 : * be interpreted as CREATE EXTENSION.
13218 : */
13219 66 : useParams = (funcInfo != NULL &&
13220 282 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13221 66 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13222 :
13223 150 : defqry = createPQExpBuffer();
13224 150 : delqry = createPQExpBuffer();
13225 :
13226 150 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13227 :
13228 150 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13229 : qlanname);
13230 :
13231 150 : if (useParams)
13232 : {
13233 66 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13234 66 : plang->lanpltrusted ? "TRUSTED " : "",
13235 : qlanname);
13236 66 : appendPQExpBuffer(defqry, " HANDLER %s",
13237 66 : fmtQualifiedDumpable(funcInfo));
13238 66 : if (OidIsValid(plang->laninline))
13239 0 : appendPQExpBuffer(defqry, " INLINE %s",
13240 0 : fmtQualifiedDumpable(inlineInfo));
13241 66 : if (OidIsValid(plang->lanvalidator))
13242 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13243 0 : fmtQualifiedDumpable(validatorInfo));
13244 : }
13245 : else
13246 : {
13247 : /*
13248 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13249 : * command will not fail if the language is preinstalled in the target
13250 : * database.
13251 : *
13252 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13253 : * EXISTS; perhaps we should emit that instead? But it might just add
13254 : * confusion.
13255 : */
13256 84 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13257 : qlanname);
13258 : }
13259 150 : appendPQExpBufferStr(defqry, ";\n");
13260 :
13261 150 : if (dopt->binary_upgrade)
13262 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13263 : "LANGUAGE", qlanname, NULL);
13264 :
13265 150 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13266 68 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13267 68 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13268 : .owner = plang->lanowner,
13269 : .description = "PROCEDURAL LANGUAGE",
13270 : .section = SECTION_PRE_DATA,
13271 : .createStmt = defqry->data,
13272 : .dropStmt = delqry->data,
13273 : ));
13274 :
13275 : /* Dump Proc Lang Comments and Security Labels */
13276 150 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13277 0 : dumpComment(fout, "LANGUAGE", qlanname,
13278 0 : NULL, plang->lanowner,
13279 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13280 :
13281 150 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13282 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13283 0 : NULL, plang->lanowner,
13284 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13285 :
13286 150 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13287 82 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13288 : qlanname, NULL, NULL,
13289 82 : NULL, plang->lanowner, &plang->dacl);
13290 :
13291 150 : free(qlanname);
13292 :
13293 150 : destroyPQExpBuffer(defqry);
13294 150 : destroyPQExpBuffer(delqry);
13295 : }
13296 :
13297 : /*
13298 : * format_function_arguments: generate function name and argument list
13299 : *
13300 : * This is used when we can rely on pg_get_function_arguments to format
13301 : * the argument list. Note, however, that pg_get_function_arguments
13302 : * does not special-case zero-argument aggregates.
13303 : */
13304 : static char *
13305 8276 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13306 : {
13307 : PQExpBufferData fn;
13308 :
13309 8276 : initPQExpBuffer(&fn);
13310 8276 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13311 8276 : if (is_agg && finfo->nargs == 0)
13312 160 : appendPQExpBufferStr(&fn, "(*)");
13313 : else
13314 8116 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13315 8276 : return fn.data;
13316 : }
13317 :
13318 : /*
13319 : * format_function_signature: generate function name and argument list
13320 : *
13321 : * Only a minimal list of input argument types is generated; this is
13322 : * sufficient to reference the function, but not to define it.
13323 : *
13324 : * If honor_quotes is false then the function name is never quoted.
13325 : * This is appropriate for use in TOC tags, but not in SQL commands.
13326 : */
13327 : static char *
13328 4372 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13329 : {
13330 : PQExpBufferData fn;
13331 : int j;
13332 :
13333 4372 : initPQExpBuffer(&fn);
13334 4372 : if (honor_quotes)
13335 810 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13336 : else
13337 3562 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13338 8012 : for (j = 0; j < finfo->nargs; j++)
13339 : {
13340 3640 : if (j > 0)
13341 844 : appendPQExpBufferStr(&fn, ", ");
13342 :
13343 3640 : appendPQExpBufferStr(&fn,
13344 3640 : getFormattedTypeName(fout, finfo->argtypes[j],
13345 : zeroIsError));
13346 : }
13347 4372 : appendPQExpBufferChar(&fn, ')');
13348 4372 : return fn.data;
13349 : }
13350 :
13351 :
13352 : /*
13353 : * dumpFunc:
13354 : * dump out one function
13355 : */
13356 : static void
13357 3686 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13358 : {
13359 3686 : DumpOptions *dopt = fout->dopt;
13360 : PQExpBuffer query;
13361 : PQExpBuffer q;
13362 : PQExpBuffer delqry;
13363 : PQExpBuffer asPart;
13364 : PGresult *res;
13365 : char *funcsig; /* identity signature */
13366 3686 : char *funcfullsig = NULL; /* full signature */
13367 : char *funcsig_tag;
13368 : char *qual_funcsig;
13369 : char *proretset;
13370 : char *prosrc;
13371 : char *probin;
13372 : char *prosqlbody;
13373 : char *funcargs;
13374 : char *funciargs;
13375 : char *funcresult;
13376 : char *protrftypes;
13377 : char *prokind;
13378 : char *provolatile;
13379 : char *proisstrict;
13380 : char *prosecdef;
13381 : char *proleakproof;
13382 : char *proconfig;
13383 : char *procost;
13384 : char *prorows;
13385 : char *prosupport;
13386 : char *proparallel;
13387 : char *lanname;
13388 3686 : char **configitems = NULL;
13389 3686 : int nconfigitems = 0;
13390 : const char *keyword;
13391 :
13392 : /* Do nothing if not dumping schema */
13393 3686 : if (!dopt->dumpSchema)
13394 124 : return;
13395 :
13396 3562 : query = createPQExpBuffer();
13397 3562 : q = createPQExpBuffer();
13398 3562 : delqry = createPQExpBuffer();
13399 3562 : asPart = createPQExpBuffer();
13400 :
13401 3562 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13402 : {
13403 : /* Set up query for function-specific details */
13404 128 : appendPQExpBufferStr(query,
13405 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13406 :
13407 128 : appendPQExpBufferStr(query,
13408 : "SELECT\n"
13409 : "proretset,\n"
13410 : "prosrc,\n"
13411 : "probin,\n"
13412 : "provolatile,\n"
13413 : "proisstrict,\n"
13414 : "prosecdef,\n"
13415 : "lanname,\n"
13416 : "proconfig,\n"
13417 : "procost,\n"
13418 : "prorows,\n"
13419 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13420 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13421 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13422 : "proleakproof,\n");
13423 :
13424 128 : if (fout->remoteVersion >= 90500)
13425 128 : appendPQExpBufferStr(query,
13426 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13427 : else
13428 0 : appendPQExpBufferStr(query,
13429 : "NULL AS protrftypes,\n");
13430 :
13431 128 : if (fout->remoteVersion >= 90600)
13432 128 : appendPQExpBufferStr(query,
13433 : "proparallel,\n");
13434 : else
13435 0 : appendPQExpBufferStr(query,
13436 : "'u' AS proparallel,\n");
13437 :
13438 128 : if (fout->remoteVersion >= 110000)
13439 128 : appendPQExpBufferStr(query,
13440 : "prokind,\n");
13441 : else
13442 0 : appendPQExpBufferStr(query,
13443 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13444 :
13445 128 : if (fout->remoteVersion >= 120000)
13446 128 : appendPQExpBufferStr(query,
13447 : "prosupport,\n");
13448 : else
13449 0 : appendPQExpBufferStr(query,
13450 : "'-' AS prosupport,\n");
13451 :
13452 128 : if (fout->remoteVersion >= 140000)
13453 128 : appendPQExpBufferStr(query,
13454 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13455 : else
13456 0 : appendPQExpBufferStr(query,
13457 : "NULL AS prosqlbody\n");
13458 :
13459 128 : appendPQExpBufferStr(query,
13460 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13461 : "WHERE p.oid = $1 "
13462 : "AND l.oid = p.prolang");
13463 :
13464 128 : ExecuteSqlStatement(fout, query->data);
13465 :
13466 128 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13467 : }
13468 :
13469 3562 : printfPQExpBuffer(query,
13470 : "EXECUTE dumpFunc('%u')",
13471 3562 : finfo->dobj.catId.oid);
13472 :
13473 3562 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13474 :
13475 3562 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13476 3562 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13477 : {
13478 3460 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13479 3460 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13480 3460 : prosqlbody = NULL;
13481 : }
13482 : else
13483 : {
13484 102 : prosrc = NULL;
13485 102 : probin = NULL;
13486 102 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13487 : }
13488 3562 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13489 3562 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13490 3562 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13491 3562 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13492 3562 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13493 3562 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13494 3562 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13495 3562 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13496 3562 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13497 3562 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13498 3562 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13499 3562 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13500 3562 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13501 3562 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13502 3562 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13503 :
13504 : /*
13505 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13506 : * is used.
13507 : */
13508 3562 : if (prosqlbody)
13509 : {
13510 102 : appendPQExpBufferStr(asPart, prosqlbody);
13511 : }
13512 3460 : else if (probin[0] != '\0')
13513 : {
13514 270 : appendPQExpBufferStr(asPart, "AS ");
13515 270 : appendStringLiteralAH(asPart, probin, fout);
13516 270 : if (prosrc[0] != '\0')
13517 : {
13518 270 : appendPQExpBufferStr(asPart, ", ");
13519 :
13520 : /*
13521 : * where we have bin, use dollar quoting if allowed and src
13522 : * contains quote or backslash; else use regular quoting.
13523 : */
13524 270 : if (dopt->disable_dollar_quoting ||
13525 270 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13526 270 : appendStringLiteralAH(asPart, prosrc, fout);
13527 : else
13528 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13529 : }
13530 : }
13531 : else
13532 : {
13533 3190 : appendPQExpBufferStr(asPart, "AS ");
13534 : /* with no bin, dollar quote src unconditionally if allowed */
13535 3190 : if (dopt->disable_dollar_quoting)
13536 0 : appendStringLiteralAH(asPart, prosrc, fout);
13537 : else
13538 3190 : appendStringLiteralDQ(asPart, prosrc, NULL);
13539 : }
13540 :
13541 3562 : if (*proconfig)
13542 : {
13543 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13544 0 : pg_fatal("could not parse %s array", "proconfig");
13545 : }
13546 : else
13547 : {
13548 3532 : configitems = NULL;
13549 3532 : nconfigitems = 0;
13550 : }
13551 :
13552 3562 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13553 3562 : funcsig = format_function_arguments(finfo, funciargs, false);
13554 :
13555 3562 : funcsig_tag = format_function_signature(fout, finfo, false);
13556 :
13557 3562 : qual_funcsig = psprintf("%s.%s",
13558 3562 : fmtId(finfo->dobj.namespace->dobj.name),
13559 : funcsig);
13560 :
13561 3562 : if (prokind[0] == PROKIND_PROCEDURE)
13562 190 : keyword = "PROCEDURE";
13563 : else
13564 3372 : keyword = "FUNCTION"; /* works for window functions too */
13565 :
13566 3562 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13567 : keyword, qual_funcsig);
13568 :
13569 7124 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13570 : keyword,
13571 3562 : fmtId(finfo->dobj.namespace->dobj.name),
13572 : funcfullsig ? funcfullsig :
13573 : funcsig);
13574 :
13575 3562 : if (prokind[0] == PROKIND_PROCEDURE)
13576 : /* no result type to output */ ;
13577 3372 : else if (funcresult)
13578 3372 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13579 : else
13580 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13581 0 : (proretset[0] == 't') ? "SETOF " : "",
13582 0 : getFormattedTypeName(fout, finfo->prorettype,
13583 : zeroIsError));
13584 :
13585 3562 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13586 :
13587 3562 : if (*protrftypes)
13588 : {
13589 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13590 : int i;
13591 :
13592 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13593 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13594 0 : for (i = 0; typeids[i]; i++)
13595 : {
13596 0 : if (i != 0)
13597 0 : appendPQExpBufferStr(q, ", ");
13598 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13599 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13600 : }
13601 :
13602 0 : free(typeids);
13603 : }
13604 :
13605 3562 : if (prokind[0] == PROKIND_WINDOW)
13606 10 : appendPQExpBufferStr(q, " WINDOW");
13607 :
13608 3562 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13609 : {
13610 714 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13611 672 : appendPQExpBufferStr(q, " IMMUTABLE");
13612 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13613 42 : appendPQExpBufferStr(q, " STABLE");
13614 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13615 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13616 : finfo->dobj.name);
13617 : }
13618 :
13619 3562 : if (proisstrict[0] == 't')
13620 724 : appendPQExpBufferStr(q, " STRICT");
13621 :
13622 3562 : if (prosecdef[0] == 't')
13623 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13624 :
13625 3562 : if (proleakproof[0] == 't')
13626 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13627 :
13628 : /*
13629 : * COST and ROWS are emitted only if present and not default, so as not to
13630 : * break backwards-compatibility of the dump without need. Keep this code
13631 : * in sync with the defaults in functioncmds.c.
13632 : */
13633 3562 : if (strcmp(procost, "0") != 0)
13634 : {
13635 3562 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13636 : {
13637 : /* default cost is 1 */
13638 752 : if (strcmp(procost, "1") != 0)
13639 0 : appendPQExpBuffer(q, " COST %s", procost);
13640 : }
13641 : else
13642 : {
13643 : /* default cost is 100 */
13644 2810 : if (strcmp(procost, "100") != 0)
13645 12 : appendPQExpBuffer(q, " COST %s", procost);
13646 : }
13647 : }
13648 3562 : if (proretset[0] == 't' &&
13649 380 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13650 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13651 :
13652 3562 : if (strcmp(prosupport, "-") != 0)
13653 : {
13654 : /* We rely on regprocout to provide quoting and qualification */
13655 90 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13656 : }
13657 :
13658 3562 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13659 : {
13660 244 : if (proparallel[0] == PROPARALLEL_SAFE)
13661 234 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13662 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13663 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13664 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13665 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13666 : finfo->dobj.name);
13667 : }
13668 :
13669 3632 : for (int i = 0; i < nconfigitems; i++)
13670 : {
13671 : /* we feel free to scribble on configitems[] here */
13672 70 : char *configitem = configitems[i];
13673 : char *pos;
13674 :
13675 70 : pos = strchr(configitem, '=');
13676 70 : if (pos == NULL)
13677 0 : continue;
13678 70 : *pos++ = '\0';
13679 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13680 :
13681 : /*
13682 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13683 : * by flatten_set_variable_args() before they were put into the
13684 : * proconfig array. However, because the quoting rules used there
13685 : * aren't exactly like SQL's, we have to break the list value apart
13686 : * and then quote the elements as string literals. (The elements may
13687 : * be double-quoted as-is, but we can't just feed them to the SQL
13688 : * parser; it would do the wrong thing with elements that are
13689 : * zero-length or longer than NAMEDATALEN.)
13690 : *
13691 : * Variables that are not so marked should just be emitted as simple
13692 : * string literals. If the variable is not known to
13693 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13694 : * to use GUC_LIST_QUOTE for extension variables.
13695 : */
13696 70 : if (variable_is_guc_list_quote(configitem))
13697 : {
13698 : char **namelist;
13699 : char **nameptr;
13700 :
13701 : /* Parse string into list of identifiers */
13702 : /* this shouldn't fail really */
13703 20 : if (SplitGUCList(pos, ',', &namelist))
13704 : {
13705 70 : for (nameptr = namelist; *nameptr; nameptr++)
13706 : {
13707 50 : if (nameptr != namelist)
13708 30 : appendPQExpBufferStr(q, ", ");
13709 50 : appendStringLiteralAH(q, *nameptr, fout);
13710 : }
13711 : }
13712 20 : pg_free(namelist);
13713 : }
13714 : else
13715 50 : appendStringLiteralAH(q, pos, fout);
13716 : }
13717 :
13718 3562 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13719 :
13720 3562 : append_depends_on_extension(fout, q, &finfo->dobj,
13721 : "pg_catalog.pg_proc", keyword,
13722 : qual_funcsig);
13723 :
13724 3562 : if (dopt->binary_upgrade)
13725 576 : binary_upgrade_extension_member(q, &finfo->dobj,
13726 : keyword, funcsig,
13727 576 : finfo->dobj.namespace->dobj.name);
13728 :
13729 3562 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13730 3358 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13731 3358 : ARCHIVE_OPTS(.tag = funcsig_tag,
13732 : .namespace = finfo->dobj.namespace->dobj.name,
13733 : .owner = finfo->rolname,
13734 : .description = keyword,
13735 : .section = finfo->postponed_def ?
13736 : SECTION_POST_DATA : SECTION_PRE_DATA,
13737 : .createStmt = q->data,
13738 : .dropStmt = delqry->data));
13739 :
13740 : /* Dump Function Comments and Security Labels */
13741 3562 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13742 18 : dumpComment(fout, keyword, funcsig,
13743 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13744 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13745 :
13746 3562 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13747 0 : dumpSecLabel(fout, keyword, funcsig,
13748 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13749 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13750 :
13751 3562 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13752 212 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13753 : funcsig, NULL,
13754 212 : finfo->dobj.namespace->dobj.name,
13755 212 : NULL, finfo->rolname, &finfo->dacl);
13756 :
13757 3562 : PQclear(res);
13758 :
13759 3562 : destroyPQExpBuffer(query);
13760 3562 : destroyPQExpBuffer(q);
13761 3562 : destroyPQExpBuffer(delqry);
13762 3562 : destroyPQExpBuffer(asPart);
13763 3562 : free(funcsig);
13764 3562 : free(funcfullsig);
13765 3562 : free(funcsig_tag);
13766 3562 : free(qual_funcsig);
13767 3562 : free(configitems);
13768 : }
13769 :
13770 :
13771 : /*
13772 : * Dump a user-defined cast
13773 : */
13774 : static void
13775 140 : dumpCast(Archive *fout, const CastInfo *cast)
13776 : {
13777 140 : DumpOptions *dopt = fout->dopt;
13778 : PQExpBuffer defqry;
13779 : PQExpBuffer delqry;
13780 : PQExpBuffer labelq;
13781 : PQExpBuffer castargs;
13782 140 : FuncInfo *funcInfo = NULL;
13783 : const char *sourceType;
13784 : const char *targetType;
13785 :
13786 : /* Do nothing if not dumping schema */
13787 140 : if (!dopt->dumpSchema)
13788 12 : return;
13789 :
13790 : /* Cannot dump if we don't have the cast function's info */
13791 128 : if (OidIsValid(cast->castfunc))
13792 : {
13793 78 : funcInfo = findFuncByOid(cast->castfunc);
13794 78 : if (funcInfo == NULL)
13795 0 : pg_fatal("could not find function definition for function with OID %u",
13796 : cast->castfunc);
13797 : }
13798 :
13799 128 : defqry = createPQExpBuffer();
13800 128 : delqry = createPQExpBuffer();
13801 128 : labelq = createPQExpBuffer();
13802 128 : castargs = createPQExpBuffer();
13803 :
13804 128 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13805 128 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13806 128 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13807 : sourceType, targetType);
13808 :
13809 128 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13810 : sourceType, targetType);
13811 :
13812 128 : switch (cast->castmethod)
13813 : {
13814 50 : case COERCION_METHOD_BINARY:
13815 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13816 50 : break;
13817 0 : case COERCION_METHOD_INOUT:
13818 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13819 0 : break;
13820 78 : case COERCION_METHOD_FUNCTION:
13821 78 : if (funcInfo)
13822 : {
13823 78 : char *fsig = format_function_signature(fout, funcInfo, true);
13824 :
13825 : /*
13826 : * Always qualify the function name (format_function_signature
13827 : * won't qualify it).
13828 : */
13829 78 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13830 78 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13831 78 : free(fsig);
13832 : }
13833 : else
13834 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13835 78 : break;
13836 0 : default:
13837 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13838 : }
13839 :
13840 128 : if (cast->castcontext == 'a')
13841 68 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13842 60 : else if (cast->castcontext == 'i')
13843 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13844 128 : appendPQExpBufferStr(defqry, ";\n");
13845 :
13846 128 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13847 : sourceType, targetType);
13848 :
13849 128 : appendPQExpBuffer(castargs, "(%s AS %s)",
13850 : sourceType, targetType);
13851 :
13852 128 : if (dopt->binary_upgrade)
13853 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13854 14 : "CAST", castargs->data, NULL);
13855 :
13856 128 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13857 128 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13858 128 : ARCHIVE_OPTS(.tag = labelq->data,
13859 : .description = "CAST",
13860 : .section = SECTION_PRE_DATA,
13861 : .createStmt = defqry->data,
13862 : .dropStmt = delqry->data));
13863 :
13864 : /* Dump Cast Comments */
13865 128 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13866 0 : dumpComment(fout, "CAST", castargs->data,
13867 : NULL, "",
13868 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13869 :
13870 128 : destroyPQExpBuffer(defqry);
13871 128 : destroyPQExpBuffer(delqry);
13872 128 : destroyPQExpBuffer(labelq);
13873 128 : destroyPQExpBuffer(castargs);
13874 : }
13875 :
13876 : /*
13877 : * Dump a transform
13878 : */
13879 : static void
13880 90 : dumpTransform(Archive *fout, const TransformInfo *transform)
13881 : {
13882 90 : DumpOptions *dopt = fout->dopt;
13883 : PQExpBuffer defqry;
13884 : PQExpBuffer delqry;
13885 : PQExpBuffer labelq;
13886 : PQExpBuffer transformargs;
13887 90 : FuncInfo *fromsqlFuncInfo = NULL;
13888 90 : FuncInfo *tosqlFuncInfo = NULL;
13889 : char *lanname;
13890 : const char *transformType;
13891 :
13892 : /* Do nothing if not dumping schema */
13893 90 : if (!dopt->dumpSchema)
13894 12 : return;
13895 :
13896 : /* Cannot dump if we don't have the transform functions' info */
13897 78 : if (OidIsValid(transform->trffromsql))
13898 : {
13899 78 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13900 78 : if (fromsqlFuncInfo == NULL)
13901 0 : pg_fatal("could not find function definition for function with OID %u",
13902 : transform->trffromsql);
13903 : }
13904 78 : if (OidIsValid(transform->trftosql))
13905 : {
13906 78 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13907 78 : if (tosqlFuncInfo == NULL)
13908 0 : pg_fatal("could not find function definition for function with OID %u",
13909 : transform->trftosql);
13910 : }
13911 :
13912 78 : defqry = createPQExpBuffer();
13913 78 : delqry = createPQExpBuffer();
13914 78 : labelq = createPQExpBuffer();
13915 78 : transformargs = createPQExpBuffer();
13916 :
13917 78 : lanname = get_language_name(fout, transform->trflang);
13918 78 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13919 :
13920 78 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13921 : transformType, lanname);
13922 :
13923 78 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13924 : transformType, lanname);
13925 :
13926 78 : if (!transform->trffromsql && !transform->trftosql)
13927 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13928 :
13929 78 : if (transform->trffromsql)
13930 : {
13931 78 : if (fromsqlFuncInfo)
13932 : {
13933 78 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13934 :
13935 : /*
13936 : * Always qualify the function name (format_function_signature
13937 : * won't qualify it).
13938 : */
13939 78 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13940 78 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13941 78 : free(fsig);
13942 : }
13943 : else
13944 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13945 : }
13946 :
13947 78 : if (transform->trftosql)
13948 : {
13949 78 : if (transform->trffromsql)
13950 78 : appendPQExpBufferStr(defqry, ", ");
13951 :
13952 78 : if (tosqlFuncInfo)
13953 : {
13954 78 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13955 :
13956 : /*
13957 : * Always qualify the function name (format_function_signature
13958 : * won't qualify it).
13959 : */
13960 78 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13961 78 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13962 78 : free(fsig);
13963 : }
13964 : else
13965 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13966 : }
13967 :
13968 78 : appendPQExpBufferStr(defqry, ");\n");
13969 :
13970 78 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13971 : transformType, lanname);
13972 :
13973 78 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13974 : transformType, lanname);
13975 :
13976 78 : if (dopt->binary_upgrade)
13977 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13978 4 : "TRANSFORM", transformargs->data, NULL);
13979 :
13980 78 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13981 78 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13982 78 : ARCHIVE_OPTS(.tag = labelq->data,
13983 : .description = "TRANSFORM",
13984 : .section = SECTION_PRE_DATA,
13985 : .createStmt = defqry->data,
13986 : .dropStmt = delqry->data,
13987 : .deps = transform->dobj.dependencies,
13988 : .nDeps = transform->dobj.nDeps));
13989 :
13990 : /* Dump Transform Comments */
13991 78 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13992 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13993 : NULL, "",
13994 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
13995 :
13996 78 : free(lanname);
13997 78 : destroyPQExpBuffer(defqry);
13998 78 : destroyPQExpBuffer(delqry);
13999 78 : destroyPQExpBuffer(labelq);
14000 78 : destroyPQExpBuffer(transformargs);
14001 : }
14002 :
14003 :
14004 : /*
14005 : * dumpOpr
14006 : * write out a single operator definition
14007 : */
14008 : static void
14009 5014 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14010 : {
14011 5014 : DumpOptions *dopt = fout->dopt;
14012 : PQExpBuffer query;
14013 : PQExpBuffer q;
14014 : PQExpBuffer delq;
14015 : PQExpBuffer oprid;
14016 : PQExpBuffer details;
14017 : PGresult *res;
14018 : int i_oprkind;
14019 : int i_oprcode;
14020 : int i_oprleft;
14021 : int i_oprright;
14022 : int i_oprcom;
14023 : int i_oprnegate;
14024 : int i_oprrest;
14025 : int i_oprjoin;
14026 : int i_oprcanmerge;
14027 : int i_oprcanhash;
14028 : char *oprkind;
14029 : char *oprcode;
14030 : char *oprleft;
14031 : char *oprright;
14032 : char *oprcom;
14033 : char *oprnegate;
14034 : char *oprrest;
14035 : char *oprjoin;
14036 : char *oprcanmerge;
14037 : char *oprcanhash;
14038 : char *oprregproc;
14039 : char *oprref;
14040 :
14041 : /* Do nothing if not dumping schema */
14042 5014 : if (!dopt->dumpSchema)
14043 12 : return;
14044 :
14045 : /*
14046 : * some operators are invalid because they were the result of user
14047 : * defining operators before commutators exist
14048 : */
14049 5002 : if (!OidIsValid(oprinfo->oprcode))
14050 28 : return;
14051 :
14052 4974 : query = createPQExpBuffer();
14053 4974 : q = createPQExpBuffer();
14054 4974 : delq = createPQExpBuffer();
14055 4974 : oprid = createPQExpBuffer();
14056 4974 : details = createPQExpBuffer();
14057 :
14058 4974 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14059 : {
14060 : /* Set up query for operator-specific details */
14061 86 : appendPQExpBufferStr(query,
14062 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14063 : "SELECT oprkind, "
14064 : "oprcode::pg_catalog.regprocedure, "
14065 : "oprleft::pg_catalog.regtype, "
14066 : "oprright::pg_catalog.regtype, "
14067 : "oprcom, "
14068 : "oprnegate, "
14069 : "oprrest::pg_catalog.regprocedure, "
14070 : "oprjoin::pg_catalog.regprocedure, "
14071 : "oprcanmerge, oprcanhash "
14072 : "FROM pg_catalog.pg_operator "
14073 : "WHERE oid = $1");
14074 :
14075 86 : ExecuteSqlStatement(fout, query->data);
14076 :
14077 86 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14078 : }
14079 :
14080 4974 : printfPQExpBuffer(query,
14081 : "EXECUTE dumpOpr('%u')",
14082 4974 : oprinfo->dobj.catId.oid);
14083 :
14084 4974 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14085 :
14086 4974 : i_oprkind = PQfnumber(res, "oprkind");
14087 4974 : i_oprcode = PQfnumber(res, "oprcode");
14088 4974 : i_oprleft = PQfnumber(res, "oprleft");
14089 4974 : i_oprright = PQfnumber(res, "oprright");
14090 4974 : i_oprcom = PQfnumber(res, "oprcom");
14091 4974 : i_oprnegate = PQfnumber(res, "oprnegate");
14092 4974 : i_oprrest = PQfnumber(res, "oprrest");
14093 4974 : i_oprjoin = PQfnumber(res, "oprjoin");
14094 4974 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14095 4974 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14096 :
14097 4974 : oprkind = PQgetvalue(res, 0, i_oprkind);
14098 4974 : oprcode = PQgetvalue(res, 0, i_oprcode);
14099 4974 : oprleft = PQgetvalue(res, 0, i_oprleft);
14100 4974 : oprright = PQgetvalue(res, 0, i_oprright);
14101 4974 : oprcom = PQgetvalue(res, 0, i_oprcom);
14102 4974 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14103 4974 : oprrest = PQgetvalue(res, 0, i_oprrest);
14104 4974 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14105 4974 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14106 4974 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14107 :
14108 : /* In PG14 upwards postfix operator support does not exist anymore. */
14109 4974 : if (strcmp(oprkind, "r") == 0)
14110 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14111 : oprcode);
14112 :
14113 4974 : oprregproc = convertRegProcReference(oprcode);
14114 4974 : if (oprregproc)
14115 : {
14116 4974 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14117 4974 : free(oprregproc);
14118 : }
14119 :
14120 4974 : appendPQExpBuffer(oprid, "%s (",
14121 4974 : oprinfo->dobj.name);
14122 :
14123 : /*
14124 : * right unary means there's a left arg and left unary means there's a
14125 : * right arg. (Although the "r" case is dead code for PG14 and later,
14126 : * continue to support it in case we're dumping from an old server.)
14127 : */
14128 4974 : if (strcmp(oprkind, "r") == 0 ||
14129 4974 : strcmp(oprkind, "b") == 0)
14130 : {
14131 4688 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14132 4688 : appendPQExpBufferStr(oprid, oprleft);
14133 : }
14134 : else
14135 286 : appendPQExpBufferStr(oprid, "NONE");
14136 :
14137 4974 : if (strcmp(oprkind, "l") == 0 ||
14138 4688 : strcmp(oprkind, "b") == 0)
14139 : {
14140 4974 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14141 4974 : appendPQExpBuffer(oprid, ", %s)", oprright);
14142 : }
14143 : else
14144 0 : appendPQExpBufferStr(oprid, ", NONE)");
14145 :
14146 4974 : oprref = getFormattedOperatorName(oprcom);
14147 4974 : if (oprref)
14148 : {
14149 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14150 3322 : free(oprref);
14151 : }
14152 :
14153 4974 : oprref = getFormattedOperatorName(oprnegate);
14154 4974 : if (oprref)
14155 : {
14156 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14157 2326 : free(oprref);
14158 : }
14159 :
14160 4974 : if (strcmp(oprcanmerge, "t") == 0)
14161 370 : appendPQExpBufferStr(details, ",\n MERGES");
14162 :
14163 4974 : if (strcmp(oprcanhash, "t") == 0)
14164 276 : appendPQExpBufferStr(details, ",\n HASHES");
14165 :
14166 4974 : oprregproc = convertRegProcReference(oprrest);
14167 4974 : if (oprregproc)
14168 : {
14169 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14170 3028 : free(oprregproc);
14171 : }
14172 :
14173 4974 : oprregproc = convertRegProcReference(oprjoin);
14174 4974 : if (oprregproc)
14175 : {
14176 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14177 3028 : free(oprregproc);
14178 : }
14179 :
14180 4974 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14181 4974 : fmtId(oprinfo->dobj.namespace->dobj.name),
14182 : oprid->data);
14183 :
14184 4974 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14185 4974 : fmtId(oprinfo->dobj.namespace->dobj.name),
14186 4974 : oprinfo->dobj.name, details->data);
14187 :
14188 4974 : if (dopt->binary_upgrade)
14189 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14190 24 : "OPERATOR", oprid->data,
14191 24 : oprinfo->dobj.namespace->dobj.name);
14192 :
14193 4974 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14194 4974 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14195 4974 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14196 : .namespace = oprinfo->dobj.namespace->dobj.name,
14197 : .owner = oprinfo->rolname,
14198 : .description = "OPERATOR",
14199 : .section = SECTION_PRE_DATA,
14200 : .createStmt = q->data,
14201 : .dropStmt = delq->data));
14202 :
14203 : /* Dump Operator Comments */
14204 4974 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14205 4794 : dumpComment(fout, "OPERATOR", oprid->data,
14206 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14207 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14208 :
14209 4974 : PQclear(res);
14210 :
14211 4974 : destroyPQExpBuffer(query);
14212 4974 : destroyPQExpBuffer(q);
14213 4974 : destroyPQExpBuffer(delq);
14214 4974 : destroyPQExpBuffer(oprid);
14215 4974 : destroyPQExpBuffer(details);
14216 : }
14217 :
14218 : /*
14219 : * Convert a function reference obtained from pg_operator
14220 : *
14221 : * Returns allocated string of what to print, or NULL if function references
14222 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14223 : *
14224 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14225 : * part.
14226 : */
14227 : static char *
14228 14922 : convertRegProcReference(const char *proc)
14229 : {
14230 : char *name;
14231 : char *paren;
14232 : bool inquote;
14233 :
14234 : /* In all cases "-" means a null reference */
14235 14922 : if (strcmp(proc, "-") == 0)
14236 3892 : return NULL;
14237 :
14238 11030 : name = pg_strdup(proc);
14239 : /* find non-double-quoted left paren */
14240 11030 : inquote = false;
14241 132906 : for (paren = name; *paren; paren++)
14242 : {
14243 132906 : if (*paren == '(' && !inquote)
14244 : {
14245 11030 : *paren = '\0';
14246 11030 : break;
14247 : }
14248 121876 : if (*paren == '"')
14249 100 : inquote = !inquote;
14250 : }
14251 11030 : return name;
14252 : }
14253 :
14254 : /*
14255 : * getFormattedOperatorName - retrieve the operator name for the
14256 : * given operator OID (presented in string form).
14257 : *
14258 : * Returns an allocated string, or NULL if the given OID is invalid.
14259 : * Caller is responsible for free'ing result string.
14260 : *
14261 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14262 : * useful in commands where the operator's argument types can be inferred from
14263 : * context. We always schema-qualify the name, though. The predecessor to
14264 : * this code tried to skip the schema qualification if possible, but that led
14265 : * to wrong results in corner cases, such as if an operator and its negator
14266 : * are in different schemas.
14267 : */
14268 : static char *
14269 10524 : getFormattedOperatorName(const char *oproid)
14270 : {
14271 : OprInfo *oprInfo;
14272 :
14273 : /* In all cases "0" means a null reference */
14274 10524 : if (strcmp(oproid, "0") == 0)
14275 4876 : return NULL;
14276 :
14277 5648 : oprInfo = findOprByOid(atooid(oproid));
14278 5648 : if (oprInfo == NULL)
14279 : {
14280 0 : pg_log_warning("could not find operator with OID %s",
14281 : oproid);
14282 0 : return NULL;
14283 : }
14284 :
14285 5648 : return psprintf("OPERATOR(%s.%s)",
14286 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
14287 : oprInfo->dobj.name);
14288 : }
14289 :
14290 : /*
14291 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14292 : *
14293 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14294 : * argument lists of these functions are predetermined. Note that the
14295 : * caller should ensure we are in the proper schema, because the results
14296 : * are search path dependent!
14297 : */
14298 : static char *
14299 440 : convertTSFunction(Archive *fout, Oid funcOid)
14300 : {
14301 : char *result;
14302 : char query[128];
14303 : PGresult *res;
14304 :
14305 440 : snprintf(query, sizeof(query),
14306 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14307 440 : res = ExecuteSqlQueryForSingleRow(fout, query);
14308 :
14309 440 : result = pg_strdup(PQgetvalue(res, 0, 0));
14310 :
14311 440 : PQclear(res);
14312 :
14313 440 : return result;
14314 : }
14315 :
14316 : /*
14317 : * dumpAccessMethod
14318 : * write out a single access method definition
14319 : */
14320 : static void
14321 172 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14322 : {
14323 172 : DumpOptions *dopt = fout->dopt;
14324 : PQExpBuffer q;
14325 : PQExpBuffer delq;
14326 : char *qamname;
14327 :
14328 : /* Do nothing if not dumping schema */
14329 172 : if (!dopt->dumpSchema)
14330 24 : return;
14331 :
14332 148 : q = createPQExpBuffer();
14333 148 : delq = createPQExpBuffer();
14334 :
14335 148 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14336 :
14337 148 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14338 :
14339 148 : switch (aminfo->amtype)
14340 : {
14341 70 : case AMTYPE_INDEX:
14342 70 : appendPQExpBufferStr(q, "TYPE INDEX ");
14343 70 : break;
14344 78 : case AMTYPE_TABLE:
14345 78 : appendPQExpBufferStr(q, "TYPE TABLE ");
14346 78 : break;
14347 0 : default:
14348 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14349 : aminfo->amtype, qamname);
14350 0 : destroyPQExpBuffer(q);
14351 0 : destroyPQExpBuffer(delq);
14352 0 : free(qamname);
14353 0 : return;
14354 : }
14355 :
14356 148 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14357 :
14358 148 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14359 : qamname);
14360 :
14361 148 : if (dopt->binary_upgrade)
14362 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14363 : "ACCESS METHOD", qamname, NULL);
14364 :
14365 148 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14366 148 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14367 148 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14368 : .description = "ACCESS METHOD",
14369 : .section = SECTION_PRE_DATA,
14370 : .createStmt = q->data,
14371 : .dropStmt = delq->data));
14372 :
14373 : /* Dump Access Method Comments */
14374 148 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14375 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14376 : NULL, "",
14377 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14378 :
14379 148 : destroyPQExpBuffer(q);
14380 148 : destroyPQExpBuffer(delq);
14381 148 : free(qamname);
14382 : }
14383 :
14384 : /*
14385 : * dumpOpclass
14386 : * write out a single operator class definition
14387 : */
14388 : static void
14389 1338 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14390 : {
14391 1338 : DumpOptions *dopt = fout->dopt;
14392 : PQExpBuffer query;
14393 : PQExpBuffer q;
14394 : PQExpBuffer delq;
14395 : PQExpBuffer nameusing;
14396 : PGresult *res;
14397 : int ntups;
14398 : int i_opcintype;
14399 : int i_opckeytype;
14400 : int i_opcdefault;
14401 : int i_opcfamily;
14402 : int i_opcfamilyname;
14403 : int i_opcfamilynsp;
14404 : int i_amname;
14405 : int i_amopstrategy;
14406 : int i_amopopr;
14407 : int i_sortfamily;
14408 : int i_sortfamilynsp;
14409 : int i_amprocnum;
14410 : int i_amproc;
14411 : int i_amproclefttype;
14412 : int i_amprocrighttype;
14413 : char *opcintype;
14414 : char *opckeytype;
14415 : char *opcdefault;
14416 : char *opcfamily;
14417 : char *opcfamilyname;
14418 : char *opcfamilynsp;
14419 : char *amname;
14420 : char *amopstrategy;
14421 : char *amopopr;
14422 : char *sortfamily;
14423 : char *sortfamilynsp;
14424 : char *amprocnum;
14425 : char *amproc;
14426 : char *amproclefttype;
14427 : char *amprocrighttype;
14428 : bool needComma;
14429 : int i;
14430 :
14431 : /* Do nothing if not dumping schema */
14432 1338 : if (!dopt->dumpSchema)
14433 36 : return;
14434 :
14435 1302 : query = createPQExpBuffer();
14436 1302 : q = createPQExpBuffer();
14437 1302 : delq = createPQExpBuffer();
14438 1302 : nameusing = createPQExpBuffer();
14439 :
14440 : /* Get additional fields from the pg_opclass row */
14441 1302 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14442 : "opckeytype::pg_catalog.regtype, "
14443 : "opcdefault, opcfamily, "
14444 : "opfname AS opcfamilyname, "
14445 : "nspname AS opcfamilynsp, "
14446 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14447 : "FROM pg_catalog.pg_opclass c "
14448 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14449 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14450 : "WHERE c.oid = '%u'::pg_catalog.oid",
14451 1302 : opcinfo->dobj.catId.oid);
14452 :
14453 1302 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14454 :
14455 1302 : i_opcintype = PQfnumber(res, "opcintype");
14456 1302 : i_opckeytype = PQfnumber(res, "opckeytype");
14457 1302 : i_opcdefault = PQfnumber(res, "opcdefault");
14458 1302 : i_opcfamily = PQfnumber(res, "opcfamily");
14459 1302 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14460 1302 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14461 1302 : i_amname = PQfnumber(res, "amname");
14462 :
14463 : /* opcintype may still be needed after we PQclear res */
14464 1302 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14465 1302 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14466 1302 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14467 : /* opcfamily will still be needed after we PQclear res */
14468 1302 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14469 1302 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14470 1302 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14471 : /* amname will still be needed after we PQclear res */
14472 1302 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14473 :
14474 1302 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14475 1302 : fmtQualifiedDumpable(opcinfo));
14476 1302 : appendPQExpBuffer(delq, " USING %s;\n",
14477 : fmtId(amname));
14478 :
14479 : /* Build the fixed portion of the CREATE command */
14480 1302 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14481 1302 : fmtQualifiedDumpable(opcinfo));
14482 1302 : if (strcmp(opcdefault, "t") == 0)
14483 714 : appendPQExpBufferStr(q, "DEFAULT ");
14484 1302 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14485 : opcintype,
14486 : fmtId(amname));
14487 1302 : if (strlen(opcfamilyname) > 0)
14488 : {
14489 1302 : appendPQExpBufferStr(q, " FAMILY ");
14490 1302 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14491 1302 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14492 : }
14493 1302 : appendPQExpBufferStr(q, " AS\n ");
14494 :
14495 1302 : needComma = false;
14496 :
14497 1302 : if (strcmp(opckeytype, "-") != 0)
14498 : {
14499 504 : appendPQExpBuffer(q, "STORAGE %s",
14500 : opckeytype);
14501 504 : needComma = true;
14502 : }
14503 :
14504 1302 : PQclear(res);
14505 :
14506 : /*
14507 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14508 : *
14509 : * Print only those opfamily members that are tied to the opclass by
14510 : * pg_depend entries.
14511 : */
14512 1302 : resetPQExpBuffer(query);
14513 1302 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14514 : "amopopr::pg_catalog.regoperator, "
14515 : "opfname AS sortfamily, "
14516 : "nspname AS sortfamilynsp "
14517 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14518 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14519 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14520 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14521 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14522 : "AND refobjid = '%u'::pg_catalog.oid "
14523 : "AND amopfamily = '%s'::pg_catalog.oid "
14524 : "ORDER BY amopstrategy",
14525 1302 : opcinfo->dobj.catId.oid,
14526 : opcfamily);
14527 :
14528 1302 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14529 :
14530 1302 : ntups = PQntuples(res);
14531 :
14532 1302 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14533 1302 : i_amopopr = PQfnumber(res, "amopopr");
14534 1302 : i_sortfamily = PQfnumber(res, "sortfamily");
14535 1302 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14536 :
14537 1742 : for (i = 0; i < ntups; i++)
14538 : {
14539 440 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14540 440 : amopopr = PQgetvalue(res, i, i_amopopr);
14541 440 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14542 440 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14543 :
14544 440 : if (needComma)
14545 280 : appendPQExpBufferStr(q, " ,\n ");
14546 :
14547 440 : appendPQExpBuffer(q, "OPERATOR %s %s",
14548 : amopstrategy, amopopr);
14549 :
14550 440 : if (strlen(sortfamily) > 0)
14551 : {
14552 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14553 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14554 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14555 : }
14556 :
14557 440 : needComma = true;
14558 : }
14559 :
14560 1302 : PQclear(res);
14561 :
14562 : /*
14563 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14564 : *
14565 : * Print only those opfamily members that are tied to the opclass by
14566 : * pg_depend entries.
14567 : *
14568 : * We print the amproclefttype/amprocrighttype even though in most cases
14569 : * the backend could deduce the right values, because of the corner case
14570 : * of a btree sort support function for a cross-type comparison.
14571 : */
14572 1302 : resetPQExpBuffer(query);
14573 :
14574 1302 : appendPQExpBuffer(query, "SELECT amprocnum, "
14575 : "amproc::pg_catalog.regprocedure, "
14576 : "amproclefttype::pg_catalog.regtype, "
14577 : "amprocrighttype::pg_catalog.regtype "
14578 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14579 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14580 : "AND refobjid = '%u'::pg_catalog.oid "
14581 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14582 : "AND objid = ap.oid "
14583 : "ORDER BY amprocnum",
14584 1302 : opcinfo->dobj.catId.oid);
14585 :
14586 1302 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14587 :
14588 1302 : ntups = PQntuples(res);
14589 :
14590 1302 : i_amprocnum = PQfnumber(res, "amprocnum");
14591 1302 : i_amproc = PQfnumber(res, "amproc");
14592 1302 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14593 1302 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14594 :
14595 1372 : for (i = 0; i < ntups; i++)
14596 : {
14597 70 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14598 70 : amproc = PQgetvalue(res, i, i_amproc);
14599 70 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14600 70 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14601 :
14602 70 : if (needComma)
14603 70 : appendPQExpBufferStr(q, " ,\n ");
14604 :
14605 70 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14606 :
14607 70 : if (*amproclefttype && *amprocrighttype)
14608 70 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14609 :
14610 70 : appendPQExpBuffer(q, " %s", amproc);
14611 :
14612 70 : needComma = true;
14613 : }
14614 :
14615 1302 : PQclear(res);
14616 :
14617 : /*
14618 : * If needComma is still false it means we haven't added anything after
14619 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14620 : * clause with the same datatype. This isn't sanctioned by the
14621 : * documentation, but actually DefineOpClass will treat it as a no-op.
14622 : */
14623 1302 : if (!needComma)
14624 638 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14625 :
14626 1302 : appendPQExpBufferStr(q, ";\n");
14627 :
14628 1302 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14629 1302 : appendPQExpBuffer(nameusing, " USING %s",
14630 : fmtId(amname));
14631 :
14632 1302 : if (dopt->binary_upgrade)
14633 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14634 12 : "OPERATOR CLASS", nameusing->data,
14635 12 : opcinfo->dobj.namespace->dobj.name);
14636 :
14637 1302 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14638 1302 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14639 1302 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14640 : .namespace = opcinfo->dobj.namespace->dobj.name,
14641 : .owner = opcinfo->rolname,
14642 : .description = "OPERATOR CLASS",
14643 : .section = SECTION_PRE_DATA,
14644 : .createStmt = q->data,
14645 : .dropStmt = delq->data));
14646 :
14647 : /* Dump Operator Class Comments */
14648 1302 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14649 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14650 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14651 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14652 :
14653 1302 : free(opcintype);
14654 1302 : free(opcfamily);
14655 1302 : free(amname);
14656 1302 : destroyPQExpBuffer(query);
14657 1302 : destroyPQExpBuffer(q);
14658 1302 : destroyPQExpBuffer(delq);
14659 1302 : destroyPQExpBuffer(nameusing);
14660 : }
14661 :
14662 : /*
14663 : * dumpOpfamily
14664 : * write out a single operator family definition
14665 : *
14666 : * Note: this also dumps any "loose" operator members that aren't bound to a
14667 : * specific opclass within the opfamily.
14668 : */
14669 : static void
14670 1110 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14671 : {
14672 1110 : DumpOptions *dopt = fout->dopt;
14673 : PQExpBuffer query;
14674 : PQExpBuffer q;
14675 : PQExpBuffer delq;
14676 : PQExpBuffer nameusing;
14677 : PGresult *res;
14678 : PGresult *res_ops;
14679 : PGresult *res_procs;
14680 : int ntups;
14681 : int i_amname;
14682 : int i_amopstrategy;
14683 : int i_amopopr;
14684 : int i_sortfamily;
14685 : int i_sortfamilynsp;
14686 : int i_amprocnum;
14687 : int i_amproc;
14688 : int i_amproclefttype;
14689 : int i_amprocrighttype;
14690 : char *amname;
14691 : char *amopstrategy;
14692 : char *amopopr;
14693 : char *sortfamily;
14694 : char *sortfamilynsp;
14695 : char *amprocnum;
14696 : char *amproc;
14697 : char *amproclefttype;
14698 : char *amprocrighttype;
14699 : bool needComma;
14700 : int i;
14701 :
14702 : /* Do nothing if not dumping schema */
14703 1110 : if (!dopt->dumpSchema)
14704 24 : return;
14705 :
14706 1086 : query = createPQExpBuffer();
14707 1086 : q = createPQExpBuffer();
14708 1086 : delq = createPQExpBuffer();
14709 1086 : nameusing = createPQExpBuffer();
14710 :
14711 : /*
14712 : * Fetch only those opfamily members that are tied directly to the
14713 : * opfamily by pg_depend entries.
14714 : */
14715 1086 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14716 : "amopopr::pg_catalog.regoperator, "
14717 : "opfname AS sortfamily, "
14718 : "nspname AS sortfamilynsp "
14719 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14720 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14721 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14722 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14723 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14724 : "AND refobjid = '%u'::pg_catalog.oid "
14725 : "AND amopfamily = '%u'::pg_catalog.oid "
14726 : "ORDER BY amopstrategy",
14727 1086 : opfinfo->dobj.catId.oid,
14728 1086 : opfinfo->dobj.catId.oid);
14729 :
14730 1086 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14731 :
14732 1086 : resetPQExpBuffer(query);
14733 :
14734 1086 : appendPQExpBuffer(query, "SELECT amprocnum, "
14735 : "amproc::pg_catalog.regprocedure, "
14736 : "amproclefttype::pg_catalog.regtype, "
14737 : "amprocrighttype::pg_catalog.regtype "
14738 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14739 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14740 : "AND refobjid = '%u'::pg_catalog.oid "
14741 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14742 : "AND objid = ap.oid "
14743 : "ORDER BY amprocnum",
14744 1086 : opfinfo->dobj.catId.oid);
14745 :
14746 1086 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14747 :
14748 : /* Get additional fields from the pg_opfamily row */
14749 1086 : resetPQExpBuffer(query);
14750 :
14751 1086 : appendPQExpBuffer(query, "SELECT "
14752 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14753 : "FROM pg_catalog.pg_opfamily "
14754 : "WHERE oid = '%u'::pg_catalog.oid",
14755 1086 : opfinfo->dobj.catId.oid);
14756 :
14757 1086 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14758 :
14759 1086 : i_amname = PQfnumber(res, "amname");
14760 :
14761 : /* amname will still be needed after we PQclear res */
14762 1086 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14763 :
14764 1086 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14765 1086 : fmtQualifiedDumpable(opfinfo));
14766 1086 : appendPQExpBuffer(delq, " USING %s;\n",
14767 : fmtId(amname));
14768 :
14769 : /* Build the fixed portion of the CREATE command */
14770 1086 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14771 1086 : fmtQualifiedDumpable(opfinfo));
14772 1086 : appendPQExpBuffer(q, " USING %s;\n",
14773 : fmtId(amname));
14774 :
14775 1086 : PQclear(res);
14776 :
14777 : /* Do we need an ALTER to add loose members? */
14778 1086 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14779 : {
14780 100 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14781 100 : fmtQualifiedDumpable(opfinfo));
14782 100 : appendPQExpBuffer(q, " USING %s ADD\n ",
14783 : fmtId(amname));
14784 :
14785 100 : needComma = false;
14786 :
14787 : /*
14788 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14789 : */
14790 100 : ntups = PQntuples(res_ops);
14791 :
14792 100 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14793 100 : i_amopopr = PQfnumber(res_ops, "amopopr");
14794 100 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14795 100 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14796 :
14797 450 : for (i = 0; i < ntups; i++)
14798 : {
14799 350 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14800 350 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14801 350 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14802 350 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14803 :
14804 350 : if (needComma)
14805 280 : appendPQExpBufferStr(q, " ,\n ");
14806 :
14807 350 : appendPQExpBuffer(q, "OPERATOR %s %s",
14808 : amopstrategy, amopopr);
14809 :
14810 350 : if (strlen(sortfamily) > 0)
14811 : {
14812 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14813 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14814 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14815 : }
14816 :
14817 350 : needComma = true;
14818 : }
14819 :
14820 : /*
14821 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14822 : */
14823 100 : ntups = PQntuples(res_procs);
14824 :
14825 100 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14826 100 : i_amproc = PQfnumber(res_procs, "amproc");
14827 100 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14828 100 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14829 :
14830 480 : for (i = 0; i < ntups; i++)
14831 : {
14832 380 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14833 380 : amproc = PQgetvalue(res_procs, i, i_amproc);
14834 380 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14835 380 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14836 :
14837 380 : if (needComma)
14838 350 : appendPQExpBufferStr(q, " ,\n ");
14839 :
14840 380 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14841 : amprocnum, amproclefttype, amprocrighttype,
14842 : amproc);
14843 :
14844 380 : needComma = true;
14845 : }
14846 :
14847 100 : appendPQExpBufferStr(q, ";\n");
14848 : }
14849 :
14850 1086 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14851 1086 : appendPQExpBuffer(nameusing, " USING %s",
14852 : fmtId(amname));
14853 :
14854 1086 : if (dopt->binary_upgrade)
14855 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14856 18 : "OPERATOR FAMILY", nameusing->data,
14857 18 : opfinfo->dobj.namespace->dobj.name);
14858 :
14859 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14860 1086 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14861 1086 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14862 : .namespace = opfinfo->dobj.namespace->dobj.name,
14863 : .owner = opfinfo->rolname,
14864 : .description = "OPERATOR FAMILY",
14865 : .section = SECTION_PRE_DATA,
14866 : .createStmt = q->data,
14867 : .dropStmt = delq->data));
14868 :
14869 : /* Dump Operator Family Comments */
14870 1086 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14871 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14872 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14873 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14874 :
14875 1086 : free(amname);
14876 1086 : PQclear(res_ops);
14877 1086 : PQclear(res_procs);
14878 1086 : destroyPQExpBuffer(query);
14879 1086 : destroyPQExpBuffer(q);
14880 1086 : destroyPQExpBuffer(delq);
14881 1086 : destroyPQExpBuffer(nameusing);
14882 : }
14883 :
14884 : /*
14885 : * dumpCollation
14886 : * write out a single collation definition
14887 : */
14888 : static void
14889 5086 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14890 : {
14891 5086 : DumpOptions *dopt = fout->dopt;
14892 : PQExpBuffer query;
14893 : PQExpBuffer q;
14894 : PQExpBuffer delq;
14895 : char *qcollname;
14896 : PGresult *res;
14897 : int i_collprovider;
14898 : int i_collisdeterministic;
14899 : int i_collcollate;
14900 : int i_collctype;
14901 : int i_colllocale;
14902 : int i_collicurules;
14903 : const char *collprovider;
14904 : const char *collcollate;
14905 : const char *collctype;
14906 : const char *colllocale;
14907 : const char *collicurules;
14908 :
14909 : /* Do nothing if not dumping schema */
14910 5086 : if (!dopt->dumpSchema)
14911 24 : return;
14912 :
14913 5062 : query = createPQExpBuffer();
14914 5062 : q = createPQExpBuffer();
14915 5062 : delq = createPQExpBuffer();
14916 :
14917 5062 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14918 :
14919 : /* Get collation-specific details */
14920 5062 : appendPQExpBufferStr(query, "SELECT ");
14921 :
14922 5062 : if (fout->remoteVersion >= 100000)
14923 5062 : appendPQExpBufferStr(query,
14924 : "collprovider, "
14925 : "collversion, ");
14926 : else
14927 0 : appendPQExpBufferStr(query,
14928 : "'c' AS collprovider, "
14929 : "NULL AS collversion, ");
14930 :
14931 5062 : if (fout->remoteVersion >= 120000)
14932 5062 : appendPQExpBufferStr(query,
14933 : "collisdeterministic, ");
14934 : else
14935 0 : appendPQExpBufferStr(query,
14936 : "true AS collisdeterministic, ");
14937 :
14938 5062 : if (fout->remoteVersion >= 170000)
14939 5062 : appendPQExpBufferStr(query,
14940 : "colllocale, ");
14941 0 : else if (fout->remoteVersion >= 150000)
14942 0 : appendPQExpBufferStr(query,
14943 : "colliculocale AS colllocale, ");
14944 : else
14945 0 : appendPQExpBufferStr(query,
14946 : "NULL AS colllocale, ");
14947 :
14948 5062 : if (fout->remoteVersion >= 160000)
14949 5062 : appendPQExpBufferStr(query,
14950 : "collicurules, ");
14951 : else
14952 0 : appendPQExpBufferStr(query,
14953 : "NULL AS collicurules, ");
14954 :
14955 5062 : appendPQExpBuffer(query,
14956 : "collcollate, "
14957 : "collctype "
14958 : "FROM pg_catalog.pg_collation c "
14959 : "WHERE c.oid = '%u'::pg_catalog.oid",
14960 5062 : collinfo->dobj.catId.oid);
14961 :
14962 5062 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14963 :
14964 5062 : i_collprovider = PQfnumber(res, "collprovider");
14965 5062 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14966 5062 : i_collcollate = PQfnumber(res, "collcollate");
14967 5062 : i_collctype = PQfnumber(res, "collctype");
14968 5062 : i_colllocale = PQfnumber(res, "colllocale");
14969 5062 : i_collicurules = PQfnumber(res, "collicurules");
14970 :
14971 5062 : collprovider = PQgetvalue(res, 0, i_collprovider);
14972 :
14973 5062 : if (!PQgetisnull(res, 0, i_collcollate))
14974 98 : collcollate = PQgetvalue(res, 0, i_collcollate);
14975 : else
14976 4964 : collcollate = NULL;
14977 :
14978 5062 : if (!PQgetisnull(res, 0, i_collctype))
14979 98 : collctype = PQgetvalue(res, 0, i_collctype);
14980 : else
14981 4964 : collctype = NULL;
14982 :
14983 : /*
14984 : * Before version 15, collcollate and collctype were of type NAME and
14985 : * non-nullable. Treat empty strings as NULL for consistency.
14986 : */
14987 5062 : if (fout->remoteVersion < 150000)
14988 : {
14989 0 : if (collcollate[0] == '\0')
14990 0 : collcollate = NULL;
14991 0 : if (collctype[0] == '\0')
14992 0 : collctype = NULL;
14993 : }
14994 :
14995 5062 : if (!PQgetisnull(res, 0, i_colllocale))
14996 4958 : colllocale = PQgetvalue(res, 0, i_colllocale);
14997 : else
14998 104 : colllocale = NULL;
14999 :
15000 5062 : if (!PQgetisnull(res, 0, i_collicurules))
15001 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15002 : else
15003 5062 : collicurules = NULL;
15004 :
15005 5062 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15006 5062 : fmtQualifiedDumpable(collinfo));
15007 :
15008 5062 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15009 5062 : fmtQualifiedDumpable(collinfo));
15010 :
15011 5062 : appendPQExpBufferStr(q, "provider = ");
15012 5062 : if (collprovider[0] == 'b')
15013 38 : appendPQExpBufferStr(q, "builtin");
15014 5024 : else if (collprovider[0] == 'c')
15015 98 : appendPQExpBufferStr(q, "libc");
15016 4926 : else if (collprovider[0] == 'i')
15017 4920 : appendPQExpBufferStr(q, "icu");
15018 6 : else if (collprovider[0] == 'd')
15019 : /* to allow dumping pg_catalog; not accepted on input */
15020 6 : appendPQExpBufferStr(q, "default");
15021 : else
15022 0 : pg_fatal("unrecognized collation provider: %s",
15023 : collprovider);
15024 :
15025 5062 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15026 0 : appendPQExpBufferStr(q, ", deterministic = false");
15027 :
15028 5062 : if (collprovider[0] == 'd')
15029 : {
15030 6 : if (collcollate || collctype || colllocale || collicurules)
15031 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15032 :
15033 : /* no locale -- the default collation cannot be reloaded anyway */
15034 : }
15035 5056 : else if (collprovider[0] == 'b')
15036 : {
15037 38 : if (collcollate || collctype || !colllocale || collicurules)
15038 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15039 :
15040 38 : appendPQExpBufferStr(q, ", locale = ");
15041 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15042 : fout);
15043 : }
15044 5018 : else if (collprovider[0] == 'i')
15045 : {
15046 4920 : if (fout->remoteVersion >= 150000)
15047 : {
15048 4920 : if (collcollate || collctype || !colllocale)
15049 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15050 :
15051 4920 : appendPQExpBufferStr(q, ", locale = ");
15052 4920 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15053 : fout);
15054 : }
15055 : else
15056 : {
15057 0 : if (!collcollate || !collctype || colllocale ||
15058 0 : strcmp(collcollate, collctype) != 0)
15059 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15060 :
15061 0 : appendPQExpBufferStr(q, ", locale = ");
15062 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15063 : }
15064 :
15065 4920 : if (collicurules)
15066 : {
15067 0 : appendPQExpBufferStr(q, ", rules = ");
15068 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15069 : }
15070 : }
15071 98 : else if (collprovider[0] == 'c')
15072 : {
15073 98 : if (colllocale || collicurules || !collcollate || !collctype)
15074 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15075 :
15076 98 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15077 : {
15078 98 : appendPQExpBufferStr(q, ", locale = ");
15079 98 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15080 : }
15081 : else
15082 : {
15083 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15084 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15085 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15086 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15087 : }
15088 : }
15089 : else
15090 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15091 :
15092 : /*
15093 : * For binary upgrade, carry over the collation version. For normal
15094 : * dump/restore, omit the version, so that it is computed upon restore.
15095 : */
15096 5062 : if (dopt->binary_upgrade)
15097 : {
15098 : int i_collversion;
15099 :
15100 10 : i_collversion = PQfnumber(res, "collversion");
15101 10 : if (!PQgetisnull(res, 0, i_collversion))
15102 : {
15103 8 : appendPQExpBufferStr(q, ", version = ");
15104 8 : appendStringLiteralAH(q,
15105 : PQgetvalue(res, 0, i_collversion),
15106 : fout);
15107 : }
15108 : }
15109 :
15110 5062 : appendPQExpBufferStr(q, ");\n");
15111 :
15112 5062 : if (dopt->binary_upgrade)
15113 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15114 : "COLLATION", qcollname,
15115 10 : collinfo->dobj.namespace->dobj.name);
15116 :
15117 5062 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15118 5062 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15119 5062 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15120 : .namespace = collinfo->dobj.namespace->dobj.name,
15121 : .owner = collinfo->rolname,
15122 : .description = "COLLATION",
15123 : .section = SECTION_PRE_DATA,
15124 : .createStmt = q->data,
15125 : .dropStmt = delq->data));
15126 :
15127 : /* Dump Collation Comments */
15128 5062 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15129 4868 : dumpComment(fout, "COLLATION", qcollname,
15130 4868 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15131 4868 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15132 :
15133 5062 : PQclear(res);
15134 :
15135 5062 : destroyPQExpBuffer(query);
15136 5062 : destroyPQExpBuffer(q);
15137 5062 : destroyPQExpBuffer(delq);
15138 5062 : free(qcollname);
15139 : }
15140 :
15141 : /*
15142 : * dumpConversion
15143 : * write out a single conversion definition
15144 : */
15145 : static void
15146 850 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15147 : {
15148 850 : DumpOptions *dopt = fout->dopt;
15149 : PQExpBuffer query;
15150 : PQExpBuffer q;
15151 : PQExpBuffer delq;
15152 : char *qconvname;
15153 : PGresult *res;
15154 : int i_conforencoding;
15155 : int i_contoencoding;
15156 : int i_conproc;
15157 : int i_condefault;
15158 : const char *conforencoding;
15159 : const char *contoencoding;
15160 : const char *conproc;
15161 : bool condefault;
15162 :
15163 : /* Do nothing if not dumping schema */
15164 850 : if (!dopt->dumpSchema)
15165 12 : return;
15166 :
15167 838 : query = createPQExpBuffer();
15168 838 : q = createPQExpBuffer();
15169 838 : delq = createPQExpBuffer();
15170 :
15171 838 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15172 :
15173 : /* Get conversion-specific details */
15174 838 : appendPQExpBuffer(query, "SELECT "
15175 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15176 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15177 : "conproc, condefault "
15178 : "FROM pg_catalog.pg_conversion c "
15179 : "WHERE c.oid = '%u'::pg_catalog.oid",
15180 838 : convinfo->dobj.catId.oid);
15181 :
15182 838 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15183 :
15184 838 : i_conforencoding = PQfnumber(res, "conforencoding");
15185 838 : i_contoencoding = PQfnumber(res, "contoencoding");
15186 838 : i_conproc = PQfnumber(res, "conproc");
15187 838 : i_condefault = PQfnumber(res, "condefault");
15188 :
15189 838 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15190 838 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15191 838 : conproc = PQgetvalue(res, 0, i_conproc);
15192 838 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15193 :
15194 838 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15195 838 : fmtQualifiedDumpable(convinfo));
15196 :
15197 838 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15198 : (condefault) ? "DEFAULT " : "",
15199 838 : fmtQualifiedDumpable(convinfo));
15200 838 : appendStringLiteralAH(q, conforencoding, fout);
15201 838 : appendPQExpBufferStr(q, " TO ");
15202 838 : appendStringLiteralAH(q, contoencoding, fout);
15203 : /* regproc output is already sufficiently quoted */
15204 838 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15205 :
15206 838 : if (dopt->binary_upgrade)
15207 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15208 : "CONVERSION", qconvname,
15209 2 : convinfo->dobj.namespace->dobj.name);
15210 :
15211 838 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15212 838 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15213 838 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15214 : .namespace = convinfo->dobj.namespace->dobj.name,
15215 : .owner = convinfo->rolname,
15216 : .description = "CONVERSION",
15217 : .section = SECTION_PRE_DATA,
15218 : .createStmt = q->data,
15219 : .dropStmt = delq->data));
15220 :
15221 : /* Dump Conversion Comments */
15222 838 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15223 838 : dumpComment(fout, "CONVERSION", qconvname,
15224 838 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15225 838 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15226 :
15227 838 : PQclear(res);
15228 :
15229 838 : destroyPQExpBuffer(query);
15230 838 : destroyPQExpBuffer(q);
15231 838 : destroyPQExpBuffer(delq);
15232 838 : free(qconvname);
15233 : }
15234 :
15235 : /*
15236 : * format_aggregate_signature: generate aggregate name and argument list
15237 : *
15238 : * The argument type names are qualified if needed. The aggregate name
15239 : * is never qualified.
15240 : */
15241 : static char *
15242 576 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15243 : {
15244 : PQExpBufferData buf;
15245 : int j;
15246 :
15247 576 : initPQExpBuffer(&buf);
15248 576 : if (honor_quotes)
15249 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15250 : else
15251 576 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15252 :
15253 576 : if (agginfo->aggfn.nargs == 0)
15254 80 : appendPQExpBufferStr(&buf, "(*)");
15255 : else
15256 : {
15257 496 : appendPQExpBufferChar(&buf, '(');
15258 1082 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15259 586 : appendPQExpBuffer(&buf, "%s%s",
15260 : (j > 0) ? ", " : "",
15261 : getFormattedTypeName(fout,
15262 586 : agginfo->aggfn.argtypes[j],
15263 : zeroIsError));
15264 496 : appendPQExpBufferChar(&buf, ')');
15265 : }
15266 576 : return buf.data;
15267 : }
15268 :
15269 : /*
15270 : * dumpAgg
15271 : * write out a single aggregate definition
15272 : */
15273 : static void
15274 590 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15275 : {
15276 590 : DumpOptions *dopt = fout->dopt;
15277 : PQExpBuffer query;
15278 : PQExpBuffer q;
15279 : PQExpBuffer delq;
15280 : PQExpBuffer details;
15281 : char *aggsig; /* identity signature */
15282 590 : char *aggfullsig = NULL; /* full signature */
15283 : char *aggsig_tag;
15284 : PGresult *res;
15285 : int i_agginitval;
15286 : int i_aggminitval;
15287 : const char *aggtransfn;
15288 : const char *aggfinalfn;
15289 : const char *aggcombinefn;
15290 : const char *aggserialfn;
15291 : const char *aggdeserialfn;
15292 : const char *aggmtransfn;
15293 : const char *aggminvtransfn;
15294 : const char *aggmfinalfn;
15295 : bool aggfinalextra;
15296 : bool aggmfinalextra;
15297 : char aggfinalmodify;
15298 : char aggmfinalmodify;
15299 : const char *aggsortop;
15300 : char *aggsortconvop;
15301 : char aggkind;
15302 : const char *aggtranstype;
15303 : const char *aggtransspace;
15304 : const char *aggmtranstype;
15305 : const char *aggmtransspace;
15306 : const char *agginitval;
15307 : const char *aggminitval;
15308 : const char *proparallel;
15309 : char defaultfinalmodify;
15310 :
15311 : /* Do nothing if not dumping schema */
15312 590 : if (!dopt->dumpSchema)
15313 14 : return;
15314 :
15315 576 : query = createPQExpBuffer();
15316 576 : q = createPQExpBuffer();
15317 576 : delq = createPQExpBuffer();
15318 576 : details = createPQExpBuffer();
15319 :
15320 576 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15321 : {
15322 : /* Set up query for aggregate-specific details */
15323 116 : appendPQExpBufferStr(query,
15324 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15325 :
15326 116 : appendPQExpBufferStr(query,
15327 : "SELECT "
15328 : "aggtransfn,\n"
15329 : "aggfinalfn,\n"
15330 : "aggtranstype::pg_catalog.regtype,\n"
15331 : "agginitval,\n"
15332 : "aggsortop,\n"
15333 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15334 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15335 :
15336 116 : if (fout->remoteVersion >= 90400)
15337 116 : appendPQExpBufferStr(query,
15338 : "aggkind,\n"
15339 : "aggmtransfn,\n"
15340 : "aggminvtransfn,\n"
15341 : "aggmfinalfn,\n"
15342 : "aggmtranstype::pg_catalog.regtype,\n"
15343 : "aggfinalextra,\n"
15344 : "aggmfinalextra,\n"
15345 : "aggtransspace,\n"
15346 : "aggmtransspace,\n"
15347 : "aggminitval,\n");
15348 : else
15349 0 : appendPQExpBufferStr(query,
15350 : "'n' AS aggkind,\n"
15351 : "'-' AS aggmtransfn,\n"
15352 : "'-' AS aggminvtransfn,\n"
15353 : "'-' AS aggmfinalfn,\n"
15354 : "0 AS aggmtranstype,\n"
15355 : "false AS aggfinalextra,\n"
15356 : "false AS aggmfinalextra,\n"
15357 : "0 AS aggtransspace,\n"
15358 : "0 AS aggmtransspace,\n"
15359 : "NULL AS aggminitval,\n");
15360 :
15361 116 : if (fout->remoteVersion >= 90600)
15362 116 : appendPQExpBufferStr(query,
15363 : "aggcombinefn,\n"
15364 : "aggserialfn,\n"
15365 : "aggdeserialfn,\n"
15366 : "proparallel,\n");
15367 : else
15368 0 : appendPQExpBufferStr(query,
15369 : "'-' AS aggcombinefn,\n"
15370 : "'-' AS aggserialfn,\n"
15371 : "'-' AS aggdeserialfn,\n"
15372 : "'u' AS proparallel,\n");
15373 :
15374 116 : if (fout->remoteVersion >= 110000)
15375 116 : appendPQExpBufferStr(query,
15376 : "aggfinalmodify,\n"
15377 : "aggmfinalmodify\n");
15378 : else
15379 0 : appendPQExpBufferStr(query,
15380 : "'0' AS aggfinalmodify,\n"
15381 : "'0' AS aggmfinalmodify\n");
15382 :
15383 116 : appendPQExpBufferStr(query,
15384 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15385 : "WHERE a.aggfnoid = p.oid "
15386 : "AND p.oid = $1");
15387 :
15388 116 : ExecuteSqlStatement(fout, query->data);
15389 :
15390 116 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15391 : }
15392 :
15393 576 : printfPQExpBuffer(query,
15394 : "EXECUTE dumpAgg('%u')",
15395 576 : agginfo->aggfn.dobj.catId.oid);
15396 :
15397 576 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15398 :
15399 576 : i_agginitval = PQfnumber(res, "agginitval");
15400 576 : i_aggminitval = PQfnumber(res, "aggminitval");
15401 :
15402 576 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15403 576 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15404 576 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15405 576 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15406 576 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15407 576 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15408 576 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15409 576 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15410 576 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15411 576 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15412 576 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15413 576 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15414 576 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15415 576 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15416 576 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15417 576 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15418 576 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15419 576 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15420 576 : agginitval = PQgetvalue(res, 0, i_agginitval);
15421 576 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15422 576 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15423 :
15424 : {
15425 : char *funcargs;
15426 : char *funciargs;
15427 :
15428 576 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15429 576 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15430 576 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15431 576 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15432 : }
15433 :
15434 576 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15435 :
15436 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15437 576 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15438 : /* replace omitted flags for old versions */
15439 576 : if (aggfinalmodify == '0')
15440 0 : aggfinalmodify = defaultfinalmodify;
15441 576 : if (aggmfinalmodify == '0')
15442 0 : aggmfinalmodify = defaultfinalmodify;
15443 :
15444 : /* regproc and regtype output is already sufficiently quoted */
15445 576 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15446 : aggtransfn, aggtranstype);
15447 :
15448 576 : if (strcmp(aggtransspace, "0") != 0)
15449 : {
15450 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15451 : aggtransspace);
15452 : }
15453 :
15454 576 : if (!PQgetisnull(res, 0, i_agginitval))
15455 : {
15456 420 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15457 420 : appendStringLiteralAH(details, agginitval, fout);
15458 : }
15459 :
15460 576 : if (strcmp(aggfinalfn, "-") != 0)
15461 : {
15462 270 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15463 : aggfinalfn);
15464 270 : if (aggfinalextra)
15465 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15466 270 : if (aggfinalmodify != defaultfinalmodify)
15467 : {
15468 70 : switch (aggfinalmodify)
15469 : {
15470 0 : case AGGMODIFY_READ_ONLY:
15471 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15472 0 : break;
15473 70 : case AGGMODIFY_SHAREABLE:
15474 70 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15475 70 : break;
15476 0 : case AGGMODIFY_READ_WRITE:
15477 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15478 0 : break;
15479 0 : default:
15480 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15481 : agginfo->aggfn.dobj.name);
15482 : break;
15483 : }
15484 : }
15485 : }
15486 :
15487 576 : if (strcmp(aggcombinefn, "-") != 0)
15488 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15489 :
15490 576 : if (strcmp(aggserialfn, "-") != 0)
15491 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15492 :
15493 576 : if (strcmp(aggdeserialfn, "-") != 0)
15494 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15495 :
15496 576 : if (strcmp(aggmtransfn, "-") != 0)
15497 : {
15498 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15499 : aggmtransfn,
15500 : aggminvtransfn,
15501 : aggmtranstype);
15502 : }
15503 :
15504 576 : if (strcmp(aggmtransspace, "0") != 0)
15505 : {
15506 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15507 : aggmtransspace);
15508 : }
15509 :
15510 576 : if (!PQgetisnull(res, 0, i_aggminitval))
15511 : {
15512 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15513 20 : appendStringLiteralAH(details, aggminitval, fout);
15514 : }
15515 :
15516 576 : if (strcmp(aggmfinalfn, "-") != 0)
15517 : {
15518 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15519 : aggmfinalfn);
15520 0 : if (aggmfinalextra)
15521 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15522 0 : if (aggmfinalmodify != defaultfinalmodify)
15523 : {
15524 0 : switch (aggmfinalmodify)
15525 : {
15526 0 : case AGGMODIFY_READ_ONLY:
15527 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15528 0 : break;
15529 0 : case AGGMODIFY_SHAREABLE:
15530 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15531 0 : break;
15532 0 : case AGGMODIFY_READ_WRITE:
15533 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15534 0 : break;
15535 0 : default:
15536 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15537 : agginfo->aggfn.dobj.name);
15538 : break;
15539 : }
15540 : }
15541 : }
15542 :
15543 576 : aggsortconvop = getFormattedOperatorName(aggsortop);
15544 576 : if (aggsortconvop)
15545 : {
15546 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15547 : aggsortconvop);
15548 0 : free(aggsortconvop);
15549 : }
15550 :
15551 576 : if (aggkind == AGGKIND_HYPOTHETICAL)
15552 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15553 :
15554 576 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15555 : {
15556 10 : if (proparallel[0] == PROPARALLEL_SAFE)
15557 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15558 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15559 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15560 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15561 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15562 : agginfo->aggfn.dobj.name);
15563 : }
15564 :
15565 576 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15566 576 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15567 : aggsig);
15568 :
15569 1152 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15570 576 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15571 : aggfullsig ? aggfullsig : aggsig, details->data);
15572 :
15573 576 : if (dopt->binary_upgrade)
15574 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15575 : "AGGREGATE", aggsig,
15576 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15577 :
15578 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15579 542 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15580 542 : agginfo->aggfn.dobj.dumpId,
15581 542 : ARCHIVE_OPTS(.tag = aggsig_tag,
15582 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15583 : .owner = agginfo->aggfn.rolname,
15584 : .description = "AGGREGATE",
15585 : .section = SECTION_PRE_DATA,
15586 : .createStmt = q->data,
15587 : .dropStmt = delq->data));
15588 :
15589 : /* Dump Aggregate Comments */
15590 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15591 20 : dumpComment(fout, "AGGREGATE", aggsig,
15592 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15593 20 : agginfo->aggfn.rolname,
15594 20 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15595 :
15596 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15597 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15598 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15599 0 : agginfo->aggfn.rolname,
15600 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15601 :
15602 : /*
15603 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15604 : * command look like a function's GRANT; in particular this affects the
15605 : * syntax for zero-argument aggregates and ordered-set aggregates.
15606 : */
15607 576 : free(aggsig);
15608 :
15609 576 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15610 :
15611 576 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15612 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15613 : "FUNCTION", aggsig, NULL,
15614 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15615 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15616 :
15617 576 : free(aggsig);
15618 576 : free(aggfullsig);
15619 576 : free(aggsig_tag);
15620 :
15621 576 : PQclear(res);
15622 :
15623 576 : destroyPQExpBuffer(query);
15624 576 : destroyPQExpBuffer(q);
15625 576 : destroyPQExpBuffer(delq);
15626 576 : destroyPQExpBuffer(details);
15627 : }
15628 :
15629 : /*
15630 : * dumpTSParser
15631 : * write out a single text search parser
15632 : */
15633 : static void
15634 88 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15635 : {
15636 88 : DumpOptions *dopt = fout->dopt;
15637 : PQExpBuffer q;
15638 : PQExpBuffer delq;
15639 : char *qprsname;
15640 :
15641 : /* Do nothing if not dumping schema */
15642 88 : if (!dopt->dumpSchema)
15643 12 : return;
15644 :
15645 76 : q = createPQExpBuffer();
15646 76 : delq = createPQExpBuffer();
15647 :
15648 76 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15649 :
15650 76 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15651 76 : fmtQualifiedDumpable(prsinfo));
15652 :
15653 76 : appendPQExpBuffer(q, " START = %s,\n",
15654 76 : convertTSFunction(fout, prsinfo->prsstart));
15655 76 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15656 76 : convertTSFunction(fout, prsinfo->prstoken));
15657 76 : appendPQExpBuffer(q, " END = %s,\n",
15658 76 : convertTSFunction(fout, prsinfo->prsend));
15659 76 : if (prsinfo->prsheadline != InvalidOid)
15660 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15661 6 : convertTSFunction(fout, prsinfo->prsheadline));
15662 76 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15663 76 : convertTSFunction(fout, prsinfo->prslextype));
15664 :
15665 76 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15666 76 : fmtQualifiedDumpable(prsinfo));
15667 :
15668 76 : if (dopt->binary_upgrade)
15669 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15670 : "TEXT SEARCH PARSER", qprsname,
15671 2 : prsinfo->dobj.namespace->dobj.name);
15672 :
15673 76 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15674 76 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15675 76 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15676 : .namespace = prsinfo->dobj.namespace->dobj.name,
15677 : .description = "TEXT SEARCH PARSER",
15678 : .section = SECTION_PRE_DATA,
15679 : .createStmt = q->data,
15680 : .dropStmt = delq->data));
15681 :
15682 : /* Dump Parser Comments */
15683 76 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15684 76 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15685 76 : prsinfo->dobj.namespace->dobj.name, "",
15686 76 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15687 :
15688 76 : destroyPQExpBuffer(q);
15689 76 : destroyPQExpBuffer(delq);
15690 76 : free(qprsname);
15691 : }
15692 :
15693 : /*
15694 : * dumpTSDictionary
15695 : * write out a single text search dictionary
15696 : */
15697 : static void
15698 352 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15699 : {
15700 352 : DumpOptions *dopt = fout->dopt;
15701 : PQExpBuffer q;
15702 : PQExpBuffer delq;
15703 : PQExpBuffer query;
15704 : char *qdictname;
15705 : PGresult *res;
15706 : char *nspname;
15707 : char *tmplname;
15708 :
15709 : /* Do nothing if not dumping schema */
15710 352 : if (!dopt->dumpSchema)
15711 12 : return;
15712 :
15713 340 : q = createPQExpBuffer();
15714 340 : delq = createPQExpBuffer();
15715 340 : query = createPQExpBuffer();
15716 :
15717 340 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15718 :
15719 : /* Fetch name and namespace of the dictionary's template */
15720 340 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15721 : "FROM pg_ts_template p, pg_namespace n "
15722 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15723 340 : dictinfo->dicttemplate);
15724 340 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15725 340 : nspname = PQgetvalue(res, 0, 0);
15726 340 : tmplname = PQgetvalue(res, 0, 1);
15727 :
15728 340 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15729 340 : fmtQualifiedDumpable(dictinfo));
15730 :
15731 340 : appendPQExpBufferStr(q, " TEMPLATE = ");
15732 340 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15733 340 : appendPQExpBufferStr(q, fmtId(tmplname));
15734 :
15735 340 : PQclear(res);
15736 :
15737 : /* the dictinitoption can be dumped straight into the command */
15738 340 : if (dictinfo->dictinitoption)
15739 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15740 :
15741 340 : appendPQExpBufferStr(q, " );\n");
15742 :
15743 340 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15744 340 : fmtQualifiedDumpable(dictinfo));
15745 :
15746 340 : if (dopt->binary_upgrade)
15747 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15748 : "TEXT SEARCH DICTIONARY", qdictname,
15749 20 : dictinfo->dobj.namespace->dobj.name);
15750 :
15751 340 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15752 340 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15753 340 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15754 : .namespace = dictinfo->dobj.namespace->dobj.name,
15755 : .owner = dictinfo->rolname,
15756 : .description = "TEXT SEARCH DICTIONARY",
15757 : .section = SECTION_PRE_DATA,
15758 : .createStmt = q->data,
15759 : .dropStmt = delq->data));
15760 :
15761 : /* Dump Dictionary Comments */
15762 340 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15763 250 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15764 250 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15765 250 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15766 :
15767 340 : destroyPQExpBuffer(q);
15768 340 : destroyPQExpBuffer(delq);
15769 340 : destroyPQExpBuffer(query);
15770 340 : free(qdictname);
15771 : }
15772 :
15773 : /*
15774 : * dumpTSTemplate
15775 : * write out a single text search template
15776 : */
15777 : static void
15778 112 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15779 : {
15780 112 : DumpOptions *dopt = fout->dopt;
15781 : PQExpBuffer q;
15782 : PQExpBuffer delq;
15783 : char *qtmplname;
15784 :
15785 : /* Do nothing if not dumping schema */
15786 112 : if (!dopt->dumpSchema)
15787 12 : return;
15788 :
15789 100 : q = createPQExpBuffer();
15790 100 : delq = createPQExpBuffer();
15791 :
15792 100 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15793 :
15794 100 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15795 100 : fmtQualifiedDumpable(tmplinfo));
15796 :
15797 100 : if (tmplinfo->tmplinit != InvalidOid)
15798 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15799 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15800 100 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15801 100 : convertTSFunction(fout, tmplinfo->tmpllexize));
15802 :
15803 100 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15804 100 : fmtQualifiedDumpable(tmplinfo));
15805 :
15806 100 : if (dopt->binary_upgrade)
15807 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15808 : "TEXT SEARCH TEMPLATE", qtmplname,
15809 2 : tmplinfo->dobj.namespace->dobj.name);
15810 :
15811 100 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15812 100 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15813 100 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15814 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15815 : .description = "TEXT SEARCH TEMPLATE",
15816 : .section = SECTION_PRE_DATA,
15817 : .createStmt = q->data,
15818 : .dropStmt = delq->data));
15819 :
15820 : /* Dump Template Comments */
15821 100 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15822 100 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15823 100 : tmplinfo->dobj.namespace->dobj.name, "",
15824 100 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15825 :
15826 100 : destroyPQExpBuffer(q);
15827 100 : destroyPQExpBuffer(delq);
15828 100 : free(qtmplname);
15829 : }
15830 :
15831 : /*
15832 : * dumpTSConfig
15833 : * write out a single text search configuration
15834 : */
15835 : static void
15836 302 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15837 : {
15838 302 : DumpOptions *dopt = fout->dopt;
15839 : PQExpBuffer q;
15840 : PQExpBuffer delq;
15841 : PQExpBuffer query;
15842 : char *qcfgname;
15843 : PGresult *res;
15844 : char *nspname;
15845 : char *prsname;
15846 : int ntups,
15847 : i;
15848 : int i_tokenname;
15849 : int i_dictname;
15850 :
15851 : /* Do nothing if not dumping schema */
15852 302 : if (!dopt->dumpSchema)
15853 12 : return;
15854 :
15855 290 : q = createPQExpBuffer();
15856 290 : delq = createPQExpBuffer();
15857 290 : query = createPQExpBuffer();
15858 :
15859 290 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15860 :
15861 : /* Fetch name and namespace of the config's parser */
15862 290 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15863 : "FROM pg_ts_parser p, pg_namespace n "
15864 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15865 290 : cfginfo->cfgparser);
15866 290 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15867 290 : nspname = PQgetvalue(res, 0, 0);
15868 290 : prsname = PQgetvalue(res, 0, 1);
15869 :
15870 290 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15871 290 : fmtQualifiedDumpable(cfginfo));
15872 :
15873 290 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15874 290 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15875 :
15876 290 : PQclear(res);
15877 :
15878 290 : resetPQExpBuffer(query);
15879 290 : appendPQExpBuffer(query,
15880 : "SELECT\n"
15881 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15882 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15883 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15884 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15885 : "WHERE m.mapcfg = '%u'\n"
15886 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15887 290 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15888 :
15889 290 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15890 290 : ntups = PQntuples(res);
15891 :
15892 290 : i_tokenname = PQfnumber(res, "tokenname");
15893 290 : i_dictname = PQfnumber(res, "dictname");
15894 :
15895 6070 : for (i = 0; i < ntups; i++)
15896 : {
15897 5780 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15898 5780 : char *dictname = PQgetvalue(res, i, i_dictname);
15899 :
15900 5780 : if (i == 0 ||
15901 5490 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15902 : {
15903 : /* starting a new token type, so start a new command */
15904 5510 : if (i > 0)
15905 5220 : appendPQExpBufferStr(q, ";\n");
15906 5510 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15907 5510 : fmtQualifiedDumpable(cfginfo));
15908 : /* tokenname needs quoting, dictname does NOT */
15909 5510 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15910 : fmtId(tokenname), dictname);
15911 : }
15912 : else
15913 270 : appendPQExpBuffer(q, ", %s", dictname);
15914 : }
15915 :
15916 290 : if (ntups > 0)
15917 290 : appendPQExpBufferStr(q, ";\n");
15918 :
15919 290 : PQclear(res);
15920 :
15921 290 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15922 290 : fmtQualifiedDumpable(cfginfo));
15923 :
15924 290 : if (dopt->binary_upgrade)
15925 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15926 : "TEXT SEARCH CONFIGURATION", qcfgname,
15927 10 : cfginfo->dobj.namespace->dobj.name);
15928 :
15929 290 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15930 290 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15931 290 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15932 : .namespace = cfginfo->dobj.namespace->dobj.name,
15933 : .owner = cfginfo->rolname,
15934 : .description = "TEXT SEARCH CONFIGURATION",
15935 : .section = SECTION_PRE_DATA,
15936 : .createStmt = q->data,
15937 : .dropStmt = delq->data));
15938 :
15939 : /* Dump Configuration Comments */
15940 290 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15941 250 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15942 250 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15943 250 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15944 :
15945 290 : destroyPQExpBuffer(q);
15946 290 : destroyPQExpBuffer(delq);
15947 290 : destroyPQExpBuffer(query);
15948 290 : free(qcfgname);
15949 : }
15950 :
15951 : /*
15952 : * dumpForeignDataWrapper
15953 : * write out a single foreign-data wrapper definition
15954 : */
15955 : static void
15956 110 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15957 : {
15958 110 : DumpOptions *dopt = fout->dopt;
15959 : PQExpBuffer q;
15960 : PQExpBuffer delq;
15961 : char *qfdwname;
15962 :
15963 : /* Do nothing if not dumping schema */
15964 110 : if (!dopt->dumpSchema)
15965 14 : return;
15966 :
15967 96 : q = createPQExpBuffer();
15968 96 : delq = createPQExpBuffer();
15969 :
15970 96 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15971 :
15972 96 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15973 : qfdwname);
15974 :
15975 96 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15976 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15977 :
15978 96 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15979 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15980 :
15981 96 : if (strlen(fdwinfo->fdwoptions) > 0)
15982 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15983 :
15984 96 : appendPQExpBufferStr(q, ";\n");
15985 :
15986 96 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15987 : qfdwname);
15988 :
15989 96 : if (dopt->binary_upgrade)
15990 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15991 : "FOREIGN DATA WRAPPER", qfdwname,
15992 : NULL);
15993 :
15994 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15995 96 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15996 96 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15997 : .owner = fdwinfo->rolname,
15998 : .description = "FOREIGN DATA WRAPPER",
15999 : .section = SECTION_PRE_DATA,
16000 : .createStmt = q->data,
16001 : .dropStmt = delq->data));
16002 :
16003 : /* Dump Foreign Data Wrapper Comments */
16004 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16005 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16006 0 : NULL, fdwinfo->rolname,
16007 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16008 :
16009 : /* Handle the ACL */
16010 96 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16011 68 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16012 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16013 68 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16014 :
16015 96 : free(qfdwname);
16016 :
16017 96 : destroyPQExpBuffer(q);
16018 96 : destroyPQExpBuffer(delq);
16019 : }
16020 :
16021 : /*
16022 : * dumpForeignServer
16023 : * write out a foreign server definition
16024 : */
16025 : static void
16026 118 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16027 : {
16028 118 : DumpOptions *dopt = fout->dopt;
16029 : PQExpBuffer q;
16030 : PQExpBuffer delq;
16031 : PQExpBuffer query;
16032 : PGresult *res;
16033 : char *qsrvname;
16034 : char *fdwname;
16035 :
16036 : /* Do nothing if not dumping schema */
16037 118 : if (!dopt->dumpSchema)
16038 18 : return;
16039 :
16040 100 : q = createPQExpBuffer();
16041 100 : delq = createPQExpBuffer();
16042 100 : query = createPQExpBuffer();
16043 :
16044 100 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16045 :
16046 : /* look up the foreign-data wrapper */
16047 100 : appendPQExpBuffer(query, "SELECT fdwname "
16048 : "FROM pg_foreign_data_wrapper w "
16049 : "WHERE w.oid = '%u'",
16050 100 : srvinfo->srvfdw);
16051 100 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16052 100 : fdwname = PQgetvalue(res, 0, 0);
16053 :
16054 100 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16055 100 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16056 : {
16057 0 : appendPQExpBufferStr(q, " TYPE ");
16058 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16059 : }
16060 100 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16061 : {
16062 0 : appendPQExpBufferStr(q, " VERSION ");
16063 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16064 : }
16065 :
16066 100 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16067 100 : appendPQExpBufferStr(q, fmtId(fdwname));
16068 :
16069 100 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16070 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16071 :
16072 100 : appendPQExpBufferStr(q, ";\n");
16073 :
16074 100 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16075 : qsrvname);
16076 :
16077 100 : if (dopt->binary_upgrade)
16078 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16079 : "SERVER", qsrvname, NULL);
16080 :
16081 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16082 100 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16083 100 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16084 : .owner = srvinfo->rolname,
16085 : .description = "SERVER",
16086 : .section = SECTION_PRE_DATA,
16087 : .createStmt = q->data,
16088 : .dropStmt = delq->data));
16089 :
16090 : /* Dump Foreign Server Comments */
16091 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16092 0 : dumpComment(fout, "SERVER", qsrvname,
16093 0 : NULL, srvinfo->rolname,
16094 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16095 :
16096 : /* Handle the ACL */
16097 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16098 68 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16099 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16100 68 : NULL, srvinfo->rolname, &srvinfo->dacl);
16101 :
16102 : /* Dump user mappings */
16103 100 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16104 100 : dumpUserMappings(fout,
16105 100 : srvinfo->dobj.name, NULL,
16106 100 : srvinfo->rolname,
16107 100 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16108 :
16109 100 : PQclear(res);
16110 :
16111 100 : free(qsrvname);
16112 :
16113 100 : destroyPQExpBuffer(q);
16114 100 : destroyPQExpBuffer(delq);
16115 100 : destroyPQExpBuffer(query);
16116 : }
16117 :
16118 : /*
16119 : * dumpUserMappings
16120 : *
16121 : * This routine is used to dump any user mappings associated with the
16122 : * server handed to this routine. Should be called after ArchiveEntry()
16123 : * for the server.
16124 : */
16125 : static void
16126 100 : dumpUserMappings(Archive *fout,
16127 : const char *servername, const char *namespace,
16128 : const char *owner,
16129 : CatalogId catalogId, DumpId dumpId)
16130 : {
16131 : PQExpBuffer q;
16132 : PQExpBuffer delq;
16133 : PQExpBuffer query;
16134 : PQExpBuffer tag;
16135 : PGresult *res;
16136 : int ntups;
16137 : int i_usename;
16138 : int i_umoptions;
16139 : int i;
16140 :
16141 100 : q = createPQExpBuffer();
16142 100 : tag = createPQExpBuffer();
16143 100 : delq = createPQExpBuffer();
16144 100 : query = createPQExpBuffer();
16145 :
16146 : /*
16147 : * We read from the publicly accessible view pg_user_mappings, so as not
16148 : * to fail if run by a non-superuser. Note that the view will show
16149 : * umoptions as null if the user hasn't got privileges for the associated
16150 : * server; this means that pg_dump will dump such a mapping, but with no
16151 : * OPTIONS clause. A possible alternative is to skip such mappings
16152 : * altogether, but it's not clear that that's an improvement.
16153 : */
16154 100 : appendPQExpBuffer(query,
16155 : "SELECT usename, "
16156 : "array_to_string(ARRAY("
16157 : "SELECT quote_ident(option_name) || ' ' || "
16158 : "quote_literal(option_value) "
16159 : "FROM pg_options_to_table(umoptions) "
16160 : "ORDER BY option_name"
16161 : "), E',\n ') AS umoptions "
16162 : "FROM pg_user_mappings "
16163 : "WHERE srvid = '%u' "
16164 : "ORDER BY usename",
16165 : catalogId.oid);
16166 :
16167 100 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16168 :
16169 100 : ntups = PQntuples(res);
16170 100 : i_usename = PQfnumber(res, "usename");
16171 100 : i_umoptions = PQfnumber(res, "umoptions");
16172 :
16173 168 : for (i = 0; i < ntups; i++)
16174 : {
16175 : char *usename;
16176 : char *umoptions;
16177 :
16178 68 : usename = PQgetvalue(res, i, i_usename);
16179 68 : umoptions = PQgetvalue(res, i, i_umoptions);
16180 :
16181 68 : resetPQExpBuffer(q);
16182 68 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16183 68 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16184 :
16185 68 : if (umoptions && strlen(umoptions) > 0)
16186 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16187 :
16188 68 : appendPQExpBufferStr(q, ";\n");
16189 :
16190 68 : resetPQExpBuffer(delq);
16191 68 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16192 68 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16193 :
16194 68 : resetPQExpBuffer(tag);
16195 68 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16196 : usename, servername);
16197 :
16198 68 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16199 68 : ARCHIVE_OPTS(.tag = tag->data,
16200 : .namespace = namespace,
16201 : .owner = owner,
16202 : .description = "USER MAPPING",
16203 : .section = SECTION_PRE_DATA,
16204 : .createStmt = q->data,
16205 : .dropStmt = delq->data));
16206 : }
16207 :
16208 100 : PQclear(res);
16209 :
16210 100 : destroyPQExpBuffer(query);
16211 100 : destroyPQExpBuffer(delq);
16212 100 : destroyPQExpBuffer(tag);
16213 100 : destroyPQExpBuffer(q);
16214 100 : }
16215 :
16216 : /*
16217 : * Write out default privileges information
16218 : */
16219 : static void
16220 344 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16221 : {
16222 344 : DumpOptions *dopt = fout->dopt;
16223 : PQExpBuffer q;
16224 : PQExpBuffer tag;
16225 : const char *type;
16226 :
16227 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16228 344 : if (!dopt->dumpSchema || dopt->aclsSkip)
16229 56 : return;
16230 :
16231 288 : q = createPQExpBuffer();
16232 288 : tag = createPQExpBuffer();
16233 :
16234 288 : switch (daclinfo->defaclobjtype)
16235 : {
16236 134 : case DEFACLOBJ_RELATION:
16237 134 : type = "TABLES";
16238 134 : break;
16239 0 : case DEFACLOBJ_SEQUENCE:
16240 0 : type = "SEQUENCES";
16241 0 : break;
16242 134 : case DEFACLOBJ_FUNCTION:
16243 134 : type = "FUNCTIONS";
16244 134 : break;
16245 20 : case DEFACLOBJ_TYPE:
16246 20 : type = "TYPES";
16247 20 : break;
16248 0 : case DEFACLOBJ_NAMESPACE:
16249 0 : type = "SCHEMAS";
16250 0 : break;
16251 0 : case DEFACLOBJ_LARGEOBJECT:
16252 0 : type = "LARGE OBJECTS";
16253 0 : break;
16254 0 : default:
16255 : /* shouldn't get here */
16256 0 : pg_fatal("unrecognized object type in default privileges: %d",
16257 : (int) daclinfo->defaclobjtype);
16258 : type = ""; /* keep compiler quiet */
16259 : }
16260 :
16261 288 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16262 :
16263 : /* build the actual command(s) for this tuple */
16264 288 : if (!buildDefaultACLCommands(type,
16265 288 : daclinfo->dobj.namespace != NULL ?
16266 136 : daclinfo->dobj.namespace->dobj.name : NULL,
16267 288 : daclinfo->dacl.acl,
16268 288 : daclinfo->dacl.acldefault,
16269 288 : daclinfo->defaclrole,
16270 : fout->remoteVersion,
16271 : q))
16272 0 : pg_fatal("could not parse default ACL list (%s)",
16273 : daclinfo->dacl.acl);
16274 :
16275 288 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16276 288 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16277 288 : ARCHIVE_OPTS(.tag = tag->data,
16278 : .namespace = daclinfo->dobj.namespace ?
16279 : daclinfo->dobj.namespace->dobj.name : NULL,
16280 : .owner = daclinfo->defaclrole,
16281 : .description = "DEFAULT ACL",
16282 : .section = SECTION_POST_DATA,
16283 : .createStmt = q->data));
16284 :
16285 288 : destroyPQExpBuffer(tag);
16286 288 : destroyPQExpBuffer(q);
16287 : }
16288 :
16289 : /*----------
16290 : * Write out grant/revoke information
16291 : *
16292 : * 'objDumpId' is the dump ID of the underlying object.
16293 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16294 : * or InvalidDumpId if there is no need for a second dependency.
16295 : * 'type' must be one of
16296 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16297 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16298 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16299 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16300 : * (Currently we assume that subname is only provided for table columns.)
16301 : * 'nspname' is the namespace the object is in (NULL if none).
16302 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16303 : * to use the default for the object type.
16304 : * 'owner' is the owner, NULL if there is no owner (for languages).
16305 : * 'dacl' is the DumpableAcl struct for the object.
16306 : *
16307 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16308 : * no ACL entry was created.
16309 : *----------
16310 : */
16311 : static DumpId
16312 55376 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16313 : const char *type, const char *name, const char *subname,
16314 : const char *nspname, const char *tag, const char *owner,
16315 : const DumpableAcl *dacl)
16316 : {
16317 55376 : DumpId aclDumpId = InvalidDumpId;
16318 55376 : DumpOptions *dopt = fout->dopt;
16319 55376 : const char *acls = dacl->acl;
16320 55376 : const char *acldefault = dacl->acldefault;
16321 55376 : char privtype = dacl->privtype;
16322 55376 : const char *initprivs = dacl->initprivs;
16323 : const char *baseacls;
16324 : PQExpBuffer sql;
16325 :
16326 : /* Do nothing if ACL dump is not enabled */
16327 55376 : if (dopt->aclsSkip)
16328 648 : return InvalidDumpId;
16329 :
16330 : /* --data-only skips ACLs *except* large object ACLs */
16331 54728 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16332 0 : return InvalidDumpId;
16333 :
16334 54728 : sql = createPQExpBuffer();
16335 :
16336 : /*
16337 : * In binary upgrade mode, we don't run an extension's script but instead
16338 : * dump out the objects independently and then recreate them. To preserve
16339 : * any initial privileges which were set on extension objects, we need to
16340 : * compute the set of GRANT and REVOKE commands necessary to get from the
16341 : * default privileges of an object to its initial privileges as recorded
16342 : * in pg_init_privs.
16343 : *
16344 : * At restore time, we apply these commands after having called
16345 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16346 : * copy the results into pg_init_privs. This is how we preserve the
16347 : * contents of that catalog across binary upgrades.
16348 : */
16349 54728 : if (dopt->binary_upgrade && privtype == 'e' &&
16350 26 : initprivs && *initprivs != '\0')
16351 : {
16352 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16353 26 : if (!buildACLCommands(name, subname, nspname, type,
16354 : initprivs, acldefault, owner,
16355 : "", fout->remoteVersion, sql))
16356 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16357 : initprivs, acldefault, name, type);
16358 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16359 : }
16360 :
16361 : /*
16362 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16363 : * actual current ACL, starting from the initprivs if given, else from the
16364 : * object-type-specific default. Also, while buildACLCommands will assume
16365 : * that a NULL/empty acls string means it needn't do anything, what that
16366 : * actually represents is the object-type-specific default; so we need to
16367 : * substitute the acldefault string to get the right results in that case.
16368 : */
16369 54728 : if (initprivs && *initprivs != '\0')
16370 : {
16371 50988 : baseacls = initprivs;
16372 50988 : if (acls == NULL || *acls == '\0')
16373 34 : acls = acldefault;
16374 : }
16375 : else
16376 3740 : baseacls = acldefault;
16377 :
16378 54728 : if (!buildACLCommands(name, subname, nspname, type,
16379 : acls, baseacls, owner,
16380 : "", fout->remoteVersion, sql))
16381 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16382 : acls, baseacls, name, type);
16383 :
16384 54728 : if (sql->len > 0)
16385 : {
16386 3874 : PQExpBuffer tagbuf = createPQExpBuffer();
16387 : DumpId aclDeps[2];
16388 3874 : int nDeps = 0;
16389 :
16390 3874 : if (tag)
16391 0 : appendPQExpBufferStr(tagbuf, tag);
16392 3874 : else if (subname)
16393 2286 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16394 : else
16395 1588 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16396 :
16397 3874 : aclDeps[nDeps++] = objDumpId;
16398 3874 : if (altDumpId != InvalidDumpId)
16399 2112 : aclDeps[nDeps++] = altDumpId;
16400 :
16401 3874 : aclDumpId = createDumpId();
16402 :
16403 3874 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16404 3874 : ARCHIVE_OPTS(.tag = tagbuf->data,
16405 : .namespace = nspname,
16406 : .owner = owner,
16407 : .description = "ACL",
16408 : .section = SECTION_NONE,
16409 : .createStmt = sql->data,
16410 : .deps = aclDeps,
16411 : .nDeps = nDeps));
16412 :
16413 3874 : destroyPQExpBuffer(tagbuf);
16414 : }
16415 :
16416 54728 : destroyPQExpBuffer(sql);
16417 :
16418 54728 : return aclDumpId;
16419 : }
16420 :
16421 : /*
16422 : * dumpSecLabel
16423 : *
16424 : * This routine is used to dump any security labels associated with the
16425 : * object handed to this routine. The routine takes the object type
16426 : * and object name (ready to print, except for schema decoration), plus
16427 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16428 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16429 : * plus the dump ID for the object (for setting a dependency).
16430 : * If a matching pg_seclabel entry is found, it is dumped.
16431 : *
16432 : * Note: although this routine takes a dumpId for dependency purposes,
16433 : * that purpose is just to mark the dependency in the emitted dump file
16434 : * for possible future use by pg_restore. We do NOT use it for determining
16435 : * ordering of the label in the dump file, because this routine is called
16436 : * after dependency sorting occurs. This routine should be called just after
16437 : * calling ArchiveEntry() for the specified object.
16438 : */
16439 : static void
16440 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16441 : const char *namespace, const char *owner,
16442 : CatalogId catalogId, int subid, DumpId dumpId)
16443 : {
16444 0 : DumpOptions *dopt = fout->dopt;
16445 : SecLabelItem *labels;
16446 : int nlabels;
16447 : int i;
16448 : PQExpBuffer query;
16449 :
16450 : /* do nothing, if --no-security-labels is supplied */
16451 0 : if (dopt->no_security_labels)
16452 0 : return;
16453 :
16454 : /*
16455 : * Security labels are schema not data ... except large object labels are
16456 : * data
16457 : */
16458 0 : if (strcmp(type, "LARGE OBJECT") != 0)
16459 : {
16460 0 : if (!dopt->dumpSchema)
16461 0 : return;
16462 : }
16463 : else
16464 : {
16465 : /* We do dump large object security labels in binary-upgrade mode */
16466 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
16467 0 : return;
16468 : }
16469 :
16470 : /* Search for security labels associated with catalogId, using table */
16471 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16472 :
16473 0 : query = createPQExpBuffer();
16474 :
16475 0 : for (i = 0; i < nlabels; i++)
16476 : {
16477 : /*
16478 : * Ignore label entries for which the subid doesn't match.
16479 : */
16480 0 : if (labels[i].objsubid != subid)
16481 0 : continue;
16482 :
16483 0 : appendPQExpBuffer(query,
16484 : "SECURITY LABEL FOR %s ON %s ",
16485 0 : fmtId(labels[i].provider), type);
16486 0 : if (namespace && *namespace)
16487 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16488 0 : appendPQExpBuffer(query, "%s IS ", name);
16489 0 : appendStringLiteralAH(query, labels[i].label, fout);
16490 0 : appendPQExpBufferStr(query, ";\n");
16491 : }
16492 :
16493 0 : if (query->len > 0)
16494 : {
16495 0 : PQExpBuffer tag = createPQExpBuffer();
16496 :
16497 0 : appendPQExpBuffer(tag, "%s %s", type, name);
16498 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16499 0 : ARCHIVE_OPTS(.tag = tag->data,
16500 : .namespace = namespace,
16501 : .owner = owner,
16502 : .description = "SECURITY LABEL",
16503 : .section = SECTION_NONE,
16504 : .createStmt = query->data,
16505 : .deps = &dumpId,
16506 : .nDeps = 1));
16507 0 : destroyPQExpBuffer(tag);
16508 : }
16509 :
16510 0 : destroyPQExpBuffer(query);
16511 : }
16512 :
16513 : /*
16514 : * dumpTableSecLabel
16515 : *
16516 : * As above, but dump security label for both the specified table (or view)
16517 : * and its columns.
16518 : */
16519 : static void
16520 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16521 : {
16522 0 : DumpOptions *dopt = fout->dopt;
16523 : SecLabelItem *labels;
16524 : int nlabels;
16525 : int i;
16526 : PQExpBuffer query;
16527 : PQExpBuffer target;
16528 :
16529 : /* do nothing, if --no-security-labels is supplied */
16530 0 : if (dopt->no_security_labels)
16531 0 : return;
16532 :
16533 : /* SecLabel are SCHEMA not data */
16534 0 : if (!dopt->dumpSchema)
16535 0 : return;
16536 :
16537 : /* Search for comments associated with relation, using table */
16538 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16539 0 : tbinfo->dobj.catId.oid,
16540 : &labels);
16541 :
16542 : /* If security labels exist, build SECURITY LABEL statements */
16543 0 : if (nlabels <= 0)
16544 0 : return;
16545 :
16546 0 : query = createPQExpBuffer();
16547 0 : target = createPQExpBuffer();
16548 :
16549 0 : for (i = 0; i < nlabels; i++)
16550 : {
16551 : const char *colname;
16552 0 : const char *provider = labels[i].provider;
16553 0 : const char *label = labels[i].label;
16554 0 : int objsubid = labels[i].objsubid;
16555 :
16556 0 : resetPQExpBuffer(target);
16557 0 : if (objsubid == 0)
16558 : {
16559 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16560 0 : fmtQualifiedDumpable(tbinfo));
16561 : }
16562 : else
16563 : {
16564 0 : colname = getAttrName(objsubid, tbinfo);
16565 : /* first fmtXXX result must be consumed before calling again */
16566 0 : appendPQExpBuffer(target, "COLUMN %s",
16567 0 : fmtQualifiedDumpable(tbinfo));
16568 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16569 : }
16570 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16571 : fmtId(provider), target->data);
16572 0 : appendStringLiteralAH(query, label, fout);
16573 0 : appendPQExpBufferStr(query, ";\n");
16574 : }
16575 0 : if (query->len > 0)
16576 : {
16577 0 : resetPQExpBuffer(target);
16578 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16579 0 : fmtId(tbinfo->dobj.name));
16580 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16581 0 : ARCHIVE_OPTS(.tag = target->data,
16582 : .namespace = tbinfo->dobj.namespace->dobj.name,
16583 : .owner = tbinfo->rolname,
16584 : .description = "SECURITY LABEL",
16585 : .section = SECTION_NONE,
16586 : .createStmt = query->data,
16587 : .deps = &(tbinfo->dobj.dumpId),
16588 : .nDeps = 1));
16589 : }
16590 0 : destroyPQExpBuffer(query);
16591 0 : destroyPQExpBuffer(target);
16592 : }
16593 :
16594 : /*
16595 : * findSecLabels
16596 : *
16597 : * Find the security label(s), if any, associated with the given object.
16598 : * All the objsubid values associated with the given classoid/objoid are
16599 : * found with one search.
16600 : */
16601 : static int
16602 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16603 : {
16604 0 : SecLabelItem *middle = NULL;
16605 : SecLabelItem *low;
16606 : SecLabelItem *high;
16607 : int nmatch;
16608 :
16609 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16610 : {
16611 0 : *items = NULL;
16612 0 : return 0;
16613 : }
16614 :
16615 : /*
16616 : * Do binary search to find some item matching the object.
16617 : */
16618 0 : low = &seclabels[0];
16619 0 : high = &seclabels[nseclabels - 1];
16620 0 : while (low <= high)
16621 : {
16622 0 : middle = low + (high - low) / 2;
16623 :
16624 0 : if (classoid < middle->classoid)
16625 0 : high = middle - 1;
16626 0 : else if (classoid > middle->classoid)
16627 0 : low = middle + 1;
16628 0 : else if (objoid < middle->objoid)
16629 0 : high = middle - 1;
16630 0 : else if (objoid > middle->objoid)
16631 0 : low = middle + 1;
16632 : else
16633 0 : break; /* found a match */
16634 : }
16635 :
16636 0 : if (low > high) /* no matches */
16637 : {
16638 0 : *items = NULL;
16639 0 : return 0;
16640 : }
16641 :
16642 : /*
16643 : * Now determine how many items match the object. The search loop
16644 : * invariant still holds: only items between low and high inclusive could
16645 : * match.
16646 : */
16647 0 : nmatch = 1;
16648 0 : while (middle > low)
16649 : {
16650 0 : if (classoid != middle[-1].classoid ||
16651 0 : objoid != middle[-1].objoid)
16652 : break;
16653 0 : middle--;
16654 0 : nmatch++;
16655 : }
16656 :
16657 0 : *items = middle;
16658 :
16659 0 : middle += nmatch;
16660 0 : while (middle <= high)
16661 : {
16662 0 : if (classoid != middle->classoid ||
16663 0 : objoid != middle->objoid)
16664 : break;
16665 0 : middle++;
16666 0 : nmatch++;
16667 : }
16668 :
16669 0 : return nmatch;
16670 : }
16671 :
16672 : /*
16673 : * collectSecLabels
16674 : *
16675 : * Construct a table of all security labels available for database objects;
16676 : * also set the has-seclabel component flag for each relevant object.
16677 : *
16678 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16679 : */
16680 : static void
16681 364 : collectSecLabels(Archive *fout)
16682 : {
16683 : PGresult *res;
16684 : PQExpBuffer query;
16685 : int i_label;
16686 : int i_provider;
16687 : int i_classoid;
16688 : int i_objoid;
16689 : int i_objsubid;
16690 : int ntups;
16691 : int i;
16692 : DumpableObject *dobj;
16693 :
16694 364 : query = createPQExpBuffer();
16695 :
16696 364 : appendPQExpBufferStr(query,
16697 : "SELECT label, provider, classoid, objoid, objsubid "
16698 : "FROM pg_catalog.pg_seclabel "
16699 : "ORDER BY classoid, objoid, objsubid");
16700 :
16701 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16702 :
16703 : /* Construct lookup table containing OIDs in numeric form */
16704 364 : i_label = PQfnumber(res, "label");
16705 364 : i_provider = PQfnumber(res, "provider");
16706 364 : i_classoid = PQfnumber(res, "classoid");
16707 364 : i_objoid = PQfnumber(res, "objoid");
16708 364 : i_objsubid = PQfnumber(res, "objsubid");
16709 :
16710 364 : ntups = PQntuples(res);
16711 :
16712 364 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16713 364 : nseclabels = 0;
16714 364 : dobj = NULL;
16715 :
16716 364 : for (i = 0; i < ntups; i++)
16717 : {
16718 : CatalogId objId;
16719 : int subid;
16720 :
16721 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16722 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16723 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16724 :
16725 : /* We needn't remember labels that don't match any dumpable object */
16726 0 : if (dobj == NULL ||
16727 0 : dobj->catId.tableoid != objId.tableoid ||
16728 0 : dobj->catId.oid != objId.oid)
16729 0 : dobj = findObjectByCatalogId(objId);
16730 0 : if (dobj == NULL)
16731 0 : continue;
16732 :
16733 : /*
16734 : * Labels on columns of composite types are linked to the type's
16735 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16736 : * in the type's own DumpableObject.
16737 : */
16738 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16739 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16740 0 : {
16741 : TypeInfo *cTypeInfo;
16742 :
16743 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16744 0 : if (cTypeInfo)
16745 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16746 : }
16747 : else
16748 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16749 :
16750 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16751 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16752 0 : seclabels[nseclabels].classoid = objId.tableoid;
16753 0 : seclabels[nseclabels].objoid = objId.oid;
16754 0 : seclabels[nseclabels].objsubid = subid;
16755 0 : nseclabels++;
16756 : }
16757 :
16758 364 : PQclear(res);
16759 364 : destroyPQExpBuffer(query);
16760 364 : }
16761 :
16762 : /*
16763 : * dumpTable
16764 : * write out to fout the declarations (not data) of a user-defined table
16765 : */
16766 : static void
16767 61058 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16768 : {
16769 61058 : DumpOptions *dopt = fout->dopt;
16770 61058 : DumpId tableAclDumpId = InvalidDumpId;
16771 : char *namecopy;
16772 :
16773 : /* Do nothing if not dumping schema */
16774 61058 : if (!dopt->dumpSchema)
16775 3036 : return;
16776 :
16777 58022 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16778 : {
16779 13636 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16780 768 : dumpSequence(fout, tbinfo);
16781 : else
16782 12868 : dumpTableSchema(fout, tbinfo);
16783 : }
16784 :
16785 : /* Handle the ACL here */
16786 58022 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16787 58022 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16788 : {
16789 45864 : const char *objtype =
16790 45864 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16791 :
16792 : tableAclDumpId =
16793 45864 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16794 : objtype, namecopy, NULL,
16795 45864 : tbinfo->dobj.namespace->dobj.name,
16796 45864 : NULL, tbinfo->rolname, &tbinfo->dacl);
16797 : }
16798 :
16799 : /*
16800 : * Handle column ACLs, if any. Note: we pull these with a separate query
16801 : * rather than trying to fetch them during getTableAttrs, so that we won't
16802 : * miss ACLs on system columns. Doing it this way also allows us to dump
16803 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16804 : */
16805 58022 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16806 : {
16807 572 : PQExpBuffer query = createPQExpBuffer();
16808 : PGresult *res;
16809 : int i;
16810 :
16811 572 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16812 : {
16813 : /* Set up query for column ACLs */
16814 312 : appendPQExpBufferStr(query,
16815 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16816 :
16817 312 : if (fout->remoteVersion >= 90600)
16818 : {
16819 : /*
16820 : * In principle we should call acldefault('c', relowner) to
16821 : * get the default ACL for a column. However, we don't
16822 : * currently store the numeric OID of the relowner in
16823 : * TableInfo. We could convert the owner name using regrole,
16824 : * but that creates a risk of failure due to concurrent role
16825 : * renames. Given that the default ACL for columns is empty
16826 : * and is likely to stay that way, it's not worth extra cycles
16827 : * and risk to avoid hard-wiring that knowledge here.
16828 : */
16829 312 : appendPQExpBufferStr(query,
16830 : "SELECT at.attname, "
16831 : "at.attacl, "
16832 : "'{}' AS acldefault, "
16833 : "pip.privtype, pip.initprivs "
16834 : "FROM pg_catalog.pg_attribute at "
16835 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16836 : "(at.attrelid = pip.objoid "
16837 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16838 : "AND at.attnum = pip.objsubid) "
16839 : "WHERE at.attrelid = $1 AND "
16840 : "NOT at.attisdropped "
16841 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16842 : "ORDER BY at.attnum");
16843 : }
16844 : else
16845 : {
16846 0 : appendPQExpBufferStr(query,
16847 : "SELECT attname, attacl, '{}' AS acldefault, "
16848 : "NULL AS privtype, NULL AS initprivs "
16849 : "FROM pg_catalog.pg_attribute "
16850 : "WHERE attrelid = $1 AND NOT attisdropped "
16851 : "AND attacl IS NOT NULL "
16852 : "ORDER BY attnum");
16853 : }
16854 :
16855 312 : ExecuteSqlStatement(fout, query->data);
16856 :
16857 312 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16858 : }
16859 :
16860 572 : printfPQExpBuffer(query,
16861 : "EXECUTE getColumnACLs('%u')",
16862 572 : tbinfo->dobj.catId.oid);
16863 :
16864 572 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16865 :
16866 8366 : for (i = 0; i < PQntuples(res); i++)
16867 : {
16868 7794 : char *attname = PQgetvalue(res, i, 0);
16869 7794 : char *attacl = PQgetvalue(res, i, 1);
16870 7794 : char *acldefault = PQgetvalue(res, i, 2);
16871 7794 : char privtype = *(PQgetvalue(res, i, 3));
16872 7794 : char *initprivs = PQgetvalue(res, i, 4);
16873 : DumpableAcl coldacl;
16874 : char *attnamecopy;
16875 :
16876 7794 : coldacl.acl = attacl;
16877 7794 : coldacl.acldefault = acldefault;
16878 7794 : coldacl.privtype = privtype;
16879 7794 : coldacl.initprivs = initprivs;
16880 7794 : attnamecopy = pg_strdup(fmtId(attname));
16881 :
16882 : /*
16883 : * Column's GRANT type is always TABLE. Each column ACL depends
16884 : * on the table-level ACL, since we can restore column ACLs in
16885 : * parallel but the table-level ACL has to be done first.
16886 : */
16887 7794 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16888 : "TABLE", namecopy, attnamecopy,
16889 7794 : tbinfo->dobj.namespace->dobj.name,
16890 7794 : NULL, tbinfo->rolname, &coldacl);
16891 7794 : free(attnamecopy);
16892 : }
16893 572 : PQclear(res);
16894 572 : destroyPQExpBuffer(query);
16895 : }
16896 :
16897 58022 : free(namecopy);
16898 : }
16899 :
16900 : /*
16901 : * Create the AS clause for a view or materialized view. The semicolon is
16902 : * stripped because a materialized view must add a WITH NO DATA clause.
16903 : *
16904 : * This returns a new buffer which must be freed by the caller.
16905 : */
16906 : static PQExpBuffer
16907 1846 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16908 : {
16909 1846 : PQExpBuffer query = createPQExpBuffer();
16910 1846 : PQExpBuffer result = createPQExpBuffer();
16911 : PGresult *res;
16912 : int len;
16913 :
16914 : /* Fetch the view definition */
16915 1846 : appendPQExpBuffer(query,
16916 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16917 1846 : tbinfo->dobj.catId.oid);
16918 :
16919 1846 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16920 :
16921 1846 : if (PQntuples(res) != 1)
16922 : {
16923 0 : if (PQntuples(res) < 1)
16924 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16925 : tbinfo->dobj.name);
16926 : else
16927 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16928 : tbinfo->dobj.name);
16929 : }
16930 :
16931 1846 : len = PQgetlength(res, 0, 0);
16932 :
16933 1846 : if (len == 0)
16934 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16935 : tbinfo->dobj.name);
16936 :
16937 : /* Strip off the trailing semicolon so that other things may follow. */
16938 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16939 1846 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16940 :
16941 1846 : PQclear(res);
16942 1846 : destroyPQExpBuffer(query);
16943 :
16944 1846 : return result;
16945 : }
16946 :
16947 : /*
16948 : * Create a dummy AS clause for a view. This is used when the real view
16949 : * definition has to be postponed because of circular dependencies.
16950 : * We must duplicate the view's external properties -- column names and types
16951 : * (including collation) -- so that it works for subsequent references.
16952 : *
16953 : * This returns a new buffer which must be freed by the caller.
16954 : */
16955 : static PQExpBuffer
16956 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16957 : {
16958 40 : PQExpBuffer result = createPQExpBuffer();
16959 : int j;
16960 :
16961 40 : appendPQExpBufferStr(result, "SELECT");
16962 :
16963 80 : for (j = 0; j < tbinfo->numatts; j++)
16964 : {
16965 40 : if (j > 0)
16966 20 : appendPQExpBufferChar(result, ',');
16967 40 : appendPQExpBufferStr(result, "\n ");
16968 :
16969 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16970 :
16971 : /*
16972 : * Must add collation if not default for the type, because CREATE OR
16973 : * REPLACE VIEW won't change it
16974 : */
16975 40 : if (OidIsValid(tbinfo->attcollation[j]))
16976 : {
16977 : CollInfo *coll;
16978 :
16979 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16980 0 : if (coll)
16981 0 : appendPQExpBuffer(result, " COLLATE %s",
16982 0 : fmtQualifiedDumpable(coll));
16983 : }
16984 :
16985 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16986 : }
16987 :
16988 40 : return result;
16989 : }
16990 :
16991 : /*
16992 : * dumpTableSchema
16993 : * write the declaration (not data) of one user-defined table or view
16994 : */
16995 : static void
16996 12868 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16997 : {
16998 12868 : DumpOptions *dopt = fout->dopt;
16999 12868 : PQExpBuffer q = createPQExpBuffer();
17000 12868 : PQExpBuffer delq = createPQExpBuffer();
17001 12868 : PQExpBuffer extra = createPQExpBuffer();
17002 : char *qrelname;
17003 : char *qualrelname;
17004 : int numParents;
17005 : TableInfo **parents;
17006 : int actual_atts; /* number of attrs in this CREATE statement */
17007 : const char *reltypename;
17008 : char *storage;
17009 : int j,
17010 : k;
17011 :
17012 : /* We had better have loaded per-column details about this table */
17013 : Assert(tbinfo->interesting);
17014 :
17015 12868 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17016 12868 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17017 :
17018 12868 : if (tbinfo->hasoids)
17019 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17020 : qrelname);
17021 :
17022 12868 : if (dopt->binary_upgrade)
17023 1736 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17024 :
17025 : /* Is it a table or a view? */
17026 12868 : if (tbinfo->relkind == RELKIND_VIEW)
17027 : {
17028 : PQExpBuffer result;
17029 :
17030 : /*
17031 : * Note: keep this code in sync with the is_view case in dumpRule()
17032 : */
17033 :
17034 1066 : reltypename = "VIEW";
17035 :
17036 1066 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17037 :
17038 1066 : if (dopt->binary_upgrade)
17039 104 : binary_upgrade_set_pg_class_oids(fout, q,
17040 104 : tbinfo->dobj.catId.oid);
17041 :
17042 1066 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17043 :
17044 1066 : if (tbinfo->dummy_view)
17045 20 : result = createDummyViewAsClause(fout, tbinfo);
17046 : else
17047 : {
17048 1046 : if (nonemptyReloptions(tbinfo->reloptions))
17049 : {
17050 128 : appendPQExpBufferStr(q, " WITH (");
17051 128 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17052 128 : appendPQExpBufferChar(q, ')');
17053 : }
17054 1046 : result = createViewAsClause(fout, tbinfo);
17055 : }
17056 1066 : appendPQExpBuffer(q, " AS\n%s", result->data);
17057 1066 : destroyPQExpBuffer(result);
17058 :
17059 1066 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17060 70 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17061 1066 : appendPQExpBufferStr(q, ";\n");
17062 : }
17063 : else
17064 : {
17065 11802 : char *partkeydef = NULL;
17066 11802 : char *ftoptions = NULL;
17067 11802 : char *srvname = NULL;
17068 11802 : const char *foreign = "";
17069 :
17070 : /*
17071 : * Set reltypename, and collect any relkind-specific data that we
17072 : * didn't fetch during getTables().
17073 : */
17074 11802 : switch (tbinfo->relkind)
17075 : {
17076 1156 : case RELKIND_PARTITIONED_TABLE:
17077 : {
17078 1156 : PQExpBuffer query = createPQExpBuffer();
17079 : PGresult *res;
17080 :
17081 1156 : reltypename = "TABLE";
17082 :
17083 : /* retrieve partition key definition */
17084 1156 : appendPQExpBuffer(query,
17085 : "SELECT pg_get_partkeydef('%u')",
17086 1156 : tbinfo->dobj.catId.oid);
17087 1156 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17088 1156 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17089 1156 : PQclear(res);
17090 1156 : destroyPQExpBuffer(query);
17091 1156 : break;
17092 : }
17093 74 : case RELKIND_FOREIGN_TABLE:
17094 : {
17095 74 : PQExpBuffer query = createPQExpBuffer();
17096 : PGresult *res;
17097 : int i_srvname;
17098 : int i_ftoptions;
17099 :
17100 74 : reltypename = "FOREIGN TABLE";
17101 :
17102 : /* retrieve name of foreign server and generic options */
17103 74 : appendPQExpBuffer(query,
17104 : "SELECT fs.srvname, "
17105 : "pg_catalog.array_to_string(ARRAY("
17106 : "SELECT pg_catalog.quote_ident(option_name) || "
17107 : "' ' || pg_catalog.quote_literal(option_value) "
17108 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17109 : "ORDER BY option_name"
17110 : "), E',\n ') AS ftoptions "
17111 : "FROM pg_catalog.pg_foreign_table ft "
17112 : "JOIN pg_catalog.pg_foreign_server fs "
17113 : "ON (fs.oid = ft.ftserver) "
17114 : "WHERE ft.ftrelid = '%u'",
17115 74 : tbinfo->dobj.catId.oid);
17116 74 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17117 74 : i_srvname = PQfnumber(res, "srvname");
17118 74 : i_ftoptions = PQfnumber(res, "ftoptions");
17119 74 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17120 74 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17121 74 : PQclear(res);
17122 74 : destroyPQExpBuffer(query);
17123 :
17124 74 : foreign = "FOREIGN ";
17125 74 : break;
17126 : }
17127 780 : case RELKIND_MATVIEW:
17128 780 : reltypename = "MATERIALIZED VIEW";
17129 780 : break;
17130 9792 : default:
17131 9792 : reltypename = "TABLE";
17132 9792 : break;
17133 : }
17134 :
17135 11802 : numParents = tbinfo->numParents;
17136 11802 : parents = tbinfo->parents;
17137 :
17138 11802 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17139 :
17140 11802 : if (dopt->binary_upgrade)
17141 1632 : binary_upgrade_set_pg_class_oids(fout, q,
17142 1632 : tbinfo->dobj.catId.oid);
17143 :
17144 : /*
17145 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17146 : * ignore it when dumping if it was set in this case.
17147 : */
17148 11802 : appendPQExpBuffer(q, "CREATE %s%s %s",
17149 11802 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17150 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17151 : "UNLOGGED " : "",
17152 : reltypename,
17153 : qualrelname);
17154 :
17155 : /*
17156 : * Attach to type, if reloftype; except in case of a binary upgrade,
17157 : * we dump the table normally and attach it to the type afterward.
17158 : */
17159 11802 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17160 48 : appendPQExpBuffer(q, " OF %s",
17161 48 : getFormattedTypeName(fout, tbinfo->reloftype,
17162 : zeroIsError));
17163 :
17164 11802 : if (tbinfo->relkind != RELKIND_MATVIEW)
17165 : {
17166 : /* Dump the attributes */
17167 11022 : actual_atts = 0;
17168 51178 : for (j = 0; j < tbinfo->numatts; j++)
17169 : {
17170 : /*
17171 : * Normally, dump if it's locally defined in this table, and
17172 : * not dropped. But for binary upgrade, we'll dump all the
17173 : * columns, and then fix up the dropped and nonlocal cases
17174 : * below.
17175 : */
17176 40156 : if (shouldPrintColumn(dopt, tbinfo, j))
17177 : {
17178 : bool print_default;
17179 : bool print_notnull;
17180 :
17181 : /*
17182 : * Default value --- suppress if to be printed separately
17183 : * or not at all.
17184 : */
17185 78458 : print_default = (tbinfo->attrdefs[j] != NULL &&
17186 40208 : tbinfo->attrdefs[j]->dobj.dump &&
17187 2058 : !tbinfo->attrdefs[j]->separate);
17188 :
17189 : /*
17190 : * Not Null constraint --- print it if it is locally
17191 : * defined, or if binary upgrade. (In the latter case, we
17192 : * reset conislocal below.)
17193 : */
17194 42710 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17195 4560 : (tbinfo->notnull_islocal[j] ||
17196 1268 : dopt->binary_upgrade ||
17197 1100 : tbinfo->ispartition));
17198 :
17199 : /*
17200 : * Skip column if fully defined by reloftype, except in
17201 : * binary upgrade
17202 : */
17203 38150 : if (OidIsValid(tbinfo->reloftype) &&
17204 100 : !print_default && !print_notnull &&
17205 60 : !dopt->binary_upgrade)
17206 48 : continue;
17207 :
17208 : /* Format properly if not first attr */
17209 38102 : if (actual_atts == 0)
17210 10342 : appendPQExpBufferStr(q, " (");
17211 : else
17212 27760 : appendPQExpBufferChar(q, ',');
17213 38102 : appendPQExpBufferStr(q, "\n ");
17214 38102 : actual_atts++;
17215 :
17216 : /* Attribute name */
17217 38102 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17218 :
17219 38102 : if (tbinfo->attisdropped[j])
17220 : {
17221 : /*
17222 : * ALTER TABLE DROP COLUMN clears
17223 : * pg_attribute.atttypid, so we will not have gotten a
17224 : * valid type name; insert INTEGER as a stopgap. We'll
17225 : * clean things up later.
17226 : */
17227 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17228 : /* and skip to the next column */
17229 168 : continue;
17230 : }
17231 :
17232 : /*
17233 : * Attribute type; print it except when creating a typed
17234 : * table ('OF type_name'), but in binary-upgrade mode,
17235 : * print it in that case too.
17236 : */
17237 37934 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17238 : {
17239 37902 : appendPQExpBuffer(q, " %s",
17240 37902 : tbinfo->atttypnames[j]);
17241 : }
17242 :
17243 37934 : if (print_default)
17244 : {
17245 1786 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17246 572 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17247 572 : tbinfo->attrdefs[j]->adef_expr);
17248 1214 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17249 452 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17250 452 : tbinfo->attrdefs[j]->adef_expr);
17251 : else
17252 762 : appendPQExpBuffer(q, " DEFAULT %s",
17253 762 : tbinfo->attrdefs[j]->adef_expr);
17254 : }
17255 :
17256 37934 : if (print_notnull)
17257 : {
17258 4492 : if (tbinfo->notnull_constrs[j][0] == '\0')
17259 3170 : appendPQExpBufferStr(q, " NOT NULL");
17260 : else
17261 1322 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17262 1322 : fmtId(tbinfo->notnull_constrs[j]));
17263 :
17264 4492 : if (tbinfo->notnull_noinh[j])
17265 0 : appendPQExpBufferStr(q, " NO INHERIT");
17266 : }
17267 :
17268 : /* Add collation if not default for the type */
17269 37934 : if (OidIsValid(tbinfo->attcollation[j]))
17270 : {
17271 : CollInfo *coll;
17272 :
17273 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
17274 394 : if (coll)
17275 394 : appendPQExpBuffer(q, " COLLATE %s",
17276 394 : fmtQualifiedDumpable(coll));
17277 : }
17278 : }
17279 :
17280 : /*
17281 : * On the other hand, if we choose not to print a column
17282 : * (likely because it is created by inheritance), but the
17283 : * column has a locally-defined not-null constraint, we need
17284 : * to dump the constraint as a standalone object.
17285 : *
17286 : * This syntax isn't SQL-conforming, but if you wanted
17287 : * standard output you wouldn't be creating non-standard
17288 : * objects to begin with.
17289 : */
17290 39940 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17291 2006 : !tbinfo->attisdropped[j] &&
17292 1274 : tbinfo->notnull_constrs[j] != NULL &&
17293 364 : tbinfo->notnull_islocal[j])
17294 : {
17295 : /* Format properly if not first attr */
17296 116 : if (actual_atts == 0)
17297 108 : appendPQExpBufferStr(q, " (");
17298 : else
17299 8 : appendPQExpBufferChar(q, ',');
17300 116 : appendPQExpBufferStr(q, "\n ");
17301 116 : actual_atts++;
17302 :
17303 116 : if (tbinfo->notnull_constrs[j][0] == '\0')
17304 8 : appendPQExpBuffer(q, "NOT NULL %s",
17305 8 : fmtId(tbinfo->attnames[j]));
17306 : else
17307 216 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17308 108 : tbinfo->notnull_constrs[j],
17309 108 : fmtId(tbinfo->attnames[j]));
17310 : }
17311 : }
17312 :
17313 : /*
17314 : * Add non-inherited CHECK constraints, if any.
17315 : *
17316 : * For partitions, we need to include check constraints even if
17317 : * they're not defined locally, because the ALTER TABLE ATTACH
17318 : * PARTITION that we'll emit later expects the constraint to be
17319 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17320 : */
17321 12248 : for (j = 0; j < tbinfo->ncheck; j++)
17322 : {
17323 1226 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17324 :
17325 1226 : if (constr->separate ||
17326 1086 : (!constr->conislocal && !tbinfo->ispartition))
17327 220 : continue;
17328 :
17329 1006 : if (actual_atts == 0)
17330 32 : appendPQExpBufferStr(q, " (\n ");
17331 : else
17332 974 : appendPQExpBufferStr(q, ",\n ");
17333 :
17334 1006 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17335 1006 : fmtId(constr->dobj.name));
17336 1006 : appendPQExpBufferStr(q, constr->condef);
17337 :
17338 1006 : actual_atts++;
17339 : }
17340 :
17341 11022 : if (actual_atts)
17342 10482 : appendPQExpBufferStr(q, "\n)");
17343 540 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17344 : {
17345 : /*
17346 : * No attributes? we must have a parenthesized attribute list,
17347 : * even though empty, when not using the OF TYPE syntax.
17348 : */
17349 516 : appendPQExpBufferStr(q, " (\n)");
17350 : }
17351 :
17352 : /*
17353 : * Emit the INHERITS clause (not for partitions), except in
17354 : * binary-upgrade mode.
17355 : */
17356 11022 : if (numParents > 0 && !tbinfo->ispartition &&
17357 1004 : !dopt->binary_upgrade)
17358 : {
17359 878 : appendPQExpBufferStr(q, "\nINHERITS (");
17360 1904 : for (k = 0; k < numParents; k++)
17361 : {
17362 1026 : TableInfo *parentRel = parents[k];
17363 :
17364 1026 : if (k > 0)
17365 148 : appendPQExpBufferStr(q, ", ");
17366 1026 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17367 : }
17368 878 : appendPQExpBufferChar(q, ')');
17369 : }
17370 :
17371 11022 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17372 1156 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17373 :
17374 11022 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17375 74 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17376 : }
17377 :
17378 23314 : if (nonemptyReloptions(tbinfo->reloptions) ||
17379 11512 : nonemptyReloptions(tbinfo->toast_reloptions))
17380 : {
17381 290 : bool addcomma = false;
17382 :
17383 290 : appendPQExpBufferStr(q, "\nWITH (");
17384 290 : if (nonemptyReloptions(tbinfo->reloptions))
17385 : {
17386 290 : addcomma = true;
17387 290 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17388 : }
17389 290 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17390 : {
17391 10 : if (addcomma)
17392 10 : appendPQExpBufferStr(q, ", ");
17393 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17394 : fout);
17395 : }
17396 290 : appendPQExpBufferChar(q, ')');
17397 : }
17398 :
17399 : /* Dump generic options if any */
17400 11802 : if (ftoptions && ftoptions[0])
17401 70 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17402 :
17403 : /*
17404 : * For materialized views, create the AS clause just like a view. At
17405 : * this point, we always mark the view as not populated.
17406 : */
17407 11802 : if (tbinfo->relkind == RELKIND_MATVIEW)
17408 : {
17409 : PQExpBuffer result;
17410 :
17411 780 : result = createViewAsClause(fout, tbinfo);
17412 780 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17413 : result->data);
17414 780 : destroyPQExpBuffer(result);
17415 : }
17416 : else
17417 11022 : appendPQExpBufferStr(q, ";\n");
17418 :
17419 : /* Materialized views can depend on extensions */
17420 11802 : if (tbinfo->relkind == RELKIND_MATVIEW)
17421 780 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17422 : "pg_catalog.pg_class",
17423 : "MATERIALIZED VIEW",
17424 : qualrelname);
17425 :
17426 : /*
17427 : * in binary upgrade mode, update the catalog with any missing values
17428 : * that might be present.
17429 : */
17430 11802 : if (dopt->binary_upgrade)
17431 : {
17432 7904 : for (j = 0; j < tbinfo->numatts; j++)
17433 : {
17434 6272 : if (tbinfo->attmissingval[j][0] != '\0')
17435 : {
17436 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17437 4 : appendPQExpBufferStr(q,
17438 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17439 4 : appendStringLiteralAH(q, qualrelname, fout);
17440 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17441 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17442 4 : appendPQExpBufferChar(q, ',');
17443 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17444 4 : appendPQExpBufferStr(q, ");\n\n");
17445 : }
17446 : }
17447 : }
17448 :
17449 : /*
17450 : * To create binary-compatible heap files, we have to ensure the same
17451 : * physical column order, including dropped columns, as in the
17452 : * original. Therefore, we create dropped columns above and drop them
17453 : * here, also updating their attlen/attalign values so that the
17454 : * dropped column can be skipped properly. (We do not bother with
17455 : * restoring the original attbyval setting.) Also, inheritance
17456 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17457 : * using an INHERITS clause --- the latter would possibly mess up the
17458 : * column order. That also means we have to take care about setting
17459 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17460 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17461 : *
17462 : * We process foreign and partitioned tables here, even though they
17463 : * lack heap storage, because they can participate in inheritance
17464 : * relationships and we want this stuff to be consistent across the
17465 : * inheritance tree. We can exclude indexes, toast tables, sequences
17466 : * and matviews, even though they have storage, because we don't
17467 : * support altering or dropping columns in them, nor can they be part
17468 : * of inheritance trees.
17469 : */
17470 11802 : if (dopt->binary_upgrade &&
17471 1632 : (tbinfo->relkind == RELKIND_RELATION ||
17472 220 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17473 218 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17474 : {
17475 : bool firstitem;
17476 : bool firstitem_extra;
17477 :
17478 : /*
17479 : * Drop any dropped columns. Merge the pg_attribute manipulations
17480 : * into a single SQL command, so that we don't cause repeated
17481 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17482 : * relcache bloat while dropping N columns.
17483 : */
17484 1596 : resetPQExpBuffer(extra);
17485 1596 : firstitem = true;
17486 7824 : for (j = 0; j < tbinfo->numatts; j++)
17487 : {
17488 6228 : if (tbinfo->attisdropped[j])
17489 : {
17490 168 : if (firstitem)
17491 : {
17492 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17493 : "UPDATE pg_catalog.pg_attribute\n"
17494 : "SET attlen = v.dlen, "
17495 : "attalign = v.dalign, "
17496 : "attbyval = false\n"
17497 : "FROM (VALUES ");
17498 76 : firstitem = false;
17499 : }
17500 : else
17501 92 : appendPQExpBufferStr(q, ",\n ");
17502 168 : appendPQExpBufferChar(q, '(');
17503 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17504 168 : appendPQExpBuffer(q, ", %d, '%c')",
17505 168 : tbinfo->attlen[j],
17506 168 : tbinfo->attalign[j]);
17507 : /* The ALTER ... DROP COLUMN commands must come after */
17508 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17509 : foreign, qualrelname);
17510 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17511 168 : fmtId(tbinfo->attnames[j]));
17512 : }
17513 : }
17514 1596 : if (!firstitem)
17515 : {
17516 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17517 : "WHERE attrelid = ");
17518 76 : appendStringLiteralAH(q, qualrelname, fout);
17519 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17520 : " AND attname = v.dname;\n");
17521 : /* Now we can issue the actual DROP COLUMN commands */
17522 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17523 : }
17524 :
17525 : /*
17526 : * Fix up inherited columns. As above, do the pg_attribute
17527 : * manipulations in a single SQL command.
17528 : */
17529 1596 : firstitem = true;
17530 7824 : for (j = 0; j < tbinfo->numatts; j++)
17531 : {
17532 6228 : if (!tbinfo->attisdropped[j] &&
17533 6060 : !tbinfo->attislocal[j])
17534 : {
17535 1206 : if (firstitem)
17536 : {
17537 532 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17538 532 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17539 : "SET attislocal = false\n"
17540 : "WHERE attrelid = ");
17541 532 : appendStringLiteralAH(q, qualrelname, fout);
17542 532 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17543 : " AND attname IN (");
17544 532 : firstitem = false;
17545 : }
17546 : else
17547 674 : appendPQExpBufferStr(q, ", ");
17548 1206 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17549 : }
17550 : }
17551 1596 : if (!firstitem)
17552 532 : appendPQExpBufferStr(q, ");\n");
17553 :
17554 : /*
17555 : * Fix up not-null constraints that come from inheritance. As
17556 : * above, do the pg_constraint manipulations in a single SQL
17557 : * command. (Actually, two in special cases, if we're doing an
17558 : * upgrade from < 18).
17559 : */
17560 1596 : firstitem = true;
17561 1596 : firstitem_extra = true;
17562 1596 : resetPQExpBuffer(extra);
17563 7824 : for (j = 0; j < tbinfo->numatts; j++)
17564 : {
17565 : /*
17566 : * If a not-null constraint comes from inheritance, reset
17567 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17568 : * below. Special hack: in versions < 18, columns with no
17569 : * local definition need their constraint to be matched by
17570 : * column number in conkeys instead of by constraint name,
17571 : * because the latter is not available. (We distinguish the
17572 : * case because the constraint name is the empty string.)
17573 : */
17574 6228 : if (tbinfo->notnull_constrs[j] != NULL &&
17575 580 : !tbinfo->notnull_islocal[j])
17576 : {
17577 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17578 : {
17579 142 : if (firstitem)
17580 : {
17581 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17582 : "SET conislocal = false\n"
17583 : "WHERE contype = 'n' AND conrelid = ");
17584 122 : appendStringLiteralAH(q, qualrelname, fout);
17585 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17586 : "conname IN (");
17587 122 : firstitem = false;
17588 : }
17589 : else
17590 20 : appendPQExpBufferStr(q, ", ");
17591 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17592 : }
17593 : else
17594 : {
17595 26 : if (firstitem_extra)
17596 : {
17597 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17598 : "SET conislocal = false\n"
17599 : "WHERE contype = 'n' AND conrelid = ");
17600 26 : appendStringLiteralAH(extra, qualrelname, fout);
17601 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17602 : "conkey IN (");
17603 26 : firstitem_extra = false;
17604 : }
17605 : else
17606 0 : appendPQExpBufferStr(extra, ", ");
17607 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17608 : }
17609 : }
17610 : }
17611 1596 : if (!firstitem)
17612 122 : appendPQExpBufferStr(q, ");\n");
17613 1596 : if (!firstitem_extra)
17614 26 : appendPQExpBufferStr(extra, ");\n");
17615 :
17616 1596 : if (extra->len > 0)
17617 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17618 :
17619 : /*
17620 : * Add inherited CHECK constraints, if any.
17621 : *
17622 : * For partitions, they were already dumped, and conislocal
17623 : * doesn't need fixing.
17624 : *
17625 : * As above, issue only one direct manipulation of pg_constraint.
17626 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17627 : * commands into one as well, refrain for now due to concern about
17628 : * possible backend memory bloat if there are many such
17629 : * constraints.
17630 : */
17631 1596 : resetPQExpBuffer(extra);
17632 1596 : firstitem = true;
17633 1724 : for (k = 0; k < tbinfo->ncheck; k++)
17634 : {
17635 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17636 :
17637 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17638 124 : continue;
17639 :
17640 4 : if (firstitem)
17641 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17642 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17643 : foreign, qualrelname,
17644 4 : fmtId(constr->dobj.name),
17645 : constr->condef);
17646 : /* Update pg_constraint after all the ALTER TABLEs */
17647 4 : if (firstitem)
17648 : {
17649 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17650 : "SET conislocal = false\n"
17651 : "WHERE contype = 'c' AND conrelid = ");
17652 4 : appendStringLiteralAH(extra, qualrelname, fout);
17653 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17654 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17655 4 : firstitem = false;
17656 : }
17657 : else
17658 0 : appendPQExpBufferStr(extra, ", ");
17659 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17660 : }
17661 1596 : if (!firstitem)
17662 : {
17663 4 : appendPQExpBufferStr(extra, ");\n");
17664 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17665 : }
17666 :
17667 1596 : if (numParents > 0 && !tbinfo->ispartition)
17668 : {
17669 126 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17670 274 : for (k = 0; k < numParents; k++)
17671 : {
17672 148 : TableInfo *parentRel = parents[k];
17673 :
17674 148 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17675 : qualrelname,
17676 148 : fmtQualifiedDumpable(parentRel));
17677 : }
17678 : }
17679 :
17680 1596 : if (OidIsValid(tbinfo->reloftype))
17681 : {
17682 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17683 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17684 : qualrelname,
17685 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17686 : zeroIsError));
17687 : }
17688 : }
17689 :
17690 : /*
17691 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17692 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17693 : * TOAST tables semi-independently, here we see them only as children
17694 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17695 : * child toast table is handled below.)
17696 : */
17697 11802 : if (dopt->binary_upgrade &&
17698 1632 : (tbinfo->relkind == RELKIND_RELATION ||
17699 220 : tbinfo->relkind == RELKIND_MATVIEW))
17700 : {
17701 1448 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17702 1448 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17703 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17704 : "WHERE oid = ",
17705 1448 : tbinfo->frozenxid, tbinfo->minmxid);
17706 1448 : appendStringLiteralAH(q, qualrelname, fout);
17707 1448 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17708 :
17709 1448 : if (tbinfo->toast_oid)
17710 : {
17711 : /*
17712 : * The toast table will have the same OID at restore, so we
17713 : * can safely target it by OID.
17714 : */
17715 560 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17716 560 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17717 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17718 : "WHERE oid = '%u';\n",
17719 560 : tbinfo->toast_frozenxid,
17720 560 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17721 : }
17722 : }
17723 :
17724 : /*
17725 : * In binary_upgrade mode, restore matviews' populated status by
17726 : * poking pg_class directly. This is pretty ugly, but we can't use
17727 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17728 : * matview is not populated even though this matview is; in any case,
17729 : * we want to transfer the matview's heap storage, not run REFRESH.
17730 : */
17731 11802 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17732 36 : tbinfo->relispopulated)
17733 : {
17734 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17735 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17736 : "SET relispopulated = 't'\n"
17737 : "WHERE oid = ");
17738 32 : appendStringLiteralAH(q, qualrelname, fout);
17739 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17740 : }
17741 :
17742 : /*
17743 : * Dump additional per-column properties that we can't handle in the
17744 : * main CREATE TABLE command.
17745 : */
17746 52898 : for (j = 0; j < tbinfo->numatts; j++)
17747 : {
17748 : /* None of this applies to dropped columns */
17749 41096 : if (tbinfo->attisdropped[j])
17750 900 : continue;
17751 :
17752 : /*
17753 : * Dump per-column statistics information. We only issue an ALTER
17754 : * TABLE statement if the attstattarget entry for this column is
17755 : * not the default value.
17756 : */
17757 40196 : if (tbinfo->attstattarget[j] >= 0)
17758 70 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17759 : foreign, qualrelname,
17760 70 : fmtId(tbinfo->attnames[j]),
17761 70 : tbinfo->attstattarget[j]);
17762 :
17763 : /*
17764 : * Dump per-column storage information. The statement is only
17765 : * dumped if the storage has been changed from the type's default.
17766 : */
17767 40196 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17768 : {
17769 170 : switch (tbinfo->attstorage[j])
17770 : {
17771 20 : case TYPSTORAGE_PLAIN:
17772 20 : storage = "PLAIN";
17773 20 : break;
17774 80 : case TYPSTORAGE_EXTERNAL:
17775 80 : storage = "EXTERNAL";
17776 80 : break;
17777 0 : case TYPSTORAGE_EXTENDED:
17778 0 : storage = "EXTENDED";
17779 0 : break;
17780 70 : case TYPSTORAGE_MAIN:
17781 70 : storage = "MAIN";
17782 70 : break;
17783 0 : default:
17784 0 : storage = NULL;
17785 : }
17786 :
17787 : /*
17788 : * Only dump the statement if it's a storage type we recognize
17789 : */
17790 170 : if (storage != NULL)
17791 170 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17792 : foreign, qualrelname,
17793 170 : fmtId(tbinfo->attnames[j]),
17794 : storage);
17795 : }
17796 :
17797 : /*
17798 : * Dump per-column compression, if it's been set.
17799 : */
17800 40196 : if (!dopt->no_toast_compression)
17801 : {
17802 : const char *cmname;
17803 :
17804 40000 : switch (tbinfo->attcompression[j])
17805 : {
17806 148 : case 'p':
17807 148 : cmname = "pglz";
17808 148 : break;
17809 186 : case 'l':
17810 186 : cmname = "lz4";
17811 186 : break;
17812 39666 : default:
17813 39666 : cmname = NULL;
17814 39666 : break;
17815 : }
17816 :
17817 40000 : if (cmname != NULL)
17818 334 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17819 : foreign, qualrelname,
17820 334 : fmtId(tbinfo->attnames[j]),
17821 : cmname);
17822 : }
17823 :
17824 : /*
17825 : * Dump per-column attributes.
17826 : */
17827 40196 : if (tbinfo->attoptions[j][0] != '\0')
17828 70 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17829 : foreign, qualrelname,
17830 70 : fmtId(tbinfo->attnames[j]),
17831 70 : tbinfo->attoptions[j]);
17832 :
17833 : /*
17834 : * Dump per-column fdw options.
17835 : */
17836 40196 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17837 74 : tbinfo->attfdwoptions[j][0] != '\0')
17838 70 : appendPQExpBuffer(q,
17839 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17840 : " %s\n"
17841 : ");\n",
17842 : qualrelname,
17843 70 : fmtId(tbinfo->attnames[j]),
17844 70 : tbinfo->attfdwoptions[j]);
17845 : } /* end loop over columns */
17846 :
17847 11802 : free(partkeydef);
17848 11802 : free(ftoptions);
17849 11802 : free(srvname);
17850 : }
17851 :
17852 : /*
17853 : * dump properties we only have ALTER TABLE syntax for
17854 : */
17855 12868 : if ((tbinfo->relkind == RELKIND_RELATION ||
17856 3076 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17857 1920 : tbinfo->relkind == RELKIND_MATVIEW) &&
17858 11728 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17859 : {
17860 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17861 : {
17862 : /* nothing to do, will be set when the index is dumped */
17863 : }
17864 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17865 : {
17866 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17867 : qualrelname);
17868 : }
17869 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17870 : {
17871 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17872 : qualrelname);
17873 : }
17874 : }
17875 :
17876 12868 : if (tbinfo->forcerowsec)
17877 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17878 : qualrelname);
17879 :
17880 12868 : if (dopt->binary_upgrade)
17881 1736 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17882 : reltypename, qrelname,
17883 1736 : tbinfo->dobj.namespace->dobj.name);
17884 :
17885 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17886 : {
17887 12868 : char *tablespace = NULL;
17888 12868 : char *tableam = NULL;
17889 :
17890 : /*
17891 : * _selectTablespace() relies on tablespace-enabled objects in the
17892 : * default tablespace to have a tablespace of "" (empty string) versus
17893 : * non-tablespace-enabled objects to have a tablespace of NULL.
17894 : * getTables() sets tbinfo->reltablespace to "" for the default
17895 : * tablespace (not NULL).
17896 : */
17897 12868 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17898 11728 : tablespace = tbinfo->reltablespace;
17899 :
17900 12868 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17901 2296 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17902 11728 : tableam = tbinfo->amname;
17903 :
17904 12868 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17905 12868 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17906 : .namespace = tbinfo->dobj.namespace->dobj.name,
17907 : .tablespace = tablespace,
17908 : .tableam = tableam,
17909 : .relkind = tbinfo->relkind,
17910 : .owner = tbinfo->rolname,
17911 : .description = reltypename,
17912 : .section = tbinfo->postponed_def ?
17913 : SECTION_POST_DATA : SECTION_PRE_DATA,
17914 : .createStmt = q->data,
17915 : .dropStmt = delq->data));
17916 : }
17917 :
17918 : /* Dump Table Comments */
17919 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17920 160 : dumpTableComment(fout, tbinfo, reltypename);
17921 :
17922 : /* Dump Table Security Labels */
17923 12868 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17924 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17925 :
17926 : /*
17927 : * Dump comments for not-null constraints that aren't to be dumped
17928 : * separately (those are processed by collectComments/dumpComment).
17929 : */
17930 12868 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
17931 12868 : fout->remoteVersion >= 180000)
17932 : {
17933 12868 : PQExpBuffer comment = NULL;
17934 12868 : PQExpBuffer tag = NULL;
17935 :
17936 60666 : for (j = 0; j < tbinfo->numatts; j++)
17937 : {
17938 47798 : if (tbinfo->notnull_constrs[j] != NULL &&
17939 4924 : tbinfo->notnull_comment[j] != NULL)
17940 : {
17941 90 : if (comment == NULL)
17942 : {
17943 90 : comment = createPQExpBuffer();
17944 90 : tag = createPQExpBuffer();
17945 : }
17946 : else
17947 : {
17948 0 : resetPQExpBuffer(comment);
17949 0 : resetPQExpBuffer(tag);
17950 : }
17951 :
17952 90 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
17953 90 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
17954 90 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
17955 90 : appendPQExpBufferStr(comment, ";\n");
17956 :
17957 90 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
17958 90 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
17959 :
17960 90 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17961 90 : ARCHIVE_OPTS(.tag = tag->data,
17962 : .namespace = tbinfo->dobj.namespace->dobj.name,
17963 : .owner = tbinfo->rolname,
17964 : .description = "COMMENT",
17965 : .section = SECTION_NONE,
17966 : .createStmt = comment->data,
17967 : .deps = &(tbinfo->dobj.dumpId),
17968 : .nDeps = 1));
17969 : }
17970 : }
17971 :
17972 12868 : destroyPQExpBuffer(comment);
17973 12868 : destroyPQExpBuffer(tag);
17974 : }
17975 :
17976 : /* Dump comments on inlined table constraints */
17977 14094 : for (j = 0; j < tbinfo->ncheck; j++)
17978 : {
17979 1226 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17980 :
17981 1226 : if (constr->separate || !constr->conislocal)
17982 518 : continue;
17983 :
17984 708 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17985 80 : dumpTableConstraintComment(fout, constr);
17986 : }
17987 :
17988 12868 : destroyPQExpBuffer(q);
17989 12868 : destroyPQExpBuffer(delq);
17990 12868 : destroyPQExpBuffer(extra);
17991 12868 : free(qrelname);
17992 12868 : free(qualrelname);
17993 12868 : }
17994 :
17995 : /*
17996 : * dumpTableAttach
17997 : * write to fout the commands to attach a child partition
17998 : *
17999 : * Child partitions are always made by creating them separately
18000 : * and then using ATTACH PARTITION, rather than using
18001 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18002 : * any possible discrepancy in column layout, to allow assigning the
18003 : * correct tablespace if different, and so that it's possible to restore
18004 : * a partition without restoring its parent. (You'll get an error from
18005 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18006 : * using "pg_restore -L" if you prefer.) The last point motivates
18007 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18008 : * rather than emitting it within the child partition's ArchiveEntry.
18009 : */
18010 : static void
18011 2816 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18012 : {
18013 2816 : DumpOptions *dopt = fout->dopt;
18014 : PQExpBuffer q;
18015 : PGresult *res;
18016 : char *partbound;
18017 :
18018 : /* Do nothing if not dumping schema */
18019 2816 : if (!dopt->dumpSchema)
18020 108 : return;
18021 :
18022 2708 : q = createPQExpBuffer();
18023 :
18024 2708 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18025 : {
18026 : /* Set up query for partbound details */
18027 92 : appendPQExpBufferStr(q,
18028 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18029 :
18030 92 : appendPQExpBufferStr(q,
18031 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18032 : "FROM pg_class c "
18033 : "WHERE c.oid = $1");
18034 :
18035 92 : ExecuteSqlStatement(fout, q->data);
18036 :
18037 92 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18038 : }
18039 :
18040 2708 : printfPQExpBuffer(q,
18041 : "EXECUTE dumpTableAttach('%u')",
18042 2708 : attachinfo->partitionTbl->dobj.catId.oid);
18043 :
18044 2708 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18045 2708 : partbound = PQgetvalue(res, 0, 0);
18046 :
18047 : /* Perform ALTER TABLE on the parent */
18048 2708 : printfPQExpBuffer(q,
18049 : "ALTER TABLE ONLY %s ",
18050 2708 : fmtQualifiedDumpable(attachinfo->parentTbl));
18051 2708 : appendPQExpBuffer(q,
18052 : "ATTACH PARTITION %s %s;\n",
18053 2708 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18054 : partbound);
18055 :
18056 : /*
18057 : * There is no point in creating a drop query as the drop is done by table
18058 : * drop. (If you think to change this, see also _printTocEntry().)
18059 : * Although this object doesn't really have ownership as such, set the
18060 : * owner field anyway to ensure that the command is run by the correct
18061 : * role at restore time.
18062 : */
18063 2708 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18064 2708 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18065 : .namespace = attachinfo->dobj.namespace->dobj.name,
18066 : .owner = attachinfo->partitionTbl->rolname,
18067 : .description = "TABLE ATTACH",
18068 : .section = SECTION_PRE_DATA,
18069 : .createStmt = q->data));
18070 :
18071 2708 : PQclear(res);
18072 2708 : destroyPQExpBuffer(q);
18073 : }
18074 :
18075 : /*
18076 : * dumpAttrDef --- dump an attribute's default-value declaration
18077 : */
18078 : static void
18079 2136 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18080 : {
18081 2136 : DumpOptions *dopt = fout->dopt;
18082 2136 : TableInfo *tbinfo = adinfo->adtable;
18083 2136 : int adnum = adinfo->adnum;
18084 : PQExpBuffer q;
18085 : PQExpBuffer delq;
18086 : char *qualrelname;
18087 : char *tag;
18088 : char *foreign;
18089 :
18090 : /* Do nothing if not dumping schema */
18091 2136 : if (!dopt->dumpSchema)
18092 0 : return;
18093 :
18094 : /* Skip if not "separate"; it was dumped in the table's definition */
18095 2136 : if (!adinfo->separate)
18096 1786 : return;
18097 :
18098 350 : q = createPQExpBuffer();
18099 350 : delq = createPQExpBuffer();
18100 :
18101 350 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18102 :
18103 350 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18104 :
18105 350 : appendPQExpBuffer(q,
18106 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18107 350 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18108 350 : adinfo->adef_expr);
18109 :
18110 350 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18111 : foreign, qualrelname,
18112 350 : fmtId(tbinfo->attnames[adnum - 1]));
18113 :
18114 350 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18115 :
18116 350 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18117 350 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18118 350 : ARCHIVE_OPTS(.tag = tag,
18119 : .namespace = tbinfo->dobj.namespace->dobj.name,
18120 : .owner = tbinfo->rolname,
18121 : .description = "DEFAULT",
18122 : .section = SECTION_PRE_DATA,
18123 : .createStmt = q->data,
18124 : .dropStmt = delq->data));
18125 :
18126 350 : free(tag);
18127 350 : destroyPQExpBuffer(q);
18128 350 : destroyPQExpBuffer(delq);
18129 350 : free(qualrelname);
18130 : }
18131 :
18132 : /*
18133 : * getAttrName: extract the correct name for an attribute
18134 : *
18135 : * The array tblInfo->attnames[] only provides names of user attributes;
18136 : * if a system attribute number is supplied, we have to fake it.
18137 : * We also do a little bit of bounds checking for safety's sake.
18138 : */
18139 : static const char *
18140 4192 : getAttrName(int attrnum, const TableInfo *tblInfo)
18141 : {
18142 4192 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18143 4192 : return tblInfo->attnames[attrnum - 1];
18144 0 : switch (attrnum)
18145 : {
18146 0 : case SelfItemPointerAttributeNumber:
18147 0 : return "ctid";
18148 0 : case MinTransactionIdAttributeNumber:
18149 0 : return "xmin";
18150 0 : case MinCommandIdAttributeNumber:
18151 0 : return "cmin";
18152 0 : case MaxTransactionIdAttributeNumber:
18153 0 : return "xmax";
18154 0 : case MaxCommandIdAttributeNumber:
18155 0 : return "cmax";
18156 0 : case TableOidAttributeNumber:
18157 0 : return "tableoid";
18158 : }
18159 0 : pg_fatal("invalid column number %d for table \"%s\"",
18160 : attrnum, tblInfo->dobj.name);
18161 : return NULL; /* keep compiler quiet */
18162 : }
18163 :
18164 : /*
18165 : * dumpIndex
18166 : * write out to fout a user-defined index
18167 : */
18168 : static void
18169 5262 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18170 : {
18171 5262 : DumpOptions *dopt = fout->dopt;
18172 5262 : TableInfo *tbinfo = indxinfo->indextable;
18173 5262 : bool is_constraint = (indxinfo->indexconstraint != 0);
18174 : PQExpBuffer q;
18175 : PQExpBuffer delq;
18176 : char *qindxname;
18177 : char *qqindxname;
18178 :
18179 : /* Do nothing if not dumping schema */
18180 5262 : if (!dopt->dumpSchema)
18181 234 : return;
18182 :
18183 5028 : q = createPQExpBuffer();
18184 5028 : delq = createPQExpBuffer();
18185 :
18186 5028 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18187 5028 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18188 :
18189 : /*
18190 : * If there's an associated constraint, don't dump the index per se, but
18191 : * do dump any comment for it. (This is safe because dependency ordering
18192 : * will have ensured the constraint is emitted first.) Note that the
18193 : * emitted comment has to be shown as depending on the constraint, not the
18194 : * index, in such cases.
18195 : */
18196 5028 : if (!is_constraint)
18197 : {
18198 2112 : char *indstatcols = indxinfo->indstatcols;
18199 2112 : char *indstatvals = indxinfo->indstatvals;
18200 2112 : char **indstatcolsarray = NULL;
18201 2112 : char **indstatvalsarray = NULL;
18202 2112 : int nstatcols = 0;
18203 2112 : int nstatvals = 0;
18204 :
18205 2112 : if (dopt->binary_upgrade)
18206 312 : binary_upgrade_set_pg_class_oids(fout, q,
18207 312 : indxinfo->dobj.catId.oid);
18208 :
18209 : /* Plain secondary index */
18210 2112 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18211 :
18212 : /*
18213 : * Append ALTER TABLE commands as needed to set properties that we
18214 : * only have ALTER TABLE syntax for. Keep this in sync with the
18215 : * similar code in dumpConstraint!
18216 : */
18217 :
18218 : /* If the index is clustered, we need to record that. */
18219 2112 : if (indxinfo->indisclustered)
18220 : {
18221 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18222 0 : fmtQualifiedDumpable(tbinfo));
18223 : /* index name is not qualified in this syntax */
18224 0 : appendPQExpBuffer(q, " ON %s;\n",
18225 : qindxname);
18226 : }
18227 :
18228 : /*
18229 : * If the index has any statistics on some of its columns, generate
18230 : * the associated ALTER INDEX queries.
18231 : */
18232 2112 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18233 : {
18234 : int j;
18235 :
18236 70 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18237 0 : pg_fatal("could not parse index statistic columns");
18238 70 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18239 0 : pg_fatal("could not parse index statistic values");
18240 70 : if (nstatcols != nstatvals)
18241 0 : pg_fatal("mismatched number of columns and values for index statistics");
18242 :
18243 210 : for (j = 0; j < nstatcols; j++)
18244 : {
18245 140 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18246 :
18247 : /*
18248 : * Note that this is a column number, so no quotes should be
18249 : * used.
18250 : */
18251 140 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18252 140 : indstatcolsarray[j]);
18253 140 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18254 140 : indstatvalsarray[j]);
18255 : }
18256 : }
18257 :
18258 : /* Indexes can depend on extensions */
18259 2112 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18260 : "pg_catalog.pg_class",
18261 : "INDEX", qqindxname);
18262 :
18263 : /* If the index defines identity, we need to record that. */
18264 2112 : if (indxinfo->indisreplident)
18265 : {
18266 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18267 0 : fmtQualifiedDumpable(tbinfo));
18268 : /* index name is not qualified in this syntax */
18269 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18270 : qindxname);
18271 : }
18272 :
18273 : /*
18274 : * If this index is a member of a partitioned index, the backend will
18275 : * not allow us to drop it separately, so don't try. It will go away
18276 : * automatically when we drop either the index's table or the
18277 : * partitioned index. (If, in a selective restore with --clean, we
18278 : * drop neither of those, then this index will not be dropped either.
18279 : * But that's fine, and even if you think it's not, the backend won't
18280 : * let us do differently.)
18281 : */
18282 2112 : if (indxinfo->parentidx == 0)
18283 1724 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18284 :
18285 2112 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18286 2112 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18287 2112 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18288 : .namespace = tbinfo->dobj.namespace->dobj.name,
18289 : .tablespace = indxinfo->tablespace,
18290 : .owner = tbinfo->rolname,
18291 : .description = "INDEX",
18292 : .section = SECTION_POST_DATA,
18293 : .createStmt = q->data,
18294 : .dropStmt = delq->data));
18295 :
18296 2112 : free(indstatcolsarray);
18297 2112 : free(indstatvalsarray);
18298 : }
18299 :
18300 : /* Dump Index Comments */
18301 5028 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18302 30 : dumpComment(fout, "INDEX", qindxname,
18303 30 : tbinfo->dobj.namespace->dobj.name,
18304 : tbinfo->rolname,
18305 : indxinfo->dobj.catId, 0,
18306 : is_constraint ? indxinfo->indexconstraint :
18307 : indxinfo->dobj.dumpId);
18308 :
18309 5028 : destroyPQExpBuffer(q);
18310 5028 : destroyPQExpBuffer(delq);
18311 5028 : free(qindxname);
18312 5028 : free(qqindxname);
18313 : }
18314 :
18315 : /*
18316 : * dumpIndexAttach
18317 : * write out to fout a partitioned-index attachment clause
18318 : */
18319 : static void
18320 1176 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18321 : {
18322 : /* Do nothing if not dumping schema */
18323 1176 : if (!fout->dopt->dumpSchema)
18324 96 : return;
18325 :
18326 1080 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18327 : {
18328 1080 : PQExpBuffer q = createPQExpBuffer();
18329 :
18330 1080 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18331 1080 : fmtQualifiedDumpable(attachinfo->parentIdx));
18332 1080 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18333 1080 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18334 :
18335 : /*
18336 : * There is no need for a dropStmt since the drop is done implicitly
18337 : * when we drop either the index's table or the partitioned index.
18338 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18339 : * there's no way to do it anyway. (If you think to change this,
18340 : * consider also what to do with --if-exists.)
18341 : *
18342 : * Although this object doesn't really have ownership as such, set the
18343 : * owner field anyway to ensure that the command is run by the correct
18344 : * role at restore time.
18345 : */
18346 1080 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18347 1080 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18348 : .namespace = attachinfo->dobj.namespace->dobj.name,
18349 : .owner = attachinfo->parentIdx->indextable->rolname,
18350 : .description = "INDEX ATTACH",
18351 : .section = SECTION_POST_DATA,
18352 : .createStmt = q->data));
18353 :
18354 1080 : destroyPQExpBuffer(q);
18355 : }
18356 : }
18357 :
18358 : /*
18359 : * dumpStatisticsExt
18360 : * write out to fout an extended statistics object
18361 : */
18362 : static void
18363 284 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18364 : {
18365 284 : DumpOptions *dopt = fout->dopt;
18366 : PQExpBuffer q;
18367 : PQExpBuffer delq;
18368 : PQExpBuffer query;
18369 : char *qstatsextname;
18370 : PGresult *res;
18371 : char *stxdef;
18372 :
18373 : /* Do nothing if not dumping schema */
18374 284 : if (!dopt->dumpSchema)
18375 36 : return;
18376 :
18377 248 : q = createPQExpBuffer();
18378 248 : delq = createPQExpBuffer();
18379 248 : query = createPQExpBuffer();
18380 :
18381 248 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18382 :
18383 248 : appendPQExpBuffer(query, "SELECT "
18384 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18385 248 : statsextinfo->dobj.catId.oid);
18386 :
18387 248 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18388 :
18389 248 : stxdef = PQgetvalue(res, 0, 0);
18390 :
18391 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18392 248 : appendPQExpBuffer(q, "%s;\n", stxdef);
18393 :
18394 : /*
18395 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18396 : * for this statistics object is not the default value.
18397 : */
18398 248 : if (statsextinfo->stattarget >= 0)
18399 : {
18400 70 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18401 70 : fmtQualifiedDumpable(statsextinfo));
18402 70 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18403 70 : statsextinfo->stattarget);
18404 : }
18405 :
18406 248 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18407 248 : fmtQualifiedDumpable(statsextinfo));
18408 :
18409 248 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18410 248 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18411 248 : statsextinfo->dobj.dumpId,
18412 248 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18413 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18414 : .owner = statsextinfo->rolname,
18415 : .description = "STATISTICS",
18416 : .section = SECTION_POST_DATA,
18417 : .createStmt = q->data,
18418 : .dropStmt = delq->data));
18419 :
18420 : /* Dump Statistics Comments */
18421 248 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18422 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18423 0 : statsextinfo->dobj.namespace->dobj.name,
18424 0 : statsextinfo->rolname,
18425 : statsextinfo->dobj.catId, 0,
18426 0 : statsextinfo->dobj.dumpId);
18427 :
18428 248 : PQclear(res);
18429 248 : destroyPQExpBuffer(q);
18430 248 : destroyPQExpBuffer(delq);
18431 248 : destroyPQExpBuffer(query);
18432 248 : free(qstatsextname);
18433 : }
18434 :
18435 : /*
18436 : * dumpConstraint
18437 : * write out to fout a user-defined constraint
18438 : */
18439 : static void
18440 5128 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18441 : {
18442 5128 : DumpOptions *dopt = fout->dopt;
18443 5128 : TableInfo *tbinfo = coninfo->contable;
18444 : PQExpBuffer q;
18445 : PQExpBuffer delq;
18446 5128 : char *tag = NULL;
18447 : char *foreign;
18448 :
18449 : /* Do nothing if not dumping schema */
18450 5128 : if (!dopt->dumpSchema)
18451 196 : return;
18452 :
18453 4932 : q = createPQExpBuffer();
18454 4932 : delq = createPQExpBuffer();
18455 :
18456 9544 : foreign = tbinfo &&
18457 4932 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18458 :
18459 4932 : if (coninfo->contype == 'p' ||
18460 2500 : coninfo->contype == 'u' ||
18461 2036 : coninfo->contype == 'x')
18462 2916 : {
18463 : /* Index-related constraint */
18464 : IndxInfo *indxinfo;
18465 : int k;
18466 :
18467 2916 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18468 :
18469 2916 : if (indxinfo == NULL)
18470 0 : pg_fatal("missing index for constraint \"%s\"",
18471 : coninfo->dobj.name);
18472 :
18473 2916 : if (dopt->binary_upgrade)
18474 292 : binary_upgrade_set_pg_class_oids(fout, q,
18475 : indxinfo->dobj.catId.oid);
18476 :
18477 2916 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18478 2916 : fmtQualifiedDumpable(tbinfo));
18479 2916 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18480 2916 : fmtId(coninfo->dobj.name));
18481 :
18482 2916 : if (coninfo->condef)
18483 : {
18484 : /* pg_get_constraintdef should have provided everything */
18485 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18486 : }
18487 : else
18488 : {
18489 2896 : appendPQExpBufferStr(q,
18490 2896 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18491 :
18492 : /*
18493 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18494 : * indexes. Being able to create this was fixed, but we need to
18495 : * make the index distinct in order to be able to restore the
18496 : * dump.
18497 : */
18498 2896 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18499 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18500 2896 : appendPQExpBufferStr(q, " (");
18501 7008 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18502 : {
18503 4112 : int indkey = (int) indxinfo->indkeys[k];
18504 : const char *attname;
18505 :
18506 4112 : if (indkey == InvalidAttrNumber)
18507 0 : break;
18508 4112 : attname = getAttrName(indkey, tbinfo);
18509 :
18510 4112 : appendPQExpBuffer(q, "%s%s",
18511 : (k == 0) ? "" : ", ",
18512 : fmtId(attname));
18513 : }
18514 2896 : if (coninfo->conperiod)
18515 220 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18516 :
18517 2896 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18518 40 : appendPQExpBufferStr(q, ") INCLUDE (");
18519 :
18520 2976 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18521 : {
18522 80 : int indkey = (int) indxinfo->indkeys[k];
18523 : const char *attname;
18524 :
18525 80 : if (indkey == InvalidAttrNumber)
18526 0 : break;
18527 80 : attname = getAttrName(indkey, tbinfo);
18528 :
18529 160 : appendPQExpBuffer(q, "%s%s",
18530 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18531 : fmtId(attname));
18532 : }
18533 :
18534 2896 : appendPQExpBufferChar(q, ')');
18535 :
18536 2896 : if (nonemptyReloptions(indxinfo->indreloptions))
18537 : {
18538 0 : appendPQExpBufferStr(q, " WITH (");
18539 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18540 0 : appendPQExpBufferChar(q, ')');
18541 : }
18542 :
18543 2896 : if (coninfo->condeferrable)
18544 : {
18545 50 : appendPQExpBufferStr(q, " DEFERRABLE");
18546 50 : if (coninfo->condeferred)
18547 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18548 : }
18549 :
18550 2896 : appendPQExpBufferStr(q, ";\n");
18551 : }
18552 :
18553 : /*
18554 : * Append ALTER TABLE commands as needed to set properties that we
18555 : * only have ALTER TABLE syntax for. Keep this in sync with the
18556 : * similar code in dumpIndex!
18557 : */
18558 :
18559 : /* If the index is clustered, we need to record that. */
18560 2916 : if (indxinfo->indisclustered)
18561 : {
18562 70 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18563 70 : fmtQualifiedDumpable(tbinfo));
18564 : /* index name is not qualified in this syntax */
18565 70 : appendPQExpBuffer(q, " ON %s;\n",
18566 70 : fmtId(indxinfo->dobj.name));
18567 : }
18568 :
18569 : /* If the index defines identity, we need to record that. */
18570 2916 : if (indxinfo->indisreplident)
18571 : {
18572 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18573 0 : fmtQualifiedDumpable(tbinfo));
18574 : /* index name is not qualified in this syntax */
18575 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18576 0 : fmtId(indxinfo->dobj.name));
18577 : }
18578 :
18579 : /* Indexes can depend on extensions */
18580 2916 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18581 : "pg_catalog.pg_class", "INDEX",
18582 2916 : fmtQualifiedDumpable(indxinfo));
18583 :
18584 2916 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18585 2916 : fmtQualifiedDumpable(tbinfo));
18586 2916 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18587 2916 : fmtId(coninfo->dobj.name));
18588 :
18589 2916 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18590 :
18591 2916 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18592 2916 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18593 2916 : ARCHIVE_OPTS(.tag = tag,
18594 : .namespace = tbinfo->dobj.namespace->dobj.name,
18595 : .tablespace = indxinfo->tablespace,
18596 : .owner = tbinfo->rolname,
18597 : .description = "CONSTRAINT",
18598 : .section = SECTION_POST_DATA,
18599 : .createStmt = q->data,
18600 : .dropStmt = delq->data));
18601 : }
18602 2016 : else if (coninfo->contype == 'f')
18603 : {
18604 : char *only;
18605 :
18606 : /*
18607 : * Foreign keys on partitioned tables are always declared as
18608 : * inheriting to partitions; for all other cases, emit them as
18609 : * applying ONLY directly to the named table, because that's how they
18610 : * work for regular inherited tables.
18611 : */
18612 330 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18613 :
18614 : /*
18615 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18616 : * current table data is not processed
18617 : */
18618 330 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18619 330 : only, fmtQualifiedDumpable(tbinfo));
18620 330 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18621 330 : fmtId(coninfo->dobj.name),
18622 330 : coninfo->condef);
18623 :
18624 330 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18625 330 : only, fmtQualifiedDumpable(tbinfo));
18626 330 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18627 330 : fmtId(coninfo->dobj.name));
18628 :
18629 330 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18630 :
18631 330 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18632 330 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18633 330 : ARCHIVE_OPTS(.tag = tag,
18634 : .namespace = tbinfo->dobj.namespace->dobj.name,
18635 : .owner = tbinfo->rolname,
18636 : .description = "FK CONSTRAINT",
18637 : .section = SECTION_POST_DATA,
18638 : .createStmt = q->data,
18639 : .dropStmt = delq->data));
18640 : }
18641 1686 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18642 : {
18643 : /* CHECK or invalid not-null constraint on a table */
18644 :
18645 : /* Ignore if not to be dumped separately, or if it was inherited */
18646 1366 : if (coninfo->separate && coninfo->conislocal)
18647 : {
18648 : const char *keyword;
18649 :
18650 220 : if (coninfo->contype == 'c')
18651 90 : keyword = "CHECK CONSTRAINT";
18652 : else
18653 130 : keyword = "CONSTRAINT";
18654 :
18655 : /* not ONLY since we want it to propagate to children */
18656 220 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18657 220 : fmtQualifiedDumpable(tbinfo));
18658 220 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18659 220 : fmtId(coninfo->dobj.name),
18660 220 : coninfo->condef);
18661 :
18662 220 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18663 220 : fmtQualifiedDumpable(tbinfo));
18664 220 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18665 220 : fmtId(coninfo->dobj.name));
18666 :
18667 220 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18668 :
18669 220 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18670 220 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18671 220 : ARCHIVE_OPTS(.tag = tag,
18672 : .namespace = tbinfo->dobj.namespace->dobj.name,
18673 : .owner = tbinfo->rolname,
18674 : .description = keyword,
18675 : .section = SECTION_POST_DATA,
18676 : .createStmt = q->data,
18677 : .dropStmt = delq->data));
18678 : }
18679 : }
18680 320 : else if (tbinfo == NULL)
18681 : {
18682 : /* CHECK, NOT NULL constraint on a domain */
18683 320 : TypeInfo *tyinfo = coninfo->condomain;
18684 :
18685 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18686 :
18687 : /* Ignore if not to be dumped separately */
18688 320 : if (coninfo->separate)
18689 : {
18690 : const char *keyword;
18691 :
18692 10 : if (coninfo->contype == 'c')
18693 10 : keyword = "CHECK CONSTRAINT";
18694 : else
18695 0 : keyword = "CONSTRAINT";
18696 :
18697 10 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18698 10 : fmtQualifiedDumpable(tyinfo));
18699 10 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18700 10 : fmtId(coninfo->dobj.name),
18701 10 : coninfo->condef);
18702 :
18703 10 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18704 10 : fmtQualifiedDumpable(tyinfo));
18705 10 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18706 10 : fmtId(coninfo->dobj.name));
18707 :
18708 10 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18709 :
18710 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18711 10 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18712 10 : ARCHIVE_OPTS(.tag = tag,
18713 : .namespace = tyinfo->dobj.namespace->dobj.name,
18714 : .owner = tyinfo->rolname,
18715 : .description = keyword,
18716 : .section = SECTION_POST_DATA,
18717 : .createStmt = q->data,
18718 : .dropStmt = delq->data));
18719 :
18720 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18721 : {
18722 10 : PQExpBuffer conprefix = createPQExpBuffer();
18723 10 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18724 :
18725 10 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18726 10 : fmtId(coninfo->dobj.name));
18727 :
18728 10 : dumpComment(fout, conprefix->data, qtypname,
18729 10 : tyinfo->dobj.namespace->dobj.name,
18730 : tyinfo->rolname,
18731 : coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
18732 10 : destroyPQExpBuffer(conprefix);
18733 10 : free(qtypname);
18734 : }
18735 : }
18736 : }
18737 : else
18738 : {
18739 0 : pg_fatal("unrecognized constraint type: %c",
18740 : coninfo->contype);
18741 : }
18742 :
18743 : /* Dump Constraint Comments --- only works for table constraints */
18744 4932 : if (tbinfo && coninfo->separate &&
18745 3526 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18746 100 : dumpTableConstraintComment(fout, coninfo);
18747 :
18748 4932 : free(tag);
18749 4932 : destroyPQExpBuffer(q);
18750 4932 : destroyPQExpBuffer(delq);
18751 : }
18752 :
18753 : /*
18754 : * dumpTableConstraintComment --- dump a constraint's comment if any
18755 : *
18756 : * This is split out because we need the function in two different places
18757 : * depending on whether the constraint is dumped as part of CREATE TABLE
18758 : * or as a separate ALTER command.
18759 : */
18760 : static void
18761 180 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18762 : {
18763 180 : TableInfo *tbinfo = coninfo->contable;
18764 180 : PQExpBuffer conprefix = createPQExpBuffer();
18765 : char *qtabname;
18766 :
18767 180 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18768 :
18769 180 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18770 180 : fmtId(coninfo->dobj.name));
18771 :
18772 180 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18773 180 : dumpComment(fout, conprefix->data, qtabname,
18774 180 : tbinfo->dobj.namespace->dobj.name,
18775 : tbinfo->rolname,
18776 : coninfo->dobj.catId, 0,
18777 180 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18778 :
18779 180 : destroyPQExpBuffer(conprefix);
18780 180 : free(qtabname);
18781 180 : }
18782 :
18783 : static inline SeqType
18784 1294 : parse_sequence_type(const char *name)
18785 : {
18786 2886 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18787 : {
18788 2886 : if (strcmp(SeqTypeNames[i], name) == 0)
18789 1294 : return (SeqType) i;
18790 : }
18791 :
18792 0 : pg_fatal("unrecognized sequence type: %s", name);
18793 : return (SeqType) 0; /* keep compiler quiet */
18794 : }
18795 :
18796 : /*
18797 : * bsearch() comparator for SequenceItem
18798 : */
18799 : static int
18800 5968 : SequenceItemCmp(const void *p1, const void *p2)
18801 : {
18802 5968 : SequenceItem v1 = *((const SequenceItem *) p1);
18803 5968 : SequenceItem v2 = *((const SequenceItem *) p2);
18804 :
18805 5968 : return pg_cmp_u32(v1.oid, v2.oid);
18806 : }
18807 :
18808 : /*
18809 : * collectSequences
18810 : *
18811 : * Construct a table of sequence information. This table is sorted by OID for
18812 : * speed in lookup.
18813 : */
18814 : static void
18815 364 : collectSequences(Archive *fout)
18816 : {
18817 : PGresult *res;
18818 : const char *query;
18819 :
18820 : /*
18821 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18822 : * some extra effort, we might be able to use the sorted table for those
18823 : * versions, but for now it seems unlikely to be worth it.
18824 : *
18825 : * Since version 18, we can gather the sequence data in this query with
18826 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18827 : */
18828 364 : if (fout->remoteVersion < 100000)
18829 0 : return;
18830 364 : else if (fout->remoteVersion < 180000 ||
18831 364 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18832 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18833 : "seqstart, seqincrement, "
18834 : "seqmax, seqmin, "
18835 : "seqcache, seqcycle, "
18836 : "NULL, 'f' "
18837 : "FROM pg_catalog.pg_sequence "
18838 : "ORDER BY seqrelid";
18839 : else
18840 348 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18841 : "seqstart, seqincrement, "
18842 : "seqmax, seqmin, "
18843 : "seqcache, seqcycle, "
18844 : "last_value, is_called "
18845 : "FROM pg_catalog.pg_sequence, "
18846 : "pg_get_sequence_data(seqrelid) "
18847 : "ORDER BY seqrelid;";
18848 :
18849 364 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18850 :
18851 364 : nsequences = PQntuples(res);
18852 364 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18853 :
18854 1658 : for (int i = 0; i < nsequences; i++)
18855 : {
18856 1294 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18857 1294 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18858 1294 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18859 1294 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18860 1294 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18861 1294 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18862 1294 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18863 1294 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18864 1294 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18865 1294 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18866 : }
18867 :
18868 364 : PQclear(res);
18869 : }
18870 :
18871 : /*
18872 : * dumpSequence
18873 : * write the declaration (not data) of one user-defined sequence
18874 : */
18875 : static void
18876 768 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18877 : {
18878 768 : DumpOptions *dopt = fout->dopt;
18879 : SequenceItem *seq;
18880 : bool is_ascending;
18881 : int64 default_minv,
18882 : default_maxv;
18883 768 : PQExpBuffer query = createPQExpBuffer();
18884 768 : PQExpBuffer delqry = createPQExpBuffer();
18885 : char *qseqname;
18886 768 : TableInfo *owning_tab = NULL;
18887 :
18888 768 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18889 :
18890 : /*
18891 : * For versions >= 10, the sequence information is gathered in a sorted
18892 : * table before any calls to dumpSequence(). See collectSequences() for
18893 : * more information.
18894 : */
18895 768 : if (fout->remoteVersion >= 100000)
18896 : {
18897 768 : SequenceItem key = {0};
18898 :
18899 : Assert(sequences);
18900 :
18901 768 : key.oid = tbinfo->dobj.catId.oid;
18902 768 : seq = bsearch(&key, sequences, nsequences,
18903 : sizeof(SequenceItem), SequenceItemCmp);
18904 : }
18905 : else
18906 : {
18907 : PGresult *res;
18908 :
18909 : /*
18910 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18911 : *
18912 : * Note: it might seem that 'bigint' potentially needs to be
18913 : * schema-qualified, but actually that's a keyword.
18914 : */
18915 0 : appendPQExpBuffer(query,
18916 : "SELECT 'bigint' AS sequence_type, "
18917 : "start_value, increment_by, max_value, min_value, "
18918 : "cache_value, is_cycled FROM %s",
18919 0 : fmtQualifiedDumpable(tbinfo));
18920 :
18921 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18922 :
18923 0 : if (PQntuples(res) != 1)
18924 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18925 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18926 : PQntuples(res)),
18927 : tbinfo->dobj.name, PQntuples(res));
18928 :
18929 0 : seq = pg_malloc0(sizeof(SequenceItem));
18930 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18931 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18932 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18933 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18934 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18935 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18936 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18937 :
18938 0 : PQclear(res);
18939 : }
18940 :
18941 : /* Calculate default limits for a sequence of this type */
18942 768 : is_ascending = (seq->incby >= 0);
18943 768 : if (seq->seqtype == SEQTYPE_SMALLINT)
18944 : {
18945 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18946 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18947 : }
18948 718 : else if (seq->seqtype == SEQTYPE_INTEGER)
18949 : {
18950 586 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18951 586 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18952 : }
18953 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
18954 : {
18955 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18956 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18957 : }
18958 : else
18959 : {
18960 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18961 : default_minv = default_maxv = 0; /* keep compiler quiet */
18962 : }
18963 :
18964 : /*
18965 : * Identity sequences are not to be dropped separately.
18966 : */
18967 768 : if (!tbinfo->is_identity_sequence)
18968 : {
18969 478 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18970 478 : fmtQualifiedDumpable(tbinfo));
18971 : }
18972 :
18973 768 : resetPQExpBuffer(query);
18974 :
18975 768 : if (dopt->binary_upgrade)
18976 : {
18977 132 : binary_upgrade_set_pg_class_oids(fout, query,
18978 132 : tbinfo->dobj.catId.oid);
18979 :
18980 : /*
18981 : * In older PG versions a sequence will have a pg_type entry, but v14
18982 : * and up don't use that, so don't attempt to preserve the type OID.
18983 : */
18984 : }
18985 :
18986 768 : if (tbinfo->is_identity_sequence)
18987 : {
18988 290 : owning_tab = findTableByOid(tbinfo->owning_tab);
18989 :
18990 290 : appendPQExpBuffer(query,
18991 : "ALTER TABLE %s ",
18992 290 : fmtQualifiedDumpable(owning_tab));
18993 290 : appendPQExpBuffer(query,
18994 : "ALTER COLUMN %s ADD GENERATED ",
18995 290 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18996 290 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18997 210 : appendPQExpBufferStr(query, "ALWAYS");
18998 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18999 80 : appendPQExpBufferStr(query, "BY DEFAULT");
19000 290 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19001 290 : fmtQualifiedDumpable(tbinfo));
19002 :
19003 : /*
19004 : * Emit persistence option only if it's different from the owning
19005 : * table's. This avoids using this new syntax unnecessarily.
19006 : */
19007 290 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19008 20 : appendPQExpBuffer(query, " %s\n",
19009 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19010 : "UNLOGGED" : "LOGGED");
19011 : }
19012 : else
19013 : {
19014 478 : appendPQExpBuffer(query,
19015 : "CREATE %sSEQUENCE %s\n",
19016 478 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19017 : "UNLOGGED " : "",
19018 478 : fmtQualifiedDumpable(tbinfo));
19019 :
19020 478 : if (seq->seqtype != SEQTYPE_BIGINT)
19021 376 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19022 : }
19023 :
19024 768 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19025 :
19026 768 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19027 :
19028 768 : if (seq->minv != default_minv)
19029 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19030 : else
19031 738 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19032 :
19033 768 : if (seq->maxv != default_maxv)
19034 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19035 : else
19036 738 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19037 :
19038 768 : appendPQExpBuffer(query,
19039 : " CACHE " INT64_FORMAT "%s",
19040 768 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19041 :
19042 768 : if (tbinfo->is_identity_sequence)
19043 290 : appendPQExpBufferStr(query, "\n);\n");
19044 : else
19045 478 : appendPQExpBufferStr(query, ";\n");
19046 :
19047 : /* binary_upgrade: no need to clear TOAST table oid */
19048 :
19049 768 : if (dopt->binary_upgrade)
19050 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19051 : "SEQUENCE", qseqname,
19052 132 : tbinfo->dobj.namespace->dobj.name);
19053 :
19054 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19055 768 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19056 768 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19057 : .namespace = tbinfo->dobj.namespace->dobj.name,
19058 : .owner = tbinfo->rolname,
19059 : .description = "SEQUENCE",
19060 : .section = SECTION_PRE_DATA,
19061 : .createStmt = query->data,
19062 : .dropStmt = delqry->data));
19063 :
19064 : /*
19065 : * If the sequence is owned by a table column, emit the ALTER for it as a
19066 : * separate TOC entry immediately following the sequence's own entry. It's
19067 : * OK to do this rather than using full sorting logic, because the
19068 : * dependency that tells us it's owned will have forced the table to be
19069 : * created first. We can't just include the ALTER in the TOC entry
19070 : * because it will fail if we haven't reassigned the sequence owner to
19071 : * match the table's owner.
19072 : *
19073 : * We need not schema-qualify the table reference because both sequence
19074 : * and table must be in the same schema.
19075 : */
19076 768 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19077 : {
19078 286 : owning_tab = findTableByOid(tbinfo->owning_tab);
19079 :
19080 286 : if (owning_tab == NULL)
19081 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19082 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19083 :
19084 286 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19085 : {
19086 282 : resetPQExpBuffer(query);
19087 282 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19088 282 : fmtQualifiedDumpable(tbinfo));
19089 282 : appendPQExpBuffer(query, " OWNED BY %s",
19090 282 : fmtQualifiedDumpable(owning_tab));
19091 282 : appendPQExpBuffer(query, ".%s;\n",
19092 282 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19093 :
19094 282 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19095 282 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19096 282 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19097 : .namespace = tbinfo->dobj.namespace->dobj.name,
19098 : .owner = tbinfo->rolname,
19099 : .description = "SEQUENCE OWNED BY",
19100 : .section = SECTION_PRE_DATA,
19101 : .createStmt = query->data,
19102 : .deps = &(tbinfo->dobj.dumpId),
19103 : .nDeps = 1));
19104 : }
19105 : }
19106 :
19107 : /* Dump Sequence Comments and Security Labels */
19108 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19109 0 : dumpComment(fout, "SEQUENCE", qseqname,
19110 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19111 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19112 :
19113 768 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19114 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19115 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19116 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19117 :
19118 768 : if (fout->remoteVersion < 100000)
19119 0 : pg_free(seq);
19120 768 : destroyPQExpBuffer(query);
19121 768 : destroyPQExpBuffer(delqry);
19122 768 : free(qseqname);
19123 768 : }
19124 :
19125 : /*
19126 : * dumpSequenceData
19127 : * write the data of one user-defined sequence
19128 : */
19129 : static void
19130 804 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19131 : {
19132 804 : TableInfo *tbinfo = tdinfo->tdtable;
19133 : int64 last;
19134 : bool called;
19135 804 : PQExpBuffer query = createPQExpBuffer();
19136 :
19137 : /*
19138 : * For versions >= 18, the sequence information is gathered in the sorted
19139 : * array before any calls to dumpSequenceData(). See collectSequences()
19140 : * for more information.
19141 : *
19142 : * For older versions, we have to query the sequence relations
19143 : * individually.
19144 : */
19145 804 : if (fout->remoteVersion < 180000)
19146 : {
19147 : PGresult *res;
19148 :
19149 0 : appendPQExpBuffer(query,
19150 : "SELECT last_value, is_called FROM %s",
19151 0 : fmtQualifiedDumpable(tbinfo));
19152 :
19153 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19154 :
19155 0 : if (PQntuples(res) != 1)
19156 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19157 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19158 : PQntuples(res)),
19159 : tbinfo->dobj.name, PQntuples(res));
19160 :
19161 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19162 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19163 :
19164 0 : PQclear(res);
19165 : }
19166 : else
19167 : {
19168 804 : SequenceItem key = {0};
19169 : SequenceItem *entry;
19170 :
19171 : Assert(sequences);
19172 : Assert(tbinfo->dobj.catId.oid);
19173 :
19174 804 : key.oid = tbinfo->dobj.catId.oid;
19175 804 : entry = bsearch(&key, sequences, nsequences,
19176 : sizeof(SequenceItem), SequenceItemCmp);
19177 :
19178 804 : last = entry->last_value;
19179 804 : called = entry->is_called;
19180 : }
19181 :
19182 804 : resetPQExpBuffer(query);
19183 804 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19184 804 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19185 804 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19186 : last, (called ? "true" : "false"));
19187 :
19188 804 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19189 804 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19190 804 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19191 : .namespace = tbinfo->dobj.namespace->dobj.name,
19192 : .owner = tbinfo->rolname,
19193 : .description = "SEQUENCE SET",
19194 : .section = SECTION_DATA,
19195 : .createStmt = query->data,
19196 : .deps = &(tbinfo->dobj.dumpId),
19197 : .nDeps = 1));
19198 :
19199 804 : destroyPQExpBuffer(query);
19200 804 : }
19201 :
19202 : /*
19203 : * dumpTrigger
19204 : * write the declaration of one user-defined table trigger
19205 : */
19206 : static void
19207 1076 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19208 : {
19209 1076 : DumpOptions *dopt = fout->dopt;
19210 1076 : TableInfo *tbinfo = tginfo->tgtable;
19211 : PQExpBuffer query;
19212 : PQExpBuffer delqry;
19213 : PQExpBuffer trigprefix;
19214 : PQExpBuffer trigidentity;
19215 : char *qtabname;
19216 : char *tag;
19217 :
19218 : /* Do nothing if not dumping schema */
19219 1076 : if (!dopt->dumpSchema)
19220 62 : return;
19221 :
19222 1014 : query = createPQExpBuffer();
19223 1014 : delqry = createPQExpBuffer();
19224 1014 : trigprefix = createPQExpBuffer();
19225 1014 : trigidentity = createPQExpBuffer();
19226 :
19227 1014 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19228 :
19229 1014 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19230 1014 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19231 :
19232 1014 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19233 1014 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19234 :
19235 : /* Triggers can depend on extensions */
19236 1014 : append_depends_on_extension(fout, query, &tginfo->dobj,
19237 : "pg_catalog.pg_trigger", "TRIGGER",
19238 1014 : trigidentity->data);
19239 :
19240 1014 : if (tginfo->tgispartition)
19241 : {
19242 : Assert(tbinfo->ispartition);
19243 :
19244 : /*
19245 : * Partition triggers only appear here because their 'tgenabled' flag
19246 : * differs from its parent's. The trigger is created already, so
19247 : * remove the CREATE and replace it with an ALTER. (Clear out the
19248 : * DROP query too, so that pg_dump --create does not cause errors.)
19249 : */
19250 236 : resetPQExpBuffer(query);
19251 236 : resetPQExpBuffer(delqry);
19252 236 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19253 236 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19254 236 : fmtQualifiedDumpable(tbinfo));
19255 236 : switch (tginfo->tgenabled)
19256 : {
19257 82 : case 'f':
19258 : case 'D':
19259 82 : appendPQExpBufferStr(query, "DISABLE");
19260 82 : break;
19261 0 : case 't':
19262 : case 'O':
19263 0 : appendPQExpBufferStr(query, "ENABLE");
19264 0 : break;
19265 72 : case 'R':
19266 72 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19267 72 : break;
19268 82 : case 'A':
19269 82 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19270 82 : break;
19271 : }
19272 236 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19273 236 : fmtId(tginfo->dobj.name));
19274 : }
19275 778 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19276 : {
19277 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19278 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19279 0 : fmtQualifiedDumpable(tbinfo));
19280 0 : switch (tginfo->tgenabled)
19281 : {
19282 0 : case 'D':
19283 : case 'f':
19284 0 : appendPQExpBufferStr(query, "DISABLE");
19285 0 : break;
19286 0 : case 'A':
19287 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19288 0 : break;
19289 0 : case 'R':
19290 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19291 0 : break;
19292 0 : default:
19293 0 : appendPQExpBufferStr(query, "ENABLE");
19294 0 : break;
19295 : }
19296 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19297 0 : fmtId(tginfo->dobj.name));
19298 : }
19299 :
19300 1014 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19301 1014 : fmtId(tginfo->dobj.name));
19302 :
19303 1014 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19304 :
19305 1014 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19306 1014 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19307 1014 : ARCHIVE_OPTS(.tag = tag,
19308 : .namespace = tbinfo->dobj.namespace->dobj.name,
19309 : .owner = tbinfo->rolname,
19310 : .description = "TRIGGER",
19311 : .section = SECTION_POST_DATA,
19312 : .createStmt = query->data,
19313 : .dropStmt = delqry->data));
19314 :
19315 1014 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19316 0 : dumpComment(fout, trigprefix->data, qtabname,
19317 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19318 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19319 :
19320 1014 : free(tag);
19321 1014 : destroyPQExpBuffer(query);
19322 1014 : destroyPQExpBuffer(delqry);
19323 1014 : destroyPQExpBuffer(trigprefix);
19324 1014 : destroyPQExpBuffer(trigidentity);
19325 1014 : free(qtabname);
19326 : }
19327 :
19328 : /*
19329 : * dumpEventTrigger
19330 : * write the declaration of one user-defined event trigger
19331 : */
19332 : static void
19333 90 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19334 : {
19335 90 : DumpOptions *dopt = fout->dopt;
19336 : PQExpBuffer query;
19337 : PQExpBuffer delqry;
19338 : char *qevtname;
19339 :
19340 : /* Do nothing if not dumping schema */
19341 90 : if (!dopt->dumpSchema)
19342 12 : return;
19343 :
19344 78 : query = createPQExpBuffer();
19345 78 : delqry = createPQExpBuffer();
19346 :
19347 78 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19348 :
19349 78 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19350 78 : appendPQExpBufferStr(query, qevtname);
19351 78 : appendPQExpBufferStr(query, " ON ");
19352 78 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19353 :
19354 78 : if (strcmp("", evtinfo->evttags) != 0)
19355 : {
19356 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19357 10 : appendPQExpBufferStr(query, evtinfo->evttags);
19358 10 : appendPQExpBufferChar(query, ')');
19359 : }
19360 :
19361 78 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19362 78 : appendPQExpBufferStr(query, evtinfo->evtfname);
19363 78 : appendPQExpBufferStr(query, "();\n");
19364 :
19365 78 : if (evtinfo->evtenabled != 'O')
19366 : {
19367 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19368 : qevtname);
19369 0 : switch (evtinfo->evtenabled)
19370 : {
19371 0 : case 'D':
19372 0 : appendPQExpBufferStr(query, "DISABLE");
19373 0 : break;
19374 0 : case 'A':
19375 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19376 0 : break;
19377 0 : case 'R':
19378 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19379 0 : break;
19380 0 : default:
19381 0 : appendPQExpBufferStr(query, "ENABLE");
19382 0 : break;
19383 : }
19384 0 : appendPQExpBufferStr(query, ";\n");
19385 : }
19386 :
19387 78 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19388 : qevtname);
19389 :
19390 78 : if (dopt->binary_upgrade)
19391 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19392 : "EVENT TRIGGER", qevtname, NULL);
19393 :
19394 78 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19395 78 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19396 78 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19397 : .owner = evtinfo->evtowner,
19398 : .description = "EVENT TRIGGER",
19399 : .section = SECTION_POST_DATA,
19400 : .createStmt = query->data,
19401 : .dropStmt = delqry->data));
19402 :
19403 78 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19404 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19405 0 : NULL, evtinfo->evtowner,
19406 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19407 :
19408 78 : destroyPQExpBuffer(query);
19409 78 : destroyPQExpBuffer(delqry);
19410 78 : free(qevtname);
19411 : }
19412 :
19413 : /*
19414 : * dumpRule
19415 : * Dump a rule
19416 : */
19417 : static void
19418 2380 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19419 : {
19420 2380 : DumpOptions *dopt = fout->dopt;
19421 2380 : TableInfo *tbinfo = rinfo->ruletable;
19422 : bool is_view;
19423 : PQExpBuffer query;
19424 : PQExpBuffer cmd;
19425 : PQExpBuffer delcmd;
19426 : PQExpBuffer ruleprefix;
19427 : char *qtabname;
19428 : PGresult *res;
19429 : char *tag;
19430 :
19431 : /* Do nothing if not dumping schema */
19432 2380 : if (!dopt->dumpSchema)
19433 132 : return;
19434 :
19435 : /*
19436 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19437 : * we do not want to dump it as a separate object.
19438 : */
19439 2248 : if (!rinfo->separate)
19440 1826 : return;
19441 :
19442 : /*
19443 : * If it's an ON SELECT rule, we want to print it as a view definition,
19444 : * instead of a rule.
19445 : */
19446 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19447 :
19448 422 : query = createPQExpBuffer();
19449 422 : cmd = createPQExpBuffer();
19450 422 : delcmd = createPQExpBuffer();
19451 422 : ruleprefix = createPQExpBuffer();
19452 :
19453 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19454 :
19455 422 : if (is_view)
19456 : {
19457 : PQExpBuffer result;
19458 :
19459 : /*
19460 : * We need OR REPLACE here because we'll be replacing a dummy view.
19461 : * Otherwise this should look largely like the regular view dump code.
19462 : */
19463 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19464 20 : fmtQualifiedDumpable(tbinfo));
19465 20 : if (nonemptyReloptions(tbinfo->reloptions))
19466 : {
19467 0 : appendPQExpBufferStr(cmd, " WITH (");
19468 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19469 0 : appendPQExpBufferChar(cmd, ')');
19470 : }
19471 20 : result = createViewAsClause(fout, tbinfo);
19472 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19473 20 : destroyPQExpBuffer(result);
19474 20 : if (tbinfo->checkoption != NULL)
19475 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19476 : tbinfo->checkoption);
19477 20 : appendPQExpBufferStr(cmd, ";\n");
19478 : }
19479 : else
19480 : {
19481 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19482 402 : appendPQExpBuffer(query,
19483 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19484 402 : rinfo->dobj.catId.oid);
19485 :
19486 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19487 :
19488 402 : if (PQntuples(res) != 1)
19489 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19490 : rinfo->dobj.name, tbinfo->dobj.name);
19491 :
19492 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19493 :
19494 402 : PQclear(res);
19495 : }
19496 :
19497 : /*
19498 : * Add the command to alter the rules replication firing semantics if it
19499 : * differs from the default.
19500 : */
19501 422 : if (rinfo->ev_enabled != 'O')
19502 : {
19503 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19504 30 : switch (rinfo->ev_enabled)
19505 : {
19506 0 : case 'A':
19507 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19508 0 : fmtId(rinfo->dobj.name));
19509 0 : break;
19510 0 : case 'R':
19511 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19512 0 : fmtId(rinfo->dobj.name));
19513 0 : break;
19514 30 : case 'D':
19515 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19516 30 : fmtId(rinfo->dobj.name));
19517 30 : break;
19518 : }
19519 : }
19520 :
19521 422 : if (is_view)
19522 : {
19523 : /*
19524 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19525 : * REPLACE VIEW to replace the rule with something with minimal
19526 : * dependencies.
19527 : */
19528 : PQExpBuffer result;
19529 :
19530 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19531 20 : fmtQualifiedDumpable(tbinfo));
19532 20 : result = createDummyViewAsClause(fout, tbinfo);
19533 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19534 20 : destroyPQExpBuffer(result);
19535 : }
19536 : else
19537 : {
19538 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19539 402 : fmtId(rinfo->dobj.name));
19540 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
19541 402 : fmtQualifiedDumpable(tbinfo));
19542 : }
19543 :
19544 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19545 422 : fmtId(rinfo->dobj.name));
19546 :
19547 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19548 :
19549 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19550 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19551 422 : ARCHIVE_OPTS(.tag = tag,
19552 : .namespace = tbinfo->dobj.namespace->dobj.name,
19553 : .owner = tbinfo->rolname,
19554 : .description = "RULE",
19555 : .section = SECTION_POST_DATA,
19556 : .createStmt = cmd->data,
19557 : .dropStmt = delcmd->data));
19558 :
19559 : /* Dump rule comments */
19560 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19561 0 : dumpComment(fout, ruleprefix->data, qtabname,
19562 0 : tbinfo->dobj.namespace->dobj.name,
19563 : tbinfo->rolname,
19564 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19565 :
19566 422 : free(tag);
19567 422 : destroyPQExpBuffer(query);
19568 422 : destroyPQExpBuffer(cmd);
19569 422 : destroyPQExpBuffer(delcmd);
19570 422 : destroyPQExpBuffer(ruleprefix);
19571 422 : free(qtabname);
19572 : }
19573 :
19574 : /*
19575 : * getExtensionMembership --- obtain extension membership data
19576 : *
19577 : * We need to identify objects that are extension members as soon as they're
19578 : * loaded, so that we can correctly determine whether they need to be dumped.
19579 : * Generally speaking, extension member objects will get marked as *not* to
19580 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19581 : * command. However, in binary upgrade mode we still need to dump the members
19582 : * individually.
19583 : */
19584 : void
19585 366 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19586 : int numExtensions)
19587 : {
19588 : PQExpBuffer query;
19589 : PGresult *res;
19590 : int ntups,
19591 : i;
19592 : int i_classid,
19593 : i_objid,
19594 : i_refobjid;
19595 : ExtensionInfo *ext;
19596 :
19597 : /* Nothing to do if no extensions */
19598 366 : if (numExtensions == 0)
19599 0 : return;
19600 :
19601 366 : query = createPQExpBuffer();
19602 :
19603 : /* refclassid constraint is redundant but may speed the search */
19604 366 : appendPQExpBufferStr(query, "SELECT "
19605 : "classid, objid, refobjid "
19606 : "FROM pg_depend "
19607 : "WHERE refclassid = 'pg_extension'::regclass "
19608 : "AND deptype = 'e' "
19609 : "ORDER BY 3");
19610 :
19611 366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19612 :
19613 366 : ntups = PQntuples(res);
19614 :
19615 366 : i_classid = PQfnumber(res, "classid");
19616 366 : i_objid = PQfnumber(res, "objid");
19617 366 : i_refobjid = PQfnumber(res, "refobjid");
19618 :
19619 : /*
19620 : * Since we ordered the SELECT by referenced ID, we can expect that
19621 : * multiple entries for the same extension will appear together; this
19622 : * saves on searches.
19623 : */
19624 366 : ext = NULL;
19625 :
19626 3030 : for (i = 0; i < ntups; i++)
19627 : {
19628 : CatalogId objId;
19629 : Oid extId;
19630 :
19631 2664 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19632 2664 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19633 2664 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19634 :
19635 2664 : if (ext == NULL ||
19636 2298 : ext->dobj.catId.oid != extId)
19637 416 : ext = findExtensionByOid(extId);
19638 :
19639 2664 : if (ext == NULL)
19640 : {
19641 : /* shouldn't happen */
19642 0 : pg_log_warning("could not find referenced extension %u", extId);
19643 0 : continue;
19644 : }
19645 :
19646 2664 : recordExtensionMembership(objId, ext);
19647 : }
19648 :
19649 366 : PQclear(res);
19650 :
19651 366 : destroyPQExpBuffer(query);
19652 : }
19653 :
19654 : /*
19655 : * processExtensionTables --- deal with extension configuration tables
19656 : *
19657 : * There are two parts to this process:
19658 : *
19659 : * 1. Identify and create dump records for extension configuration tables.
19660 : *
19661 : * Extensions can mark tables as "configuration", which means that the user
19662 : * is able and expected to modify those tables after the extension has been
19663 : * loaded. For these tables, we dump out only the data- the structure is
19664 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19665 : * foreign keys, which brings us to-
19666 : *
19667 : * 2. Record FK dependencies between configuration tables.
19668 : *
19669 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19670 : * the data is loaded, we have to work out what the best order for reloading
19671 : * the data is, to avoid FK violations when the tables are restored. This is
19672 : * not perfect- we can't handle circular dependencies and if any exist they
19673 : * will cause an invalid dump to be produced (though at least all of the data
19674 : * is included for a user to manually restore). This is currently documented
19675 : * but perhaps we can provide a better solution in the future.
19676 : */
19677 : void
19678 364 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19679 : int numExtensions)
19680 : {
19681 364 : DumpOptions *dopt = fout->dopt;
19682 : PQExpBuffer query;
19683 : PGresult *res;
19684 : int ntups,
19685 : i;
19686 : int i_conrelid,
19687 : i_confrelid;
19688 :
19689 : /* Nothing to do if no extensions */
19690 364 : if (numExtensions == 0)
19691 0 : return;
19692 :
19693 : /*
19694 : * Identify extension configuration tables and create TableDataInfo
19695 : * objects for them, ensuring their data will be dumped even though the
19696 : * tables themselves won't be.
19697 : *
19698 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19699 : * user data in a configuration table is treated like schema data. This
19700 : * seems appropriate since system data in a config table would get
19701 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19702 : * list of extensions to be included, none of its data is dumped.
19703 : */
19704 778 : for (i = 0; i < numExtensions; i++)
19705 : {
19706 414 : ExtensionInfo *curext = &(extinfo[i]);
19707 414 : char *extconfig = curext->extconfig;
19708 414 : char *extcondition = curext->extcondition;
19709 414 : char **extconfigarray = NULL;
19710 414 : char **extconditionarray = NULL;
19711 414 : int nconfigitems = 0;
19712 414 : int nconditionitems = 0;
19713 :
19714 : /*
19715 : * Check if this extension is listed as to include in the dump. If
19716 : * not, any table data associated with it is discarded.
19717 : */
19718 414 : if (extension_include_oids.head != NULL &&
19719 16 : !simple_oid_list_member(&extension_include_oids,
19720 : curext->dobj.catId.oid))
19721 12 : continue;
19722 :
19723 : /*
19724 : * Check if this extension is listed as to exclude in the dump. If
19725 : * yes, any table data associated with it is discarded.
19726 : */
19727 414 : if (extension_exclude_oids.head != NULL &&
19728 8 : simple_oid_list_member(&extension_exclude_oids,
19729 : curext->dobj.catId.oid))
19730 4 : continue;
19731 :
19732 402 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19733 : {
19734 : int j;
19735 :
19736 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19737 0 : pg_fatal("could not parse %s array", "extconfig");
19738 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19739 0 : pg_fatal("could not parse %s array", "extcondition");
19740 40 : if (nconfigitems != nconditionitems)
19741 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19742 :
19743 120 : for (j = 0; j < nconfigitems; j++)
19744 : {
19745 : TableInfo *configtbl;
19746 80 : Oid configtbloid = atooid(extconfigarray[j]);
19747 80 : bool dumpobj =
19748 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19749 :
19750 80 : configtbl = findTableByOid(configtbloid);
19751 80 : if (configtbl == NULL)
19752 0 : continue;
19753 :
19754 : /*
19755 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19756 : * unless the table or its schema is explicitly included
19757 : */
19758 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19759 : {
19760 : /* check table explicitly requested */
19761 4 : if (table_include_oids.head != NULL &&
19762 0 : simple_oid_list_member(&table_include_oids,
19763 : configtbloid))
19764 0 : dumpobj = true;
19765 :
19766 : /* check table's schema explicitly requested */
19767 4 : if (configtbl->dobj.namespace->dobj.dump &
19768 : DUMP_COMPONENT_DATA)
19769 4 : dumpobj = true;
19770 : }
19771 :
19772 : /* check table excluded by an exclusion switch */
19773 88 : if (table_exclude_oids.head != NULL &&
19774 8 : simple_oid_list_member(&table_exclude_oids,
19775 : configtbloid))
19776 2 : dumpobj = false;
19777 :
19778 : /* check schema excluded by an exclusion switch */
19779 80 : if (simple_oid_list_member(&schema_exclude_oids,
19780 80 : configtbl->dobj.namespace->dobj.catId.oid))
19781 0 : dumpobj = false;
19782 :
19783 80 : if (dumpobj)
19784 : {
19785 78 : makeTableDataInfo(dopt, configtbl);
19786 78 : if (configtbl->dataObj != NULL)
19787 : {
19788 78 : if (strlen(extconditionarray[j]) > 0)
19789 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19790 : }
19791 : }
19792 : }
19793 : }
19794 402 : if (extconfigarray)
19795 40 : free(extconfigarray);
19796 402 : if (extconditionarray)
19797 40 : free(extconditionarray);
19798 : }
19799 :
19800 : /*
19801 : * Now that all the TableDataInfo objects have been created for all the
19802 : * extensions, check their FK dependencies and register them to try and
19803 : * dump the data out in an order that they can be restored in.
19804 : *
19805 : * Note that this is not a problem for user tables as their FKs are
19806 : * recreated after the data has been loaded.
19807 : */
19808 :
19809 364 : query = createPQExpBuffer();
19810 :
19811 364 : printfPQExpBuffer(query,
19812 : "SELECT conrelid, confrelid "
19813 : "FROM pg_constraint "
19814 : "JOIN pg_depend ON (objid = confrelid) "
19815 : "WHERE contype = 'f' "
19816 : "AND refclassid = 'pg_extension'::regclass "
19817 : "AND classid = 'pg_class'::regclass;");
19818 :
19819 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19820 364 : ntups = PQntuples(res);
19821 :
19822 364 : i_conrelid = PQfnumber(res, "conrelid");
19823 364 : i_confrelid = PQfnumber(res, "confrelid");
19824 :
19825 : /* Now get the dependencies and register them */
19826 364 : for (i = 0; i < ntups; i++)
19827 : {
19828 : Oid conrelid,
19829 : confrelid;
19830 : TableInfo *reftable,
19831 : *contable;
19832 :
19833 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19834 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19835 0 : contable = findTableByOid(conrelid);
19836 0 : reftable = findTableByOid(confrelid);
19837 :
19838 0 : if (reftable == NULL ||
19839 0 : reftable->dataObj == NULL ||
19840 0 : contable == NULL ||
19841 0 : contable->dataObj == NULL)
19842 0 : continue;
19843 :
19844 : /*
19845 : * Make referencing TABLE_DATA object depend on the referenced table's
19846 : * TABLE_DATA object.
19847 : */
19848 0 : addObjectDependency(&contable->dataObj->dobj,
19849 0 : reftable->dataObj->dobj.dumpId);
19850 : }
19851 364 : PQclear(res);
19852 364 : destroyPQExpBuffer(query);
19853 : }
19854 :
19855 : /*
19856 : * getDependencies --- obtain available dependency data
19857 : */
19858 : static void
19859 364 : getDependencies(Archive *fout)
19860 : {
19861 : PQExpBuffer query;
19862 : PGresult *res;
19863 : int ntups,
19864 : i;
19865 : int i_classid,
19866 : i_objid,
19867 : i_refclassid,
19868 : i_refobjid,
19869 : i_deptype;
19870 : DumpableObject *dobj,
19871 : *refdobj;
19872 :
19873 364 : pg_log_info("reading dependency data");
19874 :
19875 364 : query = createPQExpBuffer();
19876 :
19877 : /*
19878 : * Messy query to collect the dependency data we need. Note that we
19879 : * ignore the sub-object column, so that dependencies of or on a column
19880 : * look the same as dependencies of or on a whole table.
19881 : *
19882 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19883 : * already processed by getExtensionMembership.
19884 : */
19885 364 : appendPQExpBufferStr(query, "SELECT "
19886 : "classid, objid, refclassid, refobjid, deptype "
19887 : "FROM pg_depend "
19888 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19889 :
19890 : /*
19891 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19892 : * have to translate their dependencies into dependencies of their parent
19893 : * opfamily. Ignore internal dependencies though, as those will point to
19894 : * their parent opclass, which we needn't consider here (and if we did,
19895 : * it'd just result in circular dependencies). Also, "loose" opfamily
19896 : * entries will have dependencies on their parent opfamily, which we
19897 : * should drop since they'd likewise become useless self-dependencies.
19898 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19899 : */
19900 364 : appendPQExpBufferStr(query, "UNION ALL\n"
19901 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19902 : "FROM pg_depend d, pg_amop o "
19903 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19904 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19905 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19906 :
19907 : /* Likewise for pg_amproc entries */
19908 364 : appendPQExpBufferStr(query, "UNION ALL\n"
19909 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19910 : "FROM pg_depend d, pg_amproc p "
19911 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19912 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19913 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19914 :
19915 : /* Sort the output for efficiency below */
19916 364 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19917 :
19918 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19919 :
19920 364 : ntups = PQntuples(res);
19921 :
19922 364 : i_classid = PQfnumber(res, "classid");
19923 364 : i_objid = PQfnumber(res, "objid");
19924 364 : i_refclassid = PQfnumber(res, "refclassid");
19925 364 : i_refobjid = PQfnumber(res, "refobjid");
19926 364 : i_deptype = PQfnumber(res, "deptype");
19927 :
19928 : /*
19929 : * Since we ordered the SELECT by referencing ID, we can expect that
19930 : * multiple entries for the same object will appear together; this saves
19931 : * on searches.
19932 : */
19933 364 : dobj = NULL;
19934 :
19935 791026 : for (i = 0; i < ntups; i++)
19936 : {
19937 : CatalogId objId;
19938 : CatalogId refobjId;
19939 : char deptype;
19940 :
19941 790662 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19942 790662 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19943 790662 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19944 790662 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19945 790662 : deptype = *(PQgetvalue(res, i, i_deptype));
19946 :
19947 790662 : if (dobj == NULL ||
19948 740192 : dobj->catId.tableoid != objId.tableoid ||
19949 736028 : dobj->catId.oid != objId.oid)
19950 348854 : dobj = findObjectByCatalogId(objId);
19951 :
19952 : /*
19953 : * Failure to find objects mentioned in pg_depend is not unexpected,
19954 : * since for example we don't collect info about TOAST tables.
19955 : */
19956 790662 : if (dobj == NULL)
19957 : {
19958 : #ifdef NOT_USED
19959 : pg_log_warning("no referencing object %u %u",
19960 : objId.tableoid, objId.oid);
19961 : #endif
19962 51774 : continue;
19963 : }
19964 :
19965 740556 : refdobj = findObjectByCatalogId(refobjId);
19966 :
19967 740556 : if (refdobj == NULL)
19968 : {
19969 : #ifdef NOT_USED
19970 : pg_log_warning("no referenced object %u %u",
19971 : refobjId.tableoid, refobjId.oid);
19972 : #endif
19973 1668 : continue;
19974 : }
19975 :
19976 : /*
19977 : * For 'x' dependencies, mark the object for later; we still add the
19978 : * normal dependency, for possible ordering purposes. Currently
19979 : * pg_dump_sort.c knows to put extensions ahead of all object types
19980 : * that could possibly depend on them, but this is safer.
19981 : */
19982 738888 : if (deptype == 'x')
19983 88 : dobj->depends_on_ext = true;
19984 :
19985 : /*
19986 : * Ordinarily, table rowtypes have implicit dependencies on their
19987 : * tables. However, for a composite type the implicit dependency goes
19988 : * the other way in pg_depend; which is the right thing for DROP but
19989 : * it doesn't produce the dependency ordering we need. So in that one
19990 : * case, we reverse the direction of the dependency.
19991 : */
19992 738888 : if (deptype == 'i' &&
19993 208208 : dobj->objType == DO_TABLE &&
19994 2524 : refdobj->objType == DO_TYPE)
19995 370 : addObjectDependency(refdobj, dobj->dumpId);
19996 : else
19997 : /* normal case */
19998 738518 : addObjectDependency(dobj, refdobj->dumpId);
19999 : }
20000 :
20001 364 : PQclear(res);
20002 :
20003 364 : destroyPQExpBuffer(query);
20004 364 : }
20005 :
20006 :
20007 : /*
20008 : * createBoundaryObjects - create dummy DumpableObjects to represent
20009 : * dump section boundaries.
20010 : */
20011 : static DumpableObject *
20012 364 : createBoundaryObjects(void)
20013 : {
20014 : DumpableObject *dobjs;
20015 :
20016 364 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20017 :
20018 364 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20019 364 : dobjs[0].catId = nilCatalogId;
20020 364 : AssignDumpId(dobjs + 0);
20021 364 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20022 :
20023 364 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20024 364 : dobjs[1].catId = nilCatalogId;
20025 364 : AssignDumpId(dobjs + 1);
20026 364 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20027 :
20028 364 : return dobjs;
20029 : }
20030 :
20031 : /*
20032 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20033 : * section boundaries.
20034 : */
20035 : static void
20036 364 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20037 : DumpableObject *boundaryObjs)
20038 : {
20039 364 : DumpableObject *preDataBound = boundaryObjs + 0;
20040 364 : DumpableObject *postDataBound = boundaryObjs + 1;
20041 : int i;
20042 :
20043 1355506 : for (i = 0; i < numObjs; i++)
20044 : {
20045 1355142 : DumpableObject *dobj = dobjs[i];
20046 :
20047 : /*
20048 : * The classification of object types here must match the SECTION_xxx
20049 : * values assigned during subsequent ArchiveEntry calls!
20050 : */
20051 1355142 : switch (dobj->objType)
20052 : {
20053 1264126 : case DO_NAMESPACE:
20054 : case DO_EXTENSION:
20055 : case DO_TYPE:
20056 : case DO_SHELL_TYPE:
20057 : case DO_FUNC:
20058 : case DO_AGG:
20059 : case DO_OPERATOR:
20060 : case DO_ACCESS_METHOD:
20061 : case DO_OPCLASS:
20062 : case DO_OPFAMILY:
20063 : case DO_COLLATION:
20064 : case DO_CONVERSION:
20065 : case DO_TABLE:
20066 : case DO_TABLE_ATTACH:
20067 : case DO_ATTRDEF:
20068 : case DO_PROCLANG:
20069 : case DO_CAST:
20070 : case DO_DUMMY_TYPE:
20071 : case DO_TSPARSER:
20072 : case DO_TSDICT:
20073 : case DO_TSTEMPLATE:
20074 : case DO_TSCONFIG:
20075 : case DO_FDW:
20076 : case DO_FOREIGN_SERVER:
20077 : case DO_TRANSFORM:
20078 : /* Pre-data objects: must come before the pre-data boundary */
20079 1264126 : addObjectDependency(preDataBound, dobj->dumpId);
20080 1264126 : break;
20081 9884 : case DO_TABLE_DATA:
20082 : case DO_SEQUENCE_SET:
20083 : case DO_LARGE_OBJECT:
20084 : case DO_LARGE_OBJECT_DATA:
20085 : /* Data objects: must come between the boundaries */
20086 9884 : addObjectDependency(dobj, preDataBound->dumpId);
20087 9884 : addObjectDependency(postDataBound, dobj->dumpId);
20088 9884 : break;
20089 11736 : case DO_INDEX:
20090 : case DO_INDEX_ATTACH:
20091 : case DO_STATSEXT:
20092 : case DO_REFRESH_MATVIEW:
20093 : case DO_TRIGGER:
20094 : case DO_EVENT_TRIGGER:
20095 : case DO_DEFAULT_ACL:
20096 : case DO_POLICY:
20097 : case DO_PUBLICATION:
20098 : case DO_PUBLICATION_REL:
20099 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20100 : case DO_SUBSCRIPTION:
20101 : case DO_SUBSCRIPTION_REL:
20102 : /* Post-data objects: must come after the post-data boundary */
20103 11736 : addObjectDependency(dobj, postDataBound->dumpId);
20104 11736 : break;
20105 56300 : case DO_RULE:
20106 : /* Rules are post-data, but only if dumped separately */
20107 56300 : if (((RuleInfo *) dobj)->separate)
20108 1274 : addObjectDependency(dobj, postDataBound->dumpId);
20109 56300 : break;
20110 5216 : case DO_CONSTRAINT:
20111 : case DO_FK_CONSTRAINT:
20112 : /* Constraints are post-data, but only if dumped separately */
20113 5216 : if (((ConstraintInfo *) dobj)->separate)
20114 3708 : addObjectDependency(dobj, postDataBound->dumpId);
20115 5216 : break;
20116 364 : case DO_PRE_DATA_BOUNDARY:
20117 : /* nothing to do */
20118 364 : break;
20119 364 : case DO_POST_DATA_BOUNDARY:
20120 : /* must come after the pre-data boundary */
20121 364 : addObjectDependency(dobj, preDataBound->dumpId);
20122 364 : break;
20123 7152 : case DO_REL_STATS:
20124 : /* stats section varies by parent object type, DATA or POST */
20125 7152 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20126 : {
20127 4586 : addObjectDependency(dobj, preDataBound->dumpId);
20128 4586 : addObjectDependency(postDataBound, dobj->dumpId);
20129 : }
20130 : else
20131 2566 : addObjectDependency(dobj, postDataBound->dumpId);
20132 7152 : break;
20133 : }
20134 : }
20135 364 : }
20136 :
20137 :
20138 : /*
20139 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20140 : *
20141 : * The raw dependency data obtained by getDependencies() is not terribly
20142 : * useful in an archive dump, because in many cases there are dependency
20143 : * chains linking through objects that don't appear explicitly in the dump.
20144 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20145 : * will depend on other objects --- but the rule will not appear as a separate
20146 : * object in the dump. We need to adjust the view's dependencies to include
20147 : * whatever the rule depends on that is included in the dump.
20148 : *
20149 : * Just to make things more complicated, there are also "special" dependencies
20150 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20151 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20152 : * its table. In these cases we must leave the dependencies strictly as-is
20153 : * even if they refer to not-to-be-dumped objects.
20154 : *
20155 : * To handle this, the convention is that "special" dependencies are created
20156 : * during ArchiveEntry calls, and an archive TOC item that has any such
20157 : * entries will not be touched here. Otherwise, we recursively search the
20158 : * DumpableObject data structures to build the correct dependencies for each
20159 : * archive TOC item.
20160 : */
20161 : static void
20162 106 : BuildArchiveDependencies(Archive *fout)
20163 : {
20164 106 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20165 : TocEntry *te;
20166 :
20167 : /* Scan all TOC entries in the archive */
20168 14520 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20169 : {
20170 : DumpableObject *dobj;
20171 : DumpId *dependencies;
20172 : int nDeps;
20173 : int allocDeps;
20174 :
20175 : /* No need to process entries that will not be dumped */
20176 14414 : if (te->reqs == 0)
20177 7082 : continue;
20178 : /* Ignore entries that already have "special" dependencies */
20179 14408 : if (te->nDeps > 0)
20180 6274 : continue;
20181 : /* Otherwise, look up the item's original DumpableObject, if any */
20182 8134 : dobj = findObjectByDumpId(te->dumpId);
20183 8134 : if (dobj == NULL)
20184 582 : continue;
20185 : /* No work if it has no dependencies */
20186 7552 : if (dobj->nDeps <= 0)
20187 220 : continue;
20188 : /* Set up work array */
20189 7332 : allocDeps = 64;
20190 7332 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20191 7332 : nDeps = 0;
20192 : /* Recursively find all dumpable dependencies */
20193 7332 : findDumpableDependencies(AH, dobj,
20194 : &dependencies, &nDeps, &allocDeps);
20195 : /* And save 'em ... */
20196 7332 : if (nDeps > 0)
20197 : {
20198 5640 : dependencies = (DumpId *) pg_realloc(dependencies,
20199 : nDeps * sizeof(DumpId));
20200 5640 : te->dependencies = dependencies;
20201 5640 : te->nDeps = nDeps;
20202 : }
20203 : else
20204 1692 : free(dependencies);
20205 : }
20206 106 : }
20207 :
20208 : /* Recursive search subroutine for BuildArchiveDependencies */
20209 : static void
20210 17574 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20211 : DumpId **dependencies, int *nDeps, int *allocDeps)
20212 : {
20213 : int i;
20214 :
20215 : /*
20216 : * Ignore section boundary objects: if we search through them, we'll
20217 : * report lots of bogus dependencies.
20218 : */
20219 17574 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20220 17542 : dobj->objType == DO_POST_DATA_BOUNDARY)
20221 3060 : return;
20222 :
20223 36632 : for (i = 0; i < dobj->nDeps; i++)
20224 : {
20225 22118 : DumpId depid = dobj->dependencies[i];
20226 :
20227 22118 : if (TocIDRequired(AH, depid) != 0)
20228 : {
20229 : /* Object will be dumped, so just reference it as a dependency */
20230 11876 : if (*nDeps >= *allocDeps)
20231 : {
20232 0 : *allocDeps *= 2;
20233 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20234 0 : *allocDeps * sizeof(DumpId));
20235 : }
20236 11876 : (*dependencies)[*nDeps] = depid;
20237 11876 : (*nDeps)++;
20238 : }
20239 : else
20240 : {
20241 : /*
20242 : * Object will not be dumped, so recursively consider its deps. We
20243 : * rely on the assumption that sortDumpableObjects already broke
20244 : * any dependency loops, else we might recurse infinitely.
20245 : */
20246 10242 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20247 :
20248 10242 : if (otherdobj)
20249 10242 : findDumpableDependencies(AH, otherdobj,
20250 : dependencies, nDeps, allocDeps);
20251 : }
20252 : }
20253 : }
20254 :
20255 :
20256 : /*
20257 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20258 : * given type OID.
20259 : *
20260 : * This does not guarantee to schema-qualify the output, so it should not
20261 : * be used to create the target object name for CREATE or ALTER commands.
20262 : *
20263 : * Note that the result is cached and must not be freed by the caller.
20264 : */
20265 : static const char *
20266 4672 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20267 : {
20268 : TypeInfo *typeInfo;
20269 : char *result;
20270 : PQExpBuffer query;
20271 : PGresult *res;
20272 :
20273 4672 : if (oid == 0)
20274 : {
20275 0 : if ((opts & zeroAsStar) != 0)
20276 0 : return "*";
20277 0 : else if ((opts & zeroAsNone) != 0)
20278 0 : return "NONE";
20279 : }
20280 :
20281 : /* see if we have the result cached in the type's TypeInfo record */
20282 4672 : typeInfo = findTypeByOid(oid);
20283 4672 : if (typeInfo && typeInfo->ftypname)
20284 3696 : return typeInfo->ftypname;
20285 :
20286 976 : query = createPQExpBuffer();
20287 976 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20288 : oid);
20289 :
20290 976 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20291 :
20292 : /* result of format_type is already quoted */
20293 976 : result = pg_strdup(PQgetvalue(res, 0, 0));
20294 :
20295 976 : PQclear(res);
20296 976 : destroyPQExpBuffer(query);
20297 :
20298 : /*
20299 : * Cache the result for re-use in later requests, if possible. If we
20300 : * don't have a TypeInfo for the type, the string will be leaked once the
20301 : * caller is done with it ... but that case really should not happen, so
20302 : * leaking if it does seems acceptable.
20303 : */
20304 976 : if (typeInfo)
20305 976 : typeInfo->ftypname = result;
20306 :
20307 976 : return result;
20308 : }
20309 :
20310 : /*
20311 : * Return a column list clause for the given relation.
20312 : *
20313 : * Special case: if there are no undropped columns in the relation, return
20314 : * "", not an invalid "()" column list.
20315 : */
20316 : static const char *
20317 17020 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20318 : {
20319 17020 : int numatts = ti->numatts;
20320 17020 : char **attnames = ti->attnames;
20321 17020 : bool *attisdropped = ti->attisdropped;
20322 17020 : char *attgenerated = ti->attgenerated;
20323 : bool needComma;
20324 : int i;
20325 :
20326 17020 : appendPQExpBufferChar(buffer, '(');
20327 17020 : needComma = false;
20328 81696 : for (i = 0; i < numatts; i++)
20329 : {
20330 64676 : if (attisdropped[i])
20331 1208 : continue;
20332 63468 : if (attgenerated[i])
20333 2304 : continue;
20334 61164 : if (needComma)
20335 44628 : appendPQExpBufferStr(buffer, ", ");
20336 61164 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20337 61164 : needComma = true;
20338 : }
20339 :
20340 17020 : if (!needComma)
20341 484 : return ""; /* no undropped columns */
20342 :
20343 16536 : appendPQExpBufferChar(buffer, ')');
20344 16536 : return buffer->data;
20345 : }
20346 :
20347 : /*
20348 : * Check if a reloptions array is nonempty.
20349 : */
20350 : static bool
20351 27856 : nonemptyReloptions(const char *reloptions)
20352 : {
20353 : /* Don't want to print it if it's just "{}" */
20354 27856 : return (reloptions != NULL && strlen(reloptions) > 2);
20355 : }
20356 :
20357 : /*
20358 : * Format a reloptions array and append it to the given buffer.
20359 : *
20360 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20361 : */
20362 : static void
20363 428 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20364 : const char *prefix, Archive *fout)
20365 : {
20366 : bool res;
20367 :
20368 428 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20369 428 : fout->std_strings);
20370 428 : if (!res)
20371 0 : pg_log_warning("could not parse %s array", "reloptions");
20372 428 : }
20373 :
20374 : /*
20375 : * read_dump_filters - retrieve object identifier patterns from file
20376 : *
20377 : * Parse the specified filter file for include and exclude patterns, and add
20378 : * them to the relevant lists. If the filename is "-" then filters will be
20379 : * read from STDIN rather than a file.
20380 : */
20381 : static void
20382 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20383 : {
20384 : FilterStateData fstate;
20385 : char *objname;
20386 : FilterCommandType comtype;
20387 : FilterObjectType objtype;
20388 :
20389 52 : filter_init(&fstate, filename, exit_nicely);
20390 :
20391 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20392 : {
20393 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20394 : {
20395 34 : switch (objtype)
20396 : {
20397 0 : case FILTER_OBJECT_TYPE_NONE:
20398 0 : break;
20399 0 : case FILTER_OBJECT_TYPE_DATABASE:
20400 : case FILTER_OBJECT_TYPE_FUNCTION:
20401 : case FILTER_OBJECT_TYPE_INDEX:
20402 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20403 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20404 : case FILTER_OBJECT_TYPE_TRIGGER:
20405 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20406 : "include",
20407 : filter_object_type_name(objtype));
20408 0 : exit_nicely(1);
20409 : break; /* unreachable */
20410 :
20411 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20412 2 : simple_string_list_append(&extension_include_patterns, objname);
20413 2 : break;
20414 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20415 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20416 2 : break;
20417 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20418 2 : simple_string_list_append(&schema_include_patterns, objname);
20419 2 : dopt->include_everything = false;
20420 2 : break;
20421 26 : case FILTER_OBJECT_TYPE_TABLE:
20422 26 : simple_string_list_append(&table_include_patterns, objname);
20423 26 : dopt->include_everything = false;
20424 26 : break;
20425 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20426 2 : simple_string_list_append(&table_include_patterns_and_children,
20427 : objname);
20428 2 : dopt->include_everything = false;
20429 2 : break;
20430 : }
20431 : }
20432 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20433 : {
20434 18 : switch (objtype)
20435 : {
20436 0 : case FILTER_OBJECT_TYPE_NONE:
20437 0 : break;
20438 2 : case FILTER_OBJECT_TYPE_DATABASE:
20439 : case FILTER_OBJECT_TYPE_FUNCTION:
20440 : case FILTER_OBJECT_TYPE_INDEX:
20441 : case FILTER_OBJECT_TYPE_TRIGGER:
20442 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20443 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20444 : "exclude",
20445 : filter_object_type_name(objtype));
20446 2 : exit_nicely(1);
20447 : break;
20448 :
20449 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20450 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20451 2 : break;
20452 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20453 2 : simple_string_list_append(&tabledata_exclude_patterns,
20454 : objname);
20455 2 : break;
20456 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20457 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20458 : objname);
20459 2 : break;
20460 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20461 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20462 4 : break;
20463 4 : case FILTER_OBJECT_TYPE_TABLE:
20464 4 : simple_string_list_append(&table_exclude_patterns, objname);
20465 4 : break;
20466 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20467 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20468 : objname);
20469 2 : break;
20470 : }
20471 : }
20472 : else
20473 : {
20474 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20475 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20476 : }
20477 :
20478 64 : if (objname)
20479 50 : free(objname);
20480 : }
20481 :
20482 44 : filter_free(&fstate);
20483 44 : }
|