Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_dump.c
4 : * pg_dump is a utility for dumping out a postgres database
5 : * into a script file.
6 : *
7 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_default_acl_d.h"
51 : #include "catalog/pg_largeobject_d.h"
52 : #include "catalog/pg_proc_d.h"
53 : #include "catalog/pg_subscription_d.h"
54 : #include "catalog/pg_type_d.h"
55 : #include "common/connect.h"
56 : #include "common/int.h"
57 : #include "common/relpath.h"
58 : #include "compress_io.h"
59 : #include "dumputils.h"
60 : #include "fe_utils/option_utils.h"
61 : #include "fe_utils/string_utils.h"
62 : #include "filter.h"
63 : #include "getopt_long.h"
64 : #include "libpq/libpq-fs.h"
65 : #include "parallel.h"
66 : #include "pg_backup_db.h"
67 : #include "pg_backup_utils.h"
68 : #include "pg_dump.h"
69 : #include "storage/block.h"
70 :
71 : typedef struct
72 : {
73 : Oid roleoid; /* role's OID */
74 : const char *rolename; /* role's name */
75 : } RoleNameItem;
76 :
77 : typedef struct
78 : {
79 : const char *descr; /* comment for an object */
80 : Oid classoid; /* object class (catalog OID) */
81 : Oid objoid; /* object OID */
82 : int objsubid; /* subobject (table column #) */
83 : } CommentItem;
84 :
85 : typedef struct
86 : {
87 : const char *provider; /* label provider of this security label */
88 : const char *label; /* security label for an object */
89 : Oid classoid; /* object class (catalog OID) */
90 : Oid objoid; /* object OID */
91 : int objsubid; /* subobject (table column #) */
92 : } SecLabelItem;
93 :
94 : typedef struct
95 : {
96 : Oid oid; /* object OID */
97 : char relkind; /* object kind */
98 : RelFileNumber relfilenumber; /* object filenode */
99 : Oid toast_oid; /* toast table OID */
100 : RelFileNumber toast_relfilenumber; /* toast table filenode */
101 : Oid toast_index_oid; /* toast table index OID */
102 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
103 : } BinaryUpgradeClassOidItem;
104 :
105 : /* sequence types */
106 : typedef enum SeqType
107 : {
108 : SEQTYPE_SMALLINT,
109 : SEQTYPE_INTEGER,
110 : SEQTYPE_BIGINT,
111 : } SeqType;
112 :
113 : static const char *const SeqTypeNames[] =
114 : {
115 : [SEQTYPE_SMALLINT] = "smallint",
116 : [SEQTYPE_INTEGER] = "integer",
117 : [SEQTYPE_BIGINT] = "bigint",
118 : };
119 :
120 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
121 : "array length mismatch");
122 :
123 : typedef struct
124 : {
125 : Oid oid; /* sequence OID */
126 : SeqType seqtype; /* data type of sequence */
127 : bool cycled; /* whether sequence cycles */
128 : int64 minv; /* minimum value */
129 : int64 maxv; /* maximum value */
130 : int64 startv; /* start value */
131 : int64 incby; /* increment value */
132 : int64 cache; /* cache size */
133 : int64 last_value; /* last value of sequence */
134 : bool is_called; /* whether nextval advances before returning */
135 : } SequenceItem;
136 :
137 : typedef enum OidOptions
138 : {
139 : zeroIsError = 1,
140 : zeroAsStar = 2,
141 : zeroAsNone = 4,
142 : } OidOptions;
143 :
144 : /* global decls */
145 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
146 :
147 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
148 :
149 : /* The specified names/patterns should to match at least one entity */
150 : static int strict_names = 0;
151 :
152 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
153 :
154 : /*
155 : * Object inclusion/exclusion lists
156 : *
157 : * The string lists record the patterns given by command-line switches,
158 : * which we then convert to lists of OIDs of matching objects.
159 : */
160 : static SimpleStringList schema_include_patterns = {NULL, NULL};
161 : static SimpleOidList schema_include_oids = {NULL, NULL};
162 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
163 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
164 :
165 : static SimpleStringList table_include_patterns = {NULL, NULL};
166 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
167 : static SimpleOidList table_include_oids = {NULL, NULL};
168 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
169 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
170 : static SimpleOidList table_exclude_oids = {NULL, NULL};
171 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
172 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
173 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
174 :
175 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
176 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
177 :
178 : static SimpleStringList extension_include_patterns = {NULL, NULL};
179 : static SimpleOidList extension_include_oids = {NULL, NULL};
180 :
181 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
182 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
183 :
184 : static const CatalogId nilCatalogId = {0, 0};
185 :
186 : /* override for standard extra_float_digits setting */
187 : static bool have_extra_float_digits = false;
188 : static int extra_float_digits;
189 :
190 : /* sorted table of role names */
191 : static RoleNameItem *rolenames = NULL;
192 : static int nrolenames = 0;
193 :
194 : /* sorted table of comments */
195 : static CommentItem *comments = NULL;
196 : static int ncomments = 0;
197 :
198 : /* sorted table of security labels */
199 : static SecLabelItem *seclabels = NULL;
200 : static int nseclabels = 0;
201 :
202 : /* sorted table of pg_class information for binary upgrade */
203 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
204 : static int nbinaryUpgradeClassOids = 0;
205 :
206 : /* sorted table of sequences */
207 : static SequenceItem *sequences = NULL;
208 : static int nsequences = 0;
209 :
210 : /*
211 : * The default number of rows per INSERT when
212 : * --inserts is specified without --rows-per-insert
213 : */
214 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
215 :
216 : /*
217 : * Maximum number of large objects to group into a single ArchiveEntry.
218 : * At some point we might want to make this user-controllable, but for now
219 : * a hard-wired setting will suffice.
220 : */
221 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
222 :
223 : /*
224 : * Macro for producing quoted, schema-qualified name of a dumpable object.
225 : */
226 : #define fmtQualifiedDumpable(obj) \
227 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
228 : (obj)->dobj.name)
229 :
230 : static void help(const char *progname);
231 : static void setup_connection(Archive *AH,
232 : const char *dumpencoding, const char *dumpsnapshot,
233 : char *use_role);
234 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
235 : static void expand_schema_name_patterns(Archive *fout,
236 : SimpleStringList *patterns,
237 : SimpleOidList *oids,
238 : bool strict_names);
239 : static void expand_extension_name_patterns(Archive *fout,
240 : SimpleStringList *patterns,
241 : SimpleOidList *oids,
242 : bool strict_names);
243 : static void expand_foreign_server_name_patterns(Archive *fout,
244 : SimpleStringList *patterns,
245 : SimpleOidList *oids);
246 : static void expand_table_name_patterns(Archive *fout,
247 : SimpleStringList *patterns,
248 : SimpleOidList *oids,
249 : bool strict_names,
250 : bool with_child_tables);
251 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
252 : const char *pattern);
253 :
254 : static NamespaceInfo *findNamespace(Oid nsoid);
255 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
256 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
257 : static const char *getRoleName(const char *roleoid_str);
258 : static void collectRoleNames(Archive *fout);
259 : static void getAdditionalACLs(Archive *fout);
260 : static void dumpCommentExtended(Archive *fout, const char *type,
261 : const char *name, const char *namespace,
262 : const char *owner, CatalogId catalogId,
263 : int subid, DumpId dumpId,
264 : const char *initdb_comment);
265 : static inline void dumpComment(Archive *fout, const char *type,
266 : const char *name, const char *namespace,
267 : const char *owner, CatalogId catalogId,
268 : int subid, DumpId dumpId);
269 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
270 : static void collectComments(Archive *fout);
271 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
272 : const char *namespace, const char *owner,
273 : CatalogId catalogId, int subid, DumpId dumpId);
274 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
275 : static void collectSecLabels(Archive *fout);
276 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
277 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
278 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
279 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
280 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
281 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
282 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
283 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
284 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
285 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
286 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
287 : PGresult *res);
288 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
289 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
290 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
291 : static void dumpCast(Archive *fout, const CastInfo *cast);
292 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
293 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
294 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
295 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
296 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
297 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
298 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
299 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
300 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
301 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
302 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
303 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
304 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
305 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
306 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
307 : static void collectSequences(Archive *fout);
308 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
309 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
310 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
311 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
312 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
313 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
314 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
315 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
316 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
317 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
318 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
319 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
320 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
321 : static void dumpUserMappings(Archive *fout,
322 : const char *servername, const char *namespace,
323 : const char *owner, CatalogId catalogId, DumpId dumpId);
324 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
325 :
326 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
327 : const char *type, const char *name, const char *subname,
328 : const char *nspname, const char *tag, const char *owner,
329 : const DumpableAcl *dacl);
330 :
331 : static void getDependencies(Archive *fout);
332 : static void BuildArchiveDependencies(Archive *fout);
333 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
334 : DumpId **dependencies, int *nDeps, int *allocDeps);
335 :
336 : static DumpableObject *createBoundaryObjects(void);
337 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
338 : DumpableObject *boundaryObjs);
339 :
340 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
341 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
342 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
343 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
344 : static void buildMatViewRefreshDependencies(Archive *fout);
345 : static void getTableDataFKConstraints(void);
346 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
347 : TableInfo *tbinfo, int j,
348 : int i_notnull_name, int i_notnull_noinherit,
349 : int i_notnull_islocal);
350 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
351 : bool is_agg);
352 : static char *format_function_signature(Archive *fout,
353 : const FuncInfo *finfo, bool honor_quotes);
354 : static char *convertRegProcReference(const char *proc);
355 : static char *getFormattedOperatorName(const char *oproid);
356 : static char *convertTSFunction(Archive *fout, Oid funcOid);
357 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
358 : static void getLOs(Archive *fout);
359 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
360 : static int dumpLOs(Archive *fout, const void *arg);
361 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
362 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
363 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
364 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
365 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
366 : static void dumpDatabase(Archive *fout);
367 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
368 : const char *dbname, Oid dboid);
369 : static void dumpEncoding(Archive *AH);
370 : static void dumpStdStrings(Archive *AH);
371 : static void dumpSearchPath(Archive *AH);
372 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
373 : PQExpBuffer upgrade_buffer,
374 : Oid pg_type_oid,
375 : bool force_array_type,
376 : bool include_multirange_type);
377 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
378 : PQExpBuffer upgrade_buffer,
379 : const TableInfo *tbinfo);
380 : static void collectBinaryUpgradeClassOids(Archive *fout);
381 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
382 : PQExpBuffer upgrade_buffer,
383 : Oid pg_class_oid);
384 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
385 : const DumpableObject *dobj,
386 : const char *objtype,
387 : const char *objname,
388 : const char *objnamespace);
389 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
390 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
391 : static bool nonemptyReloptions(const char *reloptions);
392 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
393 : const char *prefix, Archive *fout);
394 : static char *get_synchronized_snapshot(Archive *fout);
395 : static void set_restrict_relation_kind(Archive *AH, const char *value);
396 : static void setupDumpWorker(Archive *AH);
397 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
398 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
399 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
400 :
401 :
402 : int
403 486 : main(int argc, char **argv)
404 : {
405 : int c;
406 486 : const char *filename = NULL;
407 486 : const char *format = "p";
408 : TableInfo *tblinfo;
409 : int numTables;
410 : DumpableObject **dobjs;
411 : int numObjs;
412 : DumpableObject *boundaryObjs;
413 : int i;
414 : int optindex;
415 : RestoreOptions *ropt;
416 : Archive *fout; /* the script file */
417 486 : bool g_verbose = false;
418 486 : const char *dumpencoding = NULL;
419 486 : const char *dumpsnapshot = NULL;
420 486 : char *use_role = NULL;
421 486 : int numWorkers = 1;
422 486 : int plainText = 0;
423 486 : ArchiveFormat archiveFormat = archUnknown;
424 : ArchiveMode archiveMode;
425 486 : pg_compress_specification compression_spec = {0};
426 486 : char *compression_detail = NULL;
427 486 : char *compression_algorithm_str = "none";
428 486 : char *error_detail = NULL;
429 486 : bool user_compression_defined = false;
430 486 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
431 486 : bool data_only = false;
432 486 : bool schema_only = false;
433 :
434 : static DumpOptions dopt;
435 :
436 : static struct option long_options[] = {
437 : {"data-only", no_argument, NULL, 'a'},
438 : {"blobs", no_argument, NULL, 'b'},
439 : {"large-objects", no_argument, NULL, 'b'},
440 : {"no-blobs", no_argument, NULL, 'B'},
441 : {"no-large-objects", no_argument, NULL, 'B'},
442 : {"clean", no_argument, NULL, 'c'},
443 : {"create", no_argument, NULL, 'C'},
444 : {"dbname", required_argument, NULL, 'd'},
445 : {"extension", required_argument, NULL, 'e'},
446 : {"file", required_argument, NULL, 'f'},
447 : {"format", required_argument, NULL, 'F'},
448 : {"host", required_argument, NULL, 'h'},
449 : {"jobs", 1, NULL, 'j'},
450 : {"no-reconnect", no_argument, NULL, 'R'},
451 : {"no-owner", no_argument, NULL, 'O'},
452 : {"port", required_argument, NULL, 'p'},
453 : {"schema", required_argument, NULL, 'n'},
454 : {"exclude-schema", required_argument, NULL, 'N'},
455 : {"schema-only", no_argument, NULL, 's'},
456 : {"superuser", required_argument, NULL, 'S'},
457 : {"table", required_argument, NULL, 't'},
458 : {"exclude-table", required_argument, NULL, 'T'},
459 : {"no-password", no_argument, NULL, 'w'},
460 : {"password", no_argument, NULL, 'W'},
461 : {"username", required_argument, NULL, 'U'},
462 : {"verbose", no_argument, NULL, 'v'},
463 : {"no-privileges", no_argument, NULL, 'x'},
464 : {"no-acl", no_argument, NULL, 'x'},
465 : {"compress", required_argument, NULL, 'Z'},
466 : {"encoding", required_argument, NULL, 'E'},
467 : {"help", no_argument, NULL, '?'},
468 : {"version", no_argument, NULL, 'V'},
469 :
470 : /*
471 : * the following options don't have an equivalent short option letter
472 : */
473 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
474 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
475 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
476 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
477 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
478 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
479 : {"exclude-table-data", required_argument, NULL, 4},
480 : {"extra-float-digits", required_argument, NULL, 8},
481 : {"if-exists", no_argument, &dopt.if_exists, 1},
482 : {"inserts", no_argument, NULL, 9},
483 : {"lock-wait-timeout", required_argument, NULL, 2},
484 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
485 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
486 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
487 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
488 : {"role", required_argument, NULL, 3},
489 : {"section", required_argument, NULL, 5},
490 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
491 : {"snapshot", required_argument, NULL, 6},
492 : {"strict-names", no_argument, &strict_names, 1},
493 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
494 : {"no-comments", no_argument, &dopt.no_comments, 1},
495 : {"no-publications", no_argument, &dopt.no_publications, 1},
496 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
497 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
498 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
499 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
500 : {"no-sync", no_argument, NULL, 7},
501 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
502 : {"rows-per-insert", required_argument, NULL, 10},
503 : {"include-foreign-data", required_argument, NULL, 11},
504 : {"table-and-children", required_argument, NULL, 12},
505 : {"exclude-table-and-children", required_argument, NULL, 13},
506 : {"exclude-table-data-and-children", required_argument, NULL, 14},
507 : {"sync-method", required_argument, NULL, 15},
508 : {"filter", required_argument, NULL, 16},
509 : {"exclude-extension", required_argument, NULL, 17},
510 :
511 : {NULL, 0, NULL, 0}
512 : };
513 :
514 486 : pg_logging_init(argv[0]);
515 486 : pg_logging_set_level(PG_LOG_WARNING);
516 486 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
517 :
518 : /*
519 : * Initialize what we need for parallel execution, especially for thread
520 : * support on Windows.
521 : */
522 486 : init_parallel_dump_utils();
523 :
524 486 : progname = get_progname(argv[0]);
525 :
526 486 : if (argc > 1)
527 : {
528 486 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
529 : {
530 2 : help(progname);
531 2 : exit_nicely(0);
532 : }
533 484 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
534 : {
535 94 : puts("pg_dump (PostgreSQL) " PG_VERSION);
536 94 : exit_nicely(0);
537 : }
538 : }
539 :
540 390 : InitDumpOptions(&dopt);
541 :
542 1712 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
543 : long_options, &optindex)) != -1)
544 : {
545 1338 : switch (c)
546 : {
547 16 : case 'a': /* Dump data only */
548 16 : data_only = true;
549 16 : break;
550 :
551 2 : case 'b': /* Dump LOs */
552 2 : dopt.outputLOs = true;
553 2 : break;
554 :
555 4 : case 'B': /* Don't dump LOs */
556 4 : dopt.dontOutputLOs = true;
557 4 : break;
558 :
559 12 : case 'c': /* clean (i.e., drop) schema prior to create */
560 12 : dopt.outputClean = 1;
561 12 : break;
562 :
563 58 : case 'C': /* Create DB */
564 58 : dopt.outputCreateDB = 1;
565 58 : break;
566 :
567 10 : case 'd': /* database name */
568 10 : dopt.cparams.dbname = pg_strdup(optarg);
569 10 : break;
570 :
571 8 : case 'e': /* include extension(s) */
572 8 : simple_string_list_append(&extension_include_patterns, optarg);
573 8 : dopt.include_everything = false;
574 8 : break;
575 :
576 4 : case 'E': /* Dump encoding */
577 4 : dumpencoding = pg_strdup(optarg);
578 4 : break;
579 :
580 306 : case 'f':
581 306 : filename = pg_strdup(optarg);
582 306 : break;
583 :
584 170 : case 'F':
585 170 : format = pg_strdup(optarg);
586 170 : break;
587 :
588 24 : case 'h': /* server host */
589 24 : dopt.cparams.pghost = pg_strdup(optarg);
590 24 : break;
591 :
592 22 : case 'j': /* number of dump jobs */
593 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
594 : PG_MAX_JOBS,
595 : &numWorkers))
596 2 : exit_nicely(1);
597 20 : break;
598 :
599 34 : case 'n': /* include schema(s) */
600 34 : simple_string_list_append(&schema_include_patterns, optarg);
601 34 : dopt.include_everything = false;
602 34 : break;
603 :
604 2 : case 'N': /* exclude schema(s) */
605 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
606 2 : break;
607 :
608 4 : case 'O': /* Don't reconnect to match owner */
609 4 : dopt.outputNoOwner = 1;
610 4 : break;
611 :
612 100 : case 'p': /* server port */
613 100 : dopt.cparams.pgport = pg_strdup(optarg);
614 100 : break;
615 :
616 4 : case 'R':
617 : /* no-op, still accepted for backwards compatibility */
618 4 : break;
619 :
620 36 : case 's': /* dump schema only */
621 36 : schema_only = true;
622 36 : break;
623 :
624 2 : case 'S': /* Username for superuser in plain text output */
625 2 : dopt.outputSuperuser = pg_strdup(optarg);
626 2 : break;
627 :
628 16 : case 't': /* include table(s) */
629 16 : simple_string_list_append(&table_include_patterns, optarg);
630 16 : dopt.include_everything = false;
631 16 : break;
632 :
633 8 : case 'T': /* exclude table(s) */
634 8 : simple_string_list_append(&table_exclude_patterns, optarg);
635 8 : break;
636 :
637 28 : case 'U':
638 28 : dopt.cparams.username = pg_strdup(optarg);
639 28 : break;
640 :
641 12 : case 'v': /* verbose */
642 12 : g_verbose = true;
643 12 : pg_logging_increase_verbosity();
644 12 : break;
645 :
646 2 : case 'w':
647 2 : dopt.cparams.promptPassword = TRI_NO;
648 2 : break;
649 :
650 0 : case 'W':
651 0 : dopt.cparams.promptPassword = TRI_YES;
652 0 : break;
653 :
654 4 : case 'x': /* skip ACL dump */
655 4 : dopt.aclsSkip = true;
656 4 : break;
657 :
658 24 : case 'Z': /* Compression */
659 24 : parse_compress_options(optarg, &compression_algorithm_str,
660 : &compression_detail);
661 24 : user_compression_defined = true;
662 24 : break;
663 :
664 100 : case 0:
665 : /* This covers the long options. */
666 100 : break;
667 :
668 4 : case 2: /* lock-wait-timeout */
669 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
670 4 : break;
671 :
672 6 : case 3: /* SET ROLE */
673 6 : use_role = pg_strdup(optarg);
674 6 : break;
675 :
676 2 : case 4: /* exclude table(s) data */
677 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
678 2 : break;
679 :
680 12 : case 5: /* section */
681 12 : set_dump_section(optarg, &dopt.dumpSections);
682 12 : break;
683 :
684 0 : case 6: /* snapshot */
685 0 : dumpsnapshot = pg_strdup(optarg);
686 0 : break;
687 :
688 222 : case 7: /* no-sync */
689 222 : dosync = false;
690 222 : break;
691 :
692 2 : case 8:
693 2 : have_extra_float_digits = true;
694 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
695 : &extra_float_digits))
696 2 : exit_nicely(1);
697 0 : break;
698 :
699 4 : case 9: /* inserts */
700 :
701 : /*
702 : * dump_inserts also stores --rows-per-insert, careful not to
703 : * overwrite that.
704 : */
705 4 : if (dopt.dump_inserts == 0)
706 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
707 4 : break;
708 :
709 4 : case 10: /* rows per insert */
710 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
711 : &dopt.dump_inserts))
712 2 : exit_nicely(1);
713 2 : break;
714 :
715 8 : case 11: /* include foreign data */
716 8 : simple_string_list_append(&foreign_servers_include_patterns,
717 : optarg);
718 8 : break;
719 :
720 2 : case 12: /* include table(s) and their children */
721 2 : simple_string_list_append(&table_include_patterns_and_children,
722 : optarg);
723 2 : dopt.include_everything = false;
724 2 : break;
725 :
726 2 : case 13: /* exclude table(s) and their children */
727 2 : simple_string_list_append(&table_exclude_patterns_and_children,
728 : optarg);
729 2 : break;
730 :
731 2 : case 14: /* exclude data of table(s) and children */
732 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
733 : optarg);
734 2 : break;
735 :
736 0 : case 15:
737 0 : if (!parse_sync_method(optarg, &sync_method))
738 0 : exit_nicely(1);
739 0 : break;
740 :
741 52 : case 16: /* read object filters from file */
742 52 : read_dump_filters(optarg, &dopt);
743 44 : break;
744 :
745 2 : case 17: /* exclude extension(s) */
746 2 : simple_string_list_append(&extension_exclude_patterns,
747 : optarg);
748 2 : break;
749 :
750 2 : default:
751 : /* getopt_long already emitted a complaint */
752 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
753 2 : exit_nicely(1);
754 : }
755 : }
756 :
757 : /*
758 : * Non-option argument specifies database name as long as it wasn't
759 : * already specified with -d / --dbname
760 : */
761 374 : if (optind < argc && dopt.cparams.dbname == NULL)
762 310 : dopt.cparams.dbname = argv[optind++];
763 :
764 : /* Complain if any arguments remain */
765 374 : if (optind < argc)
766 : {
767 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
768 : argv[optind]);
769 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
770 2 : exit_nicely(1);
771 : }
772 :
773 : /* --column-inserts implies --inserts */
774 372 : if (dopt.column_inserts && dopt.dump_inserts == 0)
775 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
776 :
777 : /*
778 : * Binary upgrade mode implies dumping sequence data even in schema-only
779 : * mode. This is not exposed as a separate option, but kept separate
780 : * internally for clarity.
781 : */
782 372 : if (dopt.binary_upgrade)
783 28 : dopt.sequence_data = 1;
784 :
785 372 : if (data_only && schema_only)
786 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
787 :
788 370 : if (schema_only && foreign_servers_include_patterns.head != NULL)
789 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
790 :
791 368 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
792 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
793 :
794 366 : if (data_only && dopt.outputClean)
795 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
796 :
797 364 : if (dopt.if_exists && !dopt.outputClean)
798 2 : pg_fatal("option --if-exists requires option -c/--clean");
799 :
800 : /* set derivative flags */
801 362 : dopt.dumpSchema = (!data_only);
802 362 : dopt.dumpData = (!schema_only);
803 :
804 : /*
805 : * --inserts are already implied above if --column-inserts or
806 : * --rows-per-insert were specified.
807 : */
808 362 : if (dopt.do_nothing && dopt.dump_inserts == 0)
809 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
810 :
811 : /* Identify archive format to emit */
812 360 : archiveFormat = parseArchiveFormat(format, &archiveMode);
813 :
814 : /* archiveFormat specific setup */
815 358 : if (archiveFormat == archNull)
816 294 : plainText = 1;
817 :
818 : /*
819 : * Custom and directory formats are compressed by default with gzip when
820 : * available, not the others. If gzip is not available, no compression is
821 : * done by default.
822 : */
823 358 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
824 58 : !user_compression_defined)
825 : {
826 : #ifdef HAVE_LIBZ
827 48 : compression_algorithm_str = "gzip";
828 : #else
829 : compression_algorithm_str = "none";
830 : #endif
831 : }
832 :
833 : /*
834 : * Compression options
835 : */
836 358 : if (!parse_compress_algorithm(compression_algorithm_str,
837 : &compression_algorithm))
838 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
839 : compression_algorithm_str);
840 :
841 356 : parse_compress_specification(compression_algorithm, compression_detail,
842 : &compression_spec);
843 356 : error_detail = validate_compress_specification(&compression_spec);
844 356 : if (error_detail != NULL)
845 6 : pg_fatal("invalid compression specification: %s",
846 : error_detail);
847 :
848 350 : error_detail = supports_compression(compression_spec);
849 350 : if (error_detail != NULL)
850 0 : pg_fatal("%s", error_detail);
851 :
852 : /*
853 : * Disable support for zstd workers for now - these are based on
854 : * threading, and it's unclear how it interacts with parallel dumps on
855 : * platforms where that relies on threads too (e.g. Windows).
856 : */
857 350 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
858 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
859 : "workers");
860 :
861 : /*
862 : * If emitting an archive format, we always want to emit a DATABASE item,
863 : * in case --create is specified at pg_restore time.
864 : */
865 350 : if (!plainText)
866 64 : dopt.outputCreateDB = 1;
867 :
868 : /* Parallel backup only in the directory archive format so far */
869 350 : if (archiveFormat != archDirectory && numWorkers > 1)
870 2 : pg_fatal("parallel backup only supported by the directory format");
871 :
872 : /* Open the output file */
873 348 : fout = CreateArchive(filename, archiveFormat, compression_spec,
874 : dosync, archiveMode, setupDumpWorker, sync_method);
875 :
876 : /* Make dump options accessible right away */
877 346 : SetArchiveOptions(fout, &dopt, NULL);
878 :
879 : /* Register the cleanup hook */
880 346 : on_exit_close_archive(fout);
881 :
882 : /* Let the archiver know how noisy to be */
883 346 : fout->verbose = g_verbose;
884 :
885 :
886 : /*
887 : * We allow the server to be back to 9.2, and up to any minor release of
888 : * our own major version. (See also version check in pg_dumpall.c.)
889 : */
890 346 : fout->minRemoteVersion = 90200;
891 346 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
892 :
893 346 : fout->numWorkers = numWorkers;
894 :
895 : /*
896 : * Open the database using the Archiver, so it knows about it. Errors mean
897 : * death.
898 : */
899 346 : ConnectDatabase(fout, &dopt.cparams, false);
900 342 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
901 :
902 : /*
903 : * On hot standbys, never try to dump unlogged table data, since it will
904 : * just throw an error.
905 : */
906 342 : if (fout->isStandby)
907 8 : dopt.no_unlogged_table_data = true;
908 :
909 : /*
910 : * Find the last built-in OID, if needed (prior to 8.1)
911 : *
912 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
913 : */
914 342 : g_last_builtin_oid = FirstNormalObjectId - 1;
915 :
916 342 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
917 :
918 : /* Expand schema selection patterns into OID lists */
919 342 : if (schema_include_patterns.head != NULL)
920 : {
921 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
922 : &schema_include_oids,
923 : strict_names);
924 24 : if (schema_include_oids.head == NULL)
925 2 : pg_fatal("no matching schemas were found");
926 : }
927 328 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
928 : &schema_exclude_oids,
929 : false);
930 : /* non-matching exclusion patterns aren't an error */
931 :
932 : /* Expand table selection patterns into OID lists */
933 328 : expand_table_name_patterns(fout, &table_include_patterns,
934 : &table_include_oids,
935 : strict_names, false);
936 318 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
937 : &table_include_oids,
938 : strict_names, true);
939 318 : if ((table_include_patterns.head != NULL ||
940 296 : table_include_patterns_and_children.head != NULL) &&
941 26 : table_include_oids.head == NULL)
942 4 : pg_fatal("no matching tables were found");
943 :
944 314 : expand_table_name_patterns(fout, &table_exclude_patterns,
945 : &table_exclude_oids,
946 : false, false);
947 314 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
948 : &table_exclude_oids,
949 : false, true);
950 :
951 314 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
952 : &tabledata_exclude_oids,
953 : false, false);
954 314 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
955 : &tabledata_exclude_oids,
956 : false, true);
957 :
958 314 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
959 : &foreign_servers_include_oids);
960 :
961 : /* non-matching exclusion patterns aren't an error */
962 :
963 : /* Expand extension selection patterns into OID lists */
964 312 : if (extension_include_patterns.head != NULL)
965 : {
966 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
967 : &extension_include_oids,
968 : strict_names);
969 10 : if (extension_include_oids.head == NULL)
970 2 : pg_fatal("no matching extensions were found");
971 : }
972 310 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
973 : &extension_exclude_oids,
974 : false);
975 : /* non-matching exclusion patterns aren't an error */
976 :
977 : /*
978 : * Dumping LOs is the default for dumps where an inclusion switch is not
979 : * used (an "include everything" dump). -B can be used to exclude LOs
980 : * from those dumps. -b can be used to include LOs even when an inclusion
981 : * switch is used.
982 : *
983 : * -s means "schema only" and LOs are data, not schema, so we never
984 : * include LOs when -s is used.
985 : */
986 310 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
987 228 : dopt.outputLOs = true;
988 :
989 : /*
990 : * Collect role names so we can map object owner OIDs to names.
991 : */
992 310 : collectRoleNames(fout);
993 :
994 : /*
995 : * Now scan the database and create DumpableObject structs for all the
996 : * objects we intend to dump.
997 : */
998 310 : tblinfo = getSchemaData(fout, &numTables);
999 :
1000 308 : if (dopt.dumpData)
1001 : {
1002 276 : getTableData(&dopt, tblinfo, numTables, 0);
1003 276 : buildMatViewRefreshDependencies(fout);
1004 276 : if (!dopt.dumpSchema)
1005 12 : getTableDataFKConstraints();
1006 : }
1007 :
1008 308 : if (!dopt.dumpData && dopt.sequence_data)
1009 28 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1010 :
1011 : /*
1012 : * In binary-upgrade mode, we do not have to worry about the actual LO
1013 : * data or the associated metadata that resides in the pg_largeobject and
1014 : * pg_largeobject_metadata tables, respectively.
1015 : *
1016 : * However, we do need to collect LO information as there may be comments
1017 : * or other information on LOs that we do need to dump out.
1018 : */
1019 308 : if (dopt.outputLOs || dopt.binary_upgrade)
1020 256 : getLOs(fout);
1021 :
1022 : /*
1023 : * Collect dependency data to assist in ordering the objects.
1024 : */
1025 308 : getDependencies(fout);
1026 :
1027 : /*
1028 : * Collect ACLs, comments, and security labels, if wanted.
1029 : */
1030 308 : if (!dopt.aclsSkip)
1031 304 : getAdditionalACLs(fout);
1032 308 : if (!dopt.no_comments)
1033 308 : collectComments(fout);
1034 308 : if (!dopt.no_security_labels)
1035 308 : collectSecLabels(fout);
1036 :
1037 : /* For binary upgrade mode, collect required pg_class information. */
1038 308 : if (dopt.binary_upgrade)
1039 28 : collectBinaryUpgradeClassOids(fout);
1040 :
1041 : /* Collect sequence information. */
1042 308 : collectSequences(fout);
1043 :
1044 : /* Lastly, create dummy objects to represent the section boundaries */
1045 308 : boundaryObjs = createBoundaryObjects();
1046 :
1047 : /* Get pointers to all the known DumpableObjects */
1048 308 : getDumpableObjects(&dobjs, &numObjs);
1049 :
1050 : /*
1051 : * Add dummy dependencies to enforce the dump section ordering.
1052 : */
1053 308 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1054 :
1055 : /*
1056 : * Sort the objects into a safe dump order (no forward references).
1057 : *
1058 : * We rely on dependency information to help us determine a safe order, so
1059 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1060 : * ensure that logically identical schemas will dump identically.
1061 : */
1062 308 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1063 :
1064 308 : sortDumpableObjects(dobjs, numObjs,
1065 308 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1066 :
1067 : /*
1068 : * Create archive TOC entries for all the objects to be dumped, in a safe
1069 : * order.
1070 : */
1071 :
1072 : /*
1073 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1074 : */
1075 308 : dumpEncoding(fout);
1076 308 : dumpStdStrings(fout);
1077 308 : dumpSearchPath(fout);
1078 :
1079 : /* The database items are always next, unless we don't want them at all */
1080 308 : if (dopt.outputCreateDB)
1081 120 : dumpDatabase(fout);
1082 :
1083 : /* Now the rearrangeable objects. */
1084 1126600 : for (i = 0; i < numObjs; i++)
1085 1126292 : dumpDumpableObject(fout, dobjs[i]);
1086 :
1087 : /*
1088 : * Set up options info to ensure we dump what we want.
1089 : */
1090 308 : ropt = NewRestoreOptions();
1091 308 : ropt->filename = filename;
1092 :
1093 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1094 308 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1095 308 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1096 308 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1097 308 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1098 308 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1099 308 : ropt->dropSchema = dopt.outputClean;
1100 308 : ropt->dumpData = dopt.dumpData;
1101 308 : ropt->dumpSchema = dopt.dumpSchema;
1102 308 : ropt->if_exists = dopt.if_exists;
1103 308 : ropt->column_inserts = dopt.column_inserts;
1104 308 : ropt->dumpSections = dopt.dumpSections;
1105 308 : ropt->aclsSkip = dopt.aclsSkip;
1106 308 : ropt->superuser = dopt.outputSuperuser;
1107 308 : ropt->createDB = dopt.outputCreateDB;
1108 308 : ropt->noOwner = dopt.outputNoOwner;
1109 308 : ropt->noTableAm = dopt.outputNoTableAm;
1110 308 : ropt->noTablespace = dopt.outputNoTablespaces;
1111 308 : ropt->disable_triggers = dopt.disable_triggers;
1112 308 : ropt->use_setsessauth = dopt.use_setsessauth;
1113 308 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1114 308 : ropt->dump_inserts = dopt.dump_inserts;
1115 308 : ropt->no_comments = dopt.no_comments;
1116 308 : ropt->no_publications = dopt.no_publications;
1117 308 : ropt->no_security_labels = dopt.no_security_labels;
1118 308 : ropt->no_subscriptions = dopt.no_subscriptions;
1119 308 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1120 308 : ropt->include_everything = dopt.include_everything;
1121 308 : ropt->enable_row_security = dopt.enable_row_security;
1122 308 : ropt->sequence_data = dopt.sequence_data;
1123 308 : ropt->binary_upgrade = dopt.binary_upgrade;
1124 :
1125 308 : ropt->compression_spec = compression_spec;
1126 :
1127 308 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1128 :
1129 308 : SetArchiveOptions(fout, &dopt, ropt);
1130 :
1131 : /* Mark which entries should be output */
1132 308 : ProcessArchiveRestoreOptions(fout);
1133 :
1134 : /*
1135 : * The archive's TOC entries are now marked as to which ones will actually
1136 : * be output, so we can set up their dependency lists properly. This isn't
1137 : * necessary for plain-text output, though.
1138 : */
1139 308 : if (!plainText)
1140 62 : BuildArchiveDependencies(fout);
1141 :
1142 : /*
1143 : * And finally we can do the actual output.
1144 : *
1145 : * Note: for non-plain-text output formats, the output file is written
1146 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1147 : * right now.
1148 : */
1149 308 : if (plainText)
1150 246 : RestoreArchive(fout);
1151 :
1152 306 : CloseArchive(fout);
1153 :
1154 306 : exit_nicely(0);
1155 : }
1156 :
1157 :
1158 : static void
1159 2 : help(const char *progname)
1160 : {
1161 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1162 2 : printf(_("Usage:\n"));
1163 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1164 :
1165 2 : printf(_("\nGeneral options:\n"));
1166 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1167 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1168 : " plain text (default))\n"));
1169 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1170 2 : printf(_(" -v, --verbose verbose mode\n"));
1171 2 : printf(_(" -V, --version output version information, then exit\n"));
1172 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1173 : " compress as specified\n"));
1174 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1175 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1176 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1177 2 : printf(_(" -?, --help show this help, then exit\n"));
1178 :
1179 2 : printf(_("\nOptions controlling the output content:\n"));
1180 2 : printf(_(" -a, --data-only dump only the data, not the schema\n"));
1181 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1182 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1183 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1184 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1185 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1186 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1187 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1188 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1189 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1190 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1191 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1192 : " plain-text format\n"));
1193 2 : printf(_(" -s, --schema-only dump only the schema, no data\n"));
1194 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1195 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1196 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1197 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1198 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1199 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1200 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1201 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1202 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1203 : " access to)\n"));
1204 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1205 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1206 : " do NOT dump the specified table(s), including\n"
1207 : " child and partition tables\n"));
1208 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1209 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1210 : " do NOT dump data for the specified table(s),\n"
1211 : " including child and partition tables\n"));
1212 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1213 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1214 : " based on expressions in FILENAME\n"));
1215 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1216 2 : printf(_(" --include-foreign-data=PATTERN\n"
1217 : " include data of foreign tables on foreign\n"
1218 : " servers matching PATTERN\n"));
1219 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1220 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1221 2 : printf(_(" --no-comments do not dump comment commands\n"));
1222 2 : printf(_(" --no-publications do not dump publications\n"));
1223 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1224 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1225 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1226 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1227 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1228 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1229 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1230 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1231 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1232 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1233 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1234 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1235 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1236 : " match at least one entity each\n"));
1237 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1238 : " child and partition tables\n"));
1239 2 : printf(_(" --use-set-session-authorization\n"
1240 : " use SET SESSION AUTHORIZATION commands instead of\n"
1241 : " ALTER OWNER commands to set ownership\n"));
1242 :
1243 2 : printf(_("\nConnection options:\n"));
1244 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1245 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1246 2 : printf(_(" -p, --port=PORT database server port number\n"));
1247 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1248 2 : printf(_(" -w, --no-password never prompt for password\n"));
1249 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1250 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1251 :
1252 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1253 : "variable value is used.\n\n"));
1254 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1255 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1256 2 : }
1257 :
1258 : static void
1259 374 : setup_connection(Archive *AH, const char *dumpencoding,
1260 : const char *dumpsnapshot, char *use_role)
1261 : {
1262 374 : DumpOptions *dopt = AH->dopt;
1263 374 : PGconn *conn = GetConnection(AH);
1264 : const char *std_strings;
1265 :
1266 374 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1267 :
1268 : /*
1269 : * Set the client encoding if requested.
1270 : */
1271 374 : if (dumpencoding)
1272 : {
1273 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1274 0 : pg_fatal("invalid client encoding \"%s\" specified",
1275 : dumpencoding);
1276 : }
1277 :
1278 : /*
1279 : * Get the active encoding and the standard_conforming_strings setting, so
1280 : * we know how to escape strings.
1281 : */
1282 374 : AH->encoding = PQclientEncoding(conn);
1283 :
1284 374 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1285 374 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1286 :
1287 : /*
1288 : * Set the role if requested. In a parallel dump worker, we'll be passed
1289 : * use_role == NULL, but AH->use_role is already set (if user specified it
1290 : * originally) and we should use that.
1291 : */
1292 374 : if (!use_role && AH->use_role)
1293 4 : use_role = AH->use_role;
1294 :
1295 : /* Set the role if requested */
1296 374 : if (use_role)
1297 : {
1298 10 : PQExpBuffer query = createPQExpBuffer();
1299 :
1300 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1301 10 : ExecuteSqlStatement(AH, query->data);
1302 10 : destroyPQExpBuffer(query);
1303 :
1304 : /* save it for possible later use by parallel workers */
1305 10 : if (!AH->use_role)
1306 6 : AH->use_role = pg_strdup(use_role);
1307 : }
1308 :
1309 : /* Set the datestyle to ISO to ensure the dump's portability */
1310 374 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1311 :
1312 : /* Likewise, avoid using sql_standard intervalstyle */
1313 374 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1314 :
1315 : /*
1316 : * Use an explicitly specified extra_float_digits if it has been provided.
1317 : * Otherwise, set extra_float_digits so that we can dump float data
1318 : * exactly (given correctly implemented float I/O code, anyway).
1319 : */
1320 374 : if (have_extra_float_digits)
1321 : {
1322 0 : PQExpBuffer q = createPQExpBuffer();
1323 :
1324 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1325 : extra_float_digits);
1326 0 : ExecuteSqlStatement(AH, q->data);
1327 0 : destroyPQExpBuffer(q);
1328 : }
1329 : else
1330 374 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1331 :
1332 : /*
1333 : * Disable synchronized scanning, to prevent unpredictable changes in row
1334 : * ordering across a dump and reload.
1335 : */
1336 374 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1337 :
1338 : /*
1339 : * Disable timeouts if supported.
1340 : */
1341 374 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1342 374 : if (AH->remoteVersion >= 90300)
1343 374 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1344 374 : if (AH->remoteVersion >= 90600)
1345 374 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1346 374 : if (AH->remoteVersion >= 170000)
1347 374 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1348 :
1349 : /*
1350 : * Quote all identifiers, if requested.
1351 : */
1352 374 : if (quote_all_identifiers)
1353 24 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1354 :
1355 : /*
1356 : * Adjust row-security mode, if supported.
1357 : */
1358 374 : if (AH->remoteVersion >= 90500)
1359 : {
1360 374 : if (dopt->enable_row_security)
1361 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1362 : else
1363 374 : ExecuteSqlStatement(AH, "SET row_security = off");
1364 : }
1365 :
1366 : /*
1367 : * For security reasons, we restrict the expansion of non-system views and
1368 : * access to foreign tables during the pg_dump process. This restriction
1369 : * is adjusted when dumping foreign table data.
1370 : */
1371 374 : set_restrict_relation_kind(AH, "view, foreign-table");
1372 :
1373 : /*
1374 : * Initialize prepared-query state to "nothing prepared". We do this here
1375 : * so that a parallel dump worker will have its own state.
1376 : */
1377 374 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1378 :
1379 : /*
1380 : * Start transaction-snapshot mode transaction to dump consistent data.
1381 : */
1382 374 : ExecuteSqlStatement(AH, "BEGIN");
1383 :
1384 : /*
1385 : * To support the combination of serializable_deferrable with the jobs
1386 : * option we use REPEATABLE READ for the worker connections that are
1387 : * passed a snapshot. As long as the snapshot is acquired in a
1388 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1389 : * REPEATABLE READ transaction provides the appropriate integrity
1390 : * guarantees. This is a kluge, but safe for back-patching.
1391 : */
1392 374 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1393 0 : ExecuteSqlStatement(AH,
1394 : "SET TRANSACTION ISOLATION LEVEL "
1395 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1396 : else
1397 374 : ExecuteSqlStatement(AH,
1398 : "SET TRANSACTION ISOLATION LEVEL "
1399 : "REPEATABLE READ, READ ONLY");
1400 :
1401 : /*
1402 : * If user specified a snapshot to use, select that. In a parallel dump
1403 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1404 : * is already set (if the server can handle it) and we should use that.
1405 : */
1406 374 : if (dumpsnapshot)
1407 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1408 :
1409 374 : if (AH->sync_snapshot_id)
1410 : {
1411 32 : PQExpBuffer query = createPQExpBuffer();
1412 :
1413 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1414 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1415 32 : ExecuteSqlStatement(AH, query->data);
1416 32 : destroyPQExpBuffer(query);
1417 : }
1418 342 : else if (AH->numWorkers > 1)
1419 : {
1420 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1421 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1422 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1423 : }
1424 374 : }
1425 :
1426 : /* Set up connection for a parallel worker process */
1427 : static void
1428 32 : setupDumpWorker(Archive *AH)
1429 : {
1430 : /*
1431 : * We want to re-select all the same values the leader connection is
1432 : * using. We'll have inherited directly-usable values in
1433 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1434 : * inherited encoding value back to a string to pass to setup_connection.
1435 : */
1436 32 : setup_connection(AH,
1437 : pg_encoding_to_char(AH->encoding),
1438 : NULL,
1439 : NULL);
1440 32 : }
1441 :
1442 : static char *
1443 16 : get_synchronized_snapshot(Archive *fout)
1444 : {
1445 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1446 : char *result;
1447 : PGresult *res;
1448 :
1449 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1450 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1451 16 : PQclear(res);
1452 :
1453 16 : return result;
1454 : }
1455 :
1456 : static ArchiveFormat
1457 360 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1458 : {
1459 : ArchiveFormat archiveFormat;
1460 :
1461 360 : *mode = archModeWrite;
1462 :
1463 360 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1464 : {
1465 : /* This is used by pg_dumpall, and is not documented */
1466 86 : archiveFormat = archNull;
1467 86 : *mode = archModeAppend;
1468 : }
1469 274 : else if (pg_strcasecmp(format, "c") == 0)
1470 8 : archiveFormat = archCustom;
1471 266 : else if (pg_strcasecmp(format, "custom") == 0)
1472 30 : archiveFormat = archCustom;
1473 236 : else if (pg_strcasecmp(format, "d") == 0)
1474 14 : archiveFormat = archDirectory;
1475 222 : else if (pg_strcasecmp(format, "directory") == 0)
1476 6 : archiveFormat = archDirectory;
1477 216 : else if (pg_strcasecmp(format, "p") == 0)
1478 202 : archiveFormat = archNull;
1479 14 : else if (pg_strcasecmp(format, "plain") == 0)
1480 6 : archiveFormat = archNull;
1481 8 : else if (pg_strcasecmp(format, "t") == 0)
1482 4 : archiveFormat = archTar;
1483 4 : else if (pg_strcasecmp(format, "tar") == 0)
1484 2 : archiveFormat = archTar;
1485 : else
1486 2 : pg_fatal("invalid output format \"%s\" specified", format);
1487 358 : return archiveFormat;
1488 : }
1489 :
1490 : /*
1491 : * Find the OIDs of all schemas matching the given list of patterns,
1492 : * and append them to the given OID list.
1493 : */
1494 : static void
1495 364 : expand_schema_name_patterns(Archive *fout,
1496 : SimpleStringList *patterns,
1497 : SimpleOidList *oids,
1498 : bool strict_names)
1499 : {
1500 : PQExpBuffer query;
1501 : PGresult *res;
1502 : SimpleStringListCell *cell;
1503 : int i;
1504 :
1505 364 : if (patterns->head == NULL)
1506 322 : return; /* nothing to do */
1507 :
1508 42 : query = createPQExpBuffer();
1509 :
1510 : /*
1511 : * The loop below runs multiple SELECTs might sometimes result in
1512 : * duplicate entries in the OID list, but we don't care.
1513 : */
1514 :
1515 72 : for (cell = patterns->head; cell; cell = cell->next)
1516 : {
1517 : PQExpBufferData dbbuf;
1518 : int dotcnt;
1519 :
1520 42 : appendPQExpBufferStr(query,
1521 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1522 42 : initPQExpBuffer(&dbbuf);
1523 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1524 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1525 : &dotcnt);
1526 42 : if (dotcnt > 1)
1527 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1528 : cell->val);
1529 38 : else if (dotcnt == 1)
1530 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1531 32 : termPQExpBuffer(&dbbuf);
1532 :
1533 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1534 32 : if (strict_names && PQntuples(res) == 0)
1535 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1536 :
1537 58 : for (i = 0; i < PQntuples(res); i++)
1538 : {
1539 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1540 : }
1541 :
1542 30 : PQclear(res);
1543 30 : resetPQExpBuffer(query);
1544 : }
1545 :
1546 30 : destroyPQExpBuffer(query);
1547 : }
1548 :
1549 : /*
1550 : * Find the OIDs of all extensions matching the given list of patterns,
1551 : * and append them to the given OID list.
1552 : */
1553 : static void
1554 320 : expand_extension_name_patterns(Archive *fout,
1555 : SimpleStringList *patterns,
1556 : SimpleOidList *oids,
1557 : bool strict_names)
1558 : {
1559 : PQExpBuffer query;
1560 : PGresult *res;
1561 : SimpleStringListCell *cell;
1562 : int i;
1563 :
1564 320 : if (patterns->head == NULL)
1565 306 : return; /* nothing to do */
1566 :
1567 14 : query = createPQExpBuffer();
1568 :
1569 : /*
1570 : * The loop below runs multiple SELECTs might sometimes result in
1571 : * duplicate entries in the OID list, but we don't care.
1572 : */
1573 28 : for (cell = patterns->head; cell; cell = cell->next)
1574 : {
1575 : int dotcnt;
1576 :
1577 14 : appendPQExpBufferStr(query,
1578 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1579 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1580 : false, NULL, "e.extname", NULL, NULL, NULL,
1581 : &dotcnt);
1582 14 : if (dotcnt > 0)
1583 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1584 : cell->val);
1585 :
1586 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1587 14 : if (strict_names && PQntuples(res) == 0)
1588 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1589 :
1590 26 : for (i = 0; i < PQntuples(res); i++)
1591 : {
1592 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1593 : }
1594 :
1595 14 : PQclear(res);
1596 14 : resetPQExpBuffer(query);
1597 : }
1598 :
1599 14 : destroyPQExpBuffer(query);
1600 : }
1601 :
1602 : /*
1603 : * Find the OIDs of all foreign servers matching the given list of patterns,
1604 : * and append them to the given OID list.
1605 : */
1606 : static void
1607 314 : expand_foreign_server_name_patterns(Archive *fout,
1608 : SimpleStringList *patterns,
1609 : SimpleOidList *oids)
1610 : {
1611 : PQExpBuffer query;
1612 : PGresult *res;
1613 : SimpleStringListCell *cell;
1614 : int i;
1615 :
1616 314 : if (patterns->head == NULL)
1617 308 : return; /* nothing to do */
1618 :
1619 6 : query = createPQExpBuffer();
1620 :
1621 : /*
1622 : * The loop below runs multiple SELECTs might sometimes result in
1623 : * duplicate entries in the OID list, but we don't care.
1624 : */
1625 :
1626 10 : for (cell = patterns->head; cell; cell = cell->next)
1627 : {
1628 : int dotcnt;
1629 :
1630 6 : appendPQExpBufferStr(query,
1631 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1632 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1633 : false, NULL, "s.srvname", NULL, NULL, NULL,
1634 : &dotcnt);
1635 6 : if (dotcnt > 0)
1636 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1637 : cell->val);
1638 :
1639 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1640 6 : if (PQntuples(res) == 0)
1641 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1642 :
1643 8 : for (i = 0; i < PQntuples(res); i++)
1644 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1645 :
1646 4 : PQclear(res);
1647 4 : resetPQExpBuffer(query);
1648 : }
1649 :
1650 4 : destroyPQExpBuffer(query);
1651 : }
1652 :
1653 : /*
1654 : * Find the OIDs of all tables matching the given list of patterns,
1655 : * and append them to the given OID list. See also expand_dbname_patterns()
1656 : * in pg_dumpall.c
1657 : */
1658 : static void
1659 1902 : expand_table_name_patterns(Archive *fout,
1660 : SimpleStringList *patterns, SimpleOidList *oids,
1661 : bool strict_names, bool with_child_tables)
1662 : {
1663 : PQExpBuffer query;
1664 : PGresult *res;
1665 : SimpleStringListCell *cell;
1666 : int i;
1667 :
1668 1902 : if (patterns->head == NULL)
1669 1844 : return; /* nothing to do */
1670 :
1671 58 : query = createPQExpBuffer();
1672 :
1673 : /*
1674 : * this might sometimes result in duplicate entries in the OID list, but
1675 : * we don't care.
1676 : */
1677 :
1678 118 : for (cell = patterns->head; cell; cell = cell->next)
1679 : {
1680 : PQExpBufferData dbbuf;
1681 : int dotcnt;
1682 :
1683 : /*
1684 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1685 : * would be unnecessary given a pg_table_is_visible() variant taking a
1686 : * search_path argument.
1687 : *
1688 : * For with_child_tables, we start with the basic query's results and
1689 : * recursively search the inheritance tree to add child tables.
1690 : */
1691 70 : if (with_child_tables)
1692 : {
1693 12 : appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1694 : }
1695 :
1696 70 : appendPQExpBuffer(query,
1697 : "SELECT c.oid"
1698 : "\nFROM pg_catalog.pg_class c"
1699 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1700 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1701 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1702 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1703 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1704 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1705 : RELKIND_PARTITIONED_TABLE);
1706 70 : initPQExpBuffer(&dbbuf);
1707 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1708 : false, "n.nspname", "c.relname", NULL,
1709 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1710 : &dotcnt);
1711 70 : if (dotcnt > 2)
1712 2 : pg_fatal("improper relation name (too many dotted names): %s",
1713 : cell->val);
1714 68 : else if (dotcnt == 2)
1715 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1716 64 : termPQExpBuffer(&dbbuf);
1717 :
1718 64 : if (with_child_tables)
1719 : {
1720 12 : appendPQExpBuffer(query, "UNION"
1721 : "\nSELECT i.inhrelid"
1722 : "\nFROM partition_tree p"
1723 : "\n JOIN pg_catalog.pg_inherits i"
1724 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1725 : "\n)"
1726 : "\nSELECT relid FROM partition_tree");
1727 : }
1728 :
1729 64 : ExecuteSqlStatement(fout, "RESET search_path");
1730 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1731 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1732 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1733 64 : if (strict_names && PQntuples(res) == 0)
1734 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1735 :
1736 148 : for (i = 0; i < PQntuples(res); i++)
1737 : {
1738 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1739 : }
1740 :
1741 60 : PQclear(res);
1742 60 : resetPQExpBuffer(query);
1743 : }
1744 :
1745 48 : destroyPQExpBuffer(query);
1746 : }
1747 :
1748 : /*
1749 : * Verifies that the connected database name matches the given database name,
1750 : * and if not, dies with an error about the given pattern.
1751 : *
1752 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1753 : */
1754 : static void
1755 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1756 : {
1757 : const char *db;
1758 :
1759 10 : db = PQdb(conn);
1760 10 : if (db == NULL)
1761 0 : pg_fatal("You are currently not connected to a database.");
1762 :
1763 10 : if (strcmp(db, dbname) != 0)
1764 10 : pg_fatal("cross-database references are not implemented: %s",
1765 : pattern);
1766 0 : }
1767 :
1768 : /*
1769 : * checkExtensionMembership
1770 : * Determine whether object is an extension member, and if so,
1771 : * record an appropriate dependency and set the object's dump flag.
1772 : *
1773 : * It's important to call this for each object that could be an extension
1774 : * member. Generally, we integrate this with determining the object's
1775 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1776 : *
1777 : * Returns true if object is an extension member, else false.
1778 : */
1779 : static bool
1780 959922 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1781 : {
1782 959922 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1783 :
1784 959922 : if (ext == NULL)
1785 958530 : return false;
1786 :
1787 1392 : dobj->ext_member = true;
1788 :
1789 : /* Record dependency so that getDependencies needn't deal with that */
1790 1392 : addObjectDependency(dobj, ext->dobj.dumpId);
1791 :
1792 : /*
1793 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1794 : * dumped. (Any initial ACLs will be removed later, using data from
1795 : * pg_init_privs, so that we'll dump only the delta from the extension's
1796 : * initial setup.)
1797 : *
1798 : * Prior to 9.6, we do not include any extension member components.
1799 : *
1800 : * In binary upgrades, we still dump all components of the members
1801 : * individually, since the idea is to exactly reproduce the database
1802 : * contents rather than replace the extension contents with something
1803 : * different.
1804 : *
1805 : * Note: it might be interesting someday to implement storage and delta
1806 : * dumping of extension members' RLS policies and/or security labels.
1807 : * However there is a pitfall for RLS policies: trying to dump them
1808 : * requires getting a lock on their tables, and the calling user might not
1809 : * have privileges for that. We need no lock to examine a table's ACLs,
1810 : * so the current feature doesn't have a problem of that sort.
1811 : */
1812 1392 : if (fout->dopt->binary_upgrade)
1813 152 : dobj->dump = ext->dobj.dump;
1814 : else
1815 : {
1816 1240 : if (fout->remoteVersion < 90600)
1817 0 : dobj->dump = DUMP_COMPONENT_NONE;
1818 : else
1819 1240 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1820 : }
1821 :
1822 1392 : return true;
1823 : }
1824 :
1825 : /*
1826 : * selectDumpableNamespace: policy-setting subroutine
1827 : * Mark a namespace as to be dumped or not
1828 : */
1829 : static void
1830 2510 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1831 : {
1832 : /*
1833 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1834 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1835 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1836 : */
1837 2510 : nsinfo->create = true;
1838 :
1839 : /*
1840 : * If specific tables are being dumped, do not dump any complete
1841 : * namespaces. If specific namespaces are being dumped, dump just those
1842 : * namespaces. Otherwise, dump all non-system namespaces.
1843 : */
1844 2510 : if (table_include_oids.head != NULL)
1845 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1846 2410 : else if (schema_include_oids.head != NULL)
1847 354 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1848 354 : simple_oid_list_member(&schema_include_oids,
1849 : nsinfo->dobj.catId.oid) ?
1850 354 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1851 2056 : else if (fout->remoteVersion >= 90600 &&
1852 2056 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1853 : {
1854 : /*
1855 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1856 : * they are interesting (and not the original ACLs which were set at
1857 : * initdb time, see pg_init_privs).
1858 : */
1859 266 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1860 : }
1861 1790 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1862 820 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1863 : {
1864 : /* Other system schemas don't get dumped */
1865 1236 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1866 : }
1867 554 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1868 : {
1869 : /*
1870 : * The public schema is a strange beast that sits in a sort of
1871 : * no-mans-land between being a system object and a user object.
1872 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1873 : * a comment and an indication of ownership. If the owner is the
1874 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1875 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1876 : */
1877 258 : nsinfo->create = false;
1878 258 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1879 258 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1880 178 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1881 258 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1882 :
1883 : /*
1884 : * Also, make like it has a comment even if it doesn't; this is so
1885 : * that we'll emit a command to drop the comment, if appropriate.
1886 : * (Without this, we'd not call dumpCommentExtended for it.)
1887 : */
1888 258 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1889 : }
1890 : else
1891 296 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1892 :
1893 : /*
1894 : * In any case, a namespace can be excluded by an exclusion switch
1895 : */
1896 3352 : if (nsinfo->dobj.dump_contains &&
1897 842 : simple_oid_list_member(&schema_exclude_oids,
1898 : nsinfo->dobj.catId.oid))
1899 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1900 :
1901 : /*
1902 : * If the schema belongs to an extension, allow extension membership to
1903 : * override the dump decision for the schema itself. However, this does
1904 : * not change dump_contains, so this won't change what we do with objects
1905 : * within the schema. (If they belong to the extension, they'll get
1906 : * suppressed by it, otherwise not.)
1907 : */
1908 2510 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1909 2510 : }
1910 :
1911 : /*
1912 : * selectDumpableTable: policy-setting subroutine
1913 : * Mark a table as to be dumped or not
1914 : */
1915 : static void
1916 81078 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1917 : {
1918 81078 : if (checkExtensionMembership(&tbinfo->dobj, fout))
1919 450 : return; /* extension membership overrides all else */
1920 :
1921 : /*
1922 : * If specific tables are being dumped, dump just those tables; else, dump
1923 : * according to the parent namespace's dump flag.
1924 : */
1925 80628 : if (table_include_oids.head != NULL)
1926 10104 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1927 : tbinfo->dobj.catId.oid) ?
1928 5052 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1929 : else
1930 75576 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1931 :
1932 : /*
1933 : * In any case, a table can be excluded by an exclusion switch
1934 : */
1935 130666 : if (tbinfo->dobj.dump &&
1936 50038 : simple_oid_list_member(&table_exclude_oids,
1937 : tbinfo->dobj.catId.oid))
1938 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1939 : }
1940 :
1941 : /*
1942 : * selectDumpableType: policy-setting subroutine
1943 : * Mark a type as to be dumped or not
1944 : *
1945 : * If it's a table's rowtype or an autogenerated array type, we also apply a
1946 : * special type code to facilitate sorting into the desired order. (We don't
1947 : * want to consider those to be ordinary types because that would bring tables
1948 : * up into the datatype part of the dump order.) We still set the object's
1949 : * dump flag; that's not going to cause the dummy type to be dumped, but we
1950 : * need it so that casts involving such types will be dumped correctly -- see
1951 : * dumpCast. This means the flag should be set the same as for the underlying
1952 : * object (the table or base type).
1953 : */
1954 : static void
1955 221940 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1956 : {
1957 : /* skip complex types, except for standalone composite types */
1958 221940 : if (OidIsValid(tyinfo->typrelid) &&
1959 79738 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1960 : {
1961 79378 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1962 :
1963 79378 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1964 79378 : if (tytable != NULL)
1965 79378 : tyinfo->dobj.dump = tytable->dobj.dump;
1966 : else
1967 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1968 79378 : return;
1969 : }
1970 :
1971 : /* skip auto-generated array and multirange types */
1972 142562 : if (tyinfo->isArray || tyinfo->isMultirange)
1973 : {
1974 108530 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1975 :
1976 : /*
1977 : * Fall through to set the dump flag; we assume that the subsequent
1978 : * rules will do the same thing as they would for the array's base
1979 : * type or multirange's range type. (We cannot reliably look up the
1980 : * base type here, since getTypes may not have processed it yet.)
1981 : */
1982 : }
1983 :
1984 142562 : if (checkExtensionMembership(&tyinfo->dobj, fout))
1985 300 : return; /* extension membership overrides all else */
1986 :
1987 : /* Dump based on if the contents of the namespace are being dumped */
1988 142262 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1989 : }
1990 :
1991 : /*
1992 : * selectDumpableDefaultACL: policy-setting subroutine
1993 : * Mark a default ACL as to be dumped or not
1994 : *
1995 : * For per-schema default ACLs, dump if the schema is to be dumped.
1996 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
1997 : * and aclsSkip are checked separately.
1998 : */
1999 : static void
2000 344 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2001 : {
2002 : /* Default ACLs can't be extension members */
2003 :
2004 344 : if (dinfo->dobj.namespace)
2005 : /* default ACLs are considered part of the namespace */
2006 172 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2007 : else
2008 172 : dinfo->dobj.dump = dopt->include_everything ?
2009 172 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2010 344 : }
2011 :
2012 : /*
2013 : * selectDumpableCast: policy-setting subroutine
2014 : * Mark a cast as to be dumped or not
2015 : *
2016 : * Casts do not belong to any particular namespace (since they haven't got
2017 : * names), nor do they have identifiable owners. To distinguish user-defined
2018 : * casts from built-in ones, we must resort to checking whether the cast's
2019 : * OID is in the range reserved for initdb.
2020 : */
2021 : static void
2022 68854 : selectDumpableCast(CastInfo *cast, Archive *fout)
2023 : {
2024 68854 : if (checkExtensionMembership(&cast->dobj, fout))
2025 0 : return; /* extension membership overrides all else */
2026 :
2027 : /*
2028 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2029 : * support ACLs currently.
2030 : */
2031 68854 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2032 68684 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2033 : else
2034 170 : cast->dobj.dump = fout->dopt->include_everything ?
2035 170 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2036 : }
2037 :
2038 : /*
2039 : * selectDumpableProcLang: policy-setting subroutine
2040 : * Mark a procedural language as to be dumped or not
2041 : *
2042 : * Procedural languages do not belong to any particular namespace. To
2043 : * identify built-in languages, we must resort to checking whether the
2044 : * language's OID is in the range reserved for initdb.
2045 : */
2046 : static void
2047 394 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2048 : {
2049 394 : if (checkExtensionMembership(&plang->dobj, fout))
2050 308 : return; /* extension membership overrides all else */
2051 :
2052 : /*
2053 : * Only include procedural languages when we are dumping everything.
2054 : *
2055 : * For from-initdb procedural languages, only include ACLs, as we do for
2056 : * the pg_catalog namespace. We need this because procedural languages do
2057 : * not live in any namespace.
2058 : */
2059 86 : if (!fout->dopt->include_everything)
2060 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2061 : else
2062 : {
2063 70 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2064 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2065 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2066 : else
2067 70 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2068 : }
2069 : }
2070 :
2071 : /*
2072 : * selectDumpableAccessMethod: policy-setting subroutine
2073 : * Mark an access method as to be dumped or not
2074 : *
2075 : * Access methods do not belong to any particular namespace. To identify
2076 : * built-in access methods, we must resort to checking whether the
2077 : * method's OID is in the range reserved for initdb.
2078 : */
2079 : static void
2080 2392 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2081 : {
2082 2392 : if (checkExtensionMembership(&method->dobj, fout))
2083 50 : return; /* extension membership overrides all else */
2084 :
2085 : /*
2086 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2087 : * they do not support ACLs currently.
2088 : */
2089 2342 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2090 2156 : method->dobj.dump = DUMP_COMPONENT_NONE;
2091 : else
2092 186 : method->dobj.dump = fout->dopt->include_everything ?
2093 186 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2094 : }
2095 :
2096 : /*
2097 : * selectDumpableExtension: policy-setting subroutine
2098 : * Mark an extension as to be dumped or not
2099 : *
2100 : * Built-in extensions should be skipped except for checking ACLs, since we
2101 : * assume those will already be installed in the target database. We identify
2102 : * such extensions by their having OIDs in the range reserved for initdb.
2103 : * We dump all user-added extensions by default. No extensions are dumped
2104 : * if include_everything is false (i.e., a --schema or --table switch was
2105 : * given), except if --extension specifies a list of extensions to dump.
2106 : */
2107 : static void
2108 360 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2109 : {
2110 : /*
2111 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2112 : * change permissions on their member objects, if they wish to, and have
2113 : * those changes preserved.
2114 : */
2115 360 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2116 310 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2117 : else
2118 : {
2119 : /* check if there is a list of extensions to dump */
2120 50 : if (extension_include_oids.head != NULL)
2121 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2122 8 : simple_oid_list_member(&extension_include_oids,
2123 : extinfo->dobj.catId.oid) ?
2124 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2125 : else
2126 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2127 42 : dopt->include_everything ?
2128 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2129 :
2130 : /* check that the extension is not explicitly excluded */
2131 92 : if (extinfo->dobj.dump &&
2132 42 : simple_oid_list_member(&extension_exclude_oids,
2133 : extinfo->dobj.catId.oid))
2134 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2135 : }
2136 360 : }
2137 :
2138 : /*
2139 : * selectDumpablePublicationObject: policy-setting subroutine
2140 : * Mark a publication object as to be dumped or not
2141 : *
2142 : * A publication can have schemas and tables which have schemas, but those are
2143 : * ignored in decision making, because publications are only dumped when we are
2144 : * dumping everything.
2145 : */
2146 : static void
2147 652 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2148 : {
2149 652 : if (checkExtensionMembership(dobj, fout))
2150 0 : return; /* extension membership overrides all else */
2151 :
2152 652 : dobj->dump = fout->dopt->include_everything ?
2153 652 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2154 : }
2155 :
2156 : /*
2157 : * selectDumpableStatisticsObject: policy-setting subroutine
2158 : * Mark an extended statistics object as to be dumped or not
2159 : *
2160 : * We dump an extended statistics object if the schema it's in and the table
2161 : * it's for are being dumped. (This'll need more thought if statistics
2162 : * objects ever support cross-table stats.)
2163 : */
2164 : static void
2165 314 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2166 : {
2167 314 : if (checkExtensionMembership(&sobj->dobj, fout))
2168 0 : return; /* extension membership overrides all else */
2169 :
2170 314 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2171 314 : if (sobj->stattable == NULL ||
2172 314 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2173 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2174 : }
2175 :
2176 : /*
2177 : * selectDumpableObject: policy-setting subroutine
2178 : * Mark a generic dumpable object as to be dumped or not
2179 : *
2180 : * Use this only for object types without a special-case routine above.
2181 : */
2182 : static void
2183 661166 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2184 : {
2185 661166 : if (checkExtensionMembership(dobj, fout))
2186 234 : return; /* extension membership overrides all else */
2187 :
2188 : /*
2189 : * Default policy is to dump if parent namespace is dumpable, or for
2190 : * non-namespace-associated items, dump if we're dumping "everything".
2191 : */
2192 660932 : if (dobj->namespace)
2193 659762 : dobj->dump = dobj->namespace->dobj.dump_contains;
2194 : else
2195 1170 : dobj->dump = fout->dopt->include_everything ?
2196 1170 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2197 : }
2198 :
2199 : /*
2200 : * Dump a table's contents for loading using the COPY command
2201 : * - this routine is called by the Archiver when it wants the table
2202 : * to be dumped.
2203 : */
2204 : static int
2205 7026 : dumpTableData_copy(Archive *fout, const void *dcontext)
2206 : {
2207 7026 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2208 7026 : TableInfo *tbinfo = tdinfo->tdtable;
2209 7026 : const char *classname = tbinfo->dobj.name;
2210 7026 : PQExpBuffer q = createPQExpBuffer();
2211 :
2212 : /*
2213 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2214 : * which uses it already.
2215 : */
2216 7026 : PQExpBuffer clistBuf = createPQExpBuffer();
2217 7026 : PGconn *conn = GetConnection(fout);
2218 : PGresult *res;
2219 : int ret;
2220 : char *copybuf;
2221 : const char *column_list;
2222 :
2223 7026 : pg_log_info("dumping contents of table \"%s.%s\"",
2224 : tbinfo->dobj.namespace->dobj.name, classname);
2225 :
2226 : /*
2227 : * Specify the column list explicitly so that we have no possibility of
2228 : * retrieving data in the wrong column order. (The default column
2229 : * ordering of COPY will not be what we want in certain corner cases
2230 : * involving ADD COLUMN and inheritance.)
2231 : */
2232 7026 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2233 :
2234 : /*
2235 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2236 : * a filter condition was specified. For other cases a simple COPY
2237 : * suffices.
2238 : */
2239 7026 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2240 : {
2241 : /* Temporary allows to access to foreign tables to dump data */
2242 2 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2243 2 : set_restrict_relation_kind(fout, "view");
2244 :
2245 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2246 : /* klugery to get rid of parens in column list */
2247 2 : if (strlen(column_list) > 2)
2248 : {
2249 2 : appendPQExpBufferStr(q, column_list + 1);
2250 2 : q->data[q->len - 1] = ' ';
2251 : }
2252 : else
2253 0 : appendPQExpBufferStr(q, "* ");
2254 :
2255 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2256 2 : fmtQualifiedDumpable(tbinfo),
2257 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2258 : }
2259 : else
2260 : {
2261 7024 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2262 7024 : fmtQualifiedDumpable(tbinfo),
2263 : column_list);
2264 : }
2265 7026 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2266 7024 : PQclear(res);
2267 7024 : destroyPQExpBuffer(clistBuf);
2268 :
2269 : for (;;)
2270 : {
2271 3602012 : ret = PQgetCopyData(conn, ©buf, 0);
2272 :
2273 3602012 : if (ret < 0)
2274 7024 : break; /* done or error */
2275 :
2276 3594988 : if (copybuf)
2277 : {
2278 3594988 : WriteData(fout, copybuf, ret);
2279 3594988 : PQfreemem(copybuf);
2280 : }
2281 :
2282 : /* ----------
2283 : * THROTTLE:
2284 : *
2285 : * There was considerable discussion in late July, 2000 regarding
2286 : * slowing down pg_dump when backing up large tables. Users with both
2287 : * slow & fast (multi-processor) machines experienced performance
2288 : * degradation when doing a backup.
2289 : *
2290 : * Initial attempts based on sleeping for a number of ms for each ms
2291 : * of work were deemed too complex, then a simple 'sleep in each loop'
2292 : * implementation was suggested. The latter failed because the loop
2293 : * was too tight. Finally, the following was implemented:
2294 : *
2295 : * If throttle is non-zero, then
2296 : * See how long since the last sleep.
2297 : * Work out how long to sleep (based on ratio).
2298 : * If sleep is more than 100ms, then
2299 : * sleep
2300 : * reset timer
2301 : * EndIf
2302 : * EndIf
2303 : *
2304 : * where the throttle value was the number of ms to sleep per ms of
2305 : * work. The calculation was done in each loop.
2306 : *
2307 : * Most of the hard work is done in the backend, and this solution
2308 : * still did not work particularly well: on slow machines, the ratio
2309 : * was 50:1, and on medium paced machines, 1:1, and on fast
2310 : * multi-processor machines, it had little or no effect, for reasons
2311 : * that were unclear.
2312 : *
2313 : * Further discussion ensued, and the proposal was dropped.
2314 : *
2315 : * For those people who want this feature, it can be implemented using
2316 : * gettimeofday in each loop, calculating the time since last sleep,
2317 : * multiplying that by the sleep ratio, then if the result is more
2318 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2319 : * function to sleep for a subsecond period ie.
2320 : *
2321 : * select(0, NULL, NULL, NULL, &tvi);
2322 : *
2323 : * This will return after the interval specified in the structure tvi.
2324 : * Finally, call gettimeofday again to save the 'last sleep time'.
2325 : * ----------
2326 : */
2327 : }
2328 7024 : archprintf(fout, "\\.\n\n\n");
2329 :
2330 7024 : if (ret == -2)
2331 : {
2332 : /* copy data transfer failed */
2333 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2334 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2335 0 : pg_log_error_detail("Command was: %s", q->data);
2336 0 : exit_nicely(1);
2337 : }
2338 :
2339 : /* Check command status and return to normal libpq state */
2340 7024 : res = PQgetResult(conn);
2341 7024 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2342 : {
2343 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2344 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2345 0 : pg_log_error_detail("Command was: %s", q->data);
2346 0 : exit_nicely(1);
2347 : }
2348 7024 : PQclear(res);
2349 :
2350 : /* Do this to ensure we've pumped libpq back to idle state */
2351 7024 : if (PQgetResult(conn) != NULL)
2352 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2353 : classname);
2354 :
2355 7024 : destroyPQExpBuffer(q);
2356 :
2357 : /* Revert back the setting */
2358 7024 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2359 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2360 :
2361 7024 : return 1;
2362 : }
2363 :
2364 : /*
2365 : * Dump table data using INSERT commands.
2366 : *
2367 : * Caution: when we restore from an archive file direct to database, the
2368 : * INSERT commands emitted by this function have to be parsed by
2369 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2370 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2371 : */
2372 : static int
2373 138 : dumpTableData_insert(Archive *fout, const void *dcontext)
2374 : {
2375 138 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2376 138 : TableInfo *tbinfo = tdinfo->tdtable;
2377 138 : DumpOptions *dopt = fout->dopt;
2378 138 : PQExpBuffer q = createPQExpBuffer();
2379 138 : PQExpBuffer insertStmt = NULL;
2380 : char *attgenerated;
2381 : PGresult *res;
2382 : int nfields,
2383 : i;
2384 138 : int rows_per_statement = dopt->dump_inserts;
2385 138 : int rows_this_statement = 0;
2386 :
2387 : /* Temporary allows to access to foreign tables to dump data */
2388 138 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2389 0 : set_restrict_relation_kind(fout, "view");
2390 :
2391 : /*
2392 : * If we're going to emit INSERTs with column names, the most efficient
2393 : * way to deal with generated columns is to exclude them entirely. For
2394 : * INSERTs without column names, we have to emit DEFAULT rather than the
2395 : * actual column value --- but we can save a few cycles by fetching nulls
2396 : * rather than the uninteresting-to-us value.
2397 : */
2398 138 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2399 138 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2400 138 : nfields = 0;
2401 442 : for (i = 0; i < tbinfo->numatts; i++)
2402 : {
2403 304 : if (tbinfo->attisdropped[i])
2404 4 : continue;
2405 300 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2406 10 : continue;
2407 290 : if (nfields > 0)
2408 166 : appendPQExpBufferStr(q, ", ");
2409 290 : if (tbinfo->attgenerated[i])
2410 10 : appendPQExpBufferStr(q, "NULL");
2411 : else
2412 280 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2413 290 : attgenerated[nfields] = tbinfo->attgenerated[i];
2414 290 : nfields++;
2415 : }
2416 : /* Servers before 9.4 will complain about zero-column SELECT */
2417 138 : if (nfields == 0)
2418 14 : appendPQExpBufferStr(q, "NULL");
2419 138 : appendPQExpBuffer(q, " FROM ONLY %s",
2420 138 : fmtQualifiedDumpable(tbinfo));
2421 138 : if (tdinfo->filtercond)
2422 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2423 :
2424 138 : ExecuteSqlStatement(fout, q->data);
2425 :
2426 : while (1)
2427 : {
2428 238 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2429 : PGRES_TUPLES_OK);
2430 :
2431 : /* cross-check field count, allowing for dummy NULL if any */
2432 238 : if (nfields != PQnfields(res) &&
2433 20 : !(nfields == 0 && PQnfields(res) == 1))
2434 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2435 : tbinfo->dobj.name);
2436 :
2437 : /*
2438 : * First time through, we build as much of the INSERT statement as
2439 : * possible in "insertStmt", which we can then just print for each
2440 : * statement. If the table happens to have zero dumpable columns then
2441 : * this will be a complete statement, otherwise it will end in
2442 : * "VALUES" and be ready to have the row's column values printed.
2443 : */
2444 238 : if (insertStmt == NULL)
2445 : {
2446 : TableInfo *targettab;
2447 :
2448 138 : insertStmt = createPQExpBuffer();
2449 :
2450 : /*
2451 : * When load-via-partition-root is set or forced, get the root
2452 : * table name for the partition table, so that we can reload data
2453 : * through the root table.
2454 : */
2455 138 : if (tbinfo->ispartition &&
2456 80 : (dopt->load_via_partition_root ||
2457 40 : forcePartitionRootLoad(tbinfo)))
2458 6 : targettab = getRootTableInfo(tbinfo);
2459 : else
2460 132 : targettab = tbinfo;
2461 :
2462 138 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2463 138 : fmtQualifiedDumpable(targettab));
2464 :
2465 : /* corner case for zero-column table */
2466 138 : if (nfields == 0)
2467 : {
2468 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2469 : }
2470 : else
2471 : {
2472 : /* append the list of column names if required */
2473 124 : if (dopt->column_inserts)
2474 : {
2475 54 : appendPQExpBufferChar(insertStmt, '(');
2476 176 : for (int field = 0; field < nfields; field++)
2477 : {
2478 122 : if (field > 0)
2479 68 : appendPQExpBufferStr(insertStmt, ", ");
2480 122 : appendPQExpBufferStr(insertStmt,
2481 122 : fmtId(PQfname(res, field)));
2482 : }
2483 54 : appendPQExpBufferStr(insertStmt, ") ");
2484 : }
2485 :
2486 124 : if (tbinfo->needs_override)
2487 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2488 :
2489 124 : appendPQExpBufferStr(insertStmt, "VALUES");
2490 : }
2491 : }
2492 :
2493 6380 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2494 : {
2495 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2496 6142 : if (rows_this_statement == 0)
2497 6130 : archputs(insertStmt->data, fout);
2498 :
2499 : /*
2500 : * If it is zero-column table then we've already written the
2501 : * complete statement, which will mean we've disobeyed
2502 : * --rows-per-insert when it's set greater than 1. We do support
2503 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2504 : * UNION ALL ... but that's non-standard so we should avoid it
2505 : * given that using INSERTs is mostly only ever needed for
2506 : * cross-database exports.
2507 : */
2508 6142 : if (nfields == 0)
2509 12 : continue;
2510 :
2511 : /* Emit a row heading */
2512 6130 : if (rows_per_statement == 1)
2513 6112 : archputs(" (", fout);
2514 18 : else if (rows_this_statement > 0)
2515 12 : archputs(",\n\t(", fout);
2516 : else
2517 6 : archputs("\n\t(", fout);
2518 :
2519 18498 : for (int field = 0; field < nfields; field++)
2520 : {
2521 12368 : if (field > 0)
2522 6238 : archputs(", ", fout);
2523 12368 : if (attgenerated[field])
2524 : {
2525 4 : archputs("DEFAULT", fout);
2526 4 : continue;
2527 : }
2528 12364 : if (PQgetisnull(res, tuple, field))
2529 : {
2530 166 : archputs("NULL", fout);
2531 166 : continue;
2532 : }
2533 :
2534 : /* XXX This code is partially duplicated in ruleutils.c */
2535 12198 : switch (PQftype(res, field))
2536 : {
2537 8138 : case INT2OID:
2538 : case INT4OID:
2539 : case INT8OID:
2540 : case OIDOID:
2541 : case FLOAT4OID:
2542 : case FLOAT8OID:
2543 : case NUMERICOID:
2544 : {
2545 : /*
2546 : * These types are printed without quotes unless
2547 : * they contain values that aren't accepted by the
2548 : * scanner unquoted (e.g., 'NaN'). Note that
2549 : * strtod() and friends might accept NaN, so we
2550 : * can't use that to test.
2551 : *
2552 : * In reality we only need to defend against
2553 : * infinity and NaN, so we need not get too crazy
2554 : * about pattern matching here.
2555 : */
2556 8138 : const char *s = PQgetvalue(res, tuple, field);
2557 :
2558 8138 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2559 8134 : archputs(s, fout);
2560 : else
2561 4 : archprintf(fout, "'%s'", s);
2562 : }
2563 8138 : break;
2564 :
2565 4 : case BITOID:
2566 : case VARBITOID:
2567 4 : archprintf(fout, "B'%s'",
2568 : PQgetvalue(res, tuple, field));
2569 4 : break;
2570 :
2571 8 : case BOOLOID:
2572 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2573 4 : archputs("true", fout);
2574 : else
2575 4 : archputs("false", fout);
2576 8 : break;
2577 :
2578 4048 : default:
2579 : /* All other types are printed as string literals. */
2580 4048 : resetPQExpBuffer(q);
2581 4048 : appendStringLiteralAH(q,
2582 : PQgetvalue(res, tuple, field),
2583 : fout);
2584 4048 : archputs(q->data, fout);
2585 4048 : break;
2586 : }
2587 : }
2588 :
2589 : /* Terminate the row ... */
2590 6130 : archputs(")", fout);
2591 :
2592 : /* ... and the statement, if the target no. of rows is reached */
2593 6130 : if (++rows_this_statement >= rows_per_statement)
2594 : {
2595 6116 : if (dopt->do_nothing)
2596 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2597 : else
2598 6116 : archputs(";\n", fout);
2599 : /* Reset the row counter */
2600 6116 : rows_this_statement = 0;
2601 : }
2602 : }
2603 :
2604 238 : if (PQntuples(res) <= 0)
2605 : {
2606 138 : PQclear(res);
2607 138 : break;
2608 : }
2609 100 : PQclear(res);
2610 : }
2611 :
2612 : /* Terminate any statements that didn't make the row count. */
2613 138 : if (rows_this_statement > 0)
2614 : {
2615 2 : if (dopt->do_nothing)
2616 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2617 : else
2618 2 : archputs(";\n", fout);
2619 : }
2620 :
2621 138 : archputs("\n\n", fout);
2622 :
2623 138 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2624 :
2625 138 : destroyPQExpBuffer(q);
2626 138 : if (insertStmt != NULL)
2627 138 : destroyPQExpBuffer(insertStmt);
2628 138 : free(attgenerated);
2629 :
2630 : /* Revert back the setting */
2631 138 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2632 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2633 :
2634 138 : return 1;
2635 : }
2636 :
2637 : /*
2638 : * getRootTableInfo:
2639 : * get the root TableInfo for the given partition table.
2640 : */
2641 : static TableInfo *
2642 18 : getRootTableInfo(const TableInfo *tbinfo)
2643 : {
2644 : TableInfo *parentTbinfo;
2645 :
2646 : Assert(tbinfo->ispartition);
2647 : Assert(tbinfo->numParents == 1);
2648 :
2649 18 : parentTbinfo = tbinfo->parents[0];
2650 18 : while (parentTbinfo->ispartition)
2651 : {
2652 : Assert(parentTbinfo->numParents == 1);
2653 0 : parentTbinfo = parentTbinfo->parents[0];
2654 : }
2655 :
2656 18 : return parentTbinfo;
2657 : }
2658 :
2659 : /*
2660 : * forcePartitionRootLoad
2661 : * Check if we must force load_via_partition_root for this partition.
2662 : *
2663 : * This is required if any level of ancestral partitioned table has an
2664 : * unsafe partitioning scheme.
2665 : */
2666 : static bool
2667 1884 : forcePartitionRootLoad(const TableInfo *tbinfo)
2668 : {
2669 : TableInfo *parentTbinfo;
2670 :
2671 : Assert(tbinfo->ispartition);
2672 : Assert(tbinfo->numParents == 1);
2673 :
2674 1884 : parentTbinfo = tbinfo->parents[0];
2675 1884 : if (parentTbinfo->unsafe_partitions)
2676 18 : return true;
2677 2298 : while (parentTbinfo->ispartition)
2678 : {
2679 : Assert(parentTbinfo->numParents == 1);
2680 432 : parentTbinfo = parentTbinfo->parents[0];
2681 432 : if (parentTbinfo->unsafe_partitions)
2682 0 : return true;
2683 : }
2684 :
2685 1866 : return false;
2686 : }
2687 :
2688 : /*
2689 : * dumpTableData -
2690 : * dump the contents of a single table
2691 : *
2692 : * Actually, this just makes an ArchiveEntry for the table contents.
2693 : */
2694 : static void
2695 7300 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2696 : {
2697 7300 : DumpOptions *dopt = fout->dopt;
2698 7300 : TableInfo *tbinfo = tdinfo->tdtable;
2699 7300 : PQExpBuffer copyBuf = createPQExpBuffer();
2700 7300 : PQExpBuffer clistBuf = createPQExpBuffer();
2701 : DataDumperPtr dumpFn;
2702 7300 : char *tdDefn = NULL;
2703 : char *copyStmt;
2704 : const char *copyFrom;
2705 :
2706 : /* We had better have loaded per-column details about this table */
2707 : Assert(tbinfo->interesting);
2708 :
2709 : /*
2710 : * When load-via-partition-root is set or forced, get the root table name
2711 : * for the partition table, so that we can reload data through the root
2712 : * table. Then construct a comment to be inserted into the TOC entry's
2713 : * defn field, so that such cases can be identified reliably.
2714 : */
2715 7300 : if (tbinfo->ispartition &&
2716 3688 : (dopt->load_via_partition_root ||
2717 1844 : forcePartitionRootLoad(tbinfo)))
2718 12 : {
2719 : TableInfo *parentTbinfo;
2720 :
2721 12 : parentTbinfo = getRootTableInfo(tbinfo);
2722 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2723 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2724 : copyFrom);
2725 12 : tdDefn = pg_strdup(copyBuf->data);
2726 : }
2727 : else
2728 7288 : copyFrom = fmtQualifiedDumpable(tbinfo);
2729 :
2730 7300 : if (dopt->dump_inserts == 0)
2731 : {
2732 : /* Dump/restore using COPY */
2733 7162 : dumpFn = dumpTableData_copy;
2734 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2735 7162 : printfPQExpBuffer(copyBuf, "COPY %s ",
2736 : copyFrom);
2737 7162 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2738 : fmtCopyColumnList(tbinfo, clistBuf));
2739 7162 : copyStmt = copyBuf->data;
2740 : }
2741 : else
2742 : {
2743 : /* Restore using INSERT */
2744 138 : dumpFn = dumpTableData_insert;
2745 138 : copyStmt = NULL;
2746 : }
2747 :
2748 : /*
2749 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2750 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2751 : * See comments for BuildArchiveDependencies.
2752 : */
2753 7300 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2754 : {
2755 : TocEntry *te;
2756 :
2757 7300 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2758 7300 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2759 : .namespace = tbinfo->dobj.namespace->dobj.name,
2760 : .owner = tbinfo->rolname,
2761 : .description = "TABLE DATA",
2762 : .section = SECTION_DATA,
2763 : .createStmt = tdDefn,
2764 : .copyStmt = copyStmt,
2765 : .deps = &(tbinfo->dobj.dumpId),
2766 : .nDeps = 1,
2767 : .dumpFn = dumpFn,
2768 : .dumpArg = tdinfo));
2769 :
2770 : /*
2771 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2772 : * and want to order dump jobs by table size. We choose to measure
2773 : * dataLength in table pages (including TOAST pages) during dump, so
2774 : * no scaling is needed.
2775 : *
2776 : * However, relpages is declared as "integer" in pg_class, and hence
2777 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2778 : * Cast so that we get the right interpretation of table sizes
2779 : * exceeding INT_MAX pages.
2780 : */
2781 7300 : te->dataLength = (BlockNumber) tbinfo->relpages;
2782 7300 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2783 :
2784 : /*
2785 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2786 : * and instead we'd better worry about integer overflow. Clamp to
2787 : * INT_MAX if the correct result exceeds that.
2788 : */
2789 : if (sizeof(te->dataLength) == 4 &&
2790 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2791 : te->dataLength < 0))
2792 : te->dataLength = INT_MAX;
2793 : }
2794 :
2795 7300 : destroyPQExpBuffer(copyBuf);
2796 7300 : destroyPQExpBuffer(clistBuf);
2797 7300 : }
2798 :
2799 : /*
2800 : * refreshMatViewData -
2801 : * load or refresh the contents of a single materialized view
2802 : *
2803 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2804 : * statement.
2805 : */
2806 : static void
2807 676 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2808 : {
2809 676 : TableInfo *tbinfo = tdinfo->tdtable;
2810 : PQExpBuffer q;
2811 :
2812 : /* If the materialized view is not flagged as populated, skip this. */
2813 676 : if (!tbinfo->relispopulated)
2814 136 : return;
2815 :
2816 540 : q = createPQExpBuffer();
2817 :
2818 540 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2819 540 : fmtQualifiedDumpable(tbinfo));
2820 :
2821 540 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2822 540 : ArchiveEntry(fout,
2823 : tdinfo->dobj.catId, /* catalog ID */
2824 : tdinfo->dobj.dumpId, /* dump ID */
2825 540 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2826 : .namespace = tbinfo->dobj.namespace->dobj.name,
2827 : .owner = tbinfo->rolname,
2828 : .description = "MATERIALIZED VIEW DATA",
2829 : .section = SECTION_POST_DATA,
2830 : .createStmt = q->data,
2831 : .deps = tdinfo->dobj.dependencies,
2832 : .nDeps = tdinfo->dobj.nDeps));
2833 :
2834 540 : destroyPQExpBuffer(q);
2835 : }
2836 :
2837 : /*
2838 : * getTableData -
2839 : * set up dumpable objects representing the contents of tables
2840 : */
2841 : static void
2842 304 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2843 : {
2844 : int i;
2845 :
2846 80262 : for (i = 0; i < numTables; i++)
2847 : {
2848 79958 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2849 1636 : (!relkind || tblinfo[i].relkind == relkind))
2850 10550 : makeTableDataInfo(dopt, &(tblinfo[i]));
2851 : }
2852 304 : }
2853 :
2854 : /*
2855 : * Make a dumpable object for the data of this specific table
2856 : *
2857 : * Note: we make a TableDataInfo if and only if we are going to dump the
2858 : * table data; the "dump" field in such objects isn't very interesting.
2859 : */
2860 : static void
2861 10628 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2862 : {
2863 : TableDataInfo *tdinfo;
2864 :
2865 : /*
2866 : * Nothing to do if we already decided to dump the table. This will
2867 : * happen for "config" tables.
2868 : */
2869 10628 : if (tbinfo->dataObj != NULL)
2870 2 : return;
2871 :
2872 : /* Skip VIEWs (no data to dump) */
2873 10626 : if (tbinfo->relkind == RELKIND_VIEW)
2874 920 : return;
2875 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2876 9706 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2877 76 : (foreign_servers_include_oids.head == NULL ||
2878 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2879 : tbinfo->foreign_server)))
2880 74 : return;
2881 : /* Skip partitioned tables (data in partitions) */
2882 9632 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2883 876 : return;
2884 :
2885 : /* Don't dump data in unlogged tables, if so requested */
2886 8756 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2887 82 : dopt->no_unlogged_table_data)
2888 36 : return;
2889 :
2890 : /* Check that the data is not explicitly excluded */
2891 8720 : if (simple_oid_list_member(&tabledata_exclude_oids,
2892 : tbinfo->dobj.catId.oid))
2893 16 : return;
2894 :
2895 : /* OK, let's dump it */
2896 8704 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2897 :
2898 8704 : if (tbinfo->relkind == RELKIND_MATVIEW)
2899 676 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2900 8028 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2901 728 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2902 : else
2903 7300 : tdinfo->dobj.objType = DO_TABLE_DATA;
2904 :
2905 : /*
2906 : * Note: use tableoid 0 so that this object won't be mistaken for
2907 : * something that pg_depend entries apply to.
2908 : */
2909 8704 : tdinfo->dobj.catId.tableoid = 0;
2910 8704 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2911 8704 : AssignDumpId(&tdinfo->dobj);
2912 8704 : tdinfo->dobj.name = tbinfo->dobj.name;
2913 8704 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2914 8704 : tdinfo->tdtable = tbinfo;
2915 8704 : tdinfo->filtercond = NULL; /* might get set later */
2916 8704 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2917 :
2918 : /* A TableDataInfo contains data, of course */
2919 8704 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2920 :
2921 8704 : tbinfo->dataObj = tdinfo;
2922 :
2923 : /* Make sure that we'll collect per-column info for this table. */
2924 8704 : tbinfo->interesting = true;
2925 : }
2926 :
2927 : /*
2928 : * The refresh for a materialized view must be dependent on the refresh for
2929 : * any materialized view that this one is dependent on.
2930 : *
2931 : * This must be called after all the objects are created, but before they are
2932 : * sorted.
2933 : */
2934 : static void
2935 276 : buildMatViewRefreshDependencies(Archive *fout)
2936 : {
2937 : PQExpBuffer query;
2938 : PGresult *res;
2939 : int ntups,
2940 : i;
2941 : int i_classid,
2942 : i_objid,
2943 : i_refobjid;
2944 :
2945 : /* No Mat Views before 9.3. */
2946 276 : if (fout->remoteVersion < 90300)
2947 0 : return;
2948 :
2949 276 : query = createPQExpBuffer();
2950 :
2951 276 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2952 : "( "
2953 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2954 : "FROM pg_depend d1 "
2955 : "JOIN pg_class c1 ON c1.oid = d1.objid "
2956 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2957 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2958 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2959 : "AND d2.objid = r1.oid "
2960 : "AND d2.refobjid <> d1.objid "
2961 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2962 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2963 : CppAsString2(RELKIND_VIEW) ") "
2964 : "WHERE d1.classid = 'pg_class'::regclass "
2965 : "UNION "
2966 : "SELECT w.objid, d3.refobjid, c3.relkind "
2967 : "FROM w "
2968 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2969 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2970 : "AND d3.objid = r3.oid "
2971 : "AND d3.refobjid <> w.refobjid "
2972 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2973 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2974 : CppAsString2(RELKIND_VIEW) ") "
2975 : ") "
2976 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2977 : "FROM w "
2978 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2979 :
2980 276 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2981 :
2982 276 : ntups = PQntuples(res);
2983 :
2984 276 : i_classid = PQfnumber(res, "classid");
2985 276 : i_objid = PQfnumber(res, "objid");
2986 276 : i_refobjid = PQfnumber(res, "refobjid");
2987 :
2988 804 : for (i = 0; i < ntups; i++)
2989 : {
2990 : CatalogId objId;
2991 : CatalogId refobjId;
2992 : DumpableObject *dobj;
2993 : DumpableObject *refdobj;
2994 : TableInfo *tbinfo;
2995 : TableInfo *reftbinfo;
2996 :
2997 528 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2998 528 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
2999 528 : refobjId.tableoid = objId.tableoid;
3000 528 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3001 :
3002 528 : dobj = findObjectByCatalogId(objId);
3003 528 : if (dobj == NULL)
3004 96 : continue;
3005 :
3006 : Assert(dobj->objType == DO_TABLE);
3007 528 : tbinfo = (TableInfo *) dobj;
3008 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3009 528 : dobj = (DumpableObject *) tbinfo->dataObj;
3010 528 : if (dobj == NULL)
3011 96 : continue;
3012 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3013 :
3014 432 : refdobj = findObjectByCatalogId(refobjId);
3015 432 : if (refdobj == NULL)
3016 0 : continue;
3017 :
3018 : Assert(refdobj->objType == DO_TABLE);
3019 432 : reftbinfo = (TableInfo *) refdobj;
3020 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3021 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3022 432 : if (refdobj == NULL)
3023 0 : continue;
3024 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3025 :
3026 432 : addObjectDependency(dobj, refdobj->dumpId);
3027 :
3028 432 : if (!reftbinfo->relispopulated)
3029 68 : tbinfo->relispopulated = false;
3030 : }
3031 :
3032 276 : PQclear(res);
3033 :
3034 276 : destroyPQExpBuffer(query);
3035 : }
3036 :
3037 : /*
3038 : * getTableDataFKConstraints -
3039 : * add dump-order dependencies reflecting foreign key constraints
3040 : *
3041 : * This code is executed only in a data-only dump --- in schema+data dumps
3042 : * we handle foreign key issues by not creating the FK constraints until
3043 : * after the data is loaded. In a data-only dump, however, we want to
3044 : * order the table data objects in such a way that a table's referenced
3045 : * tables are restored first. (In the presence of circular references or
3046 : * self-references this may be impossible; we'll detect and complain about
3047 : * that during the dependency sorting step.)
3048 : */
3049 : static void
3050 12 : getTableDataFKConstraints(void)
3051 : {
3052 : DumpableObject **dobjs;
3053 : int numObjs;
3054 : int i;
3055 :
3056 : /* Search through all the dumpable objects for FK constraints */
3057 12 : getDumpableObjects(&dobjs, &numObjs);
3058 42790 : for (i = 0; i < numObjs; i++)
3059 : {
3060 42778 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3061 : {
3062 12 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3063 : TableInfo *ftable;
3064 :
3065 : /* Not interesting unless both tables are to be dumped */
3066 12 : if (cinfo->contable == NULL ||
3067 12 : cinfo->contable->dataObj == NULL)
3068 6 : continue;
3069 6 : ftable = findTableByOid(cinfo->confrelid);
3070 6 : if (ftable == NULL ||
3071 6 : ftable->dataObj == NULL)
3072 0 : continue;
3073 :
3074 : /*
3075 : * Okay, make referencing table's TABLE_DATA object depend on the
3076 : * referenced table's TABLE_DATA object.
3077 : */
3078 6 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3079 6 : ftable->dataObj->dobj.dumpId);
3080 : }
3081 : }
3082 12 : free(dobjs);
3083 12 : }
3084 :
3085 :
3086 : /*
3087 : * dumpDatabase:
3088 : * dump the database definition
3089 : */
3090 : static void
3091 120 : dumpDatabase(Archive *fout)
3092 : {
3093 120 : DumpOptions *dopt = fout->dopt;
3094 120 : PQExpBuffer dbQry = createPQExpBuffer();
3095 120 : PQExpBuffer delQry = createPQExpBuffer();
3096 120 : PQExpBuffer creaQry = createPQExpBuffer();
3097 120 : PQExpBuffer labelq = createPQExpBuffer();
3098 120 : PGconn *conn = GetConnection(fout);
3099 : PGresult *res;
3100 : int i_tableoid,
3101 : i_oid,
3102 : i_datname,
3103 : i_datdba,
3104 : i_encoding,
3105 : i_datlocprovider,
3106 : i_collate,
3107 : i_ctype,
3108 : i_datlocale,
3109 : i_daticurules,
3110 : i_frozenxid,
3111 : i_minmxid,
3112 : i_datacl,
3113 : i_acldefault,
3114 : i_datistemplate,
3115 : i_datconnlimit,
3116 : i_datcollversion,
3117 : i_tablespace;
3118 : CatalogId dbCatId;
3119 : DumpId dbDumpId;
3120 : DumpableAcl dbdacl;
3121 : const char *datname,
3122 : *dba,
3123 : *encoding,
3124 : *datlocprovider,
3125 : *collate,
3126 : *ctype,
3127 : *locale,
3128 : *icurules,
3129 : *datistemplate,
3130 : *datconnlimit,
3131 : *tablespace;
3132 : uint32 frozenxid,
3133 : minmxid;
3134 : char *qdatname;
3135 :
3136 120 : pg_log_info("saving database definition");
3137 :
3138 : /*
3139 : * Fetch the database-level properties for this database.
3140 : */
3141 120 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3142 : "datdba, "
3143 : "pg_encoding_to_char(encoding) AS encoding, "
3144 : "datcollate, datctype, datfrozenxid, "
3145 : "datacl, acldefault('d', datdba) AS acldefault, "
3146 : "datistemplate, datconnlimit, ");
3147 120 : if (fout->remoteVersion >= 90300)
3148 120 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3149 : else
3150 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3151 120 : if (fout->remoteVersion >= 170000)
3152 120 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3153 0 : else if (fout->remoteVersion >= 150000)
3154 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3155 : else
3156 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3157 120 : if (fout->remoteVersion >= 160000)
3158 120 : appendPQExpBufferStr(dbQry, "daticurules, ");
3159 : else
3160 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3161 120 : appendPQExpBufferStr(dbQry,
3162 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3163 : "shobj_description(oid, 'pg_database') AS description "
3164 : "FROM pg_database "
3165 : "WHERE datname = current_database()");
3166 :
3167 120 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3168 :
3169 120 : i_tableoid = PQfnumber(res, "tableoid");
3170 120 : i_oid = PQfnumber(res, "oid");
3171 120 : i_datname = PQfnumber(res, "datname");
3172 120 : i_datdba = PQfnumber(res, "datdba");
3173 120 : i_encoding = PQfnumber(res, "encoding");
3174 120 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3175 120 : i_collate = PQfnumber(res, "datcollate");
3176 120 : i_ctype = PQfnumber(res, "datctype");
3177 120 : i_datlocale = PQfnumber(res, "datlocale");
3178 120 : i_daticurules = PQfnumber(res, "daticurules");
3179 120 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3180 120 : i_minmxid = PQfnumber(res, "datminmxid");
3181 120 : i_datacl = PQfnumber(res, "datacl");
3182 120 : i_acldefault = PQfnumber(res, "acldefault");
3183 120 : i_datistemplate = PQfnumber(res, "datistemplate");
3184 120 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3185 120 : i_datcollversion = PQfnumber(res, "datcollversion");
3186 120 : i_tablespace = PQfnumber(res, "tablespace");
3187 :
3188 120 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3189 120 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3190 120 : datname = PQgetvalue(res, 0, i_datname);
3191 120 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3192 120 : encoding = PQgetvalue(res, 0, i_encoding);
3193 120 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3194 120 : collate = PQgetvalue(res, 0, i_collate);
3195 120 : ctype = PQgetvalue(res, 0, i_ctype);
3196 120 : if (!PQgetisnull(res, 0, i_datlocale))
3197 28 : locale = PQgetvalue(res, 0, i_datlocale);
3198 : else
3199 92 : locale = NULL;
3200 120 : if (!PQgetisnull(res, 0, i_daticurules))
3201 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3202 : else
3203 120 : icurules = NULL;
3204 120 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3205 120 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3206 120 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3207 120 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3208 120 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3209 120 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3210 120 : tablespace = PQgetvalue(res, 0, i_tablespace);
3211 :
3212 120 : qdatname = pg_strdup(fmtId(datname));
3213 :
3214 : /*
3215 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3216 : * to preserve that), as well as the encoding, locale, and tablespace
3217 : * since those can't be altered later. Other DB properties are left to
3218 : * the DATABASE PROPERTIES entry, so that they can be applied after
3219 : * reconnecting to the target DB.
3220 : *
3221 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3222 : * shown it to be faster. When the server is in binary upgrade mode, it
3223 : * will also skip the checkpoints this strategy ordinarily performs.
3224 : */
3225 120 : if (dopt->binary_upgrade)
3226 : {
3227 26 : appendPQExpBuffer(creaQry,
3228 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3229 : "OID = %u STRATEGY = FILE_COPY",
3230 : qdatname, dbCatId.oid);
3231 : }
3232 : else
3233 : {
3234 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3235 : qdatname);
3236 : }
3237 120 : if (strlen(encoding) > 0)
3238 : {
3239 120 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3240 120 : appendStringLiteralAH(creaQry, encoding, fout);
3241 : }
3242 :
3243 120 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3244 120 : if (datlocprovider[0] == 'b')
3245 28 : appendPQExpBufferStr(creaQry, "builtin");
3246 92 : else if (datlocprovider[0] == 'c')
3247 92 : appendPQExpBufferStr(creaQry, "libc");
3248 0 : else if (datlocprovider[0] == 'i')
3249 0 : appendPQExpBufferStr(creaQry, "icu");
3250 : else
3251 0 : pg_fatal("unrecognized locale provider: %s",
3252 : datlocprovider);
3253 :
3254 120 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3255 : {
3256 120 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3257 120 : appendStringLiteralAH(creaQry, collate, fout);
3258 : }
3259 : else
3260 : {
3261 0 : if (strlen(collate) > 0)
3262 : {
3263 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3264 0 : appendStringLiteralAH(creaQry, collate, fout);
3265 : }
3266 0 : if (strlen(ctype) > 0)
3267 : {
3268 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3269 0 : appendStringLiteralAH(creaQry, ctype, fout);
3270 : }
3271 : }
3272 120 : if (locale)
3273 : {
3274 28 : if (datlocprovider[0] == 'b')
3275 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3276 : else
3277 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3278 :
3279 28 : appendStringLiteralAH(creaQry, locale, fout);
3280 : }
3281 :
3282 120 : if (icurules)
3283 : {
3284 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3285 0 : appendStringLiteralAH(creaQry, icurules, fout);
3286 : }
3287 :
3288 : /*
3289 : * For binary upgrade, carry over the collation version. For normal
3290 : * dump/restore, omit the version, so that it is computed upon restore.
3291 : */
3292 120 : if (dopt->binary_upgrade)
3293 : {
3294 26 : if (!PQgetisnull(res, 0, i_datcollversion))
3295 : {
3296 26 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3297 26 : appendStringLiteralAH(creaQry,
3298 : PQgetvalue(res, 0, i_datcollversion),
3299 : fout);
3300 : }
3301 : }
3302 :
3303 : /*
3304 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3305 : * thing; the decision whether to specify a tablespace should be left till
3306 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3307 : * label the DATABASE entry with the tablespace and let the normal
3308 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3309 : * attention to default_tablespace, so that won't work.
3310 : */
3311 120 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3312 0 : !dopt->outputNoTablespaces)
3313 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3314 : fmtId(tablespace));
3315 120 : appendPQExpBufferStr(creaQry, ";\n");
3316 :
3317 120 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3318 : qdatname);
3319 :
3320 120 : dbDumpId = createDumpId();
3321 :
3322 120 : ArchiveEntry(fout,
3323 : dbCatId, /* catalog ID */
3324 : dbDumpId, /* dump ID */
3325 120 : ARCHIVE_OPTS(.tag = datname,
3326 : .owner = dba,
3327 : .description = "DATABASE",
3328 : .section = SECTION_PRE_DATA,
3329 : .createStmt = creaQry->data,
3330 : .dropStmt = delQry->data));
3331 :
3332 : /* Compute correct tag for archive entry */
3333 120 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3334 :
3335 : /* Dump DB comment if any */
3336 : {
3337 : /*
3338 : * 8.2 and up keep comments on shared objects in a shared table, so we
3339 : * cannot use the dumpComment() code used for other database objects.
3340 : * Be careful that the ArchiveEntry parameters match that function.
3341 : */
3342 120 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3343 :
3344 120 : if (comment && *comment && !dopt->no_comments)
3345 : {
3346 50 : resetPQExpBuffer(dbQry);
3347 :
3348 : /*
3349 : * Generates warning when loaded into a differently-named
3350 : * database.
3351 : */
3352 50 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3353 50 : appendStringLiteralAH(dbQry, comment, fout);
3354 50 : appendPQExpBufferStr(dbQry, ";\n");
3355 :
3356 50 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3357 50 : ARCHIVE_OPTS(.tag = labelq->data,
3358 : .owner = dba,
3359 : .description = "COMMENT",
3360 : .section = SECTION_NONE,
3361 : .createStmt = dbQry->data,
3362 : .deps = &dbDumpId,
3363 : .nDeps = 1));
3364 : }
3365 : }
3366 :
3367 : /* Dump DB security label, if enabled */
3368 120 : if (!dopt->no_security_labels)
3369 : {
3370 : PGresult *shres;
3371 : PQExpBuffer seclabelQry;
3372 :
3373 120 : seclabelQry = createPQExpBuffer();
3374 :
3375 120 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3376 120 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3377 120 : resetPQExpBuffer(seclabelQry);
3378 120 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3379 120 : if (seclabelQry->len > 0)
3380 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3381 0 : ARCHIVE_OPTS(.tag = labelq->data,
3382 : .owner = dba,
3383 : .description = "SECURITY LABEL",
3384 : .section = SECTION_NONE,
3385 : .createStmt = seclabelQry->data,
3386 : .deps = &dbDumpId,
3387 : .nDeps = 1));
3388 120 : destroyPQExpBuffer(seclabelQry);
3389 120 : PQclear(shres);
3390 : }
3391 :
3392 : /*
3393 : * Dump ACL if any. Note that we do not support initial privileges
3394 : * (pg_init_privs) on databases.
3395 : */
3396 120 : dbdacl.privtype = 0;
3397 120 : dbdacl.initprivs = NULL;
3398 :
3399 120 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3400 : qdatname, NULL, NULL,
3401 : NULL, dba, &dbdacl);
3402 :
3403 : /*
3404 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3405 : * non-default database-level properties. (The reason this must be
3406 : * separate is that we cannot put any additional commands into the TOC
3407 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3408 : * in an implicit transaction block, and the backend won't allow CREATE
3409 : * DATABASE in that context.)
3410 : */
3411 120 : resetPQExpBuffer(creaQry);
3412 120 : resetPQExpBuffer(delQry);
3413 :
3414 120 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3415 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3416 : qdatname, datconnlimit);
3417 :
3418 120 : if (strcmp(datistemplate, "t") == 0)
3419 : {
3420 8 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3421 : qdatname);
3422 :
3423 : /*
3424 : * The backend won't accept DROP DATABASE on a template database. We
3425 : * can deal with that by removing the template marking before the DROP
3426 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3427 : * since no such command is currently supported, fake it with a direct
3428 : * UPDATE on pg_database.
3429 : */
3430 8 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3431 : "SET datistemplate = false WHERE datname = ");
3432 8 : appendStringLiteralAH(delQry, datname, fout);
3433 8 : appendPQExpBufferStr(delQry, ";\n");
3434 : }
3435 :
3436 : /*
3437 : * We do not restore pg_database.dathasloginevt because it is set
3438 : * automatically on login event trigger creation.
3439 : */
3440 :
3441 : /* Add database-specific SET options */
3442 120 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3443 :
3444 : /*
3445 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3446 : * entry, too, for lack of a better place.
3447 : */
3448 120 : if (dopt->binary_upgrade)
3449 : {
3450 26 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3451 26 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3452 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3453 : "WHERE datname = ",
3454 : frozenxid, minmxid);
3455 26 : appendStringLiteralAH(creaQry, datname, fout);
3456 26 : appendPQExpBufferStr(creaQry, ";\n");
3457 : }
3458 :
3459 120 : if (creaQry->len > 0)
3460 34 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3461 34 : ARCHIVE_OPTS(.tag = datname,
3462 : .owner = dba,
3463 : .description = "DATABASE PROPERTIES",
3464 : .section = SECTION_PRE_DATA,
3465 : .createStmt = creaQry->data,
3466 : .dropStmt = delQry->data,
3467 : .deps = &dbDumpId));
3468 :
3469 : /*
3470 : * pg_largeobject comes from the old system intact, so set its
3471 : * relfrozenxids, relminmxids and relfilenode.
3472 : */
3473 120 : if (dopt->binary_upgrade)
3474 : {
3475 : PGresult *lo_res;
3476 26 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3477 26 : PQExpBuffer loOutQry = createPQExpBuffer();
3478 26 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3479 : int ii_relfrozenxid,
3480 : ii_relfilenode,
3481 : ii_oid,
3482 : ii_relminmxid;
3483 :
3484 : /*
3485 : * pg_largeobject
3486 : */
3487 26 : if (fout->remoteVersion >= 90300)
3488 26 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3489 : "FROM pg_catalog.pg_class\n"
3490 : "WHERE oid IN (%u, %u);\n",
3491 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3492 : else
3493 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3494 : "FROM pg_catalog.pg_class\n"
3495 : "WHERE oid IN (%u, %u);\n",
3496 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3497 :
3498 26 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3499 :
3500 26 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3501 26 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3502 26 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3503 26 : ii_oid = PQfnumber(lo_res, "oid");
3504 :
3505 26 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3506 26 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3507 78 : for (int i = 0; i < PQntuples(lo_res); ++i)
3508 : {
3509 : Oid oid;
3510 : RelFileNumber relfilenumber;
3511 :
3512 52 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3513 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3514 : "WHERE oid = %u;\n",
3515 52 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3516 52 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3517 52 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3518 :
3519 52 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3520 52 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3521 :
3522 52 : if (oid == LargeObjectRelationId)
3523 26 : appendPQExpBuffer(loOutQry,
3524 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3525 : relfilenumber);
3526 26 : else if (oid == LargeObjectLOidPNIndexId)
3527 26 : appendPQExpBuffer(loOutQry,
3528 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3529 : relfilenumber);
3530 : }
3531 :
3532 26 : appendPQExpBufferStr(loOutQry,
3533 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3534 26 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3535 :
3536 26 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3537 26 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3538 : .description = "pg_largeobject",
3539 : .section = SECTION_PRE_DATA,
3540 : .createStmt = loOutQry->data));
3541 :
3542 26 : PQclear(lo_res);
3543 :
3544 26 : destroyPQExpBuffer(loFrozenQry);
3545 26 : destroyPQExpBuffer(loHorizonQry);
3546 26 : destroyPQExpBuffer(loOutQry);
3547 : }
3548 :
3549 120 : PQclear(res);
3550 :
3551 120 : free(qdatname);
3552 120 : destroyPQExpBuffer(dbQry);
3553 120 : destroyPQExpBuffer(delQry);
3554 120 : destroyPQExpBuffer(creaQry);
3555 120 : destroyPQExpBuffer(labelq);
3556 120 : }
3557 :
3558 : /*
3559 : * Collect any database-specific or role-and-database-specific SET options
3560 : * for this database, and append them to outbuf.
3561 : */
3562 : static void
3563 120 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3564 : const char *dbname, Oid dboid)
3565 : {
3566 120 : PGconn *conn = GetConnection(AH);
3567 120 : PQExpBuffer buf = createPQExpBuffer();
3568 : PGresult *res;
3569 :
3570 : /* First collect database-specific options */
3571 120 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3572 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3573 : dboid);
3574 :
3575 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3576 :
3577 180 : for (int i = 0; i < PQntuples(res); i++)
3578 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3579 : "DATABASE", dbname, NULL, NULL,
3580 : outbuf);
3581 :
3582 120 : PQclear(res);
3583 :
3584 : /* Now look for role-and-database-specific options */
3585 120 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3586 : "FROM pg_db_role_setting s, pg_roles r "
3587 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3588 : dboid);
3589 :
3590 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3591 :
3592 120 : for (int i = 0; i < PQntuples(res); i++)
3593 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3594 0 : "ROLE", PQgetvalue(res, i, 0),
3595 : "DATABASE", dbname,
3596 : outbuf);
3597 :
3598 120 : PQclear(res);
3599 :
3600 120 : destroyPQExpBuffer(buf);
3601 120 : }
3602 :
3603 : /*
3604 : * dumpEncoding: put the correct encoding into the archive
3605 : */
3606 : static void
3607 308 : dumpEncoding(Archive *AH)
3608 : {
3609 308 : const char *encname = pg_encoding_to_char(AH->encoding);
3610 308 : PQExpBuffer qry = createPQExpBuffer();
3611 :
3612 308 : pg_log_info("saving encoding = %s", encname);
3613 :
3614 308 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3615 308 : appendStringLiteralAH(qry, encname, AH);
3616 308 : appendPQExpBufferStr(qry, ";\n");
3617 :
3618 308 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3619 308 : ARCHIVE_OPTS(.tag = "ENCODING",
3620 : .description = "ENCODING",
3621 : .section = SECTION_PRE_DATA,
3622 : .createStmt = qry->data));
3623 :
3624 308 : destroyPQExpBuffer(qry);
3625 308 : }
3626 :
3627 :
3628 : /*
3629 : * dumpStdStrings: put the correct escape string behavior into the archive
3630 : */
3631 : static void
3632 308 : dumpStdStrings(Archive *AH)
3633 : {
3634 308 : const char *stdstrings = AH->std_strings ? "on" : "off";
3635 308 : PQExpBuffer qry = createPQExpBuffer();
3636 :
3637 308 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3638 : stdstrings);
3639 :
3640 308 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3641 : stdstrings);
3642 :
3643 308 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3644 308 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3645 : .description = "STDSTRINGS",
3646 : .section = SECTION_PRE_DATA,
3647 : .createStmt = qry->data));
3648 :
3649 308 : destroyPQExpBuffer(qry);
3650 308 : }
3651 :
3652 : /*
3653 : * dumpSearchPath: record the active search_path in the archive
3654 : */
3655 : static void
3656 308 : dumpSearchPath(Archive *AH)
3657 : {
3658 308 : PQExpBuffer qry = createPQExpBuffer();
3659 308 : PQExpBuffer path = createPQExpBuffer();
3660 : PGresult *res;
3661 308 : char **schemanames = NULL;
3662 308 : int nschemanames = 0;
3663 : int i;
3664 :
3665 : /*
3666 : * We use the result of current_schemas(), not the search_path GUC,
3667 : * because that might contain wildcards such as "$user", which won't
3668 : * necessarily have the same value during restore. Also, this way avoids
3669 : * listing schemas that may appear in search_path but not actually exist,
3670 : * which seems like a prudent exclusion.
3671 : */
3672 308 : res = ExecuteSqlQueryForSingleRow(AH,
3673 : "SELECT pg_catalog.current_schemas(false)");
3674 :
3675 308 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3676 0 : pg_fatal("could not parse result of current_schemas()");
3677 :
3678 : /*
3679 : * We use set_config(), not a simple "SET search_path" command, because
3680 : * the latter has less-clean behavior if the search path is empty. While
3681 : * that's likely to get fixed at some point, it seems like a good idea to
3682 : * be as backwards-compatible as possible in what we put into archives.
3683 : */
3684 308 : for (i = 0; i < nschemanames; i++)
3685 : {
3686 0 : if (i > 0)
3687 0 : appendPQExpBufferStr(path, ", ");
3688 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3689 : }
3690 :
3691 308 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3692 308 : appendStringLiteralAH(qry, path->data, AH);
3693 308 : appendPQExpBufferStr(qry, ", false);\n");
3694 :
3695 308 : pg_log_info("saving \"search_path = %s\"", path->data);
3696 :
3697 308 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3698 308 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3699 : .description = "SEARCHPATH",
3700 : .section = SECTION_PRE_DATA,
3701 : .createStmt = qry->data));
3702 :
3703 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3704 308 : AH->searchpath = pg_strdup(qry->data);
3705 :
3706 308 : free(schemanames);
3707 308 : PQclear(res);
3708 308 : destroyPQExpBuffer(qry);
3709 308 : destroyPQExpBuffer(path);
3710 308 : }
3711 :
3712 :
3713 : /*
3714 : * getLOs:
3715 : * Collect schema-level data about large objects
3716 : */
3717 : static void
3718 256 : getLOs(Archive *fout)
3719 : {
3720 256 : DumpOptions *dopt = fout->dopt;
3721 256 : PQExpBuffer loQry = createPQExpBuffer();
3722 : PGresult *res;
3723 : int ntups;
3724 : int i;
3725 : int n;
3726 : int i_oid;
3727 : int i_lomowner;
3728 : int i_lomacl;
3729 : int i_acldefault;
3730 :
3731 256 : pg_log_info("reading large objects");
3732 :
3733 : /*
3734 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3735 : * with the same owner/ACL appear together.
3736 : */
3737 256 : appendPQExpBufferStr(loQry,
3738 : "SELECT oid, lomowner, lomacl, "
3739 : "acldefault('L', lomowner) AS acldefault "
3740 : "FROM pg_largeobject_metadata "
3741 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3742 :
3743 256 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3744 :
3745 256 : i_oid = PQfnumber(res, "oid");
3746 256 : i_lomowner = PQfnumber(res, "lomowner");
3747 256 : i_lomacl = PQfnumber(res, "lomacl");
3748 256 : i_acldefault = PQfnumber(res, "acldefault");
3749 :
3750 256 : ntups = PQntuples(res);
3751 :
3752 : /*
3753 : * Group the blobs into suitably-sized groups that have the same owner and
3754 : * ACL setting, and build a metadata and a data DumpableObject for each
3755 : * group. (If we supported initprivs for blobs, we'd have to insist that
3756 : * groups also share initprivs settings, since the DumpableObject only has
3757 : * room for one.) i is the index of the first tuple in the current group,
3758 : * and n is the number of tuples we include in the group.
3759 : */
3760 402 : for (i = 0; i < ntups; i += n)
3761 : {
3762 146 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3763 146 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3764 146 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3765 : LoInfo *loinfo;
3766 : DumpableObject *lodata;
3767 : char namebuf[64];
3768 :
3769 : /* Scan to find first tuple not to be included in group */
3770 146 : n = 1;
3771 166 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3772 : {
3773 88 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3774 88 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3775 : break;
3776 20 : n++;
3777 : }
3778 :
3779 : /* Build the metadata DumpableObject */
3780 146 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3781 :
3782 146 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3783 146 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3784 146 : loinfo->dobj.catId.oid = thisoid;
3785 146 : AssignDumpId(&loinfo->dobj);
3786 :
3787 146 : if (n > 1)
3788 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3789 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3790 : else
3791 136 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3792 146 : loinfo->dobj.name = pg_strdup(namebuf);
3793 146 : loinfo->dacl.acl = pg_strdup(thisacl);
3794 146 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3795 146 : loinfo->dacl.privtype = 0;
3796 146 : loinfo->dacl.initprivs = NULL;
3797 146 : loinfo->rolname = getRoleName(thisowner);
3798 146 : loinfo->numlos = n;
3799 146 : loinfo->looids[0] = thisoid;
3800 : /* Collect OIDs of the remaining blobs in this group */
3801 166 : for (int k = 1; k < n; k++)
3802 : {
3803 : CatalogId extraID;
3804 :
3805 20 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3806 :
3807 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3808 20 : extraID.tableoid = LargeObjectRelationId;
3809 20 : extraID.oid = loinfo->looids[k];
3810 20 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3811 : }
3812 :
3813 : /* LOs have data */
3814 146 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3815 :
3816 : /* Mark whether LO group has a non-empty ACL */
3817 146 : if (!PQgetisnull(res, i, i_lomacl))
3818 68 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3819 :
3820 : /*
3821 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3822 : * as it will be copied by pg_upgrade, which simply copies the
3823 : * pg_largeobject table. We *do* however dump out anything but the
3824 : * data, as pg_upgrade copies just pg_largeobject, but not
3825 : * pg_largeobject_metadata, after the dump is restored.
3826 : */
3827 146 : if (dopt->binary_upgrade)
3828 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3829 :
3830 : /*
3831 : * Create a "BLOBS" data item for the group, too. This is just a
3832 : * placeholder for sorting; it carries no data now.
3833 : */
3834 146 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3835 146 : lodata->objType = DO_LARGE_OBJECT_DATA;
3836 146 : lodata->catId = nilCatalogId;
3837 146 : AssignDumpId(lodata);
3838 146 : lodata->name = pg_strdup(namebuf);
3839 146 : lodata->components |= DUMP_COMPONENT_DATA;
3840 : /* Set up explicit dependency from data to metadata */
3841 146 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3842 146 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3843 146 : lodata->nDeps = lodata->allocDeps = 1;
3844 : }
3845 :
3846 256 : PQclear(res);
3847 256 : destroyPQExpBuffer(loQry);
3848 256 : }
3849 :
3850 : /*
3851 : * dumpLO
3852 : *
3853 : * dump the definition (metadata) of the given large object group
3854 : */
3855 : static void
3856 146 : dumpLO(Archive *fout, const LoInfo *loinfo)
3857 : {
3858 146 : PQExpBuffer cquery = createPQExpBuffer();
3859 :
3860 : /*
3861 : * The "definition" is just a newline-separated list of OIDs. We need to
3862 : * put something into the dropStmt too, but it can just be a comment.
3863 : */
3864 312 : for (int i = 0; i < loinfo->numlos; i++)
3865 166 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3866 :
3867 146 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3868 146 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3869 146 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3870 : .owner = loinfo->rolname,
3871 : .description = "BLOB METADATA",
3872 : .section = SECTION_DATA,
3873 : .createStmt = cquery->data,
3874 : .dropStmt = "-- dummy"));
3875 :
3876 : /*
3877 : * Dump per-blob comments and seclabels if any. We assume these are rare
3878 : * enough that it's okay to generate retail TOC entries for them.
3879 : */
3880 146 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3881 : DUMP_COMPONENT_SECLABEL))
3882 : {
3883 176 : for (int i = 0; i < loinfo->numlos; i++)
3884 : {
3885 : CatalogId catId;
3886 : char namebuf[32];
3887 :
3888 : /* Build identifying info for this blob */
3889 98 : catId.tableoid = loinfo->dobj.catId.tableoid;
3890 98 : catId.oid = loinfo->looids[i];
3891 98 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3892 :
3893 98 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3894 98 : dumpComment(fout, "LARGE OBJECT", namebuf,
3895 : NULL, loinfo->rolname,
3896 : catId, 0, loinfo->dobj.dumpId);
3897 :
3898 98 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3899 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
3900 : NULL, loinfo->rolname,
3901 : catId, 0, loinfo->dobj.dumpId);
3902 : }
3903 : }
3904 :
3905 : /*
3906 : * Dump the ACLs if any (remember that all blobs in the group will have
3907 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
3908 : * there's more, make a "LARGE OBJECTS" entry that really contains only
3909 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
3910 : * string to emit a mutated version for each blob.
3911 : */
3912 146 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3913 : {
3914 : char namebuf[32];
3915 :
3916 : /* Build identifying info for the first blob */
3917 68 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
3918 :
3919 68 : if (loinfo->numlos > 1)
3920 : {
3921 : char tagbuf[64];
3922 :
3923 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
3924 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
3925 :
3926 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3927 : "LARGE OBJECT", namebuf, NULL, NULL,
3928 : tagbuf, loinfo->rolname, &loinfo->dacl);
3929 : }
3930 : else
3931 : {
3932 68 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3933 : "LARGE OBJECT", namebuf, NULL, NULL,
3934 : NULL, loinfo->rolname, &loinfo->dacl);
3935 : }
3936 : }
3937 :
3938 146 : destroyPQExpBuffer(cquery);
3939 146 : }
3940 :
3941 : /*
3942 : * dumpLOs:
3943 : * dump the data contents of the large objects in the given group
3944 : */
3945 : static int
3946 132 : dumpLOs(Archive *fout, const void *arg)
3947 : {
3948 132 : const LoInfo *loinfo = (const LoInfo *) arg;
3949 132 : PGconn *conn = GetConnection(fout);
3950 : char buf[LOBBUFSIZE];
3951 :
3952 132 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
3953 :
3954 280 : for (int i = 0; i < loinfo->numlos; i++)
3955 : {
3956 148 : Oid loOid = loinfo->looids[i];
3957 : int loFd;
3958 : int cnt;
3959 :
3960 : /* Open the LO */
3961 148 : loFd = lo_open(conn, loOid, INV_READ);
3962 148 : if (loFd == -1)
3963 0 : pg_fatal("could not open large object %u: %s",
3964 : loOid, PQerrorMessage(conn));
3965 :
3966 148 : StartLO(fout, loOid);
3967 :
3968 : /* Now read it in chunks, sending data to archive */
3969 : do
3970 : {
3971 226 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3972 226 : if (cnt < 0)
3973 0 : pg_fatal("error reading large object %u: %s",
3974 : loOid, PQerrorMessage(conn));
3975 :
3976 226 : WriteData(fout, buf, cnt);
3977 226 : } while (cnt > 0);
3978 :
3979 148 : lo_close(conn, loFd);
3980 :
3981 148 : EndLO(fout, loOid);
3982 : }
3983 :
3984 132 : return 1;
3985 : }
3986 :
3987 : /*
3988 : * getPolicies
3989 : * get information about all RLS policies on dumpable tables.
3990 : */
3991 : void
3992 308 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3993 : {
3994 : PQExpBuffer query;
3995 : PQExpBuffer tbloids;
3996 : PGresult *res;
3997 : PolicyInfo *polinfo;
3998 : int i_oid;
3999 : int i_tableoid;
4000 : int i_polrelid;
4001 : int i_polname;
4002 : int i_polcmd;
4003 : int i_polpermissive;
4004 : int i_polroles;
4005 : int i_polqual;
4006 : int i_polwithcheck;
4007 : int i,
4008 : j,
4009 : ntups;
4010 :
4011 : /* No policies before 9.5 */
4012 308 : if (fout->remoteVersion < 90500)
4013 0 : return;
4014 :
4015 308 : query = createPQExpBuffer();
4016 308 : tbloids = createPQExpBuffer();
4017 :
4018 : /*
4019 : * Identify tables of interest, and check which ones have RLS enabled.
4020 : */
4021 308 : appendPQExpBufferChar(tbloids, '{');
4022 81230 : for (i = 0; i < numTables; i++)
4023 : {
4024 80922 : TableInfo *tbinfo = &tblinfo[i];
4025 :
4026 : /* Ignore row security on tables not to be dumped */
4027 80922 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4028 68750 : continue;
4029 :
4030 : /* It can't have RLS or policies if it's not a table */
4031 12172 : if (tbinfo->relkind != RELKIND_RELATION &&
4032 3598 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4033 2544 : continue;
4034 :
4035 : /* Add it to the list of table OIDs to be probed below */
4036 9628 : if (tbloids->len > 1) /* do we have more than the '{'? */
4037 9432 : appendPQExpBufferChar(tbloids, ',');
4038 9628 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4039 :
4040 : /* Is RLS enabled? (That's separate from whether it has policies) */
4041 9628 : if (tbinfo->rowsec)
4042 : {
4043 104 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4044 :
4045 : /*
4046 : * We represent RLS being enabled on a table by creating a
4047 : * PolicyInfo object with null polname.
4048 : *
4049 : * Note: use tableoid 0 so that this object won't be mistaken for
4050 : * something that pg_depend entries apply to.
4051 : */
4052 104 : polinfo = pg_malloc(sizeof(PolicyInfo));
4053 104 : polinfo->dobj.objType = DO_POLICY;
4054 104 : polinfo->dobj.catId.tableoid = 0;
4055 104 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4056 104 : AssignDumpId(&polinfo->dobj);
4057 104 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4058 104 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4059 104 : polinfo->poltable = tbinfo;
4060 104 : polinfo->polname = NULL;
4061 104 : polinfo->polcmd = '\0';
4062 104 : polinfo->polpermissive = 0;
4063 104 : polinfo->polroles = NULL;
4064 104 : polinfo->polqual = NULL;
4065 104 : polinfo->polwithcheck = NULL;
4066 : }
4067 : }
4068 308 : appendPQExpBufferChar(tbloids, '}');
4069 :
4070 : /*
4071 : * Now, read all RLS policies belonging to the tables of interest, and
4072 : * create PolicyInfo objects for them. (Note that we must filter the
4073 : * results server-side not locally, because we dare not apply pg_get_expr
4074 : * to tables we don't have lock on.)
4075 : */
4076 308 : pg_log_info("reading row-level security policies");
4077 :
4078 308 : printfPQExpBuffer(query,
4079 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4080 308 : if (fout->remoteVersion >= 100000)
4081 308 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4082 : else
4083 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4084 308 : appendPQExpBuffer(query,
4085 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4086 : " 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, "
4087 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4088 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4089 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4090 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4091 : tbloids->data);
4092 :
4093 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4094 :
4095 308 : ntups = PQntuples(res);
4096 308 : if (ntups > 0)
4097 : {
4098 84 : i_oid = PQfnumber(res, "oid");
4099 84 : i_tableoid = PQfnumber(res, "tableoid");
4100 84 : i_polrelid = PQfnumber(res, "polrelid");
4101 84 : i_polname = PQfnumber(res, "polname");
4102 84 : i_polcmd = PQfnumber(res, "polcmd");
4103 84 : i_polpermissive = PQfnumber(res, "polpermissive");
4104 84 : i_polroles = PQfnumber(res, "polroles");
4105 84 : i_polqual = PQfnumber(res, "polqual");
4106 84 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4107 :
4108 84 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4109 :
4110 618 : for (j = 0; j < ntups; j++)
4111 : {
4112 534 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4113 534 : TableInfo *tbinfo = findTableByOid(polrelid);
4114 :
4115 534 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4116 :
4117 534 : polinfo[j].dobj.objType = DO_POLICY;
4118 534 : polinfo[j].dobj.catId.tableoid =
4119 534 : atooid(PQgetvalue(res, j, i_tableoid));
4120 534 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4121 534 : AssignDumpId(&polinfo[j].dobj);
4122 534 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4123 534 : polinfo[j].poltable = tbinfo;
4124 534 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4125 534 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4126 :
4127 534 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4128 534 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4129 :
4130 534 : if (PQgetisnull(res, j, i_polroles))
4131 238 : polinfo[j].polroles = NULL;
4132 : else
4133 296 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4134 :
4135 534 : if (PQgetisnull(res, j, i_polqual))
4136 74 : polinfo[j].polqual = NULL;
4137 : else
4138 460 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4139 :
4140 534 : if (PQgetisnull(res, j, i_polwithcheck))
4141 282 : polinfo[j].polwithcheck = NULL;
4142 : else
4143 252 : polinfo[j].polwithcheck
4144 252 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4145 : }
4146 : }
4147 :
4148 308 : PQclear(res);
4149 :
4150 308 : destroyPQExpBuffer(query);
4151 308 : destroyPQExpBuffer(tbloids);
4152 : }
4153 :
4154 : /*
4155 : * dumpPolicy
4156 : * dump the definition of the given policy
4157 : */
4158 : static void
4159 638 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4160 : {
4161 638 : DumpOptions *dopt = fout->dopt;
4162 638 : TableInfo *tbinfo = polinfo->poltable;
4163 : PQExpBuffer query;
4164 : PQExpBuffer delqry;
4165 : PQExpBuffer polprefix;
4166 : char *qtabname;
4167 : const char *cmd;
4168 : char *tag;
4169 :
4170 : /* Do nothing if not dumping schema */
4171 638 : if (!dopt->dumpSchema)
4172 56 : return;
4173 :
4174 : /*
4175 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4176 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4177 : * ROW LEVEL SECURITY.
4178 : */
4179 582 : if (polinfo->polname == NULL)
4180 : {
4181 96 : query = createPQExpBuffer();
4182 :
4183 96 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4184 96 : fmtQualifiedDumpable(tbinfo));
4185 :
4186 : /*
4187 : * We must emit the ROW SECURITY object's dependency on its table
4188 : * explicitly, because it will not match anything in pg_depend (unlike
4189 : * the case for other PolicyInfo objects).
4190 : */
4191 96 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4192 96 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4193 96 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4194 : .namespace = polinfo->dobj.namespace->dobj.name,
4195 : .owner = tbinfo->rolname,
4196 : .description = "ROW SECURITY",
4197 : .section = SECTION_POST_DATA,
4198 : .createStmt = query->data,
4199 : .deps = &(tbinfo->dobj.dumpId),
4200 : .nDeps = 1));
4201 :
4202 96 : destroyPQExpBuffer(query);
4203 96 : return;
4204 : }
4205 :
4206 486 : if (polinfo->polcmd == '*')
4207 162 : cmd = "";
4208 324 : else if (polinfo->polcmd == 'r')
4209 86 : cmd = " FOR SELECT";
4210 238 : else if (polinfo->polcmd == 'a')
4211 66 : cmd = " FOR INSERT";
4212 172 : else if (polinfo->polcmd == 'w')
4213 86 : cmd = " FOR UPDATE";
4214 86 : else if (polinfo->polcmd == 'd')
4215 86 : cmd = " FOR DELETE";
4216 : else
4217 0 : pg_fatal("unexpected policy command type: %c",
4218 : polinfo->polcmd);
4219 :
4220 486 : query = createPQExpBuffer();
4221 486 : delqry = createPQExpBuffer();
4222 486 : polprefix = createPQExpBuffer();
4223 :
4224 486 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4225 :
4226 486 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4227 :
4228 486 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4229 486 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4230 :
4231 486 : if (polinfo->polroles != NULL)
4232 264 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4233 :
4234 486 : if (polinfo->polqual != NULL)
4235 420 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4236 :
4237 486 : if (polinfo->polwithcheck != NULL)
4238 228 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4239 :
4240 486 : appendPQExpBufferStr(query, ";\n");
4241 :
4242 486 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4243 486 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4244 :
4245 486 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4246 486 : fmtId(polinfo->polname));
4247 :
4248 486 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4249 :
4250 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4251 486 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4252 486 : ARCHIVE_OPTS(.tag = tag,
4253 : .namespace = polinfo->dobj.namespace->dobj.name,
4254 : .owner = tbinfo->rolname,
4255 : .description = "POLICY",
4256 : .section = SECTION_POST_DATA,
4257 : .createStmt = query->data,
4258 : .dropStmt = delqry->data));
4259 :
4260 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4261 0 : dumpComment(fout, polprefix->data, qtabname,
4262 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4263 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4264 :
4265 486 : free(tag);
4266 486 : destroyPQExpBuffer(query);
4267 486 : destroyPQExpBuffer(delqry);
4268 486 : destroyPQExpBuffer(polprefix);
4269 486 : free(qtabname);
4270 : }
4271 :
4272 : /*
4273 : * getPublications
4274 : * get information about publications
4275 : */
4276 : void
4277 308 : getPublications(Archive *fout)
4278 : {
4279 308 : DumpOptions *dopt = fout->dopt;
4280 : PQExpBuffer query;
4281 : PGresult *res;
4282 : PublicationInfo *pubinfo;
4283 : int i_tableoid;
4284 : int i_oid;
4285 : int i_pubname;
4286 : int i_pubowner;
4287 : int i_puballtables;
4288 : int i_pubinsert;
4289 : int i_pubupdate;
4290 : int i_pubdelete;
4291 : int i_pubtruncate;
4292 : int i_pubviaroot;
4293 : int i_pubgencols;
4294 : int i,
4295 : ntups;
4296 :
4297 308 : if (dopt->no_publications || fout->remoteVersion < 100000)
4298 0 : return;
4299 :
4300 308 : query = createPQExpBuffer();
4301 :
4302 : /* Get the publications. */
4303 308 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4304 : "p.pubowner, p.puballtables, p.pubinsert, "
4305 : "p.pubupdate, p.pubdelete, ");
4306 :
4307 308 : if (fout->remoteVersion >= 110000)
4308 308 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4309 : else
4310 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4311 :
4312 308 : if (fout->remoteVersion >= 130000)
4313 308 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4314 : else
4315 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4316 :
4317 308 : if (fout->remoteVersion >= 180000)
4318 308 : appendPQExpBufferStr(query, "p.pubgencols ");
4319 : else
4320 0 : appendPQExpBufferStr(query, "false AS pubgencols ");
4321 :
4322 308 : appendPQExpBufferStr(query, "FROM pg_publication p");
4323 :
4324 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4325 :
4326 308 : ntups = PQntuples(res);
4327 :
4328 308 : if (ntups == 0)
4329 220 : goto cleanup;
4330 :
4331 88 : i_tableoid = PQfnumber(res, "tableoid");
4332 88 : i_oid = PQfnumber(res, "oid");
4333 88 : i_pubname = PQfnumber(res, "pubname");
4334 88 : i_pubowner = PQfnumber(res, "pubowner");
4335 88 : i_puballtables = PQfnumber(res, "puballtables");
4336 88 : i_pubinsert = PQfnumber(res, "pubinsert");
4337 88 : i_pubupdate = PQfnumber(res, "pubupdate");
4338 88 : i_pubdelete = PQfnumber(res, "pubdelete");
4339 88 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4340 88 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4341 88 : i_pubgencols = PQfnumber(res, "pubgencols");
4342 :
4343 88 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4344 :
4345 520 : for (i = 0; i < ntups; i++)
4346 : {
4347 432 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4348 432 : pubinfo[i].dobj.catId.tableoid =
4349 432 : atooid(PQgetvalue(res, i, i_tableoid));
4350 432 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4351 432 : AssignDumpId(&pubinfo[i].dobj);
4352 432 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4353 432 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4354 432 : pubinfo[i].puballtables =
4355 432 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4356 432 : pubinfo[i].pubinsert =
4357 432 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4358 432 : pubinfo[i].pubupdate =
4359 432 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4360 432 : pubinfo[i].pubdelete =
4361 432 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4362 432 : pubinfo[i].pubtruncate =
4363 432 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4364 432 : pubinfo[i].pubviaroot =
4365 432 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4366 432 : pubinfo[i].pubgencols =
4367 432 : (strcmp(PQgetvalue(res, i, i_pubgencols), "t") == 0);
4368 :
4369 : /* Decide whether we want to dump it */
4370 432 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4371 : }
4372 :
4373 88 : cleanup:
4374 308 : PQclear(res);
4375 :
4376 308 : destroyPQExpBuffer(query);
4377 : }
4378 :
4379 : /*
4380 : * dumpPublication
4381 : * dump the definition of the given publication
4382 : */
4383 : static void
4384 352 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4385 : {
4386 352 : DumpOptions *dopt = fout->dopt;
4387 : PQExpBuffer delq;
4388 : PQExpBuffer query;
4389 : char *qpubname;
4390 352 : bool first = true;
4391 :
4392 : /* Do nothing if not dumping schema */
4393 352 : if (!dopt->dumpSchema)
4394 30 : return;
4395 :
4396 322 : delq = createPQExpBuffer();
4397 322 : query = createPQExpBuffer();
4398 :
4399 322 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4400 :
4401 322 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4402 : qpubname);
4403 :
4404 322 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4405 : qpubname);
4406 :
4407 322 : if (pubinfo->puballtables)
4408 66 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4409 :
4410 322 : appendPQExpBufferStr(query, " WITH (publish = '");
4411 322 : if (pubinfo->pubinsert)
4412 : {
4413 258 : appendPQExpBufferStr(query, "insert");
4414 258 : first = false;
4415 : }
4416 :
4417 322 : if (pubinfo->pubupdate)
4418 : {
4419 258 : if (!first)
4420 258 : appendPQExpBufferStr(query, ", ");
4421 :
4422 258 : appendPQExpBufferStr(query, "update");
4423 258 : first = false;
4424 : }
4425 :
4426 322 : if (pubinfo->pubdelete)
4427 : {
4428 258 : if (!first)
4429 258 : appendPQExpBufferStr(query, ", ");
4430 :
4431 258 : appendPQExpBufferStr(query, "delete");
4432 258 : first = false;
4433 : }
4434 :
4435 322 : if (pubinfo->pubtruncate)
4436 : {
4437 258 : if (!first)
4438 258 : appendPQExpBufferStr(query, ", ");
4439 :
4440 258 : appendPQExpBufferStr(query, "truncate");
4441 258 : first = false;
4442 : }
4443 :
4444 322 : appendPQExpBufferChar(query, '\'');
4445 :
4446 322 : if (pubinfo->pubviaroot)
4447 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4448 :
4449 322 : if (pubinfo->pubgencols)
4450 64 : appendPQExpBufferStr(query, ", publish_generated_columns = true");
4451 :
4452 322 : appendPQExpBufferStr(query, ");\n");
4453 :
4454 322 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4455 322 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4456 322 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4457 : .owner = pubinfo->rolname,
4458 : .description = "PUBLICATION",
4459 : .section = SECTION_POST_DATA,
4460 : .createStmt = query->data,
4461 : .dropStmt = delq->data));
4462 :
4463 322 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4464 64 : dumpComment(fout, "PUBLICATION", qpubname,
4465 : NULL, pubinfo->rolname,
4466 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4467 :
4468 322 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4469 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4470 : NULL, pubinfo->rolname,
4471 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4472 :
4473 322 : destroyPQExpBuffer(delq);
4474 322 : destroyPQExpBuffer(query);
4475 322 : free(qpubname);
4476 : }
4477 :
4478 : /*
4479 : * getPublicationNamespaces
4480 : * get information about publication membership for dumpable schemas.
4481 : */
4482 : void
4483 308 : getPublicationNamespaces(Archive *fout)
4484 : {
4485 : PQExpBuffer query;
4486 : PGresult *res;
4487 : PublicationSchemaInfo *pubsinfo;
4488 308 : DumpOptions *dopt = fout->dopt;
4489 : int i_tableoid;
4490 : int i_oid;
4491 : int i_pnpubid;
4492 : int i_pnnspid;
4493 : int i,
4494 : j,
4495 : ntups;
4496 :
4497 308 : if (dopt->no_publications || fout->remoteVersion < 150000)
4498 0 : return;
4499 :
4500 308 : query = createPQExpBuffer();
4501 :
4502 : /* Collect all publication membership info. */
4503 308 : appendPQExpBufferStr(query,
4504 : "SELECT tableoid, oid, pnpubid, pnnspid "
4505 : "FROM pg_catalog.pg_publication_namespace");
4506 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4507 :
4508 308 : ntups = PQntuples(res);
4509 :
4510 308 : i_tableoid = PQfnumber(res, "tableoid");
4511 308 : i_oid = PQfnumber(res, "oid");
4512 308 : i_pnpubid = PQfnumber(res, "pnpubid");
4513 308 : i_pnnspid = PQfnumber(res, "pnnspid");
4514 :
4515 : /* this allocation may be more than we need */
4516 308 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4517 308 : j = 0;
4518 :
4519 480 : for (i = 0; i < ntups; i++)
4520 : {
4521 172 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4522 172 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4523 : PublicationInfo *pubinfo;
4524 : NamespaceInfo *nspinfo;
4525 :
4526 : /*
4527 : * Ignore any entries for which we aren't interested in either the
4528 : * publication or the rel.
4529 : */
4530 172 : pubinfo = findPublicationByOid(pnpubid);
4531 172 : if (pubinfo == NULL)
4532 0 : continue;
4533 172 : nspinfo = findNamespaceByOid(pnnspid);
4534 172 : if (nspinfo == NULL)
4535 0 : continue;
4536 :
4537 : /*
4538 : * We always dump publication namespaces unless the corresponding
4539 : * namespace is excluded from the dump.
4540 : */
4541 172 : if (nspinfo->dobj.dump == DUMP_COMPONENT_NONE)
4542 30 : continue;
4543 :
4544 : /* OK, make a DumpableObject for this relationship */
4545 142 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4546 142 : pubsinfo[j].dobj.catId.tableoid =
4547 142 : atooid(PQgetvalue(res, i, i_tableoid));
4548 142 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4549 142 : AssignDumpId(&pubsinfo[j].dobj);
4550 142 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4551 142 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4552 142 : pubsinfo[j].publication = pubinfo;
4553 142 : pubsinfo[j].pubschema = nspinfo;
4554 :
4555 : /* Decide whether we want to dump it */
4556 142 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4557 :
4558 142 : j++;
4559 : }
4560 :
4561 308 : PQclear(res);
4562 308 : destroyPQExpBuffer(query);
4563 : }
4564 :
4565 : /*
4566 : * getPublicationTables
4567 : * get information about publication membership for dumpable tables.
4568 : */
4569 : void
4570 308 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4571 : {
4572 : PQExpBuffer query;
4573 : PGresult *res;
4574 : PublicationRelInfo *pubrinfo;
4575 308 : DumpOptions *dopt = fout->dopt;
4576 : int i_tableoid;
4577 : int i_oid;
4578 : int i_prpubid;
4579 : int i_prrelid;
4580 : int i_prrelqual;
4581 : int i_prattrs;
4582 : int i,
4583 : j,
4584 : ntups;
4585 :
4586 308 : if (dopt->no_publications || fout->remoteVersion < 100000)
4587 0 : return;
4588 :
4589 308 : query = createPQExpBuffer();
4590 :
4591 : /* Collect all publication membership info. */
4592 308 : if (fout->remoteVersion >= 150000)
4593 308 : appendPQExpBufferStr(query,
4594 : "SELECT tableoid, oid, prpubid, prrelid, "
4595 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4596 : "(CASE\n"
4597 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4598 : " (SELECT array_agg(attname)\n"
4599 : " FROM\n"
4600 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4601 : " pg_catalog.pg_attribute\n"
4602 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4603 : " ELSE NULL END) prattrs "
4604 : "FROM pg_catalog.pg_publication_rel pr");
4605 : else
4606 0 : appendPQExpBufferStr(query,
4607 : "SELECT tableoid, oid, prpubid, prrelid, "
4608 : "NULL AS prrelqual, NULL AS prattrs "
4609 : "FROM pg_catalog.pg_publication_rel");
4610 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4611 :
4612 308 : ntups = PQntuples(res);
4613 :
4614 308 : i_tableoid = PQfnumber(res, "tableoid");
4615 308 : i_oid = PQfnumber(res, "oid");
4616 308 : i_prpubid = PQfnumber(res, "prpubid");
4617 308 : i_prrelid = PQfnumber(res, "prrelid");
4618 308 : i_prrelqual = PQfnumber(res, "prrelqual");
4619 308 : i_prattrs = PQfnumber(res, "prattrs");
4620 :
4621 : /* this allocation may be more than we need */
4622 308 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4623 308 : j = 0;
4624 :
4625 910 : for (i = 0; i < ntups; i++)
4626 : {
4627 602 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4628 602 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4629 : PublicationInfo *pubinfo;
4630 : TableInfo *tbinfo;
4631 :
4632 : /*
4633 : * Ignore any entries for which we aren't interested in either the
4634 : * publication or the rel.
4635 : */
4636 602 : pubinfo = findPublicationByOid(prpubid);
4637 602 : if (pubinfo == NULL)
4638 0 : continue;
4639 602 : tbinfo = findTableByOid(prrelid);
4640 602 : if (tbinfo == NULL)
4641 0 : continue;
4642 :
4643 : /*
4644 : * Ignore publication membership of tables whose definitions are not
4645 : * to be dumped.
4646 : */
4647 602 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4648 92 : continue;
4649 :
4650 : /* OK, make a DumpableObject for this relationship */
4651 510 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4652 510 : pubrinfo[j].dobj.catId.tableoid =
4653 510 : atooid(PQgetvalue(res, i, i_tableoid));
4654 510 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4655 510 : AssignDumpId(&pubrinfo[j].dobj);
4656 510 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4657 510 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4658 510 : pubrinfo[j].publication = pubinfo;
4659 510 : pubrinfo[j].pubtable = tbinfo;
4660 510 : if (PQgetisnull(res, i, i_prrelqual))
4661 292 : pubrinfo[j].pubrelqual = NULL;
4662 : else
4663 218 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4664 :
4665 510 : if (!PQgetisnull(res, i, i_prattrs))
4666 : {
4667 : char **attnames;
4668 : int nattnames;
4669 : PQExpBuffer attribs;
4670 :
4671 144 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4672 : &attnames, &nattnames))
4673 0 : pg_fatal("could not parse %s array", "prattrs");
4674 144 : attribs = createPQExpBuffer();
4675 432 : for (int k = 0; k < nattnames; k++)
4676 : {
4677 288 : if (k > 0)
4678 144 : appendPQExpBufferStr(attribs, ", ");
4679 :
4680 288 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4681 : }
4682 144 : pubrinfo[j].pubrattrs = attribs->data;
4683 : }
4684 : else
4685 366 : pubrinfo[j].pubrattrs = NULL;
4686 :
4687 : /* Decide whether we want to dump it */
4688 510 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4689 :
4690 510 : j++;
4691 : }
4692 :
4693 308 : PQclear(res);
4694 308 : destroyPQExpBuffer(query);
4695 : }
4696 :
4697 : /*
4698 : * dumpPublicationNamespace
4699 : * dump the definition of the given publication schema mapping.
4700 : */
4701 : static void
4702 138 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4703 : {
4704 138 : DumpOptions *dopt = fout->dopt;
4705 138 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4706 138 : PublicationInfo *pubinfo = pubsinfo->publication;
4707 : PQExpBuffer query;
4708 : char *tag;
4709 :
4710 : /* Do nothing if not dumping schema */
4711 138 : if (!dopt->dumpSchema)
4712 12 : return;
4713 :
4714 126 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4715 :
4716 126 : query = createPQExpBuffer();
4717 :
4718 126 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4719 126 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4720 :
4721 : /*
4722 : * There is no point in creating drop query as the drop is done by schema
4723 : * drop.
4724 : */
4725 126 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4726 126 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4727 126 : ARCHIVE_OPTS(.tag = tag,
4728 : .namespace = schemainfo->dobj.name,
4729 : .owner = pubinfo->rolname,
4730 : .description = "PUBLICATION TABLES IN SCHEMA",
4731 : .section = SECTION_POST_DATA,
4732 : .createStmt = query->data));
4733 :
4734 : /* These objects can't currently have comments or seclabels */
4735 :
4736 126 : free(tag);
4737 126 : destroyPQExpBuffer(query);
4738 : }
4739 :
4740 : /*
4741 : * dumpPublicationTable
4742 : * dump the definition of the given publication table mapping
4743 : */
4744 : static void
4745 470 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4746 : {
4747 470 : DumpOptions *dopt = fout->dopt;
4748 470 : PublicationInfo *pubinfo = pubrinfo->publication;
4749 470 : TableInfo *tbinfo = pubrinfo->pubtable;
4750 : PQExpBuffer query;
4751 : char *tag;
4752 :
4753 : /* Do nothing if not dumping schema */
4754 470 : if (!dopt->dumpSchema)
4755 42 : return;
4756 :
4757 428 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4758 :
4759 428 : query = createPQExpBuffer();
4760 :
4761 428 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4762 428 : fmtId(pubinfo->dobj.name));
4763 428 : appendPQExpBuffer(query, " %s",
4764 428 : fmtQualifiedDumpable(tbinfo));
4765 :
4766 428 : if (pubrinfo->pubrattrs)
4767 124 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4768 :
4769 428 : if (pubrinfo->pubrelqual)
4770 : {
4771 : /*
4772 : * It's necessary to add parentheses around the expression because
4773 : * pg_get_expr won't supply the parentheses for things like WHERE
4774 : * TRUE.
4775 : */
4776 184 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4777 : }
4778 428 : appendPQExpBufferStr(query, ";\n");
4779 :
4780 : /*
4781 : * There is no point in creating a drop query as the drop is done by table
4782 : * drop. (If you think to change this, see also _printTocEntry().)
4783 : * Although this object doesn't really have ownership as such, set the
4784 : * owner field anyway to ensure that the command is run by the correct
4785 : * role at restore time.
4786 : */
4787 428 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4788 428 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4789 428 : ARCHIVE_OPTS(.tag = tag,
4790 : .namespace = tbinfo->dobj.namespace->dobj.name,
4791 : .owner = pubinfo->rolname,
4792 : .description = "PUBLICATION TABLE",
4793 : .section = SECTION_POST_DATA,
4794 : .createStmt = query->data));
4795 :
4796 : /* These objects can't currently have comments or seclabels */
4797 :
4798 428 : free(tag);
4799 428 : destroyPQExpBuffer(query);
4800 : }
4801 :
4802 : /*
4803 : * Is the currently connected user a superuser?
4804 : */
4805 : static bool
4806 308 : is_superuser(Archive *fout)
4807 : {
4808 308 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4809 : const char *val;
4810 :
4811 308 : val = PQparameterStatus(AH->connection, "is_superuser");
4812 :
4813 308 : if (val && strcmp(val, "on") == 0)
4814 302 : return true;
4815 :
4816 6 : return false;
4817 : }
4818 :
4819 : /*
4820 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4821 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4822 : * the setting query is effective only where available.
4823 : */
4824 : static void
4825 376 : set_restrict_relation_kind(Archive *AH, const char *value)
4826 : {
4827 376 : PQExpBuffer query = createPQExpBuffer();
4828 : PGresult *res;
4829 :
4830 376 : appendPQExpBuffer(query,
4831 : "SELECT set_config(name, '%s', false) "
4832 : "FROM pg_settings "
4833 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4834 : value);
4835 376 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4836 :
4837 376 : PQclear(res);
4838 376 : destroyPQExpBuffer(query);
4839 376 : }
4840 :
4841 : /*
4842 : * getSubscriptions
4843 : * get information about subscriptions
4844 : */
4845 : void
4846 308 : getSubscriptions(Archive *fout)
4847 : {
4848 308 : DumpOptions *dopt = fout->dopt;
4849 : PQExpBuffer query;
4850 : PGresult *res;
4851 : SubscriptionInfo *subinfo;
4852 : int i_tableoid;
4853 : int i_oid;
4854 : int i_subname;
4855 : int i_subowner;
4856 : int i_subbinary;
4857 : int i_substream;
4858 : int i_subtwophasestate;
4859 : int i_subdisableonerr;
4860 : int i_subpasswordrequired;
4861 : int i_subrunasowner;
4862 : int i_subconninfo;
4863 : int i_subslotname;
4864 : int i_subsynccommit;
4865 : int i_subpublications;
4866 : int i_suborigin;
4867 : int i_suboriginremotelsn;
4868 : int i_subenabled;
4869 : int i_subfailover;
4870 : int i,
4871 : ntups;
4872 :
4873 308 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4874 0 : return;
4875 :
4876 308 : if (!is_superuser(fout))
4877 : {
4878 : int n;
4879 :
4880 6 : res = ExecuteSqlQuery(fout,
4881 : "SELECT count(*) FROM pg_subscription "
4882 : "WHERE subdbid = (SELECT oid FROM pg_database"
4883 : " WHERE datname = current_database())",
4884 : PGRES_TUPLES_OK);
4885 6 : n = atoi(PQgetvalue(res, 0, 0));
4886 6 : if (n > 0)
4887 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4888 6 : PQclear(res);
4889 6 : return;
4890 : }
4891 :
4892 302 : query = createPQExpBuffer();
4893 :
4894 : /* Get the subscriptions in current database. */
4895 302 : appendPQExpBufferStr(query,
4896 : "SELECT s.tableoid, s.oid, s.subname,\n"
4897 : " s.subowner,\n"
4898 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4899 : " s.subpublications,\n");
4900 :
4901 302 : if (fout->remoteVersion >= 140000)
4902 302 : appendPQExpBufferStr(query, " s.subbinary,\n");
4903 : else
4904 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4905 :
4906 302 : if (fout->remoteVersion >= 140000)
4907 302 : appendPQExpBufferStr(query, " s.substream,\n");
4908 : else
4909 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
4910 :
4911 302 : if (fout->remoteVersion >= 150000)
4912 302 : appendPQExpBufferStr(query,
4913 : " s.subtwophasestate,\n"
4914 : " s.subdisableonerr,\n");
4915 : else
4916 0 : appendPQExpBuffer(query,
4917 : " '%c' AS subtwophasestate,\n"
4918 : " false AS subdisableonerr,\n",
4919 : LOGICALREP_TWOPHASE_STATE_DISABLED);
4920 :
4921 302 : if (fout->remoteVersion >= 160000)
4922 302 : appendPQExpBufferStr(query,
4923 : " s.subpasswordrequired,\n"
4924 : " s.subrunasowner,\n"
4925 : " s.suborigin,\n");
4926 : else
4927 0 : appendPQExpBuffer(query,
4928 : " 't' AS subpasswordrequired,\n"
4929 : " 't' AS subrunasowner,\n"
4930 : " '%s' AS suborigin,\n",
4931 : LOGICALREP_ORIGIN_ANY);
4932 :
4933 302 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4934 28 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
4935 : " s.subenabled,\n");
4936 : else
4937 274 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
4938 : " false AS subenabled,\n");
4939 :
4940 302 : if (fout->remoteVersion >= 170000)
4941 302 : appendPQExpBufferStr(query,
4942 : " s.subfailover\n");
4943 : else
4944 0 : appendPQExpBuffer(query,
4945 : " false AS subfailover\n");
4946 :
4947 302 : appendPQExpBufferStr(query,
4948 : "FROM pg_subscription s\n");
4949 :
4950 302 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4951 28 : appendPQExpBufferStr(query,
4952 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
4953 : " ON o.external_id = 'pg_' || s.oid::text \n");
4954 :
4955 302 : appendPQExpBufferStr(query,
4956 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4957 : " WHERE datname = current_database())");
4958 :
4959 302 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4960 :
4961 302 : ntups = PQntuples(res);
4962 :
4963 : /*
4964 : * Get subscription fields. We don't include subskiplsn in the dump as
4965 : * after restoring the dump this value may no longer be relevant.
4966 : */
4967 302 : i_tableoid = PQfnumber(res, "tableoid");
4968 302 : i_oid = PQfnumber(res, "oid");
4969 302 : i_subname = PQfnumber(res, "subname");
4970 302 : i_subowner = PQfnumber(res, "subowner");
4971 302 : i_subenabled = PQfnumber(res, "subenabled");
4972 302 : i_subbinary = PQfnumber(res, "subbinary");
4973 302 : i_substream = PQfnumber(res, "substream");
4974 302 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
4975 302 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
4976 302 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
4977 302 : i_subrunasowner = PQfnumber(res, "subrunasowner");
4978 302 : i_subfailover = PQfnumber(res, "subfailover");
4979 302 : i_subconninfo = PQfnumber(res, "subconninfo");
4980 302 : i_subslotname = PQfnumber(res, "subslotname");
4981 302 : i_subsynccommit = PQfnumber(res, "subsynccommit");
4982 302 : i_subpublications = PQfnumber(res, "subpublications");
4983 302 : i_suborigin = PQfnumber(res, "suborigin");
4984 302 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
4985 :
4986 302 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4987 :
4988 552 : for (i = 0; i < ntups; i++)
4989 : {
4990 250 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4991 250 : subinfo[i].dobj.catId.tableoid =
4992 250 : atooid(PQgetvalue(res, i, i_tableoid));
4993 250 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4994 250 : AssignDumpId(&subinfo[i].dobj);
4995 250 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4996 250 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
4997 :
4998 250 : subinfo[i].subenabled =
4999 250 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5000 250 : subinfo[i].subbinary =
5001 250 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5002 250 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5003 250 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5004 250 : subinfo[i].subdisableonerr =
5005 250 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5006 250 : subinfo[i].subpasswordrequired =
5007 250 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5008 250 : subinfo[i].subrunasowner =
5009 250 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5010 250 : subinfo[i].subfailover =
5011 250 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5012 500 : subinfo[i].subconninfo =
5013 250 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5014 250 : if (PQgetisnull(res, i, i_subslotname))
5015 0 : subinfo[i].subslotname = NULL;
5016 : else
5017 250 : subinfo[i].subslotname =
5018 250 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5019 500 : subinfo[i].subsynccommit =
5020 250 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5021 500 : subinfo[i].subpublications =
5022 250 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5023 250 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5024 250 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5025 248 : subinfo[i].suboriginremotelsn = NULL;
5026 : else
5027 2 : subinfo[i].suboriginremotelsn =
5028 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5029 :
5030 : /* Decide whether we want to dump it */
5031 250 : selectDumpableObject(&(subinfo[i].dobj), fout);
5032 : }
5033 302 : PQclear(res);
5034 :
5035 302 : destroyPQExpBuffer(query);
5036 : }
5037 :
5038 : /*
5039 : * getSubscriptionTables
5040 : * Get information about subscription membership for dumpable tables. This
5041 : * will be used only in binary-upgrade mode for PG17 or later versions.
5042 : */
5043 : void
5044 308 : getSubscriptionTables(Archive *fout)
5045 : {
5046 308 : DumpOptions *dopt = fout->dopt;
5047 308 : SubscriptionInfo *subinfo = NULL;
5048 : SubRelInfo *subrinfo;
5049 : PGresult *res;
5050 : int i_srsubid;
5051 : int i_srrelid;
5052 : int i_srsubstate;
5053 : int i_srsublsn;
5054 : int ntups;
5055 308 : Oid last_srsubid = InvalidOid;
5056 :
5057 308 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5058 28 : fout->remoteVersion < 170000)
5059 280 : return;
5060 :
5061 28 : res = ExecuteSqlQuery(fout,
5062 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5063 : "FROM pg_catalog.pg_subscription_rel "
5064 : "ORDER BY srsubid",
5065 : PGRES_TUPLES_OK);
5066 28 : ntups = PQntuples(res);
5067 28 : if (ntups == 0)
5068 26 : goto cleanup;
5069 :
5070 : /* Get pg_subscription_rel attributes */
5071 2 : i_srsubid = PQfnumber(res, "srsubid");
5072 2 : i_srrelid = PQfnumber(res, "srrelid");
5073 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5074 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5075 :
5076 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5077 6 : for (int i = 0; i < ntups; i++)
5078 : {
5079 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5080 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5081 : TableInfo *tblinfo;
5082 :
5083 : /*
5084 : * If we switched to a new subscription, check if the subscription
5085 : * exists.
5086 : */
5087 4 : if (cur_srsubid != last_srsubid)
5088 : {
5089 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5090 4 : if (subinfo == NULL)
5091 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5092 :
5093 4 : last_srsubid = cur_srsubid;
5094 : }
5095 :
5096 4 : tblinfo = findTableByOid(relid);
5097 4 : if (tblinfo == NULL)
5098 0 : pg_fatal("failed sanity check, table with OID %u not found",
5099 : relid);
5100 :
5101 : /* OK, make a DumpableObject for this relationship */
5102 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5103 4 : subrinfo[i].dobj.catId.tableoid = relid;
5104 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5105 4 : AssignDumpId(&subrinfo[i].dobj);
5106 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5107 4 : subrinfo[i].tblinfo = tblinfo;
5108 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5109 4 : if (PQgetisnull(res, i, i_srsublsn))
5110 2 : subrinfo[i].srsublsn = NULL;
5111 : else
5112 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5113 :
5114 4 : subrinfo[i].subinfo = subinfo;
5115 :
5116 : /* Decide whether we want to dump it */
5117 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5118 : }
5119 :
5120 2 : cleanup:
5121 28 : PQclear(res);
5122 : }
5123 :
5124 : /*
5125 : * dumpSubscriptionTable
5126 : * Dump the definition of the given subscription table mapping. This will be
5127 : * used only in binary-upgrade mode for PG17 or later versions.
5128 : */
5129 : static void
5130 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5131 : {
5132 4 : DumpOptions *dopt = fout->dopt;
5133 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5134 : PQExpBuffer query;
5135 : char *tag;
5136 :
5137 : /* Do nothing if not dumping schema */
5138 4 : if (!dopt->dumpSchema)
5139 0 : return;
5140 :
5141 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5142 :
5143 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5144 :
5145 4 : query = createPQExpBuffer();
5146 :
5147 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5148 : {
5149 : /*
5150 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5151 : * to pg_subscription_rel table. This will be used only in
5152 : * binary-upgrade mode.
5153 : */
5154 4 : appendPQExpBufferStr(query,
5155 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5156 4 : appendPQExpBufferStr(query,
5157 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5158 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5159 4 : appendPQExpBuffer(query,
5160 : ", %u, '%c'",
5161 4 : subrinfo->tblinfo->dobj.catId.oid,
5162 4 : subrinfo->srsubstate);
5163 :
5164 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5165 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5166 : else
5167 2 : appendPQExpBuffer(query, ", NULL");
5168 :
5169 4 : appendPQExpBufferStr(query, ");\n");
5170 : }
5171 :
5172 : /*
5173 : * There is no point in creating a drop query as the drop is done by table
5174 : * drop. (If you think to change this, see also _printTocEntry().)
5175 : * Although this object doesn't really have ownership as such, set the
5176 : * owner field anyway to ensure that the command is run by the correct
5177 : * role at restore time.
5178 : */
5179 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5180 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5181 4 : ARCHIVE_OPTS(.tag = tag,
5182 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5183 : .owner = subinfo->rolname,
5184 : .description = "SUBSCRIPTION TABLE",
5185 : .section = SECTION_POST_DATA,
5186 : .createStmt = query->data));
5187 :
5188 : /* These objects can't currently have comments or seclabels */
5189 :
5190 4 : free(tag);
5191 4 : destroyPQExpBuffer(query);
5192 : }
5193 :
5194 : /*
5195 : * dumpSubscription
5196 : * dump the definition of the given subscription
5197 : */
5198 : static void
5199 214 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5200 : {
5201 214 : DumpOptions *dopt = fout->dopt;
5202 : PQExpBuffer delq;
5203 : PQExpBuffer query;
5204 : PQExpBuffer publications;
5205 : char *qsubname;
5206 214 : char **pubnames = NULL;
5207 214 : int npubnames = 0;
5208 : int i;
5209 :
5210 : /* Do nothing if not dumping schema */
5211 214 : if (!dopt->dumpSchema)
5212 18 : return;
5213 :
5214 196 : delq = createPQExpBuffer();
5215 196 : query = createPQExpBuffer();
5216 :
5217 196 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5218 :
5219 196 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5220 : qsubname);
5221 :
5222 196 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5223 : qsubname);
5224 196 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5225 :
5226 : /* Build list of quoted publications and append them to query. */
5227 196 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5228 0 : pg_fatal("could not parse %s array", "subpublications");
5229 :
5230 196 : publications = createPQExpBuffer();
5231 392 : for (i = 0; i < npubnames; i++)
5232 : {
5233 196 : if (i > 0)
5234 0 : appendPQExpBufferStr(publications, ", ");
5235 :
5236 196 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5237 : }
5238 :
5239 196 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5240 196 : if (subinfo->subslotname)
5241 196 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5242 : else
5243 0 : appendPQExpBufferStr(query, "NONE");
5244 :
5245 196 : if (subinfo->subbinary)
5246 0 : appendPQExpBufferStr(query, ", binary = true");
5247 :
5248 196 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5249 64 : appendPQExpBufferStr(query, ", streaming = on");
5250 132 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5251 68 : appendPQExpBufferStr(query, ", streaming = parallel");
5252 : else
5253 64 : appendPQExpBufferStr(query, ", streaming = off");
5254 :
5255 196 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5256 0 : appendPQExpBufferStr(query, ", two_phase = on");
5257 :
5258 196 : if (subinfo->subdisableonerr)
5259 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5260 :
5261 196 : if (!subinfo->subpasswordrequired)
5262 0 : appendPQExpBuffer(query, ", password_required = false");
5263 :
5264 196 : if (subinfo->subrunasowner)
5265 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5266 :
5267 196 : if (subinfo->subfailover)
5268 2 : appendPQExpBufferStr(query, ", failover = true");
5269 :
5270 196 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5271 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5272 :
5273 196 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5274 64 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5275 :
5276 196 : appendPQExpBufferStr(query, ");\n");
5277 :
5278 : /*
5279 : * In binary-upgrade mode, we allow the replication to continue after the
5280 : * upgrade.
5281 : */
5282 196 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5283 : {
5284 10 : if (subinfo->suboriginremotelsn)
5285 : {
5286 : /*
5287 : * Preserve the remote_lsn for the subscriber's replication
5288 : * origin. This value is required to start the replication from
5289 : * the position before the upgrade. This value will be stale if
5290 : * the publisher gets upgraded before the subscriber node.
5291 : * However, this shouldn't be a problem as the upgrade of the
5292 : * publisher ensures that all the transactions were replicated
5293 : * before upgrading it.
5294 : */
5295 2 : appendPQExpBufferStr(query,
5296 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5297 2 : appendPQExpBufferStr(query,
5298 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5299 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5300 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5301 : }
5302 :
5303 10 : if (subinfo->subenabled)
5304 : {
5305 : /*
5306 : * Enable the subscription to allow the replication to continue
5307 : * after the upgrade.
5308 : */
5309 2 : appendPQExpBufferStr(query,
5310 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5311 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5312 : }
5313 : }
5314 :
5315 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5316 196 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5317 196 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5318 : .owner = subinfo->rolname,
5319 : .description = "SUBSCRIPTION",
5320 : .section = SECTION_POST_DATA,
5321 : .createStmt = query->data,
5322 : .dropStmt = delq->data));
5323 :
5324 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5325 64 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5326 : NULL, subinfo->rolname,
5327 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5328 :
5329 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5330 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5331 : NULL, subinfo->rolname,
5332 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5333 :
5334 196 : destroyPQExpBuffer(publications);
5335 196 : free(pubnames);
5336 :
5337 196 : destroyPQExpBuffer(delq);
5338 196 : destroyPQExpBuffer(query);
5339 196 : free(qsubname);
5340 : }
5341 :
5342 : /*
5343 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5344 : * the object needs.
5345 : */
5346 : static void
5347 9756 : append_depends_on_extension(Archive *fout,
5348 : PQExpBuffer create,
5349 : const DumpableObject *dobj,
5350 : const char *catalog,
5351 : const char *keyword,
5352 : const char *objname)
5353 : {
5354 9756 : if (dobj->depends_on_ext)
5355 : {
5356 : char *nm;
5357 : PGresult *res;
5358 : PQExpBuffer query;
5359 : int ntups;
5360 : int i_extname;
5361 : int i;
5362 :
5363 : /* dodge fmtId() non-reentrancy */
5364 84 : nm = pg_strdup(objname);
5365 :
5366 84 : query = createPQExpBuffer();
5367 84 : appendPQExpBuffer(query,
5368 : "SELECT e.extname "
5369 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5370 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5371 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5372 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5373 : catalog,
5374 : dobj->catId.oid);
5375 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5376 84 : ntups = PQntuples(res);
5377 84 : i_extname = PQfnumber(res, "extname");
5378 168 : for (i = 0; i < ntups; i++)
5379 : {
5380 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5381 : keyword, nm,
5382 84 : fmtId(PQgetvalue(res, i, i_extname)));
5383 : }
5384 :
5385 84 : PQclear(res);
5386 84 : destroyPQExpBuffer(query);
5387 84 : pg_free(nm);
5388 : }
5389 9756 : }
5390 :
5391 : static Oid
5392 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5393 : {
5394 : /*
5395 : * If the old version didn't assign an array type, but the new version
5396 : * does, we must select an unused type OID to assign. This currently only
5397 : * happens for domains, when upgrading pre-v11 to v11 and up.
5398 : *
5399 : * Note: local state here is kind of ugly, but we must have some, since we
5400 : * mustn't choose the same unused OID more than once.
5401 : */
5402 : static Oid next_possible_free_oid = FirstNormalObjectId;
5403 : PGresult *res;
5404 : bool is_dup;
5405 :
5406 : do
5407 : {
5408 0 : ++next_possible_free_oid;
5409 0 : printfPQExpBuffer(upgrade_query,
5410 : "SELECT EXISTS(SELECT 1 "
5411 : "FROM pg_catalog.pg_type "
5412 : "WHERE oid = '%u'::pg_catalog.oid);",
5413 : next_possible_free_oid);
5414 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5415 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5416 0 : PQclear(res);
5417 0 : } while (is_dup);
5418 :
5419 0 : return next_possible_free_oid;
5420 : }
5421 :
5422 : static void
5423 1660 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5424 : PQExpBuffer upgrade_buffer,
5425 : Oid pg_type_oid,
5426 : bool force_array_type,
5427 : bool include_multirange_type)
5428 : {
5429 1660 : PQExpBuffer upgrade_query = createPQExpBuffer();
5430 : PGresult *res;
5431 : Oid pg_type_array_oid;
5432 : Oid pg_type_multirange_oid;
5433 : Oid pg_type_multirange_array_oid;
5434 : TypeInfo *tinfo;
5435 :
5436 1660 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5437 1660 : appendPQExpBuffer(upgrade_buffer,
5438 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5439 : pg_type_oid);
5440 :
5441 1660 : tinfo = findTypeByOid(pg_type_oid);
5442 1660 : if (tinfo)
5443 1660 : pg_type_array_oid = tinfo->typarray;
5444 : else
5445 0 : pg_type_array_oid = InvalidOid;
5446 :
5447 1660 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5448 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5449 :
5450 1660 : if (OidIsValid(pg_type_array_oid))
5451 : {
5452 1656 : appendPQExpBufferStr(upgrade_buffer,
5453 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5454 1656 : appendPQExpBuffer(upgrade_buffer,
5455 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5456 : pg_type_array_oid);
5457 : }
5458 :
5459 : /*
5460 : * Pre-set the multirange type oid and its own array type oid.
5461 : */
5462 1660 : if (include_multirange_type)
5463 : {
5464 12 : if (fout->remoteVersion >= 140000)
5465 : {
5466 12 : printfPQExpBuffer(upgrade_query,
5467 : "SELECT t.oid, t.typarray "
5468 : "FROM pg_catalog.pg_type t "
5469 : "JOIN pg_catalog.pg_range r "
5470 : "ON t.oid = r.rngmultitypid "
5471 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5472 : pg_type_oid);
5473 :
5474 12 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5475 :
5476 12 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5477 12 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5478 :
5479 12 : PQclear(res);
5480 : }
5481 : else
5482 : {
5483 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5484 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5485 : }
5486 :
5487 12 : appendPQExpBufferStr(upgrade_buffer,
5488 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5489 12 : appendPQExpBuffer(upgrade_buffer,
5490 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5491 : pg_type_multirange_oid);
5492 12 : appendPQExpBufferStr(upgrade_buffer,
5493 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5494 12 : appendPQExpBuffer(upgrade_buffer,
5495 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5496 : pg_type_multirange_array_oid);
5497 : }
5498 :
5499 1660 : destroyPQExpBuffer(upgrade_query);
5500 1660 : }
5501 :
5502 : static void
5503 1524 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5504 : PQExpBuffer upgrade_buffer,
5505 : const TableInfo *tbinfo)
5506 : {
5507 1524 : Oid pg_type_oid = tbinfo->reltype;
5508 :
5509 1524 : if (OidIsValid(pg_type_oid))
5510 1524 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5511 : pg_type_oid, false, false);
5512 1524 : }
5513 :
5514 : /*
5515 : * bsearch() comparator for BinaryUpgradeClassOidItem
5516 : */
5517 : static int
5518 21832 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5519 : {
5520 21832 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5521 21832 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5522 :
5523 21832 : return pg_cmp_u32(v1.oid, v2.oid);
5524 : }
5525 :
5526 : /*
5527 : * collectBinaryUpgradeClassOids
5528 : *
5529 : * Construct a table of pg_class information required for
5530 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5531 : * lookup.
5532 : */
5533 : static void
5534 28 : collectBinaryUpgradeClassOids(Archive *fout)
5535 : {
5536 : PGresult *res;
5537 : const char *query;
5538 :
5539 28 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5540 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5541 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5542 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5543 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5544 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5545 : "ORDER BY c.oid;";
5546 :
5547 28 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5548 :
5549 28 : nbinaryUpgradeClassOids = PQntuples(res);
5550 28 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5551 28 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5552 :
5553 14966 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5554 : {
5555 14938 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5556 14938 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5557 14938 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5558 14938 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5559 14938 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5560 14938 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5561 14938 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5562 : }
5563 :
5564 28 : PQclear(res);
5565 28 : }
5566 :
5567 : static void
5568 2222 : binary_upgrade_set_pg_class_oids(Archive *fout,
5569 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5570 : {
5571 2222 : BinaryUpgradeClassOidItem key = {0};
5572 : BinaryUpgradeClassOidItem *entry;
5573 :
5574 : Assert(binaryUpgradeClassOids);
5575 :
5576 : /*
5577 : * Preserve the OID and relfilenumber of the table, table's index, table's
5578 : * toast table and toast table's index if any.
5579 : *
5580 : * One complexity is that the current table definition might not require
5581 : * the creation of a TOAST table, but the old database might have a TOAST
5582 : * table that was created earlier, before some wide columns were dropped.
5583 : * By setting the TOAST oid we force creation of the TOAST heap and index
5584 : * by the new backend, so we can copy the files during binary upgrade
5585 : * without worrying about this case.
5586 : */
5587 2222 : key.oid = pg_class_oid;
5588 2222 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5589 : sizeof(BinaryUpgradeClassOidItem),
5590 : BinaryUpgradeClassOidItemCmp);
5591 :
5592 2222 : appendPQExpBufferStr(upgrade_buffer,
5593 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5594 :
5595 2222 : if (entry->relkind != RELKIND_INDEX &&
5596 1722 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5597 : {
5598 1672 : appendPQExpBuffer(upgrade_buffer,
5599 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5600 : pg_class_oid);
5601 :
5602 : /*
5603 : * Not every relation has storage. Also, in a pre-v12 database,
5604 : * partitioned tables have a relfilenumber, which should not be
5605 : * preserved when upgrading.
5606 : */
5607 1672 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5608 1364 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5609 1364 : appendPQExpBuffer(upgrade_buffer,
5610 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5611 : entry->relfilenumber);
5612 :
5613 : /*
5614 : * In a pre-v12 database, partitioned tables might be marked as having
5615 : * toast tables, but we should ignore them if so.
5616 : */
5617 1672 : if (OidIsValid(entry->toast_oid) &&
5618 548 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5619 : {
5620 548 : appendPQExpBuffer(upgrade_buffer,
5621 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5622 : entry->toast_oid);
5623 548 : appendPQExpBuffer(upgrade_buffer,
5624 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5625 : entry->toast_relfilenumber);
5626 :
5627 : /* every toast table has an index */
5628 548 : appendPQExpBuffer(upgrade_buffer,
5629 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5630 : entry->toast_index_oid);
5631 548 : appendPQExpBuffer(upgrade_buffer,
5632 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5633 : entry->toast_index_relfilenumber);
5634 : }
5635 : }
5636 : else
5637 : {
5638 : /* Preserve the OID and relfilenumber of the index */
5639 550 : appendPQExpBuffer(upgrade_buffer,
5640 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5641 : pg_class_oid);
5642 550 : appendPQExpBuffer(upgrade_buffer,
5643 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5644 : entry->relfilenumber);
5645 : }
5646 :
5647 2222 : appendPQExpBufferChar(upgrade_buffer, '\n');
5648 2222 : }
5649 :
5650 : /*
5651 : * If the DumpableObject is a member of an extension, add a suitable
5652 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5653 : *
5654 : * For somewhat historical reasons, objname should already be quoted,
5655 : * but not objnamespace (if any).
5656 : */
5657 : static void
5658 2650 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5659 : const DumpableObject *dobj,
5660 : const char *objtype,
5661 : const char *objname,
5662 : const char *objnamespace)
5663 : {
5664 2650 : DumpableObject *extobj = NULL;
5665 : int i;
5666 :
5667 2650 : if (!dobj->ext_member)
5668 2618 : return;
5669 :
5670 : /*
5671 : * Find the parent extension. We could avoid this search if we wanted to
5672 : * add a link field to DumpableObject, but the space costs of that would
5673 : * be considerable. We assume that member objects could only have a
5674 : * direct dependency on their own extension, not any others.
5675 : */
5676 32 : for (i = 0; i < dobj->nDeps; i++)
5677 : {
5678 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5679 32 : if (extobj && extobj->objType == DO_EXTENSION)
5680 32 : break;
5681 0 : extobj = NULL;
5682 : }
5683 32 : if (extobj == NULL)
5684 0 : pg_fatal("could not find parent extension for %s %s",
5685 : objtype, objname);
5686 :
5687 32 : appendPQExpBufferStr(upgrade_buffer,
5688 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5689 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5690 32 : fmtId(extobj->name),
5691 : objtype);
5692 32 : if (objnamespace && *objnamespace)
5693 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5694 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5695 : }
5696 :
5697 : /*
5698 : * getNamespaces:
5699 : * get information about all namespaces in the system catalogs
5700 : */
5701 : void
5702 310 : getNamespaces(Archive *fout)
5703 : {
5704 : PGresult *res;
5705 : int ntups;
5706 : int i;
5707 : PQExpBuffer query;
5708 : NamespaceInfo *nsinfo;
5709 : int i_tableoid;
5710 : int i_oid;
5711 : int i_nspname;
5712 : int i_nspowner;
5713 : int i_nspacl;
5714 : int i_acldefault;
5715 :
5716 310 : query = createPQExpBuffer();
5717 :
5718 : /*
5719 : * we fetch all namespaces including system ones, so that every object we
5720 : * read in can be linked to a containing namespace.
5721 : */
5722 310 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5723 : "n.nspowner, "
5724 : "n.nspacl, "
5725 : "acldefault('n', n.nspowner) AS acldefault "
5726 : "FROM pg_namespace n");
5727 :
5728 310 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5729 :
5730 310 : ntups = PQntuples(res);
5731 :
5732 310 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5733 :
5734 310 : i_tableoid = PQfnumber(res, "tableoid");
5735 310 : i_oid = PQfnumber(res, "oid");
5736 310 : i_nspname = PQfnumber(res, "nspname");
5737 310 : i_nspowner = PQfnumber(res, "nspowner");
5738 310 : i_nspacl = PQfnumber(res, "nspacl");
5739 310 : i_acldefault = PQfnumber(res, "acldefault");
5740 :
5741 2820 : for (i = 0; i < ntups; i++)
5742 : {
5743 : const char *nspowner;
5744 :
5745 2510 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5746 2510 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5747 2510 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5748 2510 : AssignDumpId(&nsinfo[i].dobj);
5749 2510 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5750 2510 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5751 2510 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5752 2510 : nsinfo[i].dacl.privtype = 0;
5753 2510 : nsinfo[i].dacl.initprivs = NULL;
5754 2510 : nspowner = PQgetvalue(res, i, i_nspowner);
5755 2510 : nsinfo[i].nspowner = atooid(nspowner);
5756 2510 : nsinfo[i].rolname = getRoleName(nspowner);
5757 :
5758 : /* Decide whether to dump this namespace */
5759 2510 : selectDumpableNamespace(&nsinfo[i], fout);
5760 :
5761 : /* Mark whether namespace has an ACL */
5762 2510 : if (!PQgetisnull(res, i, i_nspacl))
5763 1036 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5764 :
5765 : /*
5766 : * We ignore any pg_init_privs.initprivs entry for the public schema
5767 : * and assume a predetermined default, for several reasons. First,
5768 : * dropping and recreating the schema removes its pg_init_privs entry,
5769 : * but an empty destination database starts with this ACL nonetheless.
5770 : * Second, we support dump/reload of public schema ownership changes.
5771 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5772 : * initprivs continues to reflect the initial owner. Hence,
5773 : * synthesize the value that nspacl will have after the restore's
5774 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5775 : * match the source's ACL, even if the latter was an initdb-default
5776 : * ACL, which changed in v15. An upgrade pulls in changes to most
5777 : * system object ACLs that the DBA had not customized. We've made the
5778 : * public schema depart from that, because changing its ACL so easily
5779 : * breaks applications.
5780 : */
5781 2510 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5782 : {
5783 302 : PQExpBuffer aclarray = createPQExpBuffer();
5784 302 : PQExpBuffer aclitem = createPQExpBuffer();
5785 :
5786 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5787 302 : appendPQExpBufferChar(aclarray, '{');
5788 302 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5789 302 : appendPQExpBufferStr(aclitem, "=UC/");
5790 302 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5791 302 : appendPGArray(aclarray, aclitem->data);
5792 302 : resetPQExpBuffer(aclitem);
5793 302 : appendPQExpBufferStr(aclitem, "=U/");
5794 302 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5795 302 : appendPGArray(aclarray, aclitem->data);
5796 302 : appendPQExpBufferChar(aclarray, '}');
5797 :
5798 302 : nsinfo[i].dacl.privtype = 'i';
5799 302 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5800 302 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5801 :
5802 302 : destroyPQExpBuffer(aclarray);
5803 302 : destroyPQExpBuffer(aclitem);
5804 : }
5805 : }
5806 :
5807 310 : PQclear(res);
5808 310 : destroyPQExpBuffer(query);
5809 310 : }
5810 :
5811 : /*
5812 : * findNamespace:
5813 : * given a namespace OID, look up the info read by getNamespaces
5814 : */
5815 : static NamespaceInfo *
5816 963862 : findNamespace(Oid nsoid)
5817 : {
5818 : NamespaceInfo *nsinfo;
5819 :
5820 963862 : nsinfo = findNamespaceByOid(nsoid);
5821 963862 : if (nsinfo == NULL)
5822 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5823 963862 : return nsinfo;
5824 : }
5825 :
5826 : /*
5827 : * getExtensions:
5828 : * read all extensions in the system catalogs and return them in the
5829 : * ExtensionInfo* structure
5830 : *
5831 : * numExtensions is set to the number of extensions read in
5832 : */
5833 : ExtensionInfo *
5834 310 : getExtensions(Archive *fout, int *numExtensions)
5835 : {
5836 310 : DumpOptions *dopt = fout->dopt;
5837 : PGresult *res;
5838 : int ntups;
5839 : int i;
5840 : PQExpBuffer query;
5841 310 : ExtensionInfo *extinfo = NULL;
5842 : int i_tableoid;
5843 : int i_oid;
5844 : int i_extname;
5845 : int i_nspname;
5846 : int i_extrelocatable;
5847 : int i_extversion;
5848 : int i_extconfig;
5849 : int i_extcondition;
5850 :
5851 310 : query = createPQExpBuffer();
5852 :
5853 310 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5854 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5855 : "FROM pg_extension x "
5856 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5857 :
5858 310 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5859 :
5860 310 : ntups = PQntuples(res);
5861 310 : if (ntups == 0)
5862 0 : goto cleanup;
5863 :
5864 310 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5865 :
5866 310 : i_tableoid = PQfnumber(res, "tableoid");
5867 310 : i_oid = PQfnumber(res, "oid");
5868 310 : i_extname = PQfnumber(res, "extname");
5869 310 : i_nspname = PQfnumber(res, "nspname");
5870 310 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5871 310 : i_extversion = PQfnumber(res, "extversion");
5872 310 : i_extconfig = PQfnumber(res, "extconfig");
5873 310 : i_extcondition = PQfnumber(res, "extcondition");
5874 :
5875 670 : for (i = 0; i < ntups; i++)
5876 : {
5877 360 : extinfo[i].dobj.objType = DO_EXTENSION;
5878 360 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5879 360 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5880 360 : AssignDumpId(&extinfo[i].dobj);
5881 360 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5882 360 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5883 360 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5884 360 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5885 360 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5886 360 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5887 :
5888 : /* Decide whether we want to dump it */
5889 360 : selectDumpableExtension(&(extinfo[i]), dopt);
5890 : }
5891 :
5892 310 : cleanup:
5893 310 : PQclear(res);
5894 310 : destroyPQExpBuffer(query);
5895 :
5896 310 : *numExtensions = ntups;
5897 :
5898 310 : return extinfo;
5899 : }
5900 :
5901 : /*
5902 : * getTypes:
5903 : * get information about all types in the system catalogs
5904 : *
5905 : * NB: this must run after getFuncs() because we assume we can do
5906 : * findFuncByOid().
5907 : */
5908 : void
5909 308 : getTypes(Archive *fout)
5910 : {
5911 : PGresult *res;
5912 : int ntups;
5913 : int i;
5914 308 : PQExpBuffer query = createPQExpBuffer();
5915 : TypeInfo *tyinfo;
5916 : ShellTypeInfo *stinfo;
5917 : int i_tableoid;
5918 : int i_oid;
5919 : int i_typname;
5920 : int i_typnamespace;
5921 : int i_typacl;
5922 : int i_acldefault;
5923 : int i_typowner;
5924 : int i_typelem;
5925 : int i_typrelid;
5926 : int i_typrelkind;
5927 : int i_typtype;
5928 : int i_typisdefined;
5929 : int i_isarray;
5930 : int i_typarray;
5931 :
5932 : /*
5933 : * we include even the built-in types because those may be used as array
5934 : * elements by user-defined types
5935 : *
5936 : * we filter out the built-in types when we dump out the types
5937 : *
5938 : * same approach for undefined (shell) types and array types
5939 : *
5940 : * Note: as of 8.3 we can reliably detect whether a type is an
5941 : * auto-generated array type by checking the element type's typarray.
5942 : * (Before that the test is capable of generating false positives.) We
5943 : * still check for name beginning with '_', though, so as to avoid the
5944 : * cost of the subselect probe for all standard types. This would have to
5945 : * be revisited if the backend ever allows renaming of array types.
5946 : */
5947 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5948 : "typnamespace, typacl, "
5949 : "acldefault('T', typowner) AS acldefault, "
5950 : "typowner, "
5951 : "typelem, typrelid, typarray, "
5952 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5953 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5954 : "typtype, typisdefined, "
5955 : "typname[0] = '_' AND typelem != 0 AND "
5956 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5957 : "FROM pg_type");
5958 :
5959 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5960 :
5961 308 : ntups = PQntuples(res);
5962 :
5963 308 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5964 :
5965 308 : i_tableoid = PQfnumber(res, "tableoid");
5966 308 : i_oid = PQfnumber(res, "oid");
5967 308 : i_typname = PQfnumber(res, "typname");
5968 308 : i_typnamespace = PQfnumber(res, "typnamespace");
5969 308 : i_typacl = PQfnumber(res, "typacl");
5970 308 : i_acldefault = PQfnumber(res, "acldefault");
5971 308 : i_typowner = PQfnumber(res, "typowner");
5972 308 : i_typelem = PQfnumber(res, "typelem");
5973 308 : i_typrelid = PQfnumber(res, "typrelid");
5974 308 : i_typrelkind = PQfnumber(res, "typrelkind");
5975 308 : i_typtype = PQfnumber(res, "typtype");
5976 308 : i_typisdefined = PQfnumber(res, "typisdefined");
5977 308 : i_isarray = PQfnumber(res, "isarray");
5978 308 : i_typarray = PQfnumber(res, "typarray");
5979 :
5980 222248 : for (i = 0; i < ntups; i++)
5981 : {
5982 221940 : tyinfo[i].dobj.objType = DO_TYPE;
5983 221940 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5984 221940 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5985 221940 : AssignDumpId(&tyinfo[i].dobj);
5986 221940 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
5987 443880 : tyinfo[i].dobj.namespace =
5988 221940 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
5989 221940 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
5990 221940 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5991 221940 : tyinfo[i].dacl.privtype = 0;
5992 221940 : tyinfo[i].dacl.initprivs = NULL;
5993 221940 : tyinfo[i].ftypname = NULL; /* may get filled later */
5994 221940 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
5995 221940 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
5996 221940 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
5997 221940 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
5998 221940 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
5999 221940 : tyinfo[i].shellType = NULL;
6000 :
6001 221940 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6002 221840 : tyinfo[i].isDefined = true;
6003 : else
6004 100 : tyinfo[i].isDefined = false;
6005 :
6006 221940 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6007 106454 : tyinfo[i].isArray = true;
6008 : else
6009 115486 : tyinfo[i].isArray = false;
6010 :
6011 221940 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6012 :
6013 221940 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6014 2076 : tyinfo[i].isMultirange = true;
6015 : else
6016 219864 : tyinfo[i].isMultirange = false;
6017 :
6018 : /* Decide whether we want to dump it */
6019 221940 : selectDumpableType(&tyinfo[i], fout);
6020 :
6021 : /* Mark whether type has an ACL */
6022 221940 : if (!PQgetisnull(res, i, i_typacl))
6023 394 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6024 :
6025 : /*
6026 : * If it's a domain, fetch info about its constraints, if any
6027 : */
6028 221940 : tyinfo[i].nDomChecks = 0;
6029 221940 : tyinfo[i].domChecks = NULL;
6030 221940 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6031 26350 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6032 272 : getDomainConstraints(fout, &(tyinfo[i]));
6033 :
6034 : /*
6035 : * If it's a base type, make a DumpableObject representing a shell
6036 : * definition of the type. We will need to dump that ahead of the I/O
6037 : * functions for the type. Similarly, range types need a shell
6038 : * definition in case they have a canonicalize function.
6039 : *
6040 : * Note: the shell type doesn't have a catId. You might think it
6041 : * should copy the base type's catId, but then it might capture the
6042 : * pg_depend entries for the type, which we don't want.
6043 : */
6044 221940 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6045 26350 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6046 12756 : tyinfo[i].typtype == TYPTYPE_RANGE))
6047 : {
6048 13814 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6049 13814 : stinfo->dobj.objType = DO_SHELL_TYPE;
6050 13814 : stinfo->dobj.catId = nilCatalogId;
6051 13814 : AssignDumpId(&stinfo->dobj);
6052 13814 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6053 13814 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6054 13814 : stinfo->baseType = &(tyinfo[i]);
6055 13814 : tyinfo[i].shellType = stinfo;
6056 :
6057 : /*
6058 : * Initially mark the shell type as not to be dumped. We'll only
6059 : * dump it if the I/O or canonicalize functions need to be dumped;
6060 : * this is taken care of while sorting dependencies.
6061 : */
6062 13814 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6063 : }
6064 : }
6065 :
6066 308 : PQclear(res);
6067 :
6068 308 : destroyPQExpBuffer(query);
6069 308 : }
6070 :
6071 : /*
6072 : * getOperators:
6073 : * get information about all operators in the system catalogs
6074 : */
6075 : void
6076 308 : getOperators(Archive *fout)
6077 : {
6078 : PGresult *res;
6079 : int ntups;
6080 : int i;
6081 308 : PQExpBuffer query = createPQExpBuffer();
6082 : OprInfo *oprinfo;
6083 : int i_tableoid;
6084 : int i_oid;
6085 : int i_oprname;
6086 : int i_oprnamespace;
6087 : int i_oprowner;
6088 : int i_oprkind;
6089 : int i_oprcode;
6090 :
6091 : /*
6092 : * find all operators, including builtin operators; we filter out
6093 : * system-defined operators at dump-out time.
6094 : */
6095 :
6096 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6097 : "oprnamespace, "
6098 : "oprowner, "
6099 : "oprkind, "
6100 : "oprcode::oid AS oprcode "
6101 : "FROM pg_operator");
6102 :
6103 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6104 :
6105 308 : ntups = PQntuples(res);
6106 :
6107 308 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6108 :
6109 308 : i_tableoid = PQfnumber(res, "tableoid");
6110 308 : i_oid = PQfnumber(res, "oid");
6111 308 : i_oprname = PQfnumber(res, "oprname");
6112 308 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6113 308 : i_oprowner = PQfnumber(res, "oprowner");
6114 308 : i_oprkind = PQfnumber(res, "oprkind");
6115 308 : i_oprcode = PQfnumber(res, "oprcode");
6116 :
6117 246680 : for (i = 0; i < ntups; i++)
6118 : {
6119 246372 : oprinfo[i].dobj.objType = DO_OPERATOR;
6120 246372 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6121 246372 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6122 246372 : AssignDumpId(&oprinfo[i].dobj);
6123 246372 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6124 492744 : oprinfo[i].dobj.namespace =
6125 246372 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6126 246372 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6127 246372 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6128 246372 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6129 :
6130 : /* Decide whether we want to dump it */
6131 246372 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6132 : }
6133 :
6134 308 : PQclear(res);
6135 :
6136 308 : destroyPQExpBuffer(query);
6137 308 : }
6138 :
6139 : /*
6140 : * getCollations:
6141 : * get information about all collations in the system catalogs
6142 : */
6143 : void
6144 308 : getCollations(Archive *fout)
6145 : {
6146 : PGresult *res;
6147 : int ntups;
6148 : int i;
6149 : PQExpBuffer query;
6150 : CollInfo *collinfo;
6151 : int i_tableoid;
6152 : int i_oid;
6153 : int i_collname;
6154 : int i_collnamespace;
6155 : int i_collowner;
6156 :
6157 308 : query = createPQExpBuffer();
6158 :
6159 : /*
6160 : * find all collations, including builtin collations; we filter out
6161 : * system-defined collations at dump-out time.
6162 : */
6163 :
6164 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6165 : "collnamespace, "
6166 : "collowner "
6167 : "FROM pg_collation");
6168 :
6169 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6170 :
6171 308 : ntups = PQntuples(res);
6172 :
6173 308 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6174 :
6175 308 : i_tableoid = PQfnumber(res, "tableoid");
6176 308 : i_oid = PQfnumber(res, "oid");
6177 308 : i_collname = PQfnumber(res, "collname");
6178 308 : i_collnamespace = PQfnumber(res, "collnamespace");
6179 308 : i_collowner = PQfnumber(res, "collowner");
6180 :
6181 244766 : for (i = 0; i < ntups; i++)
6182 : {
6183 244458 : collinfo[i].dobj.objType = DO_COLLATION;
6184 244458 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6185 244458 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6186 244458 : AssignDumpId(&collinfo[i].dobj);
6187 244458 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6188 488916 : collinfo[i].dobj.namespace =
6189 244458 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6190 244458 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6191 :
6192 : /* Decide whether we want to dump it */
6193 244458 : selectDumpableObject(&(collinfo[i].dobj), fout);
6194 : }
6195 :
6196 308 : PQclear(res);
6197 :
6198 308 : destroyPQExpBuffer(query);
6199 308 : }
6200 :
6201 : /*
6202 : * getConversions:
6203 : * get information about all conversions in the system catalogs
6204 : */
6205 : void
6206 308 : getConversions(Archive *fout)
6207 : {
6208 : PGresult *res;
6209 : int ntups;
6210 : int i;
6211 : PQExpBuffer query;
6212 : ConvInfo *convinfo;
6213 : int i_tableoid;
6214 : int i_oid;
6215 : int i_conname;
6216 : int i_connamespace;
6217 : int i_conowner;
6218 :
6219 308 : query = createPQExpBuffer();
6220 :
6221 : /*
6222 : * find all conversions, including builtin conversions; we filter out
6223 : * system-defined conversions at dump-out time.
6224 : */
6225 :
6226 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6227 : "connamespace, "
6228 : "conowner "
6229 : "FROM pg_conversion");
6230 :
6231 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6232 :
6233 308 : ntups = PQntuples(res);
6234 :
6235 308 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6236 :
6237 308 : i_tableoid = PQfnumber(res, "tableoid");
6238 308 : i_oid = PQfnumber(res, "oid");
6239 308 : i_conname = PQfnumber(res, "conname");
6240 308 : i_connamespace = PQfnumber(res, "connamespace");
6241 308 : i_conowner = PQfnumber(res, "conowner");
6242 :
6243 39818 : for (i = 0; i < ntups; i++)
6244 : {
6245 39510 : convinfo[i].dobj.objType = DO_CONVERSION;
6246 39510 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6247 39510 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6248 39510 : AssignDumpId(&convinfo[i].dobj);
6249 39510 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6250 79020 : convinfo[i].dobj.namespace =
6251 39510 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6252 39510 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6253 :
6254 : /* Decide whether we want to dump it */
6255 39510 : selectDumpableObject(&(convinfo[i].dobj), fout);
6256 : }
6257 :
6258 308 : PQclear(res);
6259 :
6260 308 : destroyPQExpBuffer(query);
6261 308 : }
6262 :
6263 : /*
6264 : * getAccessMethods:
6265 : * get information about all user-defined access methods
6266 : */
6267 : void
6268 308 : getAccessMethods(Archive *fout)
6269 : {
6270 : PGresult *res;
6271 : int ntups;
6272 : int i;
6273 : PQExpBuffer query;
6274 : AccessMethodInfo *aminfo;
6275 : int i_tableoid;
6276 : int i_oid;
6277 : int i_amname;
6278 : int i_amhandler;
6279 : int i_amtype;
6280 :
6281 : /* Before 9.6, there are no user-defined access methods */
6282 308 : if (fout->remoteVersion < 90600)
6283 0 : return;
6284 :
6285 308 : query = createPQExpBuffer();
6286 :
6287 : /* Select all access methods from pg_am table */
6288 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6289 : "amhandler::pg_catalog.regproc AS amhandler "
6290 : "FROM pg_am");
6291 :
6292 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6293 :
6294 308 : ntups = PQntuples(res);
6295 :
6296 308 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6297 :
6298 308 : i_tableoid = PQfnumber(res, "tableoid");
6299 308 : i_oid = PQfnumber(res, "oid");
6300 308 : i_amname = PQfnumber(res, "amname");
6301 308 : i_amhandler = PQfnumber(res, "amhandler");
6302 308 : i_amtype = PQfnumber(res, "amtype");
6303 :
6304 2700 : for (i = 0; i < ntups; i++)
6305 : {
6306 2392 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6307 2392 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6308 2392 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6309 2392 : AssignDumpId(&aminfo[i].dobj);
6310 2392 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6311 2392 : aminfo[i].dobj.namespace = NULL;
6312 2392 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6313 2392 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6314 :
6315 : /* Decide whether we want to dump it */
6316 2392 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6317 : }
6318 :
6319 308 : PQclear(res);
6320 :
6321 308 : destroyPQExpBuffer(query);
6322 : }
6323 :
6324 :
6325 : /*
6326 : * getOpclasses:
6327 : * get information about all opclasses in the system catalogs
6328 : */
6329 : void
6330 308 : getOpclasses(Archive *fout)
6331 : {
6332 : PGresult *res;
6333 : int ntups;
6334 : int i;
6335 308 : PQExpBuffer query = createPQExpBuffer();
6336 : OpclassInfo *opcinfo;
6337 : int i_tableoid;
6338 : int i_oid;
6339 : int i_opcname;
6340 : int i_opcnamespace;
6341 : int i_opcowner;
6342 :
6343 : /*
6344 : * find all opclasses, including builtin opclasses; we filter out
6345 : * system-defined opclasses at dump-out time.
6346 : */
6347 :
6348 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6349 : "opcnamespace, "
6350 : "opcowner "
6351 : "FROM pg_opclass");
6352 :
6353 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6354 :
6355 308 : ntups = PQntuples(res);
6356 :
6357 308 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6358 :
6359 308 : i_tableoid = PQfnumber(res, "tableoid");
6360 308 : i_oid = PQfnumber(res, "oid");
6361 308 : i_opcname = PQfnumber(res, "opcname");
6362 308 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6363 308 : i_opcowner = PQfnumber(res, "opcowner");
6364 :
6365 55124 : for (i = 0; i < ntups; i++)
6366 : {
6367 54816 : opcinfo[i].dobj.objType = DO_OPCLASS;
6368 54816 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6369 54816 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6370 54816 : AssignDumpId(&opcinfo[i].dobj);
6371 54816 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6372 109632 : opcinfo[i].dobj.namespace =
6373 54816 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6374 54816 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6375 :
6376 : /* Decide whether we want to dump it */
6377 54816 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6378 : }
6379 :
6380 308 : PQclear(res);
6381 :
6382 308 : destroyPQExpBuffer(query);
6383 308 : }
6384 :
6385 : /*
6386 : * getOpfamilies:
6387 : * get information about all opfamilies in the system catalogs
6388 : */
6389 : void
6390 308 : getOpfamilies(Archive *fout)
6391 : {
6392 : PGresult *res;
6393 : int ntups;
6394 : int i;
6395 : PQExpBuffer query;
6396 : OpfamilyInfo *opfinfo;
6397 : int i_tableoid;
6398 : int i_oid;
6399 : int i_opfname;
6400 : int i_opfnamespace;
6401 : int i_opfowner;
6402 :
6403 308 : query = createPQExpBuffer();
6404 :
6405 : /*
6406 : * find all opfamilies, including builtin opfamilies; we filter out
6407 : * system-defined opfamilies at dump-out time.
6408 : */
6409 :
6410 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6411 : "opfnamespace, "
6412 : "opfowner "
6413 : "FROM pg_opfamily");
6414 :
6415 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6416 :
6417 308 : ntups = PQntuples(res);
6418 :
6419 308 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6420 :
6421 308 : i_tableoid = PQfnumber(res, "tableoid");
6422 308 : i_oid = PQfnumber(res, "oid");
6423 308 : i_opfname = PQfnumber(res, "opfname");
6424 308 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6425 308 : i_opfowner = PQfnumber(res, "opfowner");
6426 :
6427 45546 : for (i = 0; i < ntups; i++)
6428 : {
6429 45238 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6430 45238 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6431 45238 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6432 45238 : AssignDumpId(&opfinfo[i].dobj);
6433 45238 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6434 90476 : opfinfo[i].dobj.namespace =
6435 45238 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6436 45238 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6437 :
6438 : /* Decide whether we want to dump it */
6439 45238 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6440 : }
6441 :
6442 308 : PQclear(res);
6443 :
6444 308 : destroyPQExpBuffer(query);
6445 308 : }
6446 :
6447 : /*
6448 : * getAggregates:
6449 : * get information about all user-defined aggregates in the system catalogs
6450 : */
6451 : void
6452 308 : getAggregates(Archive *fout)
6453 : {
6454 308 : DumpOptions *dopt = fout->dopt;
6455 : PGresult *res;
6456 : int ntups;
6457 : int i;
6458 308 : PQExpBuffer query = createPQExpBuffer();
6459 : AggInfo *agginfo;
6460 : int i_tableoid;
6461 : int i_oid;
6462 : int i_aggname;
6463 : int i_aggnamespace;
6464 : int i_pronargs;
6465 : int i_proargtypes;
6466 : int i_proowner;
6467 : int i_aggacl;
6468 : int i_acldefault;
6469 :
6470 : /*
6471 : * Find all interesting aggregates. See comment in getFuncs() for the
6472 : * rationale behind the filtering logic.
6473 : */
6474 308 : if (fout->remoteVersion >= 90600)
6475 : {
6476 : const char *agg_check;
6477 :
6478 616 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6479 308 : : "p.proisagg");
6480 :
6481 308 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6482 : "p.proname AS aggname, "
6483 : "p.pronamespace AS aggnamespace, "
6484 : "p.pronargs, p.proargtypes, "
6485 : "p.proowner, "
6486 : "p.proacl AS aggacl, "
6487 : "acldefault('f', p.proowner) AS acldefault "
6488 : "FROM pg_proc p "
6489 : "LEFT JOIN pg_init_privs pip ON "
6490 : "(p.oid = pip.objoid "
6491 : "AND pip.classoid = 'pg_proc'::regclass "
6492 : "AND pip.objsubid = 0) "
6493 : "WHERE %s AND ("
6494 : "p.pronamespace != "
6495 : "(SELECT oid FROM pg_namespace "
6496 : "WHERE nspname = 'pg_catalog') OR "
6497 : "p.proacl IS DISTINCT FROM pip.initprivs",
6498 : agg_check);
6499 308 : if (dopt->binary_upgrade)
6500 28 : appendPQExpBufferStr(query,
6501 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6502 : "classid = 'pg_proc'::regclass AND "
6503 : "objid = p.oid AND "
6504 : "refclassid = 'pg_extension'::regclass AND "
6505 : "deptype = 'e')");
6506 308 : appendPQExpBufferChar(query, ')');
6507 : }
6508 : else
6509 : {
6510 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6511 : "pronamespace AS aggnamespace, "
6512 : "pronargs, proargtypes, "
6513 : "proowner, "
6514 : "proacl AS aggacl, "
6515 : "acldefault('f', proowner) AS acldefault "
6516 : "FROM pg_proc p "
6517 : "WHERE proisagg AND ("
6518 : "pronamespace != "
6519 : "(SELECT oid FROM pg_namespace "
6520 : "WHERE nspname = 'pg_catalog')");
6521 0 : if (dopt->binary_upgrade)
6522 0 : appendPQExpBufferStr(query,
6523 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6524 : "classid = 'pg_proc'::regclass AND "
6525 : "objid = p.oid AND "
6526 : "refclassid = 'pg_extension'::regclass AND "
6527 : "deptype = 'e')");
6528 0 : appendPQExpBufferChar(query, ')');
6529 : }
6530 :
6531 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6532 :
6533 308 : ntups = PQntuples(res);
6534 :
6535 308 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6536 :
6537 308 : i_tableoid = PQfnumber(res, "tableoid");
6538 308 : i_oid = PQfnumber(res, "oid");
6539 308 : i_aggname = PQfnumber(res, "aggname");
6540 308 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6541 308 : i_pronargs = PQfnumber(res, "pronargs");
6542 308 : i_proargtypes = PQfnumber(res, "proargtypes");
6543 308 : i_proowner = PQfnumber(res, "proowner");
6544 308 : i_aggacl = PQfnumber(res, "aggacl");
6545 308 : i_acldefault = PQfnumber(res, "acldefault");
6546 :
6547 1102 : for (i = 0; i < ntups; i++)
6548 : {
6549 794 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6550 794 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6551 794 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6552 794 : AssignDumpId(&agginfo[i].aggfn.dobj);
6553 794 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6554 1588 : agginfo[i].aggfn.dobj.namespace =
6555 794 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6556 794 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6557 794 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6558 794 : agginfo[i].aggfn.dacl.privtype = 0;
6559 794 : agginfo[i].aggfn.dacl.initprivs = NULL;
6560 794 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6561 794 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6562 794 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6563 794 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6564 794 : if (agginfo[i].aggfn.nargs == 0)
6565 112 : agginfo[i].aggfn.argtypes = NULL;
6566 : else
6567 : {
6568 682 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6569 682 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6570 682 : agginfo[i].aggfn.argtypes,
6571 682 : agginfo[i].aggfn.nargs);
6572 : }
6573 794 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6574 :
6575 : /* Decide whether we want to dump it */
6576 794 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6577 :
6578 : /* Mark whether aggregate has an ACL */
6579 794 : if (!PQgetisnull(res, i, i_aggacl))
6580 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6581 : }
6582 :
6583 308 : PQclear(res);
6584 :
6585 308 : destroyPQExpBuffer(query);
6586 308 : }
6587 :
6588 : /*
6589 : * getFuncs:
6590 : * get information about all user-defined functions in the system catalogs
6591 : */
6592 : void
6593 308 : getFuncs(Archive *fout)
6594 : {
6595 308 : DumpOptions *dopt = fout->dopt;
6596 : PGresult *res;
6597 : int ntups;
6598 : int i;
6599 308 : PQExpBuffer query = createPQExpBuffer();
6600 : FuncInfo *finfo;
6601 : int i_tableoid;
6602 : int i_oid;
6603 : int i_proname;
6604 : int i_pronamespace;
6605 : int i_proowner;
6606 : int i_prolang;
6607 : int i_pronargs;
6608 : int i_proargtypes;
6609 : int i_prorettype;
6610 : int i_proacl;
6611 : int i_acldefault;
6612 :
6613 : /*
6614 : * Find all interesting functions. This is a bit complicated:
6615 : *
6616 : * 1. Always exclude aggregates; those are handled elsewhere.
6617 : *
6618 : * 2. Always exclude functions that are internally dependent on something
6619 : * else, since presumably those will be created as a result of creating
6620 : * the something else. This currently acts only to suppress constructor
6621 : * functions for range types. Note this is OK only because the
6622 : * constructors don't have any dependencies the range type doesn't have;
6623 : * otherwise we might not get creation ordering correct.
6624 : *
6625 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6626 : * they're members of extensions and we are in binary-upgrade mode then
6627 : * include them, since we want to dump extension members individually in
6628 : * that mode. Also, if they are used by casts or transforms then we need
6629 : * to gather the information about them, though they won't be dumped if
6630 : * they are built-in. Also, in 9.6 and up, include functions in
6631 : * pg_catalog if they have an ACL different from what's shown in
6632 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6633 : */
6634 308 : if (fout->remoteVersion >= 90600)
6635 : {
6636 : const char *not_agg_check;
6637 :
6638 616 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6639 308 : : "NOT p.proisagg");
6640 :
6641 308 : appendPQExpBuffer(query,
6642 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6643 : "p.pronargs, p.proargtypes, p.prorettype, "
6644 : "p.proacl, "
6645 : "acldefault('f', p.proowner) AS acldefault, "
6646 : "p.pronamespace, "
6647 : "p.proowner "
6648 : "FROM pg_proc p "
6649 : "LEFT JOIN pg_init_privs pip ON "
6650 : "(p.oid = pip.objoid "
6651 : "AND pip.classoid = 'pg_proc'::regclass "
6652 : "AND pip.objsubid = 0) "
6653 : "WHERE %s"
6654 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6655 : "WHERE classid = 'pg_proc'::regclass AND "
6656 : "objid = p.oid AND deptype = 'i')"
6657 : "\n AND ("
6658 : "\n pronamespace != "
6659 : "(SELECT oid FROM pg_namespace "
6660 : "WHERE nspname = 'pg_catalog')"
6661 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6662 : "\n WHERE pg_cast.oid > %u "
6663 : "\n AND p.oid = pg_cast.castfunc)"
6664 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6665 : "\n WHERE pg_transform.oid > %u AND "
6666 : "\n (p.oid = pg_transform.trffromsql"
6667 : "\n OR p.oid = pg_transform.trftosql))",
6668 : not_agg_check,
6669 : g_last_builtin_oid,
6670 : g_last_builtin_oid);
6671 308 : if (dopt->binary_upgrade)
6672 28 : appendPQExpBufferStr(query,
6673 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6674 : "classid = 'pg_proc'::regclass AND "
6675 : "objid = p.oid AND "
6676 : "refclassid = 'pg_extension'::regclass AND "
6677 : "deptype = 'e')");
6678 308 : appendPQExpBufferStr(query,
6679 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6680 308 : appendPQExpBufferChar(query, ')');
6681 : }
6682 : else
6683 : {
6684 0 : appendPQExpBuffer(query,
6685 : "SELECT tableoid, oid, proname, prolang, "
6686 : "pronargs, proargtypes, prorettype, proacl, "
6687 : "acldefault('f', proowner) AS acldefault, "
6688 : "pronamespace, "
6689 : "proowner "
6690 : "FROM pg_proc p "
6691 : "WHERE NOT proisagg"
6692 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6693 : "WHERE classid = 'pg_proc'::regclass AND "
6694 : "objid = p.oid AND deptype = 'i')"
6695 : "\n AND ("
6696 : "\n pronamespace != "
6697 : "(SELECT oid FROM pg_namespace "
6698 : "WHERE nspname = 'pg_catalog')"
6699 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6700 : "\n WHERE pg_cast.oid > '%u'::oid"
6701 : "\n AND p.oid = pg_cast.castfunc)",
6702 : g_last_builtin_oid);
6703 :
6704 0 : if (fout->remoteVersion >= 90500)
6705 0 : appendPQExpBuffer(query,
6706 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6707 : "\n WHERE pg_transform.oid > '%u'::oid"
6708 : "\n AND (p.oid = pg_transform.trffromsql"
6709 : "\n OR p.oid = pg_transform.trftosql))",
6710 : g_last_builtin_oid);
6711 :
6712 0 : if (dopt->binary_upgrade)
6713 0 : appendPQExpBufferStr(query,
6714 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6715 : "classid = 'pg_proc'::regclass AND "
6716 : "objid = p.oid AND "
6717 : "refclassid = 'pg_extension'::regclass AND "
6718 : "deptype = 'e')");
6719 0 : appendPQExpBufferChar(query, ')');
6720 : }
6721 :
6722 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6723 :
6724 308 : ntups = PQntuples(res);
6725 :
6726 308 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6727 :
6728 308 : i_tableoid = PQfnumber(res, "tableoid");
6729 308 : i_oid = PQfnumber(res, "oid");
6730 308 : i_proname = PQfnumber(res, "proname");
6731 308 : i_pronamespace = PQfnumber(res, "pronamespace");
6732 308 : i_proowner = PQfnumber(res, "proowner");
6733 308 : i_prolang = PQfnumber(res, "prolang");
6734 308 : i_pronargs = PQfnumber(res, "pronargs");
6735 308 : i_proargtypes = PQfnumber(res, "proargtypes");
6736 308 : i_prorettype = PQfnumber(res, "prorettype");
6737 308 : i_proacl = PQfnumber(res, "proacl");
6738 308 : i_acldefault = PQfnumber(res, "acldefault");
6739 :
6740 8878 : for (i = 0; i < ntups; i++)
6741 : {
6742 8570 : finfo[i].dobj.objType = DO_FUNC;
6743 8570 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6744 8570 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6745 8570 : AssignDumpId(&finfo[i].dobj);
6746 8570 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6747 17140 : finfo[i].dobj.namespace =
6748 8570 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6749 8570 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6750 8570 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6751 8570 : finfo[i].dacl.privtype = 0;
6752 8570 : finfo[i].dacl.initprivs = NULL;
6753 8570 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6754 8570 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6755 8570 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6756 8570 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6757 8570 : if (finfo[i].nargs == 0)
6758 2020 : finfo[i].argtypes = NULL;
6759 : else
6760 : {
6761 6550 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6762 6550 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6763 6550 : finfo[i].argtypes, finfo[i].nargs);
6764 : }
6765 8570 : finfo[i].postponed_def = false; /* might get set during sort */
6766 :
6767 : /* Decide whether we want to dump it */
6768 8570 : selectDumpableObject(&(finfo[i].dobj), fout);
6769 :
6770 : /* Mark whether function has an ACL */
6771 8570 : if (!PQgetisnull(res, i, i_proacl))
6772 272 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6773 : }
6774 :
6775 308 : PQclear(res);
6776 :
6777 308 : destroyPQExpBuffer(query);
6778 308 : }
6779 :
6780 : /*
6781 : * getTables
6782 : * read all the tables (no indexes) in the system catalogs,
6783 : * and return them as an array of TableInfo structures
6784 : *
6785 : * *numTables is set to the number of tables read in
6786 : */
6787 : TableInfo *
6788 310 : getTables(Archive *fout, int *numTables)
6789 : {
6790 310 : DumpOptions *dopt = fout->dopt;
6791 : PGresult *res;
6792 : int ntups;
6793 : int i;
6794 310 : PQExpBuffer query = createPQExpBuffer();
6795 : TableInfo *tblinfo;
6796 : int i_reltableoid;
6797 : int i_reloid;
6798 : int i_relname;
6799 : int i_relnamespace;
6800 : int i_relkind;
6801 : int i_reltype;
6802 : int i_relowner;
6803 : int i_relchecks;
6804 : int i_relhasindex;
6805 : int i_relhasrules;
6806 : int i_relpages;
6807 : int i_toastpages;
6808 : int i_owning_tab;
6809 : int i_owning_col;
6810 : int i_reltablespace;
6811 : int i_relhasoids;
6812 : int i_relhastriggers;
6813 : int i_relpersistence;
6814 : int i_relispopulated;
6815 : int i_relreplident;
6816 : int i_relrowsec;
6817 : int i_relforcerowsec;
6818 : int i_relfrozenxid;
6819 : int i_toastfrozenxid;
6820 : int i_toastoid;
6821 : int i_relminmxid;
6822 : int i_toastminmxid;
6823 : int i_reloptions;
6824 : int i_checkoption;
6825 : int i_toastreloptions;
6826 : int i_reloftype;
6827 : int i_foreignserver;
6828 : int i_amname;
6829 : int i_is_identity_sequence;
6830 : int i_relacl;
6831 : int i_acldefault;
6832 : int i_ispartition;
6833 :
6834 : /*
6835 : * Find all the tables and table-like objects.
6836 : *
6837 : * We must fetch all tables in this phase because otherwise we cannot
6838 : * correctly identify inherited columns, owned sequences, etc.
6839 : *
6840 : * We include system catalogs, so that we can work if a user table is
6841 : * defined to inherit from a system catalog (pretty weird, but...)
6842 : *
6843 : * Note: in this phase we should collect only a minimal amount of
6844 : * information about each table, basically just enough to decide if it is
6845 : * interesting. In particular, since we do not yet have lock on any user
6846 : * table, we MUST NOT invoke any server-side data collection functions
6847 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6848 : * wrong answers if any concurrent DDL is happening.
6849 : */
6850 :
6851 310 : appendPQExpBufferStr(query,
6852 : "SELECT c.tableoid, c.oid, c.relname, "
6853 : "c.relnamespace, c.relkind, c.reltype, "
6854 : "c.relowner, "
6855 : "c.relchecks, "
6856 : "c.relhasindex, c.relhasrules, c.relpages, "
6857 : "c.relhastriggers, "
6858 : "c.relpersistence, "
6859 : "c.reloftype, "
6860 : "c.relacl, "
6861 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6862 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6863 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6864 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6865 : "ELSE 0 END AS foreignserver, "
6866 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6867 : "tc.oid AS toid, "
6868 : "tc.relpages AS toastpages, "
6869 : "tc.reloptions AS toast_reloptions, "
6870 : "d.refobjid AS owning_tab, "
6871 : "d.refobjsubid AS owning_col, "
6872 : "tsp.spcname AS reltablespace, ");
6873 :
6874 310 : if (fout->remoteVersion >= 120000)
6875 310 : appendPQExpBufferStr(query,
6876 : "false AS relhasoids, ");
6877 : else
6878 0 : appendPQExpBufferStr(query,
6879 : "c.relhasoids, ");
6880 :
6881 310 : if (fout->remoteVersion >= 90300)
6882 310 : appendPQExpBufferStr(query,
6883 : "c.relispopulated, ");
6884 : else
6885 0 : appendPQExpBufferStr(query,
6886 : "'t' as relispopulated, ");
6887 :
6888 310 : if (fout->remoteVersion >= 90400)
6889 310 : appendPQExpBufferStr(query,
6890 : "c.relreplident, ");
6891 : else
6892 0 : appendPQExpBufferStr(query,
6893 : "'d' AS relreplident, ");
6894 :
6895 310 : if (fout->remoteVersion >= 90500)
6896 310 : appendPQExpBufferStr(query,
6897 : "c.relrowsecurity, c.relforcerowsecurity, ");
6898 : else
6899 0 : appendPQExpBufferStr(query,
6900 : "false AS relrowsecurity, "
6901 : "false AS relforcerowsecurity, ");
6902 :
6903 310 : if (fout->remoteVersion >= 90300)
6904 310 : appendPQExpBufferStr(query,
6905 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
6906 : else
6907 0 : appendPQExpBufferStr(query,
6908 : "0 AS relminmxid, 0 AS tminmxid, ");
6909 :
6910 310 : if (fout->remoteVersion >= 90300)
6911 310 : appendPQExpBufferStr(query,
6912 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6913 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6914 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6915 : else
6916 0 : appendPQExpBufferStr(query,
6917 : "c.reloptions, NULL AS checkoption, ");
6918 :
6919 310 : if (fout->remoteVersion >= 90600)
6920 310 : appendPQExpBufferStr(query,
6921 : "am.amname, ");
6922 : else
6923 0 : appendPQExpBufferStr(query,
6924 : "NULL AS amname, ");
6925 :
6926 310 : if (fout->remoteVersion >= 90600)
6927 310 : appendPQExpBufferStr(query,
6928 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6929 : else
6930 0 : appendPQExpBufferStr(query,
6931 : "false AS is_identity_sequence, ");
6932 :
6933 310 : if (fout->remoteVersion >= 100000)
6934 310 : appendPQExpBufferStr(query,
6935 : "c.relispartition AS ispartition ");
6936 : else
6937 0 : appendPQExpBufferStr(query,
6938 : "false AS ispartition ");
6939 :
6940 : /*
6941 : * Left join to pg_depend to pick up dependency info linking sequences to
6942 : * their owning column, if any (note this dependency is AUTO except for
6943 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
6944 : * collect the spcname.
6945 : */
6946 310 : appendPQExpBufferStr(query,
6947 : "\nFROM pg_class c\n"
6948 : "LEFT JOIN pg_depend d ON "
6949 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
6950 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
6951 : "d.objsubid = 0 AND "
6952 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
6953 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
6954 :
6955 : /*
6956 : * In 9.6 and up, left join to pg_am to pick up the amname.
6957 : */
6958 310 : if (fout->remoteVersion >= 90600)
6959 310 : appendPQExpBufferStr(query,
6960 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
6961 :
6962 : /*
6963 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
6964 : * that versions 10 and 11 have them, but later versions do not, so
6965 : * emitting them causes the upgrade to fail.
6966 : */
6967 310 : appendPQExpBufferStr(query,
6968 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
6969 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
6970 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
6971 :
6972 : /*
6973 : * Restrict to interesting relkinds (in particular, not indexes). Not all
6974 : * relkinds are possible in older servers, but it's not worth the trouble
6975 : * to emit a version-dependent list.
6976 : *
6977 : * Composite-type table entries won't be dumped as such, but we have to
6978 : * make a DumpableObject for them so that we can track dependencies of the
6979 : * composite type (pg_depend entries for columns of the composite type
6980 : * link to the pg_class entry not the pg_type entry).
6981 : */
6982 310 : appendPQExpBufferStr(query,
6983 : "WHERE c.relkind IN ("
6984 : CppAsString2(RELKIND_RELATION) ", "
6985 : CppAsString2(RELKIND_SEQUENCE) ", "
6986 : CppAsString2(RELKIND_VIEW) ", "
6987 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
6988 : CppAsString2(RELKIND_MATVIEW) ", "
6989 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
6990 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
6991 : "ORDER BY c.oid");
6992 :
6993 310 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6994 :
6995 310 : ntups = PQntuples(res);
6996 :
6997 310 : *numTables = ntups;
6998 :
6999 : /*
7000 : * Extract data from result and lock dumpable tables. We do the locking
7001 : * before anything else, to minimize the window wherein a table could
7002 : * disappear under us.
7003 : *
7004 : * Note that we have to save info about all tables here, even when dumping
7005 : * only one, because we don't yet know which tables might be inheritance
7006 : * ancestors of the target table.
7007 : */
7008 310 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7009 :
7010 310 : i_reltableoid = PQfnumber(res, "tableoid");
7011 310 : i_reloid = PQfnumber(res, "oid");
7012 310 : i_relname = PQfnumber(res, "relname");
7013 310 : i_relnamespace = PQfnumber(res, "relnamespace");
7014 310 : i_relkind = PQfnumber(res, "relkind");
7015 310 : i_reltype = PQfnumber(res, "reltype");
7016 310 : i_relowner = PQfnumber(res, "relowner");
7017 310 : i_relchecks = PQfnumber(res, "relchecks");
7018 310 : i_relhasindex = PQfnumber(res, "relhasindex");
7019 310 : i_relhasrules = PQfnumber(res, "relhasrules");
7020 310 : i_relpages = PQfnumber(res, "relpages");
7021 310 : i_toastpages = PQfnumber(res, "toastpages");
7022 310 : i_owning_tab = PQfnumber(res, "owning_tab");
7023 310 : i_owning_col = PQfnumber(res, "owning_col");
7024 310 : i_reltablespace = PQfnumber(res, "reltablespace");
7025 310 : i_relhasoids = PQfnumber(res, "relhasoids");
7026 310 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7027 310 : i_relpersistence = PQfnumber(res, "relpersistence");
7028 310 : i_relispopulated = PQfnumber(res, "relispopulated");
7029 310 : i_relreplident = PQfnumber(res, "relreplident");
7030 310 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7031 310 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7032 310 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7033 310 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7034 310 : i_toastoid = PQfnumber(res, "toid");
7035 310 : i_relminmxid = PQfnumber(res, "relminmxid");
7036 310 : i_toastminmxid = PQfnumber(res, "tminmxid");
7037 310 : i_reloptions = PQfnumber(res, "reloptions");
7038 310 : i_checkoption = PQfnumber(res, "checkoption");
7039 310 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7040 310 : i_reloftype = PQfnumber(res, "reloftype");
7041 310 : i_foreignserver = PQfnumber(res, "foreignserver");
7042 310 : i_amname = PQfnumber(res, "amname");
7043 310 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7044 310 : i_relacl = PQfnumber(res, "relacl");
7045 310 : i_acldefault = PQfnumber(res, "acldefault");
7046 310 : i_ispartition = PQfnumber(res, "ispartition");
7047 :
7048 310 : if (dopt->lockWaitTimeout)
7049 : {
7050 : /*
7051 : * Arrange to fail instead of waiting forever for a table lock.
7052 : *
7053 : * NB: this coding assumes that the only queries issued within the
7054 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7055 : * applied to other things too.
7056 : */
7057 4 : resetPQExpBuffer(query);
7058 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7059 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7060 4 : ExecuteSqlStatement(fout, query->data);
7061 : }
7062 :
7063 310 : resetPQExpBuffer(query);
7064 :
7065 81750 : for (i = 0; i < ntups; i++)
7066 : {
7067 81440 : tblinfo[i].dobj.objType = DO_TABLE;
7068 81440 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7069 81440 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7070 81440 : AssignDumpId(&tblinfo[i].dobj);
7071 81440 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7072 162880 : tblinfo[i].dobj.namespace =
7073 81440 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7074 81440 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7075 81440 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7076 81440 : tblinfo[i].dacl.privtype = 0;
7077 81440 : tblinfo[i].dacl.initprivs = NULL;
7078 81440 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7079 81440 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7080 81440 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7081 81440 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7082 81440 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7083 81440 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7084 81440 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7085 81440 : if (PQgetisnull(res, i, i_toastpages))
7086 64190 : tblinfo[i].toastpages = 0;
7087 : else
7088 17250 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7089 81440 : if (PQgetisnull(res, i, i_owning_tab))
7090 : {
7091 80692 : tblinfo[i].owning_tab = InvalidOid;
7092 80692 : tblinfo[i].owning_col = 0;
7093 : }
7094 : else
7095 : {
7096 748 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7097 748 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7098 : }
7099 81440 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7100 81440 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7101 81440 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7102 81440 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7103 81440 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7104 81440 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7105 81440 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7106 81440 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7107 81440 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7108 81440 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7109 81440 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7110 81440 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7111 81440 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7112 81440 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7113 81440 : if (PQgetisnull(res, i, i_checkoption))
7114 81352 : tblinfo[i].checkoption = NULL;
7115 : else
7116 88 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7117 81440 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7118 81440 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7119 81440 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7120 81440 : if (PQgetisnull(res, i, i_amname))
7121 48084 : tblinfo[i].amname = NULL;
7122 : else
7123 33356 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7124 81440 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7125 81440 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7126 :
7127 : /* other fields were zeroed above */
7128 :
7129 : /*
7130 : * Decide whether we want to dump this table.
7131 : */
7132 81440 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7133 362 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7134 : else
7135 81078 : selectDumpableTable(&tblinfo[i], fout);
7136 :
7137 : /*
7138 : * Now, consider the table "interesting" if we need to dump its
7139 : * definition or its data. Later on, we'll skip a lot of data
7140 : * collection for uninteresting tables.
7141 : *
7142 : * Note: the "interesting" flag will also be set by flagInhTables for
7143 : * parents of interesting tables, so that we collect necessary
7144 : * inheritance info even when the parents are not themselves being
7145 : * dumped. This is the main reason why we need an "interesting" flag
7146 : * that's separate from the components-to-dump bitmask.
7147 : */
7148 81440 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7149 : (DUMP_COMPONENT_DEFINITION |
7150 81440 : DUMP_COMPONENT_DATA)) != 0;
7151 :
7152 81440 : tblinfo[i].dummy_view = false; /* might get set during sort */
7153 81440 : tblinfo[i].postponed_def = false; /* might get set during sort */
7154 :
7155 : /* Tables have data */
7156 81440 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7157 :
7158 : /* Mark whether table has an ACL */
7159 81440 : if (!PQgetisnull(res, i, i_relacl))
7160 64352 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7161 81440 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7162 :
7163 : /*
7164 : * Read-lock target tables to make sure they aren't DROPPED or altered
7165 : * in schema before we get around to dumping them.
7166 : *
7167 : * Note that we don't explicitly lock parents of the target tables; we
7168 : * assume our lock on the child is enough to prevent schema
7169 : * alterations to parent tables.
7170 : *
7171 : * NOTE: it'd be kinda nice to lock other relations too, not only
7172 : * plain or partitioned tables, but the backend doesn't presently
7173 : * allow that.
7174 : *
7175 : * We only need to lock the table for certain components; see
7176 : * pg_dump.h
7177 : */
7178 81440 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7179 12260 : (tblinfo[i].relkind == RELKIND_RELATION ||
7180 3626 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7181 : {
7182 : /*
7183 : * Tables are locked in batches. When dumping from a remote
7184 : * server this can save a significant amount of time by reducing
7185 : * the number of round trips.
7186 : */
7187 9694 : if (query->len == 0)
7188 198 : appendPQExpBuffer(query, "LOCK TABLE %s",
7189 198 : fmtQualifiedDumpable(&tblinfo[i]));
7190 : else
7191 : {
7192 9496 : appendPQExpBuffer(query, ", %s",
7193 9496 : fmtQualifiedDumpable(&tblinfo[i]));
7194 :
7195 : /* Arbitrarily end a batch when query length reaches 100K. */
7196 9496 : if (query->len >= 100000)
7197 : {
7198 : /* Lock another batch of tables. */
7199 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7200 0 : ExecuteSqlStatement(fout, query->data);
7201 0 : resetPQExpBuffer(query);
7202 : }
7203 : }
7204 : }
7205 : }
7206 :
7207 310 : if (query->len != 0)
7208 : {
7209 : /* Lock the tables in the last batch. */
7210 198 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7211 198 : ExecuteSqlStatement(fout, query->data);
7212 : }
7213 :
7214 308 : if (dopt->lockWaitTimeout)
7215 : {
7216 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7217 : }
7218 :
7219 308 : PQclear(res);
7220 :
7221 308 : destroyPQExpBuffer(query);
7222 :
7223 308 : return tblinfo;
7224 : }
7225 :
7226 : /*
7227 : * getOwnedSeqs
7228 : * identify owned sequences and mark them as dumpable if owning table is
7229 : *
7230 : * We used to do this in getTables(), but it's better to do it after the
7231 : * index used by findTableByOid() has been set up.
7232 : */
7233 : void
7234 308 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7235 : {
7236 : int i;
7237 :
7238 : /*
7239 : * Force sequences that are "owned" by table columns to be dumped whenever
7240 : * their owning table is being dumped.
7241 : */
7242 81230 : for (i = 0; i < numTables; i++)
7243 : {
7244 80922 : TableInfo *seqinfo = &tblinfo[i];
7245 : TableInfo *owning_tab;
7246 :
7247 80922 : if (!OidIsValid(seqinfo->owning_tab))
7248 80180 : continue; /* not an owned sequence */
7249 :
7250 742 : owning_tab = findTableByOid(seqinfo->owning_tab);
7251 742 : if (owning_tab == NULL)
7252 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7253 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7254 :
7255 : /*
7256 : * For an identity sequence, dump exactly the same components for the
7257 : * sequence as for the owning table. This is important because we
7258 : * treat the identity sequence as an integral part of the table. For
7259 : * example, there is not any DDL command that allows creation of such
7260 : * a sequence independently of the table.
7261 : *
7262 : * For other owned sequences such as serial sequences, we need to dump
7263 : * the components that are being dumped for the table and any
7264 : * components that the sequence is explicitly marked with.
7265 : *
7266 : * We can't simply use the set of components which are being dumped
7267 : * for the table as the table might be in an extension (and only the
7268 : * non-extension components, eg: ACLs if changed, security labels, and
7269 : * policies, are being dumped) while the sequence is not (and
7270 : * therefore the definition and other components should also be
7271 : * dumped).
7272 : *
7273 : * If the sequence is part of the extension then it should be properly
7274 : * marked by checkExtensionMembership() and this will be a no-op as
7275 : * the table will be equivalently marked.
7276 : */
7277 742 : if (seqinfo->is_identity_sequence)
7278 394 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7279 : else
7280 348 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7281 :
7282 : /* Make sure that necessary data is available if we're dumping it */
7283 742 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7284 : {
7285 570 : seqinfo->interesting = true;
7286 570 : owning_tab->interesting = true;
7287 : }
7288 : }
7289 308 : }
7290 :
7291 : /*
7292 : * getInherits
7293 : * read all the inheritance information
7294 : * from the system catalogs return them in the InhInfo* structure
7295 : *
7296 : * numInherits is set to the number of pairs read in
7297 : */
7298 : InhInfo *
7299 308 : getInherits(Archive *fout, int *numInherits)
7300 : {
7301 : PGresult *res;
7302 : int ntups;
7303 : int i;
7304 308 : PQExpBuffer query = createPQExpBuffer();
7305 : InhInfo *inhinfo;
7306 :
7307 : int i_inhrelid;
7308 : int i_inhparent;
7309 :
7310 : /* find all the inheritance information */
7311 308 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7312 :
7313 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7314 :
7315 308 : ntups = PQntuples(res);
7316 :
7317 308 : *numInherits = ntups;
7318 :
7319 308 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7320 :
7321 308 : i_inhrelid = PQfnumber(res, "inhrelid");
7322 308 : i_inhparent = PQfnumber(res, "inhparent");
7323 :
7324 6096 : for (i = 0; i < ntups; i++)
7325 : {
7326 5788 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7327 5788 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7328 : }
7329 :
7330 308 : PQclear(res);
7331 :
7332 308 : destroyPQExpBuffer(query);
7333 :
7334 308 : return inhinfo;
7335 : }
7336 :
7337 : /*
7338 : * getPartitioningInfo
7339 : * get information about partitioning
7340 : *
7341 : * For the most part, we only collect partitioning info about tables we
7342 : * intend to dump. However, this function has to consider all partitioned
7343 : * tables in the database, because we need to know about parents of partitions
7344 : * we are going to dump even if the parents themselves won't be dumped.
7345 : *
7346 : * Specifically, what we need to know is whether each partitioned table
7347 : * has an "unsafe" partitioning scheme that requires us to force
7348 : * load-via-partition-root mode for its children. Currently the only case
7349 : * for which we force that is hash partitioning on enum columns, since the
7350 : * hash codes depend on enum value OIDs which won't be replicated across
7351 : * dump-and-reload. There are other cases in which load-via-partition-root
7352 : * might be necessary, but we expect users to cope with them.
7353 : */
7354 : void
7355 308 : getPartitioningInfo(Archive *fout)
7356 : {
7357 : PQExpBuffer query;
7358 : PGresult *res;
7359 : int ntups;
7360 :
7361 : /* hash partitioning didn't exist before v11 */
7362 308 : if (fout->remoteVersion < 110000)
7363 0 : return;
7364 : /* needn't bother if not dumping data */
7365 308 : if (!fout->dopt->dumpData)
7366 32 : return;
7367 :
7368 276 : query = createPQExpBuffer();
7369 :
7370 : /*
7371 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7372 : * appears among the partition opclasses. We needn't check partstrat.
7373 : *
7374 : * Note that this query may well retrieve info about tables we aren't
7375 : * going to dump and hence have no lock on. That's okay since we need not
7376 : * invoke any unsafe server-side functions.
7377 : */
7378 276 : appendPQExpBufferStr(query,
7379 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7380 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7381 : "ON c.opcmethod = a.oid\n"
7382 : "WHERE opcname = 'enum_ops' "
7383 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7384 : "AND amname = 'hash') = ANY(partclass)");
7385 :
7386 276 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7387 :
7388 276 : ntups = PQntuples(res);
7389 :
7390 280 : for (int i = 0; i < ntups; i++)
7391 : {
7392 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7393 : TableInfo *tbinfo;
7394 :
7395 4 : tbinfo = findTableByOid(tabrelid);
7396 4 : if (tbinfo == NULL)
7397 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7398 : tabrelid);
7399 4 : tbinfo->unsafe_partitions = true;
7400 : }
7401 :
7402 276 : PQclear(res);
7403 :
7404 276 : destroyPQExpBuffer(query);
7405 : }
7406 :
7407 : /*
7408 : * getIndexes
7409 : * get information about every index on a dumpable table
7410 : *
7411 : * Note: index data is not returned directly to the caller, but it
7412 : * does get entered into the DumpableObject tables.
7413 : */
7414 : void
7415 308 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7416 : {
7417 308 : PQExpBuffer query = createPQExpBuffer();
7418 308 : PQExpBuffer tbloids = createPQExpBuffer();
7419 : PGresult *res;
7420 : int ntups;
7421 : int curtblindx;
7422 : IndxInfo *indxinfo;
7423 : int i_tableoid,
7424 : i_oid,
7425 : i_indrelid,
7426 : i_indexname,
7427 : i_parentidx,
7428 : i_indexdef,
7429 : i_indnkeyatts,
7430 : i_indnatts,
7431 : i_indkey,
7432 : i_indisclustered,
7433 : i_indisreplident,
7434 : i_indnullsnotdistinct,
7435 : i_contype,
7436 : i_conname,
7437 : i_condeferrable,
7438 : i_condeferred,
7439 : i_conperiod,
7440 : i_contableoid,
7441 : i_conoid,
7442 : i_condef,
7443 : i_tablespace,
7444 : i_indreloptions,
7445 : i_indstatcols,
7446 : i_indstatvals;
7447 :
7448 : /*
7449 : * We want to perform just one query against pg_index. However, we
7450 : * mustn't try to select every row of the catalog and then sort it out on
7451 : * the client side, because some of the server-side functions we need
7452 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7453 : * build an array of the OIDs of tables we care about (and now have lock
7454 : * on!), and use a WHERE clause to constrain which rows are selected.
7455 : */
7456 308 : appendPQExpBufferChar(tbloids, '{');
7457 81230 : for (int i = 0; i < numTables; i++)
7458 : {
7459 80922 : TableInfo *tbinfo = &tblinfo[i];
7460 :
7461 80922 : if (!tbinfo->hasindex)
7462 56980 : continue;
7463 :
7464 : /*
7465 : * We can ignore indexes of uninteresting tables.
7466 : */
7467 23942 : if (!tbinfo->interesting)
7468 20360 : continue;
7469 :
7470 : /* OK, we need info for this table */
7471 3582 : if (tbloids->len > 1) /* do we have more than the '{'? */
7472 3430 : appendPQExpBufferChar(tbloids, ',');
7473 3582 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7474 : }
7475 308 : appendPQExpBufferChar(tbloids, '}');
7476 :
7477 308 : appendPQExpBufferStr(query,
7478 : "SELECT t.tableoid, t.oid, i.indrelid, "
7479 : "t.relname AS indexname, "
7480 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7481 : "i.indkey, i.indisclustered, "
7482 : "c.contype, c.conname, "
7483 : "c.condeferrable, c.condeferred, "
7484 : "c.tableoid AS contableoid, "
7485 : "c.oid AS conoid, "
7486 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7487 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7488 : "t.reloptions AS indreloptions, ");
7489 :
7490 :
7491 308 : if (fout->remoteVersion >= 90400)
7492 308 : appendPQExpBufferStr(query,
7493 : "i.indisreplident, ");
7494 : else
7495 0 : appendPQExpBufferStr(query,
7496 : "false AS indisreplident, ");
7497 :
7498 308 : if (fout->remoteVersion >= 110000)
7499 308 : appendPQExpBufferStr(query,
7500 : "inh.inhparent AS parentidx, "
7501 : "i.indnkeyatts AS indnkeyatts, "
7502 : "i.indnatts AS indnatts, "
7503 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7504 : " FROM pg_catalog.pg_attribute "
7505 : " WHERE attrelid = i.indexrelid AND "
7506 : " attstattarget >= 0) AS indstatcols, "
7507 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7508 : " FROM pg_catalog.pg_attribute "
7509 : " WHERE attrelid = i.indexrelid AND "
7510 : " attstattarget >= 0) AS indstatvals, ");
7511 : else
7512 0 : appendPQExpBufferStr(query,
7513 : "0 AS parentidx, "
7514 : "i.indnatts AS indnkeyatts, "
7515 : "i.indnatts AS indnatts, "
7516 : "'' AS indstatcols, "
7517 : "'' AS indstatvals, ");
7518 :
7519 308 : if (fout->remoteVersion >= 150000)
7520 308 : appendPQExpBufferStr(query,
7521 : "i.indnullsnotdistinct, ");
7522 : else
7523 0 : appendPQExpBufferStr(query,
7524 : "false AS indnullsnotdistinct, ");
7525 :
7526 308 : if (fout->remoteVersion >= 180000)
7527 308 : appendPQExpBufferStr(query,
7528 : "c.conperiod ");
7529 : else
7530 0 : appendPQExpBufferStr(query,
7531 : "NULL AS conperiod ");
7532 :
7533 : /*
7534 : * The point of the messy-looking outer join is to find a constraint that
7535 : * is related by an internal dependency link to the index. If we find one,
7536 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7537 : * index won't have more than one internal dependency.
7538 : *
7539 : * Note: the check on conrelid is redundant, but useful because that
7540 : * column is indexed while conindid is not.
7541 : */
7542 308 : if (fout->remoteVersion >= 110000)
7543 : {
7544 308 : appendPQExpBuffer(query,
7545 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7546 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7547 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7548 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7549 : "LEFT JOIN pg_catalog.pg_constraint c "
7550 : "ON (i.indrelid = c.conrelid AND "
7551 : "i.indexrelid = c.conindid AND "
7552 : "c.contype IN ('p','u','x')) "
7553 : "LEFT JOIN pg_catalog.pg_inherits inh "
7554 : "ON (inh.inhrelid = indexrelid) "
7555 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7556 : "AND i.indisready "
7557 : "ORDER BY i.indrelid, indexname",
7558 : tbloids->data);
7559 : }
7560 : else
7561 : {
7562 : /*
7563 : * the test on indisready is necessary in 9.2, and harmless in
7564 : * earlier/later versions
7565 : */
7566 0 : appendPQExpBuffer(query,
7567 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7568 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7569 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7570 : "LEFT JOIN pg_catalog.pg_constraint c "
7571 : "ON (i.indrelid = c.conrelid AND "
7572 : "i.indexrelid = c.conindid AND "
7573 : "c.contype IN ('p','u','x')) "
7574 : "WHERE i.indisvalid AND i.indisready "
7575 : "ORDER BY i.indrelid, indexname",
7576 : tbloids->data);
7577 : }
7578 :
7579 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7580 :
7581 308 : ntups = PQntuples(res);
7582 :
7583 308 : i_tableoid = PQfnumber(res, "tableoid");
7584 308 : i_oid = PQfnumber(res, "oid");
7585 308 : i_indrelid = PQfnumber(res, "indrelid");
7586 308 : i_indexname = PQfnumber(res, "indexname");
7587 308 : i_parentidx = PQfnumber(res, "parentidx");
7588 308 : i_indexdef = PQfnumber(res, "indexdef");
7589 308 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7590 308 : i_indnatts = PQfnumber(res, "indnatts");
7591 308 : i_indkey = PQfnumber(res, "indkey");
7592 308 : i_indisclustered = PQfnumber(res, "indisclustered");
7593 308 : i_indisreplident = PQfnumber(res, "indisreplident");
7594 308 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7595 308 : i_contype = PQfnumber(res, "contype");
7596 308 : i_conname = PQfnumber(res, "conname");
7597 308 : i_condeferrable = PQfnumber(res, "condeferrable");
7598 308 : i_condeferred = PQfnumber(res, "condeferred");
7599 308 : i_conperiod = PQfnumber(res, "conperiod");
7600 308 : i_contableoid = PQfnumber(res, "contableoid");
7601 308 : i_conoid = PQfnumber(res, "conoid");
7602 308 : i_condef = PQfnumber(res, "condef");
7603 308 : i_tablespace = PQfnumber(res, "tablespace");
7604 308 : i_indreloptions = PQfnumber(res, "indreloptions");
7605 308 : i_indstatcols = PQfnumber(res, "indstatcols");
7606 308 : i_indstatvals = PQfnumber(res, "indstatvals");
7607 :
7608 308 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7609 :
7610 : /*
7611 : * Outer loop iterates once per table, not once per row. Incrementing of
7612 : * j is handled by the inner loop.
7613 : */
7614 308 : curtblindx = -1;
7615 3882 : for (int j = 0; j < ntups;)
7616 : {
7617 3574 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7618 3574 : TableInfo *tbinfo = NULL;
7619 : int numinds;
7620 :
7621 : /* Count rows for this table */
7622 4758 : for (numinds = 1; numinds < ntups - j; numinds++)
7623 4606 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7624 3422 : break;
7625 :
7626 : /*
7627 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7628 : * order.
7629 : */
7630 43156 : while (++curtblindx < numTables)
7631 : {
7632 43156 : tbinfo = &tblinfo[curtblindx];
7633 43156 : if (tbinfo->dobj.catId.oid == indrelid)
7634 3574 : break;
7635 : }
7636 3574 : if (curtblindx >= numTables)
7637 0 : pg_fatal("unrecognized table OID %u", indrelid);
7638 : /* cross-check that we only got requested tables */
7639 3574 : if (!tbinfo->hasindex ||
7640 3574 : !tbinfo->interesting)
7641 0 : pg_fatal("unexpected index data for table \"%s\"",
7642 : tbinfo->dobj.name);
7643 :
7644 : /* Save data for this table */
7645 3574 : tbinfo->indexes = indxinfo + j;
7646 3574 : tbinfo->numIndexes = numinds;
7647 :
7648 8332 : for (int c = 0; c < numinds; c++, j++)
7649 : {
7650 : char contype;
7651 :
7652 4758 : indxinfo[j].dobj.objType = DO_INDEX;
7653 4758 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7654 4758 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7655 4758 : AssignDumpId(&indxinfo[j].dobj);
7656 4758 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7657 4758 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7658 4758 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7659 4758 : indxinfo[j].indextable = tbinfo;
7660 4758 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7661 4758 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7662 4758 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7663 4758 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7664 4758 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7665 4758 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7666 4758 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7667 4758 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7668 4758 : parseOidArray(PQgetvalue(res, j, i_indkey),
7669 4758 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7670 4758 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7671 4758 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7672 4758 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7673 4758 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7674 4758 : indxinfo[j].partattaches = (SimplePtrList)
7675 : {
7676 : NULL, NULL
7677 : };
7678 4758 : contype = *(PQgetvalue(res, j, i_contype));
7679 :
7680 4758 : if (contype == 'p' || contype == 'u' || contype == 'x')
7681 2754 : {
7682 : /*
7683 : * If we found a constraint matching the index, create an
7684 : * entry for it.
7685 : */
7686 : ConstraintInfo *constrinfo;
7687 :
7688 2754 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7689 2754 : constrinfo->dobj.objType = DO_CONSTRAINT;
7690 2754 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7691 2754 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7692 2754 : AssignDumpId(&constrinfo->dobj);
7693 2754 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7694 2754 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7695 2754 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7696 2754 : constrinfo->contable = tbinfo;
7697 2754 : constrinfo->condomain = NULL;
7698 2754 : constrinfo->contype = contype;
7699 2754 : if (contype == 'x')
7700 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7701 : else
7702 2734 : constrinfo->condef = NULL;
7703 2754 : constrinfo->confrelid = InvalidOid;
7704 2754 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7705 2754 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7706 2754 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7707 2754 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7708 2754 : constrinfo->conislocal = true;
7709 2754 : constrinfo->separate = true;
7710 :
7711 2754 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7712 : }
7713 : else
7714 : {
7715 : /* Plain secondary index */
7716 2004 : indxinfo[j].indexconstraint = 0;
7717 : }
7718 : }
7719 : }
7720 :
7721 308 : PQclear(res);
7722 :
7723 308 : destroyPQExpBuffer(query);
7724 308 : destroyPQExpBuffer(tbloids);
7725 308 : }
7726 :
7727 : /*
7728 : * getExtendedStatistics
7729 : * get information about extended-statistics objects.
7730 : *
7731 : * Note: extended statistics data is not returned directly to the caller, but
7732 : * it does get entered into the DumpableObject tables.
7733 : */
7734 : void
7735 308 : getExtendedStatistics(Archive *fout)
7736 : {
7737 : PQExpBuffer query;
7738 : PGresult *res;
7739 : StatsExtInfo *statsextinfo;
7740 : int ntups;
7741 : int i_tableoid;
7742 : int i_oid;
7743 : int i_stxname;
7744 : int i_stxnamespace;
7745 : int i_stxowner;
7746 : int i_stxrelid;
7747 : int i_stattarget;
7748 : int i;
7749 :
7750 : /* Extended statistics were new in v10 */
7751 308 : if (fout->remoteVersion < 100000)
7752 0 : return;
7753 :
7754 308 : query = createPQExpBuffer();
7755 :
7756 308 : if (fout->remoteVersion < 130000)
7757 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7758 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
7759 : "FROM pg_catalog.pg_statistic_ext");
7760 : else
7761 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7762 : "stxnamespace, stxowner, stxrelid, stxstattarget "
7763 : "FROM pg_catalog.pg_statistic_ext");
7764 :
7765 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7766 :
7767 308 : ntups = PQntuples(res);
7768 :
7769 308 : i_tableoid = PQfnumber(res, "tableoid");
7770 308 : i_oid = PQfnumber(res, "oid");
7771 308 : i_stxname = PQfnumber(res, "stxname");
7772 308 : i_stxnamespace = PQfnumber(res, "stxnamespace");
7773 308 : i_stxowner = PQfnumber(res, "stxowner");
7774 308 : i_stxrelid = PQfnumber(res, "stxrelid");
7775 308 : i_stattarget = PQfnumber(res, "stxstattarget");
7776 :
7777 308 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7778 :
7779 622 : for (i = 0; i < ntups; i++)
7780 : {
7781 314 : statsextinfo[i].dobj.objType = DO_STATSEXT;
7782 314 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7783 314 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7784 314 : AssignDumpId(&statsextinfo[i].dobj);
7785 314 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7786 628 : statsextinfo[i].dobj.namespace =
7787 314 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7788 314 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7789 628 : statsextinfo[i].stattable =
7790 314 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
7791 314 : if (PQgetisnull(res, i, i_stattarget))
7792 228 : statsextinfo[i].stattarget = -1;
7793 : else
7794 86 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7795 :
7796 : /* Decide whether we want to dump it */
7797 314 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
7798 : }
7799 :
7800 308 : PQclear(res);
7801 308 : destroyPQExpBuffer(query);
7802 : }
7803 :
7804 : /*
7805 : * getConstraints
7806 : *
7807 : * Get info about constraints on dumpable tables.
7808 : *
7809 : * Currently handles foreign keys only.
7810 : * Unique and primary key constraints are handled with indexes,
7811 : * while check constraints are processed in getTableAttrs().
7812 : */
7813 : void
7814 308 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7815 : {
7816 308 : PQExpBuffer query = createPQExpBuffer();
7817 308 : PQExpBuffer tbloids = createPQExpBuffer();
7818 : PGresult *res;
7819 : int ntups;
7820 : int curtblindx;
7821 308 : TableInfo *tbinfo = NULL;
7822 : ConstraintInfo *constrinfo;
7823 : int i_contableoid,
7824 : i_conoid,
7825 : i_conrelid,
7826 : i_conname,
7827 : i_confrelid,
7828 : i_conindid,
7829 : i_condef;
7830 :
7831 : /*
7832 : * We want to perform just one query against pg_constraint. However, we
7833 : * mustn't try to select every row of the catalog and then sort it out on
7834 : * the client side, because some of the server-side functions we need
7835 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7836 : * build an array of the OIDs of tables we care about (and now have lock
7837 : * on!), and use a WHERE clause to constrain which rows are selected.
7838 : */
7839 308 : appendPQExpBufferChar(tbloids, '{');
7840 81230 : for (int i = 0; i < numTables; i++)
7841 : {
7842 80922 : TableInfo *tinfo = &tblinfo[i];
7843 :
7844 : /*
7845 : * For partitioned tables, foreign keys have no triggers so they must
7846 : * be included anyway in case some foreign keys are defined.
7847 : */
7848 80922 : if ((!tinfo->hastriggers &&
7849 78740 : tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7850 3134 : !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7851 78566 : continue;
7852 :
7853 : /* OK, we need info for this table */
7854 2356 : if (tbloids->len > 1) /* do we have more than the '{'? */
7855 2254 : appendPQExpBufferChar(tbloids, ',');
7856 2356 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7857 : }
7858 308 : appendPQExpBufferChar(tbloids, '}');
7859 :
7860 308 : appendPQExpBufferStr(query,
7861 : "SELECT c.tableoid, c.oid, "
7862 : "conrelid, conname, confrelid, ");
7863 308 : if (fout->remoteVersion >= 110000)
7864 308 : appendPQExpBufferStr(query, "conindid, ");
7865 : else
7866 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
7867 308 : appendPQExpBuffer(query,
7868 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7869 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7870 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7871 : "WHERE contype = 'f' ",
7872 : tbloids->data);
7873 308 : if (fout->remoteVersion >= 110000)
7874 308 : appendPQExpBufferStr(query,
7875 : "AND conparentid = 0 ");
7876 308 : appendPQExpBufferStr(query,
7877 : "ORDER BY conrelid, conname");
7878 :
7879 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7880 :
7881 308 : ntups = PQntuples(res);
7882 :
7883 308 : i_contableoid = PQfnumber(res, "tableoid");
7884 308 : i_conoid = PQfnumber(res, "oid");
7885 308 : i_conrelid = PQfnumber(res, "conrelid");
7886 308 : i_conname = PQfnumber(res, "conname");
7887 308 : i_confrelid = PQfnumber(res, "confrelid");
7888 308 : i_conindid = PQfnumber(res, "conindid");
7889 308 : i_condef = PQfnumber(res, "condef");
7890 :
7891 308 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7892 :
7893 308 : curtblindx = -1;
7894 652 : for (int j = 0; j < ntups; j++)
7895 : {
7896 344 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7897 : TableInfo *reftable;
7898 :
7899 : /*
7900 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7901 : * order.
7902 : */
7903 344 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7904 : {
7905 25128 : while (++curtblindx < numTables)
7906 : {
7907 25128 : tbinfo = &tblinfo[curtblindx];
7908 25128 : if (tbinfo->dobj.catId.oid == conrelid)
7909 324 : break;
7910 : }
7911 324 : if (curtblindx >= numTables)
7912 0 : pg_fatal("unrecognized table OID %u", conrelid);
7913 : }
7914 :
7915 344 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7916 344 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7917 344 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7918 344 : AssignDumpId(&constrinfo[j].dobj);
7919 344 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7920 344 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7921 344 : constrinfo[j].contable = tbinfo;
7922 344 : constrinfo[j].condomain = NULL;
7923 344 : constrinfo[j].contype = 'f';
7924 344 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7925 344 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7926 344 : constrinfo[j].conindex = 0;
7927 344 : constrinfo[j].condeferrable = false;
7928 344 : constrinfo[j].condeferred = false;
7929 344 : constrinfo[j].conislocal = true;
7930 344 : constrinfo[j].separate = true;
7931 :
7932 : /*
7933 : * Restoring an FK that points to a partitioned table requires that
7934 : * all partition indexes have been attached beforehand. Ensure that
7935 : * happens by making the constraint depend on each index partition
7936 : * attach object.
7937 : */
7938 344 : reftable = findTableByOid(constrinfo[j].confrelid);
7939 344 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
7940 : {
7941 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
7942 :
7943 40 : if (indexOid != InvalidOid)
7944 : {
7945 40 : for (int k = 0; k < reftable->numIndexes; k++)
7946 : {
7947 : IndxInfo *refidx;
7948 :
7949 : /* not our index? */
7950 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
7951 0 : continue;
7952 :
7953 40 : refidx = &reftable->indexes[k];
7954 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
7955 40 : break;
7956 : }
7957 : }
7958 : }
7959 : }
7960 :
7961 308 : PQclear(res);
7962 :
7963 308 : destroyPQExpBuffer(query);
7964 308 : destroyPQExpBuffer(tbloids);
7965 308 : }
7966 :
7967 : /*
7968 : * addConstrChildIdxDeps
7969 : *
7970 : * Recursive subroutine for getConstraints
7971 : *
7972 : * Given an object representing a foreign key constraint and an index on the
7973 : * partitioned table it references, mark the constraint object as dependent
7974 : * on the DO_INDEX_ATTACH object of each index partition, recursively
7975 : * drilling down to their partitions if any. This ensures that the FK is not
7976 : * restored until the index is fully marked valid.
7977 : */
7978 : static void
7979 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
7980 : {
7981 : SimplePtrListCell *cell;
7982 :
7983 : Assert(dobj->objType == DO_FK_CONSTRAINT);
7984 :
7985 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
7986 : {
7987 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
7988 :
7989 220 : addObjectDependency(dobj, attach->dobj.dumpId);
7990 :
7991 220 : if (attach->partitionIdx->partattaches.head != NULL)
7992 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
7993 : }
7994 90 : }
7995 :
7996 : /*
7997 : * getDomainConstraints
7998 : *
7999 : * Get info about constraints on a domain.
8000 : */
8001 : static void
8002 272 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8003 : {
8004 : int i;
8005 : ConstraintInfo *constrinfo;
8006 272 : PQExpBuffer query = createPQExpBuffer();
8007 : PGresult *res;
8008 : int i_tableoid,
8009 : i_oid,
8010 : i_conname,
8011 : i_consrc;
8012 : int ntups;
8013 :
8014 272 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8015 : {
8016 : /* Set up query for constraint-specific details */
8017 82 : appendPQExpBufferStr(query,
8018 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8019 : "SELECT tableoid, oid, conname, "
8020 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8021 : "convalidated "
8022 : "FROM pg_catalog.pg_constraint "
8023 : "WHERE contypid = $1 AND contype = 'c' "
8024 : "ORDER BY conname");
8025 :
8026 82 : ExecuteSqlStatement(fout, query->data);
8027 :
8028 82 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8029 : }
8030 :
8031 272 : printfPQExpBuffer(query,
8032 : "EXECUTE getDomainConstraints('%u')",
8033 : tyinfo->dobj.catId.oid);
8034 :
8035 272 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8036 :
8037 272 : ntups = PQntuples(res);
8038 :
8039 272 : i_tableoid = PQfnumber(res, "tableoid");
8040 272 : i_oid = PQfnumber(res, "oid");
8041 272 : i_conname = PQfnumber(res, "conname");
8042 272 : i_consrc = PQfnumber(res, "consrc");
8043 :
8044 272 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8045 :
8046 272 : tyinfo->nDomChecks = ntups;
8047 272 : tyinfo->domChecks = constrinfo;
8048 :
8049 454 : for (i = 0; i < ntups; i++)
8050 : {
8051 182 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
8052 :
8053 182 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
8054 182 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8055 182 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8056 182 : AssignDumpId(&constrinfo[i].dobj);
8057 182 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8058 182 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8059 182 : constrinfo[i].contable = NULL;
8060 182 : constrinfo[i].condomain = tyinfo;
8061 182 : constrinfo[i].contype = 'c';
8062 182 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8063 182 : constrinfo[i].confrelid = InvalidOid;
8064 182 : constrinfo[i].conindex = 0;
8065 182 : constrinfo[i].condeferrable = false;
8066 182 : constrinfo[i].condeferred = false;
8067 182 : constrinfo[i].conislocal = true;
8068 :
8069 182 : constrinfo[i].separate = !validated;
8070 :
8071 : /*
8072 : * Make the domain depend on the constraint, ensuring it won't be
8073 : * output till any constraint dependencies are OK. If the constraint
8074 : * has not been validated, it's going to be dumped after the domain
8075 : * anyway, so this doesn't matter.
8076 : */
8077 182 : if (validated)
8078 182 : addObjectDependency(&tyinfo->dobj,
8079 182 : constrinfo[i].dobj.dumpId);
8080 : }
8081 :
8082 272 : PQclear(res);
8083 :
8084 272 : destroyPQExpBuffer(query);
8085 272 : }
8086 :
8087 : /*
8088 : * getRules
8089 : * get basic information about every rule in the system
8090 : */
8091 : void
8092 308 : getRules(Archive *fout)
8093 : {
8094 : PGresult *res;
8095 : int ntups;
8096 : int i;
8097 308 : PQExpBuffer query = createPQExpBuffer();
8098 : RuleInfo *ruleinfo;
8099 : int i_tableoid;
8100 : int i_oid;
8101 : int i_rulename;
8102 : int i_ruletable;
8103 : int i_ev_type;
8104 : int i_is_instead;
8105 : int i_ev_enabled;
8106 :
8107 308 : appendPQExpBufferStr(query, "SELECT "
8108 : "tableoid, oid, rulename, "
8109 : "ev_class AS ruletable, ev_type, is_instead, "
8110 : "ev_enabled "
8111 : "FROM pg_rewrite "
8112 : "ORDER BY oid");
8113 :
8114 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8115 :
8116 308 : ntups = PQntuples(res);
8117 :
8118 308 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8119 :
8120 308 : i_tableoid = PQfnumber(res, "tableoid");
8121 308 : i_oid = PQfnumber(res, "oid");
8122 308 : i_rulename = PQfnumber(res, "rulename");
8123 308 : i_ruletable = PQfnumber(res, "ruletable");
8124 308 : i_ev_type = PQfnumber(res, "ev_type");
8125 308 : i_is_instead = PQfnumber(res, "is_instead");
8126 308 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8127 :
8128 47158 : for (i = 0; i < ntups; i++)
8129 : {
8130 : Oid ruletableoid;
8131 :
8132 46850 : ruleinfo[i].dobj.objType = DO_RULE;
8133 46850 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8134 46850 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8135 46850 : AssignDumpId(&ruleinfo[i].dobj);
8136 46850 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8137 46850 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8138 46850 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8139 46850 : if (ruleinfo[i].ruletable == NULL)
8140 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8141 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8142 46850 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8143 46850 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8144 46850 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8145 46850 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8146 46850 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8147 46850 : if (ruleinfo[i].ruletable)
8148 : {
8149 : /*
8150 : * If the table is a view or materialized view, force its ON
8151 : * SELECT rule to be sorted before the view itself --- this
8152 : * ensures that any dependencies for the rule affect the table's
8153 : * positioning. Other rules are forced to appear after their
8154 : * table.
8155 : */
8156 46850 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8157 1348 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8158 46388 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8159 : {
8160 45688 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8161 45688 : ruleinfo[i].dobj.dumpId);
8162 : /* We'll merge the rule into CREATE VIEW, if possible */
8163 45688 : ruleinfo[i].separate = false;
8164 : }
8165 : else
8166 : {
8167 1162 : addObjectDependency(&ruleinfo[i].dobj,
8168 1162 : ruleinfo[i].ruletable->dobj.dumpId);
8169 1162 : ruleinfo[i].separate = true;
8170 : }
8171 : }
8172 : else
8173 0 : ruleinfo[i].separate = true;
8174 : }
8175 :
8176 308 : PQclear(res);
8177 :
8178 308 : destroyPQExpBuffer(query);
8179 308 : }
8180 :
8181 : /*
8182 : * getTriggers
8183 : * get information about every trigger on a dumpable table
8184 : *
8185 : * Note: trigger data is not returned directly to the caller, but it
8186 : * does get entered into the DumpableObject tables.
8187 : */
8188 : void
8189 308 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8190 : {
8191 308 : PQExpBuffer query = createPQExpBuffer();
8192 308 : PQExpBuffer tbloids = createPQExpBuffer();
8193 : PGresult *res;
8194 : int ntups;
8195 : int curtblindx;
8196 : TriggerInfo *tginfo;
8197 : int i_tableoid,
8198 : i_oid,
8199 : i_tgrelid,
8200 : i_tgname,
8201 : i_tgenabled,
8202 : i_tgispartition,
8203 : i_tgdef;
8204 :
8205 : /*
8206 : * We want to perform just one query against pg_trigger. However, we
8207 : * mustn't try to select every row of the catalog and then sort it out on
8208 : * the client side, because some of the server-side functions we need
8209 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8210 : * build an array of the OIDs of tables we care about (and now have lock
8211 : * on!), and use a WHERE clause to constrain which rows are selected.
8212 : */
8213 308 : appendPQExpBufferChar(tbloids, '{');
8214 81230 : for (int i = 0; i < numTables; i++)
8215 : {
8216 80922 : TableInfo *tbinfo = &tblinfo[i];
8217 :
8218 80922 : if (!tbinfo->hastriggers ||
8219 2182 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8220 79260 : continue;
8221 :
8222 : /* OK, we need info for this table */
8223 1662 : if (tbloids->len > 1) /* do we have more than the '{'? */
8224 1564 : appendPQExpBufferChar(tbloids, ',');
8225 1662 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8226 : }
8227 308 : appendPQExpBufferChar(tbloids, '}');
8228 :
8229 308 : if (fout->remoteVersion >= 150000)
8230 : {
8231 : /*
8232 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8233 : * result in non-forward-compatible dumps of WHEN clauses due to
8234 : * under-parenthesization.
8235 : *
8236 : * NB: We need to see partition triggers in case the tgenabled flag
8237 : * has been changed from the parent.
8238 : */
8239 308 : appendPQExpBuffer(query,
8240 : "SELECT t.tgrelid, t.tgname, "
8241 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8242 : "t.tgenabled, t.tableoid, t.oid, "
8243 : "t.tgparentid <> 0 AS tgispartition\n"
8244 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8245 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8246 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8247 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8248 : "OR t.tgenabled != u.tgenabled) "
8249 : "ORDER BY t.tgrelid, t.tgname",
8250 : tbloids->data);
8251 : }
8252 0 : else if (fout->remoteVersion >= 130000)
8253 : {
8254 : /*
8255 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8256 : * result in non-forward-compatible dumps of WHEN clauses due to
8257 : * under-parenthesization.
8258 : *
8259 : * NB: We need to see tgisinternal triggers in partitions, in case the
8260 : * tgenabled flag has been changed from the parent.
8261 : */
8262 0 : appendPQExpBuffer(query,
8263 : "SELECT t.tgrelid, t.tgname, "
8264 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8265 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8266 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8267 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8268 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8269 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8270 : "ORDER BY t.tgrelid, t.tgname",
8271 : tbloids->data);
8272 : }
8273 0 : else if (fout->remoteVersion >= 110000)
8274 : {
8275 : /*
8276 : * NB: We need to see tgisinternal triggers in partitions, in case the
8277 : * tgenabled flag has been changed from the parent. No tgparentid in
8278 : * version 11-12, so we have to match them via pg_depend.
8279 : *
8280 : * See above about pretty=true in pg_get_triggerdef.
8281 : */
8282 0 : appendPQExpBuffer(query,
8283 : "SELECT t.tgrelid, t.tgname, "
8284 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8285 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8286 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8287 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8288 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8289 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8290 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8291 : " d.objid = t.oid "
8292 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8293 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8294 : "ORDER BY t.tgrelid, t.tgname",
8295 : tbloids->data);
8296 : }
8297 : else
8298 : {
8299 : /* See above about pretty=true in pg_get_triggerdef */
8300 0 : appendPQExpBuffer(query,
8301 : "SELECT t.tgrelid, t.tgname, "
8302 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8303 : "t.tgenabled, false as tgispartition, "
8304 : "t.tableoid, t.oid "
8305 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8306 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8307 : "WHERE NOT tgisinternal "
8308 : "ORDER BY t.tgrelid, t.tgname",
8309 : tbloids->data);
8310 : }
8311 :
8312 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8313 :
8314 308 : ntups = PQntuples(res);
8315 :
8316 308 : i_tableoid = PQfnumber(res, "tableoid");
8317 308 : i_oid = PQfnumber(res, "oid");
8318 308 : i_tgrelid = PQfnumber(res, "tgrelid");
8319 308 : i_tgname = PQfnumber(res, "tgname");
8320 308 : i_tgenabled = PQfnumber(res, "tgenabled");
8321 308 : i_tgispartition = PQfnumber(res, "tgispartition");
8322 308 : i_tgdef = PQfnumber(res, "tgdef");
8323 :
8324 308 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8325 :
8326 : /*
8327 : * Outer loop iterates once per table, not once per row. Incrementing of
8328 : * j is handled by the inner loop.
8329 : */
8330 308 : curtblindx = -1;
8331 890 : for (int j = 0; j < ntups;)
8332 : {
8333 582 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8334 582 : TableInfo *tbinfo = NULL;
8335 : int numtrigs;
8336 :
8337 : /* Count rows for this table */
8338 986 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8339 888 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8340 484 : break;
8341 :
8342 : /*
8343 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8344 : * order.
8345 : */
8346 30046 : while (++curtblindx < numTables)
8347 : {
8348 30046 : tbinfo = &tblinfo[curtblindx];
8349 30046 : if (tbinfo->dobj.catId.oid == tgrelid)
8350 582 : break;
8351 : }
8352 582 : if (curtblindx >= numTables)
8353 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8354 :
8355 : /* Save data for this table */
8356 582 : tbinfo->triggers = tginfo + j;
8357 582 : tbinfo->numTriggers = numtrigs;
8358 :
8359 1568 : for (int c = 0; c < numtrigs; c++, j++)
8360 : {
8361 986 : tginfo[j].dobj.objType = DO_TRIGGER;
8362 986 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8363 986 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8364 986 : AssignDumpId(&tginfo[j].dobj);
8365 986 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8366 986 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8367 986 : tginfo[j].tgtable = tbinfo;
8368 986 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8369 986 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8370 986 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8371 : }
8372 : }
8373 :
8374 308 : PQclear(res);
8375 :
8376 308 : destroyPQExpBuffer(query);
8377 308 : destroyPQExpBuffer(tbloids);
8378 308 : }
8379 :
8380 : /*
8381 : * getEventTriggers
8382 : * get information about event triggers
8383 : */
8384 : void
8385 308 : getEventTriggers(Archive *fout)
8386 : {
8387 : int i;
8388 : PQExpBuffer query;
8389 : PGresult *res;
8390 : EventTriggerInfo *evtinfo;
8391 : int i_tableoid,
8392 : i_oid,
8393 : i_evtname,
8394 : i_evtevent,
8395 : i_evtowner,
8396 : i_evttags,
8397 : i_evtfname,
8398 : i_evtenabled;
8399 : int ntups;
8400 :
8401 : /* Before 9.3, there are no event triggers */
8402 308 : if (fout->remoteVersion < 90300)
8403 0 : return;
8404 :
8405 308 : query = createPQExpBuffer();
8406 :
8407 308 : appendPQExpBufferStr(query,
8408 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8409 : "evtevent, evtowner, "
8410 : "array_to_string(array("
8411 : "select quote_literal(x) "
8412 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8413 : "e.evtfoid::regproc as evtfname "
8414 : "FROM pg_event_trigger e "
8415 : "ORDER BY e.oid");
8416 :
8417 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8418 :
8419 308 : ntups = PQntuples(res);
8420 :
8421 308 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8422 :
8423 308 : i_tableoid = PQfnumber(res, "tableoid");
8424 308 : i_oid = PQfnumber(res, "oid");
8425 308 : i_evtname = PQfnumber(res, "evtname");
8426 308 : i_evtevent = PQfnumber(res, "evtevent");
8427 308 : i_evtowner = PQfnumber(res, "evtowner");
8428 308 : i_evttags = PQfnumber(res, "evttags");
8429 308 : i_evtfname = PQfnumber(res, "evtfname");
8430 308 : i_evtenabled = PQfnumber(res, "evtenabled");
8431 :
8432 408 : for (i = 0; i < ntups; i++)
8433 : {
8434 100 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8435 100 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8436 100 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8437 100 : AssignDumpId(&evtinfo[i].dobj);
8438 100 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8439 100 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8440 100 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8441 100 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8442 100 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8443 100 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8444 100 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8445 :
8446 : /* Decide whether we want to dump it */
8447 100 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8448 : }
8449 :
8450 308 : PQclear(res);
8451 :
8452 308 : destroyPQExpBuffer(query);
8453 : }
8454 :
8455 : /*
8456 : * getProcLangs
8457 : * get basic information about every procedural language in the system
8458 : *
8459 : * NB: this must run after getFuncs() because we assume we can do
8460 : * findFuncByOid().
8461 : */
8462 : void
8463 308 : getProcLangs(Archive *fout)
8464 : {
8465 : PGresult *res;
8466 : int ntups;
8467 : int i;
8468 308 : PQExpBuffer query = createPQExpBuffer();
8469 : ProcLangInfo *planginfo;
8470 : int i_tableoid;
8471 : int i_oid;
8472 : int i_lanname;
8473 : int i_lanpltrusted;
8474 : int i_lanplcallfoid;
8475 : int i_laninline;
8476 : int i_lanvalidator;
8477 : int i_lanacl;
8478 : int i_acldefault;
8479 : int i_lanowner;
8480 :
8481 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8482 : "lanname, lanpltrusted, lanplcallfoid, "
8483 : "laninline, lanvalidator, "
8484 : "lanacl, "
8485 : "acldefault('l', lanowner) AS acldefault, "
8486 : "lanowner "
8487 : "FROM pg_language "
8488 : "WHERE lanispl "
8489 : "ORDER BY oid");
8490 :
8491 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8492 :
8493 308 : ntups = PQntuples(res);
8494 :
8495 308 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8496 :
8497 308 : i_tableoid = PQfnumber(res, "tableoid");
8498 308 : i_oid = PQfnumber(res, "oid");
8499 308 : i_lanname = PQfnumber(res, "lanname");
8500 308 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8501 308 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8502 308 : i_laninline = PQfnumber(res, "laninline");
8503 308 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8504 308 : i_lanacl = PQfnumber(res, "lanacl");
8505 308 : i_acldefault = PQfnumber(res, "acldefault");
8506 308 : i_lanowner = PQfnumber(res, "lanowner");
8507 :
8508 702 : for (i = 0; i < ntups; i++)
8509 : {
8510 394 : planginfo[i].dobj.objType = DO_PROCLANG;
8511 394 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8512 394 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8513 394 : AssignDumpId(&planginfo[i].dobj);
8514 :
8515 394 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8516 394 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8517 394 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8518 394 : planginfo[i].dacl.privtype = 0;
8519 394 : planginfo[i].dacl.initprivs = NULL;
8520 394 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8521 394 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8522 394 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8523 394 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8524 394 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8525 :
8526 : /* Decide whether we want to dump it */
8527 394 : selectDumpableProcLang(&(planginfo[i]), fout);
8528 :
8529 : /* Mark whether language has an ACL */
8530 394 : if (!PQgetisnull(res, i, i_lanacl))
8531 86 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8532 : }
8533 :
8534 308 : PQclear(res);
8535 :
8536 308 : destroyPQExpBuffer(query);
8537 308 : }
8538 :
8539 : /*
8540 : * getCasts
8541 : * get basic information about most casts in the system
8542 : *
8543 : * Skip casts from a range to its multirange, since we'll create those
8544 : * automatically.
8545 : */
8546 : void
8547 308 : getCasts(Archive *fout)
8548 : {
8549 : PGresult *res;
8550 : int ntups;
8551 : int i;
8552 308 : PQExpBuffer query = createPQExpBuffer();
8553 : CastInfo *castinfo;
8554 : int i_tableoid;
8555 : int i_oid;
8556 : int i_castsource;
8557 : int i_casttarget;
8558 : int i_castfunc;
8559 : int i_castcontext;
8560 : int i_castmethod;
8561 :
8562 308 : if (fout->remoteVersion >= 140000)
8563 : {
8564 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8565 : "castsource, casttarget, castfunc, castcontext, "
8566 : "castmethod "
8567 : "FROM pg_cast c "
8568 : "WHERE NOT EXISTS ( "
8569 : "SELECT 1 FROM pg_range r "
8570 : "WHERE c.castsource = r.rngtypid "
8571 : "AND c.casttarget = r.rngmultitypid "
8572 : ") "
8573 : "ORDER BY 3,4");
8574 : }
8575 : else
8576 : {
8577 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8578 : "castsource, casttarget, castfunc, castcontext, "
8579 : "castmethod "
8580 : "FROM pg_cast ORDER BY 3,4");
8581 : }
8582 :
8583 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8584 :
8585 308 : ntups = PQntuples(res);
8586 :
8587 308 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8588 :
8589 308 : i_tableoid = PQfnumber(res, "tableoid");
8590 308 : i_oid = PQfnumber(res, "oid");
8591 308 : i_castsource = PQfnumber(res, "castsource");
8592 308 : i_casttarget = PQfnumber(res, "casttarget");
8593 308 : i_castfunc = PQfnumber(res, "castfunc");
8594 308 : i_castcontext = PQfnumber(res, "castcontext");
8595 308 : i_castmethod = PQfnumber(res, "castmethod");
8596 :
8597 69162 : for (i = 0; i < ntups; i++)
8598 : {
8599 : PQExpBufferData namebuf;
8600 : TypeInfo *sTypeInfo;
8601 : TypeInfo *tTypeInfo;
8602 :
8603 68854 : castinfo[i].dobj.objType = DO_CAST;
8604 68854 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8605 68854 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8606 68854 : AssignDumpId(&castinfo[i].dobj);
8607 68854 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8608 68854 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8609 68854 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8610 68854 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8611 68854 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8612 :
8613 : /*
8614 : * Try to name cast as concatenation of typnames. This is only used
8615 : * for purposes of sorting. If we fail to find either type, the name
8616 : * will be an empty string.
8617 : */
8618 68854 : initPQExpBuffer(&namebuf);
8619 68854 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8620 68854 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8621 68854 : if (sTypeInfo && tTypeInfo)
8622 68854 : appendPQExpBuffer(&namebuf, "%s %s",
8623 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8624 68854 : castinfo[i].dobj.name = namebuf.data;
8625 :
8626 : /* Decide whether we want to dump it */
8627 68854 : selectDumpableCast(&(castinfo[i]), fout);
8628 : }
8629 :
8630 308 : PQclear(res);
8631 :
8632 308 : destroyPQExpBuffer(query);
8633 308 : }
8634 :
8635 : static char *
8636 174 : get_language_name(Archive *fout, Oid langid)
8637 : {
8638 : PQExpBuffer query;
8639 : PGresult *res;
8640 : char *lanname;
8641 :
8642 174 : query = createPQExpBuffer();
8643 174 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8644 174 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8645 174 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8646 174 : destroyPQExpBuffer(query);
8647 174 : PQclear(res);
8648 :
8649 174 : return lanname;
8650 : }
8651 :
8652 : /*
8653 : * getTransforms
8654 : * get basic information about every transform in the system
8655 : */
8656 : void
8657 308 : getTransforms(Archive *fout)
8658 : {
8659 : PGresult *res;
8660 : int ntups;
8661 : int i;
8662 : PQExpBuffer query;
8663 : TransformInfo *transforminfo;
8664 : int i_tableoid;
8665 : int i_oid;
8666 : int i_trftype;
8667 : int i_trflang;
8668 : int i_trffromsql;
8669 : int i_trftosql;
8670 :
8671 : /* Transforms didn't exist pre-9.5 */
8672 308 : if (fout->remoteVersion < 90500)
8673 0 : return;
8674 :
8675 308 : query = createPQExpBuffer();
8676 :
8677 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8678 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8679 : "FROM pg_transform "
8680 : "ORDER BY 3,4");
8681 :
8682 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8683 :
8684 308 : ntups = PQntuples(res);
8685 :
8686 308 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8687 :
8688 308 : i_tableoid = PQfnumber(res, "tableoid");
8689 308 : i_oid = PQfnumber(res, "oid");
8690 308 : i_trftype = PQfnumber(res, "trftype");
8691 308 : i_trflang = PQfnumber(res, "trflang");
8692 308 : i_trffromsql = PQfnumber(res, "trffromsql");
8693 308 : i_trftosql = PQfnumber(res, "trftosql");
8694 :
8695 408 : for (i = 0; i < ntups; i++)
8696 : {
8697 : PQExpBufferData namebuf;
8698 : TypeInfo *typeInfo;
8699 : char *lanname;
8700 :
8701 100 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8702 100 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8703 100 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8704 100 : AssignDumpId(&transforminfo[i].dobj);
8705 100 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8706 100 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8707 100 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8708 100 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8709 :
8710 : /*
8711 : * Try to name transform as concatenation of type and language name.
8712 : * This is only used for purposes of sorting. If we fail to find
8713 : * either, the name will be an empty string.
8714 : */
8715 100 : initPQExpBuffer(&namebuf);
8716 100 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8717 100 : lanname = get_language_name(fout, transforminfo[i].trflang);
8718 100 : if (typeInfo && lanname)
8719 100 : appendPQExpBuffer(&namebuf, "%s %s",
8720 : typeInfo->dobj.name, lanname);
8721 100 : transforminfo[i].dobj.name = namebuf.data;
8722 100 : free(lanname);
8723 :
8724 : /* Decide whether we want to dump it */
8725 100 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8726 : }
8727 :
8728 308 : PQclear(res);
8729 :
8730 308 : destroyPQExpBuffer(query);
8731 : }
8732 :
8733 : /*
8734 : * getTableAttrs -
8735 : * for each interesting table, read info about its attributes
8736 : * (names, types, default values, CHECK constraints, etc)
8737 : *
8738 : * modifies tblinfo
8739 : */
8740 : void
8741 308 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8742 : {
8743 308 : DumpOptions *dopt = fout->dopt;
8744 308 : PQExpBuffer q = createPQExpBuffer();
8745 308 : PQExpBuffer tbloids = createPQExpBuffer();
8746 308 : PQExpBuffer checkoids = createPQExpBuffer();
8747 : PGresult *res;
8748 : int ntups;
8749 : int curtblindx;
8750 : int i_attrelid;
8751 : int i_attnum;
8752 : int i_attname;
8753 : int i_atttypname;
8754 : int i_attstattarget;
8755 : int i_attstorage;
8756 : int i_typstorage;
8757 : int i_attidentity;
8758 : int i_attgenerated;
8759 : int i_attisdropped;
8760 : int i_attlen;
8761 : int i_attalign;
8762 : int i_attislocal;
8763 : int i_notnull_name;
8764 : int i_notnull_noinherit;
8765 : int i_notnull_islocal;
8766 : int i_attoptions;
8767 : int i_attcollation;
8768 : int i_attcompression;
8769 : int i_attfdwoptions;
8770 : int i_attmissingval;
8771 : int i_atthasdef;
8772 :
8773 : /*
8774 : * We want to perform just one query against pg_attribute, and then just
8775 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8776 : * (for CHECK constraints and for NOT NULL constraints). However, we
8777 : * mustn't try to select every row of those catalogs and then sort it out
8778 : * on the client side, because some of the server-side functions we need
8779 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8780 : * build an array of the OIDs of tables we care about (and now have lock
8781 : * on!), and use a WHERE clause to constrain which rows are selected.
8782 : */
8783 308 : appendPQExpBufferChar(tbloids, '{');
8784 308 : appendPQExpBufferChar(checkoids, '{');
8785 81230 : for (int i = 0; i < numTables; i++)
8786 : {
8787 80922 : TableInfo *tbinfo = &tblinfo[i];
8788 :
8789 : /* Don't bother to collect info for sequences */
8790 80922 : if (tbinfo->relkind == RELKIND_SEQUENCE)
8791 1184 : continue;
8792 :
8793 : /* Don't bother with uninteresting tables, either */
8794 79738 : if (!tbinfo->interesting)
8795 68200 : continue;
8796 :
8797 : /* OK, we need info for this table */
8798 11538 : if (tbloids->len > 1) /* do we have more than the '{'? */
8799 11334 : appendPQExpBufferChar(tbloids, ',');
8800 11538 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8801 :
8802 11538 : if (tbinfo->ncheck > 0)
8803 : {
8804 : /* Also make a list of the ones with check constraints */
8805 984 : if (checkoids->len > 1) /* do we have more than the '{'? */
8806 850 : appendPQExpBufferChar(checkoids, ',');
8807 984 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8808 : }
8809 : }
8810 308 : appendPQExpBufferChar(tbloids, '}');
8811 308 : appendPQExpBufferChar(checkoids, '}');
8812 :
8813 : /*
8814 : * Find all the user attributes and their types.
8815 : *
8816 : * Since we only want to dump COLLATE clauses for attributes whose
8817 : * collation is different from their type's default, we use a CASE here to
8818 : * suppress uninteresting attcollations cheaply.
8819 : */
8820 308 : appendPQExpBufferStr(q,
8821 : "SELECT\n"
8822 : "a.attrelid,\n"
8823 : "a.attnum,\n"
8824 : "a.attname,\n"
8825 : "a.attstattarget,\n"
8826 : "a.attstorage,\n"
8827 : "t.typstorage,\n"
8828 : "a.atthasdef,\n"
8829 : "a.attisdropped,\n"
8830 : "a.attlen,\n"
8831 : "a.attalign,\n"
8832 : "a.attislocal,\n"
8833 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8834 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8835 : "CASE WHEN a.attcollation <> t.typcollation "
8836 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8837 : "pg_catalog.array_to_string(ARRAY("
8838 : "SELECT pg_catalog.quote_ident(option_name) || "
8839 : "' ' || pg_catalog.quote_literal(option_value) "
8840 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8841 : "ORDER BY option_name"
8842 : "), E',\n ') AS attfdwoptions,\n");
8843 :
8844 : /*
8845 : * Find out any NOT NULL markings for each column. In 18 and up we read
8846 : * pg_constraint to obtain the constraint name. notnull_noinherit is set
8847 : * according to the NO INHERIT property. For versions prior to 18, we
8848 : * store an empty string as the name when a constraint is marked as
8849 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
8850 : * without a name); also, such cases are never NO INHERIT.
8851 : *
8852 : * We track in notnull_islocal whether the constraint was defined directly
8853 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
8854 : * might modify this later for servers older than 18; it's also in charge
8855 : * of determining the correct inhcount.
8856 : */
8857 308 : if (fout->remoteVersion >= 180000)
8858 308 : appendPQExpBufferStr(q,
8859 : "co.conname AS notnull_name,\n"
8860 : "co.connoinherit AS notnull_noinherit,\n"
8861 : "co.conislocal AS notnull_islocal,\n");
8862 : else
8863 0 : appendPQExpBufferStr(q,
8864 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8865 : "false AS notnull_noinherit,\n"
8866 : "a.attislocal AS notnull_islocal,\n");
8867 :
8868 308 : if (fout->remoteVersion >= 140000)
8869 308 : appendPQExpBufferStr(q,
8870 : "a.attcompression AS attcompression,\n");
8871 : else
8872 0 : appendPQExpBufferStr(q,
8873 : "'' AS attcompression,\n");
8874 :
8875 308 : if (fout->remoteVersion >= 100000)
8876 308 : appendPQExpBufferStr(q,
8877 : "a.attidentity,\n");
8878 : else
8879 0 : appendPQExpBufferStr(q,
8880 : "'' AS attidentity,\n");
8881 :
8882 308 : if (fout->remoteVersion >= 110000)
8883 308 : appendPQExpBufferStr(q,
8884 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8885 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8886 : else
8887 0 : appendPQExpBufferStr(q,
8888 : "NULL AS attmissingval,\n");
8889 :
8890 308 : if (fout->remoteVersion >= 120000)
8891 308 : appendPQExpBufferStr(q,
8892 : "a.attgenerated\n");
8893 : else
8894 0 : appendPQExpBufferStr(q,
8895 : "'' AS attgenerated\n");
8896 :
8897 : /* need left join to pg_type to not fail on dropped columns ... */
8898 308 : appendPQExpBuffer(q,
8899 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8900 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8901 : "LEFT JOIN pg_catalog.pg_type t "
8902 : "ON (a.atttypid = t.oid)\n",
8903 : tbloids->data);
8904 :
8905 : /*
8906 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
8907 : * entries. Also, we need to know if the NOT NULL for each column is
8908 : * backing a primary key.
8909 : */
8910 308 : if (fout->remoteVersion >= 180000)
8911 308 : appendPQExpBufferStr(q,
8912 : " LEFT JOIN pg_catalog.pg_constraint co ON "
8913 : "(a.attrelid = co.conrelid\n"
8914 : " AND co.contype = 'n' AND "
8915 : "co.conkey = array[a.attnum])\n");
8916 :
8917 308 : appendPQExpBufferStr(q,
8918 : "WHERE a.attnum > 0::pg_catalog.int2\n"
8919 : "ORDER BY a.attrelid, a.attnum");
8920 :
8921 308 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8922 :
8923 308 : ntups = PQntuples(res);
8924 :
8925 308 : i_attrelid = PQfnumber(res, "attrelid");
8926 308 : i_attnum = PQfnumber(res, "attnum");
8927 308 : i_attname = PQfnumber(res, "attname");
8928 308 : i_atttypname = PQfnumber(res, "atttypname");
8929 308 : i_attstattarget = PQfnumber(res, "attstattarget");
8930 308 : i_attstorage = PQfnumber(res, "attstorage");
8931 308 : i_typstorage = PQfnumber(res, "typstorage");
8932 308 : i_attidentity = PQfnumber(res, "attidentity");
8933 308 : i_attgenerated = PQfnumber(res, "attgenerated");
8934 308 : i_attisdropped = PQfnumber(res, "attisdropped");
8935 308 : i_attlen = PQfnumber(res, "attlen");
8936 308 : i_attalign = PQfnumber(res, "attalign");
8937 308 : i_attislocal = PQfnumber(res, "attislocal");
8938 308 : i_notnull_name = PQfnumber(res, "notnull_name");
8939 308 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
8940 308 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
8941 308 : i_attoptions = PQfnumber(res, "attoptions");
8942 308 : i_attcollation = PQfnumber(res, "attcollation");
8943 308 : i_attcompression = PQfnumber(res, "attcompression");
8944 308 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8945 308 : i_attmissingval = PQfnumber(res, "attmissingval");
8946 308 : i_atthasdef = PQfnumber(res, "atthasdef");
8947 :
8948 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
8949 308 : resetPQExpBuffer(tbloids);
8950 308 : appendPQExpBufferChar(tbloids, '{');
8951 :
8952 : /*
8953 : * Outer loop iterates once per table, not once per row. Incrementing of
8954 : * r is handled by the inner loop.
8955 : */
8956 308 : curtblindx = -1;
8957 11582 : for (int r = 0; r < ntups;)
8958 : {
8959 11274 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
8960 11274 : TableInfo *tbinfo = NULL;
8961 : int numatts;
8962 : bool hasdefaults;
8963 :
8964 : /* Count rows for this table */
8965 45178 : for (numatts = 1; numatts < ntups - r; numatts++)
8966 44980 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
8967 11076 : break;
8968 :
8969 : /*
8970 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8971 : * order.
8972 : */
8973 53766 : while (++curtblindx < numTables)
8974 : {
8975 53766 : tbinfo = &tblinfo[curtblindx];
8976 53766 : if (tbinfo->dobj.catId.oid == attrelid)
8977 11274 : break;
8978 : }
8979 11274 : if (curtblindx >= numTables)
8980 0 : pg_fatal("unrecognized table OID %u", attrelid);
8981 : /* cross-check that we only got requested tables */
8982 11274 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
8983 11274 : !tbinfo->interesting)
8984 0 : pg_fatal("unexpected column data for table \"%s\"",
8985 : tbinfo->dobj.name);
8986 :
8987 : /* Save data for this table */
8988 11274 : tbinfo->numatts = numatts;
8989 11274 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
8990 11274 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
8991 11274 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
8992 11274 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
8993 11274 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
8994 11274 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
8995 11274 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
8996 11274 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
8997 11274 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
8998 11274 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
8999 11274 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9000 11274 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9001 11274 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9002 11274 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9003 11274 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9004 11274 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9005 11274 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9006 11274 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9007 11274 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9008 11274 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9009 11274 : hasdefaults = false;
9010 :
9011 56452 : for (int j = 0; j < numatts; j++, r++)
9012 : {
9013 45178 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9014 0 : pg_fatal("invalid column numbering in table \"%s\"",
9015 : tbinfo->dobj.name);
9016 45178 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9017 45178 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9018 45178 : if (PQgetisnull(res, r, i_attstattarget))
9019 45102 : tbinfo->attstattarget[j] = -1;
9020 : else
9021 76 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9022 45178 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9023 45178 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9024 45178 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9025 45178 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9026 45178 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9027 45178 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9028 45178 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9029 45178 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9030 45178 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9031 :
9032 : /* Handle not-null constraint name and flags */
9033 45178 : determineNotNullFlags(fout, res, r,
9034 : tbinfo, j,
9035 : i_notnull_name, i_notnull_noinherit,
9036 : i_notnull_islocal);
9037 :
9038 45178 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9039 45178 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9040 45178 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9041 45178 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9042 45178 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9043 45178 : tbinfo->attrdefs[j] = NULL; /* fix below */
9044 45178 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9045 1726 : hasdefaults = true;
9046 : }
9047 :
9048 11274 : if (hasdefaults)
9049 : {
9050 : /* Collect OIDs of interesting tables that have defaults */
9051 1430 : if (tbloids->len > 1) /* do we have more than the '{'? */
9052 1298 : appendPQExpBufferChar(tbloids, ',');
9053 1430 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9054 : }
9055 : }
9056 :
9057 308 : PQclear(res);
9058 :
9059 : /*
9060 : * Now get info about column defaults. This is skipped for a data-only
9061 : * dump, as it is only needed for table schemas.
9062 : */
9063 308 : if (dopt->dumpSchema && tbloids->len > 1)
9064 : {
9065 : AttrDefInfo *attrdefs;
9066 : int numDefaults;
9067 122 : TableInfo *tbinfo = NULL;
9068 :
9069 122 : pg_log_info("finding table default expressions");
9070 :
9071 122 : appendPQExpBufferChar(tbloids, '}');
9072 :
9073 122 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9074 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9075 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9076 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9077 : "ORDER BY a.adrelid, a.adnum",
9078 : tbloids->data);
9079 :
9080 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9081 :
9082 122 : numDefaults = PQntuples(res);
9083 122 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9084 :
9085 122 : curtblindx = -1;
9086 1766 : for (int j = 0; j < numDefaults; j++)
9087 : {
9088 1644 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9089 1644 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9090 1644 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9091 1644 : int adnum = atoi(PQgetvalue(res, j, 3));
9092 1644 : char *adsrc = PQgetvalue(res, j, 4);
9093 :
9094 : /*
9095 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9096 : * OID order.
9097 : */
9098 1644 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9099 : {
9100 36780 : while (++curtblindx < numTables)
9101 : {
9102 36780 : tbinfo = &tblinfo[curtblindx];
9103 36780 : if (tbinfo->dobj.catId.oid == adrelid)
9104 1360 : break;
9105 : }
9106 1360 : if (curtblindx >= numTables)
9107 0 : pg_fatal("unrecognized table OID %u", adrelid);
9108 : }
9109 :
9110 1644 : if (adnum <= 0 || adnum > tbinfo->numatts)
9111 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9112 : adnum, tbinfo->dobj.name);
9113 :
9114 : /*
9115 : * dropped columns shouldn't have defaults, but just in case,
9116 : * ignore 'em
9117 : */
9118 1644 : if (tbinfo->attisdropped[adnum - 1])
9119 0 : continue;
9120 :
9121 1644 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9122 1644 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9123 1644 : attrdefs[j].dobj.catId.oid = adoid;
9124 1644 : AssignDumpId(&attrdefs[j].dobj);
9125 1644 : attrdefs[j].adtable = tbinfo;
9126 1644 : attrdefs[j].adnum = adnum;
9127 1644 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9128 :
9129 1644 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9130 1644 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9131 :
9132 1644 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9133 :
9134 : /*
9135 : * Figure out whether the default/generation expression should be
9136 : * dumped as part of the main CREATE TABLE (or similar) command or
9137 : * as a separate ALTER TABLE (or similar) command. The preference
9138 : * is to put it into the CREATE command, but in some cases that's
9139 : * not possible.
9140 : */
9141 1644 : if (tbinfo->attgenerated[adnum - 1])
9142 : {
9143 : /*
9144 : * Column generation expressions cannot be dumped separately,
9145 : * because there is no syntax for it. By setting separate to
9146 : * false here we prevent the "default" from being processed as
9147 : * its own dumpable object. Later, flagInhAttrs() will mark
9148 : * it as not to be dumped at all, if possible (that is, if it
9149 : * can be inherited from a parent).
9150 : */
9151 690 : attrdefs[j].separate = false;
9152 : }
9153 954 : else if (tbinfo->relkind == RELKIND_VIEW)
9154 : {
9155 : /*
9156 : * Defaults on a VIEW must always be dumped as separate ALTER
9157 : * TABLE commands.
9158 : */
9159 66 : attrdefs[j].separate = true;
9160 : }
9161 888 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9162 : {
9163 : /* column will be suppressed, print default separately */
9164 8 : attrdefs[j].separate = true;
9165 : }
9166 : else
9167 : {
9168 880 : attrdefs[j].separate = false;
9169 : }
9170 :
9171 1644 : if (!attrdefs[j].separate)
9172 : {
9173 : /*
9174 : * Mark the default as needing to appear before the table, so
9175 : * that any dependencies it has must be emitted before the
9176 : * CREATE TABLE. If this is not possible, we'll change to
9177 : * "separate" mode while sorting dependencies.
9178 : */
9179 1570 : addObjectDependency(&tbinfo->dobj,
9180 1570 : attrdefs[j].dobj.dumpId);
9181 : }
9182 :
9183 1644 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9184 : }
9185 :
9186 122 : PQclear(res);
9187 : }
9188 :
9189 : /*
9190 : * Get info about table CHECK constraints. This is skipped for a
9191 : * data-only dump, as it is only needed for table schemas.
9192 : */
9193 308 : if (dopt->dumpSchema && checkoids->len > 2)
9194 : {
9195 : ConstraintInfo *constrs;
9196 : int numConstrs;
9197 : int i_tableoid;
9198 : int i_oid;
9199 : int i_conrelid;
9200 : int i_conname;
9201 : int i_consrc;
9202 : int i_conislocal;
9203 : int i_convalidated;
9204 :
9205 124 : pg_log_info("finding table check constraints");
9206 :
9207 124 : resetPQExpBuffer(q);
9208 124 : appendPQExpBuffer(q,
9209 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9210 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9211 : "conislocal, convalidated "
9212 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9213 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9214 : "WHERE contype = 'c' "
9215 : "ORDER BY c.conrelid, c.conname",
9216 : checkoids->data);
9217 :
9218 124 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9219 :
9220 124 : numConstrs = PQntuples(res);
9221 124 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9222 :
9223 124 : i_tableoid = PQfnumber(res, "tableoid");
9224 124 : i_oid = PQfnumber(res, "oid");
9225 124 : i_conrelid = PQfnumber(res, "conrelid");
9226 124 : i_conname = PQfnumber(res, "conname");
9227 124 : i_consrc = PQfnumber(res, "consrc");
9228 124 : i_conislocal = PQfnumber(res, "conislocal");
9229 124 : i_convalidated = PQfnumber(res, "convalidated");
9230 :
9231 : /* As above, this loop iterates once per table, not once per row */
9232 124 : curtblindx = -1;
9233 1054 : for (int j = 0; j < numConstrs;)
9234 : {
9235 930 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9236 930 : TableInfo *tbinfo = NULL;
9237 : int numcons;
9238 :
9239 : /* Count rows for this table */
9240 1204 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9241 1080 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9242 806 : break;
9243 :
9244 : /*
9245 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9246 : * OID order.
9247 : */
9248 35470 : while (++curtblindx < numTables)
9249 : {
9250 35470 : tbinfo = &tblinfo[curtblindx];
9251 35470 : if (tbinfo->dobj.catId.oid == conrelid)
9252 930 : break;
9253 : }
9254 930 : if (curtblindx >= numTables)
9255 0 : pg_fatal("unrecognized table OID %u", conrelid);
9256 :
9257 930 : if (numcons != tbinfo->ncheck)
9258 : {
9259 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9260 : "expected %d check constraints on table \"%s\" but found %d",
9261 : tbinfo->ncheck),
9262 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9263 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9264 0 : exit_nicely(1);
9265 : }
9266 :
9267 930 : tbinfo->checkexprs = constrs + j;
9268 :
9269 2134 : for (int c = 0; c < numcons; c++, j++)
9270 : {
9271 1204 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9272 :
9273 1204 : constrs[j].dobj.objType = DO_CONSTRAINT;
9274 1204 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9275 1204 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9276 1204 : AssignDumpId(&constrs[j].dobj);
9277 1204 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9278 1204 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9279 1204 : constrs[j].contable = tbinfo;
9280 1204 : constrs[j].condomain = NULL;
9281 1204 : constrs[j].contype = 'c';
9282 1204 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9283 1204 : constrs[j].confrelid = InvalidOid;
9284 1204 : constrs[j].conindex = 0;
9285 1204 : constrs[j].condeferrable = false;
9286 1204 : constrs[j].condeferred = false;
9287 1204 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9288 :
9289 : /*
9290 : * An unvalidated constraint needs to be dumped separately, so
9291 : * that potentially-violating existing data is loaded before
9292 : * the constraint.
9293 : */
9294 1204 : constrs[j].separate = !validated;
9295 :
9296 1204 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9297 :
9298 : /*
9299 : * Mark the constraint as needing to appear before the table
9300 : * --- this is so that any other dependencies of the
9301 : * constraint will be emitted before we try to create the
9302 : * table. If the constraint is to be dumped separately, it
9303 : * will be dumped after data is loaded anyway, so don't do it.
9304 : * (There's an automatic dependency in the opposite direction
9305 : * anyway, so don't need to add one manually here.)
9306 : */
9307 1204 : if (!constrs[j].separate)
9308 1084 : addObjectDependency(&tbinfo->dobj,
9309 1084 : constrs[j].dobj.dumpId);
9310 :
9311 : /*
9312 : * We will detect later whether the constraint must be split
9313 : * out from the table definition.
9314 : */
9315 : }
9316 : }
9317 :
9318 124 : PQclear(res);
9319 : }
9320 :
9321 308 : destroyPQExpBuffer(q);
9322 308 : destroyPQExpBuffer(tbloids);
9323 308 : destroyPQExpBuffer(checkoids);
9324 308 : }
9325 :
9326 : /*
9327 : * Based on the getTableAttrs query's row corresponding to one column, set
9328 : * the name and flags to handle a not-null constraint for that column in
9329 : * the tbinfo struct.
9330 : *
9331 : * Result row 'r' is for tbinfo's attribute 'j'.
9332 : *
9333 : * There are three possibilities:
9334 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9335 : * (the constraint name) remains NULL.
9336 : * 2) The column has a constraint with no name (this is the case when
9337 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9338 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9339 : * 3) The column has a constraint with a known name; in that case
9340 : * notnull_constrs carries that name and dumpTableSchema will print
9341 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9342 : * (table_column_not_null), there's no need to print that name in the dump,
9343 : * so notnull_constrs is set to the empty string and it behaves as the case
9344 : * above.
9345 : *
9346 : * In a child table that inherits from a parent already containing NOT NULL
9347 : * constraints and the columns in the child don't have their own NOT NULL
9348 : * declarations, we suppress printing constraints in the child: the
9349 : * constraints are acquired at the point where the child is attached to the
9350 : * parent. This is tracked in ->notnull_islocal (which is set in flagInhAttrs
9351 : * for servers pre-18).
9352 : *
9353 : * Any of these constraints might have the NO INHERIT bit. If so we set
9354 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9355 : *
9356 : * In case 3 above, the name comparison is a bit of a hack; it actually fails
9357 : * to do the right thing in all but the trivial case. However, the downside
9358 : * of getting it wrong is simply that the name is printed rather than
9359 : * suppressed, so it's not a big deal.
9360 : */
9361 : static void
9362 45178 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9363 : TableInfo *tbinfo, int j,
9364 : int i_notnull_name, int i_notnull_noinherit,
9365 : int i_notnull_islocal)
9366 : {
9367 45178 : DumpOptions *dopt = fout->dopt;
9368 :
9369 : /*
9370 : * notnull_noinh is straight from the query result. notnull_islocal also,
9371 : * though flagInhAttrs may change that one later in versions < 18.
9372 : */
9373 45178 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9374 45178 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9375 :
9376 : /*
9377 : * Determine a constraint name to use. If the column is not marked not-
9378 : * null, we set NULL which cues ... to do nothing. An empty string says
9379 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9380 : * use.
9381 : */
9382 45178 : if (fout->remoteVersion < 180000)
9383 : {
9384 : /*
9385 : * < 18 doesn't have not-null names, so an unnamed constraint is
9386 : * sufficient.
9387 : */
9388 0 : if (PQgetisnull(res, r, i_notnull_name))
9389 0 : tbinfo->notnull_constrs[j] = NULL;
9390 : else
9391 0 : tbinfo->notnull_constrs[j] = "";
9392 : }
9393 : else
9394 : {
9395 45178 : if (PQgetisnull(res, r, i_notnull_name))
9396 40894 : tbinfo->notnull_constrs[j] = NULL;
9397 : else
9398 : {
9399 : /*
9400 : * In binary upgrade of inheritance child tables, must have a
9401 : * constraint name that we can UPDATE later.
9402 : */
9403 4284 : if (dopt->binary_upgrade &&
9404 480 : !tbinfo->ispartition &&
9405 344 : !tbinfo->notnull_islocal)
9406 : {
9407 0 : tbinfo->notnull_constrs[j] =
9408 0 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9409 : }
9410 : else
9411 : {
9412 : char *default_name;
9413 :
9414 : /* XXX should match ChooseConstraintName better */
9415 4284 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9416 4284 : tbinfo->attnames[j]);
9417 4284 : if (strcmp(default_name,
9418 4284 : PQgetvalue(res, r, i_notnull_name)) == 0)
9419 2898 : tbinfo->notnull_constrs[j] = "";
9420 : else
9421 : {
9422 1386 : tbinfo->notnull_constrs[j] =
9423 1386 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9424 : }
9425 : }
9426 : }
9427 : }
9428 45178 : }
9429 :
9430 : /*
9431 : * Test whether a column should be printed as part of table's CREATE TABLE.
9432 : * Column number is zero-based.
9433 : *
9434 : * Normally this is always true, but it's false for dropped columns, as well
9435 : * as those that were inherited without any local definition. (If we print
9436 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9437 : * For partitions, it's always true, because we want the partitions to be
9438 : * created independently and ATTACH PARTITION used afterwards.
9439 : *
9440 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9441 : * attisdropped state later, so as to keep control of the physical column
9442 : * order.
9443 : *
9444 : * This function exists because there are scattered nonobvious places that
9445 : * must be kept in sync with this decision.
9446 : */
9447 : bool
9448 75350 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9449 : {
9450 75350 : if (dopt->binary_upgrade)
9451 11628 : return true;
9452 63722 : if (tbinfo->attisdropped[colno])
9453 1376 : return false;
9454 62346 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9455 : }
9456 :
9457 :
9458 : /*
9459 : * getTSParsers:
9460 : * get information about all text search parsers in the system catalogs
9461 : */
9462 : void
9463 308 : getTSParsers(Archive *fout)
9464 : {
9465 : PGresult *res;
9466 : int ntups;
9467 : int i;
9468 : PQExpBuffer query;
9469 : TSParserInfo *prsinfo;
9470 : int i_tableoid;
9471 : int i_oid;
9472 : int i_prsname;
9473 : int i_prsnamespace;
9474 : int i_prsstart;
9475 : int i_prstoken;
9476 : int i_prsend;
9477 : int i_prsheadline;
9478 : int i_prslextype;
9479 :
9480 308 : query = createPQExpBuffer();
9481 :
9482 : /*
9483 : * find all text search objects, including builtin ones; we filter out
9484 : * system-defined objects at dump-out time.
9485 : */
9486 :
9487 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9488 : "prsstart::oid, prstoken::oid, "
9489 : "prsend::oid, prsheadline::oid, prslextype::oid "
9490 : "FROM pg_ts_parser");
9491 :
9492 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9493 :
9494 308 : ntups = PQntuples(res);
9495 :
9496 308 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9497 :
9498 308 : i_tableoid = PQfnumber(res, "tableoid");
9499 308 : i_oid = PQfnumber(res, "oid");
9500 308 : i_prsname = PQfnumber(res, "prsname");
9501 308 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9502 308 : i_prsstart = PQfnumber(res, "prsstart");
9503 308 : i_prstoken = PQfnumber(res, "prstoken");
9504 308 : i_prsend = PQfnumber(res, "prsend");
9505 308 : i_prsheadline = PQfnumber(res, "prsheadline");
9506 308 : i_prslextype = PQfnumber(res, "prslextype");
9507 :
9508 702 : for (i = 0; i < ntups; i++)
9509 : {
9510 394 : prsinfo[i].dobj.objType = DO_TSPARSER;
9511 394 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9512 394 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9513 394 : AssignDumpId(&prsinfo[i].dobj);
9514 394 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9515 788 : prsinfo[i].dobj.namespace =
9516 394 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9517 394 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9518 394 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9519 394 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9520 394 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9521 394 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9522 :
9523 : /* Decide whether we want to dump it */
9524 394 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9525 : }
9526 :
9527 308 : PQclear(res);
9528 :
9529 308 : destroyPQExpBuffer(query);
9530 308 : }
9531 :
9532 : /*
9533 : * getTSDictionaries:
9534 : * get information about all text search dictionaries in the system catalogs
9535 : */
9536 : void
9537 308 : getTSDictionaries(Archive *fout)
9538 : {
9539 : PGresult *res;
9540 : int ntups;
9541 : int i;
9542 : PQExpBuffer query;
9543 : TSDictInfo *dictinfo;
9544 : int i_tableoid;
9545 : int i_oid;
9546 : int i_dictname;
9547 : int i_dictnamespace;
9548 : int i_dictowner;
9549 : int i_dicttemplate;
9550 : int i_dictinitoption;
9551 :
9552 308 : query = createPQExpBuffer();
9553 :
9554 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9555 : "dictnamespace, dictowner, "
9556 : "dicttemplate, dictinitoption "
9557 : "FROM pg_ts_dict");
9558 :
9559 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9560 :
9561 308 : ntups = PQntuples(res);
9562 :
9563 308 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9564 :
9565 308 : i_tableoid = PQfnumber(res, "tableoid");
9566 308 : i_oid = PQfnumber(res, "oid");
9567 308 : i_dictname = PQfnumber(res, "dictname");
9568 308 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9569 308 : i_dictowner = PQfnumber(res, "dictowner");
9570 308 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9571 308 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9572 :
9573 9452 : for (i = 0; i < ntups; i++)
9574 : {
9575 9144 : dictinfo[i].dobj.objType = DO_TSDICT;
9576 9144 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9577 9144 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9578 9144 : AssignDumpId(&dictinfo[i].dobj);
9579 9144 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9580 18288 : dictinfo[i].dobj.namespace =
9581 9144 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9582 9144 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9583 9144 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9584 9144 : if (PQgetisnull(res, i, i_dictinitoption))
9585 394 : dictinfo[i].dictinitoption = NULL;
9586 : else
9587 8750 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9588 :
9589 : /* Decide whether we want to dump it */
9590 9144 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9591 : }
9592 :
9593 308 : PQclear(res);
9594 :
9595 308 : destroyPQExpBuffer(query);
9596 308 : }
9597 :
9598 : /*
9599 : * getTSTemplates:
9600 : * get information about all text search templates in the system catalogs
9601 : */
9602 : void
9603 308 : getTSTemplates(Archive *fout)
9604 : {
9605 : PGresult *res;
9606 : int ntups;
9607 : int i;
9608 : PQExpBuffer query;
9609 : TSTemplateInfo *tmplinfo;
9610 : int i_tableoid;
9611 : int i_oid;
9612 : int i_tmplname;
9613 : int i_tmplnamespace;
9614 : int i_tmplinit;
9615 : int i_tmpllexize;
9616 :
9617 308 : query = createPQExpBuffer();
9618 :
9619 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9620 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9621 : "FROM pg_ts_template");
9622 :
9623 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9624 :
9625 308 : ntups = PQntuples(res);
9626 :
9627 308 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9628 :
9629 308 : i_tableoid = PQfnumber(res, "tableoid");
9630 308 : i_oid = PQfnumber(res, "oid");
9631 308 : i_tmplname = PQfnumber(res, "tmplname");
9632 308 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9633 308 : i_tmplinit = PQfnumber(res, "tmplinit");
9634 308 : i_tmpllexize = PQfnumber(res, "tmpllexize");
9635 :
9636 1934 : for (i = 0; i < ntups; i++)
9637 : {
9638 1626 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9639 1626 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9640 1626 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9641 1626 : AssignDumpId(&tmplinfo[i].dobj);
9642 1626 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9643 3252 : tmplinfo[i].dobj.namespace =
9644 1626 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9645 1626 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9646 1626 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9647 :
9648 : /* Decide whether we want to dump it */
9649 1626 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
9650 : }
9651 :
9652 308 : PQclear(res);
9653 :
9654 308 : destroyPQExpBuffer(query);
9655 308 : }
9656 :
9657 : /*
9658 : * getTSConfigurations:
9659 : * get information about all text search configurations
9660 : */
9661 : void
9662 308 : getTSConfigurations(Archive *fout)
9663 : {
9664 : PGresult *res;
9665 : int ntups;
9666 : int i;
9667 : PQExpBuffer query;
9668 : TSConfigInfo *cfginfo;
9669 : int i_tableoid;
9670 : int i_oid;
9671 : int i_cfgname;
9672 : int i_cfgnamespace;
9673 : int i_cfgowner;
9674 : int i_cfgparser;
9675 :
9676 308 : query = createPQExpBuffer();
9677 :
9678 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9679 : "cfgnamespace, cfgowner, cfgparser "
9680 : "FROM pg_ts_config");
9681 :
9682 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9683 :
9684 308 : ntups = PQntuples(res);
9685 :
9686 308 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9687 :
9688 308 : i_tableoid = PQfnumber(res, "tableoid");
9689 308 : i_oid = PQfnumber(res, "oid");
9690 308 : i_cfgname = PQfnumber(res, "cfgname");
9691 308 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9692 308 : i_cfgowner = PQfnumber(res, "cfgowner");
9693 308 : i_cfgparser = PQfnumber(res, "cfgparser");
9694 :
9695 9382 : for (i = 0; i < ntups; i++)
9696 : {
9697 9074 : cfginfo[i].dobj.objType = DO_TSCONFIG;
9698 9074 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9699 9074 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9700 9074 : AssignDumpId(&cfginfo[i].dobj);
9701 9074 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9702 18148 : cfginfo[i].dobj.namespace =
9703 9074 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9704 9074 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9705 9074 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9706 :
9707 : /* Decide whether we want to dump it */
9708 9074 : selectDumpableObject(&(cfginfo[i].dobj), fout);
9709 : }
9710 :
9711 308 : PQclear(res);
9712 :
9713 308 : destroyPQExpBuffer(query);
9714 308 : }
9715 :
9716 : /*
9717 : * getForeignDataWrappers:
9718 : * get information about all foreign-data wrappers in the system catalogs
9719 : */
9720 : void
9721 308 : getForeignDataWrappers(Archive *fout)
9722 : {
9723 : PGresult *res;
9724 : int ntups;
9725 : int i;
9726 : PQExpBuffer query;
9727 : FdwInfo *fdwinfo;
9728 : int i_tableoid;
9729 : int i_oid;
9730 : int i_fdwname;
9731 : int i_fdwowner;
9732 : int i_fdwhandler;
9733 : int i_fdwvalidator;
9734 : int i_fdwacl;
9735 : int i_acldefault;
9736 : int i_fdwoptions;
9737 :
9738 308 : query = createPQExpBuffer();
9739 :
9740 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9741 : "fdwowner, "
9742 : "fdwhandler::pg_catalog.regproc, "
9743 : "fdwvalidator::pg_catalog.regproc, "
9744 : "fdwacl, "
9745 : "acldefault('F', fdwowner) AS acldefault, "
9746 : "array_to_string(ARRAY("
9747 : "SELECT quote_ident(option_name) || ' ' || "
9748 : "quote_literal(option_value) "
9749 : "FROM pg_options_to_table(fdwoptions) "
9750 : "ORDER BY option_name"
9751 : "), E',\n ') AS fdwoptions "
9752 : "FROM pg_foreign_data_wrapper");
9753 :
9754 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9755 :
9756 308 : ntups = PQntuples(res);
9757 :
9758 308 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9759 :
9760 308 : i_tableoid = PQfnumber(res, "tableoid");
9761 308 : i_oid = PQfnumber(res, "oid");
9762 308 : i_fdwname = PQfnumber(res, "fdwname");
9763 308 : i_fdwowner = PQfnumber(res, "fdwowner");
9764 308 : i_fdwhandler = PQfnumber(res, "fdwhandler");
9765 308 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9766 308 : i_fdwacl = PQfnumber(res, "fdwacl");
9767 308 : i_acldefault = PQfnumber(res, "acldefault");
9768 308 : i_fdwoptions = PQfnumber(res, "fdwoptions");
9769 :
9770 446 : for (i = 0; i < ntups; i++)
9771 : {
9772 138 : fdwinfo[i].dobj.objType = DO_FDW;
9773 138 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9774 138 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9775 138 : AssignDumpId(&fdwinfo[i].dobj);
9776 138 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9777 138 : fdwinfo[i].dobj.namespace = NULL;
9778 138 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9779 138 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9780 138 : fdwinfo[i].dacl.privtype = 0;
9781 138 : fdwinfo[i].dacl.initprivs = NULL;
9782 138 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9783 138 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9784 138 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9785 138 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9786 :
9787 : /* Decide whether we want to dump it */
9788 138 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
9789 :
9790 : /* Mark whether FDW has an ACL */
9791 138 : if (!PQgetisnull(res, i, i_fdwacl))
9792 86 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9793 : }
9794 :
9795 308 : PQclear(res);
9796 :
9797 308 : destroyPQExpBuffer(query);
9798 308 : }
9799 :
9800 : /*
9801 : * getForeignServers:
9802 : * get information about all foreign servers in the system catalogs
9803 : */
9804 : void
9805 308 : getForeignServers(Archive *fout)
9806 : {
9807 : PGresult *res;
9808 : int ntups;
9809 : int i;
9810 : PQExpBuffer query;
9811 : ForeignServerInfo *srvinfo;
9812 : int i_tableoid;
9813 : int i_oid;
9814 : int i_srvname;
9815 : int i_srvowner;
9816 : int i_srvfdw;
9817 : int i_srvtype;
9818 : int i_srvversion;
9819 : int i_srvacl;
9820 : int i_acldefault;
9821 : int i_srvoptions;
9822 :
9823 308 : query = createPQExpBuffer();
9824 :
9825 308 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9826 : "srvowner, "
9827 : "srvfdw, srvtype, srvversion, srvacl, "
9828 : "acldefault('S', srvowner) AS acldefault, "
9829 : "array_to_string(ARRAY("
9830 : "SELECT quote_ident(option_name) || ' ' || "
9831 : "quote_literal(option_value) "
9832 : "FROM pg_options_to_table(srvoptions) "
9833 : "ORDER BY option_name"
9834 : "), E',\n ') AS srvoptions "
9835 : "FROM pg_foreign_server");
9836 :
9837 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9838 :
9839 308 : ntups = PQntuples(res);
9840 :
9841 308 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9842 :
9843 308 : i_tableoid = PQfnumber(res, "tableoid");
9844 308 : i_oid = PQfnumber(res, "oid");
9845 308 : i_srvname = PQfnumber(res, "srvname");
9846 308 : i_srvowner = PQfnumber(res, "srvowner");
9847 308 : i_srvfdw = PQfnumber(res, "srvfdw");
9848 308 : i_srvtype = PQfnumber(res, "srvtype");
9849 308 : i_srvversion = PQfnumber(res, "srvversion");
9850 308 : i_srvacl = PQfnumber(res, "srvacl");
9851 308 : i_acldefault = PQfnumber(res, "acldefault");
9852 308 : i_srvoptions = PQfnumber(res, "srvoptions");
9853 :
9854 454 : for (i = 0; i < ntups; i++)
9855 : {
9856 146 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9857 146 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9858 146 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9859 146 : AssignDumpId(&srvinfo[i].dobj);
9860 146 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9861 146 : srvinfo[i].dobj.namespace = NULL;
9862 146 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9863 146 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9864 146 : srvinfo[i].dacl.privtype = 0;
9865 146 : srvinfo[i].dacl.initprivs = NULL;
9866 146 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9867 146 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9868 146 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9869 146 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9870 146 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9871 :
9872 : /* Decide whether we want to dump it */
9873 146 : selectDumpableObject(&(srvinfo[i].dobj), fout);
9874 :
9875 : /* Servers have user mappings */
9876 146 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9877 :
9878 : /* Mark whether server has an ACL */
9879 146 : if (!PQgetisnull(res, i, i_srvacl))
9880 86 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9881 : }
9882 :
9883 308 : PQclear(res);
9884 :
9885 308 : destroyPQExpBuffer(query);
9886 308 : }
9887 :
9888 : /*
9889 : * getDefaultACLs:
9890 : * get information about all default ACL information in the system catalogs
9891 : */
9892 : void
9893 308 : getDefaultACLs(Archive *fout)
9894 : {
9895 308 : DumpOptions *dopt = fout->dopt;
9896 : DefaultACLInfo *daclinfo;
9897 : PQExpBuffer query;
9898 : PGresult *res;
9899 : int i_oid;
9900 : int i_tableoid;
9901 : int i_defaclrole;
9902 : int i_defaclnamespace;
9903 : int i_defaclobjtype;
9904 : int i_defaclacl;
9905 : int i_acldefault;
9906 : int i,
9907 : ntups;
9908 :
9909 308 : query = createPQExpBuffer();
9910 :
9911 : /*
9912 : * Global entries (with defaclnamespace=0) replace the hard-wired default
9913 : * ACL for their object type. We should dump them as deltas from the
9914 : * default ACL, since that will be used as a starting point for
9915 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
9916 : * non-global entries can only add privileges not revoke them. We must
9917 : * dump those as-is (i.e., as deltas from an empty ACL).
9918 : *
9919 : * We can use defaclobjtype as the object type for acldefault(), except
9920 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
9921 : * 's'.
9922 : */
9923 308 : appendPQExpBufferStr(query,
9924 : "SELECT oid, tableoid, "
9925 : "defaclrole, "
9926 : "defaclnamespace, "
9927 : "defaclobjtype, "
9928 : "defaclacl, "
9929 : "CASE WHEN defaclnamespace = 0 THEN "
9930 : "acldefault(CASE WHEN defaclobjtype = 'S' "
9931 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
9932 : "defaclrole) ELSE '{}' END AS acldefault "
9933 : "FROM pg_default_acl");
9934 :
9935 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9936 :
9937 308 : ntups = PQntuples(res);
9938 :
9939 308 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9940 :
9941 308 : i_oid = PQfnumber(res, "oid");
9942 308 : i_tableoid = PQfnumber(res, "tableoid");
9943 308 : i_defaclrole = PQfnumber(res, "defaclrole");
9944 308 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9945 308 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9946 308 : i_defaclacl = PQfnumber(res, "defaclacl");
9947 308 : i_acldefault = PQfnumber(res, "acldefault");
9948 :
9949 652 : for (i = 0; i < ntups; i++)
9950 : {
9951 344 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9952 :
9953 344 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9954 344 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9955 344 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9956 344 : AssignDumpId(&daclinfo[i].dobj);
9957 : /* cheesy ... is it worth coming up with a better object name? */
9958 344 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9959 :
9960 344 : if (nspid != InvalidOid)
9961 172 : daclinfo[i].dobj.namespace = findNamespace(nspid);
9962 : else
9963 172 : daclinfo[i].dobj.namespace = NULL;
9964 :
9965 344 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9966 344 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9967 344 : daclinfo[i].dacl.privtype = 0;
9968 344 : daclinfo[i].dacl.initprivs = NULL;
9969 344 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
9970 344 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9971 :
9972 : /* Default ACLs are ACLs, of course */
9973 344 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9974 :
9975 : /* Decide whether we want to dump it */
9976 344 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9977 : }
9978 :
9979 308 : PQclear(res);
9980 :
9981 308 : destroyPQExpBuffer(query);
9982 308 : }
9983 :
9984 : /*
9985 : * getRoleName -- look up the name of a role, given its OID
9986 : *
9987 : * In current usage, we don't expect failures, so error out for a bad OID.
9988 : */
9989 : static const char *
9990 966250 : getRoleName(const char *roleoid_str)
9991 : {
9992 966250 : Oid roleoid = atooid(roleoid_str);
9993 :
9994 : /*
9995 : * Do binary search to find the appropriate item.
9996 : */
9997 966250 : if (nrolenames > 0)
9998 : {
9999 966250 : RoleNameItem *low = &rolenames[0];
10000 966250 : RoleNameItem *high = &rolenames[nrolenames - 1];
10001 :
10002 3865032 : while (low <= high)
10003 : {
10004 3865032 : RoleNameItem *middle = low + (high - low) / 2;
10005 :
10006 3865032 : if (roleoid < middle->roleoid)
10007 2896692 : high = middle - 1;
10008 968340 : else if (roleoid > middle->roleoid)
10009 2090 : low = middle + 1;
10010 : else
10011 966250 : return middle->rolename; /* found a match */
10012 : }
10013 : }
10014 :
10015 0 : pg_fatal("role with OID %u does not exist", roleoid);
10016 : return NULL; /* keep compiler quiet */
10017 : }
10018 :
10019 : /*
10020 : * collectRoleNames --
10021 : *
10022 : * Construct a table of all known roles.
10023 : * The table is sorted by OID for speed in lookup.
10024 : */
10025 : static void
10026 310 : collectRoleNames(Archive *fout)
10027 : {
10028 : PGresult *res;
10029 : const char *query;
10030 : int i;
10031 :
10032 310 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10033 :
10034 310 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10035 :
10036 310 : nrolenames = PQntuples(res);
10037 :
10038 310 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10039 :
10040 5980 : for (i = 0; i < nrolenames; i++)
10041 : {
10042 5670 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10043 5670 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10044 : }
10045 :
10046 310 : PQclear(res);
10047 310 : }
10048 :
10049 : /*
10050 : * getAdditionalACLs
10051 : *
10052 : * We have now created all the DumpableObjects, and collected the ACL data
10053 : * that appears in the directly-associated catalog entries. However, there's
10054 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10055 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10056 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10057 : * Also, in versions having the pg_init_privs catalog, read that and load the
10058 : * information into the relevant DumpableObjects.
10059 : */
10060 : static void
10061 304 : getAdditionalACLs(Archive *fout)
10062 : {
10063 304 : PQExpBuffer query = createPQExpBuffer();
10064 : PGresult *res;
10065 : int ntups,
10066 : i;
10067 :
10068 : /* Check for per-column ACLs */
10069 304 : appendPQExpBufferStr(query,
10070 : "SELECT DISTINCT attrelid FROM pg_attribute "
10071 : "WHERE attacl IS NOT NULL");
10072 :
10073 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10074 :
10075 304 : ntups = PQntuples(res);
10076 936 : for (i = 0; i < ntups; i++)
10077 : {
10078 632 : Oid relid = atooid(PQgetvalue(res, i, 0));
10079 : TableInfo *tblinfo;
10080 :
10081 632 : tblinfo = findTableByOid(relid);
10082 : /* OK to ignore tables we haven't got a DumpableObject for */
10083 632 : if (tblinfo)
10084 : {
10085 632 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10086 632 : tblinfo->hascolumnACLs = true;
10087 : }
10088 : }
10089 304 : PQclear(res);
10090 :
10091 : /* Fetch initial-privileges data */
10092 304 : if (fout->remoteVersion >= 90600)
10093 : {
10094 304 : printfPQExpBuffer(query,
10095 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10096 : "FROM pg_init_privs");
10097 :
10098 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10099 :
10100 304 : ntups = PQntuples(res);
10101 69320 : for (i = 0; i < ntups; i++)
10102 : {
10103 69016 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10104 69016 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10105 69016 : int objsubid = atoi(PQgetvalue(res, i, 2));
10106 69016 : char privtype = *(PQgetvalue(res, i, 3));
10107 69016 : char *initprivs = PQgetvalue(res, i, 4);
10108 : CatalogId objId;
10109 : DumpableObject *dobj;
10110 :
10111 69016 : objId.tableoid = classoid;
10112 69016 : objId.oid = objoid;
10113 69016 : dobj = findObjectByCatalogId(objId);
10114 : /* OK to ignore entries we haven't got a DumpableObject for */
10115 69016 : if (dobj)
10116 : {
10117 : /* Cope with sub-object initprivs */
10118 49644 : if (objsubid != 0)
10119 : {
10120 5216 : if (dobj->objType == DO_TABLE)
10121 : {
10122 : /* For a column initprivs, set the table's ACL flags */
10123 5216 : dobj->components |= DUMP_COMPONENT_ACL;
10124 5216 : ((TableInfo *) dobj)->hascolumnACLs = true;
10125 : }
10126 : else
10127 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10128 : classoid, objoid, objsubid);
10129 5512 : continue;
10130 : }
10131 :
10132 : /*
10133 : * We ignore any pg_init_privs.initprivs entry for the public
10134 : * schema, as explained in getNamespaces().
10135 : */
10136 44428 : if (dobj->objType == DO_NAMESPACE &&
10137 600 : strcmp(dobj->name, "public") == 0)
10138 296 : continue;
10139 :
10140 : /* Else it had better be of a type we think has ACLs */
10141 44132 : if (dobj->objType == DO_NAMESPACE ||
10142 43828 : dobj->objType == DO_TYPE ||
10143 43780 : dobj->objType == DO_FUNC ||
10144 43600 : dobj->objType == DO_AGG ||
10145 43552 : dobj->objType == DO_TABLE ||
10146 0 : dobj->objType == DO_PROCLANG ||
10147 0 : dobj->objType == DO_FDW ||
10148 0 : dobj->objType == DO_FOREIGN_SERVER)
10149 44132 : {
10150 44132 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10151 :
10152 44132 : daobj->dacl.privtype = privtype;
10153 44132 : daobj->dacl.initprivs = pstrdup(initprivs);
10154 : }
10155 : else
10156 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10157 : classoid, objoid, objsubid);
10158 : }
10159 : }
10160 304 : PQclear(res);
10161 : }
10162 :
10163 304 : destroyPQExpBuffer(query);
10164 304 : }
10165 :
10166 : /*
10167 : * dumpCommentExtended --
10168 : *
10169 : * This routine is used to dump any comments associated with the
10170 : * object handed to this routine. The routine takes the object type
10171 : * and object name (ready to print, except for schema decoration), plus
10172 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10173 : * plus catalog ID and subid which are the lookup key for pg_description,
10174 : * plus the dump ID for the object (for setting a dependency).
10175 : * If a matching pg_description entry is found, it is dumped.
10176 : *
10177 : * Note: in some cases, such as comments for triggers and rules, the "type"
10178 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10179 : * but it doesn't seem worth complicating the API for all callers to make
10180 : * it cleaner.
10181 : *
10182 : * Note: although this routine takes a dumpId for dependency purposes,
10183 : * that purpose is just to mark the dependency in the emitted dump file
10184 : * for possible future use by pg_restore. We do NOT use it for determining
10185 : * ordering of the comment in the dump file, because this routine is called
10186 : * after dependency sorting occurs. This routine should be called just after
10187 : * calling ArchiveEntry() for the specified object.
10188 : */
10189 : static void
10190 12546 : dumpCommentExtended(Archive *fout, const char *type,
10191 : const char *name, const char *namespace,
10192 : const char *owner, CatalogId catalogId,
10193 : int subid, DumpId dumpId,
10194 : const char *initdb_comment)
10195 : {
10196 12546 : DumpOptions *dopt = fout->dopt;
10197 : CommentItem *comments;
10198 : int ncomments;
10199 :
10200 : /* do nothing, if --no-comments is supplied */
10201 12546 : if (dopt->no_comments)
10202 0 : return;
10203 :
10204 : /* Comments are schema not data ... except LO comments are data */
10205 12546 : if (strcmp(type, "LARGE OBJECT") != 0)
10206 : {
10207 12448 : if (!dopt->dumpSchema)
10208 0 : return;
10209 : }
10210 : else
10211 : {
10212 : /* We do dump LO comments in binary-upgrade mode */
10213 98 : if (!dopt->dumpData && !dopt->binary_upgrade)
10214 0 : return;
10215 : }
10216 :
10217 : /* Search for comments associated with catalogId, using table */
10218 12546 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10219 : &comments);
10220 :
10221 : /* Is there one matching the subid? */
10222 12546 : while (ncomments > 0)
10223 : {
10224 12462 : if (comments->objsubid == subid)
10225 12462 : break;
10226 0 : comments++;
10227 0 : ncomments--;
10228 : }
10229 :
10230 12546 : if (initdb_comment != NULL)
10231 : {
10232 : static CommentItem empty_comment = {.descr = ""};
10233 :
10234 : /*
10235 : * initdb creates this object with a comment. Skip dumping the
10236 : * initdb-provided comment, which would complicate matters for
10237 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10238 : * comment, replicate that.
10239 : */
10240 220 : if (ncomments == 0)
10241 : {
10242 8 : comments = &empty_comment;
10243 8 : ncomments = 1;
10244 : }
10245 212 : else if (strcmp(comments->descr, initdb_comment) == 0)
10246 212 : ncomments = 0;
10247 : }
10248 :
10249 : /* If a comment exists, build COMMENT ON statement */
10250 12546 : if (ncomments > 0)
10251 : {
10252 12258 : PQExpBuffer query = createPQExpBuffer();
10253 12258 : PQExpBuffer tag = createPQExpBuffer();
10254 :
10255 12258 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10256 12258 : if (namespace && *namespace)
10257 11964 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10258 12258 : appendPQExpBuffer(query, "%s IS ", name);
10259 12258 : appendStringLiteralAH(query, comments->descr, fout);
10260 12258 : appendPQExpBufferStr(query, ";\n");
10261 :
10262 12258 : appendPQExpBuffer(tag, "%s %s", type, name);
10263 :
10264 : /*
10265 : * We mark comments as SECTION_NONE because they really belong in the
10266 : * same section as their parent, whether that is pre-data or
10267 : * post-data.
10268 : */
10269 12258 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10270 12258 : ARCHIVE_OPTS(.tag = tag->data,
10271 : .namespace = namespace,
10272 : .owner = owner,
10273 : .description = "COMMENT",
10274 : .section = SECTION_NONE,
10275 : .createStmt = query->data,
10276 : .deps = &dumpId,
10277 : .nDeps = 1));
10278 :
10279 12258 : destroyPQExpBuffer(query);
10280 12258 : destroyPQExpBuffer(tag);
10281 : }
10282 : }
10283 :
10284 : /*
10285 : * dumpComment --
10286 : *
10287 : * Typical simplification of the above function.
10288 : */
10289 : static inline void
10290 12292 : dumpComment(Archive *fout, const char *type,
10291 : const char *name, const char *namespace,
10292 : const char *owner, CatalogId catalogId,
10293 : int subid, DumpId dumpId)
10294 : {
10295 12292 : dumpCommentExtended(fout, type, name, namespace, owner,
10296 : catalogId, subid, dumpId, NULL);
10297 12292 : }
10298 :
10299 : /*
10300 : * dumpTableComment --
10301 : *
10302 : * As above, but dump comments for both the specified table (or view)
10303 : * and its columns.
10304 : */
10305 : static void
10306 152 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10307 : const char *reltypename)
10308 : {
10309 152 : DumpOptions *dopt = fout->dopt;
10310 : CommentItem *comments;
10311 : int ncomments;
10312 : PQExpBuffer query;
10313 : PQExpBuffer tag;
10314 :
10315 : /* do nothing, if --no-comments is supplied */
10316 152 : if (dopt->no_comments)
10317 0 : return;
10318 :
10319 : /* Comments are SCHEMA not data */
10320 152 : if (!dopt->dumpSchema)
10321 0 : return;
10322 :
10323 : /* Search for comments associated with relation, using table */
10324 152 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
10325 : tbinfo->dobj.catId.oid,
10326 : &comments);
10327 :
10328 : /* If comments exist, build COMMENT ON statements */
10329 152 : if (ncomments <= 0)
10330 0 : return;
10331 :
10332 152 : query = createPQExpBuffer();
10333 152 : tag = createPQExpBuffer();
10334 :
10335 436 : while (ncomments > 0)
10336 : {
10337 284 : const char *descr = comments->descr;
10338 284 : int objsubid = comments->objsubid;
10339 :
10340 284 : if (objsubid == 0)
10341 : {
10342 66 : resetPQExpBuffer(tag);
10343 66 : appendPQExpBuffer(tag, "%s %s", reltypename,
10344 66 : fmtId(tbinfo->dobj.name));
10345 :
10346 66 : resetPQExpBuffer(query);
10347 66 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10348 66 : fmtQualifiedDumpable(tbinfo));
10349 66 : appendStringLiteralAH(query, descr, fout);
10350 66 : appendPQExpBufferStr(query, ";\n");
10351 :
10352 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10353 66 : ARCHIVE_OPTS(.tag = tag->data,
10354 : .namespace = tbinfo->dobj.namespace->dobj.name,
10355 : .owner = tbinfo->rolname,
10356 : .description = "COMMENT",
10357 : .section = SECTION_NONE,
10358 : .createStmt = query->data,
10359 : .deps = &(tbinfo->dobj.dumpId),
10360 : .nDeps = 1));
10361 : }
10362 218 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10363 : {
10364 218 : resetPQExpBuffer(tag);
10365 218 : appendPQExpBuffer(tag, "COLUMN %s.",
10366 218 : fmtId(tbinfo->dobj.name));
10367 218 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10368 :
10369 218 : resetPQExpBuffer(query);
10370 218 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10371 218 : fmtQualifiedDumpable(tbinfo));
10372 218 : appendPQExpBuffer(query, "%s IS ",
10373 218 : fmtId(tbinfo->attnames[objsubid - 1]));
10374 218 : appendStringLiteralAH(query, descr, fout);
10375 218 : appendPQExpBufferStr(query, ";\n");
10376 :
10377 218 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10378 218 : ARCHIVE_OPTS(.tag = tag->data,
10379 : .namespace = tbinfo->dobj.namespace->dobj.name,
10380 : .owner = tbinfo->rolname,
10381 : .description = "COMMENT",
10382 : .section = SECTION_NONE,
10383 : .createStmt = query->data,
10384 : .deps = &(tbinfo->dobj.dumpId),
10385 : .nDeps = 1));
10386 : }
10387 :
10388 284 : comments++;
10389 284 : ncomments--;
10390 : }
10391 :
10392 152 : destroyPQExpBuffer(query);
10393 152 : destroyPQExpBuffer(tag);
10394 : }
10395 :
10396 : /*
10397 : * findComments --
10398 : *
10399 : * Find the comment(s), if any, associated with the given object. All the
10400 : * objsubid values associated with the given classoid/objoid are found with
10401 : * one search.
10402 : */
10403 : static int
10404 12764 : findComments(Oid classoid, Oid objoid, CommentItem **items)
10405 : {
10406 12764 : CommentItem *middle = NULL;
10407 : CommentItem *low;
10408 : CommentItem *high;
10409 : int nmatch;
10410 :
10411 : /*
10412 : * Do binary search to find some item matching the object.
10413 : */
10414 12764 : low = &comments[0];
10415 12764 : high = &comments[ncomments - 1];
10416 127038 : while (low <= high)
10417 : {
10418 126954 : middle = low + (high - low) / 2;
10419 :
10420 126954 : if (classoid < middle->classoid)
10421 13118 : high = middle - 1;
10422 113836 : else if (classoid > middle->classoid)
10423 13894 : low = middle + 1;
10424 99942 : else if (objoid < middle->objoid)
10425 42132 : high = middle - 1;
10426 57810 : else if (objoid > middle->objoid)
10427 45130 : low = middle + 1;
10428 : else
10429 12680 : break; /* found a match */
10430 : }
10431 :
10432 12764 : if (low > high) /* no matches */
10433 : {
10434 84 : *items = NULL;
10435 84 : return 0;
10436 : }
10437 :
10438 : /*
10439 : * Now determine how many items match the object. The search loop
10440 : * invariant still holds: only items between low and high inclusive could
10441 : * match.
10442 : */
10443 12680 : nmatch = 1;
10444 12680 : while (middle > low)
10445 : {
10446 5814 : if (classoid != middle[-1].classoid ||
10447 5642 : objoid != middle[-1].objoid)
10448 : break;
10449 0 : middle--;
10450 0 : nmatch++;
10451 : }
10452 :
10453 12680 : *items = middle;
10454 :
10455 12680 : middle += nmatch;
10456 12812 : while (middle <= high)
10457 : {
10458 6728 : if (classoid != middle->classoid ||
10459 6256 : objoid != middle->objoid)
10460 : break;
10461 132 : middle++;
10462 132 : nmatch++;
10463 : }
10464 :
10465 12680 : return nmatch;
10466 : }
10467 :
10468 : /*
10469 : * collectComments --
10470 : *
10471 : * Construct a table of all comments available for database objects;
10472 : * also set the has-comment component flag for each relevant object.
10473 : *
10474 : * We used to do per-object queries for the comments, but it's much faster
10475 : * to pull them all over at once, and on most databases the memory cost
10476 : * isn't high.
10477 : *
10478 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
10479 : */
10480 : static void
10481 308 : collectComments(Archive *fout)
10482 : {
10483 : PGresult *res;
10484 : PQExpBuffer query;
10485 : int i_description;
10486 : int i_classoid;
10487 : int i_objoid;
10488 : int i_objsubid;
10489 : int ntups;
10490 : int i;
10491 : DumpableObject *dobj;
10492 :
10493 308 : query = createPQExpBuffer();
10494 :
10495 308 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10496 : "FROM pg_catalog.pg_description "
10497 : "ORDER BY classoid, objoid, objsubid");
10498 :
10499 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10500 :
10501 : /* Construct lookup table containing OIDs in numeric form */
10502 :
10503 308 : i_description = PQfnumber(res, "description");
10504 308 : i_classoid = PQfnumber(res, "classoid");
10505 308 : i_objoid = PQfnumber(res, "objoid");
10506 308 : i_objsubid = PQfnumber(res, "objsubid");
10507 :
10508 308 : ntups = PQntuples(res);
10509 :
10510 308 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10511 308 : ncomments = 0;
10512 308 : dobj = NULL;
10513 :
10514 1621102 : for (i = 0; i < ntups; i++)
10515 : {
10516 : CatalogId objId;
10517 : int subid;
10518 :
10519 1620794 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10520 1620794 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10521 1620794 : subid = atoi(PQgetvalue(res, i, i_objsubid));
10522 :
10523 : /* We needn't remember comments that don't match any dumpable object */
10524 1620794 : if (dobj == NULL ||
10525 583810 : dobj->catId.tableoid != objId.tableoid ||
10526 579972 : dobj->catId.oid != objId.oid)
10527 1620622 : dobj = findObjectByCatalogId(objId);
10528 1620794 : if (dobj == NULL)
10529 1036688 : continue;
10530 :
10531 : /*
10532 : * Comments on columns of composite types are linked to the type's
10533 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10534 : * in the type's own DumpableObject.
10535 : */
10536 584106 : if (subid != 0 && dobj->objType == DO_TABLE &&
10537 372 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10538 86 : {
10539 : TypeInfo *cTypeInfo;
10540 :
10541 86 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10542 86 : if (cTypeInfo)
10543 86 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10544 : }
10545 : else
10546 584020 : dobj->components |= DUMP_COMPONENT_COMMENT;
10547 :
10548 584106 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10549 584106 : comments[ncomments].classoid = objId.tableoid;
10550 584106 : comments[ncomments].objoid = objId.oid;
10551 584106 : comments[ncomments].objsubid = subid;
10552 584106 : ncomments++;
10553 : }
10554 :
10555 308 : PQclear(res);
10556 308 : destroyPQExpBuffer(query);
10557 308 : }
10558 :
10559 : /*
10560 : * dumpDumpableObject
10561 : *
10562 : * This routine and its subsidiaries are responsible for creating
10563 : * ArchiveEntries (TOC objects) for each object to be dumped.
10564 : */
10565 : static void
10566 1126292 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10567 : {
10568 : /*
10569 : * Clear any dump-request bits for components that don't exist for this
10570 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10571 : * request for every kind of object.)
10572 : */
10573 1126292 : dobj->dump &= dobj->components;
10574 :
10575 : /* Now, short-circuit if there's nothing to be done here. */
10576 1126292 : if (dobj->dump == 0)
10577 1000684 : return;
10578 :
10579 125608 : switch (dobj->objType)
10580 : {
10581 784 : case DO_NAMESPACE:
10582 784 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
10583 784 : break;
10584 38 : case DO_EXTENSION:
10585 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
10586 38 : break;
10587 1672 : case DO_TYPE:
10588 1672 : dumpType(fout, (const TypeInfo *) dobj);
10589 1672 : break;
10590 142 : case DO_SHELL_TYPE:
10591 142 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
10592 142 : break;
10593 3566 : case DO_FUNC:
10594 3566 : dumpFunc(fout, (const FuncInfo *) dobj);
10595 3566 : break;
10596 580 : case DO_AGG:
10597 580 : dumpAgg(fout, (const AggInfo *) dobj);
10598 580 : break;
10599 5004 : case DO_OPERATOR:
10600 5004 : dumpOpr(fout, (const OprInfo *) dobj);
10601 5004 : break;
10602 152 : case DO_ACCESS_METHOD:
10603 152 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10604 152 : break;
10605 1308 : case DO_OPCLASS:
10606 1308 : dumpOpclass(fout, (const OpclassInfo *) dobj);
10607 1308 : break;
10608 1090 : case DO_OPFAMILY:
10609 1090 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10610 1090 : break;
10611 4928 : case DO_COLLATION:
10612 4928 : dumpCollation(fout, (const CollInfo *) dobj);
10613 4928 : break;
10614 840 : case DO_CONVERSION:
10615 840 : dumpConversion(fout, (const ConvInfo *) dobj);
10616 840 : break;
10617 49984 : case DO_TABLE:
10618 49984 : dumpTable(fout, (const TableInfo *) dobj);
10619 49984 : break;
10620 2496 : case DO_TABLE_ATTACH:
10621 2496 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10622 2496 : break;
10623 1520 : case DO_ATTRDEF:
10624 1520 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
10625 1520 : break;
10626 4740 : case DO_INDEX:
10627 4740 : dumpIndex(fout, (const IndxInfo *) dobj);
10628 4740 : break;
10629 1096 : case DO_INDEX_ATTACH:
10630 1096 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
10631 1096 : break;
10632 254 : case DO_STATSEXT:
10633 254 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
10634 254 : break;
10635 676 : case DO_REFRESH_MATVIEW:
10636 676 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
10637 676 : break;
10638 2150 : case DO_RULE:
10639 2150 : dumpRule(fout, (const RuleInfo *) dobj);
10640 2150 : break;
10641 986 : case DO_TRIGGER:
10642 986 : dumpTrigger(fout, (const TriggerInfo *) dobj);
10643 986 : break;
10644 80 : case DO_EVENT_TRIGGER:
10645 80 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
10646 80 : break;
10647 4052 : case DO_CONSTRAINT:
10648 4052 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10649 4052 : break;
10650 344 : case DO_FK_CONSTRAINT:
10651 344 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10652 344 : break;
10653 156 : case DO_PROCLANG:
10654 156 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
10655 156 : break;
10656 130 : case DO_CAST:
10657 130 : dumpCast(fout, (const CastInfo *) dobj);
10658 130 : break;
10659 80 : case DO_TRANSFORM:
10660 80 : dumpTransform(fout, (const TransformInfo *) dobj);
10661 80 : break;
10662 728 : case DO_SEQUENCE_SET:
10663 728 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
10664 728 : break;
10665 7300 : case DO_TABLE_DATA:
10666 7300 : dumpTableData(fout, (const TableDataInfo *) dobj);
10667 7300 : break;
10668 24714 : case DO_DUMMY_TYPE:
10669 : /* table rowtypes and array types are never dumped separately */
10670 24714 : break;
10671 78 : case DO_TSPARSER:
10672 78 : dumpTSParser(fout, (const TSParserInfo *) dobj);
10673 78 : break;
10674 336 : case DO_TSDICT:
10675 336 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
10676 336 : break;
10677 102 : case DO_TSTEMPLATE:
10678 102 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
10679 102 : break;
10680 286 : case DO_TSCONFIG:
10681 286 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
10682 286 : break;
10683 100 : case DO_FDW:
10684 100 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
10685 100 : break;
10686 108 : case DO_FOREIGN_SERVER:
10687 108 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
10688 108 : break;
10689 284 : case DO_DEFAULT_ACL:
10690 284 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
10691 284 : break;
10692 146 : case DO_LARGE_OBJECT:
10693 146 : dumpLO(fout, (const LoInfo *) dobj);
10694 146 : break;
10695 146 : case DO_LARGE_OBJECT_DATA:
10696 146 : if (dobj->dump & DUMP_COMPONENT_DATA)
10697 : {
10698 : LoInfo *loinfo;
10699 : TocEntry *te;
10700 :
10701 146 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
10702 146 : if (loinfo == NULL)
10703 0 : pg_fatal("missing metadata for large objects \"%s\"",
10704 : dobj->name);
10705 :
10706 146 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
10707 146 : ARCHIVE_OPTS(.tag = dobj->name,
10708 : .owner = loinfo->rolname,
10709 : .description = "BLOBS",
10710 : .section = SECTION_DATA,
10711 : .deps = dobj->dependencies,
10712 : .nDeps = dobj->nDeps,
10713 : .dumpFn = dumpLOs,
10714 : .dumpArg = loinfo));
10715 :
10716 : /*
10717 : * Set the TocEntry's dataLength in case we are doing a
10718 : * parallel dump and want to order dump jobs by table size.
10719 : * (We need some size estimate for every TocEntry with a
10720 : * DataDumper function.) We don't currently have any cheap
10721 : * way to estimate the size of LOs, but fortunately it doesn't
10722 : * matter too much as long as we get large batches of LOs
10723 : * processed reasonably early. Assume 8K per blob.
10724 : */
10725 146 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
10726 : }
10727 146 : break;
10728 638 : case DO_POLICY:
10729 638 : dumpPolicy(fout, (const PolicyInfo *) dobj);
10730 638 : break;
10731 352 : case DO_PUBLICATION:
10732 352 : dumpPublication(fout, (const PublicationInfo *) dobj);
10733 352 : break;
10734 470 : case DO_PUBLICATION_REL:
10735 470 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
10736 470 : break;
10737 138 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
10738 138 : dumpPublicationNamespace(fout,
10739 : (const PublicationSchemaInfo *) dobj);
10740 138 : break;
10741 214 : case DO_SUBSCRIPTION:
10742 214 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
10743 214 : break;
10744 4 : case DO_SUBSCRIPTION_REL:
10745 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
10746 4 : break;
10747 616 : case DO_PRE_DATA_BOUNDARY:
10748 : case DO_POST_DATA_BOUNDARY:
10749 : /* never dumped, nothing to do */
10750 616 : break;
10751 : }
10752 : }
10753 :
10754 : /*
10755 : * dumpNamespace
10756 : * writes out to fout the queries to recreate a user-defined namespace
10757 : */
10758 : static void
10759 784 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
10760 : {
10761 784 : DumpOptions *dopt = fout->dopt;
10762 : PQExpBuffer q;
10763 : PQExpBuffer delq;
10764 : char *qnspname;
10765 :
10766 : /* Do nothing if not dumping schema */
10767 784 : if (!dopt->dumpSchema)
10768 32 : return;
10769 :
10770 752 : q = createPQExpBuffer();
10771 752 : delq = createPQExpBuffer();
10772 :
10773 752 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
10774 :
10775 752 : if (nspinfo->create)
10776 : {
10777 508 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
10778 508 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
10779 : }
10780 : else
10781 : {
10782 : /* see selectDumpableNamespace() */
10783 244 : appendPQExpBufferStr(delq,
10784 : "-- *not* dropping schema, since initdb creates it\n");
10785 244 : appendPQExpBufferStr(q,
10786 : "-- *not* creating schema, since initdb creates it\n");
10787 : }
10788 :
10789 752 : if (dopt->binary_upgrade)
10790 82 : binary_upgrade_extension_member(q, &nspinfo->dobj,
10791 : "SCHEMA", qnspname, NULL);
10792 :
10793 752 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10794 326 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
10795 326 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
10796 : .owner = nspinfo->rolname,
10797 : .description = "SCHEMA",
10798 : .section = SECTION_PRE_DATA,
10799 : .createStmt = q->data,
10800 : .dropStmt = delq->data));
10801 :
10802 : /* Dump Schema Comments and Security Labels */
10803 752 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10804 : {
10805 254 : const char *initdb_comment = NULL;
10806 :
10807 254 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
10808 220 : initdb_comment = "standard public schema";
10809 254 : dumpCommentExtended(fout, "SCHEMA", qnspname,
10810 : NULL, nspinfo->rolname,
10811 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
10812 : initdb_comment);
10813 : }
10814 :
10815 752 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10816 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
10817 : NULL, nspinfo->rolname,
10818 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
10819 :
10820 752 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10821 586 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
10822 : qnspname, NULL, NULL,
10823 : NULL, nspinfo->rolname, &nspinfo->dacl);
10824 :
10825 752 : free(qnspname);
10826 :
10827 752 : destroyPQExpBuffer(q);
10828 752 : destroyPQExpBuffer(delq);
10829 : }
10830 :
10831 : /*
10832 : * dumpExtension
10833 : * writes out to fout the queries to recreate an extension
10834 : */
10835 : static void
10836 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
10837 : {
10838 38 : DumpOptions *dopt = fout->dopt;
10839 : PQExpBuffer q;
10840 : PQExpBuffer delq;
10841 : char *qextname;
10842 :
10843 : /* Do nothing if not dumping schema */
10844 38 : if (!dopt->dumpSchema)
10845 2 : return;
10846 :
10847 36 : q = createPQExpBuffer();
10848 36 : delq = createPQExpBuffer();
10849 :
10850 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
10851 :
10852 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10853 :
10854 36 : if (!dopt->binary_upgrade)
10855 : {
10856 : /*
10857 : * In a regular dump, we simply create the extension, intentionally
10858 : * not specifying a version, so that the destination installation's
10859 : * default version is used.
10860 : *
10861 : * Use of IF NOT EXISTS here is unlike our behavior for other object
10862 : * types; but there are various scenarios in which it's convenient to
10863 : * manually create the desired extension before restoring, so we
10864 : * prefer to allow it to exist already.
10865 : */
10866 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10867 34 : qextname, fmtId(extinfo->namespace));
10868 : }
10869 : else
10870 : {
10871 : /*
10872 : * In binary-upgrade mode, it's critical to reproduce the state of the
10873 : * database exactly, so our procedure is to create an empty extension,
10874 : * restore all the contained objects normally, and add them to the
10875 : * extension one by one. This function performs just the first of
10876 : * those steps. binary_upgrade_extension_member() takes care of
10877 : * adding member objects as they're created.
10878 : */
10879 : int i;
10880 : int n;
10881 :
10882 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10883 :
10884 : /*
10885 : * We unconditionally create the extension, so we must drop it if it
10886 : * exists. This could happen if the user deleted 'plpgsql' and then
10887 : * readded it, causing its oid to be greater than g_last_builtin_oid.
10888 : */
10889 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10890 :
10891 2 : appendPQExpBufferStr(q,
10892 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10893 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
10894 2 : appendPQExpBufferStr(q, ", ");
10895 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
10896 2 : appendPQExpBufferStr(q, ", ");
10897 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10898 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
10899 2 : appendPQExpBufferStr(q, ", ");
10900 :
10901 : /*
10902 : * Note that we're pushing extconfig (an OID array) back into
10903 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
10904 : * preserved in binary upgrade.
10905 : */
10906 2 : if (strlen(extinfo->extconfig) > 2)
10907 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
10908 : else
10909 0 : appendPQExpBufferStr(q, "NULL");
10910 2 : appendPQExpBufferStr(q, ", ");
10911 2 : if (strlen(extinfo->extcondition) > 2)
10912 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
10913 : else
10914 0 : appendPQExpBufferStr(q, "NULL");
10915 2 : appendPQExpBufferStr(q, ", ");
10916 2 : appendPQExpBufferStr(q, "ARRAY[");
10917 2 : n = 0;
10918 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
10919 : {
10920 : DumpableObject *extobj;
10921 :
10922 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10923 2 : if (extobj && extobj->objType == DO_EXTENSION)
10924 : {
10925 0 : if (n++ > 0)
10926 0 : appendPQExpBufferChar(q, ',');
10927 0 : appendStringLiteralAH(q, extobj->name, fout);
10928 : }
10929 : }
10930 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10931 2 : appendPQExpBufferStr(q, ");\n");
10932 : }
10933 :
10934 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10935 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10936 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10937 : .description = "EXTENSION",
10938 : .section = SECTION_PRE_DATA,
10939 : .createStmt = q->data,
10940 : .dropStmt = delq->data));
10941 :
10942 : /* Dump Extension Comments and Security Labels */
10943 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10944 36 : dumpComment(fout, "EXTENSION", qextname,
10945 : NULL, "",
10946 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10947 :
10948 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10949 0 : dumpSecLabel(fout, "EXTENSION", qextname,
10950 : NULL, "",
10951 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10952 :
10953 36 : free(qextname);
10954 :
10955 36 : destroyPQExpBuffer(q);
10956 36 : destroyPQExpBuffer(delq);
10957 : }
10958 :
10959 : /*
10960 : * dumpType
10961 : * writes out to fout the queries to recreate a user-defined type
10962 : */
10963 : static void
10964 1672 : dumpType(Archive *fout, const TypeInfo *tyinfo)
10965 : {
10966 1672 : DumpOptions *dopt = fout->dopt;
10967 :
10968 : /* Do nothing if not dumping schema */
10969 1672 : if (!dopt->dumpSchema)
10970 44 : return;
10971 :
10972 : /* Dump out in proper style */
10973 1628 : if (tyinfo->typtype == TYPTYPE_BASE)
10974 556 : dumpBaseType(fout, tyinfo);
10975 1072 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
10976 266 : dumpDomain(fout, tyinfo);
10977 806 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
10978 262 : dumpCompositeType(fout, tyinfo);
10979 544 : else if (tyinfo->typtype == TYPTYPE_ENUM)
10980 110 : dumpEnumType(fout, tyinfo);
10981 434 : else if (tyinfo->typtype == TYPTYPE_RANGE)
10982 208 : dumpRangeType(fout, tyinfo);
10983 226 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
10984 76 : dumpUndefinedType(fout, tyinfo);
10985 : else
10986 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
10987 : tyinfo->dobj.name);
10988 : }
10989 :
10990 : /*
10991 : * dumpEnumType
10992 : * writes out to fout the queries to recreate a user-defined enum type
10993 : */
10994 : static void
10995 110 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
10996 : {
10997 110 : DumpOptions *dopt = fout->dopt;
10998 110 : PQExpBuffer q = createPQExpBuffer();
10999 110 : PQExpBuffer delq = createPQExpBuffer();
11000 110 : PQExpBuffer query = createPQExpBuffer();
11001 : PGresult *res;
11002 : int num,
11003 : i;
11004 : Oid enum_oid;
11005 : char *qtypname;
11006 : char *qualtypname;
11007 : char *label;
11008 : int i_enumlabel;
11009 : int i_oid;
11010 :
11011 110 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11012 : {
11013 : /* Set up query for enum-specific details */
11014 80 : appendPQExpBufferStr(query,
11015 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11016 : "SELECT oid, enumlabel "
11017 : "FROM pg_catalog.pg_enum "
11018 : "WHERE enumtypid = $1 "
11019 : "ORDER BY enumsortorder");
11020 :
11021 80 : ExecuteSqlStatement(fout, query->data);
11022 :
11023 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11024 : }
11025 :
11026 110 : printfPQExpBuffer(query,
11027 : "EXECUTE dumpEnumType('%u')",
11028 : tyinfo->dobj.catId.oid);
11029 :
11030 110 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11031 :
11032 110 : num = PQntuples(res);
11033 :
11034 110 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11035 110 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11036 :
11037 : /*
11038 : * CASCADE shouldn't be required here as for normal types since the I/O
11039 : * functions are generic and do not get dropped.
11040 : */
11041 110 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11042 :
11043 110 : if (dopt->binary_upgrade)
11044 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11045 : tyinfo->dobj.catId.oid,
11046 : false, false);
11047 :
11048 110 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11049 : qualtypname);
11050 :
11051 110 : if (!dopt->binary_upgrade)
11052 : {
11053 100 : i_enumlabel = PQfnumber(res, "enumlabel");
11054 :
11055 : /* Labels with server-assigned oids */
11056 732 : for (i = 0; i < num; i++)
11057 : {
11058 632 : label = PQgetvalue(res, i, i_enumlabel);
11059 632 : if (i > 0)
11060 532 : appendPQExpBufferChar(q, ',');
11061 632 : appendPQExpBufferStr(q, "\n ");
11062 632 : appendStringLiteralAH(q, label, fout);
11063 : }
11064 : }
11065 :
11066 110 : appendPQExpBufferStr(q, "\n);\n");
11067 :
11068 110 : if (dopt->binary_upgrade)
11069 : {
11070 10 : i_oid = PQfnumber(res, "oid");
11071 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11072 :
11073 : /* Labels with dump-assigned (preserved) oids */
11074 116 : for (i = 0; i < num; i++)
11075 : {
11076 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11077 106 : label = PQgetvalue(res, i, i_enumlabel);
11078 :
11079 106 : if (i == 0)
11080 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11081 106 : appendPQExpBuffer(q,
11082 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11083 : enum_oid);
11084 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11085 106 : appendStringLiteralAH(q, label, fout);
11086 106 : appendPQExpBufferStr(q, ";\n\n");
11087 : }
11088 : }
11089 :
11090 110 : if (dopt->binary_upgrade)
11091 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11092 : "TYPE", qtypname,
11093 10 : tyinfo->dobj.namespace->dobj.name);
11094 :
11095 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11096 110 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11097 110 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11098 : .namespace = tyinfo->dobj.namespace->dobj.name,
11099 : .owner = tyinfo->rolname,
11100 : .description = "TYPE",
11101 : .section = SECTION_PRE_DATA,
11102 : .createStmt = q->data,
11103 : .dropStmt = delq->data));
11104 :
11105 : /* Dump Type Comments and Security Labels */
11106 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11107 66 : dumpComment(fout, "TYPE", qtypname,
11108 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11109 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11110 :
11111 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11112 0 : dumpSecLabel(fout, "TYPE", qtypname,
11113 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11114 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11115 :
11116 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11117 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11118 : qtypname, NULL,
11119 66 : tyinfo->dobj.namespace->dobj.name,
11120 : NULL, tyinfo->rolname, &tyinfo->dacl);
11121 :
11122 110 : PQclear(res);
11123 110 : destroyPQExpBuffer(q);
11124 110 : destroyPQExpBuffer(delq);
11125 110 : destroyPQExpBuffer(query);
11126 110 : free(qtypname);
11127 110 : free(qualtypname);
11128 110 : }
11129 :
11130 : /*
11131 : * dumpRangeType
11132 : * writes out to fout the queries to recreate a user-defined range type
11133 : */
11134 : static void
11135 208 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11136 : {
11137 208 : DumpOptions *dopt = fout->dopt;
11138 208 : PQExpBuffer q = createPQExpBuffer();
11139 208 : PQExpBuffer delq = createPQExpBuffer();
11140 208 : PQExpBuffer query = createPQExpBuffer();
11141 : PGresult *res;
11142 : Oid collationOid;
11143 : char *qtypname;
11144 : char *qualtypname;
11145 : char *procname;
11146 :
11147 208 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11148 : {
11149 : /* Set up query for range-specific details */
11150 82 : appendPQExpBufferStr(query,
11151 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11152 :
11153 82 : appendPQExpBufferStr(query,
11154 : "SELECT ");
11155 :
11156 82 : if (fout->remoteVersion >= 140000)
11157 82 : appendPQExpBufferStr(query,
11158 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11159 : else
11160 0 : appendPQExpBufferStr(query,
11161 : "NULL AS rngmultitype, ");
11162 :
11163 82 : appendPQExpBufferStr(query,
11164 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11165 : "opc.opcname AS opcname, "
11166 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11167 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11168 : "opc.opcdefault, "
11169 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11170 : " ELSE rngcollation END AS collation, "
11171 : "rngcanonical, rngsubdiff "
11172 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11173 : " pg_catalog.pg_opclass opc "
11174 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11175 : "rngtypid = $1");
11176 :
11177 82 : ExecuteSqlStatement(fout, query->data);
11178 :
11179 82 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11180 : }
11181 :
11182 208 : printfPQExpBuffer(query,
11183 : "EXECUTE dumpRangeType('%u')",
11184 : tyinfo->dobj.catId.oid);
11185 :
11186 208 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11187 :
11188 208 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11189 208 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11190 :
11191 : /*
11192 : * CASCADE shouldn't be required here as for normal types since the I/O
11193 : * functions are generic and do not get dropped.
11194 : */
11195 208 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11196 :
11197 208 : if (dopt->binary_upgrade)
11198 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11199 : tyinfo->dobj.catId.oid,
11200 : false, true);
11201 :
11202 208 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11203 : qualtypname);
11204 :
11205 208 : appendPQExpBuffer(q, "\n subtype = %s",
11206 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11207 :
11208 208 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11209 208 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11210 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11211 :
11212 : /* print subtype_opclass only if not default for subtype */
11213 208 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11214 : {
11215 66 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11216 66 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11217 :
11218 66 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11219 : fmtId(nspname));
11220 66 : appendPQExpBufferStr(q, fmtId(opcname));
11221 : }
11222 :
11223 208 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11224 208 : if (OidIsValid(collationOid))
11225 : {
11226 76 : CollInfo *coll = findCollationByOid(collationOid);
11227 :
11228 76 : if (coll)
11229 76 : appendPQExpBuffer(q, ",\n collation = %s",
11230 76 : fmtQualifiedDumpable(coll));
11231 : }
11232 :
11233 208 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11234 208 : if (strcmp(procname, "-") != 0)
11235 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
11236 :
11237 208 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11238 208 : if (strcmp(procname, "-") != 0)
11239 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11240 :
11241 208 : appendPQExpBufferStr(q, "\n);\n");
11242 :
11243 208 : if (dopt->binary_upgrade)
11244 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11245 : "TYPE", qtypname,
11246 12 : tyinfo->dobj.namespace->dobj.name);
11247 :
11248 208 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11249 208 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11250 208 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11251 : .namespace = tyinfo->dobj.namespace->dobj.name,
11252 : .owner = tyinfo->rolname,
11253 : .description = "TYPE",
11254 : .section = SECTION_PRE_DATA,
11255 : .createStmt = q->data,
11256 : .dropStmt = delq->data));
11257 :
11258 : /* Dump Type Comments and Security Labels */
11259 208 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11260 102 : dumpComment(fout, "TYPE", qtypname,
11261 102 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11262 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11263 :
11264 208 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11265 0 : dumpSecLabel(fout, "TYPE", qtypname,
11266 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11267 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11268 :
11269 208 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11270 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11271 : qtypname, NULL,
11272 66 : tyinfo->dobj.namespace->dobj.name,
11273 : NULL, tyinfo->rolname, &tyinfo->dacl);
11274 :
11275 208 : PQclear(res);
11276 208 : destroyPQExpBuffer(q);
11277 208 : destroyPQExpBuffer(delq);
11278 208 : destroyPQExpBuffer(query);
11279 208 : free(qtypname);
11280 208 : free(qualtypname);
11281 208 : }
11282 :
11283 : /*
11284 : * dumpUndefinedType
11285 : * writes out to fout the queries to recreate a !typisdefined type
11286 : *
11287 : * This is a shell type, but we use different terminology to distinguish
11288 : * this case from where we have to emit a shell type definition to break
11289 : * circular dependencies. An undefined type shouldn't ever have anything
11290 : * depending on it.
11291 : */
11292 : static void
11293 76 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11294 : {
11295 76 : DumpOptions *dopt = fout->dopt;
11296 76 : PQExpBuffer q = createPQExpBuffer();
11297 76 : PQExpBuffer delq = createPQExpBuffer();
11298 : char *qtypname;
11299 : char *qualtypname;
11300 :
11301 76 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11302 76 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11303 :
11304 76 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11305 :
11306 76 : if (dopt->binary_upgrade)
11307 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11308 : tyinfo->dobj.catId.oid,
11309 : false, false);
11310 :
11311 76 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11312 : qualtypname);
11313 :
11314 76 : if (dopt->binary_upgrade)
11315 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11316 : "TYPE", qtypname,
11317 4 : tyinfo->dobj.namespace->dobj.name);
11318 :
11319 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11320 76 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11321 76 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11322 : .namespace = tyinfo->dobj.namespace->dobj.name,
11323 : .owner = tyinfo->rolname,
11324 : .description = "TYPE",
11325 : .section = SECTION_PRE_DATA,
11326 : .createStmt = q->data,
11327 : .dropStmt = delq->data));
11328 :
11329 : /* Dump Type Comments and Security Labels */
11330 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11331 66 : dumpComment(fout, "TYPE", qtypname,
11332 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11333 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11334 :
11335 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11336 0 : dumpSecLabel(fout, "TYPE", qtypname,
11337 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11338 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11339 :
11340 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11341 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11342 : qtypname, NULL,
11343 0 : tyinfo->dobj.namespace->dobj.name,
11344 : NULL, tyinfo->rolname, &tyinfo->dacl);
11345 :
11346 76 : destroyPQExpBuffer(q);
11347 76 : destroyPQExpBuffer(delq);
11348 76 : free(qtypname);
11349 76 : free(qualtypname);
11350 76 : }
11351 :
11352 : /*
11353 : * dumpBaseType
11354 : * writes out to fout the queries to recreate a user-defined base type
11355 : */
11356 : static void
11357 556 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11358 : {
11359 556 : DumpOptions *dopt = fout->dopt;
11360 556 : PQExpBuffer q = createPQExpBuffer();
11361 556 : PQExpBuffer delq = createPQExpBuffer();
11362 556 : PQExpBuffer query = createPQExpBuffer();
11363 : PGresult *res;
11364 : char *qtypname;
11365 : char *qualtypname;
11366 : char *typlen;
11367 : char *typinput;
11368 : char *typoutput;
11369 : char *typreceive;
11370 : char *typsend;
11371 : char *typmodin;
11372 : char *typmodout;
11373 : char *typanalyze;
11374 : char *typsubscript;
11375 : Oid typreceiveoid;
11376 : Oid typsendoid;
11377 : Oid typmodinoid;
11378 : Oid typmodoutoid;
11379 : Oid typanalyzeoid;
11380 : Oid typsubscriptoid;
11381 : char *typcategory;
11382 : char *typispreferred;
11383 : char *typdelim;
11384 : char *typbyval;
11385 : char *typalign;
11386 : char *typstorage;
11387 : char *typcollatable;
11388 : char *typdefault;
11389 556 : bool typdefault_is_literal = false;
11390 :
11391 556 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11392 : {
11393 : /* Set up query for type-specific details */
11394 82 : appendPQExpBufferStr(query,
11395 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11396 : "SELECT typlen, "
11397 : "typinput, typoutput, typreceive, typsend, "
11398 : "typreceive::pg_catalog.oid AS typreceiveoid, "
11399 : "typsend::pg_catalog.oid AS typsendoid, "
11400 : "typanalyze, "
11401 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11402 : "typdelim, typbyval, typalign, typstorage, "
11403 : "typmodin, typmodout, "
11404 : "typmodin::pg_catalog.oid AS typmodinoid, "
11405 : "typmodout::pg_catalog.oid AS typmodoutoid, "
11406 : "typcategory, typispreferred, "
11407 : "(typcollation <> 0) AS typcollatable, "
11408 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11409 :
11410 82 : if (fout->remoteVersion >= 140000)
11411 82 : appendPQExpBufferStr(query,
11412 : "typsubscript, "
11413 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11414 : else
11415 0 : appendPQExpBufferStr(query,
11416 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
11417 :
11418 82 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11419 : "WHERE oid = $1");
11420 :
11421 82 : ExecuteSqlStatement(fout, query->data);
11422 :
11423 82 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11424 : }
11425 :
11426 556 : printfPQExpBuffer(query,
11427 : "EXECUTE dumpBaseType('%u')",
11428 : tyinfo->dobj.catId.oid);
11429 :
11430 556 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11431 :
11432 556 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11433 556 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11434 556 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11435 556 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11436 556 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11437 556 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11438 556 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11439 556 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11440 556 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11441 556 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11442 556 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11443 556 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11444 556 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11445 556 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11446 556 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11447 556 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11448 556 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11449 556 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11450 556 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11451 556 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11452 556 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11453 556 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11454 556 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11455 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11456 556 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11457 : {
11458 86 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11459 86 : typdefault_is_literal = true; /* it needs quotes */
11460 : }
11461 : else
11462 470 : typdefault = NULL;
11463 :
11464 556 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11465 556 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11466 :
11467 : /*
11468 : * The reason we include CASCADE is that the circular dependency between
11469 : * the type and its I/O functions makes it impossible to drop the type any
11470 : * other way.
11471 : */
11472 556 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11473 :
11474 : /*
11475 : * We might already have a shell type, but setting pg_type_oid is
11476 : * harmless, and in any case we'd better set the array type OID.
11477 : */
11478 556 : if (dopt->binary_upgrade)
11479 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11480 : tyinfo->dobj.catId.oid,
11481 : false, false);
11482 :
11483 556 : appendPQExpBuffer(q,
11484 : "CREATE TYPE %s (\n"
11485 : " INTERNALLENGTH = %s",
11486 : qualtypname,
11487 556 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11488 :
11489 : /* regproc result is sufficiently quoted already */
11490 556 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11491 556 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11492 556 : if (OidIsValid(typreceiveoid))
11493 408 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11494 556 : if (OidIsValid(typsendoid))
11495 408 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11496 556 : if (OidIsValid(typmodinoid))
11497 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11498 556 : if (OidIsValid(typmodoutoid))
11499 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11500 556 : if (OidIsValid(typanalyzeoid))
11501 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11502 :
11503 556 : if (strcmp(typcollatable, "t") == 0)
11504 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11505 :
11506 556 : if (typdefault != NULL)
11507 : {
11508 86 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
11509 86 : if (typdefault_is_literal)
11510 86 : appendStringLiteralAH(q, typdefault, fout);
11511 : else
11512 0 : appendPQExpBufferStr(q, typdefault);
11513 : }
11514 :
11515 556 : if (OidIsValid(typsubscriptoid))
11516 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11517 :
11518 556 : if (OidIsValid(tyinfo->typelem))
11519 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
11520 : getFormattedTypeName(fout, tyinfo->typelem,
11521 : zeroIsError));
11522 :
11523 556 : if (strcmp(typcategory, "U") != 0)
11524 : {
11525 310 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
11526 310 : appendStringLiteralAH(q, typcategory, fout);
11527 : }
11528 :
11529 556 : if (strcmp(typispreferred, "t") == 0)
11530 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
11531 :
11532 556 : if (typdelim && strcmp(typdelim, ",") != 0)
11533 : {
11534 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
11535 6 : appendStringLiteralAH(q, typdelim, fout);
11536 : }
11537 :
11538 556 : if (*typalign == TYPALIGN_CHAR)
11539 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11540 532 : else if (*typalign == TYPALIGN_SHORT)
11541 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11542 520 : else if (*typalign == TYPALIGN_INT)
11543 370 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11544 150 : else if (*typalign == TYPALIGN_DOUBLE)
11545 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11546 :
11547 556 : if (*typstorage == TYPSTORAGE_PLAIN)
11548 406 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
11549 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
11550 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
11551 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
11552 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
11553 18 : else if (*typstorage == TYPSTORAGE_MAIN)
11554 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
11555 :
11556 556 : if (strcmp(typbyval, "t") == 0)
11557 264 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11558 :
11559 556 : appendPQExpBufferStr(q, "\n);\n");
11560 :
11561 556 : if (dopt->binary_upgrade)
11562 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11563 : "TYPE", qtypname,
11564 16 : tyinfo->dobj.namespace->dobj.name);
11565 :
11566 556 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11567 556 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11568 556 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11569 : .namespace = tyinfo->dobj.namespace->dobj.name,
11570 : .owner = tyinfo->rolname,
11571 : .description = "TYPE",
11572 : .section = SECTION_PRE_DATA,
11573 : .createStmt = q->data,
11574 : .dropStmt = delq->data));
11575 :
11576 : /* Dump Type Comments and Security Labels */
11577 556 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11578 486 : dumpComment(fout, "TYPE", qtypname,
11579 486 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11580 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11581 :
11582 556 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11583 0 : dumpSecLabel(fout, "TYPE", qtypname,
11584 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11585 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11586 :
11587 556 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11588 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11589 : qtypname, NULL,
11590 66 : tyinfo->dobj.namespace->dobj.name,
11591 : NULL, tyinfo->rolname, &tyinfo->dacl);
11592 :
11593 556 : PQclear(res);
11594 556 : destroyPQExpBuffer(q);
11595 556 : destroyPQExpBuffer(delq);
11596 556 : destroyPQExpBuffer(query);
11597 556 : free(qtypname);
11598 556 : free(qualtypname);
11599 556 : }
11600 :
11601 : /*
11602 : * dumpDomain
11603 : * writes out to fout the queries to recreate a user-defined domain
11604 : */
11605 : static void
11606 266 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11607 : {
11608 266 : DumpOptions *dopt = fout->dopt;
11609 266 : PQExpBuffer q = createPQExpBuffer();
11610 266 : PQExpBuffer delq = createPQExpBuffer();
11611 266 : PQExpBuffer query = createPQExpBuffer();
11612 : PGresult *res;
11613 : int i;
11614 : char *qtypname;
11615 : char *qualtypname;
11616 : char *typnotnull;
11617 : char *typdefn;
11618 : char *typdefault;
11619 : Oid typcollation;
11620 266 : bool typdefault_is_literal = false;
11621 :
11622 266 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
11623 : {
11624 : /* Set up query for domain-specific details */
11625 76 : appendPQExpBufferStr(query,
11626 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
11627 :
11628 76 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
11629 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
11630 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
11631 : "t.typdefault, "
11632 : "CASE WHEN t.typcollation <> u.typcollation "
11633 : "THEN t.typcollation ELSE 0 END AS typcollation "
11634 : "FROM pg_catalog.pg_type t "
11635 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
11636 : "WHERE t.oid = $1");
11637 :
11638 76 : ExecuteSqlStatement(fout, query->data);
11639 :
11640 76 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
11641 : }
11642 :
11643 266 : printfPQExpBuffer(query,
11644 : "EXECUTE dumpDomain('%u')",
11645 : tyinfo->dobj.catId.oid);
11646 :
11647 266 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11648 :
11649 266 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
11650 266 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
11651 266 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11652 76 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11653 190 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11654 : {
11655 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11656 0 : typdefault_is_literal = true; /* it needs quotes */
11657 : }
11658 : else
11659 190 : typdefault = NULL;
11660 266 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
11661 :
11662 266 : if (dopt->binary_upgrade)
11663 42 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11664 : tyinfo->dobj.catId.oid,
11665 : true, /* force array type */
11666 : false); /* force multirange type */
11667 :
11668 266 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11669 266 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11670 :
11671 266 : appendPQExpBuffer(q,
11672 : "CREATE DOMAIN %s AS %s",
11673 : qualtypname,
11674 : typdefn);
11675 :
11676 : /* Print collation only if different from base type's collation */
11677 266 : if (OidIsValid(typcollation))
11678 : {
11679 : CollInfo *coll;
11680 :
11681 66 : coll = findCollationByOid(typcollation);
11682 66 : if (coll)
11683 66 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
11684 : }
11685 :
11686 266 : if (typnotnull[0] == 't')
11687 30 : appendPQExpBufferStr(q, " NOT NULL");
11688 :
11689 266 : if (typdefault != NULL)
11690 : {
11691 76 : appendPQExpBufferStr(q, " DEFAULT ");
11692 76 : if (typdefault_is_literal)
11693 0 : appendStringLiteralAH(q, typdefault, fout);
11694 : else
11695 76 : appendPQExpBufferStr(q, typdefault);
11696 : }
11697 :
11698 266 : PQclear(res);
11699 :
11700 : /*
11701 : * Add any CHECK constraints for the domain
11702 : */
11703 442 : for (i = 0; i < tyinfo->nDomChecks; i++)
11704 : {
11705 176 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11706 :
11707 176 : if (!domcheck->separate)
11708 176 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
11709 176 : fmtId(domcheck->dobj.name), domcheck->condef);
11710 : }
11711 :
11712 266 : appendPQExpBufferStr(q, ";\n");
11713 :
11714 266 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
11715 :
11716 266 : if (dopt->binary_upgrade)
11717 42 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11718 : "DOMAIN", qtypname,
11719 42 : tyinfo->dobj.namespace->dobj.name);
11720 :
11721 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11722 266 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11723 266 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11724 : .namespace = tyinfo->dobj.namespace->dobj.name,
11725 : .owner = tyinfo->rolname,
11726 : .description = "DOMAIN",
11727 : .section = SECTION_PRE_DATA,
11728 : .createStmt = q->data,
11729 : .dropStmt = delq->data));
11730 :
11731 : /* Dump Domain Comments and Security Labels */
11732 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11733 0 : dumpComment(fout, "DOMAIN", qtypname,
11734 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11735 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11736 :
11737 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11738 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
11739 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11740 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11741 :
11742 266 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11743 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11744 : qtypname, NULL,
11745 66 : tyinfo->dobj.namespace->dobj.name,
11746 : NULL, tyinfo->rolname, &tyinfo->dacl);
11747 :
11748 : /* Dump any per-constraint comments */
11749 442 : for (i = 0; i < tyinfo->nDomChecks; i++)
11750 : {
11751 176 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11752 176 : PQExpBuffer conprefix = createPQExpBuffer();
11753 :
11754 176 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
11755 176 : fmtId(domcheck->dobj.name));
11756 :
11757 176 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
11758 66 : dumpComment(fout, conprefix->data, qtypname,
11759 66 : tyinfo->dobj.namespace->dobj.name,
11760 : tyinfo->rolname,
11761 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
11762 :
11763 176 : destroyPQExpBuffer(conprefix);
11764 : }
11765 :
11766 266 : destroyPQExpBuffer(q);
11767 266 : destroyPQExpBuffer(delq);
11768 266 : destroyPQExpBuffer(query);
11769 266 : free(qtypname);
11770 266 : free(qualtypname);
11771 266 : }
11772 :
11773 : /*
11774 : * dumpCompositeType
11775 : * writes out to fout the queries to recreate a user-defined stand-alone
11776 : * composite type
11777 : */
11778 : static void
11779 262 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
11780 : {
11781 262 : DumpOptions *dopt = fout->dopt;
11782 262 : PQExpBuffer q = createPQExpBuffer();
11783 262 : PQExpBuffer dropped = createPQExpBuffer();
11784 262 : PQExpBuffer delq = createPQExpBuffer();
11785 262 : PQExpBuffer query = createPQExpBuffer();
11786 : PGresult *res;
11787 : char *qtypname;
11788 : char *qualtypname;
11789 : int ntups;
11790 : int i_attname;
11791 : int i_atttypdefn;
11792 : int i_attlen;
11793 : int i_attalign;
11794 : int i_attisdropped;
11795 : int i_attcollation;
11796 : int i;
11797 : int actual_atts;
11798 :
11799 262 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
11800 : {
11801 : /*
11802 : * Set up query for type-specific details.
11803 : *
11804 : * Since we only want to dump COLLATE clauses for attributes whose
11805 : * collation is different from their type's default, we use a CASE
11806 : * here to suppress uninteresting attcollations cheaply. atttypid
11807 : * will be 0 for dropped columns; collation does not matter for those.
11808 : */
11809 112 : appendPQExpBufferStr(query,
11810 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
11811 : "SELECT a.attname, a.attnum, "
11812 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
11813 : "a.attlen, a.attalign, a.attisdropped, "
11814 : "CASE WHEN a.attcollation <> at.typcollation "
11815 : "THEN a.attcollation ELSE 0 END AS attcollation "
11816 : "FROM pg_catalog.pg_type ct "
11817 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
11818 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
11819 : "WHERE ct.oid = $1 "
11820 : "ORDER BY a.attnum");
11821 :
11822 112 : ExecuteSqlStatement(fout, query->data);
11823 :
11824 112 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
11825 : }
11826 :
11827 262 : printfPQExpBuffer(query,
11828 : "EXECUTE dumpCompositeType('%u')",
11829 : tyinfo->dobj.catId.oid);
11830 :
11831 262 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11832 :
11833 262 : ntups = PQntuples(res);
11834 :
11835 262 : i_attname = PQfnumber(res, "attname");
11836 262 : i_atttypdefn = PQfnumber(res, "atttypdefn");
11837 262 : i_attlen = PQfnumber(res, "attlen");
11838 262 : i_attalign = PQfnumber(res, "attalign");
11839 262 : i_attisdropped = PQfnumber(res, "attisdropped");
11840 262 : i_attcollation = PQfnumber(res, "attcollation");
11841 :
11842 262 : if (dopt->binary_upgrade)
11843 : {
11844 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11845 : tyinfo->dobj.catId.oid,
11846 : false, false);
11847 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
11848 : }
11849 :
11850 262 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11851 262 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11852 :
11853 262 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11854 : qualtypname);
11855 :
11856 262 : actual_atts = 0;
11857 830 : for (i = 0; i < ntups; i++)
11858 : {
11859 : char *attname;
11860 : char *atttypdefn;
11861 : char *attlen;
11862 : char *attalign;
11863 : bool attisdropped;
11864 : Oid attcollation;
11865 :
11866 568 : attname = PQgetvalue(res, i, i_attname);
11867 568 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11868 568 : attlen = PQgetvalue(res, i, i_attlen);
11869 568 : attalign = PQgetvalue(res, i, i_attalign);
11870 568 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11871 568 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11872 :
11873 568 : if (attisdropped && !dopt->binary_upgrade)
11874 16 : continue;
11875 :
11876 : /* Format properly if not first attr */
11877 552 : if (actual_atts++ > 0)
11878 290 : appendPQExpBufferChar(q, ',');
11879 552 : appendPQExpBufferStr(q, "\n\t");
11880 :
11881 552 : if (!attisdropped)
11882 : {
11883 548 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11884 :
11885 : /* Add collation if not default for the column type */
11886 548 : if (OidIsValid(attcollation))
11887 : {
11888 : CollInfo *coll;
11889 :
11890 0 : coll = findCollationByOid(attcollation);
11891 0 : if (coll)
11892 0 : appendPQExpBuffer(q, " COLLATE %s",
11893 0 : fmtQualifiedDumpable(coll));
11894 : }
11895 : }
11896 : else
11897 : {
11898 : /*
11899 : * This is a dropped attribute and we're in binary_upgrade mode.
11900 : * Insert a placeholder for it in the CREATE TYPE command, and set
11901 : * length and alignment with direct UPDATE to the catalogs
11902 : * afterwards. See similar code in dumpTableSchema().
11903 : */
11904 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11905 :
11906 : /* stash separately for insertion after the CREATE TYPE */
11907 4 : appendPQExpBufferStr(dropped,
11908 : "\n-- For binary upgrade, recreate dropped column.\n");
11909 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11910 : "SET attlen = %s, "
11911 : "attalign = '%s', attbyval = false\n"
11912 : "WHERE attname = ", attlen, attalign);
11913 4 : appendStringLiteralAH(dropped, attname, fout);
11914 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11915 4 : appendStringLiteralAH(dropped, qualtypname, fout);
11916 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11917 :
11918 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11919 : qualtypname);
11920 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11921 : fmtId(attname));
11922 : }
11923 : }
11924 262 : appendPQExpBufferStr(q, "\n);\n");
11925 262 : appendPQExpBufferStr(q, dropped->data);
11926 :
11927 262 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11928 :
11929 262 : if (dopt->binary_upgrade)
11930 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11931 : "TYPE", qtypname,
11932 36 : tyinfo->dobj.namespace->dobj.name);
11933 :
11934 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11935 228 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11936 228 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11937 : .namespace = tyinfo->dobj.namespace->dobj.name,
11938 : .owner = tyinfo->rolname,
11939 : .description = "TYPE",
11940 : .section = SECTION_PRE_DATA,
11941 : .createStmt = q->data,
11942 : .dropStmt = delq->data));
11943 :
11944 :
11945 : /* Dump Type Comments and Security Labels */
11946 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11947 66 : dumpComment(fout, "TYPE", qtypname,
11948 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11949 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11950 :
11951 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11952 0 : dumpSecLabel(fout, "TYPE", qtypname,
11953 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11954 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11955 :
11956 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11957 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11958 : qtypname, NULL,
11959 36 : tyinfo->dobj.namespace->dobj.name,
11960 : NULL, tyinfo->rolname, &tyinfo->dacl);
11961 :
11962 : /* Dump any per-column comments */
11963 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11964 66 : dumpCompositeTypeColComments(fout, tyinfo, res);
11965 :
11966 262 : PQclear(res);
11967 262 : destroyPQExpBuffer(q);
11968 262 : destroyPQExpBuffer(dropped);
11969 262 : destroyPQExpBuffer(delq);
11970 262 : destroyPQExpBuffer(query);
11971 262 : free(qtypname);
11972 262 : free(qualtypname);
11973 262 : }
11974 :
11975 : /*
11976 : * dumpCompositeTypeColComments
11977 : * writes out to fout the queries to recreate comments on the columns of
11978 : * a user-defined stand-alone composite type.
11979 : *
11980 : * The caller has already made a query to collect the names and attnums
11981 : * of the type's columns, so we just pass that result into here rather
11982 : * than reading them again.
11983 : */
11984 : static void
11985 66 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
11986 : PGresult *res)
11987 : {
11988 : CommentItem *comments;
11989 : int ncomments;
11990 : PQExpBuffer query;
11991 : PQExpBuffer target;
11992 : int i;
11993 : int ntups;
11994 : int i_attname;
11995 : int i_attnum;
11996 : int i_attisdropped;
11997 :
11998 : /* do nothing, if --no-comments is supplied */
11999 66 : if (fout->dopt->no_comments)
12000 0 : return;
12001 :
12002 : /* Search for comments associated with type's pg_class OID */
12003 66 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12004 : &comments);
12005 :
12006 : /* If no comments exist, we're done */
12007 66 : if (ncomments <= 0)
12008 0 : return;
12009 :
12010 : /* Build COMMENT ON statements */
12011 66 : query = createPQExpBuffer();
12012 66 : target = createPQExpBuffer();
12013 :
12014 66 : ntups = PQntuples(res);
12015 66 : i_attnum = PQfnumber(res, "attnum");
12016 66 : i_attname = PQfnumber(res, "attname");
12017 66 : i_attisdropped = PQfnumber(res, "attisdropped");
12018 132 : while (ncomments > 0)
12019 : {
12020 : const char *attname;
12021 :
12022 66 : attname = NULL;
12023 66 : for (i = 0; i < ntups; i++)
12024 : {
12025 66 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12026 66 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12027 : {
12028 66 : attname = PQgetvalue(res, i, i_attname);
12029 66 : break;
12030 : }
12031 : }
12032 66 : if (attname) /* just in case we don't find it */
12033 : {
12034 66 : const char *descr = comments->descr;
12035 :
12036 66 : resetPQExpBuffer(target);
12037 66 : appendPQExpBuffer(target, "COLUMN %s.",
12038 66 : fmtId(tyinfo->dobj.name));
12039 66 : appendPQExpBufferStr(target, fmtId(attname));
12040 :
12041 66 : resetPQExpBuffer(query);
12042 66 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12043 66 : fmtQualifiedDumpable(tyinfo));
12044 66 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12045 66 : appendStringLiteralAH(query, descr, fout);
12046 66 : appendPQExpBufferStr(query, ";\n");
12047 :
12048 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12049 66 : ARCHIVE_OPTS(.tag = target->data,
12050 : .namespace = tyinfo->dobj.namespace->dobj.name,
12051 : .owner = tyinfo->rolname,
12052 : .description = "COMMENT",
12053 : .section = SECTION_NONE,
12054 : .createStmt = query->data,
12055 : .deps = &(tyinfo->dobj.dumpId),
12056 : .nDeps = 1));
12057 : }
12058 :
12059 66 : comments++;
12060 66 : ncomments--;
12061 : }
12062 :
12063 66 : destroyPQExpBuffer(query);
12064 66 : destroyPQExpBuffer(target);
12065 : }
12066 :
12067 : /*
12068 : * dumpShellType
12069 : * writes out to fout the queries to create a shell type
12070 : *
12071 : * We dump a shell definition in advance of the I/O functions for the type.
12072 : */
12073 : static void
12074 142 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12075 : {
12076 142 : DumpOptions *dopt = fout->dopt;
12077 : PQExpBuffer q;
12078 :
12079 : /* Do nothing if not dumping schema */
12080 142 : if (!dopt->dumpSchema)
12081 6 : return;
12082 :
12083 136 : q = createPQExpBuffer();
12084 :
12085 : /*
12086 : * Note the lack of a DROP command for the shell type; any required DROP
12087 : * is driven off the base type entry, instead. This interacts with
12088 : * _printTocEntry()'s use of the presence of a DROP command to decide
12089 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12090 : * the shell type's owner immediately on creation; that should happen only
12091 : * after it's filled in, otherwise the backend complains.
12092 : */
12093 :
12094 136 : if (dopt->binary_upgrade)
12095 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12096 16 : stinfo->baseType->dobj.catId.oid,
12097 : false, false);
12098 :
12099 136 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12100 136 : fmtQualifiedDumpable(stinfo));
12101 :
12102 136 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12103 136 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12104 136 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12105 : .namespace = stinfo->dobj.namespace->dobj.name,
12106 : .owner = stinfo->baseType->rolname,
12107 : .description = "SHELL TYPE",
12108 : .section = SECTION_PRE_DATA,
12109 : .createStmt = q->data));
12110 :
12111 136 : destroyPQExpBuffer(q);
12112 : }
12113 :
12114 : /*
12115 : * dumpProcLang
12116 : * writes out to fout the queries to recreate a user-defined
12117 : * procedural language
12118 : */
12119 : static void
12120 156 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12121 : {
12122 156 : DumpOptions *dopt = fout->dopt;
12123 : PQExpBuffer defqry;
12124 : PQExpBuffer delqry;
12125 : bool useParams;
12126 : char *qlanname;
12127 : FuncInfo *funcInfo;
12128 156 : FuncInfo *inlineInfo = NULL;
12129 156 : FuncInfo *validatorInfo = NULL;
12130 :
12131 : /* Do nothing if not dumping schema */
12132 156 : if (!dopt->dumpSchema)
12133 14 : return;
12134 :
12135 : /*
12136 : * Try to find the support function(s). It is not an error if we don't
12137 : * find them --- if the functions are in the pg_catalog schema, as is
12138 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12139 : * we will emit a parameterless CREATE LANGUAGE command, which will
12140 : * require PL template knowledge in the backend to reload.)
12141 : */
12142 :
12143 142 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12144 142 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12145 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12146 :
12147 142 : if (OidIsValid(plang->laninline))
12148 : {
12149 78 : inlineInfo = findFuncByOid(plang->laninline);
12150 78 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12151 2 : inlineInfo = NULL;
12152 : }
12153 :
12154 142 : if (OidIsValid(plang->lanvalidator))
12155 : {
12156 78 : validatorInfo = findFuncByOid(plang->lanvalidator);
12157 78 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12158 2 : validatorInfo = NULL;
12159 : }
12160 :
12161 : /*
12162 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12163 : * parameters. Otherwise, we'll write a parameterless command, which will
12164 : * be interpreted as CREATE EXTENSION.
12165 : */
12166 62 : useParams = (funcInfo != NULL &&
12167 266 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12168 62 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12169 :
12170 142 : defqry = createPQExpBuffer();
12171 142 : delqry = createPQExpBuffer();
12172 :
12173 142 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12174 :
12175 142 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12176 : qlanname);
12177 :
12178 142 : if (useParams)
12179 : {
12180 62 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12181 62 : plang->lanpltrusted ? "TRUSTED " : "",
12182 : qlanname);
12183 62 : appendPQExpBuffer(defqry, " HANDLER %s",
12184 62 : fmtQualifiedDumpable(funcInfo));
12185 62 : if (OidIsValid(plang->laninline))
12186 0 : appendPQExpBuffer(defqry, " INLINE %s",
12187 0 : fmtQualifiedDumpable(inlineInfo));
12188 62 : if (OidIsValid(plang->lanvalidator))
12189 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
12190 0 : fmtQualifiedDumpable(validatorInfo));
12191 : }
12192 : else
12193 : {
12194 : /*
12195 : * If not dumping parameters, then use CREATE OR REPLACE so that the
12196 : * command will not fail if the language is preinstalled in the target
12197 : * database.
12198 : *
12199 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
12200 : * EXISTS; perhaps we should emit that instead? But it might just add
12201 : * confusion.
12202 : */
12203 80 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12204 : qlanname);
12205 : }
12206 142 : appendPQExpBufferStr(defqry, ";\n");
12207 :
12208 142 : if (dopt->binary_upgrade)
12209 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
12210 : "LANGUAGE", qlanname, NULL);
12211 :
12212 142 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12213 64 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12214 64 : ARCHIVE_OPTS(.tag = plang->dobj.name,
12215 : .owner = plang->lanowner,
12216 : .description = "PROCEDURAL LANGUAGE",
12217 : .section = SECTION_PRE_DATA,
12218 : .createStmt = defqry->data,
12219 : .dropStmt = delqry->data,
12220 : ));
12221 :
12222 : /* Dump Proc Lang Comments and Security Labels */
12223 142 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12224 0 : dumpComment(fout, "LANGUAGE", qlanname,
12225 : NULL, plang->lanowner,
12226 : plang->dobj.catId, 0, plang->dobj.dumpId);
12227 :
12228 142 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12229 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
12230 : NULL, plang->lanowner,
12231 : plang->dobj.catId, 0, plang->dobj.dumpId);
12232 :
12233 142 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12234 78 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12235 : qlanname, NULL, NULL,
12236 : NULL, plang->lanowner, &plang->dacl);
12237 :
12238 142 : free(qlanname);
12239 :
12240 142 : destroyPQExpBuffer(defqry);
12241 142 : destroyPQExpBuffer(delqry);
12242 : }
12243 :
12244 : /*
12245 : * format_function_arguments: generate function name and argument list
12246 : *
12247 : * This is used when we can rely on pg_get_function_arguments to format
12248 : * the argument list. Note, however, that pg_get_function_arguments
12249 : * does not special-case zero-argument aggregates.
12250 : */
12251 : static char *
12252 8148 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12253 : {
12254 : PQExpBufferData fn;
12255 :
12256 8148 : initPQExpBuffer(&fn);
12257 8148 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12258 8148 : if (is_agg && finfo->nargs == 0)
12259 160 : appendPQExpBufferStr(&fn, "(*)");
12260 : else
12261 7988 : appendPQExpBuffer(&fn, "(%s)", funcargs);
12262 8148 : return fn.data;
12263 : }
12264 :
12265 : /*
12266 : * format_function_signature: generate function name and argument list
12267 : *
12268 : * Only a minimal list of input argument types is generated; this is
12269 : * sufficient to reference the function, but not to define it.
12270 : *
12271 : * If honor_quotes is false then the function name is never quoted.
12272 : * This is appropriate for use in TOC tags, but not in SQL commands.
12273 : */
12274 : static char *
12275 4296 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12276 : {
12277 : PQExpBufferData fn;
12278 : int j;
12279 :
12280 4296 : initPQExpBuffer(&fn);
12281 4296 : if (honor_quotes)
12282 794 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12283 : else
12284 3502 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12285 7904 : for (j = 0; j < finfo->nargs; j++)
12286 : {
12287 3608 : if (j > 0)
12288 844 : appendPQExpBufferStr(&fn, ", ");
12289 :
12290 3608 : appendPQExpBufferStr(&fn,
12291 3608 : getFormattedTypeName(fout, finfo->argtypes[j],
12292 : zeroIsError));
12293 : }
12294 4296 : appendPQExpBufferChar(&fn, ')');
12295 4296 : return fn.data;
12296 : }
12297 :
12298 :
12299 : /*
12300 : * dumpFunc:
12301 : * dump out one function
12302 : */
12303 : static void
12304 3566 : dumpFunc(Archive *fout, const FuncInfo *finfo)
12305 : {
12306 3566 : DumpOptions *dopt = fout->dopt;
12307 : PQExpBuffer query;
12308 : PQExpBuffer q;
12309 : PQExpBuffer delqry;
12310 : PQExpBuffer asPart;
12311 : PGresult *res;
12312 : char *funcsig; /* identity signature */
12313 3566 : char *funcfullsig = NULL; /* full signature */
12314 : char *funcsig_tag;
12315 : char *qual_funcsig;
12316 : char *proretset;
12317 : char *prosrc;
12318 : char *probin;
12319 : char *prosqlbody;
12320 : char *funcargs;
12321 : char *funciargs;
12322 : char *funcresult;
12323 : char *protrftypes;
12324 : char *prokind;
12325 : char *provolatile;
12326 : char *proisstrict;
12327 : char *prosecdef;
12328 : char *proleakproof;
12329 : char *proconfig;
12330 : char *procost;
12331 : char *prorows;
12332 : char *prosupport;
12333 : char *proparallel;
12334 : char *lanname;
12335 3566 : char **configitems = NULL;
12336 3566 : int nconfigitems = 0;
12337 : const char *keyword;
12338 :
12339 : /* Do nothing if not dumping schema */
12340 3566 : if (!dopt->dumpSchema)
12341 64 : return;
12342 :
12343 3502 : query = createPQExpBuffer();
12344 3502 : q = createPQExpBuffer();
12345 3502 : delqry = createPQExpBuffer();
12346 3502 : asPart = createPQExpBuffer();
12347 :
12348 3502 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12349 : {
12350 : /* Set up query for function-specific details */
12351 124 : appendPQExpBufferStr(query,
12352 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12353 :
12354 124 : appendPQExpBufferStr(query,
12355 : "SELECT\n"
12356 : "proretset,\n"
12357 : "prosrc,\n"
12358 : "probin,\n"
12359 : "provolatile,\n"
12360 : "proisstrict,\n"
12361 : "prosecdef,\n"
12362 : "lanname,\n"
12363 : "proconfig,\n"
12364 : "procost,\n"
12365 : "prorows,\n"
12366 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12367 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12368 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12369 : "proleakproof,\n");
12370 :
12371 124 : if (fout->remoteVersion >= 90500)
12372 124 : appendPQExpBufferStr(query,
12373 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12374 : else
12375 0 : appendPQExpBufferStr(query,
12376 : "NULL AS protrftypes,\n");
12377 :
12378 124 : if (fout->remoteVersion >= 90600)
12379 124 : appendPQExpBufferStr(query,
12380 : "proparallel,\n");
12381 : else
12382 0 : appendPQExpBufferStr(query,
12383 : "'u' AS proparallel,\n");
12384 :
12385 124 : if (fout->remoteVersion >= 110000)
12386 124 : appendPQExpBufferStr(query,
12387 : "prokind,\n");
12388 : else
12389 0 : appendPQExpBufferStr(query,
12390 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12391 :
12392 124 : if (fout->remoteVersion >= 120000)
12393 124 : appendPQExpBufferStr(query,
12394 : "prosupport,\n");
12395 : else
12396 0 : appendPQExpBufferStr(query,
12397 : "'-' AS prosupport,\n");
12398 :
12399 124 : if (fout->remoteVersion >= 140000)
12400 124 : appendPQExpBufferStr(query,
12401 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12402 : else
12403 0 : appendPQExpBufferStr(query,
12404 : "NULL AS prosqlbody\n");
12405 :
12406 124 : appendPQExpBufferStr(query,
12407 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12408 : "WHERE p.oid = $1 "
12409 : "AND l.oid = p.prolang");
12410 :
12411 124 : ExecuteSqlStatement(fout, query->data);
12412 :
12413 124 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12414 : }
12415 :
12416 3502 : printfPQExpBuffer(query,
12417 : "EXECUTE dumpFunc('%u')",
12418 : finfo->dobj.catId.oid);
12419 :
12420 3502 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12421 :
12422 3502 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12423 3502 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12424 : {
12425 3404 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12426 3404 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12427 3404 : prosqlbody = NULL;
12428 : }
12429 : else
12430 : {
12431 98 : prosrc = NULL;
12432 98 : probin = NULL;
12433 98 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12434 : }
12435 3502 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12436 3502 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12437 3502 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12438 3502 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12439 3502 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12440 3502 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12441 3502 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12442 3502 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12443 3502 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12444 3502 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12445 3502 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12446 3502 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12447 3502 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12448 3502 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12449 3502 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12450 :
12451 : /*
12452 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
12453 : * is used.
12454 : */
12455 3502 : if (prosqlbody)
12456 : {
12457 98 : appendPQExpBufferStr(asPart, prosqlbody);
12458 : }
12459 3404 : else if (probin[0] != '\0')
12460 : {
12461 286 : appendPQExpBufferStr(asPart, "AS ");
12462 286 : appendStringLiteralAH(asPart, probin, fout);
12463 286 : if (prosrc[0] != '\0')
12464 : {
12465 286 : appendPQExpBufferStr(asPart, ", ");
12466 :
12467 : /*
12468 : * where we have bin, use dollar quoting if allowed and src
12469 : * contains quote or backslash; else use regular quoting.
12470 : */
12471 286 : if (dopt->disable_dollar_quoting ||
12472 286 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12473 286 : appendStringLiteralAH(asPart, prosrc, fout);
12474 : else
12475 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
12476 : }
12477 : }
12478 : else
12479 : {
12480 3118 : appendPQExpBufferStr(asPart, "AS ");
12481 : /* with no bin, dollar quote src unconditionally if allowed */
12482 3118 : if (dopt->disable_dollar_quoting)
12483 0 : appendStringLiteralAH(asPart, prosrc, fout);
12484 : else
12485 3118 : appendStringLiteralDQ(asPart, prosrc, NULL);
12486 : }
12487 :
12488 3502 : if (*proconfig)
12489 : {
12490 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12491 0 : pg_fatal("could not parse %s array", "proconfig");
12492 : }
12493 : else
12494 : {
12495 3472 : configitems = NULL;
12496 3472 : nconfigitems = 0;
12497 : }
12498 :
12499 3502 : funcfullsig = format_function_arguments(finfo, funcargs, false);
12500 3502 : funcsig = format_function_arguments(finfo, funciargs, false);
12501 :
12502 3502 : funcsig_tag = format_function_signature(fout, finfo, false);
12503 :
12504 3502 : qual_funcsig = psprintf("%s.%s",
12505 3502 : fmtId(finfo->dobj.namespace->dobj.name),
12506 : funcsig);
12507 :
12508 3502 : if (prokind[0] == PROKIND_PROCEDURE)
12509 186 : keyword = "PROCEDURE";
12510 : else
12511 3316 : keyword = "FUNCTION"; /* works for window functions too */
12512 :
12513 3502 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
12514 : keyword, qual_funcsig);
12515 :
12516 7004 : appendPQExpBuffer(q, "CREATE %s %s.%s",
12517 : keyword,
12518 3502 : fmtId(finfo->dobj.namespace->dobj.name),
12519 : funcfullsig ? funcfullsig :
12520 : funcsig);
12521 :
12522 3502 : if (prokind[0] == PROKIND_PROCEDURE)
12523 : /* no result type to output */ ;
12524 3316 : else if (funcresult)
12525 3316 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
12526 : else
12527 0 : appendPQExpBuffer(q, " RETURNS %s%s",
12528 0 : (proretset[0] == 't') ? "SETOF " : "",
12529 : getFormattedTypeName(fout, finfo->prorettype,
12530 : zeroIsError));
12531 :
12532 3502 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12533 :
12534 3502 : if (*protrftypes)
12535 : {
12536 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
12537 : int i;
12538 :
12539 0 : appendPQExpBufferStr(q, " TRANSFORM ");
12540 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12541 0 : for (i = 0; typeids[i]; i++)
12542 : {
12543 0 : if (i != 0)
12544 0 : appendPQExpBufferStr(q, ", ");
12545 0 : appendPQExpBuffer(q, "FOR TYPE %s",
12546 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
12547 : }
12548 :
12549 0 : free(typeids);
12550 : }
12551 :
12552 3502 : if (prokind[0] == PROKIND_WINDOW)
12553 10 : appendPQExpBufferStr(q, " WINDOW");
12554 :
12555 3502 : if (provolatile[0] != PROVOLATILE_VOLATILE)
12556 : {
12557 696 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12558 664 : appendPQExpBufferStr(q, " IMMUTABLE");
12559 32 : else if (provolatile[0] == PROVOLATILE_STABLE)
12560 32 : appendPQExpBufferStr(q, " STABLE");
12561 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
12562 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
12563 : finfo->dobj.name);
12564 : }
12565 :
12566 3502 : if (proisstrict[0] == 't')
12567 702 : appendPQExpBufferStr(q, " STRICT");
12568 :
12569 3502 : if (prosecdef[0] == 't')
12570 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
12571 :
12572 3502 : if (proleakproof[0] == 't')
12573 20 : appendPQExpBufferStr(q, " LEAKPROOF");
12574 :
12575 : /*
12576 : * COST and ROWS are emitted only if present and not default, so as not to
12577 : * break backwards-compatibility of the dump without need. Keep this code
12578 : * in sync with the defaults in functioncmds.c.
12579 : */
12580 3502 : if (strcmp(procost, "0") != 0)
12581 : {
12582 3502 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12583 : {
12584 : /* default cost is 1 */
12585 752 : if (strcmp(procost, "1") != 0)
12586 0 : appendPQExpBuffer(q, " COST %s", procost);
12587 : }
12588 : else
12589 : {
12590 : /* default cost is 100 */
12591 2750 : if (strcmp(procost, "100") != 0)
12592 12 : appendPQExpBuffer(q, " COST %s", procost);
12593 : }
12594 : }
12595 3502 : if (proretset[0] == 't' &&
12596 376 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12597 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
12598 :
12599 3502 : if (strcmp(prosupport, "-") != 0)
12600 : {
12601 : /* We rely on regprocout to provide quoting and qualification */
12602 86 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12603 : }
12604 :
12605 3502 : if (proparallel[0] != PROPARALLEL_UNSAFE)
12606 : {
12607 236 : if (proparallel[0] == PROPARALLEL_SAFE)
12608 226 : appendPQExpBufferStr(q, " PARALLEL SAFE");
12609 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12610 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12611 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
12612 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
12613 : finfo->dobj.name);
12614 : }
12615 :
12616 3572 : for (int i = 0; i < nconfigitems; i++)
12617 : {
12618 : /* we feel free to scribble on configitems[] here */
12619 70 : char *configitem = configitems[i];
12620 : char *pos;
12621 :
12622 70 : pos = strchr(configitem, '=');
12623 70 : if (pos == NULL)
12624 0 : continue;
12625 70 : *pos++ = '\0';
12626 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12627 :
12628 : /*
12629 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12630 : * by flatten_set_variable_args() before they were put into the
12631 : * proconfig array. However, because the quoting rules used there
12632 : * aren't exactly like SQL's, we have to break the list value apart
12633 : * and then quote the elements as string literals. (The elements may
12634 : * be double-quoted as-is, but we can't just feed them to the SQL
12635 : * parser; it would do the wrong thing with elements that are
12636 : * zero-length or longer than NAMEDATALEN.)
12637 : *
12638 : * Variables that are not so marked should just be emitted as simple
12639 : * string literals. If the variable is not known to
12640 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12641 : * to use GUC_LIST_QUOTE for extension variables.
12642 : */
12643 70 : if (variable_is_guc_list_quote(configitem))
12644 : {
12645 : char **namelist;
12646 : char **nameptr;
12647 :
12648 : /* Parse string into list of identifiers */
12649 : /* this shouldn't fail really */
12650 20 : if (SplitGUCList(pos, ',', &namelist))
12651 : {
12652 70 : for (nameptr = namelist; *nameptr; nameptr++)
12653 : {
12654 50 : if (nameptr != namelist)
12655 30 : appendPQExpBufferStr(q, ", ");
12656 50 : appendStringLiteralAH(q, *nameptr, fout);
12657 : }
12658 : }
12659 20 : pg_free(namelist);
12660 : }
12661 : else
12662 50 : appendStringLiteralAH(q, pos, fout);
12663 : }
12664 :
12665 3502 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12666 :
12667 3502 : append_depends_on_extension(fout, q, &finfo->dobj,
12668 : "pg_catalog.pg_proc", keyword,
12669 : qual_funcsig);
12670 :
12671 3502 : if (dopt->binary_upgrade)
12672 572 : binary_upgrade_extension_member(q, &finfo->dobj,
12673 : keyword, funcsig,
12674 572 : finfo->dobj.namespace->dobj.name);
12675 :
12676 3502 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12677 3306 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12678 3306 : ARCHIVE_OPTS(.tag = funcsig_tag,
12679 : .namespace = finfo->dobj.namespace->dobj.name,
12680 : .owner = finfo->rolname,
12681 : .description = keyword,
12682 : .section = finfo->postponed_def ?
12683 : SECTION_POST_DATA : SECTION_PRE_DATA,
12684 : .createStmt = q->data,
12685 : .dropStmt = delqry->data));
12686 :
12687 : /* Dump Function Comments and Security Labels */
12688 3502 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12689 18 : dumpComment(fout, keyword, funcsig,
12690 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12691 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12692 :
12693 3502 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12694 0 : dumpSecLabel(fout, keyword, funcsig,
12695 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12696 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12697 :
12698 3502 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12699 204 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
12700 : funcsig, NULL,
12701 204 : finfo->dobj.namespace->dobj.name,
12702 : NULL, finfo->rolname, &finfo->dacl);
12703 :
12704 3502 : PQclear(res);
12705 :
12706 3502 : destroyPQExpBuffer(query);
12707 3502 : destroyPQExpBuffer(q);
12708 3502 : destroyPQExpBuffer(delqry);
12709 3502 : destroyPQExpBuffer(asPart);
12710 3502 : free(funcsig);
12711 3502 : free(funcfullsig);
12712 3502 : free(funcsig_tag);
12713 3502 : free(qual_funcsig);
12714 3502 : free(configitems);
12715 : }
12716 :
12717 :
12718 : /*
12719 : * Dump a user-defined cast
12720 : */
12721 : static void
12722 130 : dumpCast(Archive *fout, const CastInfo *cast)
12723 : {
12724 130 : DumpOptions *dopt = fout->dopt;
12725 : PQExpBuffer defqry;
12726 : PQExpBuffer delqry;
12727 : PQExpBuffer labelq;
12728 : PQExpBuffer castargs;
12729 130 : FuncInfo *funcInfo = NULL;
12730 : const char *sourceType;
12731 : const char *targetType;
12732 :
12733 : /* Do nothing if not dumping schema */
12734 130 : if (!dopt->dumpSchema)
12735 6 : return;
12736 :
12737 : /* Cannot dump if we don't have the cast function's info */
12738 124 : if (OidIsValid(cast->castfunc))
12739 : {
12740 74 : funcInfo = findFuncByOid(cast->castfunc);
12741 74 : if (funcInfo == NULL)
12742 0 : pg_fatal("could not find function definition for function with OID %u",
12743 : cast->castfunc);
12744 : }
12745 :
12746 124 : defqry = createPQExpBuffer();
12747 124 : delqry = createPQExpBuffer();
12748 124 : labelq = createPQExpBuffer();
12749 124 : castargs = createPQExpBuffer();
12750 :
12751 124 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12752 124 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12753 124 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12754 : sourceType, targetType);
12755 :
12756 124 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12757 : sourceType, targetType);
12758 :
12759 124 : switch (cast->castmethod)
12760 : {
12761 50 : case COERCION_METHOD_BINARY:
12762 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12763 50 : break;
12764 0 : case COERCION_METHOD_INOUT:
12765 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
12766 0 : break;
12767 74 : case COERCION_METHOD_FUNCTION:
12768 74 : if (funcInfo)
12769 : {
12770 74 : char *fsig = format_function_signature(fout, funcInfo, true);
12771 :
12772 : /*
12773 : * Always qualify the function name (format_function_signature
12774 : * won't qualify it).
12775 : */
12776 74 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12777 74 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12778 74 : free(fsig);
12779 : }
12780 : else
12781 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12782 74 : break;
12783 0 : default:
12784 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
12785 : }
12786 :
12787 124 : if (cast->castcontext == 'a')
12788 64 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12789 60 : else if (cast->castcontext == 'i')
12790 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
12791 124 : appendPQExpBufferStr(defqry, ";\n");
12792 :
12793 124 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12794 : sourceType, targetType);
12795 :
12796 124 : appendPQExpBuffer(castargs, "(%s AS %s)",
12797 : sourceType, targetType);
12798 :
12799 124 : if (dopt->binary_upgrade)
12800 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
12801 14 : "CAST", castargs->data, NULL);
12802 :
12803 124 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12804 124 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12805 124 : ARCHIVE_OPTS(.tag = labelq->data,
12806 : .description = "CAST",
12807 : .section = SECTION_PRE_DATA,
12808 : .createStmt = defqry->data,
12809 : .dropStmt = delqry->data));
12810 :
12811 : /* Dump Cast Comments */
12812 124 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12813 0 : dumpComment(fout, "CAST", castargs->data,
12814 : NULL, "",
12815 : cast->dobj.catId, 0, cast->dobj.dumpId);
12816 :
12817 124 : destroyPQExpBuffer(defqry);
12818 124 : destroyPQExpBuffer(delqry);
12819 124 : destroyPQExpBuffer(labelq);
12820 124 : destroyPQExpBuffer(castargs);
12821 : }
12822 :
12823 : /*
12824 : * Dump a transform
12825 : */
12826 : static void
12827 80 : dumpTransform(Archive *fout, const TransformInfo *transform)
12828 : {
12829 80 : DumpOptions *dopt = fout->dopt;
12830 : PQExpBuffer defqry;
12831 : PQExpBuffer delqry;
12832 : PQExpBuffer labelq;
12833 : PQExpBuffer transformargs;
12834 80 : FuncInfo *fromsqlFuncInfo = NULL;
12835 80 : FuncInfo *tosqlFuncInfo = NULL;
12836 : char *lanname;
12837 : const char *transformType;
12838 :
12839 : /* Do nothing if not dumping schema */
12840 80 : if (!dopt->dumpSchema)
12841 6 : return;
12842 :
12843 : /* Cannot dump if we don't have the transform functions' info */
12844 74 : if (OidIsValid(transform->trffromsql))
12845 : {
12846 74 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12847 74 : if (fromsqlFuncInfo == NULL)
12848 0 : pg_fatal("could not find function definition for function with OID %u",
12849 : transform->trffromsql);
12850 : }
12851 74 : if (OidIsValid(transform->trftosql))
12852 : {
12853 74 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
12854 74 : if (tosqlFuncInfo == NULL)
12855 0 : pg_fatal("could not find function definition for function with OID %u",
12856 : transform->trftosql);
12857 : }
12858 :
12859 74 : defqry = createPQExpBuffer();
12860 74 : delqry = createPQExpBuffer();
12861 74 : labelq = createPQExpBuffer();
12862 74 : transformargs = createPQExpBuffer();
12863 :
12864 74 : lanname = get_language_name(fout, transform->trflang);
12865 74 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12866 :
12867 74 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12868 : transformType, lanname);
12869 :
12870 74 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12871 : transformType, lanname);
12872 :
12873 74 : if (!transform->trffromsql && !transform->trftosql)
12874 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12875 :
12876 74 : if (transform->trffromsql)
12877 : {
12878 74 : if (fromsqlFuncInfo)
12879 : {
12880 74 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12881 :
12882 : /*
12883 : * Always qualify the function name (format_function_signature
12884 : * won't qualify it).
12885 : */
12886 74 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12887 74 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12888 74 : free(fsig);
12889 : }
12890 : else
12891 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
12892 : }
12893 :
12894 74 : if (transform->trftosql)
12895 : {
12896 74 : if (transform->trffromsql)
12897 74 : appendPQExpBufferStr(defqry, ", ");
12898 :
12899 74 : if (tosqlFuncInfo)
12900 : {
12901 74 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12902 :
12903 : /*
12904 : * Always qualify the function name (format_function_signature
12905 : * won't qualify it).
12906 : */
12907 74 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12908 74 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12909 74 : free(fsig);
12910 : }
12911 : else
12912 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
12913 : }
12914 :
12915 74 : appendPQExpBufferStr(defqry, ");\n");
12916 :
12917 74 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12918 : transformType, lanname);
12919 :
12920 74 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12921 : transformType, lanname);
12922 :
12923 74 : if (dopt->binary_upgrade)
12924 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
12925 4 : "TRANSFORM", transformargs->data, NULL);
12926 :
12927 74 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12928 74 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12929 74 : ARCHIVE_OPTS(.tag = labelq->data,
12930 : .description = "TRANSFORM",
12931 : .section = SECTION_PRE_DATA,
12932 : .createStmt = defqry->data,
12933 : .dropStmt = delqry->data,
12934 : .deps = transform->dobj.dependencies,
12935 : .nDeps = transform->dobj.nDeps));
12936 :
12937 : /* Dump Transform Comments */
12938 74 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12939 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
12940 : NULL, "",
12941 : transform->dobj.catId, 0, transform->dobj.dumpId);
12942 :
12943 74 : free(lanname);
12944 74 : destroyPQExpBuffer(defqry);
12945 74 : destroyPQExpBuffer(delqry);
12946 74 : destroyPQExpBuffer(labelq);
12947 74 : destroyPQExpBuffer(transformargs);
12948 : }
12949 :
12950 :
12951 : /*
12952 : * dumpOpr
12953 : * write out a single operator definition
12954 : */
12955 : static void
12956 5004 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
12957 : {
12958 5004 : DumpOptions *dopt = fout->dopt;
12959 : PQExpBuffer query;
12960 : PQExpBuffer q;
12961 : PQExpBuffer delq;
12962 : PQExpBuffer oprid;
12963 : PQExpBuffer details;
12964 : PGresult *res;
12965 : int i_oprkind;
12966 : int i_oprcode;
12967 : int i_oprleft;
12968 : int i_oprright;
12969 : int i_oprcom;
12970 : int i_oprnegate;
12971 : int i_oprrest;
12972 : int i_oprjoin;
12973 : int i_oprcanmerge;
12974 : int i_oprcanhash;
12975 : char *oprkind;
12976 : char *oprcode;
12977 : char *oprleft;
12978 : char *oprright;
12979 : char *oprcom;
12980 : char *oprnegate;
12981 : char *oprrest;
12982 : char *oprjoin;
12983 : char *oprcanmerge;
12984 : char *oprcanhash;
12985 : char *oprregproc;
12986 : char *oprref;
12987 :
12988 : /* Do nothing if not dumping schema */
12989 5004 : if (!dopt->dumpSchema)
12990 6 : return;
12991 :
12992 : /*
12993 : * some operators are invalid because they were the result of user
12994 : * defining operators before commutators exist
12995 : */
12996 4998 : if (!OidIsValid(oprinfo->oprcode))
12997 28 : return;
12998 :
12999 4970 : query = createPQExpBuffer();
13000 4970 : q = createPQExpBuffer();
13001 4970 : delq = createPQExpBuffer();
13002 4970 : oprid = createPQExpBuffer();
13003 4970 : details = createPQExpBuffer();
13004 :
13005 4970 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13006 : {
13007 : /* Set up query for operator-specific details */
13008 82 : appendPQExpBufferStr(query,
13009 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13010 : "SELECT oprkind, "
13011 : "oprcode::pg_catalog.regprocedure, "
13012 : "oprleft::pg_catalog.regtype, "
13013 : "oprright::pg_catalog.regtype, "
13014 : "oprcom, "
13015 : "oprnegate, "
13016 : "oprrest::pg_catalog.regprocedure, "
13017 : "oprjoin::pg_catalog.regprocedure, "
13018 : "oprcanmerge, oprcanhash "
13019 : "FROM pg_catalog.pg_operator "
13020 : "WHERE oid = $1");
13021 :
13022 82 : ExecuteSqlStatement(fout, query->data);
13023 :
13024 82 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13025 : }
13026 :
13027 4970 : printfPQExpBuffer(query,
13028 : "EXECUTE dumpOpr('%u')",
13029 : oprinfo->dobj.catId.oid);
13030 :
13031 4970 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13032 :
13033 4970 : i_oprkind = PQfnumber(res, "oprkind");
13034 4970 : i_oprcode = PQfnumber(res, "oprcode");
13035 4970 : i_oprleft = PQfnumber(res, "oprleft");
13036 4970 : i_oprright = PQfnumber(res, "oprright");
13037 4970 : i_oprcom = PQfnumber(res, "oprcom");
13038 4970 : i_oprnegate = PQfnumber(res, "oprnegate");
13039 4970 : i_oprrest = PQfnumber(res, "oprrest");
13040 4970 : i_oprjoin = PQfnumber(res, "oprjoin");
13041 4970 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13042 4970 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13043 :
13044 4970 : oprkind = PQgetvalue(res, 0, i_oprkind);
13045 4970 : oprcode = PQgetvalue(res, 0, i_oprcode);
13046 4970 : oprleft = PQgetvalue(res, 0, i_oprleft);
13047 4970 : oprright = PQgetvalue(res, 0, i_oprright);
13048 4970 : oprcom = PQgetvalue(res, 0, i_oprcom);
13049 4970 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13050 4970 : oprrest = PQgetvalue(res, 0, i_oprrest);
13051 4970 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13052 4970 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13053 4970 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13054 :
13055 : /* In PG14 upwards postfix operator support does not exist anymore. */
13056 4970 : if (strcmp(oprkind, "r") == 0)
13057 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13058 : oprcode);
13059 :
13060 4970 : oprregproc = convertRegProcReference(oprcode);
13061 4970 : if (oprregproc)
13062 : {
13063 4970 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13064 4970 : free(oprregproc);
13065 : }
13066 :
13067 4970 : appendPQExpBuffer(oprid, "%s (",
13068 : oprinfo->dobj.name);
13069 :
13070 : /*
13071 : * right unary means there's a left arg and left unary means there's a
13072 : * right arg. (Although the "r" case is dead code for PG14 and later,
13073 : * continue to support it in case we're dumping from an old server.)
13074 : */
13075 4970 : if (strcmp(oprkind, "r") == 0 ||
13076 4970 : strcmp(oprkind, "b") == 0)
13077 : {
13078 4684 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13079 4684 : appendPQExpBufferStr(oprid, oprleft);
13080 : }
13081 : else
13082 286 : appendPQExpBufferStr(oprid, "NONE");
13083 :
13084 4970 : if (strcmp(oprkind, "l") == 0 ||
13085 4684 : strcmp(oprkind, "b") == 0)
13086 : {
13087 4970 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13088 4970 : appendPQExpBuffer(oprid, ", %s)", oprright);
13089 : }
13090 : else
13091 0 : appendPQExpBufferStr(oprid, ", NONE)");
13092 :
13093 4970 : oprref = getFormattedOperatorName(oprcom);
13094 4970 : if (oprref)
13095 : {
13096 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13097 3322 : free(oprref);
13098 : }
13099 :
13100 4970 : oprref = getFormattedOperatorName(oprnegate);
13101 4970 : if (oprref)
13102 : {
13103 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13104 2326 : free(oprref);
13105 : }
13106 :
13107 4970 : if (strcmp(oprcanmerge, "t") == 0)
13108 370 : appendPQExpBufferStr(details, ",\n MERGES");
13109 :
13110 4970 : if (strcmp(oprcanhash, "t") == 0)
13111 276 : appendPQExpBufferStr(details, ",\n HASHES");
13112 :
13113 4970 : oprregproc = convertRegProcReference(oprrest);
13114 4970 : if (oprregproc)
13115 : {
13116 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13117 3028 : free(oprregproc);
13118 : }
13119 :
13120 4970 : oprregproc = convertRegProcReference(oprjoin);
13121 4970 : if (oprregproc)
13122 : {
13123 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13124 3028 : free(oprregproc);
13125 : }
13126 :
13127 4970 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13128 4970 : fmtId(oprinfo->dobj.namespace->dobj.name),
13129 : oprid->data);
13130 :
13131 4970 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13132 4970 : fmtId(oprinfo->dobj.namespace->dobj.name),
13133 : oprinfo->dobj.name, details->data);
13134 :
13135 4970 : if (dopt->binary_upgrade)
13136 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13137 24 : "OPERATOR", oprid->data,
13138 24 : oprinfo->dobj.namespace->dobj.name);
13139 :
13140 4970 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13141 4970 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13142 4970 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13143 : .namespace = oprinfo->dobj.namespace->dobj.name,
13144 : .owner = oprinfo->rolname,
13145 : .description = "OPERATOR",
13146 : .section = SECTION_PRE_DATA,
13147 : .createStmt = q->data,
13148 : .dropStmt = delq->data));
13149 :
13150 : /* Dump Operator Comments */
13151 4970 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13152 4794 : dumpComment(fout, "OPERATOR", oprid->data,
13153 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13154 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13155 :
13156 4970 : PQclear(res);
13157 :
13158 4970 : destroyPQExpBuffer(query);
13159 4970 : destroyPQExpBuffer(q);
13160 4970 : destroyPQExpBuffer(delq);
13161 4970 : destroyPQExpBuffer(oprid);
13162 4970 : destroyPQExpBuffer(details);
13163 : }
13164 :
13165 : /*
13166 : * Convert a function reference obtained from pg_operator
13167 : *
13168 : * Returns allocated string of what to print, or NULL if function references
13169 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13170 : *
13171 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13172 : * part.
13173 : */
13174 : static char *
13175 14910 : convertRegProcReference(const char *proc)
13176 : {
13177 : char *name;
13178 : char *paren;
13179 : bool inquote;
13180 :
13181 : /* In all cases "-" means a null reference */
13182 14910 : if (strcmp(proc, "-") == 0)
13183 3884 : return NULL;
13184 :
13185 11026 : name = pg_strdup(proc);
13186 : /* find non-double-quoted left paren */
13187 11026 : inquote = false;
13188 132878 : for (paren = name; *paren; paren++)
13189 : {
13190 132878 : if (*paren == '(' && !inquote)
13191 : {
13192 11026 : *paren = '\0';
13193 11026 : break;
13194 : }
13195 121852 : if (*paren == '"')
13196 100 : inquote = !inquote;
13197 : }
13198 11026 : return name;
13199 : }
13200 :
13201 : /*
13202 : * getFormattedOperatorName - retrieve the operator name for the
13203 : * given operator OID (presented in string form).
13204 : *
13205 : * Returns an allocated string, or NULL if the given OID is invalid.
13206 : * Caller is responsible for free'ing result string.
13207 : *
13208 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
13209 : * useful in commands where the operator's argument types can be inferred from
13210 : * context. We always schema-qualify the name, though. The predecessor to
13211 : * this code tried to skip the schema qualification if possible, but that led
13212 : * to wrong results in corner cases, such as if an operator and its negator
13213 : * are in different schemas.
13214 : */
13215 : static char *
13216 10512 : getFormattedOperatorName(const char *oproid)
13217 : {
13218 : OprInfo *oprInfo;
13219 :
13220 : /* In all cases "0" means a null reference */
13221 10512 : if (strcmp(oproid, "0") == 0)
13222 4864 : return NULL;
13223 :
13224 5648 : oprInfo = findOprByOid(atooid(oproid));
13225 5648 : if (oprInfo == NULL)
13226 : {
13227 0 : pg_log_warning("could not find operator with OID %s",
13228 : oproid);
13229 0 : return NULL;
13230 : }
13231 :
13232 5648 : return psprintf("OPERATOR(%s.%s)",
13233 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
13234 : oprInfo->dobj.name);
13235 : }
13236 :
13237 : /*
13238 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13239 : *
13240 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13241 : * argument lists of these functions are predetermined. Note that the
13242 : * caller should ensure we are in the proper schema, because the results
13243 : * are search path dependent!
13244 : */
13245 : static char *
13246 420 : convertTSFunction(Archive *fout, Oid funcOid)
13247 : {
13248 : char *result;
13249 : char query[128];
13250 : PGresult *res;
13251 :
13252 420 : snprintf(query, sizeof(query),
13253 : "SELECT '%u'::pg_catalog.regproc", funcOid);
13254 420 : res = ExecuteSqlQueryForSingleRow(fout, query);
13255 :
13256 420 : result = pg_strdup(PQgetvalue(res, 0, 0));
13257 :
13258 420 : PQclear(res);
13259 :
13260 420 : return result;
13261 : }
13262 :
13263 : /*
13264 : * dumpAccessMethod
13265 : * write out a single access method definition
13266 : */
13267 : static void
13268 152 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13269 : {
13270 152 : DumpOptions *dopt = fout->dopt;
13271 : PQExpBuffer q;
13272 : PQExpBuffer delq;
13273 : char *qamname;
13274 :
13275 : /* Do nothing if not dumping schema */
13276 152 : if (!dopt->dumpSchema)
13277 12 : return;
13278 :
13279 140 : q = createPQExpBuffer();
13280 140 : delq = createPQExpBuffer();
13281 :
13282 140 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
13283 :
13284 140 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13285 :
13286 140 : switch (aminfo->amtype)
13287 : {
13288 66 : case AMTYPE_INDEX:
13289 66 : appendPQExpBufferStr(q, "TYPE INDEX ");
13290 66 : break;
13291 74 : case AMTYPE_TABLE:
13292 74 : appendPQExpBufferStr(q, "TYPE TABLE ");
13293 74 : break;
13294 0 : default:
13295 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13296 : aminfo->amtype, qamname);
13297 0 : destroyPQExpBuffer(q);
13298 0 : destroyPQExpBuffer(delq);
13299 0 : free(qamname);
13300 0 : return;
13301 : }
13302 :
13303 140 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13304 :
13305 140 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13306 : qamname);
13307 :
13308 140 : if (dopt->binary_upgrade)
13309 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
13310 : "ACCESS METHOD", qamname, NULL);
13311 :
13312 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13313 140 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13314 140 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13315 : .description = "ACCESS METHOD",
13316 : .section = SECTION_PRE_DATA,
13317 : .createStmt = q->data,
13318 : .dropStmt = delq->data));
13319 :
13320 : /* Dump Access Method Comments */
13321 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13322 0 : dumpComment(fout, "ACCESS METHOD", qamname,
13323 : NULL, "",
13324 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13325 :
13326 140 : destroyPQExpBuffer(q);
13327 140 : destroyPQExpBuffer(delq);
13328 140 : free(qamname);
13329 : }
13330 :
13331 : /*
13332 : * dumpOpclass
13333 : * write out a single operator class definition
13334 : */
13335 : static void
13336 1308 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13337 : {
13338 1308 : DumpOptions *dopt = fout->dopt;
13339 : PQExpBuffer query;
13340 : PQExpBuffer q;
13341 : PQExpBuffer delq;
13342 : PQExpBuffer nameusing;
13343 : PGresult *res;
13344 : int ntups;
13345 : int i_opcintype;
13346 : int i_opckeytype;
13347 : int i_opcdefault;
13348 : int i_opcfamily;
13349 : int i_opcfamilyname;
13350 : int i_opcfamilynsp;
13351 : int i_amname;
13352 : int i_amopstrategy;
13353 : int i_amopopr;
13354 : int i_sortfamily;
13355 : int i_sortfamilynsp;
13356 : int i_amprocnum;
13357 : int i_amproc;
13358 : int i_amproclefttype;
13359 : int i_amprocrighttype;
13360 : char *opcintype;
13361 : char *opckeytype;
13362 : char *opcdefault;
13363 : char *opcfamily;
13364 : char *opcfamilyname;
13365 : char *opcfamilynsp;
13366 : char *amname;
13367 : char *amopstrategy;
13368 : char *amopopr;
13369 : char *sortfamily;
13370 : char *sortfamilynsp;
13371 : char *amprocnum;
13372 : char *amproc;
13373 : char *amproclefttype;
13374 : char *amprocrighttype;
13375 : bool needComma;
13376 : int i;
13377 :
13378 : /* Do nothing if not dumping schema */
13379 1308 : if (!dopt->dumpSchema)
13380 18 : return;
13381 :
13382 1290 : query = createPQExpBuffer();
13383 1290 : q = createPQExpBuffer();
13384 1290 : delq = createPQExpBuffer();
13385 1290 : nameusing = createPQExpBuffer();
13386 :
13387 : /* Get additional fields from the pg_opclass row */
13388 1290 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13389 : "opckeytype::pg_catalog.regtype, "
13390 : "opcdefault, opcfamily, "
13391 : "opfname AS opcfamilyname, "
13392 : "nspname AS opcfamilynsp, "
13393 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13394 : "FROM pg_catalog.pg_opclass c "
13395 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13396 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13397 : "WHERE c.oid = '%u'::pg_catalog.oid",
13398 : opcinfo->dobj.catId.oid);
13399 :
13400 1290 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13401 :
13402 1290 : i_opcintype = PQfnumber(res, "opcintype");
13403 1290 : i_opckeytype = PQfnumber(res, "opckeytype");
13404 1290 : i_opcdefault = PQfnumber(res, "opcdefault");
13405 1290 : i_opcfamily = PQfnumber(res, "opcfamily");
13406 1290 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13407 1290 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13408 1290 : i_amname = PQfnumber(res, "amname");
13409 :
13410 : /* opcintype may still be needed after we PQclear res */
13411 1290 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13412 1290 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
13413 1290 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
13414 : /* opcfamily will still be needed after we PQclear res */
13415 1290 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13416 1290 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13417 1290 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13418 : /* amname will still be needed after we PQclear res */
13419 1290 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13420 :
13421 1290 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13422 1290 : fmtQualifiedDumpable(opcinfo));
13423 1290 : appendPQExpBuffer(delq, " USING %s;\n",
13424 : fmtId(amname));
13425 :
13426 : /* Build the fixed portion of the CREATE command */
13427 1290 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13428 1290 : fmtQualifiedDumpable(opcinfo));
13429 1290 : if (strcmp(opcdefault, "t") == 0)
13430 714 : appendPQExpBufferStr(q, "DEFAULT ");
13431 1290 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13432 : opcintype,
13433 : fmtId(amname));
13434 1290 : if (strlen(opcfamilyname) > 0)
13435 : {
13436 1290 : appendPQExpBufferStr(q, " FAMILY ");
13437 1290 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13438 1290 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
13439 : }
13440 1290 : appendPQExpBufferStr(q, " AS\n ");
13441 :
13442 1290 : needComma = false;
13443 :
13444 1290 : if (strcmp(opckeytype, "-") != 0)
13445 : {
13446 504 : appendPQExpBuffer(q, "STORAGE %s",
13447 : opckeytype);
13448 504 : needComma = true;
13449 : }
13450 :
13451 1290 : PQclear(res);
13452 :
13453 : /*
13454 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13455 : *
13456 : * Print only those opfamily members that are tied to the opclass by
13457 : * pg_depend entries.
13458 : */
13459 1290 : resetPQExpBuffer(query);
13460 1290 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13461 : "amopopr::pg_catalog.regoperator, "
13462 : "opfname AS sortfamily, "
13463 : "nspname AS sortfamilynsp "
13464 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13465 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13466 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13467 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13468 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13469 : "AND refobjid = '%u'::pg_catalog.oid "
13470 : "AND amopfamily = '%s'::pg_catalog.oid "
13471 : "ORDER BY amopstrategy",
13472 : opcinfo->dobj.catId.oid,
13473 : opcfamily);
13474 :
13475 1290 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13476 :
13477 1290 : ntups = PQntuples(res);
13478 :
13479 1290 : i_amopstrategy = PQfnumber(res, "amopstrategy");
13480 1290 : i_amopopr = PQfnumber(res, "amopopr");
13481 1290 : i_sortfamily = PQfnumber(res, "sortfamily");
13482 1290 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13483 :
13484 1706 : for (i = 0; i < ntups; i++)
13485 : {
13486 416 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13487 416 : amopopr = PQgetvalue(res, i, i_amopopr);
13488 416 : sortfamily = PQgetvalue(res, i, i_sortfamily);
13489 416 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13490 :
13491 416 : if (needComma)
13492 264 : appendPQExpBufferStr(q, " ,\n ");
13493 :
13494 416 : appendPQExpBuffer(q, "OPERATOR %s %s",
13495 : amopstrategy, amopopr);
13496 :
13497 416 : if (strlen(sortfamily) > 0)
13498 : {
13499 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13500 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13501 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13502 : }
13503 :
13504 416 : needComma = true;
13505 : }
13506 :
13507 1290 : PQclear(res);
13508 :
13509 : /*
13510 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13511 : *
13512 : * Print only those opfamily members that are tied to the opclass by
13513 : * pg_depend entries.
13514 : *
13515 : * We print the amproclefttype/amprocrighttype even though in most cases
13516 : * the backend could deduce the right values, because of the corner case
13517 : * of a btree sort support function for a cross-type comparison.
13518 : */
13519 1290 : resetPQExpBuffer(query);
13520 :
13521 1290 : appendPQExpBuffer(query, "SELECT amprocnum, "
13522 : "amproc::pg_catalog.regprocedure, "
13523 : "amproclefttype::pg_catalog.regtype, "
13524 : "amprocrighttype::pg_catalog.regtype "
13525 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13526 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13527 : "AND refobjid = '%u'::pg_catalog.oid "
13528 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13529 : "AND objid = ap.oid "
13530 : "ORDER BY amprocnum",
13531 : opcinfo->dobj.catId.oid);
13532 :
13533 1290 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13534 :
13535 1290 : ntups = PQntuples(res);
13536 :
13537 1290 : i_amprocnum = PQfnumber(res, "amprocnum");
13538 1290 : i_amproc = PQfnumber(res, "amproc");
13539 1290 : i_amproclefttype = PQfnumber(res, "amproclefttype");
13540 1290 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13541 :
13542 1356 : for (i = 0; i < ntups; i++)
13543 : {
13544 66 : amprocnum = PQgetvalue(res, i, i_amprocnum);
13545 66 : amproc = PQgetvalue(res, i, i_amproc);
13546 66 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13547 66 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13548 :
13549 66 : if (needComma)
13550 66 : appendPQExpBufferStr(q, " ,\n ");
13551 :
13552 66 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13553 :
13554 66 : if (*amproclefttype && *amprocrighttype)
13555 66 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13556 :
13557 66 : appendPQExpBuffer(q, " %s", amproc);
13558 :
13559 66 : needComma = true;
13560 : }
13561 :
13562 1290 : PQclear(res);
13563 :
13564 : /*
13565 : * If needComma is still false it means we haven't added anything after
13566 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13567 : * clause with the same datatype. This isn't sanctioned by the
13568 : * documentation, but actually DefineOpClass will treat it as a no-op.
13569 : */
13570 1290 : if (!needComma)
13571 634 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
13572 :
13573 1290 : appendPQExpBufferStr(q, ";\n");
13574 :
13575 1290 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13576 1290 : appendPQExpBuffer(nameusing, " USING %s",
13577 : fmtId(amname));
13578 :
13579 1290 : if (dopt->binary_upgrade)
13580 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
13581 12 : "OPERATOR CLASS", nameusing->data,
13582 12 : opcinfo->dobj.namespace->dobj.name);
13583 :
13584 1290 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13585 1290 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13586 1290 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13587 : .namespace = opcinfo->dobj.namespace->dobj.name,
13588 : .owner = opcinfo->rolname,
13589 : .description = "OPERATOR CLASS",
13590 : .section = SECTION_PRE_DATA,
13591 : .createStmt = q->data,
13592 : .dropStmt = delq->data));
13593 :
13594 : /* Dump Operator Class Comments */
13595 1290 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13596 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13597 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13598 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13599 :
13600 1290 : free(opcintype);
13601 1290 : free(opcfamily);
13602 1290 : free(amname);
13603 1290 : destroyPQExpBuffer(query);
13604 1290 : destroyPQExpBuffer(q);
13605 1290 : destroyPQExpBuffer(delq);
13606 1290 : destroyPQExpBuffer(nameusing);
13607 : }
13608 :
13609 : /*
13610 : * dumpOpfamily
13611 : * write out a single operator family definition
13612 : *
13613 : * Note: this also dumps any "loose" operator members that aren't bound to a
13614 : * specific opclass within the opfamily.
13615 : */
13616 : static void
13617 1090 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13618 : {
13619 1090 : DumpOptions *dopt = fout->dopt;
13620 : PQExpBuffer query;
13621 : PQExpBuffer q;
13622 : PQExpBuffer delq;
13623 : PQExpBuffer nameusing;
13624 : PGresult *res;
13625 : PGresult *res_ops;
13626 : PGresult *res_procs;
13627 : int ntups;
13628 : int i_amname;
13629 : int i_amopstrategy;
13630 : int i_amopopr;
13631 : int i_sortfamily;
13632 : int i_sortfamilynsp;
13633 : int i_amprocnum;
13634 : int i_amproc;
13635 : int i_amproclefttype;
13636 : int i_amprocrighttype;
13637 : char *amname;
13638 : char *amopstrategy;
13639 : char *amopopr;
13640 : char *sortfamily;
13641 : char *sortfamilynsp;
13642 : char *amprocnum;
13643 : char *amproc;
13644 : char *amproclefttype;
13645 : char *amprocrighttype;
13646 : bool needComma;
13647 : int i;
13648 :
13649 : /* Do nothing if not dumping schema */
13650 1090 : if (!dopt->dumpSchema)
13651 12 : return;
13652 :
13653 1078 : query = createPQExpBuffer();
13654 1078 : q = createPQExpBuffer();
13655 1078 : delq = createPQExpBuffer();
13656 1078 : nameusing = createPQExpBuffer();
13657 :
13658 : /*
13659 : * Fetch only those opfamily members that are tied directly to the
13660 : * opfamily by pg_depend entries.
13661 : */
13662 1078 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13663 : "amopopr::pg_catalog.regoperator, "
13664 : "opfname AS sortfamily, "
13665 : "nspname AS sortfamilynsp "
13666 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13667 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13668 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13669 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13670 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13671 : "AND refobjid = '%u'::pg_catalog.oid "
13672 : "AND amopfamily = '%u'::pg_catalog.oid "
13673 : "ORDER BY amopstrategy",
13674 : opfinfo->dobj.catId.oid,
13675 : opfinfo->dobj.catId.oid);
13676 :
13677 1078 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13678 :
13679 1078 : resetPQExpBuffer(query);
13680 :
13681 1078 : appendPQExpBuffer(query, "SELECT amprocnum, "
13682 : "amproc::pg_catalog.regprocedure, "
13683 : "amproclefttype::pg_catalog.regtype, "
13684 : "amprocrighttype::pg_catalog.regtype "
13685 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13686 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13687 : "AND refobjid = '%u'::pg_catalog.oid "
13688 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13689 : "AND objid = ap.oid "
13690 : "ORDER BY amprocnum",
13691 : opfinfo->dobj.catId.oid);
13692 :
13693 1078 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13694 :
13695 : /* Get additional fields from the pg_opfamily row */
13696 1078 : resetPQExpBuffer(query);
13697 :
13698 1078 : appendPQExpBuffer(query, "SELECT "
13699 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13700 : "FROM pg_catalog.pg_opfamily "
13701 : "WHERE oid = '%u'::pg_catalog.oid",
13702 : opfinfo->dobj.catId.oid);
13703 :
13704 1078 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13705 :
13706 1078 : i_amname = PQfnumber(res, "amname");
13707 :
13708 : /* amname will still be needed after we PQclear res */
13709 1078 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13710 :
13711 1078 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13712 1078 : fmtQualifiedDumpable(opfinfo));
13713 1078 : appendPQExpBuffer(delq, " USING %s;\n",
13714 : fmtId(amname));
13715 :
13716 : /* Build the fixed portion of the CREATE command */
13717 1078 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13718 1078 : fmtQualifiedDumpable(opfinfo));
13719 1078 : appendPQExpBuffer(q, " USING %s;\n",
13720 : fmtId(amname));
13721 :
13722 1078 : PQclear(res);
13723 :
13724 : /* Do we need an ALTER to add loose members? */
13725 1078 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13726 : {
13727 96 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13728 96 : fmtQualifiedDumpable(opfinfo));
13729 96 : appendPQExpBuffer(q, " USING %s ADD\n ",
13730 : fmtId(amname));
13731 :
13732 96 : needComma = false;
13733 :
13734 : /*
13735 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13736 : */
13737 96 : ntups = PQntuples(res_ops);
13738 :
13739 96 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13740 96 : i_amopopr = PQfnumber(res_ops, "amopopr");
13741 96 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
13742 96 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13743 :
13744 426 : for (i = 0; i < ntups; i++)
13745 : {
13746 330 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13747 330 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
13748 330 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13749 330 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13750 :
13751 330 : if (needComma)
13752 264 : appendPQExpBufferStr(q, " ,\n ");
13753 :
13754 330 : appendPQExpBuffer(q, "OPERATOR %s %s",
13755 : amopstrategy, amopopr);
13756 :
13757 330 : if (strlen(sortfamily) > 0)
13758 : {
13759 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13760 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13761 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13762 : }
13763 :
13764 330 : needComma = true;
13765 : }
13766 :
13767 : /*
13768 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13769 : */
13770 96 : ntups = PQntuples(res_procs);
13771 :
13772 96 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
13773 96 : i_amproc = PQfnumber(res_procs, "amproc");
13774 96 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13775 96 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13776 :
13777 456 : for (i = 0; i < ntups; i++)
13778 : {
13779 360 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13780 360 : amproc = PQgetvalue(res_procs, i, i_amproc);
13781 360 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13782 360 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13783 :
13784 360 : if (needComma)
13785 330 : appendPQExpBufferStr(q, " ,\n ");
13786 :
13787 360 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13788 : amprocnum, amproclefttype, amprocrighttype,
13789 : amproc);
13790 :
13791 360 : needComma = true;
13792 : }
13793 :
13794 96 : appendPQExpBufferStr(q, ";\n");
13795 : }
13796 :
13797 1078 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13798 1078 : appendPQExpBuffer(nameusing, " USING %s",
13799 : fmtId(amname));
13800 :
13801 1078 : if (dopt->binary_upgrade)
13802 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
13803 18 : "OPERATOR FAMILY", nameusing->data,
13804 18 : opfinfo->dobj.namespace->dobj.name);
13805 :
13806 1078 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13807 1078 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13808 1078 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13809 : .namespace = opfinfo->dobj.namespace->dobj.name,
13810 : .owner = opfinfo->rolname,
13811 : .description = "OPERATOR FAMILY",
13812 : .section = SECTION_PRE_DATA,
13813 : .createStmt = q->data,
13814 : .dropStmt = delq->data));
13815 :
13816 : /* Dump Operator Family Comments */
13817 1078 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13818 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13819 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13820 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13821 :
13822 1078 : free(amname);
13823 1078 : PQclear(res_ops);
13824 1078 : PQclear(res_procs);
13825 1078 : destroyPQExpBuffer(query);
13826 1078 : destroyPQExpBuffer(q);
13827 1078 : destroyPQExpBuffer(delq);
13828 1078 : destroyPQExpBuffer(nameusing);
13829 : }
13830 :
13831 : /*
13832 : * dumpCollation
13833 : * write out a single collation definition
13834 : */
13835 : static void
13836 4928 : dumpCollation(Archive *fout, const CollInfo *collinfo)
13837 : {
13838 4928 : DumpOptions *dopt = fout->dopt;
13839 : PQExpBuffer query;
13840 : PQExpBuffer q;
13841 : PQExpBuffer delq;
13842 : char *qcollname;
13843 : PGresult *res;
13844 : int i_collprovider;
13845 : int i_collisdeterministic;
13846 : int i_collcollate;
13847 : int i_collctype;
13848 : int i_colllocale;
13849 : int i_collicurules;
13850 : const char *collprovider;
13851 : const char *collcollate;
13852 : const char *collctype;
13853 : const char *colllocale;
13854 : const char *collicurules;
13855 :
13856 : /* Do nothing if not dumping schema */
13857 4928 : if (!dopt->dumpSchema)
13858 12 : return;
13859 :
13860 4916 : query = createPQExpBuffer();
13861 4916 : q = createPQExpBuffer();
13862 4916 : delq = createPQExpBuffer();
13863 :
13864 4916 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13865 :
13866 : /* Get collation-specific details */
13867 4916 : appendPQExpBufferStr(query, "SELECT ");
13868 :
13869 4916 : if (fout->remoteVersion >= 100000)
13870 4916 : appendPQExpBufferStr(query,
13871 : "collprovider, "
13872 : "collversion, ");
13873 : else
13874 0 : appendPQExpBufferStr(query,
13875 : "'c' AS collprovider, "
13876 : "NULL AS collversion, ");
13877 :
13878 4916 : if (fout->remoteVersion >= 120000)
13879 4916 : appendPQExpBufferStr(query,
13880 : "collisdeterministic, ");
13881 : else
13882 0 : appendPQExpBufferStr(query,
13883 : "true AS collisdeterministic, ");
13884 :
13885 4916 : if (fout->remoteVersion >= 170000)
13886 4916 : appendPQExpBufferStr(query,
13887 : "colllocale, ");
13888 0 : else if (fout->remoteVersion >= 150000)
13889 0 : appendPQExpBufferStr(query,
13890 : "colliculocale AS colllocale, ");
13891 : else
13892 0 : appendPQExpBufferStr(query,
13893 : "NULL AS colllocale, ");
13894 :
13895 4916 : if (fout->remoteVersion >= 160000)
13896 4916 : appendPQExpBufferStr(query,
13897 : "collicurules, ");
13898 : else
13899 0 : appendPQExpBufferStr(query,
13900 : "NULL AS collicurules, ");
13901 :
13902 4916 : appendPQExpBuffer(query,
13903 : "collcollate, "
13904 : "collctype "
13905 : "FROM pg_catalog.pg_collation c "
13906 : "WHERE c.oid = '%u'::pg_catalog.oid",
13907 : collinfo->dobj.catId.oid);
13908 :
13909 4916 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13910 :
13911 4916 : i_collprovider = PQfnumber(res, "collprovider");
13912 4916 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13913 4916 : i_collcollate = PQfnumber(res, "collcollate");
13914 4916 : i_collctype = PQfnumber(res, "collctype");
13915 4916 : i_colllocale = PQfnumber(res, "colllocale");
13916 4916 : i_collicurules = PQfnumber(res, "collicurules");
13917 :
13918 4916 : collprovider = PQgetvalue(res, 0, i_collprovider);
13919 :
13920 4916 : if (!PQgetisnull(res, 0, i_collcollate))
13921 94 : collcollate = PQgetvalue(res, 0, i_collcollate);
13922 : else
13923 4822 : collcollate = NULL;
13924 :
13925 4916 : if (!PQgetisnull(res, 0, i_collctype))
13926 94 : collctype = PQgetvalue(res, 0, i_collctype);
13927 : else
13928 4822 : collctype = NULL;
13929 :
13930 : /*
13931 : * Before version 15, collcollate and collctype were of type NAME and
13932 : * non-nullable. Treat empty strings as NULL for consistency.
13933 : */
13934 4916 : if (fout->remoteVersion < 150000)
13935 : {
13936 0 : if (collcollate[0] == '\0')
13937 0 : collcollate = NULL;
13938 0 : if (collctype[0] == '\0')
13939 0 : collctype = NULL;
13940 : }
13941 :
13942 4916 : if (!PQgetisnull(res, 0, i_colllocale))
13943 4816 : colllocale = PQgetvalue(res, 0, i_colllocale);
13944 : else
13945 100 : colllocale = NULL;
13946 :
13947 4916 : if (!PQgetisnull(res, 0, i_collicurules))
13948 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
13949 : else
13950 4916 : collicurules = NULL;
13951 :
13952 4916 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13953 4916 : fmtQualifiedDumpable(collinfo));
13954 :
13955 4916 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
13956 4916 : fmtQualifiedDumpable(collinfo));
13957 :
13958 4916 : appendPQExpBufferStr(q, "provider = ");
13959 4916 : if (collprovider[0] == 'b')
13960 38 : appendPQExpBufferStr(q, "builtin");
13961 4878 : else if (collprovider[0] == 'c')
13962 94 : appendPQExpBufferStr(q, "libc");
13963 4784 : else if (collprovider[0] == 'i')
13964 4778 : appendPQExpBufferStr(q, "icu");
13965 6 : else if (collprovider[0] == 'd')
13966 : /* to allow dumping pg_catalog; not accepted on input */
13967 6 : appendPQExpBufferStr(q, "default");
13968 : else
13969 0 : pg_fatal("unrecognized collation provider: %s",
13970 : collprovider);
13971 :
13972 4916 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
13973 0 : appendPQExpBufferStr(q, ", deterministic = false");
13974 :
13975 4916 : if (collprovider[0] == 'd')
13976 : {
13977 6 : if (collcollate || collctype || colllocale || collicurules)
13978 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13979 :
13980 : /* no locale -- the default collation cannot be reloaded anyway */
13981 : }
13982 4910 : else if (collprovider[0] == 'b')
13983 : {
13984 38 : if (collcollate || collctype || !colllocale || collicurules)
13985 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13986 :
13987 38 : appendPQExpBufferStr(q, ", locale = ");
13988 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
13989 : fout);
13990 : }
13991 4872 : else if (collprovider[0] == 'i')
13992 : {
13993 4778 : if (fout->remoteVersion >= 150000)
13994 : {
13995 4778 : if (collcollate || collctype || !colllocale)
13996 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13997 :
13998 4778 : appendPQExpBufferStr(q, ", locale = ");
13999 4778 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14000 : fout);
14001 : }
14002 : else
14003 : {
14004 0 : if (!collcollate || !collctype || colllocale ||
14005 0 : strcmp(collcollate, collctype) != 0)
14006 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14007 :
14008 0 : appendPQExpBufferStr(q, ", locale = ");
14009 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14010 : }
14011 :
14012 4778 : if (collicurules)
14013 : {
14014 0 : appendPQExpBufferStr(q, ", rules = ");
14015 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14016 : }
14017 : }
14018 94 : else if (collprovider[0] == 'c')
14019 : {
14020 94 : if (colllocale || collicurules || !collcollate || !collctype)
14021 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14022 :
14023 94 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14024 : {
14025 94 : appendPQExpBufferStr(q, ", locale = ");
14026 94 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14027 : }
14028 : else
14029 : {
14030 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14031 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14032 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14033 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14034 : }
14035 : }
14036 : else
14037 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14038 :
14039 : /*
14040 : * For binary upgrade, carry over the collation version. For normal
14041 : * dump/restore, omit the version, so that it is computed upon restore.
14042 : */
14043 4916 : if (dopt->binary_upgrade)
14044 : {
14045 : int i_collversion;
14046 :
14047 10 : i_collversion = PQfnumber(res, "collversion");
14048 10 : if (!PQgetisnull(res, 0, i_collversion))
14049 : {
14050 8 : appendPQExpBufferStr(q, ", version = ");
14051 8 : appendStringLiteralAH(q,
14052 : PQgetvalue(res, 0, i_collversion),
14053 : fout);
14054 : }
14055 : }
14056 :
14057 4916 : appendPQExpBufferStr(q, ");\n");
14058 :
14059 4916 : if (dopt->binary_upgrade)
14060 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
14061 : "COLLATION", qcollname,
14062 10 : collinfo->dobj.namespace->dobj.name);
14063 :
14064 4916 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14065 4916 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14066 4916 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14067 : .namespace = collinfo->dobj.namespace->dobj.name,
14068 : .owner = collinfo->rolname,
14069 : .description = "COLLATION",
14070 : .section = SECTION_PRE_DATA,
14071 : .createStmt = q->data,
14072 : .dropStmt = delq->data));
14073 :
14074 : /* Dump Collation Comments */
14075 4916 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14076 4738 : dumpComment(fout, "COLLATION", qcollname,
14077 4738 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14078 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14079 :
14080 4916 : PQclear(res);
14081 :
14082 4916 : destroyPQExpBuffer(query);
14083 4916 : destroyPQExpBuffer(q);
14084 4916 : destroyPQExpBuffer(delq);
14085 4916 : free(qcollname);
14086 : }
14087 :
14088 : /*
14089 : * dumpConversion
14090 : * write out a single conversion definition
14091 : */
14092 : static void
14093 840 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14094 : {
14095 840 : DumpOptions *dopt = fout->dopt;
14096 : PQExpBuffer query;
14097 : PQExpBuffer q;
14098 : PQExpBuffer delq;
14099 : char *qconvname;
14100 : PGresult *res;
14101 : int i_conforencoding;
14102 : int i_contoencoding;
14103 : int i_conproc;
14104 : int i_condefault;
14105 : const char *conforencoding;
14106 : const char *contoencoding;
14107 : const char *conproc;
14108 : bool condefault;
14109 :
14110 : /* Do nothing if not dumping schema */
14111 840 : if (!dopt->dumpSchema)
14112 6 : return;
14113 :
14114 834 : query = createPQExpBuffer();
14115 834 : q = createPQExpBuffer();
14116 834 : delq = createPQExpBuffer();
14117 :
14118 834 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14119 :
14120 : /* Get conversion-specific details */
14121 834 : appendPQExpBuffer(query, "SELECT "
14122 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14123 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14124 : "conproc, condefault "
14125 : "FROM pg_catalog.pg_conversion c "
14126 : "WHERE c.oid = '%u'::pg_catalog.oid",
14127 : convinfo->dobj.catId.oid);
14128 :
14129 834 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14130 :
14131 834 : i_conforencoding = PQfnumber(res, "conforencoding");
14132 834 : i_contoencoding = PQfnumber(res, "contoencoding");
14133 834 : i_conproc = PQfnumber(res, "conproc");
14134 834 : i_condefault = PQfnumber(res, "condefault");
14135 :
14136 834 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14137 834 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14138 834 : conproc = PQgetvalue(res, 0, i_conproc);
14139 834 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14140 :
14141 834 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14142 834 : fmtQualifiedDumpable(convinfo));
14143 :
14144 834 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14145 : (condefault) ? "DEFAULT " : "",
14146 834 : fmtQualifiedDumpable(convinfo));
14147 834 : appendStringLiteralAH(q, conforencoding, fout);
14148 834 : appendPQExpBufferStr(q, " TO ");
14149 834 : appendStringLiteralAH(q, contoencoding, fout);
14150 : /* regproc output is already sufficiently quoted */
14151 834 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14152 :
14153 834 : if (dopt->binary_upgrade)
14154 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14155 : "CONVERSION", qconvname,
14156 2 : convinfo->dobj.namespace->dobj.name);
14157 :
14158 834 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14159 834 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14160 834 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14161 : .namespace = convinfo->dobj.namespace->dobj.name,
14162 : .owner = convinfo->rolname,
14163 : .description = "CONVERSION",
14164 : .section = SECTION_PRE_DATA,
14165 : .createStmt = q->data,
14166 : .dropStmt = delq->data));
14167 :
14168 : /* Dump Conversion Comments */
14169 834 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14170 834 : dumpComment(fout, "CONVERSION", qconvname,
14171 834 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14172 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14173 :
14174 834 : PQclear(res);
14175 :
14176 834 : destroyPQExpBuffer(query);
14177 834 : destroyPQExpBuffer(q);
14178 834 : destroyPQExpBuffer(delq);
14179 834 : free(qconvname);
14180 : }
14181 :
14182 : /*
14183 : * format_aggregate_signature: generate aggregate name and argument list
14184 : *
14185 : * The argument type names are qualified if needed. The aggregate name
14186 : * is never qualified.
14187 : */
14188 : static char *
14189 572 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14190 : {
14191 : PQExpBufferData buf;
14192 : int j;
14193 :
14194 572 : initPQExpBuffer(&buf);
14195 572 : if (honor_quotes)
14196 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14197 : else
14198 572 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14199 :
14200 572 : if (agginfo->aggfn.nargs == 0)
14201 80 : appendPQExpBufferStr(&buf, "(*)");
14202 : else
14203 : {
14204 492 : appendPQExpBufferChar(&buf, '(');
14205 1074 : for (j = 0; j < agginfo->aggfn.nargs; j++)
14206 582 : appendPQExpBuffer(&buf, "%s%s",
14207 : (j > 0) ? ", " : "",
14208 : getFormattedTypeName(fout,
14209 582 : agginfo->aggfn.argtypes[j],
14210 : zeroIsError));
14211 492 : appendPQExpBufferChar(&buf, ')');
14212 : }
14213 572 : return buf.data;
14214 : }
14215 :
14216 : /*
14217 : * dumpAgg
14218 : * write out a single aggregate definition
14219 : */
14220 : static void
14221 580 : dumpAgg(Archive *fout, const AggInfo *agginfo)
14222 : {
14223 580 : DumpOptions *dopt = fout->dopt;
14224 : PQExpBuffer query;
14225 : PQExpBuffer q;
14226 : PQExpBuffer delq;
14227 : PQExpBuffer details;
14228 : char *aggsig; /* identity signature */
14229 580 : char *aggfullsig = NULL; /* full signature */
14230 : char *aggsig_tag;
14231 : PGresult *res;
14232 : int i_agginitval;
14233 : int i_aggminitval;
14234 : const char *aggtransfn;
14235 : const char *aggfinalfn;
14236 : const char *aggcombinefn;
14237 : const char *aggserialfn;
14238 : const char *aggdeserialfn;
14239 : const char *aggmtransfn;
14240 : const char *aggminvtransfn;
14241 : const char *aggmfinalfn;
14242 : bool aggfinalextra;
14243 : bool aggmfinalextra;
14244 : char aggfinalmodify;
14245 : char aggmfinalmodify;
14246 : const char *aggsortop;
14247 : char *aggsortconvop;
14248 : char aggkind;
14249 : const char *aggtranstype;
14250 : const char *aggtransspace;
14251 : const char *aggmtranstype;
14252 : const char *aggmtransspace;
14253 : const char *agginitval;
14254 : const char *aggminitval;
14255 : const char *proparallel;
14256 : char defaultfinalmodify;
14257 :
14258 : /* Do nothing if not dumping schema */
14259 580 : if (!dopt->dumpSchema)
14260 8 : return;
14261 :
14262 572 : query = createPQExpBuffer();
14263 572 : q = createPQExpBuffer();
14264 572 : delq = createPQExpBuffer();
14265 572 : details = createPQExpBuffer();
14266 :
14267 572 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14268 : {
14269 : /* Set up query for aggregate-specific details */
14270 112 : appendPQExpBufferStr(query,
14271 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14272 :
14273 112 : appendPQExpBufferStr(query,
14274 : "SELECT "
14275 : "aggtransfn,\n"
14276 : "aggfinalfn,\n"
14277 : "aggtranstype::pg_catalog.regtype,\n"
14278 : "agginitval,\n"
14279 : "aggsortop,\n"
14280 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14281 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14282 :
14283 112 : if (fout->remoteVersion >= 90400)
14284 112 : appendPQExpBufferStr(query,
14285 : "aggkind,\n"
14286 : "aggmtransfn,\n"
14287 : "aggminvtransfn,\n"
14288 : "aggmfinalfn,\n"
14289 : "aggmtranstype::pg_catalog.regtype,\n"
14290 : "aggfinalextra,\n"
14291 : "aggmfinalextra,\n"
14292 : "aggtransspace,\n"
14293 : "aggmtransspace,\n"
14294 : "aggminitval,\n");
14295 : else
14296 0 : appendPQExpBufferStr(query,
14297 : "'n' AS aggkind,\n"
14298 : "'-' AS aggmtransfn,\n"
14299 : "'-' AS aggminvtransfn,\n"
14300 : "'-' AS aggmfinalfn,\n"
14301 : "0 AS aggmtranstype,\n"
14302 : "false AS aggfinalextra,\n"
14303 : "false AS aggmfinalextra,\n"
14304 : "0 AS aggtransspace,\n"
14305 : "0 AS aggmtransspace,\n"
14306 : "NULL AS aggminitval,\n");
14307 :
14308 112 : if (fout->remoteVersion >= 90600)
14309 112 : appendPQExpBufferStr(query,
14310 : "aggcombinefn,\n"
14311 : "aggserialfn,\n"
14312 : "aggdeserialfn,\n"
14313 : "proparallel,\n");
14314 : else
14315 0 : appendPQExpBufferStr(query,
14316 : "'-' AS aggcombinefn,\n"
14317 : "'-' AS aggserialfn,\n"
14318 : "'-' AS aggdeserialfn,\n"
14319 : "'u' AS proparallel,\n");
14320 :
14321 112 : if (fout->remoteVersion >= 110000)
14322 112 : appendPQExpBufferStr(query,
14323 : "aggfinalmodify,\n"
14324 : "aggmfinalmodify\n");
14325 : else
14326 0 : appendPQExpBufferStr(query,
14327 : "'0' AS aggfinalmodify,\n"
14328 : "'0' AS aggmfinalmodify\n");
14329 :
14330 112 : appendPQExpBufferStr(query,
14331 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14332 : "WHERE a.aggfnoid = p.oid "
14333 : "AND p.oid = $1");
14334 :
14335 112 : ExecuteSqlStatement(fout, query->data);
14336 :
14337 112 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14338 : }
14339 :
14340 572 : printfPQExpBuffer(query,
14341 : "EXECUTE dumpAgg('%u')",
14342 : agginfo->aggfn.dobj.catId.oid);
14343 :
14344 572 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14345 :
14346 572 : i_agginitval = PQfnumber(res, "agginitval");
14347 572 : i_aggminitval = PQfnumber(res, "aggminitval");
14348 :
14349 572 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14350 572 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14351 572 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14352 572 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14353 572 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14354 572 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14355 572 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14356 572 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14357 572 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14358 572 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14359 572 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14360 572 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14361 572 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14362 572 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14363 572 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14364 572 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14365 572 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14366 572 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14367 572 : agginitval = PQgetvalue(res, 0, i_agginitval);
14368 572 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
14369 572 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14370 :
14371 : {
14372 : char *funcargs;
14373 : char *funciargs;
14374 :
14375 572 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14376 572 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14377 572 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14378 572 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14379 : }
14380 :
14381 572 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14382 :
14383 : /* identify default modify flag for aggkind (must match DefineAggregate) */
14384 572 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14385 : /* replace omitted flags for old versions */
14386 572 : if (aggfinalmodify == '0')
14387 0 : aggfinalmodify = defaultfinalmodify;
14388 572 : if (aggmfinalmodify == '0')
14389 0 : aggmfinalmodify = defaultfinalmodify;
14390 :
14391 : /* regproc and regtype output is already sufficiently quoted */
14392 572 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14393 : aggtransfn, aggtranstype);
14394 :
14395 572 : if (strcmp(aggtransspace, "0") != 0)
14396 : {
14397 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
14398 : aggtransspace);
14399 : }
14400 :
14401 572 : if (!PQgetisnull(res, 0, i_agginitval))
14402 : {
14403 416 : appendPQExpBufferStr(details, ",\n INITCOND = ");
14404 416 : appendStringLiteralAH(details, agginitval, fout);
14405 : }
14406 :
14407 572 : if (strcmp(aggfinalfn, "-") != 0)
14408 : {
14409 266 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14410 : aggfinalfn);
14411 266 : if (aggfinalextra)
14412 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14413 266 : if (aggfinalmodify != defaultfinalmodify)
14414 : {
14415 66 : switch (aggfinalmodify)
14416 : {
14417 0 : case AGGMODIFY_READ_ONLY:
14418 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14419 0 : break;
14420 66 : case AGGMODIFY_SHAREABLE:
14421 66 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14422 66 : break;
14423 0 : case AGGMODIFY_READ_WRITE:
14424 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14425 0 : break;
14426 0 : default:
14427 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14428 : agginfo->aggfn.dobj.name);
14429 : break;
14430 : }
14431 506 : }
14432 : }
14433 :
14434 572 : if (strcmp(aggcombinefn, "-") != 0)
14435 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14436 :
14437 572 : if (strcmp(aggserialfn, "-") != 0)
14438 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14439 :
14440 572 : if (strcmp(aggdeserialfn, "-") != 0)
14441 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14442 :
14443 572 : if (strcmp(aggmtransfn, "-") != 0)
14444 : {
14445 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14446 : aggmtransfn,
14447 : aggminvtransfn,
14448 : aggmtranstype);
14449 : }
14450 :
14451 572 : if (strcmp(aggmtransspace, "0") != 0)
14452 : {
14453 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
14454 : aggmtransspace);
14455 : }
14456 :
14457 572 : if (!PQgetisnull(res, 0, i_aggminitval))
14458 : {
14459 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
14460 20 : appendStringLiteralAH(details, aggminitval, fout);
14461 : }
14462 :
14463 572 : if (strcmp(aggmfinalfn, "-") != 0)
14464 : {
14465 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14466 : aggmfinalfn);
14467 0 : if (aggmfinalextra)
14468 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14469 0 : if (aggmfinalmodify != defaultfinalmodify)
14470 : {
14471 0 : switch (aggmfinalmodify)
14472 : {
14473 0 : case AGGMODIFY_READ_ONLY:
14474 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14475 0 : break;
14476 0 : case AGGMODIFY_SHAREABLE:
14477 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14478 0 : break;
14479 0 : case AGGMODIFY_READ_WRITE:
14480 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14481 0 : break;
14482 0 : default:
14483 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14484 : agginfo->aggfn.dobj.name);
14485 : break;
14486 : }
14487 572 : }
14488 : }
14489 :
14490 572 : aggsortconvop = getFormattedOperatorName(aggsortop);
14491 572 : if (aggsortconvop)
14492 : {
14493 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
14494 : aggsortconvop);
14495 0 : free(aggsortconvop);
14496 : }
14497 :
14498 572 : if (aggkind == AGGKIND_HYPOTHETICAL)
14499 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14500 :
14501 572 : if (proparallel[0] != PROPARALLEL_UNSAFE)
14502 : {
14503 10 : if (proparallel[0] == PROPARALLEL_SAFE)
14504 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14505 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14506 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14507 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
14508 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
14509 : agginfo->aggfn.dobj.name);
14510 : }
14511 :
14512 572 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14513 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14514 : aggsig);
14515 :
14516 1144 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14517 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14518 : aggfullsig ? aggfullsig : aggsig, details->data);
14519 :
14520 572 : if (dopt->binary_upgrade)
14521 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14522 : "AGGREGATE", aggsig,
14523 98 : agginfo->aggfn.dobj.namespace->dobj.name);
14524 :
14525 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14526 538 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14527 : agginfo->aggfn.dobj.dumpId,
14528 538 : ARCHIVE_OPTS(.tag = aggsig_tag,
14529 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14530 : .owner = agginfo->aggfn.rolname,
14531 : .description = "AGGREGATE",
14532 : .section = SECTION_PRE_DATA,
14533 : .createStmt = q->data,
14534 : .dropStmt = delq->data));
14535 :
14536 : /* Dump Aggregate Comments */
14537 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14538 20 : dumpComment(fout, "AGGREGATE", aggsig,
14539 20 : agginfo->aggfn.dobj.namespace->dobj.name,
14540 : agginfo->aggfn.rolname,
14541 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14542 :
14543 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14544 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
14545 0 : agginfo->aggfn.dobj.namespace->dobj.name,
14546 : agginfo->aggfn.rolname,
14547 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14548 :
14549 : /*
14550 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14551 : * command look like a function's GRANT; in particular this affects the
14552 : * syntax for zero-argument aggregates and ordered-set aggregates.
14553 : */
14554 572 : free(aggsig);
14555 :
14556 572 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14557 :
14558 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14559 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14560 : "FUNCTION", aggsig, NULL,
14561 36 : agginfo->aggfn.dobj.namespace->dobj.name,
14562 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14563 :
14564 572 : free(aggsig);
14565 572 : free(aggfullsig);
14566 572 : free(aggsig_tag);
14567 :
14568 572 : PQclear(res);
14569 :
14570 572 : destroyPQExpBuffer(query);
14571 572 : destroyPQExpBuffer(q);
14572 572 : destroyPQExpBuffer(delq);
14573 572 : destroyPQExpBuffer(details);
14574 : }
14575 :
14576 : /*
14577 : * dumpTSParser
14578 : * write out a single text search parser
14579 : */
14580 : static void
14581 78 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14582 : {
14583 78 : DumpOptions *dopt = fout->dopt;
14584 : PQExpBuffer q;
14585 : PQExpBuffer delq;
14586 : char *qprsname;
14587 :
14588 : /* Do nothing if not dumping schema */
14589 78 : if (!dopt->dumpSchema)
14590 6 : return;
14591 :
14592 72 : q = createPQExpBuffer();
14593 72 : delq = createPQExpBuffer();
14594 :
14595 72 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14596 :
14597 72 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14598 72 : fmtQualifiedDumpable(prsinfo));
14599 :
14600 72 : appendPQExpBuffer(q, " START = %s,\n",
14601 : convertTSFunction(fout, prsinfo->prsstart));
14602 72 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14603 : convertTSFunction(fout, prsinfo->prstoken));
14604 72 : appendPQExpBuffer(q, " END = %s,\n",
14605 : convertTSFunction(fout, prsinfo->prsend));
14606 72 : if (prsinfo->prsheadline != InvalidOid)
14607 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
14608 : convertTSFunction(fout, prsinfo->prsheadline));
14609 72 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14610 : convertTSFunction(fout, prsinfo->prslextype));
14611 :
14612 72 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14613 72 : fmtQualifiedDumpable(prsinfo));
14614 :
14615 72 : if (dopt->binary_upgrade)
14616 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
14617 : "TEXT SEARCH PARSER", qprsname,
14618 2 : prsinfo->dobj.namespace->dobj.name);
14619 :
14620 72 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 72 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14622 72 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14623 : .namespace = prsinfo->dobj.namespace->dobj.name,
14624 : .description = "TEXT SEARCH PARSER",
14625 : .section = SECTION_PRE_DATA,
14626 : .createStmt = q->data,
14627 : .dropStmt = delq->data));
14628 :
14629 : /* Dump Parser Comments */
14630 72 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14631 72 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14632 72 : prsinfo->dobj.namespace->dobj.name, "",
14633 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14634 :
14635 72 : destroyPQExpBuffer(q);
14636 72 : destroyPQExpBuffer(delq);
14637 72 : free(qprsname);
14638 : }
14639 :
14640 : /*
14641 : * dumpTSDictionary
14642 : * write out a single text search dictionary
14643 : */
14644 : static void
14645 336 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
14646 : {
14647 336 : DumpOptions *dopt = fout->dopt;
14648 : PQExpBuffer q;
14649 : PQExpBuffer delq;
14650 : PQExpBuffer query;
14651 : char *qdictname;
14652 : PGresult *res;
14653 : char *nspname;
14654 : char *tmplname;
14655 :
14656 : /* Do nothing if not dumping schema */
14657 336 : if (!dopt->dumpSchema)
14658 6 : return;
14659 :
14660 330 : q = createPQExpBuffer();
14661 330 : delq = createPQExpBuffer();
14662 330 : query = createPQExpBuffer();
14663 :
14664 330 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14665 :
14666 : /* Fetch name and namespace of the dictionary's template */
14667 330 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
14668 : "FROM pg_ts_template p, pg_namespace n "
14669 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14670 : dictinfo->dicttemplate);
14671 330 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14672 330 : nspname = PQgetvalue(res, 0, 0);
14673 330 : tmplname = PQgetvalue(res, 0, 1);
14674 :
14675 330 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14676 330 : fmtQualifiedDumpable(dictinfo));
14677 :
14678 330 : appendPQExpBufferStr(q, " TEMPLATE = ");
14679 330 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
14680 330 : appendPQExpBufferStr(q, fmtId(tmplname));
14681 :
14682 330 : PQclear(res);
14683 :
14684 : /* the dictinitoption can be dumped straight into the command */
14685 330 : if (dictinfo->dictinitoption)
14686 258 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14687 :
14688 330 : appendPQExpBufferStr(q, " );\n");
14689 :
14690 330 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14691 330 : fmtQualifiedDumpable(dictinfo));
14692 :
14693 330 : if (dopt->binary_upgrade)
14694 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
14695 : "TEXT SEARCH DICTIONARY", qdictname,
14696 20 : dictinfo->dobj.namespace->dobj.name);
14697 :
14698 330 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14699 330 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14700 330 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14701 : .namespace = dictinfo->dobj.namespace->dobj.name,
14702 : .owner = dictinfo->rolname,
14703 : .description = "TEXT SEARCH DICTIONARY",
14704 : .section = SECTION_PRE_DATA,
14705 : .createStmt = q->data,
14706 : .dropStmt = delq->data));
14707 :
14708 : /* Dump Dictionary Comments */
14709 330 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14710 240 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14711 240 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14712 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14713 :
14714 330 : destroyPQExpBuffer(q);
14715 330 : destroyPQExpBuffer(delq);
14716 330 : destroyPQExpBuffer(query);
14717 330 : free(qdictname);
14718 : }
14719 :
14720 : /*
14721 : * dumpTSTemplate
14722 : * write out a single text search template
14723 : */
14724 : static void
14725 102 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
14726 : {
14727 102 : DumpOptions *dopt = fout->dopt;
14728 : PQExpBuffer q;
14729 : PQExpBuffer delq;
14730 : char *qtmplname;
14731 :
14732 : /* Do nothing if not dumping schema */
14733 102 : if (!dopt->dumpSchema)
14734 6 : return;
14735 :
14736 96 : q = createPQExpBuffer();
14737 96 : delq = createPQExpBuffer();
14738 :
14739 96 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14740 :
14741 96 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14742 96 : fmtQualifiedDumpable(tmplinfo));
14743 :
14744 96 : if (tmplinfo->tmplinit != InvalidOid)
14745 30 : appendPQExpBuffer(q, " INIT = %s,\n",
14746 : convertTSFunction(fout, tmplinfo->tmplinit));
14747 96 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
14748 : convertTSFunction(fout, tmplinfo->tmpllexize));
14749 :
14750 96 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14751 96 : fmtQualifiedDumpable(tmplinfo));
14752 :
14753 96 : if (dopt->binary_upgrade)
14754 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
14755 : "TEXT SEARCH TEMPLATE", qtmplname,
14756 2 : tmplinfo->dobj.namespace->dobj.name);
14757 :
14758 96 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14759 96 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14760 96 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14761 : .namespace = tmplinfo->dobj.namespace->dobj.name,
14762 : .description = "TEXT SEARCH TEMPLATE",
14763 : .section = SECTION_PRE_DATA,
14764 : .createStmt = q->data,
14765 : .dropStmt = delq->data));
14766 :
14767 : /* Dump Template Comments */
14768 96 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14769 96 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14770 96 : tmplinfo->dobj.namespace->dobj.name, "",
14771 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14772 :
14773 96 : destroyPQExpBuffer(q);
14774 96 : destroyPQExpBuffer(delq);
14775 96 : free(qtmplname);
14776 : }
14777 :
14778 : /*
14779 : * dumpTSConfig
14780 : * write out a single text search configuration
14781 : */
14782 : static void
14783 286 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
14784 : {
14785 286 : DumpOptions *dopt = fout->dopt;
14786 : PQExpBuffer q;
14787 : PQExpBuffer delq;
14788 : PQExpBuffer query;
14789 : char *qcfgname;
14790 : PGresult *res;
14791 : char *nspname;
14792 : char *prsname;
14793 : int ntups,
14794 : i;
14795 : int i_tokenname;
14796 : int i_dictname;
14797 :
14798 : /* Do nothing if not dumping schema */
14799 286 : if (!dopt->dumpSchema)
14800 6 : return;
14801 :
14802 280 : q = createPQExpBuffer();
14803 280 : delq = createPQExpBuffer();
14804 280 : query = createPQExpBuffer();
14805 :
14806 280 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14807 :
14808 : /* Fetch name and namespace of the config's parser */
14809 280 : appendPQExpBuffer(query, "SELECT nspname, prsname "
14810 : "FROM pg_ts_parser p, pg_namespace n "
14811 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14812 : cfginfo->cfgparser);
14813 280 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14814 280 : nspname = PQgetvalue(res, 0, 0);
14815 280 : prsname = PQgetvalue(res, 0, 1);
14816 :
14817 280 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14818 280 : fmtQualifiedDumpable(cfginfo));
14819 :
14820 280 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14821 280 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14822 :
14823 280 : PQclear(res);
14824 :
14825 280 : resetPQExpBuffer(query);
14826 280 : appendPQExpBuffer(query,
14827 : "SELECT\n"
14828 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14829 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14830 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14831 : "FROM pg_catalog.pg_ts_config_map AS m\n"
14832 : "WHERE m.mapcfg = '%u'\n"
14833 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14834 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14835 :
14836 280 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14837 280 : ntups = PQntuples(res);
14838 :
14839 280 : i_tokenname = PQfnumber(res, "tokenname");
14840 280 : i_dictname = PQfnumber(res, "dictname");
14841 :
14842 5870 : for (i = 0; i < ntups; i++)
14843 : {
14844 5590 : char *tokenname = PQgetvalue(res, i, i_tokenname);
14845 5590 : char *dictname = PQgetvalue(res, i, i_dictname);
14846 :
14847 5590 : if (i == 0 ||
14848 5310 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14849 : {
14850 : /* starting a new token type, so start a new command */
14851 5320 : if (i > 0)
14852 5040 : appendPQExpBufferStr(q, ";\n");
14853 5320 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14854 5320 : fmtQualifiedDumpable(cfginfo));
14855 : /* tokenname needs quoting, dictname does NOT */
14856 5320 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14857 : fmtId(tokenname), dictname);
14858 : }
14859 : else
14860 270 : appendPQExpBuffer(q, ", %s", dictname);
14861 : }
14862 :
14863 280 : if (ntups > 0)
14864 280 : appendPQExpBufferStr(q, ";\n");
14865 :
14866 280 : PQclear(res);
14867 :
14868 280 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14869 280 : fmtQualifiedDumpable(cfginfo));
14870 :
14871 280 : if (dopt->binary_upgrade)
14872 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
14873 : "TEXT SEARCH CONFIGURATION", qcfgname,
14874 10 : cfginfo->dobj.namespace->dobj.name);
14875 :
14876 280 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14877 280 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14878 280 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14879 : .namespace = cfginfo->dobj.namespace->dobj.name,
14880 : .owner = cfginfo->rolname,
14881 : .description = "TEXT SEARCH CONFIGURATION",
14882 : .section = SECTION_PRE_DATA,
14883 : .createStmt = q->data,
14884 : .dropStmt = delq->data));
14885 :
14886 : /* Dump Configuration Comments */
14887 280 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14888 240 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14889 240 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14890 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14891 :
14892 280 : destroyPQExpBuffer(q);
14893 280 : destroyPQExpBuffer(delq);
14894 280 : destroyPQExpBuffer(query);
14895 280 : free(qcfgname);
14896 : }
14897 :
14898 : /*
14899 : * dumpForeignDataWrapper
14900 : * write out a single foreign-data wrapper definition
14901 : */
14902 : static void
14903 100 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
14904 : {
14905 100 : DumpOptions *dopt = fout->dopt;
14906 : PQExpBuffer q;
14907 : PQExpBuffer delq;
14908 : char *qfdwname;
14909 :
14910 : /* Do nothing if not dumping schema */
14911 100 : if (!dopt->dumpSchema)
14912 8 : return;
14913 :
14914 92 : q = createPQExpBuffer();
14915 92 : delq = createPQExpBuffer();
14916 :
14917 92 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14918 :
14919 92 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14920 : qfdwname);
14921 :
14922 92 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14923 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14924 :
14925 92 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14926 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14927 :
14928 92 : if (strlen(fdwinfo->fdwoptions) > 0)
14929 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14930 :
14931 92 : appendPQExpBufferStr(q, ";\n");
14932 :
14933 92 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14934 : qfdwname);
14935 :
14936 92 : if (dopt->binary_upgrade)
14937 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
14938 : "FOREIGN DATA WRAPPER", qfdwname,
14939 : NULL);
14940 :
14941 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14942 92 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14943 92 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14944 : .owner = fdwinfo->rolname,
14945 : .description = "FOREIGN DATA WRAPPER",
14946 : .section = SECTION_PRE_DATA,
14947 : .createStmt = q->data,
14948 : .dropStmt = delq->data));
14949 :
14950 : /* Dump Foreign Data Wrapper Comments */
14951 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14952 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14953 : NULL, fdwinfo->rolname,
14954 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14955 :
14956 : /* Handle the ACL */
14957 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14958 64 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
14959 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
14960 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
14961 :
14962 92 : free(qfdwname);
14963 :
14964 92 : destroyPQExpBuffer(q);
14965 92 : destroyPQExpBuffer(delq);
14966 : }
14967 :
14968 : /*
14969 : * dumpForeignServer
14970 : * write out a foreign server definition
14971 : */
14972 : static void
14973 108 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
14974 : {
14975 108 : DumpOptions *dopt = fout->dopt;
14976 : PQExpBuffer q;
14977 : PQExpBuffer delq;
14978 : PQExpBuffer query;
14979 : PGresult *res;
14980 : char *qsrvname;
14981 : char *fdwname;
14982 :
14983 : /* Do nothing if not dumping schema */
14984 108 : if (!dopt->dumpSchema)
14985 12 : return;
14986 :
14987 96 : q = createPQExpBuffer();
14988 96 : delq = createPQExpBuffer();
14989 96 : query = createPQExpBuffer();
14990 :
14991 96 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14992 :
14993 : /* look up the foreign-data wrapper */
14994 96 : appendPQExpBuffer(query, "SELECT fdwname "
14995 : "FROM pg_foreign_data_wrapper w "
14996 : "WHERE w.oid = '%u'",
14997 : srvinfo->srvfdw);
14998 96 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14999 96 : fdwname = PQgetvalue(res, 0, 0);
15000 :
15001 96 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15002 96 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15003 : {
15004 0 : appendPQExpBufferStr(q, " TYPE ");
15005 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15006 : }
15007 96 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15008 : {
15009 0 : appendPQExpBufferStr(q, " VERSION ");
15010 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15011 : }
15012 :
15013 96 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15014 96 : appendPQExpBufferStr(q, fmtId(fdwname));
15015 :
15016 96 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15017 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15018 :
15019 96 : appendPQExpBufferStr(q, ";\n");
15020 :
15021 96 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15022 : qsrvname);
15023 :
15024 96 : if (dopt->binary_upgrade)
15025 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15026 : "SERVER", qsrvname, NULL);
15027 :
15028 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15029 96 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15030 96 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15031 : .owner = srvinfo->rolname,
15032 : .description = "SERVER",
15033 : .section = SECTION_PRE_DATA,
15034 : .createStmt = q->data,
15035 : .dropStmt = delq->data));
15036 :
15037 : /* Dump Foreign Server Comments */
15038 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15039 0 : dumpComment(fout, "SERVER", qsrvname,
15040 : NULL, srvinfo->rolname,
15041 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15042 :
15043 : /* Handle the ACL */
15044 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15045 64 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15046 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15047 : NULL, srvinfo->rolname, &srvinfo->dacl);
15048 :
15049 : /* Dump user mappings */
15050 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15051 96 : dumpUserMappings(fout,
15052 96 : srvinfo->dobj.name, NULL,
15053 : srvinfo->rolname,
15054 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15055 :
15056 96 : PQclear(res);
15057 :
15058 96 : free(qsrvname);
15059 :
15060 96 : destroyPQExpBuffer(q);
15061 96 : destroyPQExpBuffer(delq);
15062 96 : destroyPQExpBuffer(query);
15063 : }
15064 :
15065 : /*
15066 : * dumpUserMappings
15067 : *
15068 : * This routine is used to dump any user mappings associated with the
15069 : * server handed to this routine. Should be called after ArchiveEntry()
15070 : * for the server.
15071 : */
15072 : static void
15073 96 : dumpUserMappings(Archive *fout,
15074 : const char *servername, const char *namespace,
15075 : const char *owner,
15076 : CatalogId catalogId, DumpId dumpId)
15077 : {
15078 : PQExpBuffer q;
15079 : PQExpBuffer delq;
15080 : PQExpBuffer query;
15081 : PQExpBuffer tag;
15082 : PGresult *res;
15083 : int ntups;
15084 : int i_usename;
15085 : int i_umoptions;
15086 : int i;
15087 :
15088 96 : q = createPQExpBuffer();
15089 96 : tag = createPQExpBuffer();
15090 96 : delq = createPQExpBuffer();
15091 96 : query = createPQExpBuffer();
15092 :
15093 : /*
15094 : * We read from the publicly accessible view pg_user_mappings, so as not
15095 : * to fail if run by a non-superuser. Note that the view will show
15096 : * umoptions as null if the user hasn't got privileges for the associated
15097 : * server; this means that pg_dump will dump such a mapping, but with no
15098 : * OPTIONS clause. A possible alternative is to skip such mappings
15099 : * altogether, but it's not clear that that's an improvement.
15100 : */
15101 96 : appendPQExpBuffer(query,
15102 : "SELECT usename, "
15103 : "array_to_string(ARRAY("
15104 : "SELECT quote_ident(option_name) || ' ' || "
15105 : "quote_literal(option_value) "
15106 : "FROM pg_options_to_table(umoptions) "
15107 : "ORDER BY option_name"
15108 : "), E',\n ') AS umoptions "
15109 : "FROM pg_user_mappings "
15110 : "WHERE srvid = '%u' "
15111 : "ORDER BY usename",
15112 : catalogId.oid);
15113 :
15114 96 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15115 :
15116 96 : ntups = PQntuples(res);
15117 96 : i_usename = PQfnumber(res, "usename");
15118 96 : i_umoptions = PQfnumber(res, "umoptions");
15119 :
15120 160 : for (i = 0; i < ntups; i++)
15121 : {
15122 : char *usename;
15123 : char *umoptions;
15124 :
15125 64 : usename = PQgetvalue(res, i, i_usename);
15126 64 : umoptions = PQgetvalue(res, i, i_umoptions);
15127 :
15128 64 : resetPQExpBuffer(q);
15129 64 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15130 64 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15131 :
15132 64 : if (umoptions && strlen(umoptions) > 0)
15133 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15134 :
15135 64 : appendPQExpBufferStr(q, ";\n");
15136 :
15137 64 : resetPQExpBuffer(delq);
15138 64 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15139 64 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15140 :
15141 64 : resetPQExpBuffer(tag);
15142 64 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15143 : usename, servername);
15144 :
15145 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15146 64 : ARCHIVE_OPTS(.tag = tag->data,
15147 : .namespace = namespace,
15148 : .owner = owner,
15149 : .description = "USER MAPPING",
15150 : .section = SECTION_PRE_DATA,
15151 : .createStmt = q->data,
15152 : .dropStmt = delq->data));
15153 : }
15154 :
15155 96 : PQclear(res);
15156 :
15157 96 : destroyPQExpBuffer(query);
15158 96 : destroyPQExpBuffer(delq);
15159 96 : destroyPQExpBuffer(tag);
15160 96 : destroyPQExpBuffer(q);
15161 96 : }
15162 :
15163 : /*
15164 : * Write out default privileges information
15165 : */
15166 : static void
15167 284 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15168 : {
15169 284 : DumpOptions *dopt = fout->dopt;
15170 : PQExpBuffer q;
15171 : PQExpBuffer tag;
15172 : const char *type;
15173 :
15174 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
15175 284 : if (!dopt->dumpSchema || dopt->aclsSkip)
15176 32 : return;
15177 :
15178 252 : q = createPQExpBuffer();
15179 252 : tag = createPQExpBuffer();
15180 :
15181 252 : switch (daclinfo->defaclobjtype)
15182 : {
15183 126 : case DEFACLOBJ_RELATION:
15184 126 : type = "TABLES";
15185 126 : break;
15186 0 : case DEFACLOBJ_SEQUENCE:
15187 0 : type = "SEQUENCES";
15188 0 : break;
15189 126 : case DEFACLOBJ_FUNCTION:
15190 126 : type = "FUNCTIONS";
15191 126 : break;
15192 0 : case DEFACLOBJ_TYPE:
15193 0 : type = "TYPES";
15194 0 : break;
15195 0 : case DEFACLOBJ_NAMESPACE:
15196 0 : type = "SCHEMAS";
15197 0 : break;
15198 0 : default:
15199 : /* shouldn't get here */
15200 0 : pg_fatal("unrecognized object type in default privileges: %d",
15201 : (int) daclinfo->defaclobjtype);
15202 : type = ""; /* keep compiler quiet */
15203 : }
15204 :
15205 252 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15206 :
15207 : /* build the actual command(s) for this tuple */
15208 252 : if (!buildDefaultACLCommands(type,
15209 252 : daclinfo->dobj.namespace != NULL ?
15210 128 : daclinfo->dobj.namespace->dobj.name : NULL,
15211 252 : daclinfo->dacl.acl,
15212 252 : daclinfo->dacl.acldefault,
15213 : daclinfo->defaclrole,
15214 : fout->remoteVersion,
15215 : q))
15216 0 : pg_fatal("could not parse default ACL list (%s)",
15217 : daclinfo->dacl.acl);
15218 :
15219 252 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15220 252 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15221 252 : ARCHIVE_OPTS(.tag = tag->data,
15222 : .namespace = daclinfo->dobj.namespace ?
15223 : daclinfo->dobj.namespace->dobj.name : NULL,
15224 : .owner = daclinfo->defaclrole,
15225 : .description = "DEFAULT ACL",
15226 : .section = SECTION_POST_DATA,
15227 : .createStmt = q->data));
15228 :
15229 252 : destroyPQExpBuffer(tag);
15230 252 : destroyPQExpBuffer(q);
15231 : }
15232 :
15233 : /*----------
15234 : * Write out grant/revoke information
15235 : *
15236 : * 'objDumpId' is the dump ID of the underlying object.
15237 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15238 : * or InvalidDumpId if there is no need for a second dependency.
15239 : * 'type' must be one of
15240 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15241 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15242 : * 'name' is the formatted name of the object. Must be quoted etc. already.
15243 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15244 : * (Currently we assume that subname is only provided for table columns.)
15245 : * 'nspname' is the namespace the object is in (NULL if none).
15246 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
15247 : * to use the default for the object type.
15248 : * 'owner' is the owner, NULL if there is no owner (for languages).
15249 : * 'dacl' is the DumpableAcl struct for the object.
15250 : *
15251 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15252 : * no ACL entry was created.
15253 : *----------
15254 : */
15255 : static DumpId
15256 45836 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15257 : const char *type, const char *name, const char *subname,
15258 : const char *nspname, const char *tag, const char *owner,
15259 : const DumpableAcl *dacl)
15260 : {
15261 45836 : DumpId aclDumpId = InvalidDumpId;
15262 45836 : DumpOptions *dopt = fout->dopt;
15263 45836 : const char *acls = dacl->acl;
15264 45836 : const char *acldefault = dacl->acldefault;
15265 45836 : char privtype = dacl->privtype;
15266 45836 : const char *initprivs = dacl->initprivs;
15267 : const char *baseacls;
15268 : PQExpBuffer sql;
15269 :
15270 : /* Do nothing if ACL dump is not enabled */
15271 45836 : if (dopt->aclsSkip)
15272 636 : return InvalidDumpId;
15273 :
15274 : /* --data-only skips ACLs *except* large object ACLs */
15275 45200 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
15276 0 : return InvalidDumpId;
15277 :
15278 45200 : sql = createPQExpBuffer();
15279 :
15280 : /*
15281 : * In binary upgrade mode, we don't run an extension's script but instead
15282 : * dump out the objects independently and then recreate them. To preserve
15283 : * any initial privileges which were set on extension objects, we need to
15284 : * compute the set of GRANT and REVOKE commands necessary to get from the
15285 : * default privileges of an object to its initial privileges as recorded
15286 : * in pg_init_privs.
15287 : *
15288 : * At restore time, we apply these commands after having called
15289 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
15290 : * copy the results into pg_init_privs. This is how we preserve the
15291 : * contents of that catalog across binary upgrades.
15292 : */
15293 45200 : if (dopt->binary_upgrade && privtype == 'e' &&
15294 26 : initprivs && *initprivs != '\0')
15295 : {
15296 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15297 26 : if (!buildACLCommands(name, subname, nspname, type,
15298 : initprivs, acldefault, owner,
15299 : "", fout->remoteVersion, sql))
15300 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15301 : initprivs, acldefault, name, type);
15302 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15303 : }
15304 :
15305 : /*
15306 : * Now figure the GRANT and REVOKE commands needed to get to the object's
15307 : * actual current ACL, starting from the initprivs if given, else from the
15308 : * object-type-specific default. Also, while buildACLCommands will assume
15309 : * that a NULL/empty acls string means it needn't do anything, what that
15310 : * actually represents is the object-type-specific default; so we need to
15311 : * substitute the acldefault string to get the right results in that case.
15312 : */
15313 45200 : if (initprivs && *initprivs != '\0')
15314 : {
15315 41710 : baseacls = initprivs;
15316 41710 : if (acls == NULL || *acls == '\0')
15317 34 : acls = acldefault;
15318 : }
15319 : else
15320 3490 : baseacls = acldefault;
15321 :
15322 45200 : if (!buildACLCommands(name, subname, nspname, type,
15323 : acls, baseacls, owner,
15324 : "", fout->remoteVersion, sql))
15325 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15326 : acls, baseacls, name, type);
15327 :
15328 45200 : if (sql->len > 0)
15329 : {
15330 3648 : PQExpBuffer tagbuf = createPQExpBuffer();
15331 : DumpId aclDeps[2];
15332 3648 : int nDeps = 0;
15333 :
15334 3648 : if (tag)
15335 0 : appendPQExpBufferStr(tagbuf, tag);
15336 3648 : else if (subname)
15337 2158 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
15338 : else
15339 1490 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
15340 :
15341 3648 : aclDeps[nDeps++] = objDumpId;
15342 3648 : if (altDumpId != InvalidDumpId)
15343 1988 : aclDeps[nDeps++] = altDumpId;
15344 :
15345 3648 : aclDumpId = createDumpId();
15346 :
15347 3648 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
15348 3648 : ARCHIVE_OPTS(.tag = tagbuf->data,
15349 : .namespace = nspname,
15350 : .owner = owner,
15351 : .description = "ACL",
15352 : .section = SECTION_NONE,
15353 : .createStmt = sql->data,
15354 : .deps = aclDeps,
15355 : .nDeps = nDeps));
15356 :
15357 3648 : destroyPQExpBuffer(tagbuf);
15358 : }
15359 :
15360 45200 : destroyPQExpBuffer(sql);
15361 :
15362 45200 : return aclDumpId;
15363 : }
15364 :
15365 : /*
15366 : * dumpSecLabel
15367 : *
15368 : * This routine is used to dump any security labels associated with the
15369 : * object handed to this routine. The routine takes the object type
15370 : * and object name (ready to print, except for schema decoration), plus
15371 : * the namespace and owner of the object (for labeling the ArchiveEntry),
15372 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
15373 : * plus the dump ID for the object (for setting a dependency).
15374 : * If a matching pg_seclabel entry is found, it is dumped.
15375 : *
15376 : * Note: although this routine takes a dumpId for dependency purposes,
15377 : * that purpose is just to mark the dependency in the emitted dump file
15378 : * for possible future use by pg_restore. We do NOT use it for determining
15379 : * ordering of the label in the dump file, because this routine is called
15380 : * after dependency sorting occurs. This routine should be called just after
15381 : * calling ArchiveEntry() for the specified object.
15382 : */
15383 : static void
15384 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
15385 : const char *namespace, const char *owner,
15386 : CatalogId catalogId, int subid, DumpId dumpId)
15387 : {
15388 0 : DumpOptions *dopt = fout->dopt;
15389 : SecLabelItem *labels;
15390 : int nlabels;
15391 : int i;
15392 : PQExpBuffer query;
15393 :
15394 : /* do nothing, if --no-security-labels is supplied */
15395 0 : if (dopt->no_security_labels)
15396 0 : return;
15397 :
15398 : /*
15399 : * Security labels are schema not data ... except large object labels are
15400 : * data
15401 : */
15402 0 : if (strcmp(type, "LARGE OBJECT") != 0)
15403 : {
15404 0 : if (!dopt->dumpSchema)
15405 0 : return;
15406 : }
15407 : else
15408 : {
15409 : /* We do dump large object security labels in binary-upgrade mode */
15410 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
15411 0 : return;
15412 : }
15413 :
15414 : /* Search for security labels associated with catalogId, using table */
15415 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15416 :
15417 0 : query = createPQExpBuffer();
15418 :
15419 0 : for (i = 0; i < nlabels; i++)
15420 : {
15421 : /*
15422 : * Ignore label entries for which the subid doesn't match.
15423 : */
15424 0 : if (labels[i].objsubid != subid)
15425 0 : continue;
15426 :
15427 0 : appendPQExpBuffer(query,
15428 : "SECURITY LABEL FOR %s ON %s ",
15429 0 : fmtId(labels[i].provider), type);
15430 0 : if (namespace && *namespace)
15431 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
15432 0 : appendPQExpBuffer(query, "%s IS ", name);
15433 0 : appendStringLiteralAH(query, labels[i].label, fout);
15434 0 : appendPQExpBufferStr(query, ";\n");
15435 : }
15436 :
15437 0 : if (query->len > 0)
15438 : {
15439 0 : PQExpBuffer tag = createPQExpBuffer();
15440 :
15441 0 : appendPQExpBuffer(tag, "%s %s", type, name);
15442 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15443 0 : ARCHIVE_OPTS(.tag = tag->data,
15444 : .namespace = namespace,
15445 : .owner = owner,
15446 : .description = "SECURITY LABEL",
15447 : .section = SECTION_NONE,
15448 : .createStmt = query->data,
15449 : .deps = &dumpId,
15450 : .nDeps = 1));
15451 0 : destroyPQExpBuffer(tag);
15452 : }
15453 :
15454 0 : destroyPQExpBuffer(query);
15455 : }
15456 :
15457 : /*
15458 : * dumpTableSecLabel
15459 : *
15460 : * As above, but dump security label for both the specified table (or view)
15461 : * and its columns.
15462 : */
15463 : static void
15464 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15465 : {
15466 0 : DumpOptions *dopt = fout->dopt;
15467 : SecLabelItem *labels;
15468 : int nlabels;
15469 : int i;
15470 : PQExpBuffer query;
15471 : PQExpBuffer target;
15472 :
15473 : /* do nothing, if --no-security-labels is supplied */
15474 0 : if (dopt->no_security_labels)
15475 0 : return;
15476 :
15477 : /* SecLabel are SCHEMA not data */
15478 0 : if (!dopt->dumpSchema)
15479 0 : return;
15480 :
15481 : /* Search for comments associated with relation, using table */
15482 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15483 : tbinfo->dobj.catId.oid,
15484 : &labels);
15485 :
15486 : /* If security labels exist, build SECURITY LABEL statements */
15487 0 : if (nlabels <= 0)
15488 0 : return;
15489 :
15490 0 : query = createPQExpBuffer();
15491 0 : target = createPQExpBuffer();
15492 :
15493 0 : for (i = 0; i < nlabels; i++)
15494 : {
15495 : const char *colname;
15496 0 : const char *provider = labels[i].provider;
15497 0 : const char *label = labels[i].label;
15498 0 : int objsubid = labels[i].objsubid;
15499 :
15500 0 : resetPQExpBuffer(target);
15501 0 : if (objsubid == 0)
15502 : {
15503 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15504 0 : fmtQualifiedDumpable(tbinfo));
15505 : }
15506 : else
15507 : {
15508 0 : colname = getAttrName(objsubid, tbinfo);
15509 : /* first fmtXXX result must be consumed before calling again */
15510 0 : appendPQExpBuffer(target, "COLUMN %s",
15511 0 : fmtQualifiedDumpable(tbinfo));
15512 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
15513 : }
15514 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15515 : fmtId(provider), target->data);
15516 0 : appendStringLiteralAH(query, label, fout);
15517 0 : appendPQExpBufferStr(query, ";\n");
15518 : }
15519 0 : if (query->len > 0)
15520 : {
15521 0 : resetPQExpBuffer(target);
15522 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15523 0 : fmtId(tbinfo->dobj.name));
15524 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15525 0 : ARCHIVE_OPTS(.tag = target->data,
15526 : .namespace = tbinfo->dobj.namespace->dobj.name,
15527 : .owner = tbinfo->rolname,
15528 : .description = "SECURITY LABEL",
15529 : .section = SECTION_NONE,
15530 : .createStmt = query->data,
15531 : .deps = &(tbinfo->dobj.dumpId),
15532 : .nDeps = 1));
15533 : }
15534 0 : destroyPQExpBuffer(query);
15535 0 : destroyPQExpBuffer(target);
15536 : }
15537 :
15538 : /*
15539 : * findSecLabels
15540 : *
15541 : * Find the security label(s), if any, associated with the given object.
15542 : * All the objsubid values associated with the given classoid/objoid are
15543 : * found with one search.
15544 : */
15545 : static int
15546 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15547 : {
15548 0 : SecLabelItem *middle = NULL;
15549 : SecLabelItem *low;
15550 : SecLabelItem *high;
15551 : int nmatch;
15552 :
15553 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
15554 : {
15555 0 : *items = NULL;
15556 0 : return 0;
15557 : }
15558 :
15559 : /*
15560 : * Do binary search to find some item matching the object.
15561 : */
15562 0 : low = &seclabels[0];
15563 0 : high = &seclabels[nseclabels - 1];
15564 0 : while (low <= high)
15565 : {
15566 0 : middle = low + (high - low) / 2;
15567 :
15568 0 : if (classoid < middle->classoid)
15569 0 : high = middle - 1;
15570 0 : else if (classoid > middle->classoid)
15571 0 : low = middle + 1;
15572 0 : else if (objoid < middle->objoid)
15573 0 : high = middle - 1;
15574 0 : else if (objoid > middle->objoid)
15575 0 : low = middle + 1;
15576 : else
15577 0 : break; /* found a match */
15578 : }
15579 :
15580 0 : if (low > high) /* no matches */
15581 : {
15582 0 : *items = NULL;
15583 0 : return 0;
15584 : }
15585 :
15586 : /*
15587 : * Now determine how many items match the object. The search loop
15588 : * invariant still holds: only items between low and high inclusive could
15589 : * match.
15590 : */
15591 0 : nmatch = 1;
15592 0 : while (middle > low)
15593 : {
15594 0 : if (classoid != middle[-1].classoid ||
15595 0 : objoid != middle[-1].objoid)
15596 : break;
15597 0 : middle--;
15598 0 : nmatch++;
15599 : }
15600 :
15601 0 : *items = middle;
15602 :
15603 0 : middle += nmatch;
15604 0 : while (middle <= high)
15605 : {
15606 0 : if (classoid != middle->classoid ||
15607 0 : objoid != middle->objoid)
15608 : break;
15609 0 : middle++;
15610 0 : nmatch++;
15611 : }
15612 :
15613 0 : return nmatch;
15614 : }
15615 :
15616 : /*
15617 : * collectSecLabels
15618 : *
15619 : * Construct a table of all security labels available for database objects;
15620 : * also set the has-seclabel component flag for each relevant object.
15621 : *
15622 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
15623 : */
15624 : static void
15625 308 : collectSecLabels(Archive *fout)
15626 : {
15627 : PGresult *res;
15628 : PQExpBuffer query;
15629 : int i_label;
15630 : int i_provider;
15631 : int i_classoid;
15632 : int i_objoid;
15633 : int i_objsubid;
15634 : int ntups;
15635 : int i;
15636 : DumpableObject *dobj;
15637 :
15638 308 : query = createPQExpBuffer();
15639 :
15640 308 : appendPQExpBufferStr(query,
15641 : "SELECT label, provider, classoid, objoid, objsubid "
15642 : "FROM pg_catalog.pg_seclabel "
15643 : "ORDER BY classoid, objoid, objsubid");
15644 :
15645 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15646 :
15647 : /* Construct lookup table containing OIDs in numeric form */
15648 308 : i_label = PQfnumber(res, "label");
15649 308 : i_provider = PQfnumber(res, "provider");
15650 308 : i_classoid = PQfnumber(res, "classoid");
15651 308 : i_objoid = PQfnumber(res, "objoid");
15652 308 : i_objsubid = PQfnumber(res, "objsubid");
15653 :
15654 308 : ntups = PQntuples(res);
15655 :
15656 308 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15657 308 : nseclabels = 0;
15658 308 : dobj = NULL;
15659 :
15660 308 : for (i = 0; i < ntups; i++)
15661 : {
15662 : CatalogId objId;
15663 : int subid;
15664 :
15665 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
15666 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
15667 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
15668 :
15669 : /* We needn't remember labels that don't match any dumpable object */
15670 0 : if (dobj == NULL ||
15671 0 : dobj->catId.tableoid != objId.tableoid ||
15672 0 : dobj->catId.oid != objId.oid)
15673 0 : dobj = findObjectByCatalogId(objId);
15674 0 : if (dobj == NULL)
15675 0 : continue;
15676 :
15677 : /*
15678 : * Labels on columns of composite types are linked to the type's
15679 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
15680 : * in the type's own DumpableObject.
15681 : */
15682 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
15683 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
15684 0 : {
15685 : TypeInfo *cTypeInfo;
15686 :
15687 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
15688 0 : if (cTypeInfo)
15689 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
15690 : }
15691 : else
15692 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
15693 :
15694 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
15695 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
15696 0 : seclabels[nseclabels].classoid = objId.tableoid;
15697 0 : seclabels[nseclabels].objoid = objId.oid;
15698 0 : seclabels[nseclabels].objsubid = subid;
15699 0 : nseclabels++;
15700 : }
15701 :
15702 308 : PQclear(res);
15703 308 : destroyPQExpBuffer(query);
15704 308 : }
15705 :
15706 : /*
15707 : * dumpTable
15708 : * write out to fout the declarations (not data) of a user-defined table
15709 : */
15710 : static void
15711 49984 : dumpTable(Archive *fout, const TableInfo *tbinfo)
15712 : {
15713 49984 : DumpOptions *dopt = fout->dopt;
15714 49984 : DumpId tableAclDumpId = InvalidDumpId;
15715 : char *namecopy;
15716 :
15717 : /* Do nothing if not dumping schema */
15718 49984 : if (!dopt->dumpSchema)
15719 1734 : return;
15720 :
15721 48250 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15722 : {
15723 11876 : if (tbinfo->relkind == RELKIND_SEQUENCE)
15724 696 : dumpSequence(fout, tbinfo);
15725 : else
15726 11180 : dumpTableSchema(fout, tbinfo);
15727 : }
15728 :
15729 : /* Handle the ACL here */
15730 48250 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15731 48250 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15732 : {
15733 37806 : const char *objtype =
15734 37806 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15735 :
15736 : tableAclDumpId =
15737 37806 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15738 : objtype, namecopy, NULL,
15739 37806 : tbinfo->dobj.namespace->dobj.name,
15740 : NULL, tbinfo->rolname, &tbinfo->dacl);
15741 : }
15742 :
15743 : /*
15744 : * Handle column ACLs, if any. Note: we pull these with a separate query
15745 : * rather than trying to fetch them during getTableAttrs, so that we won't
15746 : * miss ACLs on system columns. Doing it this way also allows us to dump
15747 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
15748 : */
15749 48250 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
15750 : {
15751 510 : PQExpBuffer query = createPQExpBuffer();
15752 : PGresult *res;
15753 : int i;
15754 :
15755 510 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
15756 : {
15757 : /* Set up query for column ACLs */
15758 262 : appendPQExpBufferStr(query,
15759 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
15760 :
15761 262 : if (fout->remoteVersion >= 90600)
15762 : {
15763 : /*
15764 : * In principle we should call acldefault('c', relowner) to
15765 : * get the default ACL for a column. However, we don't
15766 : * currently store the numeric OID of the relowner in
15767 : * TableInfo. We could convert the owner name using regrole,
15768 : * but that creates a risk of failure due to concurrent role
15769 : * renames. Given that the default ACL for columns is empty
15770 : * and is likely to stay that way, it's not worth extra cycles
15771 : * and risk to avoid hard-wiring that knowledge here.
15772 : */
15773 262 : appendPQExpBufferStr(query,
15774 : "SELECT at.attname, "
15775 : "at.attacl, "
15776 : "'{}' AS acldefault, "
15777 : "pip.privtype, pip.initprivs "
15778 : "FROM pg_catalog.pg_attribute at "
15779 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15780 : "(at.attrelid = pip.objoid "
15781 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15782 : "AND at.attnum = pip.objsubid) "
15783 : "WHERE at.attrelid = $1 AND "
15784 : "NOT at.attisdropped "
15785 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
15786 : "ORDER BY at.attnum");
15787 : }
15788 : else
15789 : {
15790 0 : appendPQExpBufferStr(query,
15791 : "SELECT attname, attacl, '{}' AS acldefault, "
15792 : "NULL AS privtype, NULL AS initprivs "
15793 : "FROM pg_catalog.pg_attribute "
15794 : "WHERE attrelid = $1 AND NOT attisdropped "
15795 : "AND attacl IS NOT NULL "
15796 : "ORDER BY attnum");
15797 : }
15798 :
15799 262 : ExecuteSqlStatement(fout, query->data);
15800 :
15801 262 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
15802 : }
15803 :
15804 510 : printfPQExpBuffer(query,
15805 : "EXECUTE getColumnACLs('%u')",
15806 : tbinfo->dobj.catId.oid);
15807 :
15808 510 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15809 :
15810 7020 : for (i = 0; i < PQntuples(res); i++)
15811 : {
15812 6510 : char *attname = PQgetvalue(res, i, 0);
15813 6510 : char *attacl = PQgetvalue(res, i, 1);
15814 6510 : char *acldefault = PQgetvalue(res, i, 2);
15815 6510 : char privtype = *(PQgetvalue(res, i, 3));
15816 6510 : char *initprivs = PQgetvalue(res, i, 4);
15817 : DumpableAcl coldacl;
15818 : char *attnamecopy;
15819 :
15820 6510 : coldacl.acl = attacl;
15821 6510 : coldacl.acldefault = acldefault;
15822 6510 : coldacl.privtype = privtype;
15823 6510 : coldacl.initprivs = initprivs;
15824 6510 : attnamecopy = pg_strdup(fmtId(attname));
15825 :
15826 : /*
15827 : * Column's GRANT type is always TABLE. Each column ACL depends
15828 : * on the table-level ACL, since we can restore column ACLs in
15829 : * parallel but the table-level ACL has to be done first.
15830 : */
15831 6510 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
15832 : "TABLE", namecopy, attnamecopy,
15833 6510 : tbinfo->dobj.namespace->dobj.name,
15834 : NULL, tbinfo->rolname, &coldacl);
15835 6510 : free(attnamecopy);
15836 : }
15837 510 : PQclear(res);
15838 510 : destroyPQExpBuffer(query);
15839 : }
15840 :
15841 48250 : free(namecopy);
15842 : }
15843 :
15844 : /*
15845 : * Create the AS clause for a view or materialized view. The semicolon is
15846 : * stripped because a materialized view must add a WITH NO DATA clause.
15847 : *
15848 : * This returns a new buffer which must be freed by the caller.
15849 : */
15850 : static PQExpBuffer
15851 1688 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
15852 : {
15853 1688 : PQExpBuffer query = createPQExpBuffer();
15854 1688 : PQExpBuffer result = createPQExpBuffer();
15855 : PGresult *res;
15856 : int len;
15857 :
15858 : /* Fetch the view definition */
15859 1688 : appendPQExpBuffer(query,
15860 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15861 : tbinfo->dobj.catId.oid);
15862 :
15863 1688 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15864 :
15865 1688 : if (PQntuples(res) != 1)
15866 : {
15867 0 : if (PQntuples(res) < 1)
15868 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
15869 : tbinfo->dobj.name);
15870 : else
15871 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
15872 : tbinfo->dobj.name);
15873 : }
15874 :
15875 1688 : len = PQgetlength(res, 0, 0);
15876 :
15877 1688 : if (len == 0)
15878 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
15879 : tbinfo->dobj.name);
15880 :
15881 : /* Strip off the trailing semicolon so that other things may follow. */
15882 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15883 1688 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15884 :
15885 1688 : PQclear(res);
15886 1688 : destroyPQExpBuffer(query);
15887 :
15888 1688 : return result;
15889 : }
15890 :
15891 : /*
15892 : * Create a dummy AS clause for a view. This is used when the real view
15893 : * definition has to be postponed because of circular dependencies.
15894 : * We must duplicate the view's external properties -- column names and types
15895 : * (including collation) -- so that it works for subsequent references.
15896 : *
15897 : * This returns a new buffer which must be freed by the caller.
15898 : */
15899 : static PQExpBuffer
15900 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
15901 : {
15902 40 : PQExpBuffer result = createPQExpBuffer();
15903 : int j;
15904 :
15905 40 : appendPQExpBufferStr(result, "SELECT");
15906 :
15907 80 : for (j = 0; j < tbinfo->numatts; j++)
15908 : {
15909 40 : if (j > 0)
15910 20 : appendPQExpBufferChar(result, ',');
15911 40 : appendPQExpBufferStr(result, "\n ");
15912 :
15913 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15914 :
15915 : /*
15916 : * Must add collation if not default for the type, because CREATE OR
15917 : * REPLACE VIEW won't change it
15918 : */
15919 40 : if (OidIsValid(tbinfo->attcollation[j]))
15920 : {
15921 : CollInfo *coll;
15922 :
15923 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
15924 0 : if (coll)
15925 0 : appendPQExpBuffer(result, " COLLATE %s",
15926 0 : fmtQualifiedDumpable(coll));
15927 : }
15928 :
15929 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15930 : }
15931 :
15932 40 : return result;
15933 : }
15934 :
15935 : /*
15936 : * dumpTableSchema
15937 : * write the declaration (not data) of one user-defined table or view
15938 : */
15939 : static void
15940 11180 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
15941 : {
15942 11180 : DumpOptions *dopt = fout->dopt;
15943 11180 : PQExpBuffer q = createPQExpBuffer();
15944 11180 : PQExpBuffer delq = createPQExpBuffer();
15945 11180 : PQExpBuffer extra = createPQExpBuffer();
15946 : char *qrelname;
15947 : char *qualrelname;
15948 : int numParents;
15949 : TableInfo **parents;
15950 : int actual_atts; /* number of attrs in this CREATE statement */
15951 : const char *reltypename;
15952 : char *storage;
15953 : int j,
15954 : k;
15955 :
15956 : /* We had better have loaded per-column details about this table */
15957 : Assert(tbinfo->interesting);
15958 :
15959 11180 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
15960 11180 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15961 :
15962 11180 : if (tbinfo->hasoids)
15963 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
15964 : qrelname);
15965 :
15966 11180 : if (dopt->binary_upgrade)
15967 1524 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
15968 :
15969 : /* Is it a table or a view? */
15970 11180 : if (tbinfo->relkind == RELKIND_VIEW)
15971 : {
15972 : PQExpBuffer result;
15973 :
15974 : /*
15975 : * Note: keep this code in sync with the is_view case in dumpRule()
15976 : */
15977 :
15978 1014 : reltypename = "VIEW";
15979 :
15980 1014 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
15981 :
15982 1014 : if (dopt->binary_upgrade)
15983 98 : binary_upgrade_set_pg_class_oids(fout, q,
15984 : tbinfo->dobj.catId.oid);
15985 :
15986 1014 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
15987 :
15988 1014 : if (tbinfo->dummy_view)
15989 20 : result = createDummyViewAsClause(fout, tbinfo);
15990 : else
15991 : {
15992 994 : if (nonemptyReloptions(tbinfo->reloptions))
15993 : {
15994 124 : appendPQExpBufferStr(q, " WITH (");
15995 124 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15996 124 : appendPQExpBufferChar(q, ')');
15997 : }
15998 994 : result = createViewAsClause(fout, tbinfo);
15999 : }
16000 1014 : appendPQExpBuffer(q, " AS\n%s", result->data);
16001 1014 : destroyPQExpBuffer(result);
16002 :
16003 1014 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16004 66 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16005 1014 : appendPQExpBufferStr(q, ";\n");
16006 : }
16007 : else
16008 : {
16009 10166 : char *partkeydef = NULL;
16010 10166 : char *ftoptions = NULL;
16011 10166 : char *srvname = NULL;
16012 10166 : const char *foreign = "";
16013 :
16014 : /*
16015 : * Set reltypename, and collect any relkind-specific data that we
16016 : * didn't fetch during getTables().
16017 : */
16018 10166 : switch (tbinfo->relkind)
16019 : {
16020 1036 : case RELKIND_PARTITIONED_TABLE:
16021 : {
16022 1036 : PQExpBuffer query = createPQExpBuffer();
16023 : PGresult *res;
16024 :
16025 1036 : reltypename = "TABLE";
16026 :
16027 : /* retrieve partition key definition */
16028 1036 : appendPQExpBuffer(query,
16029 : "SELECT pg_get_partkeydef('%u')",
16030 : tbinfo->dobj.catId.oid);
16031 1036 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16032 1036 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16033 1036 : PQclear(res);
16034 1036 : destroyPQExpBuffer(query);
16035 1036 : break;
16036 : }
16037 70 : case RELKIND_FOREIGN_TABLE:
16038 : {
16039 70 : PQExpBuffer query = createPQExpBuffer();
16040 : PGresult *res;
16041 : int i_srvname;
16042 : int i_ftoptions;
16043 :
16044 70 : reltypename = "FOREIGN TABLE";
16045 :
16046 : /* retrieve name of foreign server and generic options */
16047 70 : appendPQExpBuffer(query,
16048 : "SELECT fs.srvname, "
16049 : "pg_catalog.array_to_string(ARRAY("
16050 : "SELECT pg_catalog.quote_ident(option_name) || "
16051 : "' ' || pg_catalog.quote_literal(option_value) "
16052 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16053 : "ORDER BY option_name"
16054 : "), E',\n ') AS ftoptions "
16055 : "FROM pg_catalog.pg_foreign_table ft "
16056 : "JOIN pg_catalog.pg_foreign_server fs "
16057 : "ON (fs.oid = ft.ftserver) "
16058 : "WHERE ft.ftrelid = '%u'",
16059 : tbinfo->dobj.catId.oid);
16060 70 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16061 70 : i_srvname = PQfnumber(res, "srvname");
16062 70 : i_ftoptions = PQfnumber(res, "ftoptions");
16063 70 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16064 70 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16065 70 : PQclear(res);
16066 70 : destroyPQExpBuffer(query);
16067 :
16068 70 : foreign = "FOREIGN ";
16069 70 : break;
16070 : }
16071 674 : case RELKIND_MATVIEW:
16072 674 : reltypename = "MATERIALIZED VIEW";
16073 674 : break;
16074 8386 : default:
16075 8386 : reltypename = "TABLE";
16076 8386 : break;
16077 : }
16078 :
16079 10166 : numParents = tbinfo->numParents;
16080 10166 : parents = tbinfo->parents;
16081 :
16082 10166 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16083 :
16084 10166 : if (dopt->binary_upgrade)
16085 1426 : binary_upgrade_set_pg_class_oids(fout, q,
16086 : tbinfo->dobj.catId.oid);
16087 :
16088 : /*
16089 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16090 : * ignore it when dumping if it was set in this case.
16091 : */
16092 10166 : appendPQExpBuffer(q, "CREATE %s%s %s",
16093 10166 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16094 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16095 : "UNLOGGED " : "",
16096 : reltypename,
16097 : qualrelname);
16098 :
16099 : /*
16100 : * Attach to type, if reloftype; except in case of a binary upgrade,
16101 : * we dump the table normally and attach it to the type afterward.
16102 : */
16103 10166 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16104 48 : appendPQExpBuffer(q, " OF %s",
16105 : getFormattedTypeName(fout, tbinfo->reloftype,
16106 : zeroIsError));
16107 :
16108 10166 : if (tbinfo->relkind != RELKIND_MATVIEW)
16109 : {
16110 : /* Dump the attributes */
16111 9492 : actual_atts = 0;
16112 46786 : for (j = 0; j < tbinfo->numatts; j++)
16113 : {
16114 : /*
16115 : * Normally, dump if it's locally defined in this table, and
16116 : * not dropped. But for binary upgrade, we'll dump all the
16117 : * columns, and then fix up the dropped and nonlocal cases
16118 : * below.
16119 : */
16120 37294 : if (shouldPrintColumn(dopt, tbinfo, j))
16121 : {
16122 : bool print_default;
16123 : bool print_notnull;
16124 :
16125 : /*
16126 : * Default value --- suppress if to be printed separately
16127 : * or not at all.
16128 : */
16129 72934 : print_default = (tbinfo->attrdefs[j] != NULL &&
16130 37150 : tbinfo->attrdefs[j]->dobj.dump &&
16131 1446 : !tbinfo->attrdefs[j]->separate);
16132 :
16133 : /*
16134 : * Not Null constraint --- print it if it is locally
16135 : * defined, or if binary upgrade. (In the latter case, we
16136 : * reset conislocal below.)
16137 : */
16138 39628 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16139 3924 : (tbinfo->notnull_islocal[j] ||
16140 1076 : dopt->binary_upgrade ||
16141 932 : tbinfo->ispartition));
16142 :
16143 : /*
16144 : * Skip column if fully defined by reloftype, except in
16145 : * binary upgrade
16146 : */
16147 35704 : if (OidIsValid(tbinfo->reloftype) &&
16148 100 : !print_default && !print_notnull &&
16149 60 : !dopt->binary_upgrade)
16150 48 : continue;
16151 :
16152 : /* Format properly if not first attr */
16153 35656 : if (actual_atts == 0)
16154 9012 : appendPQExpBufferStr(q, " (");
16155 : else
16156 26644 : appendPQExpBufferChar(q, ',');
16157 35656 : appendPQExpBufferStr(q, "\n ");
16158 35656 : actual_atts++;
16159 :
16160 : /* Attribute name */
16161 35656 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16162 :
16163 35656 : if (tbinfo->attisdropped[j])
16164 : {
16165 : /*
16166 : * ALTER TABLE DROP COLUMN clears
16167 : * pg_attribute.atttypid, so we will not have gotten a
16168 : * valid type name; insert INTEGER as a stopgap. We'll
16169 : * clean things up later.
16170 : */
16171 158 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16172 : /* and skip to the next column */
16173 158 : continue;
16174 : }
16175 :
16176 : /*
16177 : * Attribute type; print it except when creating a typed
16178 : * table ('OF type_name'), but in binary-upgrade mode,
16179 : * print it in that case too.
16180 : */
16181 35498 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16182 : {
16183 35466 : appendPQExpBuffer(q, " %s",
16184 35466 : tbinfo->atttypnames[j]);
16185 : }
16186 :
16187 35498 : if (print_default)
16188 : {
16189 1232 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16190 530 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16191 530 : tbinfo->attrdefs[j]->adef_expr);
16192 : else
16193 702 : appendPQExpBuffer(q, " DEFAULT %s",
16194 702 : tbinfo->attrdefs[j]->adef_expr);
16195 : }
16196 :
16197 39422 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16198 3924 : (tbinfo->notnull_islocal[j] ||
16199 1076 : dopt->binary_upgrade ||
16200 932 : tbinfo->ispartition));
16201 :
16202 35498 : if (print_notnull)
16203 : {
16204 3860 : if (tbinfo->notnull_constrs[j][0] == '\0')
16205 2766 : appendPQExpBufferStr(q, " NOT NULL");
16206 : else
16207 1094 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16208 1094 : fmtId(tbinfo->notnull_constrs[j]));
16209 :
16210 3860 : if (tbinfo->notnull_noinh[j])
16211 0 : appendPQExpBufferStr(q, " NO INHERIT");
16212 : }
16213 :
16214 : /* Add collation if not default for the type */
16215 35498 : if (OidIsValid(tbinfo->attcollation[j]))
16216 : {
16217 : CollInfo *coll;
16218 :
16219 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
16220 394 : if (coll)
16221 394 : appendPQExpBuffer(q, " COLLATE %s",
16222 394 : fmtQualifiedDumpable(coll));
16223 : }
16224 : }
16225 :
16226 : /*
16227 : * On the other hand, if we choose not to print a column
16228 : * (likely because it is created by inheritance), but the
16229 : * column has a locally-defined not-null constraint, we need
16230 : * to dump the constraint as a standalone object.
16231 : *
16232 : * This syntax isn't SQL-conforming, but if you wanted
16233 : * standard output you wouldn't be creating non-standard
16234 : * objects to begin with.
16235 : */
16236 37088 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
16237 1590 : !tbinfo->attisdropped[j] &&
16238 902 : tbinfo->notnull_constrs[j] != NULL &&
16239 176 : tbinfo->notnull_islocal[j])
16240 : {
16241 : /* Format properly if not first attr */
16242 32 : if (actual_atts == 0)
16243 24 : appendPQExpBufferStr(q, " (");
16244 : else
16245 8 : appendPQExpBufferChar(q, ',');
16246 32 : appendPQExpBufferStr(q, "\n ");
16247 32 : actual_atts++;
16248 :
16249 32 : if (tbinfo->notnull_constrs[j][0] == '\0')
16250 8 : appendPQExpBuffer(q, "NOT NULL %s",
16251 8 : fmtId(tbinfo->attnames[j]));
16252 : else
16253 48 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
16254 24 : tbinfo->notnull_constrs[j],
16255 24 : fmtId(tbinfo->attnames[j]));
16256 : }
16257 : }
16258 :
16259 : /*
16260 : * Add non-inherited CHECK constraints, if any.
16261 : *
16262 : * For partitions, we need to include check constraints even if
16263 : * they're not defined locally, because the ALTER TABLE ATTACH
16264 : * PARTITION that we'll emit later expects the constraint to be
16265 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
16266 : */
16267 10618 : for (j = 0; j < tbinfo->ncheck; j++)
16268 : {
16269 1126 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16270 :
16271 1126 : if (constr->separate ||
16272 996 : (!constr->conislocal && !tbinfo->ispartition))
16273 206 : continue;
16274 :
16275 920 : if (actual_atts == 0)
16276 32 : appendPQExpBufferStr(q, " (\n ");
16277 : else
16278 888 : appendPQExpBufferStr(q, ",\n ");
16279 :
16280 920 : appendPQExpBuffer(q, "CONSTRAINT %s ",
16281 920 : fmtId(constr->dobj.name));
16282 920 : appendPQExpBufferStr(q, constr->condef);
16283 :
16284 920 : actual_atts++;
16285 : }
16286 :
16287 9492 : if (actual_atts)
16288 9068 : appendPQExpBufferStr(q, "\n)");
16289 424 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16290 : {
16291 : /*
16292 : * No attributes? we must have a parenthesized attribute list,
16293 : * even though empty, when not using the OF TYPE syntax.
16294 : */
16295 400 : appendPQExpBufferStr(q, " (\n)");
16296 : }
16297 :
16298 : /*
16299 : * Emit the INHERITS clause (not for partitions), except in
16300 : * binary-upgrade mode.
16301 : */
16302 9492 : if (numParents > 0 && !tbinfo->ispartition &&
16303 672 : !dopt->binary_upgrade)
16304 : {
16305 574 : appendPQExpBufferStr(q, "\nINHERITS (");
16306 1204 : for (k = 0; k < numParents; k++)
16307 : {
16308 630 : TableInfo *parentRel = parents[k];
16309 :
16310 630 : if (k > 0)
16311 56 : appendPQExpBufferStr(q, ", ");
16312 630 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16313 : }
16314 574 : appendPQExpBufferChar(q, ')');
16315 : }
16316 :
16317 9492 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16318 1036 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16319 :
16320 9492 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16321 70 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16322 : }
16323 :
16324 20046 : if (nonemptyReloptions(tbinfo->reloptions) ||
16325 9880 : nonemptyReloptions(tbinfo->toast_reloptions))
16326 : {
16327 286 : bool addcomma = false;
16328 :
16329 286 : appendPQExpBufferStr(q, "\nWITH (");
16330 286 : if (nonemptyReloptions(tbinfo->reloptions))
16331 : {
16332 286 : addcomma = true;
16333 286 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16334 : }
16335 286 : if (nonemptyReloptions(tbinfo->toast_reloptions))
16336 : {
16337 10 : if (addcomma)
16338 10 : appendPQExpBufferStr(q, ", ");
16339 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16340 : fout);
16341 : }
16342 286 : appendPQExpBufferChar(q, ')');
16343 : }
16344 :
16345 : /* Dump generic options if any */
16346 10166 : if (ftoptions && ftoptions[0])
16347 66 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16348 :
16349 : /*
16350 : * For materialized views, create the AS clause just like a view. At
16351 : * this point, we always mark the view as not populated.
16352 : */
16353 10166 : if (tbinfo->relkind == RELKIND_MATVIEW)
16354 : {
16355 : PQExpBuffer result;
16356 :
16357 674 : result = createViewAsClause(fout, tbinfo);
16358 674 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16359 : result->data);
16360 674 : destroyPQExpBuffer(result);
16361 : }
16362 : else
16363 9492 : appendPQExpBufferStr(q, ";\n");
16364 :
16365 : /* Materialized views can depend on extensions */
16366 10166 : if (tbinfo->relkind == RELKIND_MATVIEW)
16367 674 : append_depends_on_extension(fout, q, &tbinfo->dobj,
16368 : "pg_catalog.pg_class",
16369 : "MATERIALIZED VIEW",
16370 : qualrelname);
16371 :
16372 : /*
16373 : * in binary upgrade mode, update the catalog with any missing values
16374 : * that might be present.
16375 : */
16376 10166 : if (dopt->binary_upgrade)
16377 : {
16378 7306 : for (j = 0; j < tbinfo->numatts; j++)
16379 : {
16380 5880 : if (tbinfo->attmissingval[j][0] != '\0')
16381 : {
16382 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
16383 4 : appendPQExpBufferStr(q,
16384 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16385 4 : appendStringLiteralAH(q, qualrelname, fout);
16386 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16387 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16388 4 : appendPQExpBufferChar(q, ',');
16389 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16390 4 : appendPQExpBufferStr(q, ");\n\n");
16391 : }
16392 : }
16393 : }
16394 :
16395 : /*
16396 : * To create binary-compatible heap files, we have to ensure the same
16397 : * physical column order, including dropped columns, as in the
16398 : * original. Therefore, we create dropped columns above and drop them
16399 : * here, also updating their attlen/attalign values so that the
16400 : * dropped column can be skipped properly. (We do not bother with
16401 : * restoring the original attbyval setting.) Also, inheritance
16402 : * relationships are set up by doing ALTER TABLE INHERIT rather than
16403 : * using an INHERITS clause --- the latter would possibly mess up the
16404 : * column order. That also means we have to take care about setting
16405 : * attislocal correctly, plus fix up any inherited CHECK constraints.
16406 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
16407 : *
16408 : * We process foreign and partitioned tables here, even though they
16409 : * lack heap storage, because they can participate in inheritance
16410 : * relationships and we want this stuff to be consistent across the
16411 : * inheritance tree. We can exclude indexes, toast tables, sequences
16412 : * and matviews, even though they have storage, because we don't
16413 : * support altering or dropping columns in them, nor can they be part
16414 : * of inheritance trees.
16415 : */
16416 10166 : if (dopt->binary_upgrade &&
16417 1426 : (tbinfo->relkind == RELKIND_RELATION ||
16418 208 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16419 206 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16420 : {
16421 : bool firstitem;
16422 : bool firstitem_extra;
16423 :
16424 : /*
16425 : * Drop any dropped columns. Merge the pg_attribute manipulations
16426 : * into a single SQL command, so that we don't cause repeated
16427 : * relcache flushes on the target table. Otherwise we risk O(N^2)
16428 : * relcache bloat while dropping N columns.
16429 : */
16430 1392 : resetPQExpBuffer(extra);
16431 1392 : firstitem = true;
16432 7232 : for (j = 0; j < tbinfo->numatts; j++)
16433 : {
16434 5840 : if (tbinfo->attisdropped[j])
16435 : {
16436 158 : if (firstitem)
16437 : {
16438 68 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
16439 : "UPDATE pg_catalog.pg_attribute\n"
16440 : "SET attlen = v.dlen, "
16441 : "attalign = v.dalign, "
16442 : "attbyval = false\n"
16443 : "FROM (VALUES ");
16444 68 : firstitem = false;
16445 : }
16446 : else
16447 90 : appendPQExpBufferStr(q, ",\n ");
16448 158 : appendPQExpBufferChar(q, '(');
16449 158 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16450 158 : appendPQExpBuffer(q, ", %d, '%c')",
16451 158 : tbinfo->attlen[j],
16452 158 : tbinfo->attalign[j]);
16453 : /* The ALTER ... DROP COLUMN commands must come after */
16454 158 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
16455 : foreign, qualrelname);
16456 158 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
16457 158 : fmtId(tbinfo->attnames[j]));
16458 : }
16459 : }
16460 1392 : if (!firstitem)
16461 : {
16462 68 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
16463 : "WHERE attrelid = ");
16464 68 : appendStringLiteralAH(q, qualrelname, fout);
16465 68 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16466 : " AND attname = v.dname;\n");
16467 : /* Now we can issue the actual DROP COLUMN commands */
16468 68 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16469 : }
16470 :
16471 : /*
16472 : * Fix up inherited columns. As above, do the pg_attribute
16473 : * manipulations in a single SQL command.
16474 : */
16475 1392 : firstitem = true;
16476 7232 : for (j = 0; j < tbinfo->numatts; j++)
16477 : {
16478 5840 : if (!tbinfo->attisdropped[j] &&
16479 5682 : !tbinfo->attislocal[j])
16480 : {
16481 1122 : if (firstitem)
16482 : {
16483 488 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
16484 488 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16485 : "SET attislocal = false\n"
16486 : "WHERE attrelid = ");
16487 488 : appendStringLiteralAH(q, qualrelname, fout);
16488 488 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16489 : " AND attname IN (");
16490 488 : firstitem = false;
16491 : }
16492 : else
16493 634 : appendPQExpBufferStr(q, ", ");
16494 1122 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16495 : }
16496 : }
16497 1392 : if (!firstitem)
16498 488 : appendPQExpBufferStr(q, ");\n");
16499 :
16500 : /*
16501 : * Fix up not-null constraints that come from inheritance. As
16502 : * above, do the pg_constraint manipulations in a single SQL
16503 : * command. (Actually, two in special cases, if we're doing an
16504 : * upgrade from < 18).
16505 : */
16506 1392 : firstitem = true;
16507 1392 : firstitem_extra = true;
16508 1392 : resetPQExpBuffer(extra);
16509 7232 : for (j = 0; j < tbinfo->numatts; j++)
16510 : {
16511 : /*
16512 : * If a not-null constraint comes from inheritance, reset
16513 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
16514 : * below. Special hack: in versions < 18, columns with no
16515 : * local definition need their constraint to be matched by
16516 : * column number in conkeys instead of by constraint name,
16517 : * because the latter is not available. (We distinguish the
16518 : * case because the constraint name is the empty string.)
16519 : */
16520 5840 : if (tbinfo->notnull_constrs[j] != NULL &&
16521 480 : !tbinfo->notnull_islocal[j])
16522 : {
16523 144 : if (tbinfo->notnull_constrs[j][0] != '\0')
16524 : {
16525 120 : if (firstitem)
16526 : {
16527 104 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16528 : "SET conislocal = false\n"
16529 : "WHERE contype = 'n' AND conrelid = ");
16530 104 : appendStringLiteralAH(q, qualrelname, fout);
16531 104 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16532 : "conname IN (");
16533 104 : firstitem = false;
16534 : }
16535 : else
16536 16 : appendPQExpBufferStr(q, ", ");
16537 120 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16538 : }
16539 : else
16540 : {
16541 24 : if (firstitem_extra)
16542 : {
16543 24 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16544 : "SET conislocal = false\n"
16545 : "WHERE contype = 'n' AND conrelid = ");
16546 24 : appendStringLiteralAH(extra, qualrelname, fout);
16547 24 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
16548 : "conkey IN (");
16549 24 : firstitem_extra = false;
16550 : }
16551 : else
16552 0 : appendPQExpBufferStr(extra, ", ");
16553 24 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
16554 : }
16555 : }
16556 : }
16557 1392 : if (!firstitem)
16558 104 : appendPQExpBufferStr(q, ");\n");
16559 1392 : if (!firstitem_extra)
16560 24 : appendPQExpBufferStr(extra, ");\n");
16561 :
16562 1392 : if (extra->len > 0)
16563 24 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16564 :
16565 : /*
16566 : * Add inherited CHECK constraints, if any.
16567 : *
16568 : * For partitions, they were already dumped, and conislocal
16569 : * doesn't need fixing.
16570 : *
16571 : * As above, issue only one direct manipulation of pg_constraint.
16572 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
16573 : * commands into one as well, refrain for now due to concern about
16574 : * possible backend memory bloat if there are many such
16575 : * constraints.
16576 : */
16577 1392 : resetPQExpBuffer(extra);
16578 1392 : firstitem = true;
16579 1508 : for (k = 0; k < tbinfo->ncheck; k++)
16580 : {
16581 116 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16582 :
16583 116 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
16584 112 : continue;
16585 :
16586 4 : if (firstitem)
16587 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
16588 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16589 : foreign, qualrelname,
16590 4 : fmtId(constr->dobj.name),
16591 : constr->condef);
16592 : /* Update pg_constraint after all the ALTER TABLEs */
16593 4 : if (firstitem)
16594 : {
16595 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16596 : "SET conislocal = false\n"
16597 : "WHERE contype = 'c' AND conrelid = ");
16598 4 : appendStringLiteralAH(extra, qualrelname, fout);
16599 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
16600 4 : appendPQExpBufferStr(extra, " AND conname IN (");
16601 4 : firstitem = false;
16602 : }
16603 : else
16604 0 : appendPQExpBufferStr(extra, ", ");
16605 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
16606 : }
16607 1392 : if (!firstitem)
16608 : {
16609 4 : appendPQExpBufferStr(extra, ");\n");
16610 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
16611 : }
16612 :
16613 1392 : if (numParents > 0 && !tbinfo->ispartition)
16614 : {
16615 98 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16616 210 : for (k = 0; k < numParents; k++)
16617 : {
16618 112 : TableInfo *parentRel = parents[k];
16619 :
16620 112 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
16621 : qualrelname,
16622 112 : fmtQualifiedDumpable(parentRel));
16623 : }
16624 : }
16625 :
16626 1392 : if (OidIsValid(tbinfo->reloftype))
16627 : {
16628 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
16629 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
16630 : qualrelname,
16631 : getFormattedTypeName(fout, tbinfo->reloftype,
16632 : zeroIsError));
16633 : }
16634 : }
16635 :
16636 : /*
16637 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
16638 : * relminmxid of all vacuumable relations. (While vacuum.c processes
16639 : * TOAST tables semi-independently, here we see them only as children
16640 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16641 : * child toast table is handled below.)
16642 : */
16643 10166 : if (dopt->binary_upgrade &&
16644 1426 : (tbinfo->relkind == RELKIND_RELATION ||
16645 208 : tbinfo->relkind == RELKIND_MATVIEW))
16646 : {
16647 1252 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
16648 1252 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16649 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16650 : "WHERE oid = ",
16651 : tbinfo->frozenxid, tbinfo->minmxid);
16652 1252 : appendStringLiteralAH(q, qualrelname, fout);
16653 1252 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16654 :
16655 1252 : if (tbinfo->toast_oid)
16656 : {
16657 : /*
16658 : * The toast table will have the same OID at restore, so we
16659 : * can safely target it by OID.
16660 : */
16661 548 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
16662 548 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16663 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16664 : "WHERE oid = '%u';\n",
16665 : tbinfo->toast_frozenxid,
16666 : tbinfo->toast_minmxid, tbinfo->toast_oid);
16667 : }
16668 : }
16669 :
16670 : /*
16671 : * In binary_upgrade mode, restore matviews' populated status by
16672 : * poking pg_class directly. This is pretty ugly, but we can't use
16673 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16674 : * matview is not populated even though this matview is; in any case,
16675 : * we want to transfer the matview's heap storage, not run REFRESH.
16676 : */
16677 10166 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16678 34 : tbinfo->relispopulated)
16679 : {
16680 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16681 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16682 : "SET relispopulated = 't'\n"
16683 : "WHERE oid = ");
16684 30 : appendStringLiteralAH(q, qualrelname, fout);
16685 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16686 : }
16687 :
16688 : /*
16689 : * Dump additional per-column properties that we can't handle in the
16690 : * main CREATE TABLE command.
16691 : */
16692 48220 : for (j = 0; j < tbinfo->numatts; j++)
16693 : {
16694 : /* None of this applies to dropped columns */
16695 38054 : if (tbinfo->attisdropped[j])
16696 846 : continue;
16697 :
16698 : /*
16699 : * Dump per-column statistics information. We only issue an ALTER
16700 : * TABLE statement if the attstattarget entry for this column is
16701 : * not the default value.
16702 : */
16703 37208 : if (tbinfo->attstattarget[j] >= 0)
16704 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
16705 : foreign, qualrelname,
16706 66 : fmtId(tbinfo->attnames[j]),
16707 66 : tbinfo->attstattarget[j]);
16708 :
16709 : /*
16710 : * Dump per-column storage information. The statement is only
16711 : * dumped if the storage has been changed from the type's default.
16712 : */
16713 37208 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16714 : {
16715 162 : switch (tbinfo->attstorage[j])
16716 : {
16717 20 : case TYPSTORAGE_PLAIN:
16718 20 : storage = "PLAIN";
16719 20 : break;
16720 76 : case TYPSTORAGE_EXTERNAL:
16721 76 : storage = "EXTERNAL";
16722 76 : break;
16723 0 : case TYPSTORAGE_EXTENDED:
16724 0 : storage = "EXTENDED";
16725 0 : break;
16726 66 : case TYPSTORAGE_MAIN:
16727 66 : storage = "MAIN";
16728 66 : break;
16729 0 : default:
16730 0 : storage = NULL;
16731 : }
16732 :
16733 : /*
16734 : * Only dump the statement if it's a storage type we recognize
16735 : */
16736 162 : if (storage != NULL)
16737 162 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
16738 : foreign, qualrelname,
16739 162 : fmtId(tbinfo->attnames[j]),
16740 : storage);
16741 : }
16742 :
16743 : /*
16744 : * Dump per-column compression, if it's been set.
16745 : */
16746 37208 : if (!dopt->no_toast_compression)
16747 : {
16748 : const char *cmname;
16749 :
16750 37042 : switch (tbinfo->attcompression[j])
16751 : {
16752 114 : case 'p':
16753 114 : cmname = "pglz";
16754 114 : break;
16755 188 : case 'l':
16756 188 : cmname = "lz4";
16757 188 : break;
16758 36740 : default:
16759 36740 : cmname = NULL;
16760 36740 : break;
16761 : }
16762 :
16763 37042 : if (cmname != NULL)
16764 302 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
16765 : foreign, qualrelname,
16766 302 : fmtId(tbinfo->attnames[j]),
16767 : cmname);
16768 : }
16769 :
16770 : /*
16771 : * Dump per-column attributes.
16772 : */
16773 37208 : if (tbinfo->attoptions[j][0] != '\0')
16774 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
16775 : foreign, qualrelname,
16776 66 : fmtId(tbinfo->attnames[j]),
16777 66 : tbinfo->attoptions[j]);
16778 :
16779 : /*
16780 : * Dump per-column fdw options.
16781 : */
16782 37208 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16783 70 : tbinfo->attfdwoptions[j][0] != '\0')
16784 66 : appendPQExpBuffer(q,
16785 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
16786 : " %s\n"
16787 : ");\n",
16788 : qualrelname,
16789 66 : fmtId(tbinfo->attnames[j]),
16790 66 : tbinfo->attfdwoptions[j]);
16791 : } /* end loop over columns */
16792 :
16793 10166 : free(partkeydef);
16794 10166 : free(ftoptions);
16795 10166 : free(srvname);
16796 : }
16797 :
16798 : /*
16799 : * dump properties we only have ALTER TABLE syntax for
16800 : */
16801 11180 : if ((tbinfo->relkind == RELKIND_RELATION ||
16802 2794 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16803 1758 : tbinfo->relkind == RELKIND_MATVIEW) &&
16804 10096 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16805 : {
16806 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16807 : {
16808 : /* nothing to do, will be set when the index is dumped */
16809 : }
16810 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16811 : {
16812 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16813 : qualrelname);
16814 : }
16815 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16816 : {
16817 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16818 : qualrelname);
16819 : }
16820 : }
16821 :
16822 11180 : if (tbinfo->forcerowsec)
16823 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16824 : qualrelname);
16825 :
16826 11180 : if (dopt->binary_upgrade)
16827 1524 : binary_upgrade_extension_member(q, &tbinfo->dobj,
16828 : reltypename, qrelname,
16829 1524 : tbinfo->dobj.namespace->dobj.name);
16830 :
16831 11180 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16832 : {
16833 11180 : char *tablespace = NULL;
16834 11180 : char *tableam = NULL;
16835 :
16836 : /*
16837 : * _selectTablespace() relies on tablespace-enabled objects in the
16838 : * default tablespace to have a tablespace of "" (empty string) versus
16839 : * non-tablespace-enabled objects to have a tablespace of NULL.
16840 : * getTables() sets tbinfo->reltablespace to "" for the default
16841 : * tablespace (not NULL).
16842 : */
16843 11180 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
16844 10096 : tablespace = tbinfo->reltablespace;
16845 :
16846 11180 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
16847 2120 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16848 10096 : tableam = tbinfo->amname;
16849 :
16850 11180 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16851 11180 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16852 : .namespace = tbinfo->dobj.namespace->dobj.name,
16853 : .tablespace = tablespace,
16854 : .tableam = tableam,
16855 : .relkind = tbinfo->relkind,
16856 : .owner = tbinfo->rolname,
16857 : .description = reltypename,
16858 : .section = tbinfo->postponed_def ?
16859 : SECTION_POST_DATA : SECTION_PRE_DATA,
16860 : .createStmt = q->data,
16861 : .dropStmt = delq->data));
16862 : }
16863 :
16864 : /* Dump Table Comments */
16865 11180 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16866 152 : dumpTableComment(fout, tbinfo, reltypename);
16867 :
16868 : /* Dump Table Security Labels */
16869 11180 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16870 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
16871 :
16872 : /* Dump comments on inlined table constraints */
16873 12306 : for (j = 0; j < tbinfo->ncheck; j++)
16874 : {
16875 1126 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16876 :
16877 1126 : if (constr->separate || !constr->conislocal)
16878 488 : continue;
16879 :
16880 638 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
16881 76 : dumpTableConstraintComment(fout, constr);
16882 : }
16883 :
16884 11180 : destroyPQExpBuffer(q);
16885 11180 : destroyPQExpBuffer(delq);
16886 11180 : destroyPQExpBuffer(extra);
16887 11180 : free(qrelname);
16888 11180 : free(qualrelname);
16889 11180 : }
16890 :
16891 : /*
16892 : * dumpTableAttach
16893 : * write to fout the commands to attach a child partition
16894 : *
16895 : * Child partitions are always made by creating them separately
16896 : * and then using ATTACH PARTITION, rather than using
16897 : * CREATE TABLE ... PARTITION OF. This is important for preserving
16898 : * any possible discrepancy in column layout, to allow assigning the
16899 : * correct tablespace if different, and so that it's possible to restore
16900 : * a partition without restoring its parent. (You'll get an error from
16901 : * the ATTACH PARTITION command, but that can be ignored, or skipped
16902 : * using "pg_restore -L" if you prefer.) The last point motivates
16903 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
16904 : * rather than emitting it within the child partition's ArchiveEntry.
16905 : */
16906 : static void
16907 2496 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
16908 : {
16909 2496 : DumpOptions *dopt = fout->dopt;
16910 : PQExpBuffer q;
16911 : PGresult *res;
16912 : char *partbound;
16913 :
16914 : /* Do nothing if not dumping schema */
16915 2496 : if (!dopt->dumpSchema)
16916 42 : return;
16917 :
16918 2454 : q = createPQExpBuffer();
16919 :
16920 2454 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
16921 : {
16922 : /* Set up query for partbound details */
16923 88 : appendPQExpBufferStr(q,
16924 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
16925 :
16926 88 : appendPQExpBufferStr(q,
16927 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
16928 : "FROM pg_class c "
16929 : "WHERE c.oid = $1");
16930 :
16931 88 : ExecuteSqlStatement(fout, q->data);
16932 :
16933 88 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
16934 : }
16935 :
16936 2454 : printfPQExpBuffer(q,
16937 : "EXECUTE dumpTableAttach('%u')",
16938 2454 : attachinfo->partitionTbl->dobj.catId.oid);
16939 :
16940 2454 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
16941 2454 : partbound = PQgetvalue(res, 0, 0);
16942 :
16943 : /* Perform ALTER TABLE on the parent */
16944 2454 : printfPQExpBuffer(q,
16945 : "ALTER TABLE ONLY %s ",
16946 2454 : fmtQualifiedDumpable(attachinfo->parentTbl));
16947 2454 : appendPQExpBuffer(q,
16948 : "ATTACH PARTITION %s %s;\n",
16949 2454 : fmtQualifiedDumpable(attachinfo->partitionTbl),
16950 : partbound);
16951 :
16952 : /*
16953 : * There is no point in creating a drop query as the drop is done by table
16954 : * drop. (If you think to change this, see also _printTocEntry().)
16955 : * Although this object doesn't really have ownership as such, set the
16956 : * owner field anyway to ensure that the command is run by the correct
16957 : * role at restore time.
16958 : */
16959 2454 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16960 2454 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16961 : .namespace = attachinfo->dobj.namespace->dobj.name,
16962 : .owner = attachinfo->partitionTbl->rolname,
16963 : .description = "TABLE ATTACH",
16964 : .section = SECTION_PRE_DATA,
16965 : .createStmt = q->data));
16966 :
16967 2454 : PQclear(res);
16968 2454 : destroyPQExpBuffer(q);
16969 : }
16970 :
16971 : /*
16972 : * dumpAttrDef --- dump an attribute's default-value declaration
16973 : */
16974 : static void
16975 1520 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
16976 : {
16977 1520 : DumpOptions *dopt = fout->dopt;
16978 1520 : TableInfo *tbinfo = adinfo->adtable;
16979 1520 : int adnum = adinfo->adnum;
16980 : PQExpBuffer q;
16981 : PQExpBuffer delq;
16982 : char *qualrelname;
16983 : char *tag;
16984 : char *foreign;
16985 :
16986 : /* Do nothing if not dumping schema */
16987 1520 : if (!dopt->dumpSchema)
16988 0 : return;
16989 :
16990 : /* Skip if not "separate"; it was dumped in the table's definition */
16991 1520 : if (!adinfo->separate)
16992 1232 : return;
16993 :
16994 288 : q = createPQExpBuffer();
16995 288 : delq = createPQExpBuffer();
16996 :
16997 288 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16998 :
16999 288 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17000 :
17001 288 : appendPQExpBuffer(q,
17002 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17003 288 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17004 : adinfo->adef_expr);
17005 :
17006 288 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17007 : foreign, qualrelname,
17008 288 : fmtId(tbinfo->attnames[adnum - 1]));
17009 :
17010 288 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17011 :
17012 288 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17013 288 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17014 288 : ARCHIVE_OPTS(.tag = tag,
17015 : .namespace = tbinfo->dobj.namespace->dobj.name,
17016 : .owner = tbinfo->rolname,
17017 : .description = "DEFAULT",
17018 : .section = SECTION_PRE_DATA,
17019 : .createStmt = q->data,
17020 : .dropStmt = delq->data));
17021 :
17022 288 : free(tag);
17023 288 : destroyPQExpBuffer(q);
17024 288 : destroyPQExpBuffer(delq);
17025 288 : free(qualrelname);
17026 : }
17027 :
17028 : /*
17029 : * getAttrName: extract the correct name for an attribute
17030 : *
17031 : * The array tblInfo->attnames[] only provides names of user attributes;
17032 : * if a system attribute number is supplied, we have to fake it.
17033 : * We also do a little bit of bounds checking for safety's sake.
17034 : */
17035 : static const char *
17036 3916 : getAttrName(int attrnum, const TableInfo *tblInfo)
17037 : {
17038 3916 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
17039 3916 : return tblInfo->attnames[attrnum - 1];
17040 0 : switch (attrnum)
17041 : {
17042 0 : case SelfItemPointerAttributeNumber:
17043 0 : return "ctid";
17044 0 : case MinTransactionIdAttributeNumber:
17045 0 : return "xmin";
17046 0 : case MinCommandIdAttributeNumber:
17047 0 : return "cmin";
17048 0 : case MaxTransactionIdAttributeNumber:
17049 0 : return "xmax";
17050 0 : case MaxCommandIdAttributeNumber:
17051 0 : return "cmax";
17052 0 : case TableOidAttributeNumber:
17053 0 : return "tableoid";
17054 : }
17055 0 : pg_fatal("invalid column number %d for table \"%s\"",
17056 : attrnum, tblInfo->dobj.name);
17057 : return NULL; /* keep compiler quiet */
17058 : }
17059 :
17060 : /*
17061 : * dumpIndex
17062 : * write out to fout a user-defined index
17063 : */
17064 : static void
17065 4740 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17066 : {
17067 4740 : DumpOptions *dopt = fout->dopt;
17068 4740 : TableInfo *tbinfo = indxinfo->indextable;
17069 4740 : bool is_constraint = (indxinfo->indexconstraint != 0);
17070 : PQExpBuffer q;
17071 : PQExpBuffer delq;
17072 : char *qindxname;
17073 : char *qqindxname;
17074 :
17075 : /* Do nothing if not dumping schema */
17076 4740 : if (!dopt->dumpSchema)
17077 114 : return;
17078 :
17079 4626 : q = createPQExpBuffer();
17080 4626 : delq = createPQExpBuffer();
17081 :
17082 4626 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17083 4626 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17084 :
17085 : /*
17086 : * If there's an associated constraint, don't dump the index per se, but
17087 : * do dump any comment for it. (This is safe because dependency ordering
17088 : * will have ensured the constraint is emitted first.) Note that the
17089 : * emitted comment has to be shown as depending on the constraint, not the
17090 : * index, in such cases.
17091 : */
17092 4626 : if (!is_constraint)
17093 : {
17094 1958 : char *indstatcols = indxinfo->indstatcols;
17095 1958 : char *indstatvals = indxinfo->indstatvals;
17096 1958 : char **indstatcolsarray = NULL;
17097 1958 : char **indstatvalsarray = NULL;
17098 1958 : int nstatcols = 0;
17099 1958 : int nstatvals = 0;
17100 :
17101 1958 : if (dopt->binary_upgrade)
17102 298 : binary_upgrade_set_pg_class_oids(fout, q,
17103 : indxinfo->dobj.catId.oid);
17104 :
17105 : /* Plain secondary index */
17106 1958 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17107 :
17108 : /*
17109 : * Append ALTER TABLE commands as needed to set properties that we
17110 : * only have ALTER TABLE syntax for. Keep this in sync with the
17111 : * similar code in dumpConstraint!
17112 : */
17113 :
17114 : /* If the index is clustered, we need to record that. */
17115 1958 : if (indxinfo->indisclustered)
17116 : {
17117 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17118 0 : fmtQualifiedDumpable(tbinfo));
17119 : /* index name is not qualified in this syntax */
17120 0 : appendPQExpBuffer(q, " ON %s;\n",
17121 : qindxname);
17122 : }
17123 :
17124 : /*
17125 : * If the index has any statistics on some of its columns, generate
17126 : * the associated ALTER INDEX queries.
17127 : */
17128 1958 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17129 : {
17130 : int j;
17131 :
17132 66 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17133 0 : pg_fatal("could not parse index statistic columns");
17134 66 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17135 0 : pg_fatal("could not parse index statistic values");
17136 66 : if (nstatcols != nstatvals)
17137 0 : pg_fatal("mismatched number of columns and values for index statistics");
17138 :
17139 198 : for (j = 0; j < nstatcols; j++)
17140 : {
17141 132 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17142 :
17143 : /*
17144 : * Note that this is a column number, so no quotes should be
17145 : * used.
17146 : */
17147 132 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
17148 132 : indstatcolsarray[j]);
17149 132 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17150 132 : indstatvalsarray[j]);
17151 : }
17152 : }
17153 :
17154 : /* Indexes can depend on extensions */
17155 1958 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17156 : "pg_catalog.pg_class",
17157 : "INDEX", qqindxname);
17158 :
17159 : /* If the index defines identity, we need to record that. */
17160 1958 : if (indxinfo->indisreplident)
17161 : {
17162 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17163 0 : fmtQualifiedDumpable(tbinfo));
17164 : /* index name is not qualified in this syntax */
17165 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17166 : qindxname);
17167 : }
17168 :
17169 1958 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17170 :
17171 1958 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17172 1958 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17173 1958 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17174 : .namespace = tbinfo->dobj.namespace->dobj.name,
17175 : .tablespace = indxinfo->tablespace,
17176 : .owner = tbinfo->rolname,
17177 : .description = "INDEX",
17178 : .section = SECTION_POST_DATA,
17179 : .createStmt = q->data,
17180 : .dropStmt = delq->data));
17181 :
17182 1958 : free(indstatcolsarray);
17183 1958 : free(indstatvalsarray);
17184 : }
17185 :
17186 : /* Dump Index Comments */
17187 4626 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17188 30 : dumpComment(fout, "INDEX", qindxname,
17189 30 : tbinfo->dobj.namespace->dobj.name,
17190 : tbinfo->rolname,
17191 : indxinfo->dobj.catId, 0,
17192 : is_constraint ? indxinfo->indexconstraint :
17193 : indxinfo->dobj.dumpId);
17194 :
17195 4626 : destroyPQExpBuffer(q);
17196 4626 : destroyPQExpBuffer(delq);
17197 4626 : free(qindxname);
17198 4626 : free(qqindxname);
17199 : }
17200 :
17201 : /*
17202 : * dumpIndexAttach
17203 : * write out to fout a partitioned-index attachment clause
17204 : */
17205 : static void
17206 1096 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17207 : {
17208 : /* Do nothing if not dumping schema */
17209 1096 : if (!fout->dopt->dumpSchema)
17210 48 : return;
17211 :
17212 1048 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17213 : {
17214 1048 : PQExpBuffer q = createPQExpBuffer();
17215 :
17216 1048 : appendPQExpBuffer(q, "ALTER INDEX %s ",
17217 1048 : fmtQualifiedDumpable(attachinfo->parentIdx));
17218 1048 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17219 1048 : fmtQualifiedDumpable(attachinfo->partitionIdx));
17220 :
17221 : /*
17222 : * There is no point in creating a drop query as the drop is done by
17223 : * index drop. (If you think to change this, see also
17224 : * _printTocEntry().) Although this object doesn't really have
17225 : * ownership as such, set the owner field anyway to ensure that the
17226 : * command is run by the correct role at restore time.
17227 : */
17228 1048 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17229 1048 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17230 : .namespace = attachinfo->dobj.namespace->dobj.name,
17231 : .owner = attachinfo->parentIdx->indextable->rolname,
17232 : .description = "INDEX ATTACH",
17233 : .section = SECTION_POST_DATA,
17234 : .createStmt = q->data));
17235 :
17236 1048 : destroyPQExpBuffer(q);
17237 : }
17238 : }
17239 :
17240 : /*
17241 : * dumpStatisticsExt
17242 : * write out to fout an extended statistics object
17243 : */
17244 : static void
17245 254 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17246 : {
17247 254 : DumpOptions *dopt = fout->dopt;
17248 : PQExpBuffer q;
17249 : PQExpBuffer delq;
17250 : PQExpBuffer query;
17251 : char *qstatsextname;
17252 : PGresult *res;
17253 : char *stxdef;
17254 :
17255 : /* Do nothing if not dumping schema */
17256 254 : if (!dopt->dumpSchema)
17257 18 : return;
17258 :
17259 236 : q = createPQExpBuffer();
17260 236 : delq = createPQExpBuffer();
17261 236 : query = createPQExpBuffer();
17262 :
17263 236 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17264 :
17265 236 : appendPQExpBuffer(query, "SELECT "
17266 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17267 : statsextinfo->dobj.catId.oid);
17268 :
17269 236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17270 :
17271 236 : stxdef = PQgetvalue(res, 0, 0);
17272 :
17273 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17274 236 : appendPQExpBuffer(q, "%s;\n", stxdef);
17275 :
17276 : /*
17277 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17278 : * for this statistics object is not the default value.
17279 : */
17280 236 : if (statsextinfo->stattarget >= 0)
17281 : {
17282 66 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17283 66 : fmtQualifiedDumpable(statsextinfo));
17284 66 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17285 : statsextinfo->stattarget);
17286 : }
17287 :
17288 236 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17289 236 : fmtQualifiedDumpable(statsextinfo));
17290 :
17291 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17292 236 : ArchiveEntry(fout, statsextinfo->dobj.catId,
17293 : statsextinfo->dobj.dumpId,
17294 236 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17295 : .namespace = statsextinfo->dobj.namespace->dobj.name,
17296 : .owner = statsextinfo->rolname,
17297 : .description = "STATISTICS",
17298 : .section = SECTION_POST_DATA,
17299 : .createStmt = q->data,
17300 : .dropStmt = delq->data));
17301 :
17302 : /* Dump Statistics Comments */
17303 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17304 0 : dumpComment(fout, "STATISTICS", qstatsextname,
17305 0 : statsextinfo->dobj.namespace->dobj.name,
17306 : statsextinfo->rolname,
17307 : statsextinfo->dobj.catId, 0,
17308 : statsextinfo->dobj.dumpId);
17309 :
17310 236 : PQclear(res);
17311 236 : destroyPQExpBuffer(q);
17312 236 : destroyPQExpBuffer(delq);
17313 236 : destroyPQExpBuffer(query);
17314 236 : free(qstatsextname);
17315 : }
17316 :
17317 : /*
17318 : * dumpConstraint
17319 : * write out to fout a user-defined constraint
17320 : */
17321 : static void
17322 4396 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17323 : {
17324 4396 : DumpOptions *dopt = fout->dopt;
17325 4396 : TableInfo *tbinfo = coninfo->contable;
17326 : PQExpBuffer q;
17327 : PQExpBuffer delq;
17328 4396 : char *tag = NULL;
17329 : char *foreign;
17330 :
17331 : /* Do nothing if not dumping schema */
17332 4396 : if (!dopt->dumpSchema)
17333 94 : return;
17334 :
17335 4302 : q = createPQExpBuffer();
17336 4302 : delq = createPQExpBuffer();
17337 :
17338 8428 : foreign = tbinfo &&
17339 4302 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17340 :
17341 4302 : if (coninfo->contype == 'p' ||
17342 2094 : coninfo->contype == 'u' ||
17343 1654 : coninfo->contype == 'x')
17344 2668 : {
17345 : /* Index-related constraint */
17346 : IndxInfo *indxinfo;
17347 : int k;
17348 :
17349 2668 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17350 :
17351 2668 : if (indxinfo == NULL)
17352 0 : pg_fatal("missing index for constraint \"%s\"",
17353 : coninfo->dobj.name);
17354 :
17355 2668 : if (dopt->binary_upgrade)
17356 252 : binary_upgrade_set_pg_class_oids(fout, q,
17357 : indxinfo->dobj.catId.oid);
17358 :
17359 2668 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17360 2668 : fmtQualifiedDumpable(tbinfo));
17361 2668 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17362 2668 : fmtId(coninfo->dobj.name));
17363 :
17364 2668 : if (coninfo->condef)
17365 : {
17366 : /* pg_get_constraintdef should have provided everything */
17367 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17368 : }
17369 : else
17370 : {
17371 2648 : appendPQExpBufferStr(q,
17372 2648 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17373 :
17374 : /*
17375 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17376 : * indexes. Being able to create this was fixed, but we need to
17377 : * make the index distinct in order to be able to restore the
17378 : * dump.
17379 : */
17380 2648 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17381 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17382 2648 : appendPQExpBufferStr(q, " (");
17383 6484 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
17384 : {
17385 3836 : int indkey = (int) indxinfo->indkeys[k];
17386 : const char *attname;
17387 :
17388 3836 : if (indkey == InvalidAttrNumber)
17389 0 : break;
17390 3836 : attname = getAttrName(indkey, tbinfo);
17391 :
17392 3836 : appendPQExpBuffer(q, "%s%s",
17393 : (k == 0) ? "" : ", ",
17394 : fmtId(attname));
17395 : }
17396 2648 : if (coninfo->conperiod)
17397 212 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17398 :
17399 2648 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17400 40 : appendPQExpBufferStr(q, ") INCLUDE (");
17401 :
17402 2728 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17403 : {
17404 80 : int indkey = (int) indxinfo->indkeys[k];
17405 : const char *attname;
17406 :
17407 80 : if (indkey == InvalidAttrNumber)
17408 0 : break;
17409 80 : attname = getAttrName(indkey, tbinfo);
17410 :
17411 160 : appendPQExpBuffer(q, "%s%s",
17412 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
17413 : fmtId(attname));
17414 : }
17415 :
17416 2648 : appendPQExpBufferChar(q, ')');
17417 :
17418 2648 : if (nonemptyReloptions(indxinfo->indreloptions))
17419 : {
17420 0 : appendPQExpBufferStr(q, " WITH (");
17421 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17422 0 : appendPQExpBufferChar(q, ')');
17423 : }
17424 :
17425 2648 : if (coninfo->condeferrable)
17426 : {
17427 50 : appendPQExpBufferStr(q, " DEFERRABLE");
17428 50 : if (coninfo->condeferred)
17429 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17430 : }
17431 :
17432 2648 : appendPQExpBufferStr(q, ";\n");
17433 : }
17434 :
17435 : /*
17436 : * Append ALTER TABLE commands as needed to set properties that we
17437 : * only have ALTER TABLE syntax for. Keep this in sync with the
17438 : * similar code in dumpIndex!
17439 : */
17440 :
17441 : /* If the index is clustered, we need to record that. */
17442 2668 : if (indxinfo->indisclustered)
17443 : {
17444 66 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17445 66 : fmtQualifiedDumpable(tbinfo));
17446 : /* index name is not qualified in this syntax */
17447 66 : appendPQExpBuffer(q, " ON %s;\n",
17448 66 : fmtId(indxinfo->dobj.name));
17449 : }
17450 :
17451 : /* If the index defines identity, we need to record that. */
17452 2668 : if (indxinfo->indisreplident)
17453 : {
17454 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17455 0 : fmtQualifiedDumpable(tbinfo));
17456 : /* index name is not qualified in this syntax */
17457 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17458 0 : fmtId(indxinfo->dobj.name));
17459 : }
17460 :
17461 : /* Indexes can depend on extensions */
17462 2668 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17463 : "pg_catalog.pg_class", "INDEX",
17464 2668 : fmtQualifiedDumpable(indxinfo));
17465 :
17466 2668 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17467 2668 : fmtQualifiedDumpable(tbinfo));
17468 2668 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17469 2668 : fmtId(coninfo->dobj.name));
17470 :
17471 2668 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17472 :
17473 2668 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17474 2668 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17475 2668 : ARCHIVE_OPTS(.tag = tag,
17476 : .namespace = tbinfo->dobj.namespace->dobj.name,
17477 : .tablespace = indxinfo->tablespace,
17478 : .owner = tbinfo->rolname,
17479 : .description = "CONSTRAINT",
17480 : .section = SECTION_POST_DATA,
17481 : .createStmt = q->data,
17482 : .dropStmt = delq->data));
17483 : }
17484 1634 : else if (coninfo->contype == 'f')
17485 : {
17486 : char *only;
17487 :
17488 : /*
17489 : * Foreign keys on partitioned tables are always declared as
17490 : * inheriting to partitions; for all other cases, emit them as
17491 : * applying ONLY directly to the named table, because that's how they
17492 : * work for regular inherited tables.
17493 : */
17494 332 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17495 :
17496 : /*
17497 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17498 : * current table data is not processed
17499 : */
17500 332 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17501 332 : only, fmtQualifiedDumpable(tbinfo));
17502 332 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17503 332 : fmtId(coninfo->dobj.name),
17504 : coninfo->condef);
17505 :
17506 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17507 332 : only, fmtQualifiedDumpable(tbinfo));
17508 332 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17509 332 : fmtId(coninfo->dobj.name));
17510 :
17511 332 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17512 :
17513 332 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17514 332 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17515 332 : ARCHIVE_OPTS(.tag = tag,
17516 : .namespace = tbinfo->dobj.namespace->dobj.name,
17517 : .owner = tbinfo->rolname,
17518 : .description = "FK CONSTRAINT",
17519 : .section = SECTION_POST_DATA,
17520 : .createStmt = q->data,
17521 : .dropStmt = delq->data));
17522 : }
17523 1302 : else if (coninfo->contype == 'c' && tbinfo)
17524 : {
17525 : /* CHECK constraint on a table */
17526 :
17527 : /* Ignore if not to be dumped separately, or if it was inherited */
17528 1126 : if (coninfo->separate && coninfo->conislocal)
17529 : {
17530 : /* not ONLY since we want it to propagate to children */
17531 80 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17532 80 : fmtQualifiedDumpable(tbinfo));
17533 80 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17534 80 : fmtId(coninfo->dobj.name),
17535 : coninfo->condef);
17536 :
17537 80 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17538 80 : fmtQualifiedDumpable(tbinfo));
17539 80 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17540 80 : fmtId(coninfo->dobj.name));
17541 :
17542 80 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17543 :
17544 80 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17545 80 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17546 80 : ARCHIVE_OPTS(.tag = tag,
17547 : .namespace = tbinfo->dobj.namespace->dobj.name,
17548 : .owner = tbinfo->rolname,
17549 : .description = "CHECK CONSTRAINT",
17550 : .section = SECTION_POST_DATA,
17551 : .createStmt = q->data,
17552 : .dropStmt = delq->data));
17553 : }
17554 : }
17555 176 : else if (coninfo->contype == 'c' && tbinfo == NULL)
17556 176 : {
17557 : /* CHECK constraint on a domain */
17558 176 : TypeInfo *tyinfo = coninfo->condomain;
17559 :
17560 : /* Ignore if not to be dumped separately */
17561 176 : if (coninfo->separate)
17562 : {
17563 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17564 0 : fmtQualifiedDumpable(tyinfo));
17565 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17566 0 : fmtId(coninfo->dobj.name),
17567 : coninfo->condef);
17568 :
17569 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17570 0 : fmtQualifiedDumpable(tyinfo));
17571 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17572 0 : fmtId(coninfo->dobj.name));
17573 :
17574 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17575 :
17576 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17577 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17578 0 : ARCHIVE_OPTS(.tag = tag,
17579 : .namespace = tyinfo->dobj.namespace->dobj.name,
17580 : .owner = tyinfo->rolname,
17581 : .description = "CHECK CONSTRAINT",
17582 : .section = SECTION_POST_DATA,
17583 : .createStmt = q->data,
17584 : .dropStmt = delq->data));
17585 : }
17586 : }
17587 : else
17588 : {
17589 0 : pg_fatal("unrecognized constraint type: %c",
17590 : coninfo->contype);
17591 : }
17592 :
17593 : /* Dump Constraint Comments --- only works for table constraints */
17594 4302 : if (tbinfo && coninfo->separate &&
17595 3130 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17596 20 : dumpTableConstraintComment(fout, coninfo);
17597 :
17598 4302 : free(tag);
17599 4302 : destroyPQExpBuffer(q);
17600 4302 : destroyPQExpBuffer(delq);
17601 : }
17602 :
17603 : /*
17604 : * dumpTableConstraintComment --- dump a constraint's comment if any
17605 : *
17606 : * This is split out because we need the function in two different places
17607 : * depending on whether the constraint is dumped as part of CREATE TABLE
17608 : * or as a separate ALTER command.
17609 : */
17610 : static void
17611 96 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17612 : {
17613 96 : TableInfo *tbinfo = coninfo->contable;
17614 96 : PQExpBuffer conprefix = createPQExpBuffer();
17615 : char *qtabname;
17616 :
17617 96 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17618 :
17619 96 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
17620 96 : fmtId(coninfo->dobj.name));
17621 :
17622 96 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17623 96 : dumpComment(fout, conprefix->data, qtabname,
17624 96 : tbinfo->dobj.namespace->dobj.name,
17625 : tbinfo->rolname,
17626 : coninfo->dobj.catId, 0,
17627 96 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
17628 :
17629 96 : destroyPQExpBuffer(conprefix);
17630 96 : free(qtabname);
17631 96 : }
17632 :
17633 : static inline SeqType
17634 1184 : parse_sequence_type(const char *name)
17635 : {
17636 2656 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
17637 : {
17638 2656 : if (strcmp(SeqTypeNames[i], name) == 0)
17639 1184 : return (SeqType) i;
17640 : }
17641 :
17642 0 : pg_fatal("unrecognized sequence type: %s", name);
17643 : return (SeqType) 0; /* keep compiler quiet */
17644 : }
17645 :
17646 : /*
17647 : * bsearch() comparator for SequenceItem
17648 : */
17649 : static int
17650 5332 : SequenceItemCmp(const void *p1, const void *p2)
17651 : {
17652 5332 : SequenceItem v1 = *((const SequenceItem *) p1);
17653 5332 : SequenceItem v2 = *((const SequenceItem *) p2);
17654 :
17655 5332 : return pg_cmp_u32(v1.oid, v2.oid);
17656 : }
17657 :
17658 : /*
17659 : * collectSequences
17660 : *
17661 : * Construct a table of sequence information. This table is sorted by OID for
17662 : * speed in lookup.
17663 : */
17664 : static void
17665 308 : collectSequences(Archive *fout)
17666 : {
17667 : PGresult *res;
17668 : const char *query;
17669 :
17670 : /*
17671 : * Before Postgres 10, sequence metadata is in the sequence itself. With
17672 : * some extra effort, we might be able to use the sorted table for those
17673 : * versions, but for now it seems unlikely to be worth it.
17674 : *
17675 : * Since version 18, we can gather the sequence data in this query with
17676 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
17677 : */
17678 308 : if (fout->remoteVersion < 100000)
17679 0 : return;
17680 308 : else if (fout->remoteVersion < 180000 ||
17681 308 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
17682 4 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
17683 : "seqstart, seqincrement, "
17684 : "seqmax, seqmin, "
17685 : "seqcache, seqcycle, "
17686 : "NULL, 'f' "
17687 : "FROM pg_catalog.pg_sequence "
17688 : "ORDER BY seqrelid";
17689 : else
17690 304 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
17691 : "seqstart, seqincrement, "
17692 : "seqmax, seqmin, "
17693 : "seqcache, seqcycle, "
17694 : "last_value, is_called "
17695 : "FROM pg_catalog.pg_sequence, "
17696 : "pg_get_sequence_data(seqrelid) "
17697 : "ORDER BY seqrelid;";
17698 :
17699 308 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
17700 :
17701 308 : nsequences = PQntuples(res);
17702 308 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
17703 :
17704 1492 : for (int i = 0; i < nsequences; i++)
17705 : {
17706 1184 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
17707 1184 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
17708 1184 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
17709 1184 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
17710 1184 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
17711 1184 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
17712 1184 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
17713 1184 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
17714 1184 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
17715 1184 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
17716 : }
17717 :
17718 308 : PQclear(res);
17719 : }
17720 :
17721 : /*
17722 : * dumpSequence
17723 : * write the declaration (not data) of one user-defined sequence
17724 : */
17725 : static void
17726 696 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
17727 : {
17728 696 : DumpOptions *dopt = fout->dopt;
17729 : SequenceItem *seq;
17730 : bool is_ascending;
17731 : int64 default_minv,
17732 : default_maxv;
17733 696 : PQExpBuffer query = createPQExpBuffer();
17734 696 : PQExpBuffer delqry = createPQExpBuffer();
17735 : char *qseqname;
17736 696 : TableInfo *owning_tab = NULL;
17737 :
17738 696 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
17739 :
17740 : /*
17741 : * For versions >= 10, the sequence information is gathered in a sorted
17742 : * table before any calls to dumpSequence(). See collectSequences() for
17743 : * more information.
17744 : */
17745 696 : if (fout->remoteVersion >= 100000)
17746 : {
17747 696 : SequenceItem key = {0};
17748 :
17749 : Assert(sequences);
17750 :
17751 696 : key.oid = tbinfo->dobj.catId.oid;
17752 696 : seq = bsearch(&key, sequences, nsequences,
17753 : sizeof(SequenceItem), SequenceItemCmp);
17754 : }
17755 : else
17756 : {
17757 : PGresult *res;
17758 :
17759 : /*
17760 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
17761 : *
17762 : * Note: it might seem that 'bigint' potentially needs to be
17763 : * schema-qualified, but actually that's a keyword.
17764 : */
17765 0 : appendPQExpBuffer(query,
17766 : "SELECT 'bigint' AS sequence_type, "
17767 : "start_value, increment_by, max_value, min_value, "
17768 : "cache_value, is_cycled FROM %s",
17769 0 : fmtQualifiedDumpable(tbinfo));
17770 :
17771 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17772 :
17773 0 : if (PQntuples(res) != 1)
17774 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17775 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17776 : PQntuples(res)),
17777 : tbinfo->dobj.name, PQntuples(res));
17778 :
17779 0 : seq = pg_malloc0(sizeof(SequenceItem));
17780 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
17781 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
17782 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
17783 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
17784 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
17785 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
17786 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
17787 :
17788 0 : PQclear(res);
17789 : }
17790 :
17791 : /* Calculate default limits for a sequence of this type */
17792 696 : is_ascending = (seq->incby >= 0);
17793 696 : if (seq->seqtype == SEQTYPE_SMALLINT)
17794 : {
17795 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
17796 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
17797 : }
17798 646 : else if (seq->seqtype == SEQTYPE_INTEGER)
17799 : {
17800 524 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
17801 524 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
17802 : }
17803 122 : else if (seq->seqtype == SEQTYPE_BIGINT)
17804 : {
17805 122 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
17806 122 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
17807 : }
17808 : else
17809 : {
17810 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
17811 : default_minv = default_maxv = 0; /* keep compiler quiet */
17812 : }
17813 :
17814 : /*
17815 : * Identity sequences are not to be dropped separately.
17816 : */
17817 696 : if (!tbinfo->is_identity_sequence)
17818 : {
17819 410 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
17820 410 : fmtQualifiedDumpable(tbinfo));
17821 : }
17822 :
17823 696 : resetPQExpBuffer(query);
17824 :
17825 696 : if (dopt->binary_upgrade)
17826 : {
17827 112 : binary_upgrade_set_pg_class_oids(fout, query,
17828 : tbinfo->dobj.catId.oid);
17829 :
17830 : /*
17831 : * In older PG versions a sequence will have a pg_type entry, but v14
17832 : * and up don't use that, so don't attempt to preserve the type OID.
17833 : */
17834 : }
17835 :
17836 696 : if (tbinfo->is_identity_sequence)
17837 : {
17838 286 : owning_tab = findTableByOid(tbinfo->owning_tab);
17839 :
17840 286 : appendPQExpBuffer(query,
17841 : "ALTER TABLE %s ",
17842 286 : fmtQualifiedDumpable(owning_tab));
17843 286 : appendPQExpBuffer(query,
17844 : "ALTER COLUMN %s ADD GENERATED ",
17845 286 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17846 286 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
17847 206 : appendPQExpBufferStr(query, "ALWAYS");
17848 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
17849 80 : appendPQExpBufferStr(query, "BY DEFAULT");
17850 286 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
17851 286 : fmtQualifiedDumpable(tbinfo));
17852 :
17853 : /*
17854 : * Emit persistence option only if it's different from the owning
17855 : * table's. This avoids using this new syntax unnecessarily.
17856 : */
17857 286 : if (tbinfo->relpersistence != owning_tab->relpersistence)
17858 20 : appendPQExpBuffer(query, " %s\n",
17859 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17860 : "UNLOGGED" : "LOGGED");
17861 : }
17862 : else
17863 : {
17864 410 : appendPQExpBuffer(query,
17865 : "CREATE %sSEQUENCE %s\n",
17866 410 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17867 : "UNLOGGED " : "",
17868 410 : fmtQualifiedDumpable(tbinfo));
17869 :
17870 410 : if (seq->seqtype != SEQTYPE_BIGINT)
17871 318 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
17872 : }
17873 :
17874 696 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
17875 :
17876 696 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
17877 :
17878 696 : if (seq->minv != default_minv)
17879 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
17880 : else
17881 666 : appendPQExpBufferStr(query, " NO MINVALUE\n");
17882 :
17883 696 : if (seq->maxv != default_maxv)
17884 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
17885 : else
17886 666 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
17887 :
17888 696 : appendPQExpBuffer(query,
17889 : " CACHE " INT64_FORMAT "%s",
17890 696 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
17891 :
17892 696 : if (tbinfo->is_identity_sequence)
17893 286 : appendPQExpBufferStr(query, "\n);\n");
17894 : else
17895 410 : appendPQExpBufferStr(query, ";\n");
17896 :
17897 : /* binary_upgrade: no need to clear TOAST table oid */
17898 :
17899 696 : if (dopt->binary_upgrade)
17900 112 : binary_upgrade_extension_member(query, &tbinfo->dobj,
17901 : "SEQUENCE", qseqname,
17902 112 : tbinfo->dobj.namespace->dobj.name);
17903 :
17904 696 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17905 696 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17906 696 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17907 : .namespace = tbinfo->dobj.namespace->dobj.name,
17908 : .owner = tbinfo->rolname,
17909 : .description = "SEQUENCE",
17910 : .section = SECTION_PRE_DATA,
17911 : .createStmt = query->data,
17912 : .dropStmt = delqry->data));
17913 :
17914 : /*
17915 : * If the sequence is owned by a table column, emit the ALTER for it as a
17916 : * separate TOC entry immediately following the sequence's own entry. It's
17917 : * OK to do this rather than using full sorting logic, because the
17918 : * dependency that tells us it's owned will have forced the table to be
17919 : * created first. We can't just include the ALTER in the TOC entry
17920 : * because it will fail if we haven't reassigned the sequence owner to
17921 : * match the table's owner.
17922 : *
17923 : * We need not schema-qualify the table reference because both sequence
17924 : * and table must be in the same schema.
17925 : */
17926 696 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17927 : {
17928 228 : owning_tab = findTableByOid(tbinfo->owning_tab);
17929 :
17930 228 : if (owning_tab == NULL)
17931 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17932 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17933 :
17934 228 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17935 : {
17936 224 : resetPQExpBuffer(query);
17937 224 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17938 224 : fmtQualifiedDumpable(tbinfo));
17939 224 : appendPQExpBuffer(query, " OWNED BY %s",
17940 224 : fmtQualifiedDumpable(owning_tab));
17941 224 : appendPQExpBuffer(query, ".%s;\n",
17942 224 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17943 :
17944 224 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17945 224 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17946 224 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17947 : .namespace = tbinfo->dobj.namespace->dobj.name,
17948 : .owner = tbinfo->rolname,
17949 : .description = "SEQUENCE OWNED BY",
17950 : .section = SECTION_PRE_DATA,
17951 : .createStmt = query->data,
17952 : .deps = &(tbinfo->dobj.dumpId),
17953 : .nDeps = 1));
17954 : }
17955 : }
17956 :
17957 : /* Dump Sequence Comments and Security Labels */
17958 696 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17959 0 : dumpComment(fout, "SEQUENCE", qseqname,
17960 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17961 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17962 :
17963 696 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17964 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
17965 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17966 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17967 :
17968 696 : if (fout->remoteVersion < 100000)
17969 0 : pg_free(seq);
17970 696 : destroyPQExpBuffer(query);
17971 696 : destroyPQExpBuffer(delqry);
17972 696 : free(qseqname);
17973 696 : }
17974 :
17975 : /*
17976 : * dumpSequenceData
17977 : * write the data of one user-defined sequence
17978 : */
17979 : static void
17980 728 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
17981 : {
17982 728 : TableInfo *tbinfo = tdinfo->tdtable;
17983 : int64 last;
17984 : bool called;
17985 728 : PQExpBuffer query = createPQExpBuffer();
17986 :
17987 : /*
17988 : * For versions >= 18, the sequence information is gathered in the sorted
17989 : * array before any calls to dumpSequenceData(). See collectSequences()
17990 : * for more information.
17991 : *
17992 : * For older versions, we have to query the sequence relations
17993 : * individually.
17994 : */
17995 728 : if (fout->remoteVersion < 180000)
17996 : {
17997 : PGresult *res;
17998 :
17999 0 : appendPQExpBuffer(query,
18000 : "SELECT last_value, is_called FROM %s",
18001 0 : fmtQualifiedDumpable(tbinfo));
18002 :
18003 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18004 :
18005 0 : if (PQntuples(res) != 1)
18006 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18007 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18008 : PQntuples(res)),
18009 : tbinfo->dobj.name, PQntuples(res));
18010 :
18011 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18012 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18013 :
18014 0 : PQclear(res);
18015 : }
18016 : else
18017 : {
18018 728 : SequenceItem key = {0};
18019 : SequenceItem *entry;
18020 :
18021 : Assert(sequences);
18022 : Assert(tbinfo->dobj.catId.oid);
18023 :
18024 728 : key.oid = tbinfo->dobj.catId.oid;
18025 728 : entry = bsearch(&key, sequences, nsequences,
18026 : sizeof(SequenceItem), SequenceItemCmp);
18027 :
18028 728 : last = entry->last_value;
18029 728 : called = entry->is_called;
18030 : }
18031 :
18032 728 : resetPQExpBuffer(query);
18033 728 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18034 728 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18035 728 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18036 : last, (called ? "true" : "false"));
18037 :
18038 728 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18039 728 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18040 728 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18041 : .namespace = tbinfo->dobj.namespace->dobj.name,
18042 : .owner = tbinfo->rolname,
18043 : .description = "SEQUENCE SET",
18044 : .section = SECTION_DATA,
18045 : .createStmt = query->data,
18046 : .deps = &(tbinfo->dobj.dumpId),
18047 : .nDeps = 1));
18048 :
18049 728 : destroyPQExpBuffer(query);
18050 728 : }
18051 :
18052 : /*
18053 : * dumpTrigger
18054 : * write the declaration of one user-defined table trigger
18055 : */
18056 : static void
18057 986 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18058 : {
18059 986 : DumpOptions *dopt = fout->dopt;
18060 986 : TableInfo *tbinfo = tginfo->tgtable;
18061 : PQExpBuffer query;
18062 : PQExpBuffer delqry;
18063 : PQExpBuffer trigprefix;
18064 : PQExpBuffer trigidentity;
18065 : char *qtabname;
18066 : char *tag;
18067 :
18068 : /* Do nothing if not dumping schema */
18069 986 : if (!dopt->dumpSchema)
18070 32 : return;
18071 :
18072 954 : query = createPQExpBuffer();
18073 954 : delqry = createPQExpBuffer();
18074 954 : trigprefix = createPQExpBuffer();
18075 954 : trigidentity = createPQExpBuffer();
18076 :
18077 954 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18078 :
18079 954 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18080 954 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18081 :
18082 954 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18083 954 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18084 :
18085 : /* Triggers can depend on extensions */
18086 954 : append_depends_on_extension(fout, query, &tginfo->dobj,
18087 : "pg_catalog.pg_trigger", "TRIGGER",
18088 954 : trigidentity->data);
18089 :
18090 954 : if (tginfo->tgispartition)
18091 : {
18092 : Assert(tbinfo->ispartition);
18093 :
18094 : /*
18095 : * Partition triggers only appear here because their 'tgenabled' flag
18096 : * differs from its parent's. The trigger is created already, so
18097 : * remove the CREATE and replace it with an ALTER. (Clear out the
18098 : * DROP query too, so that pg_dump --create does not cause errors.)
18099 : */
18100 224 : resetPQExpBuffer(query);
18101 224 : resetPQExpBuffer(delqry);
18102 224 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18103 224 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18104 224 : fmtQualifiedDumpable(tbinfo));
18105 224 : switch (tginfo->tgenabled)
18106 : {
18107 78 : case 'f':
18108 : case 'D':
18109 78 : appendPQExpBufferStr(query, "DISABLE");
18110 78 : break;
18111 0 : case 't':
18112 : case 'O':
18113 0 : appendPQExpBufferStr(query, "ENABLE");
18114 0 : break;
18115 68 : case 'R':
18116 68 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18117 68 : break;
18118 78 : case 'A':
18119 78 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18120 78 : break;
18121 : }
18122 224 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18123 224 : fmtId(tginfo->dobj.name));
18124 : }
18125 730 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
18126 : {
18127 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18128 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18129 0 : fmtQualifiedDumpable(tbinfo));
18130 0 : switch (tginfo->tgenabled)
18131 : {
18132 0 : case 'D':
18133 : case 'f':
18134 0 : appendPQExpBufferStr(query, "DISABLE");
18135 0 : break;
18136 0 : case 'A':
18137 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18138 0 : break;
18139 0 : case 'R':
18140 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18141 0 : break;
18142 0 : default:
18143 0 : appendPQExpBufferStr(query, "ENABLE");
18144 0 : break;
18145 : }
18146 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
18147 0 : fmtId(tginfo->dobj.name));
18148 : }
18149 :
18150 954 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
18151 954 : fmtId(tginfo->dobj.name));
18152 :
18153 954 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
18154 :
18155 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18156 954 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
18157 954 : ARCHIVE_OPTS(.tag = tag,
18158 : .namespace = tbinfo->dobj.namespace->dobj.name,
18159 : .owner = tbinfo->rolname,
18160 : .description = "TRIGGER",
18161 : .section = SECTION_POST_DATA,
18162 : .createStmt = query->data,
18163 : .dropStmt = delqry->data));
18164 :
18165 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18166 0 : dumpComment(fout, trigprefix->data, qtabname,
18167 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18168 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
18169 :
18170 954 : free(tag);
18171 954 : destroyPQExpBuffer(query);
18172 954 : destroyPQExpBuffer(delqry);
18173 954 : destroyPQExpBuffer(trigprefix);
18174 954 : destroyPQExpBuffer(trigidentity);
18175 954 : free(qtabname);
18176 : }
18177 :
18178 : /*
18179 : * dumpEventTrigger
18180 : * write the declaration of one user-defined event trigger
18181 : */
18182 : static void
18183 80 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
18184 : {
18185 80 : DumpOptions *dopt = fout->dopt;
18186 : PQExpBuffer query;
18187 : PQExpBuffer delqry;
18188 : char *qevtname;
18189 :
18190 : /* Do nothing if not dumping schema */
18191 80 : if (!dopt->dumpSchema)
18192 6 : return;
18193 :
18194 74 : query = createPQExpBuffer();
18195 74 : delqry = createPQExpBuffer();
18196 :
18197 74 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
18198 :
18199 74 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
18200 74 : appendPQExpBufferStr(query, qevtname);
18201 74 : appendPQExpBufferStr(query, " ON ");
18202 74 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
18203 :
18204 74 : if (strcmp("", evtinfo->evttags) != 0)
18205 : {
18206 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
18207 10 : appendPQExpBufferStr(query, evtinfo->evttags);
18208 10 : appendPQExpBufferChar(query, ')');
18209 : }
18210 :
18211 74 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
18212 74 : appendPQExpBufferStr(query, evtinfo->evtfname);
18213 74 : appendPQExpBufferStr(query, "();\n");
18214 :
18215 74 : if (evtinfo->evtenabled != 'O')
18216 : {
18217 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
18218 : qevtname);
18219 0 : switch (evtinfo->evtenabled)
18220 : {
18221 0 : case 'D':
18222 0 : appendPQExpBufferStr(query, "DISABLE");
18223 0 : break;
18224 0 : case 'A':
18225 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18226 0 : break;
18227 0 : case 'R':
18228 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18229 0 : break;
18230 0 : default:
18231 0 : appendPQExpBufferStr(query, "ENABLE");
18232 0 : break;
18233 : }
18234 0 : appendPQExpBufferStr(query, ";\n");
18235 : }
18236 :
18237 74 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
18238 : qevtname);
18239 :
18240 74 : if (dopt->binary_upgrade)
18241 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
18242 : "EVENT TRIGGER", qevtname, NULL);
18243 :
18244 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18245 74 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
18246 74 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
18247 : .owner = evtinfo->evtowner,
18248 : .description = "EVENT TRIGGER",
18249 : .section = SECTION_POST_DATA,
18250 : .createStmt = query->data,
18251 : .dropStmt = delqry->data));
18252 :
18253 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18254 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
18255 : NULL, evtinfo->evtowner,
18256 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
18257 :
18258 74 : destroyPQExpBuffer(query);
18259 74 : destroyPQExpBuffer(delqry);
18260 74 : free(qevtname);
18261 : }
18262 :
18263 : /*
18264 : * dumpRule
18265 : * Dump a rule
18266 : */
18267 : static void
18268 2150 : dumpRule(Archive *fout, const RuleInfo *rinfo)
18269 : {
18270 2150 : DumpOptions *dopt = fout->dopt;
18271 2150 : TableInfo *tbinfo = rinfo->ruletable;
18272 : bool is_view;
18273 : PQExpBuffer query;
18274 : PQExpBuffer cmd;
18275 : PQExpBuffer delcmd;
18276 : PQExpBuffer ruleprefix;
18277 : char *qtabname;
18278 : PGresult *res;
18279 : char *tag;
18280 :
18281 : /* Do nothing if not dumping schema */
18282 2150 : if (!dopt->dumpSchema)
18283 60 : return;
18284 :
18285 : /*
18286 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18287 : * we do not want to dump it as a separate object.
18288 : */
18289 2090 : if (!rinfo->separate)
18290 1668 : return;
18291 :
18292 : /*
18293 : * If it's an ON SELECT rule, we want to print it as a view definition,
18294 : * instead of a rule.
18295 : */
18296 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18297 :
18298 422 : query = createPQExpBuffer();
18299 422 : cmd = createPQExpBuffer();
18300 422 : delcmd = createPQExpBuffer();
18301 422 : ruleprefix = createPQExpBuffer();
18302 :
18303 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18304 :
18305 422 : if (is_view)
18306 : {
18307 : PQExpBuffer result;
18308 :
18309 : /*
18310 : * We need OR REPLACE here because we'll be replacing a dummy view.
18311 : * Otherwise this should look largely like the regular view dump code.
18312 : */
18313 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18314 20 : fmtQualifiedDumpable(tbinfo));
18315 20 : if (nonemptyReloptions(tbinfo->reloptions))
18316 : {
18317 0 : appendPQExpBufferStr(cmd, " WITH (");
18318 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18319 0 : appendPQExpBufferChar(cmd, ')');
18320 : }
18321 20 : result = createViewAsClause(fout, tbinfo);
18322 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
18323 20 : destroyPQExpBuffer(result);
18324 20 : if (tbinfo->checkoption != NULL)
18325 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18326 : tbinfo->checkoption);
18327 20 : appendPQExpBufferStr(cmd, ";\n");
18328 : }
18329 : else
18330 : {
18331 : /* In the rule case, just print pg_get_ruledef's result verbatim */
18332 402 : appendPQExpBuffer(query,
18333 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18334 : rinfo->dobj.catId.oid);
18335 :
18336 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18337 :
18338 402 : if (PQntuples(res) != 1)
18339 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18340 : rinfo->dobj.name, tbinfo->dobj.name);
18341 :
18342 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18343 :
18344 402 : PQclear(res);
18345 : }
18346 :
18347 : /*
18348 : * Add the command to alter the rules replication firing semantics if it
18349 : * differs from the default.
18350 : */
18351 422 : if (rinfo->ev_enabled != 'O')
18352 : {
18353 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18354 30 : switch (rinfo->ev_enabled)
18355 : {
18356 0 : case 'A':
18357 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18358 0 : fmtId(rinfo->dobj.name));
18359 0 : break;
18360 0 : case 'R':
18361 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18362 0 : fmtId(rinfo->dobj.name));
18363 0 : break;
18364 30 : case 'D':
18365 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18366 30 : fmtId(rinfo->dobj.name));
18367 30 : break;
18368 : }
18369 392 : }
18370 :
18371 422 : if (is_view)
18372 : {
18373 : /*
18374 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18375 : * REPLACE VIEW to replace the rule with something with minimal
18376 : * dependencies.
18377 : */
18378 : PQExpBuffer result;
18379 :
18380 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18381 20 : fmtQualifiedDumpable(tbinfo));
18382 20 : result = createDummyViewAsClause(fout, tbinfo);
18383 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18384 20 : destroyPQExpBuffer(result);
18385 : }
18386 : else
18387 : {
18388 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
18389 402 : fmtId(rinfo->dobj.name));
18390 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
18391 402 : fmtQualifiedDumpable(tbinfo));
18392 : }
18393 :
18394 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
18395 422 : fmtId(rinfo->dobj.name));
18396 :
18397 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18398 :
18399 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18400 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18401 422 : ARCHIVE_OPTS(.tag = tag,
18402 : .namespace = tbinfo->dobj.namespace->dobj.name,
18403 : .owner = tbinfo->rolname,
18404 : .description = "RULE",
18405 : .section = SECTION_POST_DATA,
18406 : .createStmt = cmd->data,
18407 : .dropStmt = delcmd->data));
18408 :
18409 : /* Dump rule comments */
18410 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18411 0 : dumpComment(fout, ruleprefix->data, qtabname,
18412 0 : tbinfo->dobj.namespace->dobj.name,
18413 : tbinfo->rolname,
18414 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18415 :
18416 422 : free(tag);
18417 422 : destroyPQExpBuffer(query);
18418 422 : destroyPQExpBuffer(cmd);
18419 422 : destroyPQExpBuffer(delcmd);
18420 422 : destroyPQExpBuffer(ruleprefix);
18421 422 : free(qtabname);
18422 : }
18423 :
18424 : /*
18425 : * getExtensionMembership --- obtain extension membership data
18426 : *
18427 : * We need to identify objects that are extension members as soon as they're
18428 : * loaded, so that we can correctly determine whether they need to be dumped.
18429 : * Generally speaking, extension member objects will get marked as *not* to
18430 : * be dumped, as they will be recreated by the single CREATE EXTENSION
18431 : * command. However, in binary upgrade mode we still need to dump the members
18432 : * individually.
18433 : */
18434 : void
18435 310 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18436 : int numExtensions)
18437 : {
18438 : PQExpBuffer query;
18439 : PGresult *res;
18440 : int ntups,
18441 : i;
18442 : int i_classid,
18443 : i_objid,
18444 : i_refobjid;
18445 : ExtensionInfo *ext;
18446 :
18447 : /* Nothing to do if no extensions */
18448 310 : if (numExtensions == 0)
18449 0 : return;
18450 :
18451 310 : query = createPQExpBuffer();
18452 :
18453 : /* refclassid constraint is redundant but may speed the search */
18454 310 : appendPQExpBufferStr(query, "SELECT "
18455 : "classid, objid, refobjid "
18456 : "FROM pg_depend "
18457 : "WHERE refclassid = 'pg_extension'::regclass "
18458 : "AND deptype = 'e' "
18459 : "ORDER BY 3");
18460 :
18461 310 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18462 :
18463 310 : ntups = PQntuples(res);
18464 :
18465 310 : i_classid = PQfnumber(res, "classid");
18466 310 : i_objid = PQfnumber(res, "objid");
18467 310 : i_refobjid = PQfnumber(res, "refobjid");
18468 :
18469 : /*
18470 : * Since we ordered the SELECT by referenced ID, we can expect that
18471 : * multiple entries for the same extension will appear together; this
18472 : * saves on searches.
18473 : */
18474 310 : ext = NULL;
18475 :
18476 2750 : for (i = 0; i < ntups; i++)
18477 : {
18478 : CatalogId objId;
18479 : Oid extId;
18480 :
18481 2440 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18482 2440 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18483 2440 : extId = atooid(PQgetvalue(res, i, i_refobjid));
18484 :
18485 2440 : if (ext == NULL ||
18486 2130 : ext->dobj.catId.oid != extId)
18487 360 : ext = findExtensionByOid(extId);
18488 :
18489 2440 : if (ext == NULL)
18490 : {
18491 : /* shouldn't happen */
18492 0 : pg_log_warning("could not find referenced extension %u", extId);
18493 0 : continue;
18494 : }
18495 :
18496 2440 : recordExtensionMembership(objId, ext);
18497 : }
18498 :
18499 310 : PQclear(res);
18500 :
18501 310 : destroyPQExpBuffer(query);
18502 : }
18503 :
18504 : /*
18505 : * processExtensionTables --- deal with extension configuration tables
18506 : *
18507 : * There are two parts to this process:
18508 : *
18509 : * 1. Identify and create dump records for extension configuration tables.
18510 : *
18511 : * Extensions can mark tables as "configuration", which means that the user
18512 : * is able and expected to modify those tables after the extension has been
18513 : * loaded. For these tables, we dump out only the data- the structure is
18514 : * expected to be handled at CREATE EXTENSION time, including any indexes or
18515 : * foreign keys, which brings us to-
18516 : *
18517 : * 2. Record FK dependencies between configuration tables.
18518 : *
18519 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
18520 : * the data is loaded, we have to work out what the best order for reloading
18521 : * the data is, to avoid FK violations when the tables are restored. This is
18522 : * not perfect- we can't handle circular dependencies and if any exist they
18523 : * will cause an invalid dump to be produced (though at least all of the data
18524 : * is included for a user to manually restore). This is currently documented
18525 : * but perhaps we can provide a better solution in the future.
18526 : */
18527 : void
18528 308 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18529 : int numExtensions)
18530 : {
18531 308 : DumpOptions *dopt = fout->dopt;
18532 : PQExpBuffer query;
18533 : PGresult *res;
18534 : int ntups,
18535 : i;
18536 : int i_conrelid,
18537 : i_confrelid;
18538 :
18539 : /* Nothing to do if no extensions */
18540 308 : if (numExtensions == 0)
18541 0 : return;
18542 :
18543 : /*
18544 : * Identify extension configuration tables and create TableDataInfo
18545 : * objects for them, ensuring their data will be dumped even though the
18546 : * tables themselves won't be.
18547 : *
18548 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
18549 : * user data in a configuration table is treated like schema data. This
18550 : * seems appropriate since system data in a config table would get
18551 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
18552 : * list of extensions to be included, none of its data is dumped.
18553 : */
18554 666 : for (i = 0; i < numExtensions; i++)
18555 : {
18556 358 : ExtensionInfo *curext = &(extinfo[i]);
18557 358 : char *extconfig = curext->extconfig;
18558 358 : char *extcondition = curext->extcondition;
18559 358 : char **extconfigarray = NULL;
18560 358 : char **extconditionarray = NULL;
18561 358 : int nconfigitems = 0;
18562 358 : int nconditionitems = 0;
18563 :
18564 : /*
18565 : * Check if this extension is listed as to include in the dump. If
18566 : * not, any table data associated with it is discarded.
18567 : */
18568 358 : if (extension_include_oids.head != NULL &&
18569 16 : !simple_oid_list_member(&extension_include_oids,
18570 : curext->dobj.catId.oid))
18571 12 : continue;
18572 :
18573 : /*
18574 : * Check if this extension is listed as to exclude in the dump. If
18575 : * yes, any table data associated with it is discarded.
18576 : */
18577 358 : if (extension_exclude_oids.head != NULL &&
18578 8 : simple_oid_list_member(&extension_exclude_oids,
18579 : curext->dobj.catId.oid))
18580 4 : continue;
18581 :
18582 346 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18583 : {
18584 : int j;
18585 :
18586 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18587 0 : pg_fatal("could not parse %s array", "extconfig");
18588 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18589 0 : pg_fatal("could not parse %s array", "extcondition");
18590 40 : if (nconfigitems != nconditionitems)
18591 0 : pg_fatal("mismatched number of configurations and conditions for extension");
18592 :
18593 120 : for (j = 0; j < nconfigitems; j++)
18594 : {
18595 : TableInfo *configtbl;
18596 80 : Oid configtbloid = atooid(extconfigarray[j]);
18597 80 : bool dumpobj =
18598 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18599 :
18600 80 : configtbl = findTableByOid(configtbloid);
18601 80 : if (configtbl == NULL)
18602 0 : continue;
18603 :
18604 : /*
18605 : * Tables of not-to-be-dumped extensions shouldn't be dumped
18606 : * unless the table or its schema is explicitly included
18607 : */
18608 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18609 : {
18610 : /* check table explicitly requested */
18611 4 : if (table_include_oids.head != NULL &&
18612 0 : simple_oid_list_member(&table_include_oids,
18613 : configtbloid))
18614 0 : dumpobj = true;
18615 :
18616 : /* check table's schema explicitly requested */
18617 4 : if (configtbl->dobj.namespace->dobj.dump &
18618 : DUMP_COMPONENT_DATA)
18619 4 : dumpobj = true;
18620 : }
18621 :
18622 : /* check table excluded by an exclusion switch */
18623 88 : if (table_exclude_oids.head != NULL &&
18624 8 : simple_oid_list_member(&table_exclude_oids,
18625 : configtbloid))
18626 2 : dumpobj = false;
18627 :
18628 : /* check schema excluded by an exclusion switch */
18629 80 : if (simple_oid_list_member(&schema_exclude_oids,
18630 80 : configtbl->dobj.namespace->dobj.catId.oid))
18631 0 : dumpobj = false;
18632 :
18633 80 : if (dumpobj)
18634 : {
18635 78 : makeTableDataInfo(dopt, configtbl);
18636 78 : if (configtbl->dataObj != NULL)
18637 : {
18638 78 : if (strlen(extconditionarray[j]) > 0)
18639 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
18640 : }
18641 : }
18642 : }
18643 : }
18644 346 : if (extconfigarray)
18645 40 : free(extconfigarray);
18646 346 : if (extconditionarray)
18647 40 : free(extconditionarray);
18648 : }
18649 :
18650 : /*
18651 : * Now that all the TableDataInfo objects have been created for all the
18652 : * extensions, check their FK dependencies and register them to try and
18653 : * dump the data out in an order that they can be restored in.
18654 : *
18655 : * Note that this is not a problem for user tables as their FKs are
18656 : * recreated after the data has been loaded.
18657 : */
18658 :
18659 308 : query = createPQExpBuffer();
18660 :
18661 308 : printfPQExpBuffer(query,
18662 : "SELECT conrelid, confrelid "
18663 : "FROM pg_constraint "
18664 : "JOIN pg_depend ON (objid = confrelid) "
18665 : "WHERE contype = 'f' "
18666 : "AND refclassid = 'pg_extension'::regclass "
18667 : "AND classid = 'pg_class'::regclass;");
18668 :
18669 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18670 308 : ntups = PQntuples(res);
18671 :
18672 308 : i_conrelid = PQfnumber(res, "conrelid");
18673 308 : i_confrelid = PQfnumber(res, "confrelid");
18674 :
18675 : /* Now get the dependencies and register them */
18676 308 : for (i = 0; i < ntups; i++)
18677 : {
18678 : Oid conrelid,
18679 : confrelid;
18680 : TableInfo *reftable,
18681 : *contable;
18682 :
18683 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
18684 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
18685 0 : contable = findTableByOid(conrelid);
18686 0 : reftable = findTableByOid(confrelid);
18687 :
18688 0 : if (reftable == NULL ||
18689 0 : reftable->dataObj == NULL ||
18690 0 : contable == NULL ||
18691 0 : contable->dataObj == NULL)
18692 0 : continue;
18693 :
18694 : /*
18695 : * Make referencing TABLE_DATA object depend on the referenced table's
18696 : * TABLE_DATA object.
18697 : */
18698 0 : addObjectDependency(&contable->dataObj->dobj,
18699 0 : reftable->dataObj->dobj.dumpId);
18700 : }
18701 308 : PQclear(res);
18702 308 : destroyPQExpBuffer(query);
18703 : }
18704 :
18705 : /*
18706 : * getDependencies --- obtain available dependency data
18707 : */
18708 : static void
18709 308 : getDependencies(Archive *fout)
18710 : {
18711 : PQExpBuffer query;
18712 : PGresult *res;
18713 : int ntups,
18714 : i;
18715 : int i_classid,
18716 : i_objid,
18717 : i_refclassid,
18718 : i_refobjid,
18719 : i_deptype;
18720 : DumpableObject *dobj,
18721 : *refdobj;
18722 :
18723 308 : pg_log_info("reading dependency data");
18724 :
18725 308 : query = createPQExpBuffer();
18726 :
18727 : /*
18728 : * Messy query to collect the dependency data we need. Note that we
18729 : * ignore the sub-object column, so that dependencies of or on a column
18730 : * look the same as dependencies of or on a whole table.
18731 : *
18732 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
18733 : * already processed by getExtensionMembership.
18734 : */
18735 308 : appendPQExpBufferStr(query, "SELECT "
18736 : "classid, objid, refclassid, refobjid, deptype "
18737 : "FROM pg_depend "
18738 : "WHERE deptype != 'p' AND deptype != 'e'\n");
18739 :
18740 : /*
18741 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
18742 : * have to translate their dependencies into dependencies of their parent
18743 : * opfamily. Ignore internal dependencies though, as those will point to
18744 : * their parent opclass, which we needn't consider here (and if we did,
18745 : * it'd just result in circular dependencies). Also, "loose" opfamily
18746 : * entries will have dependencies on their parent opfamily, which we
18747 : * should drop since they'd likewise become useless self-dependencies.
18748 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
18749 : */
18750 308 : appendPQExpBufferStr(query, "UNION ALL\n"
18751 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
18752 : "FROM pg_depend d, pg_amop o "
18753 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18754 : "classid = 'pg_amop'::regclass AND objid = o.oid "
18755 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
18756 :
18757 : /* Likewise for pg_amproc entries */
18758 308 : appendPQExpBufferStr(query, "UNION ALL\n"
18759 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
18760 : "FROM pg_depend d, pg_amproc p "
18761 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18762 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
18763 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
18764 :
18765 : /* Sort the output for efficiency below */
18766 308 : appendPQExpBufferStr(query, "ORDER BY 1,2");
18767 :
18768 308 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18769 :
18770 308 : ntups = PQntuples(res);
18771 :
18772 308 : i_classid = PQfnumber(res, "classid");
18773 308 : i_objid = PQfnumber(res, "objid");
18774 308 : i_refclassid = PQfnumber(res, "refclassid");
18775 308 : i_refobjid = PQfnumber(res, "refobjid");
18776 308 : i_deptype = PQfnumber(res, "deptype");
18777 :
18778 : /*
18779 : * Since we ordered the SELECT by referencing ID, we can expect that
18780 : * multiple entries for the same object will appear together; this saves
18781 : * on searches.
18782 : */
18783 308 : dobj = NULL;
18784 :
18785 668622 : for (i = 0; i < ntups; i++)
18786 : {
18787 : CatalogId objId;
18788 : CatalogId refobjId;
18789 : char deptype;
18790 :
18791 668314 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18792 668314 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18793 668314 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
18794 668314 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
18795 668314 : deptype = *(PQgetvalue(res, i, i_deptype));
18796 :
18797 668314 : if (dobj == NULL ||
18798 623152 : dobj->catId.tableoid != objId.tableoid ||
18799 619482 : dobj->catId.oid != objId.oid)
18800 295422 : dobj = findObjectByCatalogId(objId);
18801 :
18802 : /*
18803 : * Failure to find objects mentioned in pg_depend is not unexpected,
18804 : * since for example we don't collect info about TOAST tables.
18805 : */
18806 668314 : if (dobj == NULL)
18807 : {
18808 : #ifdef NOT_USED
18809 : pg_log_warning("no referencing object %u %u",
18810 : objId.tableoid, objId.oid);
18811 : #endif
18812 46380 : continue;
18813 : }
18814 :
18815 623444 : refdobj = findObjectByCatalogId(refobjId);
18816 :
18817 623444 : if (refdobj == NULL)
18818 : {
18819 : #ifdef NOT_USED
18820 : pg_log_warning("no referenced object %u %u",
18821 : refobjId.tableoid, refobjId.oid);
18822 : #endif
18823 1510 : continue;
18824 : }
18825 :
18826 : /*
18827 : * For 'x' dependencies, mark the object for later; we still add the
18828 : * normal dependency, for possible ordering purposes. Currently
18829 : * pg_dump_sort.c knows to put extensions ahead of all object types
18830 : * that could possibly depend on them, but this is safer.
18831 : */
18832 621934 : if (deptype == 'x')
18833 88 : dobj->depends_on_ext = true;
18834 :
18835 : /*
18836 : * Ordinarily, table rowtypes have implicit dependencies on their
18837 : * tables. However, for a composite type the implicit dependency goes
18838 : * the other way in pg_depend; which is the right thing for DROP but
18839 : * it doesn't produce the dependency ordering we need. So in that one
18840 : * case, we reverse the direction of the dependency.
18841 : */
18842 621934 : if (deptype == 'i' &&
18843 173406 : dobj->objType == DO_TABLE &&
18844 2322 : refdobj->objType == DO_TYPE)
18845 360 : addObjectDependency(refdobj, dobj->dumpId);
18846 : else
18847 : /* normal case */
18848 621574 : addObjectDependency(dobj, refdobj->dumpId);
18849 : }
18850 :
18851 308 : PQclear(res);
18852 :
18853 308 : destroyPQExpBuffer(query);
18854 308 : }
18855 :
18856 :
18857 : /*
18858 : * createBoundaryObjects - create dummy DumpableObjects to represent
18859 : * dump section boundaries.
18860 : */
18861 : static DumpableObject *
18862 308 : createBoundaryObjects(void)
18863 : {
18864 : DumpableObject *dobjs;
18865 :
18866 308 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18867 :
18868 308 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18869 308 : dobjs[0].catId = nilCatalogId;
18870 308 : AssignDumpId(dobjs + 0);
18871 308 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18872 :
18873 308 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18874 308 : dobjs[1].catId = nilCatalogId;
18875 308 : AssignDumpId(dobjs + 1);
18876 308 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18877 :
18878 308 : return dobjs;
18879 : }
18880 :
18881 : /*
18882 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
18883 : * section boundaries.
18884 : */
18885 : static void
18886 308 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18887 : DumpableObject *boundaryObjs)
18888 : {
18889 308 : DumpableObject *preDataBound = boundaryObjs + 0;
18890 308 : DumpableObject *postDataBound = boundaryObjs + 1;
18891 : int i;
18892 :
18893 1126600 : for (i = 0; i < numObjs; i++)
18894 : {
18895 1126292 : DumpableObject *dobj = dobjs[i];
18896 :
18897 : /*
18898 : * The classification of object types here must match the SECTION_xxx
18899 : * values assigned during subsequent ArchiveEntry calls!
18900 : */
18901 1126292 : switch (dobj->objType)
18902 : {
18903 1055772 : case DO_NAMESPACE:
18904 : case DO_EXTENSION:
18905 : case DO_TYPE:
18906 : case DO_SHELL_TYPE:
18907 : case DO_FUNC:
18908 : case DO_AGG:
18909 : case DO_OPERATOR:
18910 : case DO_ACCESS_METHOD:
18911 : case DO_OPCLASS:
18912 : case DO_OPFAMILY:
18913 : case DO_COLLATION:
18914 : case DO_CONVERSION:
18915 : case DO_TABLE:
18916 : case DO_TABLE_ATTACH:
18917 : case DO_ATTRDEF:
18918 : case DO_PROCLANG:
18919 : case DO_CAST:
18920 : case DO_DUMMY_TYPE:
18921 : case DO_TSPARSER:
18922 : case DO_TSDICT:
18923 : case DO_TSTEMPLATE:
18924 : case DO_TSCONFIG:
18925 : case DO_FDW:
18926 : case DO_FOREIGN_SERVER:
18927 : case DO_TRANSFORM:
18928 : /* Pre-data objects: must come before the pre-data boundary */
18929 1055772 : addObjectDependency(preDataBound, dobj->dumpId);
18930 1055772 : break;
18931 8320 : case DO_TABLE_DATA:
18932 : case DO_SEQUENCE_SET:
18933 : case DO_LARGE_OBJECT:
18934 : case DO_LARGE_OBJECT_DATA:
18935 : /* Data objects: must come between the boundaries */
18936 8320 : addObjectDependency(dobj, preDataBound->dumpId);
18937 8320 : addObjectDependency(postDataBound, dobj->dumpId);
18938 8320 : break;
18939 10250 : case DO_INDEX:
18940 : case DO_INDEX_ATTACH:
18941 : case DO_STATSEXT:
18942 : case DO_REFRESH_MATVIEW:
18943 : case DO_TRIGGER:
18944 : case DO_EVENT_TRIGGER:
18945 : case DO_DEFAULT_ACL:
18946 : case DO_POLICY:
18947 : case DO_PUBLICATION:
18948 : case DO_PUBLICATION_REL:
18949 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
18950 : case DO_SUBSCRIPTION:
18951 : case DO_SUBSCRIPTION_REL:
18952 : /* Post-data objects: must come after the post-data boundary */
18953 10250 : addObjectDependency(dobj, postDataBound->dumpId);
18954 10250 : break;
18955 46850 : case DO_RULE:
18956 : /* Rules are post-data, but only if dumped separately */
18957 46850 : if (((RuleInfo *) dobj)->separate)
18958 1162 : addObjectDependency(dobj, postDataBound->dumpId);
18959 46850 : break;
18960 4484 : case DO_CONSTRAINT:
18961 : case DO_FK_CONSTRAINT:
18962 : /* Constraints are post-data, but only if dumped separately */
18963 4484 : if (((ConstraintInfo *) dobj)->separate)
18964 3218 : addObjectDependency(dobj, postDataBound->dumpId);
18965 4484 : break;
18966 308 : case DO_PRE_DATA_BOUNDARY:
18967 : /* nothing to do */
18968 308 : break;
18969 308 : case DO_POST_DATA_BOUNDARY:
18970 : /* must come after the pre-data boundary */
18971 308 : addObjectDependency(dobj, preDataBound->dumpId);
18972 308 : break;
18973 : }
18974 1126292 : }
18975 308 : }
18976 :
18977 :
18978 : /*
18979 : * BuildArchiveDependencies - create dependency data for archive TOC entries
18980 : *
18981 : * The raw dependency data obtained by getDependencies() is not terribly
18982 : * useful in an archive dump, because in many cases there are dependency
18983 : * chains linking through objects that don't appear explicitly in the dump.
18984 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
18985 : * will depend on other objects --- but the rule will not appear as a separate
18986 : * object in the dump. We need to adjust the view's dependencies to include
18987 : * whatever the rule depends on that is included in the dump.
18988 : *
18989 : * Just to make things more complicated, there are also "special" dependencies
18990 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
18991 : * not rearrange because pg_restore knows that TABLE DATA only depends on
18992 : * its table. In these cases we must leave the dependencies strictly as-is
18993 : * even if they refer to not-to-be-dumped objects.
18994 : *
18995 : * To handle this, the convention is that "special" dependencies are created
18996 : * during ArchiveEntry calls, and an archive TOC item that has any such
18997 : * entries will not be touched here. Otherwise, we recursively search the
18998 : * DumpableObject data structures to build the correct dependencies for each
18999 : * archive TOC item.
19000 : */
19001 : static void
19002 62 : BuildArchiveDependencies(Archive *fout)
19003 : {
19004 62 : ArchiveHandle *AH = (ArchiveHandle *) fout;
19005 : TocEntry *te;
19006 :
19007 : /* Scan all TOC entries in the archive */
19008 9720 : for (te = AH->toc->next; te != AH->toc; te = te->next)
19009 : {
19010 : DumpableObject *dobj;
19011 : DumpId *dependencies;
19012 : int nDeps;
19013 : int allocDeps;
19014 :
19015 : /* No need to process entries that will not be dumped */
19016 9658 : if (te->reqs == 0)
19017 2954 : continue;
19018 : /* Ignore entries that already have "special" dependencies */
19019 9652 : if (te->nDeps > 0)
19020 2420 : continue;
19021 : /* Otherwise, look up the item's original DumpableObject, if any */
19022 7232 : dobj = findObjectByDumpId(te->dumpId);
19023 7232 : if (dobj == NULL)
19024 318 : continue;
19025 : /* No work if it has no dependencies */
19026 6914 : if (dobj->nDeps <= 0)
19027 210 : continue;
19028 : /* Set up work array */
19029 6704 : allocDeps = 64;
19030 6704 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19031 6704 : nDeps = 0;
19032 : /* Recursively find all dumpable dependencies */
19033 6704 : findDumpableDependencies(AH, dobj,
19034 : &dependencies, &nDeps, &allocDeps);
19035 : /* And save 'em ... */
19036 6704 : if (nDeps > 0)
19037 : {
19038 5102 : dependencies = (DumpId *) pg_realloc(dependencies,
19039 : nDeps * sizeof(DumpId));
19040 5102 : te->dependencies = dependencies;
19041 5102 : te->nDeps = nDeps;
19042 : }
19043 : else
19044 1602 : free(dependencies);
19045 : }
19046 62 : }
19047 :
19048 : /* Recursive search subroutine for BuildArchiveDependencies */
19049 : static void
19050 16448 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19051 : DumpId **dependencies, int *nDeps, int *allocDeps)
19052 : {
19053 : int i;
19054 :
19055 : /*
19056 : * Ignore section boundary objects: if we search through them, we'll
19057 : * report lots of bogus dependencies.
19058 : */
19059 16448 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19060 16410 : dobj->objType == DO_POST_DATA_BOUNDARY)
19061 2924 : return;
19062 :
19063 34256 : for (i = 0; i < dobj->nDeps; i++)
19064 : {
19065 20732 : DumpId depid = dobj->dependencies[i];
19066 :
19067 20732 : if (TocIDRequired(AH, depid) != 0)
19068 : {
19069 : /* Object will be dumped, so just reference it as a dependency */
19070 10988 : if (*nDeps >= *allocDeps)
19071 : {
19072 0 : *allocDeps *= 2;
19073 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
19074 0 : *allocDeps * sizeof(DumpId));
19075 : }
19076 10988 : (*dependencies)[*nDeps] = depid;
19077 10988 : (*nDeps)++;
19078 : }
19079 : else
19080 : {
19081 : /*
19082 : * Object will not be dumped, so recursively consider its deps. We
19083 : * rely on the assumption that sortDumpableObjects already broke
19084 : * any dependency loops, else we might recurse infinitely.
19085 : */
19086 9744 : DumpableObject *otherdobj = findObjectByDumpId(depid);
19087 :
19088 9744 : if (otherdobj)
19089 9744 : findDumpableDependencies(AH, otherdobj,
19090 : dependencies, nDeps, allocDeps);
19091 : }
19092 : }
19093 : }
19094 :
19095 :
19096 : /*
19097 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
19098 : * given type OID.
19099 : *
19100 : * This does not guarantee to schema-qualify the output, so it should not
19101 : * be used to create the target object name for CREATE or ALTER commands.
19102 : *
19103 : * Note that the result is cached and must not be freed by the caller.
19104 : */
19105 : static const char *
19106 4624 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
19107 : {
19108 : TypeInfo *typeInfo;
19109 : char *result;
19110 : PQExpBuffer query;
19111 : PGresult *res;
19112 :
19113 4624 : if (oid == 0)
19114 : {
19115 0 : if ((opts & zeroAsStar) != 0)
19116 0 : return "*";
19117 0 : else if ((opts & zeroAsNone) != 0)
19118 0 : return "NONE";
19119 : }
19120 :
19121 : /* see if we have the result cached in the type's TypeInfo record */
19122 4624 : typeInfo = findTypeByOid(oid);
19123 4624 : if (typeInfo && typeInfo->ftypname)
19124 3676 : return typeInfo->ftypname;
19125 :
19126 948 : query = createPQExpBuffer();
19127 948 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
19128 : oid);
19129 :
19130 948 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
19131 :
19132 : /* result of format_type is already quoted */
19133 948 : result = pg_strdup(PQgetvalue(res, 0, 0));
19134 :
19135 948 : PQclear(res);
19136 948 : destroyPQExpBuffer(query);
19137 :
19138 : /*
19139 : * Cache the result for re-use in later requests, if possible. If we
19140 : * don't have a TypeInfo for the type, the string will be leaked once the
19141 : * caller is done with it ... but that case really should not happen, so
19142 : * leaking if it does seems acceptable.
19143 : */
19144 948 : if (typeInfo)
19145 948 : typeInfo->ftypname = result;
19146 :
19147 948 : return result;
19148 : }
19149 :
19150 : /*
19151 : * Return a column list clause for the given relation.
19152 : *
19153 : * Special case: if there are no undropped columns in the relation, return
19154 : * "", not an invalid "()" column list.
19155 : */
19156 : static const char *
19157 14188 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
19158 : {
19159 14188 : int numatts = ti->numatts;
19160 14188 : char **attnames = ti->attnames;
19161 14188 : bool *attisdropped = ti->attisdropped;
19162 14188 : char *attgenerated = ti->attgenerated;
19163 : bool needComma;
19164 : int i;
19165 :
19166 14188 : appendPQExpBufferChar(buffer, '(');
19167 14188 : needComma = false;
19168 72572 : for (i = 0; i < numatts; i++)
19169 : {
19170 58384 : if (attisdropped[i])
19171 1116 : continue;
19172 57268 : if (attgenerated[i])
19173 1180 : continue;
19174 56088 : if (needComma)
19175 42348 : appendPQExpBufferStr(buffer, ", ");
19176 56088 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
19177 56088 : needComma = true;
19178 : }
19179 :
19180 14188 : if (!needComma)
19181 448 : return ""; /* no undropped columns */
19182 :
19183 13740 : appendPQExpBufferChar(buffer, ')');
19184 13740 : return buffer->data;
19185 : }
19186 :
19187 : /*
19188 : * Check if a reloptions array is nonempty.
19189 : */
19190 : static bool
19191 24280 : nonemptyReloptions(const char *reloptions)
19192 : {
19193 : /* Don't want to print it if it's just "{}" */
19194 24280 : return (reloptions != NULL && strlen(reloptions) > 2);
19195 : }
19196 :
19197 : /*
19198 : * Format a reloptions array and append it to the given buffer.
19199 : *
19200 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
19201 : */
19202 : static void
19203 420 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
19204 : const char *prefix, Archive *fout)
19205 : {
19206 : bool res;
19207 :
19208 420 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
19209 420 : fout->std_strings);
19210 420 : if (!res)
19211 0 : pg_log_warning("could not parse %s array", "reloptions");
19212 420 : }
19213 :
19214 : /*
19215 : * read_dump_filters - retrieve object identifier patterns from file
19216 : *
19217 : * Parse the specified filter file for include and exclude patterns, and add
19218 : * them to the relevant lists. If the filename is "-" then filters will be
19219 : * read from STDIN rather than a file.
19220 : */
19221 : static void
19222 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
19223 : {
19224 : FilterStateData fstate;
19225 : char *objname;
19226 : FilterCommandType comtype;
19227 : FilterObjectType objtype;
19228 :
19229 52 : filter_init(&fstate, filename, exit_nicely);
19230 :
19231 116 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
19232 : {
19233 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
19234 : {
19235 34 : switch (objtype)
19236 : {
19237 0 : case FILTER_OBJECT_TYPE_NONE:
19238 0 : break;
19239 0 : case FILTER_OBJECT_TYPE_DATABASE:
19240 : case FILTER_OBJECT_TYPE_FUNCTION:
19241 : case FILTER_OBJECT_TYPE_INDEX:
19242 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19243 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19244 : case FILTER_OBJECT_TYPE_TRIGGER:
19245 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19246 : "include",
19247 : filter_object_type_name(objtype));
19248 0 : exit_nicely(1);
19249 : break; /* unreachable */
19250 :
19251 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19252 2 : simple_string_list_append(&extension_include_patterns, objname);
19253 2 : break;
19254 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19255 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
19256 2 : break;
19257 2 : case FILTER_OBJECT_TYPE_SCHEMA:
19258 2 : simple_string_list_append(&schema_include_patterns, objname);
19259 2 : dopt->include_everything = false;
19260 2 : break;
19261 26 : case FILTER_OBJECT_TYPE_TABLE:
19262 26 : simple_string_list_append(&table_include_patterns, objname);
19263 26 : dopt->include_everything = false;
19264 26 : break;
19265 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19266 2 : simple_string_list_append(&table_include_patterns_and_children,
19267 : objname);
19268 2 : dopt->include_everything = false;
19269 2 : break;
19270 : }
19271 34 : }
19272 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
19273 : {
19274 18 : switch (objtype)
19275 : {
19276 0 : case FILTER_OBJECT_TYPE_NONE:
19277 0 : break;
19278 2 : case FILTER_OBJECT_TYPE_DATABASE:
19279 : case FILTER_OBJECT_TYPE_FUNCTION:
19280 : case FILTER_OBJECT_TYPE_INDEX:
19281 : case FILTER_OBJECT_TYPE_TRIGGER:
19282 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19283 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19284 : "exclude",
19285 : filter_object_type_name(objtype));
19286 2 : exit_nicely(1);
19287 : break;
19288 :
19289 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19290 2 : simple_string_list_append(&extension_exclude_patterns, objname);
19291 2 : break;
19292 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19293 2 : simple_string_list_append(&tabledata_exclude_patterns,
19294 : objname);
19295 2 : break;
19296 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19297 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
19298 : objname);
19299 2 : break;
19300 4 : case FILTER_OBJECT_TYPE_SCHEMA:
19301 4 : simple_string_list_append(&schema_exclude_patterns, objname);
19302 4 : break;
19303 4 : case FILTER_OBJECT_TYPE_TABLE:
19304 4 : simple_string_list_append(&table_exclude_patterns, objname);
19305 4 : break;
19306 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19307 2 : simple_string_list_append(&table_exclude_patterns_and_children,
19308 : objname);
19309 2 : break;
19310 : }
19311 16 : }
19312 : else
19313 : {
19314 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19315 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19316 : }
19317 :
19318 64 : if (objname)
19319 50 : free(objname);
19320 : }
19321 :
19322 44 : filter_free(&fstate);
19323 44 : }
|