Branch data 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-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * pg_dump will read the system catalogs in a database and dump out a
11 : : * script that reproduces the schema in terms of SQL that is understood
12 : : * by PostgreSQL
13 : : *
14 : : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : : * so it sees a consistent snapshot of the database including system
16 : : * catalogs. However, it relies in part on various specialized backend
17 : : * functions like pg_get_indexdef(), and those things tend to look at
18 : : * the currently committed state. So it is possible to get 'cache
19 : : * lookup failed' error if someone performs DDL changes while a dump is
20 : : * happening. The window for this sort of thing is from the acquisition
21 : : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : : * but it can happen.
24 : : *
25 : : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : : *
27 : : * IDENTIFICATION
28 : : * src/bin/pg_dump/pg_dump.c
29 : : *
30 : : *-------------------------------------------------------------------------
31 : : */
32 : : #include "postgres_fe.h"
33 : :
34 : : #include <unistd.h>
35 : : #include <ctype.h>
36 : : #include <limits.h>
37 : : #ifdef HAVE_TERMIOS_H
38 : : #include <termios.h>
39 : : #endif
40 : :
41 : : #include "access/attnum.h"
42 : : #include "access/sysattr.h"
43 : : #include "access/transam.h"
44 : : #include "catalog/pg_aggregate_d.h"
45 : : #include "catalog/pg_am_d.h"
46 : : #include "catalog/pg_attribute_d.h"
47 : : #include "catalog/pg_authid_d.h"
48 : : #include "catalog/pg_cast_d.h"
49 : : #include "catalog/pg_class_d.h"
50 : : #include "catalog/pg_constraint_d.h"
51 : : #include "catalog/pg_default_acl_d.h"
52 : : #include "catalog/pg_largeobject_d.h"
53 : : #include "catalog/pg_largeobject_metadata_d.h"
54 : : #include "catalog/pg_proc_d.h"
55 : : #include "catalog/pg_publication_d.h"
56 : : #include "catalog/pg_shdepend_d.h"
57 : : #include "catalog/pg_subscription_d.h"
58 : : #include "catalog/pg_type_d.h"
59 : : #include "common/connect.h"
60 : : #include "common/int.h"
61 : : #include "common/relpath.h"
62 : : #include "common/shortest_dec.h"
63 : : #include "compress_io.h"
64 : : #include "dumputils.h"
65 : : #include "fe_utils/option_utils.h"
66 : : #include "fe_utils/string_utils.h"
67 : : #include "filter.h"
68 : : #include "getopt_long.h"
69 : : #include "libpq/libpq-fs.h"
70 : : #include "parallel.h"
71 : : #include "pg_backup_db.h"
72 : : #include "pg_backup_utils.h"
73 : : #include "pg_dump.h"
74 : : #include "statistics/statistics_format.h"
75 : : #include "storage/block.h"
76 : :
77 : : typedef struct
78 : : {
79 : : Oid roleoid; /* role's OID */
80 : : const char *rolename; /* role's name */
81 : : } RoleNameItem;
82 : :
83 : : typedef struct
84 : : {
85 : : const char *descr; /* comment for an object */
86 : : Oid classoid; /* object class (catalog OID) */
87 : : Oid objoid; /* object OID */
88 : : int objsubid; /* subobject (table column #) */
89 : : } CommentItem;
90 : :
91 : : typedef struct
92 : : {
93 : : const char *provider; /* label provider of this security label */
94 : : const char *label; /* security label for an object */
95 : : Oid classoid; /* object class (catalog OID) */
96 : : Oid objoid; /* object OID */
97 : : int objsubid; /* subobject (table column #) */
98 : : } SecLabelItem;
99 : :
100 : : typedef struct
101 : : {
102 : : Oid oid; /* object OID */
103 : : char relkind; /* object kind */
104 : : RelFileNumber relfilenumber; /* object filenode */
105 : : Oid toast_oid; /* toast table OID */
106 : : RelFileNumber toast_relfilenumber; /* toast table filenode */
107 : : Oid toast_index_oid; /* toast table index OID */
108 : : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
109 : : } BinaryUpgradeClassOidItem;
110 : :
111 : : /* sequence types */
112 : : typedef enum SeqType
113 : : {
114 : : SEQTYPE_SMALLINT,
115 : : SEQTYPE_INTEGER,
116 : : SEQTYPE_BIGINT,
117 : : } SeqType;
118 : :
119 : : static const char *const SeqTypeNames[] =
120 : : {
121 : : [SEQTYPE_SMALLINT] = "smallint",
122 : : [SEQTYPE_INTEGER] = "integer",
123 : : [SEQTYPE_BIGINT] = "bigint",
124 : : };
125 : :
126 : : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
127 : : "array length mismatch");
128 : :
129 : : typedef struct
130 : : {
131 : : Oid oid; /* sequence OID */
132 : : SeqType seqtype; /* data type of sequence */
133 : : bool cycled; /* whether sequence cycles */
134 : : int64 minv; /* minimum value */
135 : : int64 maxv; /* maximum value */
136 : : int64 startv; /* start value */
137 : : int64 incby; /* increment value */
138 : : int64 cache; /* cache size */
139 : : int64 last_value; /* last value of sequence */
140 : : bool is_called; /* whether nextval advances before returning */
141 : : bool null_seqtuple; /* did pg_get_sequence_data return nulls? */
142 : : } SequenceItem;
143 : :
144 : : typedef enum OidOptions
145 : : {
146 : : zeroIsError = 1,
147 : : zeroAsStar = 2,
148 : : zeroAsNone = 4,
149 : : } OidOptions;
150 : :
151 : : /* global decls */
152 : : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
153 : :
154 : : static Oid g_last_builtin_oid; /* value of the last builtin oid */
155 : :
156 : : /* The specified names/patterns should to match at least one entity */
157 : : static int strict_names = 0;
158 : :
159 : : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
160 : :
161 : : /*
162 : : * Object inclusion/exclusion lists
163 : : *
164 : : * The string lists record the patterns given by command-line switches,
165 : : * which we then convert to lists of OIDs of matching objects.
166 : : */
167 : : static SimpleStringList schema_include_patterns = {NULL, NULL};
168 : : static SimpleOidList schema_include_oids = {NULL, NULL};
169 : : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
170 : : static SimpleOidList schema_exclude_oids = {NULL, NULL};
171 : :
172 : : static SimpleStringList table_include_patterns = {NULL, NULL};
173 : : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
174 : : static SimpleOidList table_include_oids = {NULL, NULL};
175 : : static SimpleStringList table_exclude_patterns = {NULL, NULL};
176 : : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
177 : : static SimpleOidList table_exclude_oids = {NULL, NULL};
178 : : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
179 : : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
180 : : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
181 : :
182 : : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
183 : : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
184 : :
185 : : static SimpleStringList extension_include_patterns = {NULL, NULL};
186 : : static SimpleOidList extension_include_oids = {NULL, NULL};
187 : :
188 : : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
189 : : static SimpleOidList extension_exclude_oids = {NULL, NULL};
190 : :
191 : : static const CatalogId nilCatalogId = {0, 0};
192 : :
193 : : /* override for standard extra_float_digits setting */
194 : : static bool have_extra_float_digits = false;
195 : : static int extra_float_digits;
196 : :
197 : : /* sorted table of role names */
198 : : static RoleNameItem *rolenames = NULL;
199 : : static int nrolenames = 0;
200 : :
201 : : /* sorted table of comments */
202 : : static CommentItem *comments = NULL;
203 : : static int ncomments = 0;
204 : :
205 : : /* sorted table of security labels */
206 : : static SecLabelItem *seclabels = NULL;
207 : : static int nseclabels = 0;
208 : :
209 : : /* sorted table of pg_class information for binary upgrade */
210 : : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
211 : : static int nbinaryUpgradeClassOids = 0;
212 : :
213 : : /* sorted table of sequences */
214 : : static SequenceItem *sequences = NULL;
215 : : static int nsequences = 0;
216 : :
217 : : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
218 : : #define MAX_ATTR_STATS_RELS 64
219 : :
220 : : /*
221 : : * The default number of rows per INSERT when
222 : : * --inserts is specified without --rows-per-insert
223 : : */
224 : : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
225 : :
226 : : /*
227 : : * Maximum number of large objects to group into a single ArchiveEntry.
228 : : * At some point we might want to make this user-controllable, but for now
229 : : * a hard-wired setting will suffice.
230 : : */
231 : : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
232 : :
233 : : /*
234 : : * Macro for producing quoted, schema-qualified name of a dumpable object.
235 : : */
236 : : #define fmtQualifiedDumpable(obj) \
237 : : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
238 : : (obj)->dobj.name)
239 : :
240 : : static void help(const char *progname);
241 : : static void setup_connection(Archive *AH,
242 : : const char *dumpencoding, const char *dumpsnapshot,
243 : : char *use_role);
244 : : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
245 : : static void expand_schema_name_patterns(Archive *fout,
246 : : SimpleStringList *patterns,
247 : : SimpleOidList *oids,
248 : : bool strict_names);
249 : : static void expand_extension_name_patterns(Archive *fout,
250 : : SimpleStringList *patterns,
251 : : SimpleOidList *oids,
252 : : bool strict_names);
253 : : static void expand_foreign_server_name_patterns(Archive *fout,
254 : : SimpleStringList *patterns,
255 : : SimpleOidList *oids);
256 : : static void expand_table_name_patterns(Archive *fout,
257 : : SimpleStringList *patterns,
258 : : SimpleOidList *oids,
259 : : bool strict_names,
260 : : bool with_child_tables);
261 : : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
262 : : const char *pattern);
263 : :
264 : : static NamespaceInfo *findNamespace(Oid nsoid);
265 : : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
266 : : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
267 : : static const char *getRoleName(const char *roleoid_str);
268 : : static void collectRoleNames(Archive *fout);
269 : : static void getAdditionalACLs(Archive *fout);
270 : : static void dumpCommentExtended(Archive *fout, const char *type,
271 : : const char *name, const char *namespace,
272 : : const char *owner, CatalogId catalogId,
273 : : int subid, DumpId dumpId,
274 : : const char *initdb_comment);
275 : : static inline void dumpComment(Archive *fout, const char *type,
276 : : const char *name, const char *namespace,
277 : : const char *owner, CatalogId catalogId,
278 : : int subid, DumpId dumpId);
279 : : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
280 : : static void collectComments(Archive *fout);
281 : : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
282 : : const char *namespace, const char *owner,
283 : : CatalogId catalogId, int subid, DumpId dumpId);
284 : : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
285 : : static void collectSecLabels(Archive *fout);
286 : : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
287 : : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
288 : : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
289 : : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
290 : : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
291 : : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
292 : : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
293 : : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
294 : : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
295 : : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
296 : : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
297 : : PGresult *res);
298 : : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
299 : : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
300 : : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
301 : : static void dumpCast(Archive *fout, const CastInfo *cast);
302 : : static void dumpTransform(Archive *fout, const TransformInfo *transform);
303 : : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
304 : : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
305 : : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
306 : : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
307 : : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
308 : : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
309 : : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
310 : : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
311 : : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
312 : : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
313 : : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
314 : : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
315 : : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
316 : : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
317 : : static void collectSequences(Archive *fout);
318 : : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
319 : : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
320 : : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
321 : : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
322 : : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
323 : : static void dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo);
324 : : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
325 : : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
326 : : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
327 : : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
328 : : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
329 : : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
330 : : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
331 : : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
332 : : static void dumpUserMappings(Archive *fout,
333 : : const char *servername, const char *namespace,
334 : : const char *owner, CatalogId catalogId, DumpId dumpId);
335 : : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
336 : :
337 : : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
338 : : const char *type, const char *name, const char *subname,
339 : : const char *nspname, const char *tag, const char *owner,
340 : : const DumpableAcl *dacl);
341 : :
342 : : static void getDependencies(Archive *fout);
343 : : static void BuildArchiveDependencies(Archive *fout);
344 : : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
345 : : DumpId **dependencies, int *nDeps, int *allocDeps);
346 : :
347 : : static DumpableObject *createBoundaryObjects(void);
348 : : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
349 : : DumpableObject *boundaryObjs);
350 : :
351 : : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
352 : : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
353 : : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
354 : : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
355 : : static void buildMatViewRefreshDependencies(Archive *fout);
356 : : static void getTableDataFKConstraints(void);
357 : : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
358 : : TableInfo *tbinfo, int j,
359 : : int i_notnull_name,
360 : : int i_notnull_comment,
361 : : int i_notnull_invalidoid,
362 : : int i_notnull_noinherit,
363 : : int i_notnull_islocal,
364 : : PQExpBuffer *invalidnotnulloids);
365 : : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
366 : : bool is_agg);
367 : : static char *format_function_signature(Archive *fout,
368 : : const FuncInfo *finfo, bool honor_quotes);
369 : : static char *convertRegProcReference(const char *proc);
370 : : static char *getFormattedOperatorName(const char *oproid);
371 : : static char *convertTSFunction(Archive *fout, Oid funcOid);
372 : : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
373 : : static void getLOs(Archive *fout);
374 : : static void dumpLO(Archive *fout, const LoInfo *loinfo);
375 : : static int dumpLOs(Archive *fout, const void *arg);
376 : : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
377 : : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
378 : : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
379 : : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
380 : : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
381 : : static void dumpDatabase(Archive *fout);
382 : : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
383 : : const char *dbname, Oid dboid);
384 : : static void dumpEncoding(Archive *AH);
385 : : static void dumpStdStrings(Archive *AH);
386 : : static void dumpSearchPath(Archive *AH);
387 : : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
388 : : PQExpBuffer upgrade_buffer,
389 : : Oid pg_type_oid,
390 : : bool force_array_type,
391 : : bool include_multirange_type);
392 : : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
393 : : PQExpBuffer upgrade_buffer,
394 : : const TableInfo *tbinfo);
395 : : static void collectBinaryUpgradeClassOids(Archive *fout);
396 : : static void binary_upgrade_set_pg_class_oids(Archive *fout,
397 : : PQExpBuffer upgrade_buffer,
398 : : Oid pg_class_oid);
399 : : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
400 : : const DumpableObject *dobj,
401 : : const char *objtype,
402 : : const char *objname,
403 : : const char *objnamespace);
404 : : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
405 : : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
406 : : static bool nonemptyReloptions(const char *reloptions);
407 : : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
408 : : const char *prefix, Archive *fout);
409 : : static char *get_synchronized_snapshot(Archive *fout);
410 : : static void set_restrict_relation_kind(Archive *AH, const char *value);
411 : : static void setupDumpWorker(Archive *AH);
412 : : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
413 : : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
414 : : static void read_dump_filters(const char *filename, DumpOptions *dopt);
415 : :
416 : :
417 : : int
418 : 302 : main(int argc, char **argv)
419 : : {
420 : : int c;
421 : 302 : const char *filename = NULL;
422 : 302 : const char *format = "p";
423 : : TableInfo *tblinfo;
424 : : int numTables;
425 : : DumpableObject **dobjs;
426 : : int numObjs;
427 : : DumpableObject *boundaryObjs;
428 : : int i;
429 : : int optindex;
430 : : RestoreOptions *ropt;
431 : : Archive *fout; /* the script file */
432 : 302 : bool g_verbose = false;
433 : 302 : const char *dumpencoding = NULL;
434 : 302 : const char *dumpsnapshot = NULL;
435 : 302 : char *use_role = NULL;
436 : 302 : int numWorkers = 1;
437 : 302 : int plainText = 0;
438 : 302 : ArchiveFormat archiveFormat = archUnknown;
439 : : ArchiveMode archiveMode;
440 : 302 : pg_compress_specification compression_spec = {0};
441 : 302 : char *compression_detail = NULL;
442 : 302 : char *compression_algorithm_str = "none";
443 : 302 : char *error_detail = NULL;
444 : 302 : bool user_compression_defined = false;
445 : 302 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
446 : 302 : bool data_only = false;
447 : 302 : bool schema_only = false;
448 : 302 : bool statistics_only = false;
449 : 302 : bool with_statistics = false;
450 : 302 : bool no_data = false;
451 : 302 : bool no_schema = false;
452 : 302 : bool no_statistics = false;
453 : :
454 : : static DumpOptions dopt;
455 : :
456 : : static struct option long_options[] = {
457 : : {"data-only", no_argument, NULL, 'a'},
458 : : {"blobs", no_argument, NULL, 'b'},
459 : : {"large-objects", no_argument, NULL, 'b'},
460 : : {"no-blobs", no_argument, NULL, 'B'},
461 : : {"no-large-objects", no_argument, NULL, 'B'},
462 : : {"clean", no_argument, NULL, 'c'},
463 : : {"create", no_argument, NULL, 'C'},
464 : : {"dbname", required_argument, NULL, 'd'},
465 : : {"extension", required_argument, NULL, 'e'},
466 : : {"file", required_argument, NULL, 'f'},
467 : : {"format", required_argument, NULL, 'F'},
468 : : {"host", required_argument, NULL, 'h'},
469 : : {"jobs", 1, NULL, 'j'},
470 : : {"no-reconnect", no_argument, NULL, 'R'},
471 : : {"no-owner", no_argument, NULL, 'O'},
472 : : {"port", required_argument, NULL, 'p'},
473 : : {"schema", required_argument, NULL, 'n'},
474 : : {"exclude-schema", required_argument, NULL, 'N'},
475 : : {"schema-only", no_argument, NULL, 's'},
476 : : {"superuser", required_argument, NULL, 'S'},
477 : : {"table", required_argument, NULL, 't'},
478 : : {"exclude-table", required_argument, NULL, 'T'},
479 : : {"no-password", no_argument, NULL, 'w'},
480 : : {"password", no_argument, NULL, 'W'},
481 : : {"username", required_argument, NULL, 'U'},
482 : : {"verbose", no_argument, NULL, 'v'},
483 : : {"no-privileges", no_argument, NULL, 'x'},
484 : : {"no-acl", no_argument, NULL, 'x'},
485 : : {"compress", required_argument, NULL, 'Z'},
486 : : {"encoding", required_argument, NULL, 'E'},
487 : : {"help", no_argument, NULL, '?'},
488 : : {"version", no_argument, NULL, 'V'},
489 : :
490 : : /*
491 : : * the following options don't have an equivalent short option letter
492 : : */
493 : : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
494 : : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
495 : : {"column-inserts", no_argument, &dopt.column_inserts, 1},
496 : : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
497 : : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
498 : : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
499 : : {"exclude-table-data", required_argument, NULL, 4},
500 : : {"extra-float-digits", required_argument, NULL, 8},
501 : : {"if-exists", no_argument, &dopt.if_exists, 1},
502 : : {"inserts", no_argument, NULL, 9},
503 : : {"lock-wait-timeout", required_argument, NULL, 2},
504 : : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
505 : : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
506 : : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
507 : : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
508 : : {"role", required_argument, NULL, 3},
509 : : {"section", required_argument, NULL, 5},
510 : : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
511 : : {"snapshot", required_argument, NULL, 6},
512 : : {"statistics", no_argument, NULL, 22},
513 : : {"statistics-only", no_argument, NULL, 18},
514 : : {"strict-names", no_argument, &strict_names, 1},
515 : : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
516 : : {"no-comments", no_argument, &dopt.no_comments, 1},
517 : : {"no-data", no_argument, NULL, 19},
518 : : {"no-policies", no_argument, &dopt.no_policies, 1},
519 : : {"no-publications", no_argument, &dopt.no_publications, 1},
520 : : {"no-schema", no_argument, NULL, 20},
521 : : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
522 : : {"no-statistics", no_argument, NULL, 21},
523 : : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
524 : : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
525 : : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
526 : : {"no-sync", no_argument, NULL, 7},
527 : : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
528 : : {"rows-per-insert", required_argument, NULL, 10},
529 : : {"include-foreign-data", required_argument, NULL, 11},
530 : : {"table-and-children", required_argument, NULL, 12},
531 : : {"exclude-table-and-children", required_argument, NULL, 13},
532 : : {"exclude-table-data-and-children", required_argument, NULL, 14},
533 : : {"sync-method", required_argument, NULL, 15},
534 : : {"filter", required_argument, NULL, 16},
535 : : {"exclude-extension", required_argument, NULL, 17},
536 : : {"sequence-data", no_argument, &dopt.sequence_data, 1},
537 : : {"restrict-key", required_argument, NULL, 25},
538 : :
539 : : {NULL, 0, NULL, 0}
540 : : };
541 : :
542 : 302 : pg_logging_init(argv[0]);
543 : 302 : pg_logging_set_level(PG_LOG_WARNING);
544 : 302 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
545 : :
546 : : /*
547 : : * Initialize what we need for parallel execution, especially for thread
548 : : * support on Windows.
549 : : */
550 : 302 : init_parallel_dump_utils();
551 : :
552 : 302 : progname = get_progname(argv[0]);
553 : :
554 [ + - ]: 302 : if (argc > 1)
555 : : {
556 [ + + - + ]: 302 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
557 : : {
558 : 1 : help(progname);
559 : 1 : exit_nicely(0);
560 : : }
561 [ + + + + ]: 301 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
562 : : {
563 : 65 : puts("pg_dump (PostgreSQL) " PG_VERSION);
564 : 65 : exit_nicely(0);
565 : : }
566 : : }
567 : :
568 : 236 : InitDumpOptions(&dopt);
569 : :
570 : 1352 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
571 [ + + ]: 1352 : long_options, &optindex)) != -1)
572 : : {
573 [ + + + + : 1124 : switch (c)
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ + + - +
+ + + + +
+ + - + +
+ + + + +
+ + ]
574 : : {
575 : 9 : case 'a': /* Dump data only */
576 : 9 : data_only = true;
577 : 9 : break;
578 : :
579 : 1 : case 'b': /* Dump LOs */
580 : 1 : dopt.outputLOs = true;
581 : 1 : break;
582 : :
583 : 2 : case 'B': /* Don't dump LOs */
584 : 2 : dopt.dontOutputLOs = true;
585 : 2 : break;
586 : :
587 : 6 : case 'c': /* clean (i.e., drop) schema prior to create */
588 : 6 : dopt.outputClean = 1;
589 : 6 : break;
590 : :
591 : 29 : case 'C': /* Create DB */
592 : 29 : dopt.outputCreateDB = 1;
593 : 29 : break;
594 : :
595 : 5 : case 'd': /* database name */
596 : 5 : dopt.cparams.dbname = pg_strdup(optarg);
597 : 5 : break;
598 : :
599 : 4 : case 'e': /* include extension(s) */
600 : 4 : simple_string_list_append(&extension_include_patterns, optarg);
601 : 4 : dopt.include_everything = false;
602 : 4 : break;
603 : :
604 : 2 : case 'E': /* Dump encoding */
605 : 2 : dumpencoding = pg_strdup(optarg);
606 : 2 : break;
607 : :
608 : 197 : case 'f':
609 : 197 : filename = pg_strdup(optarg);
610 : 197 : break;
611 : :
612 : 116 : case 'F':
613 : 116 : format = pg_strdup(optarg);
614 : 116 : break;
615 : :
616 : 38 : case 'h': /* server host */
617 : 38 : dopt.cparams.pghost = pg_strdup(optarg);
618 : 38 : break;
619 : :
620 : 11 : case 'j': /* number of dump jobs */
621 [ + + ]: 11 : if (!option_parse_int(optarg, "-j/--jobs", 1,
622 : : PG_MAX_JOBS,
623 : : &numWorkers))
624 : 1 : exit_nicely(1);
625 : 10 : break;
626 : :
627 : 18 : case 'n': /* include schema(s) */
628 : 18 : simple_string_list_append(&schema_include_patterns, optarg);
629 : 18 : dopt.include_everything = false;
630 : 18 : break;
631 : :
632 : 1 : case 'N': /* exclude schema(s) */
633 : 1 : simple_string_list_append(&schema_exclude_patterns, optarg);
634 : 1 : break;
635 : :
636 : 2 : case 'O': /* Don't reconnect to match owner */
637 : 2 : dopt.outputNoOwner = 1;
638 : 2 : break;
639 : :
640 : 77 : case 'p': /* server port */
641 : 77 : dopt.cparams.pgport = pg_strdup(optarg);
642 : 77 : break;
643 : :
644 : 2 : case 'R':
645 : : /* no-op, still accepted for backwards compatibility */
646 : 2 : break;
647 : :
648 : 7 : case 's': /* dump schema only */
649 : 7 : schema_only = true;
650 : 7 : break;
651 : :
652 : 1 : case 'S': /* Username for superuser in plain text output */
653 : 1 : dopt.outputSuperuser = pg_strdup(optarg);
654 : 1 : break;
655 : :
656 : 8 : case 't': /* include table(s) */
657 : 8 : simple_string_list_append(&table_include_patterns, optarg);
658 : 8 : dopt.include_everything = false;
659 : 8 : break;
660 : :
661 : 4 : case 'T': /* exclude table(s) */
662 : 4 : simple_string_list_append(&table_exclude_patterns, optarg);
663 : 4 : break;
664 : :
665 : 40 : case 'U':
666 : 40 : dopt.cparams.username = pg_strdup(optarg);
667 : 40 : break;
668 : :
669 : 6 : case 'v': /* verbose */
670 : 6 : g_verbose = true;
671 : 6 : pg_logging_increase_verbosity();
672 : 6 : break;
673 : :
674 : 1 : case 'w':
675 : 1 : dopt.cparams.promptPassword = TRI_NO;
676 : 1 : break;
677 : :
678 : 0 : case 'W':
679 : 0 : dopt.cparams.promptPassword = TRI_YES;
680 : 0 : break;
681 : :
682 : 2 : case 'x': /* skip ACL dump */
683 : 2 : dopt.aclsSkip = true;
684 : 2 : break;
685 : :
686 : 13 : case 'Z': /* Compression */
687 : 13 : parse_compress_options(optarg, &compression_algorithm_str,
688 : : &compression_detail);
689 : 13 : user_compression_defined = true;
690 : 13 : break;
691 : :
692 : 141 : case 0:
693 : : /* This covers the long options. */
694 : 141 : break;
695 : :
696 : 2 : case 2: /* lock-wait-timeout */
697 : 2 : dopt.lockWaitTimeout = pg_strdup(optarg);
698 : 2 : break;
699 : :
700 : 3 : case 3: /* SET ROLE */
701 : 3 : use_role = pg_strdup(optarg);
702 : 3 : break;
703 : :
704 : 1 : case 4: /* exclude table(s) data */
705 : 1 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
706 : 1 : break;
707 : :
708 : 6 : case 5: /* section */
709 : 6 : set_dump_section(optarg, &dopt.dumpSections);
710 : 6 : break;
711 : :
712 : 0 : case 6: /* snapshot */
713 : 0 : dumpsnapshot = pg_strdup(optarg);
714 : 0 : break;
715 : :
716 : 154 : case 7: /* no-sync */
717 : 154 : dosync = false;
718 : 154 : break;
719 : :
720 : 1 : case 8:
721 : 1 : have_extra_float_digits = true;
722 [ + - ]: 1 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
723 : : &extra_float_digits))
724 : 1 : exit_nicely(1);
725 : 0 : break;
726 : :
727 : 2 : case 9: /* inserts */
728 : :
729 : : /*
730 : : * dump_inserts also stores --rows-per-insert, careful not to
731 : : * overwrite that.
732 : : */
733 [ + - ]: 2 : if (dopt.dump_inserts == 0)
734 : 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
735 : 2 : break;
736 : :
737 : 2 : case 10: /* rows per insert */
738 [ + + ]: 2 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
739 : : &dopt.dump_inserts))
740 : 1 : exit_nicely(1);
741 : 1 : break;
742 : :
743 : 4 : case 11: /* include foreign data */
744 : 4 : simple_string_list_append(&foreign_servers_include_patterns,
745 : : optarg);
746 : 4 : break;
747 : :
748 : 1 : case 12: /* include table(s) and their children */
749 : 1 : simple_string_list_append(&table_include_patterns_and_children,
750 : : optarg);
751 : 1 : dopt.include_everything = false;
752 : 1 : break;
753 : :
754 : 1 : case 13: /* exclude table(s) and their children */
755 : 1 : simple_string_list_append(&table_exclude_patterns_and_children,
756 : : optarg);
757 : 1 : break;
758 : :
759 : 1 : case 14: /* exclude data of table(s) and children */
760 : 1 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
761 : : optarg);
762 : 1 : break;
763 : :
764 : 0 : case 15:
765 [ # # ]: 0 : if (!parse_sync_method(optarg, &sync_method))
766 : 0 : exit_nicely(1);
767 : 0 : break;
768 : :
769 : 26 : case 16: /* read object filters from file */
770 : 26 : read_dump_filters(optarg, &dopt);
771 : 22 : break;
772 : :
773 : 1 : case 17: /* exclude extension(s) */
774 : 1 : simple_string_list_append(&extension_exclude_patterns,
775 : : optarg);
776 : 1 : break;
777 : :
778 : 5 : case 18:
779 : 5 : statistics_only = true;
780 : 5 : break;
781 : :
782 : 40 : case 19:
783 : 40 : no_data = true;
784 : 40 : break;
785 : :
786 : 2 : case 20:
787 : 2 : no_schema = true;
788 : 2 : break;
789 : :
790 : 8 : case 21:
791 : 8 : no_statistics = true;
792 : 8 : break;
793 : :
794 : 94 : case 22:
795 : 94 : with_statistics = true;
796 : 94 : break;
797 : :
798 : 26 : case 25:
799 : 26 : dopt.restrict_key = pg_strdup(optarg);
800 : 26 : break;
801 : :
802 : 1 : default:
803 : : /* getopt_long already emitted a complaint */
804 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
805 : 1 : exit_nicely(1);
806 : : }
807 : : }
808 : :
809 : : /*
810 : : * Non-option argument specifies database name as long as it wasn't
811 : : * already specified with -d / --dbname
812 : : */
813 [ + + + - ]: 228 : if (optind < argc && dopt.cparams.dbname == NULL)
814 : 192 : dopt.cparams.dbname = argv[optind++];
815 : :
816 : : /* Complain if any arguments remain */
817 [ + + ]: 228 : if (optind < argc)
818 : : {
819 : 1 : pg_log_error("too many command-line arguments (first is \"%s\")",
820 : : argv[optind]);
821 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
822 : 1 : exit_nicely(1);
823 : : }
824 : :
825 : : /* --column-inserts implies --inserts */
826 [ + + + - ]: 227 : if (dopt.column_inserts && dopt.dump_inserts == 0)
827 : 1 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
828 : :
829 : : /* *-only options are incompatible with each other */
830 : 227 : check_mut_excl_opts(data_only, "-a/--data-only",
831 : : schema_only, "-s/--schema-only",
832 : : statistics_only, "--statistics-only");
833 : :
834 : : /* --no-* and *-only for same thing are incompatible */
835 : 224 : check_mut_excl_opts(data_only, "-a/--data-only",
836 : : no_data, "--no-data");
837 : 224 : check_mut_excl_opts(schema_only, "-s/--schema-only",
838 : : no_schema, "--no-schema");
839 : 224 : check_mut_excl_opts(statistics_only, "--statistics-only",
840 : : no_statistics, "--no-statistics");
841 : :
842 : : /* --statistics and --no-statistics are incompatible */
843 : 223 : check_mut_excl_opts(with_statistics, "--statistics",
844 : : no_statistics, "--no-statistics");
845 : :
846 : : /* --statistics is incompatible with *-only (except --statistics-only) */
847 : 223 : check_mut_excl_opts(with_statistics, "--statistics",
848 : : data_only, "-a/--data-only",
849 : : schema_only, "-s/--schema-only");
850 : :
851 : : /* --include-foreign-data is incompatible with --schema-only */
852 : 222 : check_mut_excl_opts(foreign_servers_include_patterns.head, "--include-foreign-data",
853 : : schema_only, "-s/--schema-only");
854 : :
855 [ + + + + ]: 221 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
856 : 1 : pg_fatal("option %s is not supported with parallel backup",
857 : : "--include-foreign-data");
858 : :
859 : : /* --clean is incompatible with --data-only */
860 : 220 : check_mut_excl_opts(dopt.outputClean, "-c/--clean",
861 : : data_only, "-a/--data-only");
862 : :
863 [ + + + + ]: 219 : if (dopt.if_exists && !dopt.outputClean)
864 : 1 : pg_fatal("option %s requires option %s",
865 : : "--if-exists", "-c/--clean");
866 : :
867 : : /*
868 : : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
869 : : * "--schema-only --no-schema", will have already caused an error in one
870 : : * of the checks above.
871 : : */
872 [ + + + + : 218 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
- + ]
873 [ + - + + ]: 436 : data_only) && !no_data;
874 [ + + + + : 218 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
- + ]
875 [ + - + + ]: 436 : schema_only) && !no_schema;
876 [ - - - - : 218 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
+ + ]
877 [ - + + + : 436 : (statistics_only || with_statistics)) && !no_statistics;
+ - ]
878 : :
879 : :
880 : : /*
881 : : * --inserts are already implied above if --column-inserts or
882 : : * --rows-per-insert were specified.
883 : : */
884 [ + + + - ]: 218 : if (dopt.do_nothing && dopt.dump_inserts == 0)
885 : 1 : pg_fatal("option %s requires option %s, %s, or %s",
886 : : "--on-conflict-do-nothing",
887 : : "--inserts", "--rows-per-insert", "--column-inserts");
888 : :
889 : : /* Identify archive format to emit */
890 : 217 : archiveFormat = parseArchiveFormat(format, &archiveMode);
891 : :
892 : : /* archiveFormat specific setup */
893 [ + + ]: 216 : if (archiveFormat == archNull)
894 : : {
895 : 152 : plainText = 1;
896 : :
897 : : /*
898 : : * If you don't provide a restrict key, one will be appointed for you.
899 : : */
900 [ + + ]: 152 : if (!dopt.restrict_key)
901 : 126 : dopt.restrict_key = generate_restrict_key();
902 [ - + ]: 152 : if (!dopt.restrict_key)
903 : 0 : pg_fatal("could not generate restrict key");
904 [ - + ]: 152 : if (!valid_restrict_key(dopt.restrict_key))
905 : 0 : pg_fatal("invalid restrict key");
906 : : }
907 [ - + ]: 64 : else if (dopt.restrict_key)
908 : 0 : pg_fatal("option %s can only be used with %s",
909 : : "--restrict-key", "--format=plain");
910 : :
911 : : /*
912 : : * Custom and directory formats are compressed by default with gzip when
913 : : * available, not the others. If gzip is not available, no compression is
914 : : * done by default.
915 : : */
916 [ + + + + ]: 216 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
917 [ + + ]: 61 : !user_compression_defined)
918 : : {
919 : : #ifdef HAVE_LIBZ
920 : 55 : compression_algorithm_str = "gzip";
921 : : #else
922 : : compression_algorithm_str = "none";
923 : : #endif
924 : : }
925 : :
926 : : /*
927 : : * Compression options
928 : : */
929 [ + + ]: 216 : if (!parse_compress_algorithm(compression_algorithm_str,
930 : : &compression_algorithm))
931 : 1 : pg_fatal("unrecognized compression algorithm: \"%s\"",
932 : : compression_algorithm_str);
933 : :
934 : 215 : parse_compress_specification(compression_algorithm, compression_detail,
935 : : &compression_spec);
936 : 215 : error_detail = validate_compress_specification(&compression_spec);
937 [ + + ]: 215 : if (error_detail != NULL)
938 : 3 : pg_fatal("invalid compression specification: %s",
939 : : error_detail);
940 : :
941 : 212 : error_detail = supports_compression(compression_spec);
942 [ - + ]: 212 : if (error_detail != NULL)
943 : 0 : pg_fatal("%s", error_detail);
944 : :
945 : : /*
946 : : * Disable support for zstd workers for now - these are based on
947 : : * threading, and it's unclear how it interacts with parallel dumps on
948 : : * platforms where that relies on threads too (e.g. Windows).
949 : : */
950 [ - + ]: 212 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
951 : 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
952 : : "workers");
953 : :
954 : : /*
955 : : * If emitting an archive format, we always want to emit a DATABASE item,
956 : : * in case --create is specified at pg_restore time.
957 : : */
958 [ + + ]: 212 : if (!plainText)
959 : 64 : dopt.outputCreateDB = 1;
960 : :
961 : : /* Parallel backup only in the directory archive format so far */
962 [ + + + + ]: 212 : if (archiveFormat != archDirectory && numWorkers > 1)
963 : 1 : pg_fatal("parallel backup only supported by the directory format");
964 : :
965 : : /* Open the output file */
966 : 211 : fout = CreateArchive(filename, archiveFormat, compression_spec,
967 : : dosync, archiveMode, setupDumpWorker, sync_method);
968 : :
969 : : /* Make dump options accessible right away */
970 : 210 : SetArchiveOptions(fout, &dopt, NULL);
971 : :
972 : : /* Register the cleanup hook */
973 : 210 : on_exit_close_archive(fout);
974 : :
975 : : /* Let the archiver know how noisy to be */
976 : 210 : fout->verbose = g_verbose;
977 : :
978 : :
979 : : /*
980 : : * We allow the server to be back to 10, and up to any minor release of
981 : : * our own major version. (See also version check in pg_dumpall.c.)
982 : : */
983 : 210 : fout->minRemoteVersion = 100000;
984 : 210 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
985 : :
986 : 210 : fout->numWorkers = numWorkers;
987 : :
988 : : /*
989 : : * Open the database using the Archiver, so it knows about it. Errors mean
990 : : * death.
991 : : */
992 : 210 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
993 : 208 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
994 : :
995 : : /*
996 : : * On hot standbys, never try to dump unlogged table data, since it will
997 : : * just throw an error.
998 : : */
999 [ + + ]: 208 : if (fout->isStandby)
1000 : 4 : dopt.no_unlogged_table_data = true;
1001 : :
1002 : : /*
1003 : : * Find the last built-in OID, if needed (prior to 8.1)
1004 : : *
1005 : : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1006 : : */
1007 : 208 : g_last_builtin_oid = FirstNormalObjectId - 1;
1008 : :
1009 : 208 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1010 : :
1011 : : /* Expand schema selection patterns into OID lists */
1012 [ + + ]: 208 : if (schema_include_patterns.head != NULL)
1013 : : {
1014 : 19 : expand_schema_name_patterns(fout, &schema_include_patterns,
1015 : : &schema_include_oids,
1016 : : strict_names);
1017 [ + + ]: 13 : if (schema_include_oids.head == NULL)
1018 : 1 : pg_fatal("no matching schemas were found");
1019 : : }
1020 : 201 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1021 : : &schema_exclude_oids,
1022 : : false);
1023 : : /* non-matching exclusion patterns aren't an error */
1024 : :
1025 : : /* Expand table selection patterns into OID lists */
1026 : 201 : expand_table_name_patterns(fout, &table_include_patterns,
1027 : : &table_include_oids,
1028 : : strict_names, false);
1029 : 196 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1030 : : &table_include_oids,
1031 : : strict_names, true);
1032 [ + + ]: 196 : if ((table_include_patterns.head != NULL ||
1033 [ + + ]: 185 : table_include_patterns_and_children.head != NULL) &&
1034 [ + + ]: 13 : table_include_oids.head == NULL)
1035 : 2 : pg_fatal("no matching tables were found");
1036 : :
1037 : 194 : expand_table_name_patterns(fout, &table_exclude_patterns,
1038 : : &table_exclude_oids,
1039 : : false, false);
1040 : 194 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1041 : : &table_exclude_oids,
1042 : : false, true);
1043 : :
1044 : 194 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1045 : : &tabledata_exclude_oids,
1046 : : false, false);
1047 : 194 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1048 : : &tabledata_exclude_oids,
1049 : : false, true);
1050 : :
1051 : 194 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1052 : : &foreign_servers_include_oids);
1053 : :
1054 : : /* non-matching exclusion patterns aren't an error */
1055 : :
1056 : : /* Expand extension selection patterns into OID lists */
1057 [ + + ]: 193 : if (extension_include_patterns.head != NULL)
1058 : : {
1059 : 5 : expand_extension_name_patterns(fout, &extension_include_patterns,
1060 : : &extension_include_oids,
1061 : : strict_names);
1062 [ + + ]: 5 : if (extension_include_oids.head == NULL)
1063 : 1 : pg_fatal("no matching extensions were found");
1064 : : }
1065 : 192 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1066 : : &extension_exclude_oids,
1067 : : false);
1068 : : /* non-matching exclusion patterns aren't an error */
1069 : :
1070 : : /*
1071 : : * Dumping LOs is the default for dumps where an inclusion switch is not
1072 : : * used (an "include everything" dump). -B can be used to exclude LOs
1073 : : * from those dumps. -b can be used to include LOs even when an inclusion
1074 : : * switch is used.
1075 : : *
1076 : : * -s means "schema only" and LOs are data, not schema, so we never
1077 : : * include LOs when -s is used.
1078 : : */
1079 [ + + + + : 192 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
+ + ]
1080 : 122 : dopt.outputLOs = true;
1081 : :
1082 : : /*
1083 : : * Collect role names so we can map object owner OIDs to names.
1084 : : */
1085 : 192 : collectRoleNames(fout);
1086 : :
1087 : : /*
1088 : : * Now scan the database and create DumpableObject structs for all the
1089 : : * objects we intend to dump.
1090 : : */
1091 : 192 : tblinfo = getSchemaData(fout, &numTables);
1092 : :
1093 [ + + ]: 191 : if (dopt.dumpData)
1094 : : {
1095 : 146 : getTableData(&dopt, tblinfo, numTables, 0);
1096 : 146 : buildMatViewRefreshDependencies(fout);
1097 [ + + ]: 146 : if (!dopt.dumpSchema)
1098 : 7 : getTableDataFKConstraints();
1099 : : }
1100 : :
1101 [ + + + + ]: 191 : if (!dopt.dumpData && dopt.sequence_data)
1102 : 36 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1103 : :
1104 : : /*
1105 : : * For binary upgrade mode, dump the pg_shdepend rows for large objects
1106 : : * and maybe even pg_largeobject_metadata (see comment below for details).
1107 : : * This is faster to restore than the equivalent set of large object
1108 : : * commands.
1109 : : */
1110 [ + + ]: 191 : if (dopt.binary_upgrade)
1111 : : {
1112 : : TableInfo *shdepend;
1113 : :
1114 : 40 : shdepend = findTableByOid(SharedDependRelationId);
1115 : 40 : makeTableDataInfo(&dopt, shdepend);
1116 : :
1117 : : /*
1118 : : * Only dump large object shdepend rows for this database.
1119 : : */
1120 : 40 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1121 : : "AND dbid = (SELECT oid FROM pg_database "
1122 : : " WHERE datname = current_database())";
1123 : :
1124 : : /*
1125 : : * For binary upgrades from v16 and newer versions, we can copy
1126 : : * pg_largeobject_metadata's files from the old cluster, so we don't
1127 : : * need to dump its contents. pg_upgrade can't copy/link the files
1128 : : * from older versions because aclitem (needed by
1129 : : * pg_largeobject_metadata.lomacl) changed its storage format in v16.
1130 : : */
1131 [ - + ]: 40 : if (fout->remoteVersion < 160000)
1132 : : {
1133 : : TableInfo *lo_metadata;
1134 : :
1135 : 0 : lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1136 : 0 : makeTableDataInfo(&dopt, lo_metadata);
1137 : : }
1138 : : }
1139 : :
1140 : : /*
1141 : : * In binary-upgrade mode, we do not have to worry about the actual LO
1142 : : * data or the associated metadata that resides in the pg_largeobject and
1143 : : * pg_largeobject_metadata tables, respectively.
1144 : : *
1145 : : * However, we do need to collect LO information as there may be comments
1146 : : * or other information on LOs that we do need to dump out.
1147 : : */
1148 [ + + + + ]: 191 : if (dopt.outputLOs || dopt.binary_upgrade)
1149 : 162 : getLOs(fout);
1150 : :
1151 : : /*
1152 : : * Collect dependency data to assist in ordering the objects.
1153 : : */
1154 : 191 : getDependencies(fout);
1155 : :
1156 : : /*
1157 : : * Collect ACLs, comments, and security labels, if wanted.
1158 : : */
1159 [ + + ]: 191 : if (!dopt.aclsSkip)
1160 : 189 : getAdditionalACLs(fout);
1161 [ + - ]: 191 : if (!dopt.no_comments)
1162 : 191 : collectComments(fout);
1163 [ + - ]: 191 : if (!dopt.no_security_labels)
1164 : 191 : collectSecLabels(fout);
1165 : :
1166 : : /* For binary upgrade mode, collect required pg_class information. */
1167 [ + + ]: 191 : if (dopt.binary_upgrade)
1168 : 40 : collectBinaryUpgradeClassOids(fout);
1169 : :
1170 : : /* Collect sequence information. */
1171 : 191 : collectSequences(fout);
1172 : :
1173 : : /* Lastly, create dummy objects to represent the section boundaries */
1174 : 191 : boundaryObjs = createBoundaryObjects();
1175 : :
1176 : : /* Get pointers to all the known DumpableObjects */
1177 : 191 : getDumpableObjects(&dobjs, &numObjs);
1178 : :
1179 : : /*
1180 : : * Add dummy dependencies to enforce the dump section ordering.
1181 : : */
1182 : 191 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1183 : :
1184 : : /*
1185 : : * Sort the objects into a safe dump order (no forward references).
1186 : : *
1187 : : * We rely on dependency information to help us determine a safe order, so
1188 : : * the initial sort is mostly for cosmetic purposes: we sort by name to
1189 : : * ensure that logically identical schemas will dump identically.
1190 : : */
1191 : 191 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1192 : :
1193 : 191 : sortDumpableObjects(dobjs, numObjs,
1194 : 191 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1195 : :
1196 : : /*
1197 : : * Create archive TOC entries for all the objects to be dumped, in a safe
1198 : : * order.
1199 : : */
1200 : :
1201 : : /*
1202 : : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1203 : : */
1204 : 191 : dumpEncoding(fout);
1205 : 191 : dumpStdStrings(fout);
1206 : 191 : dumpSearchPath(fout);
1207 : :
1208 : : /* The database items are always next, unless we don't want them at all */
1209 [ + + ]: 191 : if (dopt.outputCreateDB)
1210 : 92 : dumpDatabase(fout);
1211 : :
1212 : : /* Now the rearrangeable objects. */
1213 [ + + ]: 738561 : for (i = 0; i < numObjs; i++)
1214 : 738370 : dumpDumpableObject(fout, dobjs[i]);
1215 : :
1216 : : /*
1217 : : * Set up options info to ensure we dump what we want.
1218 : : */
1219 : 191 : ropt = NewRestoreOptions();
1220 : 191 : ropt->filename = filename;
1221 : :
1222 : : /* if you change this list, see dumpOptionsFromRestoreOptions */
1223 [ + + ]: 191 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1224 [ + + ]: 191 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1225 [ + + ]: 191 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1226 [ + + ]: 191 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1227 : 191 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1228 : 191 : ropt->dropSchema = dopt.outputClean;
1229 : 191 : ropt->dumpData = dopt.dumpData;
1230 : 191 : ropt->dumpSchema = dopt.dumpSchema;
1231 : 191 : ropt->dumpStatistics = dopt.dumpStatistics;
1232 : 191 : ropt->if_exists = dopt.if_exists;
1233 : 191 : ropt->column_inserts = dopt.column_inserts;
1234 : 191 : ropt->dumpSections = dopt.dumpSections;
1235 : 191 : ropt->aclsSkip = dopt.aclsSkip;
1236 : 191 : ropt->superuser = dopt.outputSuperuser;
1237 : 191 : ropt->createDB = dopt.outputCreateDB;
1238 : 191 : ropt->noOwner = dopt.outputNoOwner;
1239 : 191 : ropt->noTableAm = dopt.outputNoTableAm;
1240 : 191 : ropt->noTablespace = dopt.outputNoTablespaces;
1241 : 191 : ropt->disable_triggers = dopt.disable_triggers;
1242 : 191 : ropt->use_setsessauth = dopt.use_setsessauth;
1243 : 191 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1244 : 191 : ropt->dump_inserts = dopt.dump_inserts;
1245 : 191 : ropt->no_comments = dopt.no_comments;
1246 : 191 : ropt->no_policies = dopt.no_policies;
1247 : 191 : ropt->no_publications = dopt.no_publications;
1248 : 191 : ropt->no_security_labels = dopt.no_security_labels;
1249 : 191 : ropt->no_subscriptions = dopt.no_subscriptions;
1250 : 191 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1251 : 191 : ropt->include_everything = dopt.include_everything;
1252 : 191 : ropt->enable_row_security = dopt.enable_row_security;
1253 : 191 : ropt->sequence_data = dopt.sequence_data;
1254 : 191 : ropt->binary_upgrade = dopt.binary_upgrade;
1255 [ + + ]: 191 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1256 : :
1257 : 191 : ropt->compression_spec = compression_spec;
1258 : :
1259 : 191 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1260 : :
1261 : 191 : SetArchiveOptions(fout, &dopt, ropt);
1262 : :
1263 : : /* Mark which entries should be output */
1264 : 191 : ProcessArchiveRestoreOptions(fout);
1265 : :
1266 : : /*
1267 : : * The archive's TOC entries are now marked as to which ones will actually
1268 : : * be output, so we can set up their dependency lists properly. This isn't
1269 : : * necessary for plain-text output, though.
1270 : : */
1271 [ + + ]: 191 : if (!plainText)
1272 : 63 : BuildArchiveDependencies(fout);
1273 : :
1274 : : /*
1275 : : * And finally we can do the actual output.
1276 : : *
1277 : : * Note: for non-plain-text output formats, the output file is written
1278 : : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1279 : : * right now.
1280 : : */
1281 [ + + ]: 191 : if (plainText)
1282 : 128 : RestoreArchive(fout);
1283 : :
1284 : 190 : CloseArchive(fout);
1285 : :
1286 : 190 : exit_nicely(0);
1287 : : }
1288 : :
1289 : :
1290 : : static void
1291 : 1 : help(const char *progname)
1292 : : {
1293 : 1 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1294 : 1 : printf(_("Usage:\n"));
1295 : 1 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1296 : :
1297 : 1 : printf(_("\nGeneral options:\n"));
1298 : 1 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1299 : 1 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1300 : : " plain text (default))\n"));
1301 : 1 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1302 : 1 : printf(_(" -v, --verbose verbose mode\n"));
1303 : 1 : printf(_(" -V, --version output version information, then exit\n"));
1304 : 1 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1305 : : " compress as specified\n"));
1306 : 1 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1307 : 1 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1308 : 1 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1309 : 1 : printf(_(" -?, --help show this help, then exit\n"));
1310 : :
1311 : 1 : printf(_("\nOptions controlling the output content:\n"));
1312 : 1 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1313 : 1 : printf(_(" -b, --large-objects include large objects in dump\n"));
1314 : 1 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1315 : 1 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1316 : 1 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1317 : 1 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1318 : 1 : printf(_(" -C, --create include commands to create database in dump\n"));
1319 : 1 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1320 : 1 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1321 : 1 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1322 : 1 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1323 : 1 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1324 : : " plain-text format\n"));
1325 : 1 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1326 : 1 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1327 : 1 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1328 : 1 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1329 : 1 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1330 : 1 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1331 : 1 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1332 : 1 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1333 : 1 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1334 : 1 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1335 : : " access to)\n"));
1336 : 1 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1337 : 1 : printf(_(" --exclude-table-and-children=PATTERN\n"
1338 : : " do NOT dump the specified table(s), including\n"
1339 : : " child and partition tables\n"));
1340 : 1 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1341 : 1 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1342 : : " do NOT dump data for the specified table(s),\n"
1343 : : " including child and partition tables\n"));
1344 : 1 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1345 : 1 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1346 : : " based on expressions in FILENAME\n"));
1347 : 1 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1348 : 1 : printf(_(" --include-foreign-data=PATTERN\n"
1349 : : " include data of foreign tables on foreign\n"
1350 : : " servers matching PATTERN\n"));
1351 : 1 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1352 : 1 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1353 : 1 : printf(_(" --no-comments do not dump comment commands\n"));
1354 : 1 : printf(_(" --no-data do not dump data\n"));
1355 : 1 : printf(_(" --no-policies do not dump row security policies\n"));
1356 : 1 : printf(_(" --no-publications do not dump publications\n"));
1357 : 1 : printf(_(" --no-schema do not dump schema\n"));
1358 : 1 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1359 : 1 : printf(_(" --no-statistics do not dump statistics\n"));
1360 : 1 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1361 : 1 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1362 : 1 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1363 : 1 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1364 : 1 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1365 : 1 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1366 : 1 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1367 : 1 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1368 : 1 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1369 : 1 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1370 : 1 : printf(_(" --sequence-data include sequence data in dump\n"));
1371 : 1 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1372 : 1 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1373 : 1 : printf(_(" --statistics dump the statistics\n"));
1374 : 1 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1375 : 1 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1376 : : " match at least one entity each\n"));
1377 : 1 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1378 : : " child and partition tables\n"));
1379 : 1 : printf(_(" --use-set-session-authorization\n"
1380 : : " use SET SESSION AUTHORIZATION commands instead of\n"
1381 : : " ALTER OWNER commands to set ownership\n"));
1382 : :
1383 : 1 : printf(_("\nConnection options:\n"));
1384 : 1 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1385 : 1 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1386 : 1 : printf(_(" -p, --port=PORT database server port number\n"));
1387 : 1 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1388 : 1 : printf(_(" -w, --no-password never prompt for password\n"));
1389 : 1 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1390 : 1 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1391 : :
1392 : 1 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1393 : : "variable value is used.\n\n"));
1394 : 1 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1395 : 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1396 : 1 : }
1397 : :
1398 : : static void
1399 : 224 : setup_connection(Archive *AH, const char *dumpencoding,
1400 : : const char *dumpsnapshot, char *use_role)
1401 : : {
1402 : 224 : DumpOptions *dopt = AH->dopt;
1403 : 224 : PGconn *conn = GetConnection(AH);
1404 : :
1405 : 224 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1406 : :
1407 : : /*
1408 : : * Set the client encoding if requested.
1409 : : */
1410 [ + + ]: 224 : if (dumpencoding)
1411 : : {
1412 [ - + ]: 18 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1413 : 0 : pg_fatal("invalid client encoding \"%s\" specified",
1414 : : dumpencoding);
1415 : : }
1416 : :
1417 : : /*
1418 : : * Force standard_conforming_strings on, just in case we are dumping from
1419 : : * an old server that has it disabled. Without this, literals in views,
1420 : : * expressions, etc, would be incorrect for modern servers.
1421 : : */
1422 : 224 : ExecuteSqlStatement(AH, "SET standard_conforming_strings = on");
1423 : :
1424 : : /*
1425 : : * And reflect that to AH->std_strings. You might think that we should
1426 : : * just delete that variable and the code that checks it, but that would
1427 : : * be problematic for pg_restore, which at least for now should still cope
1428 : : * with archives containing the other setting (cf. processStdStringsEntry
1429 : : * in pg_backup_archiver.c).
1430 : : */
1431 : 224 : AH->std_strings = true;
1432 : :
1433 : : /*
1434 : : * Get the active encoding, so we know how to escape strings.
1435 : : */
1436 : 224 : AH->encoding = PQclientEncoding(conn);
1437 : 224 : setFmtEncoding(AH->encoding);
1438 : :
1439 : : /*
1440 : : * Set the role if requested. In a parallel dump worker, we'll be passed
1441 : : * use_role == NULL, but AH->use_role is already set (if user specified it
1442 : : * originally) and we should use that.
1443 : : */
1444 [ + + + + ]: 224 : if (!use_role && AH->use_role)
1445 : 2 : use_role = AH->use_role;
1446 : :
1447 : : /* Set the role if requested */
1448 [ + + ]: 224 : if (use_role)
1449 : : {
1450 : 5 : PQExpBuffer query = createPQExpBuffer();
1451 : :
1452 : 5 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1453 : 5 : ExecuteSqlStatement(AH, query->data);
1454 : 5 : destroyPQExpBuffer(query);
1455 : :
1456 : : /* save it for possible later use by parallel workers */
1457 [ + + ]: 5 : if (!AH->use_role)
1458 : 3 : AH->use_role = pg_strdup(use_role);
1459 : : }
1460 : :
1461 : : /* Set the datestyle to ISO to ensure the dump's portability */
1462 : 224 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1463 : :
1464 : : /* Likewise, avoid using sql_standard intervalstyle */
1465 : 224 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1466 : :
1467 : : /*
1468 : : * Use an explicitly specified extra_float_digits if it has been provided.
1469 : : * Otherwise, set extra_float_digits so that we can dump float data
1470 : : * exactly (given correctly implemented float I/O code, anyway).
1471 : : */
1472 [ - + ]: 224 : if (have_extra_float_digits)
1473 : : {
1474 : 0 : PQExpBuffer q = createPQExpBuffer();
1475 : :
1476 : 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1477 : : extra_float_digits);
1478 : 0 : ExecuteSqlStatement(AH, q->data);
1479 : 0 : destroyPQExpBuffer(q);
1480 : : }
1481 : : else
1482 : 224 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1483 : :
1484 : : /*
1485 : : * Disable synchronized scanning, to prevent unpredictable changes in row
1486 : : * ordering across a dump and reload.
1487 : : */
1488 : 224 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1489 : :
1490 : : /*
1491 : : * Disable timeouts if supported.
1492 : : */
1493 : 224 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1494 : 224 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1495 : 224 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1496 [ + - ]: 224 : if (AH->remoteVersion >= 170000)
1497 : 224 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1498 : :
1499 : : /*
1500 : : * Quote all identifiers, if requested.
1501 : : */
1502 [ + + ]: 224 : if (quote_all_identifiers)
1503 : 38 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1504 : :
1505 : : /*
1506 : : * Adjust row-security mode, if supported.
1507 : : */
1508 [ - + ]: 224 : if (dopt->enable_row_security)
1509 : 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1510 : : else
1511 : 224 : ExecuteSqlStatement(AH, "SET row_security = off");
1512 : :
1513 : : /*
1514 : : * For security reasons, we restrict the expansion of non-system views and
1515 : : * access to foreign tables during the pg_dump process. This restriction
1516 : : * is adjusted when dumping foreign table data.
1517 : : */
1518 : 224 : set_restrict_relation_kind(AH, "view, foreign-table");
1519 : :
1520 : : /*
1521 : : * Initialize prepared-query state to "nothing prepared". We do this here
1522 : : * so that a parallel dump worker will have its own state.
1523 : : */
1524 : 224 : AH->is_prepared = pg_malloc0_array(bool, NUM_PREP_QUERIES);
1525 : :
1526 : : /*
1527 : : * Start transaction-snapshot mode transaction to dump consistent data.
1528 : : */
1529 : 224 : ExecuteSqlStatement(AH, "BEGIN");
1530 : :
1531 : : /*
1532 : : * To support the combination of serializable_deferrable with the jobs
1533 : : * option we use REPEATABLE READ for the worker connections that are
1534 : : * passed a snapshot. As long as the snapshot is acquired in a
1535 : : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1536 : : * REPEATABLE READ transaction provides the appropriate integrity
1537 : : * guarantees. This is a kluge, but safe for back-patching.
1538 : : */
1539 [ - + - - ]: 224 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1540 : 0 : ExecuteSqlStatement(AH,
1541 : : "SET TRANSACTION ISOLATION LEVEL "
1542 : : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1543 : : else
1544 : 224 : ExecuteSqlStatement(AH,
1545 : : "SET TRANSACTION ISOLATION LEVEL "
1546 : : "REPEATABLE READ, READ ONLY");
1547 : :
1548 : : /*
1549 : : * If user specified a snapshot to use, select that. In a parallel dump
1550 : : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1551 : : * is already set (if the server can handle it) and we should use that.
1552 : : */
1553 [ - + ]: 224 : if (dumpsnapshot)
1554 : 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1555 : :
1556 [ + + ]: 224 : if (AH->sync_snapshot_id)
1557 : : {
1558 : 16 : PQExpBuffer query = createPQExpBuffer();
1559 : :
1560 : 16 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1561 : 16 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1562 : 16 : ExecuteSqlStatement(AH, query->data);
1563 : 16 : destroyPQExpBuffer(query);
1564 : : }
1565 [ + + ]: 208 : else if (AH->numWorkers > 1)
1566 : 8 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1567 : 224 : }
1568 : :
1569 : : /* Set up connection for a parallel worker process */
1570 : : static void
1571 : 16 : setupDumpWorker(Archive *AH)
1572 : : {
1573 : : /*
1574 : : * We want to re-select all the same values the leader connection is
1575 : : * using. We'll have inherited directly-usable values in
1576 : : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1577 : : * inherited encoding value back to a string to pass to setup_connection.
1578 : : */
1579 : 16 : setup_connection(AH,
1580 : : pg_encoding_to_char(AH->encoding),
1581 : : NULL,
1582 : : NULL);
1583 : 16 : }
1584 : :
1585 : : static char *
1586 : 8 : get_synchronized_snapshot(Archive *fout)
1587 : : {
1588 : 8 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1589 : : char *result;
1590 : : PGresult *res;
1591 : :
1592 : 8 : res = ExecuteSqlQueryForSingleRow(fout, query);
1593 : 8 : result = pg_strdup(PQgetvalue(res, 0, 0));
1594 : 8 : PQclear(res);
1595 : :
1596 : 8 : return result;
1597 : : }
1598 : :
1599 : : static ArchiveFormat
1600 : 217 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1601 : : {
1602 : : ArchiveFormat archiveFormat;
1603 : :
1604 : 217 : *mode = archModeWrite;
1605 : :
1606 [ + + - + ]: 217 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1607 : : {
1608 : : /* This is used by pg_dumpall, and is not documented */
1609 : 48 : archiveFormat = archNull;
1610 : 48 : *mode = archModeAppend;
1611 : : }
1612 [ - + ]: 169 : else if (pg_strcasecmp(format, "c") == 0)
1613 : 0 : archiveFormat = archCustom;
1614 [ + + ]: 169 : else if (pg_strcasecmp(format, "custom") == 0)
1615 : 51 : archiveFormat = archCustom;
1616 [ - + ]: 118 : else if (pg_strcasecmp(format, "d") == 0)
1617 : 0 : archiveFormat = archDirectory;
1618 [ + + ]: 118 : else if (pg_strcasecmp(format, "directory") == 0)
1619 : 10 : archiveFormat = archDirectory;
1620 [ + + ]: 108 : else if (pg_strcasecmp(format, "p") == 0)
1621 : 101 : archiveFormat = archNull;
1622 [ + + ]: 7 : else if (pg_strcasecmp(format, "plain") == 0)
1623 : 3 : archiveFormat = archNull;
1624 [ - + ]: 4 : else if (pg_strcasecmp(format, "t") == 0)
1625 : 0 : archiveFormat = archTar;
1626 [ + + ]: 4 : else if (pg_strcasecmp(format, "tar") == 0)
1627 : 3 : archiveFormat = archTar;
1628 : : else
1629 : 1 : pg_fatal("invalid output format \"%s\" specified", format);
1630 : 216 : return archiveFormat;
1631 : : }
1632 : :
1633 : : /*
1634 : : * Find the OIDs of all schemas matching the given list of patterns,
1635 : : * and append them to the given OID list.
1636 : : */
1637 : : static void
1638 : 220 : expand_schema_name_patterns(Archive *fout,
1639 : : SimpleStringList *patterns,
1640 : : SimpleOidList *oids,
1641 : : bool strict_names)
1642 : : {
1643 : : PQExpBuffer query;
1644 : : PGresult *res;
1645 : : SimpleStringListCell *cell;
1646 : : int i;
1647 : :
1648 [ + + ]: 220 : if (patterns->head == NULL)
1649 : 198 : return; /* nothing to do */
1650 : :
1651 : 22 : query = createPQExpBuffer();
1652 : :
1653 : : /*
1654 : : * The loop below runs multiple SELECTs might sometimes result in
1655 : : * duplicate entries in the OID list, but we don't care.
1656 : : */
1657 : :
1658 [ + + ]: 38 : for (cell = patterns->head; cell; cell = cell->next)
1659 : : {
1660 : : PQExpBufferData dbbuf;
1661 : : int dotcnt;
1662 : :
1663 : 22 : appendPQExpBufferStr(query,
1664 : : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1665 : 22 : initPQExpBuffer(&dbbuf);
1666 : 22 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1667 : : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1668 : : &dotcnt);
1669 [ + + ]: 22 : if (dotcnt > 1)
1670 : 2 : pg_fatal("improper qualified name (too many dotted names): %s",
1671 : : cell->val);
1672 [ + + ]: 20 : else if (dotcnt == 1)
1673 : 3 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1674 : 17 : termPQExpBuffer(&dbbuf);
1675 : :
1676 : 17 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1677 [ + + + - ]: 17 : if (strict_names && PQntuples(res) == 0)
1678 : 1 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1679 : :
1680 [ + + ]: 31 : for (i = 0; i < PQntuples(res); i++)
1681 : : {
1682 : 15 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1683 : : }
1684 : :
1685 : 16 : PQclear(res);
1686 : 16 : resetPQExpBuffer(query);
1687 : : }
1688 : :
1689 : 16 : destroyPQExpBuffer(query);
1690 : : }
1691 : :
1692 : : /*
1693 : : * Find the OIDs of all extensions matching the given list of patterns,
1694 : : * and append them to the given OID list.
1695 : : */
1696 : : static void
1697 : 197 : expand_extension_name_patterns(Archive *fout,
1698 : : SimpleStringList *patterns,
1699 : : SimpleOidList *oids,
1700 : : bool strict_names)
1701 : : {
1702 : : PQExpBuffer query;
1703 : : PGresult *res;
1704 : : SimpleStringListCell *cell;
1705 : : int i;
1706 : :
1707 [ + + ]: 197 : if (patterns->head == NULL)
1708 : 190 : return; /* nothing to do */
1709 : :
1710 : 7 : query = createPQExpBuffer();
1711 : :
1712 : : /*
1713 : : * The loop below runs multiple SELECTs might sometimes result in
1714 : : * duplicate entries in the OID list, but we don't care.
1715 : : */
1716 [ + + ]: 14 : for (cell = patterns->head; cell; cell = cell->next)
1717 : : {
1718 : : int dotcnt;
1719 : :
1720 : 7 : appendPQExpBufferStr(query,
1721 : : "SELECT oid FROM pg_catalog.pg_extension e\n");
1722 : 7 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1723 : : false, NULL, "e.extname", NULL, NULL, NULL,
1724 : : &dotcnt);
1725 [ - + ]: 7 : if (dotcnt > 0)
1726 : 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1727 : : cell->val);
1728 : :
1729 : 7 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1730 [ - + - - ]: 7 : if (strict_names && PQntuples(res) == 0)
1731 : 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1732 : :
1733 [ + + ]: 13 : for (i = 0; i < PQntuples(res); i++)
1734 : : {
1735 : 6 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1736 : : }
1737 : :
1738 : 7 : PQclear(res);
1739 : 7 : resetPQExpBuffer(query);
1740 : : }
1741 : :
1742 : 7 : destroyPQExpBuffer(query);
1743 : : }
1744 : :
1745 : : /*
1746 : : * Find the OIDs of all foreign servers matching the given list of patterns,
1747 : : * and append them to the given OID list.
1748 : : */
1749 : : static void
1750 : 194 : expand_foreign_server_name_patterns(Archive *fout,
1751 : : SimpleStringList *patterns,
1752 : : SimpleOidList *oids)
1753 : : {
1754 : : PQExpBuffer query;
1755 : : PGresult *res;
1756 : : SimpleStringListCell *cell;
1757 : : int i;
1758 : :
1759 [ + + ]: 194 : if (patterns->head == NULL)
1760 : 191 : return; /* nothing to do */
1761 : :
1762 : 3 : query = createPQExpBuffer();
1763 : :
1764 : : /*
1765 : : * The loop below runs multiple SELECTs might sometimes result in
1766 : : * duplicate entries in the OID list, but we don't care.
1767 : : */
1768 : :
1769 [ + + ]: 5 : for (cell = patterns->head; cell; cell = cell->next)
1770 : : {
1771 : : int dotcnt;
1772 : :
1773 : 3 : appendPQExpBufferStr(query,
1774 : : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1775 : 3 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1776 : : false, NULL, "s.srvname", NULL, NULL, NULL,
1777 : : &dotcnt);
1778 [ - + ]: 3 : if (dotcnt > 0)
1779 : 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1780 : : cell->val);
1781 : :
1782 : 3 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1783 [ + + ]: 3 : if (PQntuples(res) == 0)
1784 : 1 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1785 : :
1786 [ + + ]: 4 : for (i = 0; i < PQntuples(res); i++)
1787 : 2 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1788 : :
1789 : 2 : PQclear(res);
1790 : 2 : resetPQExpBuffer(query);
1791 : : }
1792 : :
1793 : 2 : destroyPQExpBuffer(query);
1794 : : }
1795 : :
1796 : : /*
1797 : : * Find the OIDs of all tables matching the given list of patterns,
1798 : : * and append them to the given OID list. See also expand_dbname_patterns()
1799 : : * in pg_dumpall.c
1800 : : */
1801 : : static void
1802 : 1173 : expand_table_name_patterns(Archive *fout,
1803 : : SimpleStringList *patterns, SimpleOidList *oids,
1804 : : bool strict_names, bool with_child_tables)
1805 : : {
1806 : : PQExpBuffer query;
1807 : : PGresult *res;
1808 : : SimpleStringListCell *cell;
1809 : : int i;
1810 : :
1811 [ + + ]: 1173 : if (patterns->head == NULL)
1812 : 1144 : return; /* nothing to do */
1813 : :
1814 : 29 : query = createPQExpBuffer();
1815 : :
1816 : : /*
1817 : : * this might sometimes result in duplicate entries in the OID list, but
1818 : : * we don't care.
1819 : : */
1820 : :
1821 [ + + ]: 59 : for (cell = patterns->head; cell; cell = cell->next)
1822 : : {
1823 : : PQExpBufferData dbbuf;
1824 : : int dotcnt;
1825 : :
1826 : : /*
1827 : : * Query must remain ABSOLUTELY devoid of unqualified names. This
1828 : : * would be unnecessary given a pg_table_is_visible() variant taking a
1829 : : * search_path argument.
1830 : : *
1831 : : * For with_child_tables, we start with the basic query's results and
1832 : : * recursively search the inheritance tree to add child tables.
1833 : : */
1834 [ + + ]: 35 : if (with_child_tables)
1835 : : {
1836 : 6 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1837 : : }
1838 : :
1839 : 35 : appendPQExpBuffer(query,
1840 : : "SELECT c.oid"
1841 : : "\nFROM pg_catalog.pg_class c"
1842 : : "\n LEFT JOIN pg_catalog.pg_namespace n"
1843 : : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1844 : : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1845 : : "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
1846 : : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1847 : : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1848 : : RELKIND_PARTITIONED_TABLE, RELKIND_PROPGRAPH);
1849 : 35 : initPQExpBuffer(&dbbuf);
1850 : 35 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1851 : : false, "n.nspname", "c.relname", NULL,
1852 : : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1853 : : &dotcnt);
1854 [ + + ]: 35 : if (dotcnt > 2)
1855 : 1 : pg_fatal("improper relation name (too many dotted names): %s",
1856 : : cell->val);
1857 [ + + ]: 34 : else if (dotcnt == 2)
1858 : 2 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1859 : 32 : termPQExpBuffer(&dbbuf);
1860 : :
1861 [ + + ]: 32 : if (with_child_tables)
1862 : : {
1863 : 6 : appendPQExpBufferStr(query, "UNION"
1864 : : "\nSELECT i.inhrelid"
1865 : : "\nFROM partition_tree p"
1866 : : "\n JOIN pg_catalog.pg_inherits i"
1867 : : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1868 : : "\n)"
1869 : : "\nSELECT relid FROM partition_tree");
1870 : : }
1871 : :
1872 : 32 : ExecuteSqlStatement(fout, "RESET search_path");
1873 : 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1874 : 32 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1875 : : ALWAYS_SECURE_SEARCH_PATH_SQL));
1876 [ + + + + ]: 32 : if (strict_names && PQntuples(res) == 0)
1877 : 2 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1878 : :
1879 [ + + ]: 74 : for (i = 0; i < PQntuples(res); i++)
1880 : : {
1881 : 44 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1882 : : }
1883 : :
1884 : 30 : PQclear(res);
1885 : 30 : resetPQExpBuffer(query);
1886 : : }
1887 : :
1888 : 24 : destroyPQExpBuffer(query);
1889 : : }
1890 : :
1891 : : /*
1892 : : * Verifies that the connected database name matches the given database name,
1893 : : * and if not, dies with an error about the given pattern.
1894 : : *
1895 : : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1896 : : */
1897 : : static void
1898 : 5 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1899 : : {
1900 : : const char *db;
1901 : :
1902 : 5 : db = PQdb(conn);
1903 [ - + ]: 5 : if (db == NULL)
1904 : 0 : pg_fatal("You are currently not connected to a database.");
1905 : :
1906 [ + - ]: 5 : if (strcmp(db, dbname) != 0)
1907 : 5 : pg_fatal("cross-database references are not implemented: %s",
1908 : : pattern);
1909 : 0 : }
1910 : :
1911 : : /*
1912 : : * checkExtensionMembership
1913 : : * Determine whether object is an extension member, and if so,
1914 : : * record an appropriate dependency and set the object's dump flag.
1915 : : *
1916 : : * It's important to call this for each object that could be an extension
1917 : : * member. Generally, we integrate this with determining the object's
1918 : : * to-be-dumped-ness, since extension membership overrides other rules for that.
1919 : : *
1920 : : * Returns true if object is an extension member, else false.
1921 : : */
1922 : : static bool
1923 : 623246 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1924 : : {
1925 : 623246 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1926 : :
1927 [ + + ]: 623246 : if (ext == NULL)
1928 : 622429 : return false;
1929 : :
1930 : 817 : dobj->ext_member = true;
1931 : :
1932 : : /* Record dependency so that getDependencies needn't deal with that */
1933 : 817 : addObjectDependency(dobj, ext->dobj.dumpId);
1934 : :
1935 : : /*
1936 : : * Mark the member object to have any non-initial ACLs dumped. (Any
1937 : : * initial ACLs will be removed later, using data from pg_init_privs, so
1938 : : * that we'll dump only the delta from the extension's initial setup.)
1939 : : *
1940 : : * In binary upgrades, we still dump all components of the members
1941 : : * individually, since the idea is to exactly reproduce the database
1942 : : * contents rather than replace the extension contents with something
1943 : : * different.
1944 : : *
1945 : : * Note: it might be interesting someday to implement storage and delta
1946 : : * dumping of extension members' RLS policies and/or security labels.
1947 : : * However there is a pitfall for RLS policies: trying to dump them
1948 : : * requires getting a lock on their tables, and the calling user might not
1949 : : * have privileges for that. We need no lock to examine a table's ACLs,
1950 : : * so the current feature doesn't have a problem of that sort.
1951 : : */
1952 [ + + ]: 817 : if (fout->dopt->binary_upgrade)
1953 : 186 : dobj->dump = ext->dobj.dump;
1954 : : else
1955 : 631 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1956 : :
1957 : 817 : return true;
1958 : : }
1959 : :
1960 : : /*
1961 : : * selectDumpableNamespace: policy-setting subroutine
1962 : : * Mark a namespace as to be dumped or not
1963 : : */
1964 : : static void
1965 : 1696 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1966 : : {
1967 : : /*
1968 : : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1969 : : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1970 : : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1971 : : */
1972 : 1696 : nsinfo->create = true;
1973 : :
1974 : : /*
1975 : : * If specific tables are being dumped, do not dump any complete
1976 : : * namespaces. If specific namespaces are being dumped, dump just those
1977 : : * namespaces. Otherwise, dump all non-system namespaces.
1978 : : */
1979 [ + + ]: 1696 : if (table_include_oids.head != NULL)
1980 : 61 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1981 [ + + ]: 1635 : else if (schema_include_oids.head != NULL)
1982 : 213 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1983 : 213 : simple_oid_list_member(&schema_include_oids,
1984 : : nsinfo->dobj.catId.oid) ?
1985 [ + + ]: 213 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1986 [ + + ]: 1422 : else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1987 : : {
1988 : : /*
1989 : : * We dump out any ACLs defined in pg_catalog, if they are interesting
1990 : : * (and not the original ACLs which were set at initdb time, see
1991 : : * pg_init_privs).
1992 : : */
1993 : 169 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1994 : : }
1995 [ + + ]: 1253 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1996 [ + + ]: 535 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1997 : : {
1998 : : /* Other system schemas don't get dumped */
1999 : 887 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2000 : : }
2001 [ + + ]: 366 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2002 : : {
2003 : : /*
2004 : : * The public schema is a strange beast that sits in a sort of
2005 : : * no-mans-land between being a system object and a user object.
2006 : : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2007 : : * a comment and an indication of ownership. If the owner is the
2008 : : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2009 : : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2010 : : */
2011 : 165 : nsinfo->create = false;
2012 : 165 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2013 [ + + ]: 165 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2014 : 121 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2015 : 165 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2016 : :
2017 : : /*
2018 : : * Also, make like it has a comment even if it doesn't; this is so
2019 : : * that we'll emit a command to drop the comment, if appropriate.
2020 : : * (Without this, we'd not call dumpCommentExtended for it.)
2021 : : */
2022 : 165 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2023 : : }
2024 : : else
2025 : 201 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2026 : :
2027 : : /*
2028 : : * In any case, a namespace can be excluded by an exclusion switch
2029 : : */
2030 [ + + + + ]: 2243 : if (nsinfo->dobj.dump_contains &&
2031 : 547 : simple_oid_list_member(&schema_exclude_oids,
2032 : : nsinfo->dobj.catId.oid))
2033 : 3 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2034 : :
2035 : : /*
2036 : : * If the schema belongs to an extension, allow extension membership to
2037 : : * override the dump decision for the schema itself. However, this does
2038 : : * not change dump_contains, so this won't change what we do with objects
2039 : : * within the schema. (If they belong to the extension, they'll get
2040 : : * suppressed by it, otherwise not.)
2041 : : */
2042 : 1696 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2043 : 1696 : }
2044 : :
2045 : : /*
2046 : : * selectDumpableTable: policy-setting subroutine
2047 : : * Mark a table as to be dumped or not
2048 : : */
2049 : : static void
2050 : 55102 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2051 : : {
2052 [ + + ]: 55102 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2053 : 225 : return; /* extension membership overrides all else */
2054 : :
2055 : : /*
2056 : : * If specific tables are being dumped, dump just those tables; else, dump
2057 : : * according to the parent namespace's dump flag.
2058 : : */
2059 [ + + ]: 54877 : if (table_include_oids.head != NULL)
2060 : 5668 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2061 : : tbinfo->dobj.catId.oid) ?
2062 [ + + ]: 2834 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2063 : : else
2064 : 52043 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2065 : :
2066 : : /*
2067 : : * In any case, a table can be excluded by an exclusion switch
2068 : : */
2069 [ + + + + ]: 89133 : if (tbinfo->dobj.dump &&
2070 : 34256 : simple_oid_list_member(&table_exclude_oids,
2071 : : tbinfo->dobj.catId.oid))
2072 : 12 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2073 : : }
2074 : :
2075 : : /*
2076 : : * selectDumpableType: policy-setting subroutine
2077 : : * Mark a type as to be dumped or not
2078 : : *
2079 : : * If it's a table's rowtype or an autogenerated array type, we also apply a
2080 : : * special type code to facilitate sorting into the desired order. (We don't
2081 : : * want to consider those to be ordinary types because that would bring tables
2082 : : * up into the datatype part of the dump order.) We still set the object's
2083 : : * dump flag; that's not going to cause the dummy type to be dumped, but we
2084 : : * need it so that casts involving such types will be dumped correctly -- see
2085 : : * dumpCast. This means the flag should be set the same as for the underlying
2086 : : * object (the table or base type).
2087 : : */
2088 : : static void
2089 : 147978 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2090 : : {
2091 : : /* skip complex types, except for standalone composite types */
2092 [ + + ]: 147978 : if (OidIsValid(tyinfo->typrelid) &&
2093 [ + + ]: 54200 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2094 : : {
2095 : 54015 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2096 : :
2097 : 54015 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2098 [ + - ]: 54015 : if (tytable != NULL)
2099 : 54015 : tyinfo->dobj.dump = tytable->dobj.dump;
2100 : : else
2101 : 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2102 : 54015 : return;
2103 : : }
2104 : :
2105 : : /* skip auto-generated array and multirange types */
2106 [ + + + + ]: 93963 : if (tyinfo->isArray || tyinfo->isMultirange)
2107 : : {
2108 : 72483 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2109 : :
2110 : : /*
2111 : : * Fall through to set the dump flag; we assume that the subsequent
2112 : : * rules will do the same thing as they would for the array's base
2113 : : * type or multirange's range type. (We cannot reliably look up the
2114 : : * base type here, since getTypes may not have processed it yet.)
2115 : : */
2116 : : }
2117 : :
2118 [ + + ]: 93963 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2119 : 150 : return; /* extension membership overrides all else */
2120 : :
2121 : : /* Dump based on if the contents of the namespace are being dumped */
2122 : 93813 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2123 : : }
2124 : :
2125 : : /*
2126 : : * selectDumpableDefaultACL: policy-setting subroutine
2127 : : * Mark a default ACL as to be dumped or not
2128 : : *
2129 : : * For per-schema default ACLs, dump if the schema is to be dumped.
2130 : : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2131 : : * and aclsSkip are checked separately.
2132 : : */
2133 : : static void
2134 : 206 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2135 : : {
2136 : : /* Default ACLs can't be extension members */
2137 : :
2138 [ + + ]: 206 : if (dinfo->dobj.namespace)
2139 : : /* default ACLs are considered part of the namespace */
2140 : 96 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2141 : : else
2142 : 110 : dinfo->dobj.dump = dopt->include_everything ?
2143 [ + + ]: 110 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2144 : 206 : }
2145 : :
2146 : : /*
2147 : : * selectDumpableCast: policy-setting subroutine
2148 : : * Mark a cast as to be dumped or not
2149 : : *
2150 : : * Casts do not belong to any particular namespace (since they haven't got
2151 : : * names), nor do they have identifiable owners. To distinguish user-defined
2152 : : * casts from built-in ones, we must resort to checking whether the cast's
2153 : : * OID is in the range reserved for initdb.
2154 : : */
2155 : : static void
2156 : 46503 : selectDumpableCast(CastInfo *cast, Archive *fout)
2157 : : {
2158 [ - + ]: 46503 : if (checkExtensionMembership(&cast->dobj, fout))
2159 : 0 : return; /* extension membership overrides all else */
2160 : :
2161 : : /*
2162 : : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2163 : : * support ACLs currently.
2164 : : */
2165 [ + + ]: 46503 : if (cast->dobj.catId.oid <= g_last_builtin_oid)
2166 : 46413 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2167 : : else
2168 : 90 : cast->dobj.dump = fout->dopt->include_everything ?
2169 [ + + ]: 90 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2170 : : }
2171 : :
2172 : : /*
2173 : : * selectDumpableProcLang: policy-setting subroutine
2174 : : * Mark a procedural language as to be dumped or not
2175 : : *
2176 : : * Procedural languages do not belong to any particular namespace. To
2177 : : * identify built-in languages, we must resort to checking whether the
2178 : : * language's OID is in the range reserved for initdb.
2179 : : */
2180 : : static void
2181 : 239 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2182 : : {
2183 [ + + ]: 239 : if (checkExtensionMembership(&plang->dobj, fout))
2184 : 191 : return; /* extension membership overrides all else */
2185 : :
2186 : : /*
2187 : : * Only include procedural languages when we are dumping everything.
2188 : : *
2189 : : * For from-initdb procedural languages, only include ACLs, as we do for
2190 : : * the pg_catalog namespace. We need this because procedural languages do
2191 : : * not live in any namespace.
2192 : : */
2193 [ + + ]: 48 : if (!fout->dopt->include_everything)
2194 : 9 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2195 : : else
2196 : : {
2197 [ - + ]: 39 : if (plang->dobj.catId.oid <= g_last_builtin_oid)
2198 : 0 : plang->dobj.dump = DUMP_COMPONENT_ACL;
2199 : : else
2200 : 39 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2201 : : }
2202 : : }
2203 : :
2204 : : /*
2205 : : * selectDumpableAccessMethod: policy-setting subroutine
2206 : : * Mark an access method as to be dumped or not
2207 : : *
2208 : : * Access methods do not belong to any particular namespace. To identify
2209 : : * built-in access methods, we must resort to checking whether the
2210 : : * method's OID is in the range reserved for initdb.
2211 : : */
2212 : : static void
2213 : 1465 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2214 : : {
2215 [ + + ]: 1465 : if (checkExtensionMembership(&method->dobj, fout))
2216 : 25 : return; /* extension membership overrides all else */
2217 : :
2218 : : /*
2219 : : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2220 : : * they do not support ACLs currently.
2221 : : */
2222 [ + + ]: 1440 : if (method->dobj.catId.oid <= g_last_builtin_oid)
2223 : 1337 : method->dobj.dump = DUMP_COMPONENT_NONE;
2224 : : else
2225 : 103 : method->dobj.dump = fout->dopt->include_everything ?
2226 [ + + ]: 103 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2227 : : }
2228 : :
2229 : : /*
2230 : : * selectDumpableExtension: policy-setting subroutine
2231 : : * Mark an extension as to be dumped or not
2232 : : *
2233 : : * Built-in extensions should be skipped except for checking ACLs, since we
2234 : : * assume those will already be installed in the target database. We identify
2235 : : * such extensions by their having OIDs in the range reserved for initdb.
2236 : : * We dump all user-added extensions by default. No extensions are dumped
2237 : : * if include_everything is false (i.e., a --schema or --table switch was
2238 : : * given), except if --extension specifies a list of extensions to dump.
2239 : : */
2240 : : static void
2241 : 223 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2242 : : {
2243 : : /*
2244 : : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2245 : : * change permissions on their member objects, if they wish to, and have
2246 : : * those changes preserved.
2247 : : */
2248 [ + + ]: 223 : if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2249 : 192 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2250 : : else
2251 : : {
2252 : : /* check if there is a list of extensions to dump */
2253 [ + + ]: 31 : if (extension_include_oids.head != NULL)
2254 : 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2255 : 4 : simple_oid_list_member(&extension_include_oids,
2256 : : extinfo->dobj.catId.oid) ?
2257 [ + + ]: 4 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2258 : : else
2259 : 27 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2260 : 27 : dopt->include_everything ?
2261 [ + + ]: 27 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2262 : :
2263 : : /* check that the extension is not explicitly excluded */
2264 [ + + + + ]: 58 : if (extinfo->dobj.dump &&
2265 : 27 : simple_oid_list_member(&extension_exclude_oids,
2266 : : extinfo->dobj.catId.oid))
2267 : 2 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2268 : : }
2269 : 223 : }
2270 : :
2271 : : /*
2272 : : * selectDumpablePublicationObject: policy-setting subroutine
2273 : : * Mark a publication object as to be dumped or not
2274 : : *
2275 : : * A publication can have schemas and tables which have schemas, but those are
2276 : : * ignored in decision making, because publications are only dumped when we are
2277 : : * dumping everything.
2278 : : */
2279 : : static void
2280 : 502 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2281 : : {
2282 [ - + ]: 502 : if (checkExtensionMembership(dobj, fout))
2283 : 0 : return; /* extension membership overrides all else */
2284 : :
2285 : 502 : dobj->dump = fout->dopt->include_everything ?
2286 [ + + ]: 502 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2287 : : }
2288 : :
2289 : : /*
2290 : : * selectDumpableStatisticsObject: policy-setting subroutine
2291 : : * Mark an extended statistics object as to be dumped or not
2292 : : *
2293 : : * We dump an extended statistics object if the schema it's in and the table
2294 : : * it's for are being dumped. (This'll need more thought if statistics
2295 : : * objects ever support cross-table stats.)
2296 : : */
2297 : : static void
2298 : 220 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2299 : : {
2300 [ - + ]: 220 : if (checkExtensionMembership(&sobj->dobj, fout))
2301 : 0 : return; /* extension membership overrides all else */
2302 : :
2303 : 220 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2304 [ + - ]: 220 : if (sobj->stattable == NULL ||
2305 [ + + ]: 220 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2306 : 35 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2307 : : }
2308 : :
2309 : : /*
2310 : : * selectDumpableObject: policy-setting subroutine
2311 : : * Mark a generic dumpable object as to be dumped or not
2312 : : *
2313 : : * Use this only for object types without a special-case routine above.
2314 : : */
2315 : : static void
2316 : 423556 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2317 : : {
2318 [ + + ]: 423556 : if (checkExtensionMembership(dobj, fout))
2319 : 201 : return; /* extension membership overrides all else */
2320 : :
2321 : : /*
2322 : : * Default policy is to dump if parent namespace is dumpable, or for
2323 : : * non-namespace-associated items, dump if we're dumping "everything".
2324 : : */
2325 [ + + ]: 423355 : if (dobj->namespace)
2326 : 422440 : dobj->dump = dobj->namespace->dobj.dump_contains;
2327 : : else
2328 : 915 : dobj->dump = fout->dopt->include_everything ?
2329 [ + + ]: 915 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2330 : : }
2331 : :
2332 : : /*
2333 : : * Dump a table's contents for loading using the COPY command
2334 : : * - this routine is called by the Archiver when it wants the table
2335 : : * to be dumped.
2336 : : */
2337 : : static int
2338 : 4432 : dumpTableData_copy(Archive *fout, const void *dcontext)
2339 : : {
2340 : 4432 : const TableDataInfo *tdinfo = dcontext;
2341 : 4432 : const TableInfo *tbinfo = tdinfo->tdtable;
2342 : 4432 : const char *classname = tbinfo->dobj.name;
2343 : 4432 : PQExpBuffer q = createPQExpBuffer();
2344 : :
2345 : : /*
2346 : : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2347 : : * which uses it already.
2348 : : */
2349 : 4432 : PQExpBuffer clistBuf = createPQExpBuffer();
2350 : 4432 : PGconn *conn = GetConnection(fout);
2351 : : PGresult *res;
2352 : : int ret;
2353 : : char *copybuf;
2354 : : const char *column_list;
2355 : :
2356 : 4432 : pg_log_info("dumping contents of table \"%s.%s\"",
2357 : : tbinfo->dobj.namespace->dobj.name, classname);
2358 : :
2359 : : /*
2360 : : * Specify the column list explicitly so that we have no possibility of
2361 : : * retrieving data in the wrong column order. (The default column
2362 : : * ordering of COPY will not be what we want in certain corner cases
2363 : : * involving ADD COLUMN and inheritance.)
2364 : : */
2365 : 4432 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2366 : :
2367 : : /*
2368 : : * Use COPY (SELECT ...) TO when dumping a foreign table's data, when a
2369 : : * filter condition was specified, and when in binary upgrade mode and
2370 : : * dumping an old pg_largeobject_metadata defined WITH OIDS. For other
2371 : : * cases a simple COPY suffices.
2372 : : */
2373 [ + + + + ]: 4432 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2374 [ - + - - ]: 4391 : (fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
2375 [ # # ]: 0 : tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
2376 : : {
2377 : : /* Temporary allows to access to foreign tables to dump data */
2378 [ + + ]: 41 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2379 : 1 : set_restrict_relation_kind(fout, "view");
2380 : :
2381 : 41 : appendPQExpBufferStr(q, "COPY (SELECT ");
2382 : : /* klugery to get rid of parens in column list */
2383 [ + - ]: 41 : if (strlen(column_list) > 2)
2384 : : {
2385 : 41 : appendPQExpBufferStr(q, column_list + 1);
2386 : 41 : q->data[q->len - 1] = ' ';
2387 : : }
2388 : : else
2389 : 0 : appendPQExpBufferStr(q, "* ");
2390 : :
2391 : 82 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2392 : 41 : fmtQualifiedDumpable(tbinfo),
2393 [ + + ]: 41 : tdinfo->filtercond ? tdinfo->filtercond : "");
2394 : : }
2395 : : else
2396 : : {
2397 : 4391 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2398 : 4391 : fmtQualifiedDumpable(tbinfo),
2399 : : column_list);
2400 : : }
2401 : 4432 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2402 : 4431 : PQclear(res);
2403 : 4431 : destroyPQExpBuffer(clistBuf);
2404 : :
2405 : : for (;;)
2406 : : {
2407 : 1824115 : ret = PQgetCopyData(conn, ©buf, 0);
2408 : :
2409 [ + + ]: 1824115 : if (ret < 0)
2410 : 4431 : break; /* done or error */
2411 : :
2412 [ + - ]: 1819684 : if (copybuf)
2413 : : {
2414 : 1819684 : WriteData(fout, copybuf, ret);
2415 : 1819684 : PQfreemem(copybuf);
2416 : : }
2417 : :
2418 : : /* ----------
2419 : : * THROTTLE:
2420 : : *
2421 : : * There was considerable discussion in late July, 2000 regarding
2422 : : * slowing down pg_dump when backing up large tables. Users with both
2423 : : * slow & fast (multi-processor) machines experienced performance
2424 : : * degradation when doing a backup.
2425 : : *
2426 : : * Initial attempts based on sleeping for a number of ms for each ms
2427 : : * of work were deemed too complex, then a simple 'sleep in each loop'
2428 : : * implementation was suggested. The latter failed because the loop
2429 : : * was too tight. Finally, the following was implemented:
2430 : : *
2431 : : * If throttle is non-zero, then
2432 : : * See how long since the last sleep.
2433 : : * Work out how long to sleep (based on ratio).
2434 : : * If sleep is more than 100ms, then
2435 : : * sleep
2436 : : * reset timer
2437 : : * EndIf
2438 : : * EndIf
2439 : : *
2440 : : * where the throttle value was the number of ms to sleep per ms of
2441 : : * work. The calculation was done in each loop.
2442 : : *
2443 : : * Most of the hard work is done in the backend, and this solution
2444 : : * still did not work particularly well: on slow machines, the ratio
2445 : : * was 50:1, and on medium paced machines, 1:1, and on fast
2446 : : * multi-processor machines, it had little or no effect, for reasons
2447 : : * that were unclear.
2448 : : *
2449 : : * Further discussion ensued, and the proposal was dropped.
2450 : : *
2451 : : * For those people who want this feature, it can be implemented using
2452 : : * gettimeofday in each loop, calculating the time since last sleep,
2453 : : * multiplying that by the sleep ratio, then if the result is more
2454 : : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2455 : : * function to sleep for a subsecond period ie.
2456 : : *
2457 : : * select(0, NULL, NULL, NULL, &tvi);
2458 : : *
2459 : : * This will return after the interval specified in the structure tvi.
2460 : : * Finally, call gettimeofday again to save the 'last sleep time'.
2461 : : * ----------
2462 : : */
2463 : : }
2464 : 4431 : archprintf(fout, "\\.\n\n\n");
2465 : :
2466 [ - + ]: 4431 : if (ret == -2)
2467 : : {
2468 : : /* copy data transfer failed */
2469 : 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2470 : 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2471 : 0 : pg_log_error_detail("Command was: %s", q->data);
2472 : 0 : exit_nicely(1);
2473 : : }
2474 : :
2475 : : /* Check command status and return to normal libpq state */
2476 : 4431 : res = PQgetResult(conn);
2477 [ - + ]: 4431 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2478 : : {
2479 : 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2480 : 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2481 : 0 : pg_log_error_detail("Command was: %s", q->data);
2482 : 0 : exit_nicely(1);
2483 : : }
2484 : 4431 : PQclear(res);
2485 : :
2486 : : /* Do this to ensure we've pumped libpq back to idle state */
2487 [ - + ]: 4431 : if (PQgetResult(conn) != NULL)
2488 : 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2489 : : classname);
2490 : :
2491 : 4431 : destroyPQExpBuffer(q);
2492 : :
2493 : : /* Revert back the setting */
2494 [ - + ]: 4431 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2495 : 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2496 : :
2497 : 4431 : return 1;
2498 : : }
2499 : :
2500 : : /*
2501 : : * Dump table data using INSERT commands.
2502 : : *
2503 : : * Caution: when we restore from an archive file direct to database, the
2504 : : * INSERT commands emitted by this function have to be parsed by
2505 : : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2506 : : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2507 : : */
2508 : : static int
2509 : 87 : dumpTableData_insert(Archive *fout, const void *dcontext)
2510 : : {
2511 : 87 : const TableDataInfo *tdinfo = dcontext;
2512 : 87 : const TableInfo *tbinfo = tdinfo->tdtable;
2513 : 87 : DumpOptions *dopt = fout->dopt;
2514 : 87 : PQExpBuffer q = createPQExpBuffer();
2515 : 87 : PQExpBuffer insertStmt = NULL;
2516 : : char *attgenerated;
2517 : : PGresult *res;
2518 : : int nfields,
2519 : : i;
2520 : 87 : int rows_per_statement = dopt->dump_inserts;
2521 : 87 : int rows_this_statement = 0;
2522 : :
2523 : : /* Temporary allows to access to foreign tables to dump data */
2524 [ - + ]: 87 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2525 : 0 : set_restrict_relation_kind(fout, "view");
2526 : :
2527 : : /*
2528 : : * If we're going to emit INSERTs with column names, the most efficient
2529 : : * way to deal with generated columns is to exclude them entirely. For
2530 : : * INSERTs without column names, we have to emit DEFAULT rather than the
2531 : : * actual column value --- but we can save a few cycles by fetching nulls
2532 : : * rather than the uninteresting-to-us value.
2533 : : */
2534 : 87 : attgenerated = pg_malloc_array(char, tbinfo->numatts);
2535 : 87 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2536 : 87 : nfields = 0;
2537 [ + + ]: 269 : for (i = 0; i < tbinfo->numatts; i++)
2538 : : {
2539 [ + + ]: 182 : if (tbinfo->attisdropped[i])
2540 : 2 : continue;
2541 [ + + + + ]: 180 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2542 : 8 : continue;
2543 [ + + ]: 172 : if (nfields > 0)
2544 : 92 : appendPQExpBufferStr(q, ", ");
2545 [ + + ]: 172 : if (tbinfo->attgenerated[i])
2546 : 8 : appendPQExpBufferStr(q, "NULL");
2547 : : else
2548 : 164 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2549 : 172 : attgenerated[nfields] = tbinfo->attgenerated[i];
2550 : 172 : nfields++;
2551 : : }
2552 : : /* Servers before 9.4 will complain about zero-column SELECT */
2553 [ + + ]: 87 : if (nfields == 0)
2554 : 7 : appendPQExpBufferStr(q, "NULL");
2555 : 87 : appendPQExpBuffer(q, " FROM ONLY %s",
2556 : 87 : fmtQualifiedDumpable(tbinfo));
2557 [ - + ]: 87 : if (tdinfo->filtercond)
2558 : 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2559 : :
2560 : 87 : ExecuteSqlStatement(fout, q->data);
2561 : :
2562 : : while (1)
2563 : : {
2564 : 139 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2565 : : PGRES_TUPLES_OK);
2566 : :
2567 : : /* cross-check field count, allowing for dummy NULL if any */
2568 [ + + + - ]: 139 : if (nfields != PQnfields(res) &&
2569 [ - + ]: 10 : !(nfields == 0 && PQnfields(res) == 1))
2570 : 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2571 : : tbinfo->dobj.name);
2572 : :
2573 : : /*
2574 : : * First time through, we build as much of the INSERT statement as
2575 : : * possible in "insertStmt", which we can then just print for each
2576 : : * statement. If the table happens to have zero dumpable columns then
2577 : : * this will be a complete statement, otherwise it will end in
2578 : : * "VALUES" and be ready to have the row's column values printed.
2579 : : */
2580 [ + + ]: 139 : if (insertStmt == NULL)
2581 : : {
2582 : : const TableInfo *targettab;
2583 : :
2584 : 87 : insertStmt = createPQExpBuffer();
2585 : :
2586 : : /*
2587 : : * When load-via-partition-root is set or forced, get the root
2588 : : * table name for the partition table, so that we can reload data
2589 : : * through the root table.
2590 : : */
2591 [ + + ]: 87 : if (tbinfo->ispartition &&
2592 [ + - + + ]: 48 : (dopt->load_via_partition_root ||
2593 : 24 : forcePartitionRootLoad(tbinfo)))
2594 : 7 : targettab = getRootTableInfo(tbinfo);
2595 : : else
2596 : 80 : targettab = tbinfo;
2597 : :
2598 : 87 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2599 : 87 : fmtQualifiedDumpable(targettab));
2600 : :
2601 : : /* corner case for zero-column table */
2602 [ + + ]: 87 : if (nfields == 0)
2603 : : {
2604 : 7 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2605 : : }
2606 : : else
2607 : : {
2608 : : /* append the list of column names if required */
2609 [ + + ]: 80 : if (dopt->column_inserts)
2610 : : {
2611 : 36 : appendPQExpBufferChar(insertStmt, '(');
2612 [ + + ]: 109 : for (int field = 0; field < nfields; field++)
2613 : : {
2614 [ + + ]: 73 : if (field > 0)
2615 : 37 : appendPQExpBufferStr(insertStmt, ", ");
2616 : 73 : appendPQExpBufferStr(insertStmt,
2617 : 73 : fmtId(PQfname(res, field)));
2618 : : }
2619 : 36 : appendPQExpBufferStr(insertStmt, ") ");
2620 : : }
2621 : :
2622 [ + + ]: 80 : if (tbinfo->needs_override)
2623 : 2 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2624 : :
2625 : 80 : appendPQExpBufferStr(insertStmt, "VALUES");
2626 : : }
2627 : : }
2628 : :
2629 [ + + ]: 3608 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2630 : : {
2631 : : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2632 [ + + ]: 3469 : if (rows_this_statement == 0)
2633 : 3463 : archputs(insertStmt->data, fout);
2634 : :
2635 : : /*
2636 : : * If it is zero-column table then we've already written the
2637 : : * complete statement, which will mean we've disobeyed
2638 : : * --rows-per-insert when it's set greater than 1. We do support
2639 : : * a way to make this multi-row with: SELECT UNION ALL SELECT
2640 : : * UNION ALL ... but that's non-standard so we should avoid it
2641 : : * given that using INSERTs is mostly only ever needed for
2642 : : * cross-database exports.
2643 : : */
2644 [ + + ]: 3469 : if (nfields == 0)
2645 : 6 : continue;
2646 : :
2647 : : /* Emit a row heading */
2648 [ + + ]: 3463 : if (rows_per_statement == 1)
2649 : 3454 : archputs(" (", fout);
2650 [ + + ]: 9 : else if (rows_this_statement > 0)
2651 : 6 : archputs(",\n\t(", fout);
2652 : : else
2653 : 3 : archputs("\n\t(", fout);
2654 : :
2655 [ + + ]: 10445 : for (int field = 0; field < nfields; field++)
2656 : : {
2657 [ + + ]: 6982 : if (field > 0)
2658 : 3519 : archputs(", ", fout);
2659 [ + + ]: 6982 : if (attgenerated[field])
2660 : : {
2661 : 2 : archputs("DEFAULT", fout);
2662 : 2 : continue;
2663 : : }
2664 [ + + ]: 6980 : if (PQgetisnull(res, tuple, field))
2665 : : {
2666 : 83 : archputs("NULL", fout);
2667 : 83 : continue;
2668 : : }
2669 : :
2670 : : /* XXX This code is partially duplicated in ruleutils.c */
2671 [ + + + + ]: 6897 : switch (PQftype(res, field))
2672 : : {
2673 : 4869 : case INT2OID:
2674 : : case INT4OID:
2675 : : case INT8OID:
2676 : : case OIDOID:
2677 : : case FLOAT4OID:
2678 : : case FLOAT8OID:
2679 : : case NUMERICOID:
2680 : : {
2681 : : /*
2682 : : * These types are printed without quotes unless
2683 : : * they contain values that aren't accepted by the
2684 : : * scanner unquoted (e.g., 'NaN'). Note that
2685 : : * strtod() and friends might accept NaN, so we
2686 : : * can't use that to test.
2687 : : *
2688 : : * In reality we only need to defend against
2689 : : * infinity and NaN, so we need not get too crazy
2690 : : * about pattern matching here.
2691 : : */
2692 : 4869 : const char *s = PQgetvalue(res, tuple, field);
2693 : :
2694 [ + + ]: 4869 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2695 : 4867 : archputs(s, fout);
2696 : : else
2697 : 2 : archprintf(fout, "'%s'", s);
2698 : : }
2699 : 4869 : break;
2700 : :
2701 : 2 : case BITOID:
2702 : : case VARBITOID:
2703 : 2 : archprintf(fout, "B'%s'",
2704 : : PQgetvalue(res, tuple, field));
2705 : 2 : break;
2706 : :
2707 : 4 : case BOOLOID:
2708 [ + + ]: 4 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2709 : 2 : archputs("true", fout);
2710 : : else
2711 : 2 : archputs("false", fout);
2712 : 4 : break;
2713 : :
2714 : 2022 : default:
2715 : : /* All other types are printed as string literals. */
2716 : 2022 : resetPQExpBuffer(q);
2717 : 2022 : appendStringLiteralAH(q,
2718 : : PQgetvalue(res, tuple, field),
2719 : : fout);
2720 : 2022 : archputs(q->data, fout);
2721 : 2022 : break;
2722 : : }
2723 : : }
2724 : :
2725 : : /* Terminate the row ... */
2726 : 3463 : archputs(")", fout);
2727 : :
2728 : : /* ... and the statement, if the target no. of rows is reached */
2729 [ + + ]: 3463 : if (++rows_this_statement >= rows_per_statement)
2730 : : {
2731 [ - + ]: 3456 : if (dopt->do_nothing)
2732 : 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2733 : : else
2734 : 3456 : archputs(";\n", fout);
2735 : : /* Reset the row counter */
2736 : 3456 : rows_this_statement = 0;
2737 : : }
2738 : : }
2739 : :
2740 [ + + ]: 139 : if (PQntuples(res) <= 0)
2741 : : {
2742 : 87 : PQclear(res);
2743 : 87 : break;
2744 : : }
2745 : 52 : PQclear(res);
2746 : : }
2747 : :
2748 : : /* Terminate any statements that didn't make the row count. */
2749 [ + + ]: 87 : if (rows_this_statement > 0)
2750 : : {
2751 [ - + ]: 1 : if (dopt->do_nothing)
2752 : 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2753 : : else
2754 : 1 : archputs(";\n", fout);
2755 : : }
2756 : :
2757 : 87 : archputs("\n\n", fout);
2758 : :
2759 : 87 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2760 : :
2761 : 87 : destroyPQExpBuffer(q);
2762 [ + - ]: 87 : if (insertStmt != NULL)
2763 : 87 : destroyPQExpBuffer(insertStmt);
2764 : 87 : pg_free(attgenerated);
2765 : :
2766 : : /* Revert back the setting */
2767 [ - + ]: 87 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2768 : 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2769 : :
2770 : 87 : return 1;
2771 : : }
2772 : :
2773 : : /*
2774 : : * getRootTableInfo:
2775 : : * get the root TableInfo for the given partition table.
2776 : : */
2777 : : static TableInfo *
2778 : 83 : getRootTableInfo(const TableInfo *tbinfo)
2779 : : {
2780 : : TableInfo *parentTbinfo;
2781 : :
2782 : : Assert(tbinfo->ispartition);
2783 : : Assert(tbinfo->numParents == 1);
2784 : :
2785 : 83 : parentTbinfo = tbinfo->parents[0];
2786 [ - + ]: 83 : while (parentTbinfo->ispartition)
2787 : : {
2788 : : Assert(parentTbinfo->numParents == 1);
2789 : 0 : parentTbinfo = parentTbinfo->parents[0];
2790 : : }
2791 : :
2792 : 83 : return parentTbinfo;
2793 : : }
2794 : :
2795 : : /*
2796 : : * forcePartitionRootLoad
2797 : : * Check if we must force load_via_partition_root for this partition.
2798 : : *
2799 : : * This is required if any level of ancestral partitioned table has an
2800 : : * unsafe partitioning scheme.
2801 : : */
2802 : : static bool
2803 : 1102 : forcePartitionRootLoad(const TableInfo *tbinfo)
2804 : : {
2805 : : TableInfo *parentTbinfo;
2806 : :
2807 : : Assert(tbinfo->ispartition);
2808 : : Assert(tbinfo->numParents == 1);
2809 : :
2810 : 1102 : parentTbinfo = tbinfo->parents[0];
2811 [ + + ]: 1102 : if (parentTbinfo->unsafe_partitions)
2812 : 83 : return true;
2813 [ + + ]: 1239 : while (parentTbinfo->ispartition)
2814 : : {
2815 : : Assert(parentTbinfo->numParents == 1);
2816 : 220 : parentTbinfo = parentTbinfo->parents[0];
2817 [ - + ]: 220 : if (parentTbinfo->unsafe_partitions)
2818 : 0 : return true;
2819 : : }
2820 : :
2821 : 1019 : return false;
2822 : : }
2823 : :
2824 : : /*
2825 : : * dumpTableData -
2826 : : * dump the contents of a single table
2827 : : *
2828 : : * Actually, this just makes an ArchiveEntry for the table contents.
2829 : : */
2830 : : static void
2831 : 4605 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2832 : : {
2833 : 4605 : DumpOptions *dopt = fout->dopt;
2834 : 4605 : const TableInfo *tbinfo = tdinfo->tdtable;
2835 : 4605 : PQExpBuffer copyBuf = createPQExpBuffer();
2836 : 4605 : PQExpBuffer clistBuf = createPQExpBuffer();
2837 : : DataDumperPtr dumpFn;
2838 : 4605 : char *tdDefn = NULL;
2839 : : char *copyStmt;
2840 : : const char *copyFrom;
2841 : :
2842 : : /* We had better have loaded per-column details about this table */
2843 : : Assert(tbinfo->interesting);
2844 : :
2845 : : /*
2846 : : * When load-via-partition-root is set or forced, get the root table name
2847 : : * for the partition table, so that we can reload data through the root
2848 : : * table. Then construct a comment to be inserted into the TOC entry's
2849 : : * defn field, so that such cases can be identified reliably.
2850 : : */
2851 [ + + ]: 4605 : if (tbinfo->ispartition &&
2852 [ + - + + ]: 2156 : (dopt->load_via_partition_root ||
2853 : 1078 : forcePartitionRootLoad(tbinfo)))
2854 : 76 : {
2855 : : const TableInfo *parentTbinfo;
2856 : : char *sanitized;
2857 : :
2858 : 76 : parentTbinfo = getRootTableInfo(tbinfo);
2859 : 76 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2860 : 76 : sanitized = sanitize_line(copyFrom, true);
2861 : 76 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2862 : : sanitized);
2863 : 76 : free(sanitized);
2864 : 76 : tdDefn = pg_strdup(copyBuf->data);
2865 : : }
2866 : : else
2867 : 4529 : copyFrom = fmtQualifiedDumpable(tbinfo);
2868 : :
2869 [ + + ]: 4605 : if (dopt->dump_inserts == 0)
2870 : : {
2871 : : /* Dump/restore using COPY */
2872 : 4518 : dumpFn = dumpTableData_copy;
2873 : : /* must use 2 steps here 'cause fmtId is nonreentrant */
2874 : 4518 : printfPQExpBuffer(copyBuf, "COPY %s ",
2875 : : copyFrom);
2876 : 4518 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2877 : : fmtCopyColumnList(tbinfo, clistBuf));
2878 : 4518 : copyStmt = copyBuf->data;
2879 : : }
2880 : : else
2881 : : {
2882 : : /* Restore using INSERT */
2883 : 87 : dumpFn = dumpTableData_insert;
2884 : 87 : copyStmt = NULL;
2885 : : }
2886 : :
2887 : : /*
2888 : : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2889 : : * dependency on its table as "special" and pass it to ArchiveEntry now.
2890 : : * See comments for BuildArchiveDependencies.
2891 : : */
2892 [ + - ]: 4605 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2893 : : {
2894 : : TocEntry *te;
2895 : :
2896 : 4605 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2897 : 4605 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2898 : : .namespace = tbinfo->dobj.namespace->dobj.name,
2899 : : .owner = tbinfo->rolname,
2900 : : .description = "TABLE DATA",
2901 : : .section = SECTION_DATA,
2902 : : .createStmt = tdDefn,
2903 : : .copyStmt = copyStmt,
2904 : : .deps = &(tbinfo->dobj.dumpId),
2905 : : .nDeps = 1,
2906 : : .dumpFn = dumpFn,
2907 : : .dumpArg = tdinfo));
2908 : :
2909 : : /*
2910 : : * Set the TocEntry's dataLength in case we are doing a parallel dump
2911 : : * and want to order dump jobs by table size. We choose to measure
2912 : : * dataLength in table pages (including TOAST pages) during dump, so
2913 : : * no scaling is needed.
2914 : : *
2915 : : * However, relpages is declared as "integer" in pg_class, and hence
2916 : : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2917 : : * Cast so that we get the right interpretation of table sizes
2918 : : * exceeding INT_MAX pages.
2919 : : */
2920 : 4605 : te->dataLength = (BlockNumber) tbinfo->relpages;
2921 : 4605 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2922 : :
2923 : : /*
2924 : : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2925 : : * and instead we'd better worry about integer overflow. Clamp to
2926 : : * INT_MAX if the correct result exceeds that.
2927 : : */
2928 : : if (sizeof(te->dataLength) == 4 &&
2929 : : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2930 : : te->dataLength < 0))
2931 : : te->dataLength = INT_MAX;
2932 : : }
2933 : :
2934 : 4605 : destroyPQExpBuffer(copyBuf);
2935 : 4605 : destroyPQExpBuffer(clistBuf);
2936 : 4605 : }
2937 : :
2938 : : /*
2939 : : * refreshMatViewData -
2940 : : * load or refresh the contents of a single materialized view
2941 : : *
2942 : : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2943 : : * statement.
2944 : : */
2945 : : static void
2946 : 363 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2947 : : {
2948 : 363 : TableInfo *tbinfo = tdinfo->tdtable;
2949 : : PQExpBuffer q;
2950 : :
2951 : : /* If the materialized view is not flagged as populated, skip this. */
2952 [ + + ]: 363 : if (!tbinfo->relispopulated)
2953 : 72 : return;
2954 : :
2955 : 291 : q = createPQExpBuffer();
2956 : :
2957 : 291 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2958 : 291 : fmtQualifiedDumpable(tbinfo));
2959 : :
2960 [ + - ]: 291 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2961 : 291 : ArchiveEntry(fout,
2962 : : tdinfo->dobj.catId, /* catalog ID */
2963 : 291 : tdinfo->dobj.dumpId, /* dump ID */
2964 : 291 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2965 : : .namespace = tbinfo->dobj.namespace->dobj.name,
2966 : : .owner = tbinfo->rolname,
2967 : : .description = "MATERIALIZED VIEW DATA",
2968 : : .section = SECTION_POST_DATA,
2969 : : .createStmt = q->data,
2970 : : .deps = tdinfo->dobj.dependencies,
2971 : : .nDeps = tdinfo->dobj.nDeps));
2972 : :
2973 : 291 : destroyPQExpBuffer(q);
2974 : : }
2975 : :
2976 : : /*
2977 : : * getTableData -
2978 : : * set up dumpable objects representing the contents of tables
2979 : : */
2980 : : static void
2981 : 182 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2982 : : {
2983 : : int i;
2984 : :
2985 [ + + ]: 52798 : for (i = 0; i < numTables; i++)
2986 : : {
2987 [ + + + + ]: 52616 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2988 [ + + ]: 1013 : (!relkind || tblinfo[i].relkind == relkind))
2989 : 6485 : makeTableDataInfo(dopt, &(tblinfo[i]));
2990 : : }
2991 : 182 : }
2992 : :
2993 : : /*
2994 : : * Make a dumpable object for the data of this specific table
2995 : : *
2996 : : * Note: we make a TableDataInfo if and only if we are going to dump the
2997 : : * table data; the "dump" field in such objects isn't very interesting.
2998 : : */
2999 : : static void
3000 : 6564 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3001 : : {
3002 : : TableDataInfo *tdinfo;
3003 : :
3004 : : /*
3005 : : * Nothing to do if we already decided to dump the table. This will
3006 : : * happen for "config" tables.
3007 : : */
3008 [ + + ]: 6564 : if (tbinfo->dataObj != NULL)
3009 : 1 : return;
3010 : :
3011 : : /* Skip property graphs (no data to dump) */
3012 [ + + ]: 6563 : if (tbinfo->relkind == RELKIND_PROPGRAPH)
3013 : 92 : return;
3014 : : /* Skip VIEWs (no data to dump) */
3015 [ + + ]: 6471 : if (tbinfo->relkind == RELKIND_VIEW)
3016 : 520 : return;
3017 : : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3018 [ + + ]: 5951 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3019 [ + + ]: 40 : (foreign_servers_include_oids.head == NULL ||
3020 [ + + ]: 4 : !simple_oid_list_member(&foreign_servers_include_oids,
3021 : : tbinfo->foreign_server)))
3022 : 39 : return;
3023 : : /* Skip partitioned tables (data in partitions) */
3024 [ + + ]: 5912 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3025 : 519 : return;
3026 : :
3027 : : /* Don't dump data in unlogged tables, if so requested */
3028 [ + + ]: 5393 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3029 [ + + ]: 41 : dopt->no_unlogged_table_data)
3030 : 18 : return;
3031 : :
3032 : : /* Check that the data is not explicitly excluded */
3033 [ + + ]: 5375 : if (simple_oid_list_member(&tabledata_exclude_oids,
3034 : : tbinfo->dobj.catId.oid))
3035 : 8 : return;
3036 : :
3037 : : /* OK, let's dump it */
3038 : 5367 : tdinfo = pg_malloc_object(TableDataInfo);
3039 : :
3040 [ + + ]: 5367 : if (tbinfo->relkind == RELKIND_MATVIEW)
3041 : 363 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3042 [ + + ]: 5004 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3043 : 399 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3044 : : else
3045 : 4605 : tdinfo->dobj.objType = DO_TABLE_DATA;
3046 : :
3047 : : /*
3048 : : * Note: use tableoid 0 so that this object won't be mistaken for
3049 : : * something that pg_depend entries apply to.
3050 : : */
3051 : 5367 : tdinfo->dobj.catId.tableoid = 0;
3052 : 5367 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3053 : 5367 : AssignDumpId(&tdinfo->dobj);
3054 : 5367 : tdinfo->dobj.name = tbinfo->dobj.name;
3055 : 5367 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3056 : 5367 : tdinfo->tdtable = tbinfo;
3057 : 5367 : tdinfo->filtercond = NULL; /* might get set later */
3058 : 5367 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3059 : :
3060 : : /* A TableDataInfo contains data, of course */
3061 : 5367 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3062 : :
3063 : 5367 : tbinfo->dataObj = tdinfo;
3064 : :
3065 : : /*
3066 : : * Materialized view statistics must be restored after the data, because
3067 : : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3068 : : *
3069 : : * The dependency is added here because the statistics objects are created
3070 : : * first.
3071 : : */
3072 [ + + + + ]: 5367 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3073 : : {
3074 : 286 : tbinfo->stats->section = SECTION_POST_DATA;
3075 : 286 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3076 : : }
3077 : :
3078 : : /* Make sure that we'll collect per-column info for this table. */
3079 : 5367 : tbinfo->interesting = true;
3080 : : }
3081 : :
3082 : : /*
3083 : : * The refresh for a materialized view must be dependent on the refresh for
3084 : : * any materialized view that this one is dependent on.
3085 : : *
3086 : : * This must be called after all the objects are created, but before they are
3087 : : * sorted.
3088 : : */
3089 : : static void
3090 : 146 : buildMatViewRefreshDependencies(Archive *fout)
3091 : : {
3092 : : PQExpBuffer query;
3093 : : PGresult *res;
3094 : : int ntups,
3095 : : i;
3096 : : int i_classid,
3097 : : i_objid,
3098 : : i_refobjid;
3099 : :
3100 : 146 : query = createPQExpBuffer();
3101 : :
3102 : 146 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3103 : : "( "
3104 : : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3105 : : "FROM pg_depend d1 "
3106 : : "JOIN pg_class c1 ON c1.oid = d1.objid "
3107 : : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3108 : : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3109 : : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3110 : : "AND d2.objid = r1.oid "
3111 : : "AND d2.refobjid <> d1.objid "
3112 : : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3113 : : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3114 : : CppAsString2(RELKIND_VIEW) ") "
3115 : : "WHERE d1.classid = 'pg_class'::regclass "
3116 : : "UNION "
3117 : : "SELECT w.objid, d3.refobjid, c3.relkind "
3118 : : "FROM w "
3119 : : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3120 : : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3121 : : "AND d3.objid = r3.oid "
3122 : : "AND d3.refobjid <> w.refobjid "
3123 : : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3124 : : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3125 : : CppAsString2(RELKIND_VIEW) ") "
3126 : : ") "
3127 : : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3128 : : "FROM w "
3129 : : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3130 : :
3131 : 146 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3132 : :
3133 : 146 : ntups = PQntuples(res);
3134 : :
3135 : 146 : i_classid = PQfnumber(res, "classid");
3136 : 146 : i_objid = PQfnumber(res, "objid");
3137 : 146 : i_refobjid = PQfnumber(res, "refobjid");
3138 : :
3139 [ + + ]: 422 : for (i = 0; i < ntups; i++)
3140 : : {
3141 : : CatalogId objId;
3142 : : CatalogId refobjId;
3143 : : DumpableObject *dobj;
3144 : : DumpableObject *refdobj;
3145 : : TableInfo *tbinfo;
3146 : : TableInfo *reftbinfo;
3147 : :
3148 : 276 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3149 : 276 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3150 : 276 : refobjId.tableoid = objId.tableoid;
3151 : 276 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3152 : :
3153 : 276 : dobj = findObjectByCatalogId(objId);
3154 [ - + ]: 276 : if (dobj == NULL)
3155 : 48 : continue;
3156 : :
3157 : : Assert(dobj->objType == DO_TABLE);
3158 : 276 : tbinfo = (TableInfo *) dobj;
3159 : : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3160 : 276 : dobj = (DumpableObject *) tbinfo->dataObj;
3161 [ + + ]: 276 : if (dobj == NULL)
3162 : 48 : continue;
3163 : : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3164 : :
3165 : 228 : refdobj = findObjectByCatalogId(refobjId);
3166 [ - + ]: 228 : if (refdobj == NULL)
3167 : 0 : continue;
3168 : :
3169 : : Assert(refdobj->objType == DO_TABLE);
3170 : 228 : reftbinfo = (TableInfo *) refdobj;
3171 : : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3172 : 228 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3173 [ - + ]: 228 : if (refdobj == NULL)
3174 : 0 : continue;
3175 : : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3176 : :
3177 : 228 : addObjectDependency(dobj, refdobj->dumpId);
3178 : :
3179 [ + + ]: 228 : if (!reftbinfo->relispopulated)
3180 : 36 : tbinfo->relispopulated = false;
3181 : : }
3182 : :
3183 : 146 : PQclear(res);
3184 : :
3185 : 146 : destroyPQExpBuffer(query);
3186 : 146 : }
3187 : :
3188 : : /*
3189 : : * getTableDataFKConstraints -
3190 : : * add dump-order dependencies reflecting foreign key constraints
3191 : : *
3192 : : * This code is executed only in a data-only dump --- in schema+data dumps
3193 : : * we handle foreign key issues by not creating the FK constraints until
3194 : : * after the data is loaded. In a data-only dump, however, we want to
3195 : : * order the table data objects in such a way that a table's referenced
3196 : : * tables are restored first. (In the presence of circular references or
3197 : : * self-references this may be impossible; we'll detect and complain about
3198 : : * that during the dependency sorting step.)
3199 : : */
3200 : : static void
3201 : 7 : getTableDataFKConstraints(void)
3202 : : {
3203 : : DumpableObject **dobjs;
3204 : : int numObjs;
3205 : : int i;
3206 : :
3207 : : /* Search through all the dumpable objects for FK constraints */
3208 : 7 : getDumpableObjects(&dobjs, &numObjs);
3209 [ + + ]: 26824 : for (i = 0; i < numObjs; i++)
3210 : : {
3211 [ + + ]: 26817 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3212 : : {
3213 : 8 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3214 : : TableInfo *ftable;
3215 : :
3216 : : /* Not interesting unless both tables are to be dumped */
3217 [ + - ]: 8 : if (cinfo->contable == NULL ||
3218 [ + + ]: 8 : cinfo->contable->dataObj == NULL)
3219 : 4 : continue;
3220 : 4 : ftable = findTableByOid(cinfo->confrelid);
3221 [ + - ]: 4 : if (ftable == NULL ||
3222 [ - + ]: 4 : ftable->dataObj == NULL)
3223 : 0 : continue;
3224 : :
3225 : : /*
3226 : : * Okay, make referencing table's TABLE_DATA object depend on the
3227 : : * referenced table's TABLE_DATA object.
3228 : : */
3229 : 4 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3230 : 4 : ftable->dataObj->dobj.dumpId);
3231 : : }
3232 : : }
3233 : 7 : free(dobjs);
3234 : 7 : }
3235 : :
3236 : :
3237 : : /*
3238 : : * dumpDatabase:
3239 : : * dump the database definition
3240 : : */
3241 : : static void
3242 : 92 : dumpDatabase(Archive *fout)
3243 : : {
3244 : 92 : DumpOptions *dopt = fout->dopt;
3245 : 92 : PQExpBuffer dbQry = createPQExpBuffer();
3246 : 92 : PQExpBuffer delQry = createPQExpBuffer();
3247 : 92 : PQExpBuffer creaQry = createPQExpBuffer();
3248 : 92 : PQExpBuffer labelq = createPQExpBuffer();
3249 : 92 : PGconn *conn = GetConnection(fout);
3250 : : PGresult *res;
3251 : : int i_tableoid,
3252 : : i_oid,
3253 : : i_datname,
3254 : : i_datdba,
3255 : : i_encoding,
3256 : : i_datlocprovider,
3257 : : i_collate,
3258 : : i_ctype,
3259 : : i_datlocale,
3260 : : i_daticurules,
3261 : : i_frozenxid,
3262 : : i_minmxid,
3263 : : i_datacl,
3264 : : i_acldefault,
3265 : : i_datistemplate,
3266 : : i_datconnlimit,
3267 : : i_datcollversion,
3268 : : i_tablespace;
3269 : : CatalogId dbCatId;
3270 : : DumpId dbDumpId;
3271 : : DumpableAcl dbdacl;
3272 : : const char *datname,
3273 : : *dba,
3274 : : *encoding,
3275 : : *datlocprovider,
3276 : : *collate,
3277 : : *ctype,
3278 : : *locale,
3279 : : *icurules,
3280 : : *datistemplate,
3281 : : *datconnlimit,
3282 : : *tablespace;
3283 : : uint32 frozenxid,
3284 : : minmxid;
3285 : : char *qdatname;
3286 : :
3287 : 92 : pg_log_info("saving database definition");
3288 : :
3289 : : /*
3290 : : * Fetch the database-level properties for this database.
3291 : : */
3292 : 92 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3293 : : "datdba, "
3294 : : "pg_encoding_to_char(encoding) AS encoding, "
3295 : : "datcollate, datctype, datfrozenxid, "
3296 : : "datacl, acldefault('d', datdba) AS acldefault, "
3297 : : "datistemplate, datconnlimit, ");
3298 : 92 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3299 [ + - ]: 92 : if (fout->remoteVersion >= 170000)
3300 : 92 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3301 [ # # ]: 0 : else if (fout->remoteVersion >= 150000)
3302 : 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3303 : : else
3304 : 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3305 [ + - ]: 92 : if (fout->remoteVersion >= 160000)
3306 : 92 : appendPQExpBufferStr(dbQry, "daticurules, ");
3307 : : else
3308 : 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3309 : 92 : appendPQExpBufferStr(dbQry,
3310 : : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3311 : : "shobj_description(oid, 'pg_database') AS description "
3312 : : "FROM pg_database "
3313 : : "WHERE datname = current_database()");
3314 : :
3315 : 92 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3316 : :
3317 : 92 : i_tableoid = PQfnumber(res, "tableoid");
3318 : 92 : i_oid = PQfnumber(res, "oid");
3319 : 92 : i_datname = PQfnumber(res, "datname");
3320 : 92 : i_datdba = PQfnumber(res, "datdba");
3321 : 92 : i_encoding = PQfnumber(res, "encoding");
3322 : 92 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3323 : 92 : i_collate = PQfnumber(res, "datcollate");
3324 : 92 : i_ctype = PQfnumber(res, "datctype");
3325 : 92 : i_datlocale = PQfnumber(res, "datlocale");
3326 : 92 : i_daticurules = PQfnumber(res, "daticurules");
3327 : 92 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3328 : 92 : i_minmxid = PQfnumber(res, "datminmxid");
3329 : 92 : i_datacl = PQfnumber(res, "datacl");
3330 : 92 : i_acldefault = PQfnumber(res, "acldefault");
3331 : 92 : i_datistemplate = PQfnumber(res, "datistemplate");
3332 : 92 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3333 : 92 : i_datcollversion = PQfnumber(res, "datcollversion");
3334 : 92 : i_tablespace = PQfnumber(res, "tablespace");
3335 : :
3336 : 92 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3337 : 92 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3338 : 92 : datname = PQgetvalue(res, 0, i_datname);
3339 : 92 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3340 : 92 : encoding = PQgetvalue(res, 0, i_encoding);
3341 : 92 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3342 : 92 : collate = PQgetvalue(res, 0, i_collate);
3343 : 92 : ctype = PQgetvalue(res, 0, i_ctype);
3344 [ + + ]: 92 : if (!PQgetisnull(res, 0, i_datlocale))
3345 : 14 : locale = PQgetvalue(res, 0, i_datlocale);
3346 : : else
3347 : 78 : locale = NULL;
3348 [ - + ]: 92 : if (!PQgetisnull(res, 0, i_daticurules))
3349 : 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3350 : : else
3351 : 92 : icurules = NULL;
3352 : 92 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3353 : 92 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3354 : 92 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3355 : 92 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3356 : 92 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3357 : 92 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3358 : 92 : tablespace = PQgetvalue(res, 0, i_tablespace);
3359 : :
3360 : 92 : qdatname = pg_strdup(fmtId(datname));
3361 : :
3362 : : /*
3363 : : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3364 : : * to preserve that), as well as the encoding, locale, and tablespace
3365 : : * since those can't be altered later. Other DB properties are left to
3366 : : * the DATABASE PROPERTIES entry, so that they can be applied after
3367 : : * reconnecting to the target DB.
3368 : : *
3369 : : * For binary upgrade, we use the FILE_COPY strategy because testing has
3370 : : * shown it to be faster. When the server is in binary upgrade mode, it
3371 : : * will also skip the checkpoints this strategy ordinarily performs.
3372 : : */
3373 [ + + ]: 92 : if (dopt->binary_upgrade)
3374 : : {
3375 : 39 : appendPQExpBuffer(creaQry,
3376 : : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3377 : : "OID = %u STRATEGY = FILE_COPY",
3378 : : qdatname, dbCatId.oid);
3379 : : }
3380 : : else
3381 : : {
3382 : 53 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3383 : : qdatname);
3384 : : }
3385 [ + - ]: 92 : if (strlen(encoding) > 0)
3386 : : {
3387 : 92 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3388 : 92 : appendStringLiteralAH(creaQry, encoding, fout);
3389 : : }
3390 : :
3391 : 92 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3392 [ + + ]: 92 : if (datlocprovider[0] == 'b')
3393 : 14 : appendPQExpBufferStr(creaQry, "builtin");
3394 [ + - ]: 78 : else if (datlocprovider[0] == 'c')
3395 : 78 : appendPQExpBufferStr(creaQry, "libc");
3396 [ # # ]: 0 : else if (datlocprovider[0] == 'i')
3397 : 0 : appendPQExpBufferStr(creaQry, "icu");
3398 : : else
3399 : 0 : pg_fatal("unrecognized locale provider: %s",
3400 : : datlocprovider);
3401 : :
3402 [ + - + - ]: 92 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3403 : : {
3404 : 92 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3405 : 92 : appendStringLiteralAH(creaQry, collate, fout);
3406 : : }
3407 : : else
3408 : : {
3409 [ # # ]: 0 : if (strlen(collate) > 0)
3410 : : {
3411 : 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3412 : 0 : appendStringLiteralAH(creaQry, collate, fout);
3413 : : }
3414 [ # # ]: 0 : if (strlen(ctype) > 0)
3415 : : {
3416 : 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3417 : 0 : appendStringLiteralAH(creaQry, ctype, fout);
3418 : : }
3419 : : }
3420 [ + + ]: 92 : if (locale)
3421 : : {
3422 [ + - ]: 14 : if (datlocprovider[0] == 'b')
3423 : 14 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3424 : : else
3425 : 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3426 : :
3427 : 14 : appendStringLiteralAH(creaQry, locale, fout);
3428 : : }
3429 : :
3430 [ - + ]: 92 : if (icurules)
3431 : : {
3432 : 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3433 : 0 : appendStringLiteralAH(creaQry, icurules, fout);
3434 : : }
3435 : :
3436 : : /*
3437 : : * For binary upgrade, carry over the collation version. For normal
3438 : : * dump/restore, omit the version, so that it is computed upon restore.
3439 : : */
3440 [ + + ]: 92 : if (dopt->binary_upgrade)
3441 : : {
3442 [ + - ]: 39 : if (!PQgetisnull(res, 0, i_datcollversion))
3443 : : {
3444 : 39 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3445 : 39 : appendStringLiteralAH(creaQry,
3446 : : PQgetvalue(res, 0, i_datcollversion),
3447 : : fout);
3448 : : }
3449 : : }
3450 : :
3451 : : /*
3452 : : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3453 : : * thing; the decision whether to specify a tablespace should be left till
3454 : : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3455 : : * label the DATABASE entry with the tablespace and let the normal
3456 : : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3457 : : * attention to default_tablespace, so that won't work.
3458 : : */
3459 [ + - + + ]: 92 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3460 [ + - ]: 5 : !dopt->outputNoTablespaces)
3461 : 5 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3462 : : fmtId(tablespace));
3463 : 92 : appendPQExpBufferStr(creaQry, ";\n");
3464 : :
3465 : 92 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3466 : : qdatname);
3467 : :
3468 : 92 : dbDumpId = createDumpId();
3469 : :
3470 : 92 : ArchiveEntry(fout,
3471 : : dbCatId, /* catalog ID */
3472 : : dbDumpId, /* dump ID */
3473 : 92 : ARCHIVE_OPTS(.tag = datname,
3474 : : .owner = dba,
3475 : : .description = "DATABASE",
3476 : : .section = SECTION_PRE_DATA,
3477 : : .createStmt = creaQry->data,
3478 : : .dropStmt = delQry->data));
3479 : :
3480 : : /* Compute correct tag for archive entry */
3481 : 92 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3482 : :
3483 : : /* Dump DB comment if any */
3484 : : {
3485 : : /*
3486 : : * 8.2 and up keep comments on shared objects in a shared table, so we
3487 : : * cannot use the dumpComment() code used for other database objects.
3488 : : * Be careful that the ArchiveEntry parameters match that function.
3489 : : */
3490 : 92 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3491 : :
3492 [ + - + + : 92 : if (comment && *comment && !dopt->no_comments)
+ - ]
3493 : : {
3494 : 47 : resetPQExpBuffer(dbQry);
3495 : :
3496 : : /*
3497 : : * Generates warning when loaded into a differently-named
3498 : : * database.
3499 : : */
3500 : 47 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3501 : 47 : appendStringLiteralAH(dbQry, comment, fout);
3502 : 47 : appendPQExpBufferStr(dbQry, ";\n");
3503 : :
3504 : 47 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3505 : 47 : ARCHIVE_OPTS(.tag = labelq->data,
3506 : : .owner = dba,
3507 : : .description = "COMMENT",
3508 : : .section = SECTION_NONE,
3509 : : .createStmt = dbQry->data,
3510 : : .deps = &dbDumpId,
3511 : : .nDeps = 1));
3512 : : }
3513 : : }
3514 : :
3515 : : /* Dump DB security label, if enabled */
3516 [ + - ]: 92 : if (!dopt->no_security_labels)
3517 : : {
3518 : : PGresult *shres;
3519 : : PQExpBuffer seclabelQry;
3520 : :
3521 : 92 : seclabelQry = createPQExpBuffer();
3522 : :
3523 : 92 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3524 : 92 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3525 : 92 : resetPQExpBuffer(seclabelQry);
3526 : 92 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3527 [ - + ]: 92 : if (seclabelQry->len > 0)
3528 : 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3529 : 0 : ARCHIVE_OPTS(.tag = labelq->data,
3530 : : .owner = dba,
3531 : : .description = "SECURITY LABEL",
3532 : : .section = SECTION_NONE,
3533 : : .createStmt = seclabelQry->data,
3534 : : .deps = &dbDumpId,
3535 : : .nDeps = 1));
3536 : 92 : destroyPQExpBuffer(seclabelQry);
3537 : 92 : PQclear(shres);
3538 : : }
3539 : :
3540 : : /*
3541 : : * Dump ACL if any. Note that we do not support initial privileges
3542 : : * (pg_init_privs) on databases.
3543 : : */
3544 : 92 : dbdacl.privtype = 0;
3545 : 92 : dbdacl.initprivs = NULL;
3546 : :
3547 : 92 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3548 : : qdatname, NULL, NULL,
3549 : : NULL, dba, &dbdacl);
3550 : :
3551 : : /*
3552 : : * Now construct a DATABASE PROPERTIES archive entry to restore any
3553 : : * non-default database-level properties. (The reason this must be
3554 : : * separate is that we cannot put any additional commands into the TOC
3555 : : * entry that has CREATE DATABASE. pg_restore would execute such a group
3556 : : * in an implicit transaction block, and the backend won't allow CREATE
3557 : : * DATABASE in that context.)
3558 : : */
3559 : 92 : resetPQExpBuffer(creaQry);
3560 : 92 : resetPQExpBuffer(delQry);
3561 : :
3562 [ + - - + ]: 92 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3563 : 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3564 : : qdatname, datconnlimit);
3565 : :
3566 [ + + ]: 92 : if (strcmp(datistemplate, "t") == 0)
3567 : : {
3568 : 12 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3569 : : qdatname);
3570 : :
3571 : : /*
3572 : : * The backend won't accept DROP DATABASE on a template database. We
3573 : : * can deal with that by removing the template marking before the DROP
3574 : : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3575 : : * since no such command is currently supported, fake it with a direct
3576 : : * UPDATE on pg_database.
3577 : : */
3578 : 12 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3579 : : "SET datistemplate = false WHERE datname = ");
3580 : 12 : appendStringLiteralAH(delQry, datname, fout);
3581 : 12 : appendPQExpBufferStr(delQry, ";\n");
3582 : : }
3583 : :
3584 : : /*
3585 : : * We do not restore pg_database.dathasloginevt because it is set
3586 : : * automatically on login event trigger creation.
3587 : : */
3588 : :
3589 : : /* Add database-specific SET options */
3590 : 92 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3591 : :
3592 : : /*
3593 : : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3594 : : * entry, too, for lack of a better place.
3595 : : */
3596 [ + + ]: 92 : if (dopt->binary_upgrade)
3597 : : {
3598 : 39 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3599 : 39 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3600 : : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3601 : : "WHERE datname = ",
3602 : : frozenxid, minmxid);
3603 : 39 : appendStringLiteralAH(creaQry, datname, fout);
3604 : 39 : appendPQExpBufferStr(creaQry, ";\n");
3605 : : }
3606 : :
3607 [ + + ]: 92 : if (creaQry->len > 0)
3608 : 43 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3609 : 43 : ARCHIVE_OPTS(.tag = datname,
3610 : : .owner = dba,
3611 : : .description = "DATABASE PROPERTIES",
3612 : : .section = SECTION_PRE_DATA,
3613 : : .createStmt = creaQry->data,
3614 : : .dropStmt = delQry->data,
3615 : : .deps = &dbDumpId));
3616 : :
3617 : : /*
3618 : : * pg_largeobject comes from the old system intact, so set its
3619 : : * relfrozenxids, relminmxids and relfilenode.
3620 : : *
3621 : : * pg_largeobject_metadata also comes from the old system intact for
3622 : : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3623 : : * relfilenode, too. pg_upgrade can't copy/link the files from older
3624 : : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3625 : : * changed its storage format in v16.
3626 : : */
3627 [ + + ]: 92 : if (dopt->binary_upgrade)
3628 : : {
3629 : : PGresult *lo_res;
3630 : 39 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3631 : 39 : PQExpBuffer loOutQry = createPQExpBuffer();
3632 : 39 : PQExpBuffer lomOutQry = createPQExpBuffer();
3633 : 39 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3634 : 39 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3635 : : int ii_relfrozenxid,
3636 : : ii_relfilenode,
3637 : : ii_oid,
3638 : : ii_relminmxid;
3639 : :
3640 : 39 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3641 : : "FROM pg_catalog.pg_class\n"
3642 : : "WHERE oid IN (%u, %u, %u, %u);\n",
3643 : : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3644 : : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3645 : :
3646 : 39 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3647 : :
3648 : 39 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3649 : 39 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3650 : 39 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3651 : 39 : ii_oid = PQfnumber(lo_res, "oid");
3652 : :
3653 : 39 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3654 : 39 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3655 : 39 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3656 : 39 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3657 [ + + ]: 195 : for (int i = 0; i < PQntuples(lo_res); ++i)
3658 : : {
3659 : : Oid oid;
3660 : : RelFileNumber relfilenumber;
3661 : : PQExpBuffer horizonQry;
3662 : : PQExpBuffer outQry;
3663 : :
3664 : 156 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3665 : 156 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3666 : :
3667 [ + + + + ]: 156 : if (oid == LargeObjectRelationId ||
3668 : : oid == LargeObjectLOidPNIndexId)
3669 : : {
3670 : 78 : horizonQry = loHorizonQry;
3671 : 78 : outQry = loOutQry;
3672 : : }
3673 : : else
3674 : : {
3675 : 78 : horizonQry = lomHorizonQry;
3676 : 78 : outQry = lomOutQry;
3677 : : }
3678 : :
3679 : 156 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3680 : : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3681 : : "WHERE oid = %u;\n",
3682 : 156 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3683 : 156 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3684 : 156 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3685 : :
3686 [ + + + + ]: 156 : if (oid == LargeObjectRelationId ||
3687 : : oid == LargeObjectMetadataRelationId)
3688 : 78 : appendPQExpBuffer(outQry,
3689 : : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3690 : : relfilenumber);
3691 [ + + + - ]: 78 : else if (oid == LargeObjectLOidPNIndexId ||
3692 : : oid == LargeObjectMetadataOidIndexId)
3693 : 78 : appendPQExpBuffer(outQry,
3694 : : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3695 : : relfilenumber);
3696 : : }
3697 : :
3698 : 39 : appendPQExpBufferStr(loOutQry,
3699 : : "TRUNCATE pg_catalog.pg_largeobject;\n");
3700 : 39 : appendPQExpBufferStr(lomOutQry,
3701 : : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3702 : :
3703 : 39 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3704 : 39 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3705 : :
3706 : 39 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3707 : 39 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3708 : : .description = "pg_largeobject",
3709 : : .section = SECTION_PRE_DATA,
3710 : : .createStmt = loOutQry->data));
3711 : :
3712 [ + - ]: 39 : if (fout->remoteVersion >= 160000)
3713 : 39 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3714 : 39 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3715 : : .description = "pg_largeobject_metadata",
3716 : : .section = SECTION_PRE_DATA,
3717 : : .createStmt = lomOutQry->data));
3718 : :
3719 : 39 : PQclear(lo_res);
3720 : :
3721 : 39 : destroyPQExpBuffer(loFrozenQry);
3722 : 39 : destroyPQExpBuffer(loHorizonQry);
3723 : 39 : destroyPQExpBuffer(lomHorizonQry);
3724 : 39 : destroyPQExpBuffer(loOutQry);
3725 : 39 : destroyPQExpBuffer(lomOutQry);
3726 : : }
3727 : :
3728 : 92 : PQclear(res);
3729 : :
3730 : 92 : pg_free(qdatname);
3731 : 92 : destroyPQExpBuffer(dbQry);
3732 : 92 : destroyPQExpBuffer(delQry);
3733 : 92 : destroyPQExpBuffer(creaQry);
3734 : 92 : destroyPQExpBuffer(labelq);
3735 : 92 : }
3736 : :
3737 : : /*
3738 : : * Collect any database-specific or role-and-database-specific SET options
3739 : : * for this database, and append them to outbuf.
3740 : : */
3741 : : static void
3742 : 92 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3743 : : const char *dbname, Oid dboid)
3744 : : {
3745 : 92 : PGconn *conn = GetConnection(AH);
3746 : 92 : PQExpBuffer buf = createPQExpBuffer();
3747 : : PGresult *res;
3748 : :
3749 : : /* First collect database-specific options */
3750 : 92 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3751 : : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3752 : : dboid);
3753 : :
3754 : 92 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3755 : :
3756 [ + + ]: 122 : for (int i = 0; i < PQntuples(res); i++)
3757 : 30 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3758 : : "DATABASE", dbname, NULL, NULL,
3759 : : outbuf);
3760 : :
3761 : 92 : PQclear(res);
3762 : :
3763 : : /* Now look for role-and-database-specific options */
3764 : 92 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3765 : : "FROM pg_db_role_setting s, pg_roles r "
3766 : : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3767 : : dboid);
3768 : :
3769 : 92 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3770 : :
3771 [ - + ]: 92 : for (int i = 0; i < PQntuples(res); i++)
3772 : 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3773 : 0 : "ROLE", PQgetvalue(res, i, 0),
3774 : : "DATABASE", dbname,
3775 : : outbuf);
3776 : :
3777 : 92 : PQclear(res);
3778 : :
3779 : 92 : destroyPQExpBuffer(buf);
3780 : 92 : }
3781 : :
3782 : : /*
3783 : : * dumpEncoding: put the correct encoding into the archive
3784 : : */
3785 : : static void
3786 : 191 : dumpEncoding(Archive *AH)
3787 : : {
3788 : 191 : const char *encname = pg_encoding_to_char(AH->encoding);
3789 : 191 : PQExpBuffer qry = createPQExpBuffer();
3790 : :
3791 : 191 : pg_log_info("saving encoding = %s", encname);
3792 : :
3793 : 191 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3794 : 191 : appendStringLiteralAH(qry, encname, AH);
3795 : 191 : appendPQExpBufferStr(qry, ";\n");
3796 : :
3797 : 191 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3798 : 191 : ARCHIVE_OPTS(.tag = "ENCODING",
3799 : : .description = "ENCODING",
3800 : : .section = SECTION_PRE_DATA,
3801 : : .createStmt = qry->data));
3802 : :
3803 : 191 : destroyPQExpBuffer(qry);
3804 : 191 : }
3805 : :
3806 : :
3807 : : /*
3808 : : * dumpStdStrings: put the correct escape string behavior into the archive
3809 : : */
3810 : : static void
3811 : 191 : dumpStdStrings(Archive *AH)
3812 : : {
3813 [ + - ]: 191 : const char *stdstrings = AH->std_strings ? "on" : "off";
3814 : 191 : PQExpBuffer qry = createPQExpBuffer();
3815 : :
3816 : 191 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3817 : : stdstrings);
3818 : :
3819 : 191 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3820 : : stdstrings);
3821 : :
3822 : 191 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3823 : 191 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3824 : : .description = "STDSTRINGS",
3825 : : .section = SECTION_PRE_DATA,
3826 : : .createStmt = qry->data));
3827 : :
3828 : 191 : destroyPQExpBuffer(qry);
3829 : 191 : }
3830 : :
3831 : : /*
3832 : : * dumpSearchPath: record the active search_path in the archive
3833 : : */
3834 : : static void
3835 : 191 : dumpSearchPath(Archive *AH)
3836 : : {
3837 : 191 : PQExpBuffer qry = createPQExpBuffer();
3838 : 191 : PQExpBuffer path = createPQExpBuffer();
3839 : : PGresult *res;
3840 : 191 : char **schemanames = NULL;
3841 : 191 : int nschemanames = 0;
3842 : : int i;
3843 : :
3844 : : /*
3845 : : * We use the result of current_schemas(), not the search_path GUC,
3846 : : * because that might contain wildcards such as "$user", which won't
3847 : : * necessarily have the same value during restore. Also, this way avoids
3848 : : * listing schemas that may appear in search_path but not actually exist,
3849 : : * which seems like a prudent exclusion.
3850 : : */
3851 : 191 : res = ExecuteSqlQueryForSingleRow(AH,
3852 : : "SELECT pg_catalog.current_schemas(false)");
3853 : :
3854 [ - + ]: 191 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3855 : 0 : pg_fatal("could not parse result of current_schemas()");
3856 : :
3857 : : /*
3858 : : * We use set_config(), not a simple "SET search_path" command, because
3859 : : * the latter has less-clean behavior if the search path is empty. While
3860 : : * that's likely to get fixed at some point, it seems like a good idea to
3861 : : * be as backwards-compatible as possible in what we put into archives.
3862 : : */
3863 [ - + ]: 191 : for (i = 0; i < nschemanames; i++)
3864 : : {
3865 [ # # ]: 0 : if (i > 0)
3866 : 0 : appendPQExpBufferStr(path, ", ");
3867 : 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3868 : : }
3869 : :
3870 : 191 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3871 : 191 : appendStringLiteralAH(qry, path->data, AH);
3872 : 191 : appendPQExpBufferStr(qry, ", false);\n");
3873 : :
3874 : 191 : pg_log_info("saving \"search_path = %s\"", path->data);
3875 : :
3876 : 191 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3877 : 191 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3878 : : .description = "SEARCHPATH",
3879 : : .section = SECTION_PRE_DATA,
3880 : : .createStmt = qry->data));
3881 : :
3882 : : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3883 : 191 : AH->searchpath = pg_strdup(qry->data);
3884 : :
3885 : 191 : free(schemanames);
3886 : 191 : PQclear(res);
3887 : 191 : destroyPQExpBuffer(qry);
3888 : 191 : destroyPQExpBuffer(path);
3889 : 191 : }
3890 : :
3891 : :
3892 : : /*
3893 : : * getLOs:
3894 : : * Collect schema-level data about large objects
3895 : : */
3896 : : static void
3897 : 162 : getLOs(Archive *fout)
3898 : : {
3899 : 162 : DumpOptions *dopt = fout->dopt;
3900 : 162 : PQExpBuffer loQry = createPQExpBuffer();
3901 : : PGresult *res;
3902 : : int ntups;
3903 : : int i;
3904 : : int n;
3905 : : int i_oid;
3906 : : int i_lomowner;
3907 : : int i_lomacl;
3908 : : int i_acldefault;
3909 : :
3910 : 162 : pg_log_info("reading large objects");
3911 : :
3912 : : /*
3913 : : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3914 : : * with the same owner/ACL appear together.
3915 : : */
3916 : 162 : appendPQExpBufferStr(loQry,
3917 : : "SELECT oid, lomowner, lomacl, "
3918 : : "acldefault('L', lomowner) AS acldefault "
3919 : : "FROM pg_largeobject_metadata ");
3920 : :
3921 : : /*
3922 : : * For binary upgrades, we transfer pg_largeobject_metadata via COPY or by
3923 : : * copying/linking its files from the old cluster. On such upgrades, we
3924 : : * only need to consider large objects that have comments or security
3925 : : * labels, since we still restore those objects via COMMENT/SECURITY LABEL
3926 : : * commands.
3927 : : */
3928 [ + + ]: 162 : if (dopt->binary_upgrade)
3929 : 40 : appendPQExpBufferStr(loQry,
3930 : : "WHERE oid IN "
3931 : : "(SELECT objoid FROM pg_description "
3932 : : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
3933 : : "UNION SELECT objoid FROM pg_seclabel "
3934 : : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ") ");
3935 : :
3936 : 162 : appendPQExpBufferStr(loQry,
3937 : : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3938 : :
3939 : 162 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3940 : :
3941 : 162 : i_oid = PQfnumber(res, "oid");
3942 : 162 : i_lomowner = PQfnumber(res, "lomowner");
3943 : 162 : i_lomacl = PQfnumber(res, "lomacl");
3944 : 162 : i_acldefault = PQfnumber(res, "acldefault");
3945 : :
3946 : 162 : ntups = PQntuples(res);
3947 : :
3948 : : /*
3949 : : * Group the blobs into suitably-sized groups that have the same owner and
3950 : : * ACL setting, and build a metadata and a data DumpableObject for each
3951 : : * group. (If we supported initprivs for blobs, we'd have to insist that
3952 : : * groups also share initprivs settings, since the DumpableObject only has
3953 : : * room for one.) i is the index of the first tuple in the current group,
3954 : : * and n is the number of tuples we include in the group.
3955 : : */
3956 [ + + ]: 250 : for (i = 0; i < ntups; i += n)
3957 : : {
3958 : 88 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3959 : 88 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3960 : 88 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3961 : : LoInfo *loinfo;
3962 : : DumpableObject *lodata;
3963 : : char namebuf[64];
3964 : :
3965 : : /* Scan to find first tuple not to be included in group */
3966 : 88 : n = 1;
3967 [ + - + + ]: 102 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3968 : : {
3969 [ + - ]: 49 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3970 [ + + ]: 49 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3971 : : break;
3972 : 14 : n++;
3973 : : }
3974 : :
3975 : : /* Build the metadata DumpableObject */
3976 : 88 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3977 : :
3978 : 88 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3979 : 88 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3980 : 88 : loinfo->dobj.catId.oid = thisoid;
3981 : 88 : AssignDumpId(&loinfo->dobj);
3982 : :
3983 [ + + ]: 88 : if (n > 1)
3984 : 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3985 : 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3986 : : else
3987 : 78 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3988 : 88 : loinfo->dobj.name = pg_strdup(namebuf);
3989 : 88 : loinfo->dacl.acl = pg_strdup(thisacl);
3990 : 88 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3991 : 88 : loinfo->dacl.privtype = 0;
3992 : 88 : loinfo->dacl.initprivs = NULL;
3993 : 88 : loinfo->rolname = getRoleName(thisowner);
3994 : 88 : loinfo->numlos = n;
3995 : 88 : loinfo->looids[0] = thisoid;
3996 : : /* Collect OIDs of the remaining blobs in this group */
3997 [ + + ]: 102 : for (int k = 1; k < n; k++)
3998 : : {
3999 : : CatalogId extraID;
4000 : :
4001 : 14 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4002 : :
4003 : : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4004 : 14 : extraID.tableoid = LargeObjectRelationId;
4005 : 14 : extraID.oid = loinfo->looids[k];
4006 : 14 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4007 : : }
4008 : :
4009 : : /* LOs have data */
4010 : 88 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4011 : :
4012 : : /* Mark whether LO group has a non-empty ACL */
4013 [ + + ]: 88 : if (!PQgetisnull(res, i, i_lomacl))
4014 : 36 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4015 : :
4016 : : /*
4017 : : * In binary upgrade mode, pg_largeobject and pg_largeobject_metadata
4018 : : * are transferred via COPY or by copying/linking the files from the
4019 : : * old cluster. Thus, we do not need to dump LO data, definitions, or
4020 : : * ACLs.
4021 : : */
4022 [ + + ]: 88 : if (dopt->binary_upgrade)
4023 : 7 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4024 : :
4025 : : /*
4026 : : * Create a "BLOBS" data item for the group, too. This is just a
4027 : : * placeholder for sorting; it carries no data now.
4028 : : */
4029 : 88 : lodata = pg_malloc_object(DumpableObject);
4030 : 88 : lodata->objType = DO_LARGE_OBJECT_DATA;
4031 : 88 : lodata->catId = nilCatalogId;
4032 : 88 : AssignDumpId(lodata);
4033 : 88 : lodata->name = pg_strdup(namebuf);
4034 : 88 : lodata->components |= DUMP_COMPONENT_DATA;
4035 : : /* Set up explicit dependency from data to metadata */
4036 : 88 : lodata->dependencies = pg_malloc_object(DumpId);
4037 : 88 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4038 : 88 : lodata->nDeps = lodata->allocDeps = 1;
4039 : : }
4040 : :
4041 : 162 : PQclear(res);
4042 : 162 : destroyPQExpBuffer(loQry);
4043 : 162 : }
4044 : :
4045 : : /*
4046 : : * dumpLO
4047 : : *
4048 : : * dump the definition (metadata) of the given large object group
4049 : : */
4050 : : static void
4051 : 88 : dumpLO(Archive *fout, const LoInfo *loinfo)
4052 : : {
4053 : 88 : PQExpBuffer cquery = createPQExpBuffer();
4054 : :
4055 : : /*
4056 : : * The "definition" is just a newline-separated list of OIDs. We need to
4057 : : * put something into the dropStmt too, but it can just be a comment.
4058 : : */
4059 [ + + ]: 190 : for (int i = 0; i < loinfo->numlos; i++)
4060 : 102 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4061 : :
4062 [ + + ]: 88 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4063 : 81 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4064 : 81 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4065 : : .owner = loinfo->rolname,
4066 : : .description = "BLOB METADATA",
4067 : : .section = SECTION_DATA,
4068 : : .createStmt = cquery->data,
4069 : : .dropStmt = "-- dummy"));
4070 : :
4071 : : /*
4072 : : * Dump per-blob comments and seclabels if any. We assume these are rare
4073 : : * enough that it's okay to generate retail TOC entries for them.
4074 : : */
4075 [ + + ]: 88 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4076 : : DUMP_COMPONENT_SECLABEL))
4077 : : {
4078 [ + + ]: 106 : for (int i = 0; i < loinfo->numlos; i++)
4079 : : {
4080 : : CatalogId catId;
4081 : : char namebuf[32];
4082 : :
4083 : : /* Build identifying info for this blob */
4084 : 60 : catId.tableoid = loinfo->dobj.catId.tableoid;
4085 : 60 : catId.oid = loinfo->looids[i];
4086 : 60 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4087 : :
4088 [ + - ]: 60 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4089 : 60 : dumpComment(fout, "LARGE OBJECT", namebuf,
4090 : 60 : NULL, loinfo->rolname,
4091 : 60 : catId, 0, loinfo->dobj.dumpId);
4092 : :
4093 [ + + ]: 60 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4094 : 10 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4095 : 10 : NULL, loinfo->rolname,
4096 : 10 : catId, 0, loinfo->dobj.dumpId);
4097 : : }
4098 : : }
4099 : :
4100 : : /*
4101 : : * Dump the ACLs if any (remember that all blobs in the group will have
4102 : : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4103 : : * there's more, make a "LARGE OBJECTS" entry that really contains only
4104 : : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4105 : : * string to emit a mutated version for each blob.
4106 : : */
4107 [ + + ]: 88 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4108 : : {
4109 : : char namebuf[32];
4110 : :
4111 : : /* Build identifying info for the first blob */
4112 : 35 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4113 : :
4114 [ - + ]: 35 : if (loinfo->numlos > 1)
4115 : : {
4116 : : char tagbuf[64];
4117 : :
4118 : 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4119 : 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4120 : :
4121 : 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4122 : : "LARGE OBJECT", namebuf, NULL, NULL,
4123 : 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4124 : : }
4125 : : else
4126 : : {
4127 : 35 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4128 : : "LARGE OBJECT", namebuf, NULL, NULL,
4129 : 35 : NULL, loinfo->rolname, &loinfo->dacl);
4130 : : }
4131 : : }
4132 : :
4133 : 88 : destroyPQExpBuffer(cquery);
4134 : 88 : }
4135 : :
4136 : : /*
4137 : : * dumpLOs:
4138 : : * dump the data contents of the large objects in the given group
4139 : : */
4140 : : static int
4141 : 77 : dumpLOs(Archive *fout, const void *arg)
4142 : : {
4143 : 77 : const LoInfo *loinfo = (const LoInfo *) arg;
4144 : 77 : PGconn *conn = GetConnection(fout);
4145 : : char buf[LOBBUFSIZE];
4146 : :
4147 : 77 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4148 : :
4149 [ + + ]: 162 : for (int i = 0; i < loinfo->numlos; i++)
4150 : : {
4151 : 85 : Oid loOid = loinfo->looids[i];
4152 : : int loFd;
4153 : : int cnt;
4154 : :
4155 : : /* Open the LO */
4156 : 85 : loFd = lo_open(conn, loOid, INV_READ);
4157 [ - + ]: 85 : if (loFd == -1)
4158 : 0 : pg_fatal("could not open large object %u: %s",
4159 : : loOid, PQerrorMessage(conn));
4160 : :
4161 : 85 : StartLO(fout, loOid);
4162 : :
4163 : : /* Now read it in chunks, sending data to archive */
4164 : : do
4165 : : {
4166 : 133 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4167 [ - + ]: 133 : if (cnt < 0)
4168 : 0 : pg_fatal("error reading large object %u: %s",
4169 : : loOid, PQerrorMessage(conn));
4170 : :
4171 : 133 : WriteData(fout, buf, cnt);
4172 [ + + ]: 133 : } while (cnt > 0);
4173 : :
4174 : 85 : lo_close(conn, loFd);
4175 : :
4176 : 85 : EndLO(fout, loOid);
4177 : : }
4178 : :
4179 : 77 : return 1;
4180 : : }
4181 : :
4182 : : /*
4183 : : * getPolicies
4184 : : * get information about all RLS policies on dumpable tables.
4185 : : */
4186 : : void
4187 : 191 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4188 : : {
4189 : 191 : DumpOptions *dopt = fout->dopt;
4190 : : PQExpBuffer query;
4191 : : PQExpBuffer tbloids;
4192 : : PGresult *res;
4193 : : PolicyInfo *polinfo;
4194 : : int i_oid;
4195 : : int i_tableoid;
4196 : : int i_polrelid;
4197 : : int i_polname;
4198 : : int i_polcmd;
4199 : : int i_polpermissive;
4200 : : int i_polroles;
4201 : : int i_polqual;
4202 : : int i_polwithcheck;
4203 : : int i,
4204 : : j,
4205 : : ntups;
4206 : :
4207 : : /* Skip if --no-policies was specified */
4208 [ + + ]: 191 : if (dopt->no_policies)
4209 : 1 : return;
4210 : :
4211 : 190 : query = createPQExpBuffer();
4212 : 190 : tbloids = createPQExpBuffer();
4213 : :
4214 : : /*
4215 : : * Identify tables of interest, and check which ones have RLS enabled.
4216 : : */
4217 : 190 : appendPQExpBufferChar(tbloids, '{');
4218 [ + + ]: 54888 : for (i = 0; i < numTables; i++)
4219 : : {
4220 : 54698 : TableInfo *tbinfo = &tblinfo[i];
4221 : :
4222 : : /* Ignore row security on tables not to be dumped */
4223 [ + + ]: 54698 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4224 : 47091 : continue;
4225 : :
4226 : : /* It can't have RLS or policies if it's not a table */
4227 [ + + ]: 7607 : if (tbinfo->relkind != RELKIND_RELATION &&
4228 [ + + ]: 2167 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4229 : 1540 : continue;
4230 : :
4231 : : /* Add it to the list of table OIDs to be probed below */
4232 [ + + ]: 6067 : if (tbloids->len > 1) /* do we have more than the '{'? */
4233 : 5942 : appendPQExpBufferChar(tbloids, ',');
4234 : 6067 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4235 : :
4236 : : /* Is RLS enabled? (That's separate from whether it has policies) */
4237 [ + + ]: 6067 : if (tbinfo->rowsec)
4238 : : {
4239 : 66 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4240 : :
4241 : : /*
4242 : : * We represent RLS being enabled on a table by creating a
4243 : : * PolicyInfo object with null polname.
4244 : : *
4245 : : * Note: use tableoid 0 so that this object won't be mistaken for
4246 : : * something that pg_depend entries apply to.
4247 : : */
4248 : 66 : polinfo = pg_malloc_object(PolicyInfo);
4249 : 66 : polinfo->dobj.objType = DO_POLICY;
4250 : 66 : polinfo->dobj.catId.tableoid = 0;
4251 : 66 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4252 : 66 : AssignDumpId(&polinfo->dobj);
4253 : 66 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4254 : 66 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4255 : 66 : polinfo->poltable = tbinfo;
4256 : 66 : polinfo->polname = NULL;
4257 : 66 : polinfo->polcmd = '\0';
4258 : 66 : polinfo->polpermissive = 0;
4259 : 66 : polinfo->polroles = NULL;
4260 : 66 : polinfo->polqual = NULL;
4261 : 66 : polinfo->polwithcheck = NULL;
4262 : : }
4263 : : }
4264 : 190 : appendPQExpBufferChar(tbloids, '}');
4265 : :
4266 : : /*
4267 : : * Now, read all RLS policies belonging to the tables of interest, and
4268 : : * create PolicyInfo objects for them. (Note that we must filter the
4269 : : * results server-side not locally, because we dare not apply pg_get_expr
4270 : : * to tables we don't have lock on.)
4271 : : */
4272 : 190 : pg_log_info("reading row-level security policies");
4273 : :
4274 : 190 : printfPQExpBuffer(query,
4275 : : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4276 : 190 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4277 : 190 : appendPQExpBuffer(query,
4278 : : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4279 : : " 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, "
4280 : : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4281 : : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4282 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4283 : : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4284 : : tbloids->data);
4285 : :
4286 : 190 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4287 : :
4288 : 190 : ntups = PQntuples(res);
4289 [ + + ]: 190 : if (ntups > 0)
4290 : : {
4291 : 46 : i_oid = PQfnumber(res, "oid");
4292 : 46 : i_tableoid = PQfnumber(res, "tableoid");
4293 : 46 : i_polrelid = PQfnumber(res, "polrelid");
4294 : 46 : i_polname = PQfnumber(res, "polname");
4295 : 46 : i_polcmd = PQfnumber(res, "polcmd");
4296 : 46 : i_polpermissive = PQfnumber(res, "polpermissive");
4297 : 46 : i_polroles = PQfnumber(res, "polroles");
4298 : 46 : i_polqual = PQfnumber(res, "polqual");
4299 : 46 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4300 : :
4301 : 46 : polinfo = pg_malloc_array(PolicyInfo, ntups);
4302 : :
4303 [ + + ]: 337 : for (j = 0; j < ntups; j++)
4304 : : {
4305 : 291 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4306 : 291 : TableInfo *tbinfo = findTableByOid(polrelid);
4307 : :
4308 : 291 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4309 : :
4310 : 291 : polinfo[j].dobj.objType = DO_POLICY;
4311 : 291 : polinfo[j].dobj.catId.tableoid =
4312 : 291 : atooid(PQgetvalue(res, j, i_tableoid));
4313 : 291 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4314 : 291 : AssignDumpId(&polinfo[j].dobj);
4315 : 291 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4316 : 291 : polinfo[j].poltable = tbinfo;
4317 : 291 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4318 : 291 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4319 : :
4320 : 291 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4321 : 291 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4322 : :
4323 [ + + ]: 291 : if (PQgetisnull(res, j, i_polroles))
4324 : 127 : polinfo[j].polroles = NULL;
4325 : : else
4326 : 164 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4327 : :
4328 [ + + ]: 291 : if (PQgetisnull(res, j, i_polqual))
4329 : 41 : polinfo[j].polqual = NULL;
4330 : : else
4331 : 250 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4332 : :
4333 [ + + ]: 291 : if (PQgetisnull(res, j, i_polwithcheck))
4334 : 153 : polinfo[j].polwithcheck = NULL;
4335 : : else
4336 : 138 : polinfo[j].polwithcheck
4337 : 138 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4338 : : }
4339 : : }
4340 : :
4341 : 190 : PQclear(res);
4342 : :
4343 : 190 : destroyPQExpBuffer(query);
4344 : 190 : destroyPQExpBuffer(tbloids);
4345 : : }
4346 : :
4347 : : /*
4348 : : * dumpPolicy
4349 : : * dump the definition of the given policy
4350 : : */
4351 : : static void
4352 : 357 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4353 : : {
4354 : 357 : DumpOptions *dopt = fout->dopt;
4355 : 357 : TableInfo *tbinfo = polinfo->poltable;
4356 : : PQExpBuffer query;
4357 : : PQExpBuffer delqry;
4358 : : PQExpBuffer polprefix;
4359 : : char *qtabname;
4360 : : const char *cmd;
4361 : : char *tag;
4362 : :
4363 : : /* Do nothing if not dumping schema */
4364 [ + + ]: 357 : if (!dopt->dumpSchema)
4365 : 56 : return;
4366 : :
4367 : : /*
4368 : : * If polname is NULL, then this record is just indicating that ROW LEVEL
4369 : : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4370 : : * ROW LEVEL SECURITY.
4371 : : */
4372 [ + + ]: 301 : if (polinfo->polname == NULL)
4373 : : {
4374 : 58 : query = createPQExpBuffer();
4375 : :
4376 : 58 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4377 : 58 : fmtQualifiedDumpable(tbinfo));
4378 : :
4379 : : /*
4380 : : * We must emit the ROW SECURITY object's dependency on its table
4381 : : * explicitly, because it will not match anything in pg_depend (unlike
4382 : : * the case for other PolicyInfo objects).
4383 : : */
4384 [ + - ]: 58 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4385 : 58 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4386 : 58 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4387 : : .namespace = polinfo->dobj.namespace->dobj.name,
4388 : : .owner = tbinfo->rolname,
4389 : : .description = "ROW SECURITY",
4390 : : .section = SECTION_POST_DATA,
4391 : : .createStmt = query->data,
4392 : : .deps = &(tbinfo->dobj.dumpId),
4393 : : .nDeps = 1));
4394 : :
4395 : 58 : destroyPQExpBuffer(query);
4396 : 58 : return;
4397 : : }
4398 : :
4399 [ + + ]: 243 : if (polinfo->polcmd == '*')
4400 : 81 : cmd = "";
4401 [ + + ]: 162 : else if (polinfo->polcmd == 'r')
4402 : 43 : cmd = " FOR SELECT";
4403 [ + + ]: 119 : else if (polinfo->polcmd == 'a')
4404 : 33 : cmd = " FOR INSERT";
4405 [ + + ]: 86 : else if (polinfo->polcmd == 'w')
4406 : 43 : cmd = " FOR UPDATE";
4407 [ + - ]: 43 : else if (polinfo->polcmd == 'd')
4408 : 43 : cmd = " FOR DELETE";
4409 : : else
4410 : 0 : pg_fatal("unexpected policy command type: %c",
4411 : : polinfo->polcmd);
4412 : :
4413 : 243 : query = createPQExpBuffer();
4414 : 243 : delqry = createPQExpBuffer();
4415 : 243 : polprefix = createPQExpBuffer();
4416 : :
4417 : 243 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4418 : :
4419 : 243 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4420 : :
4421 : 243 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4422 [ + + ]: 243 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4423 : :
4424 [ + + ]: 243 : if (polinfo->polroles != NULL)
4425 : 132 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4426 : :
4427 [ + + ]: 243 : if (polinfo->polqual != NULL)
4428 : 210 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4429 : :
4430 [ + + ]: 243 : if (polinfo->polwithcheck != NULL)
4431 : 114 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4432 : :
4433 : 243 : appendPQExpBufferStr(query, ";\n");
4434 : :
4435 : 243 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4436 : 243 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4437 : :
4438 : 243 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4439 : 243 : fmtId(polinfo->polname));
4440 : :
4441 : 243 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4442 : :
4443 [ + - ]: 243 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4444 : 243 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4445 : 243 : ARCHIVE_OPTS(.tag = tag,
4446 : : .namespace = polinfo->dobj.namespace->dobj.name,
4447 : : .owner = tbinfo->rolname,
4448 : : .description = "POLICY",
4449 : : .section = SECTION_POST_DATA,
4450 : : .createStmt = query->data,
4451 : : .dropStmt = delqry->data));
4452 : :
4453 [ + + ]: 243 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4454 : 33 : dumpComment(fout, polprefix->data, qtabname,
4455 : 33 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4456 : 33 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4457 : :
4458 : 243 : pfree(tag);
4459 : 243 : destroyPQExpBuffer(query);
4460 : 243 : destroyPQExpBuffer(delqry);
4461 : 243 : destroyPQExpBuffer(polprefix);
4462 : 243 : pg_free(qtabname);
4463 : : }
4464 : :
4465 : : /*
4466 : : * getPublications
4467 : : * get information about publications
4468 : : */
4469 : : void
4470 : 191 : getPublications(Archive *fout)
4471 : : {
4472 : 191 : DumpOptions *dopt = fout->dopt;
4473 : : PQExpBuffer query;
4474 : : PGresult *res;
4475 : : PublicationInfo *pubinfo;
4476 : : int i_tableoid;
4477 : : int i_oid;
4478 : : int i_pubname;
4479 : : int i_pubowner;
4480 : : int i_puballtables;
4481 : : int i_puballsequences;
4482 : : int i_pubinsert;
4483 : : int i_pubupdate;
4484 : : int i_pubdelete;
4485 : : int i_pubtruncate;
4486 : : int i_pubviaroot;
4487 : : int i_pubgencols;
4488 : : int i,
4489 : : ntups;
4490 : :
4491 [ - + ]: 191 : if (dopt->no_publications)
4492 : 0 : return;
4493 : :
4494 : 191 : query = createPQExpBuffer();
4495 : :
4496 : : /* Get the publications. */
4497 : 191 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4498 : : "p.pubowner, p.puballtables, p.pubinsert, "
4499 : : "p.pubupdate, p.pubdelete, ");
4500 : :
4501 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
4502 : 191 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4503 : : else
4504 : 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4505 : :
4506 [ + - ]: 191 : if (fout->remoteVersion >= 130000)
4507 : 191 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4508 : : else
4509 : 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4510 : :
4511 [ + - ]: 191 : if (fout->remoteVersion >= 180000)
4512 : 191 : appendPQExpBufferStr(query, "p.pubgencols, ");
4513 : : else
4514 : 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4515 : :
4516 [ + - ]: 191 : if (fout->remoteVersion >= 190000)
4517 : 191 : appendPQExpBufferStr(query, "p.puballsequences ");
4518 : : else
4519 : 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4520 : :
4521 : 191 : appendPQExpBufferStr(query, "FROM pg_publication p");
4522 : :
4523 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4524 : :
4525 : 191 : ntups = PQntuples(res);
4526 : :
4527 [ + + ]: 191 : if (ntups == 0)
4528 : 135 : goto cleanup;
4529 : :
4530 : 56 : i_tableoid = PQfnumber(res, "tableoid");
4531 : 56 : i_oid = PQfnumber(res, "oid");
4532 : 56 : i_pubname = PQfnumber(res, "pubname");
4533 : 56 : i_pubowner = PQfnumber(res, "pubowner");
4534 : 56 : i_puballtables = PQfnumber(res, "puballtables");
4535 : 56 : i_puballsequences = PQfnumber(res, "puballsequences");
4536 : 56 : i_pubinsert = PQfnumber(res, "pubinsert");
4537 : 56 : i_pubupdate = PQfnumber(res, "pubupdate");
4538 : 56 : i_pubdelete = PQfnumber(res, "pubdelete");
4539 : 56 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4540 : 56 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4541 : 56 : i_pubgencols = PQfnumber(res, "pubgencols");
4542 : :
4543 : 56 : pubinfo = pg_malloc_array(PublicationInfo, ntups);
4544 : :
4545 [ + + ]: 572 : for (i = 0; i < ntups; i++)
4546 : : {
4547 : 516 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4548 : 516 : pubinfo[i].dobj.catId.tableoid =
4549 : 516 : atooid(PQgetvalue(res, i, i_tableoid));
4550 : 516 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4551 : 516 : AssignDumpId(&pubinfo[i].dobj);
4552 : 516 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4553 : 516 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4554 : 516 : pubinfo[i].puballtables =
4555 : 516 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4556 : 516 : pubinfo[i].puballsequences =
4557 : 516 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4558 : 516 : pubinfo[i].pubinsert =
4559 : 516 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4560 : 516 : pubinfo[i].pubupdate =
4561 : 516 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4562 : 516 : pubinfo[i].pubdelete =
4563 : 516 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4564 : 516 : pubinfo[i].pubtruncate =
4565 : 516 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4566 : 516 : pubinfo[i].pubviaroot =
4567 : 516 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4568 : 516 : pubinfo[i].pubgencols_type =
4569 : 516 : *(PQgetvalue(res, i, i_pubgencols));
4570 : 516 : pubinfo[i].except_tables = (SimplePtrList)
4571 : : {
4572 : : NULL, NULL
4573 : : };
4574 : :
4575 : : /* Decide whether we want to dump it */
4576 : 516 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4577 : :
4578 : : /*
4579 : : * Get the list of tables for publications specified in the EXCEPT
4580 : : * TABLE clause.
4581 : : *
4582 : : * Although individual table entries in EXCEPT list could be stored in
4583 : : * PublicationRelInfo, dumpPublicationTable cannot be used to emit
4584 : : * them, because there is no ALTER PUBLICATION ... ADD command to add
4585 : : * individual table entries to the EXCEPT list.
4586 : : *
4587 : : * Therefore, the approach is to dump the complete EXCEPT list in a
4588 : : * single CREATE PUBLICATION statement. PublicationInfo is used to
4589 : : * collect this information, which is then emitted by
4590 : : * dumpPublication().
4591 : : */
4592 [ + - ]: 516 : if (fout->remoteVersion >= 190000)
4593 : : {
4594 : : int ntbls;
4595 : : PGresult *res_tbls;
4596 : :
4597 : 516 : resetPQExpBuffer(query);
4598 : 516 : appendPQExpBuffer(query,
4599 : : "SELECT prrelid\n"
4600 : : "FROM pg_catalog.pg_publication_rel\n"
4601 : : "WHERE prpubid = %u AND prexcept",
4602 : 516 : pubinfo[i].dobj.catId.oid);
4603 : :
4604 : 516 : res_tbls = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4605 : :
4606 : 516 : ntbls = PQntuples(res_tbls);
4607 : :
4608 [ + + ]: 756 : for (int j = 0; j < ntbls; j++)
4609 : : {
4610 : : Oid prrelid;
4611 : : TableInfo *tbinfo;
4612 : :
4613 : 240 : prrelid = atooid(PQgetvalue(res_tbls, j, 0));
4614 : :
4615 : 240 : tbinfo = findTableByOid(prrelid);
4616 : :
4617 [ + - ]: 240 : if (tbinfo != NULL)
4618 : 240 : simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
4619 : : }
4620 : :
4621 : 516 : PQclear(res_tbls);
4622 : : }
4623 : : }
4624 : :
4625 : 56 : cleanup:
4626 : 191 : PQclear(res);
4627 : :
4628 : 191 : destroyPQExpBuffer(query);
4629 : : }
4630 : :
4631 : : /*
4632 : : * dumpPublication
4633 : : * dump the definition of the given publication
4634 : : */
4635 : : static void
4636 : 416 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4637 : : {
4638 : 416 : DumpOptions *dopt = fout->dopt;
4639 : : PQExpBuffer delq;
4640 : : PQExpBuffer query;
4641 : : char *qpubname;
4642 : 416 : bool first = true;
4643 : :
4644 : : /* Do nothing if not dumping schema */
4645 [ + + ]: 416 : if (!dopt->dumpSchema)
4646 : 60 : return;
4647 : :
4648 : 356 : delq = createPQExpBuffer();
4649 : 356 : query = createPQExpBuffer();
4650 : :
4651 : 356 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4652 : :
4653 : 356 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4654 : : qpubname);
4655 : :
4656 : 356 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4657 : : qpubname);
4658 : :
4659 [ + + ]: 356 : if (pubinfo->puballtables)
4660 : : {
4661 : 166 : int n_except = 0;
4662 : :
4663 : 166 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4664 : :
4665 : : /* Include EXCEPT (TABLE) clause if there are except_tables. */
4666 [ + + ]: 331 : for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
4667 : : {
4668 : 165 : TableInfo *tbinfo = (TableInfo *) cell->ptr;
4669 : :
4670 [ + + ]: 165 : if (++n_except == 1)
4671 : 99 : appendPQExpBufferStr(query, " EXCEPT (");
4672 : : else
4673 : 66 : appendPQExpBufferStr(query, ", ");
4674 : 165 : appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
4675 : : }
4676 [ + + ]: 166 : if (n_except > 0)
4677 : 99 : appendPQExpBufferChar(query, ')');
4678 : :
4679 [ + + ]: 166 : if (pubinfo->puballsequences)
4680 : 33 : appendPQExpBufferStr(query, ", ALL SEQUENCES");
4681 : : }
4682 [ + + ]: 190 : else if (pubinfo->puballsequences)
4683 : 33 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4684 : :
4685 : 356 : appendPQExpBufferStr(query, " WITH (publish = '");
4686 [ + + ]: 356 : if (pubinfo->pubinsert)
4687 : : {
4688 : 290 : appendPQExpBufferStr(query, "insert");
4689 : 290 : first = false;
4690 : : }
4691 : :
4692 [ + + ]: 356 : if (pubinfo->pubupdate)
4693 : : {
4694 [ + - ]: 290 : if (!first)
4695 : 290 : appendPQExpBufferStr(query, ", ");
4696 : :
4697 : 290 : appendPQExpBufferStr(query, "update");
4698 : 290 : first = false;
4699 : : }
4700 : :
4701 [ + + ]: 356 : if (pubinfo->pubdelete)
4702 : : {
4703 [ + - ]: 290 : if (!first)
4704 : 290 : appendPQExpBufferStr(query, ", ");
4705 : :
4706 : 290 : appendPQExpBufferStr(query, "delete");
4707 : 290 : first = false;
4708 : : }
4709 : :
4710 [ + + ]: 356 : if (pubinfo->pubtruncate)
4711 : : {
4712 [ + - ]: 290 : if (!first)
4713 : 290 : appendPQExpBufferStr(query, ", ");
4714 : :
4715 : 290 : appendPQExpBufferStr(query, "truncate");
4716 : 290 : first = false;
4717 : : }
4718 : :
4719 : 356 : appendPQExpBufferChar(query, '\'');
4720 : :
4721 [ + + ]: 356 : if (pubinfo->pubviaroot)
4722 : 5 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4723 : :
4724 [ + + ]: 356 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4725 : 33 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4726 : :
4727 : 356 : appendPQExpBufferStr(query, ");\n");
4728 : :
4729 [ + - ]: 356 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4730 : 356 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4731 : 356 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4732 : : .owner = pubinfo->rolname,
4733 : : .description = "PUBLICATION",
4734 : : .section = SECTION_POST_DATA,
4735 : : .createStmt = query->data,
4736 : : .dropStmt = delq->data));
4737 : :
4738 [ + + ]: 356 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4739 : 33 : dumpComment(fout, "PUBLICATION", qpubname,
4740 : 33 : NULL, pubinfo->rolname,
4741 : 33 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4742 : :
4743 [ - + ]: 356 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4744 : 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4745 : 0 : NULL, pubinfo->rolname,
4746 : 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4747 : :
4748 : 356 : destroyPQExpBuffer(delq);
4749 : 356 : destroyPQExpBuffer(query);
4750 : 356 : pg_free(qpubname);
4751 : : }
4752 : :
4753 : : /*
4754 : : * getPublicationNamespaces
4755 : : * get information about publication membership for dumpable schemas.
4756 : : */
4757 : : void
4758 : 191 : getPublicationNamespaces(Archive *fout)
4759 : : {
4760 : : PQExpBuffer query;
4761 : : PGresult *res;
4762 : : PublicationSchemaInfo *pubsinfo;
4763 : 191 : DumpOptions *dopt = fout->dopt;
4764 : : int i_tableoid;
4765 : : int i_oid;
4766 : : int i_pnpubid;
4767 : : int i_pnnspid;
4768 : : int i,
4769 : : j,
4770 : : ntups;
4771 : :
4772 [ + - - + ]: 191 : if (dopt->no_publications || fout->remoteVersion < 150000)
4773 : 0 : return;
4774 : :
4775 : 191 : query = createPQExpBuffer();
4776 : :
4777 : : /* Collect all publication membership info. */
4778 : 191 : appendPQExpBufferStr(query,
4779 : : "SELECT tableoid, oid, pnpubid, pnnspid "
4780 : : "FROM pg_catalog.pg_publication_namespace");
4781 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4782 : :
4783 : 191 : ntups = PQntuples(res);
4784 : :
4785 : 191 : i_tableoid = PQfnumber(res, "tableoid");
4786 : 191 : i_oid = PQfnumber(res, "oid");
4787 : 191 : i_pnpubid = PQfnumber(res, "pnpubid");
4788 : 191 : i_pnnspid = PQfnumber(res, "pnnspid");
4789 : :
4790 : : /* this allocation may be more than we need */
4791 : 191 : pubsinfo = pg_malloc_array(PublicationSchemaInfo, ntups);
4792 : 191 : j = 0;
4793 : :
4794 [ + + ]: 322 : for (i = 0; i < ntups; i++)
4795 : : {
4796 : 131 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4797 : 131 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4798 : : PublicationInfo *pubinfo;
4799 : : NamespaceInfo *nspinfo;
4800 : :
4801 : : /*
4802 : : * Ignore any entries for which we aren't interested in either the
4803 : : * publication or the rel.
4804 : : */
4805 : 131 : pubinfo = findPublicationByOid(pnpubid);
4806 [ - + ]: 131 : if (pubinfo == NULL)
4807 : 0 : continue;
4808 : 131 : nspinfo = findNamespaceByOid(pnnspid);
4809 [ - + ]: 131 : if (nspinfo == NULL)
4810 : 0 : continue;
4811 : :
4812 : : /* OK, make a DumpableObject for this relationship */
4813 : 131 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4814 : 131 : pubsinfo[j].dobj.catId.tableoid =
4815 : 131 : atooid(PQgetvalue(res, i, i_tableoid));
4816 : 131 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4817 : 131 : AssignDumpId(&pubsinfo[j].dobj);
4818 : 131 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4819 : 131 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4820 : 131 : pubsinfo[j].publication = pubinfo;
4821 : 131 : pubsinfo[j].pubschema = nspinfo;
4822 : :
4823 : : /* Decide whether we want to dump it */
4824 : 131 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4825 : :
4826 : 131 : j++;
4827 : : }
4828 : :
4829 : 191 : PQclear(res);
4830 : 191 : destroyPQExpBuffer(query);
4831 : : }
4832 : :
4833 : : /*
4834 : : * getPublicationTables
4835 : : * get information about publication membership for dumpable tables.
4836 : : */
4837 : : void
4838 : 191 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4839 : : {
4840 : : PQExpBuffer query;
4841 : : PGresult *res;
4842 : : PublicationRelInfo *pubrinfo;
4843 : 191 : DumpOptions *dopt = fout->dopt;
4844 : : int i_tableoid;
4845 : : int i_oid;
4846 : : int i_prpubid;
4847 : : int i_prrelid;
4848 : : int i_prrelqual;
4849 : : int i_prattrs;
4850 : : int i,
4851 : : j,
4852 : : ntups;
4853 : :
4854 [ - + ]: 191 : if (dopt->no_publications)
4855 : 0 : return;
4856 : :
4857 : 191 : query = createPQExpBuffer();
4858 : :
4859 : : /* Collect all publication membership info. */
4860 [ + - ]: 191 : if (fout->remoteVersion >= 150000)
4861 : : {
4862 : 191 : appendPQExpBufferStr(query,
4863 : : "SELECT tableoid, oid, prpubid, prrelid, "
4864 : : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4865 : : "(CASE\n"
4866 : : " WHEN pr.prattrs IS NOT NULL THEN\n"
4867 : : " (SELECT array_agg(attname)\n"
4868 : : " FROM\n"
4869 : : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4870 : : " pg_catalog.pg_attribute\n"
4871 : : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4872 : : " ELSE NULL END) prattrs "
4873 : : "FROM pg_catalog.pg_publication_rel pr");
4874 [ + - ]: 191 : if (fout->remoteVersion >= 190000)
4875 : 191 : appendPQExpBufferStr(query, " WHERE NOT pr.prexcept");
4876 : : }
4877 : : else
4878 : 0 : appendPQExpBufferStr(query,
4879 : : "SELECT tableoid, oid, prpubid, prrelid, "
4880 : : "NULL AS prrelqual, NULL AS prattrs "
4881 : : "FROM pg_catalog.pg_publication_rel");
4882 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4883 : :
4884 : 191 : ntups = PQntuples(res);
4885 : :
4886 : 191 : i_tableoid = PQfnumber(res, "tableoid");
4887 : 191 : i_oid = PQfnumber(res, "oid");
4888 : 191 : i_prpubid = PQfnumber(res, "prpubid");
4889 : 191 : i_prrelid = PQfnumber(res, "prrelid");
4890 : 191 : i_prrelqual = PQfnumber(res, "prrelqual");
4891 : 191 : i_prattrs = PQfnumber(res, "prattrs");
4892 : :
4893 : : /* this allocation may be more than we need */
4894 : 191 : pubrinfo = pg_malloc_array(PublicationRelInfo, ntups);
4895 : 191 : j = 0;
4896 : :
4897 [ + + ]: 562 : for (i = 0; i < ntups; i++)
4898 : : {
4899 : 371 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4900 : 371 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4901 : : PublicationInfo *pubinfo;
4902 : : TableInfo *tbinfo;
4903 : :
4904 : : /*
4905 : : * Ignore any entries for which we aren't interested in either the
4906 : : * publication or the rel.
4907 : : */
4908 : 371 : pubinfo = findPublicationByOid(prpubid);
4909 [ - + ]: 371 : if (pubinfo == NULL)
4910 : 0 : continue;
4911 : 371 : tbinfo = findTableByOid(prrelid);
4912 [ - + ]: 371 : if (tbinfo == NULL)
4913 : 0 : continue;
4914 : :
4915 : : /* OK, make a DumpableObject for this relationship */
4916 : 371 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4917 : 371 : pubrinfo[j].dobj.catId.tableoid =
4918 : 371 : atooid(PQgetvalue(res, i, i_tableoid));
4919 : 371 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4920 : 371 : AssignDumpId(&pubrinfo[j].dobj);
4921 : 371 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4922 : 371 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4923 : 371 : pubrinfo[j].publication = pubinfo;
4924 : 371 : pubrinfo[j].pubtable = tbinfo;
4925 [ + + ]: 371 : if (PQgetisnull(res, i, i_prrelqual))
4926 : 206 : pubrinfo[j].pubrelqual = NULL;
4927 : : else
4928 : 165 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4929 : :
4930 [ + + ]: 371 : if (!PQgetisnull(res, i, i_prattrs))
4931 : : {
4932 : : char **attnames;
4933 : : int nattnames;
4934 : : PQExpBuffer attribs;
4935 : :
4936 [ - + ]: 117 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4937 : : &attnames, &nattnames))
4938 : 0 : pg_fatal("could not parse %s array", "prattrs");
4939 : 117 : attribs = createPQExpBuffer();
4940 [ + + ]: 337 : for (int k = 0; k < nattnames; k++)
4941 : : {
4942 [ + + ]: 220 : if (k > 0)
4943 : 103 : appendPQExpBufferStr(attribs, ", ");
4944 : :
4945 : 220 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4946 : : }
4947 : 117 : pubrinfo[j].pubrattrs = attribs->data;
4948 : 117 : free(attribs); /* but not attribs->data */
4949 : 117 : free(attnames);
4950 : : }
4951 : : else
4952 : 254 : pubrinfo[j].pubrattrs = NULL;
4953 : :
4954 : : /* Decide whether we want to dump it */
4955 : 371 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4956 : :
4957 : 371 : j++;
4958 : : }
4959 : :
4960 : 191 : PQclear(res);
4961 : 191 : destroyPQExpBuffer(query);
4962 : : }
4963 : :
4964 : : /*
4965 : : * dumpPublicationNamespace
4966 : : * dump the definition of the given publication schema mapping.
4967 : : */
4968 : : static void
4969 : 103 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4970 : : {
4971 : 103 : DumpOptions *dopt = fout->dopt;
4972 : 103 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4973 : 103 : PublicationInfo *pubinfo = pubsinfo->publication;
4974 : : PQExpBuffer query;
4975 : : char *tag;
4976 : :
4977 : : /* Do nothing if not dumping schema */
4978 [ + + ]: 103 : if (!dopt->dumpSchema)
4979 : 12 : return;
4980 : :
4981 : 91 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4982 : :
4983 : 91 : query = createPQExpBuffer();
4984 : :
4985 : 91 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4986 : 91 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4987 : :
4988 : : /*
4989 : : * There is no point in creating drop query as the drop is done by schema
4990 : : * drop.
4991 : : */
4992 [ + - ]: 91 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4993 : 91 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4994 : 91 : ARCHIVE_OPTS(.tag = tag,
4995 : : .namespace = schemainfo->dobj.name,
4996 : : .owner = pubinfo->rolname,
4997 : : .description = "PUBLICATION TABLES IN SCHEMA",
4998 : : .section = SECTION_POST_DATA,
4999 : : .createStmt = query->data));
5000 : :
5001 : : /* These objects can't currently have comments or seclabels */
5002 : :
5003 : 91 : pfree(tag);
5004 : 91 : destroyPQExpBuffer(query);
5005 : : }
5006 : :
5007 : : /*
5008 : : * dumpPublicationTable
5009 : : * dump the definition of the given publication table mapping
5010 : : */
5011 : : static void
5012 : 298 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
5013 : : {
5014 : 298 : DumpOptions *dopt = fout->dopt;
5015 : 298 : PublicationInfo *pubinfo = pubrinfo->publication;
5016 : 298 : TableInfo *tbinfo = pubrinfo->pubtable;
5017 : : PQExpBuffer query;
5018 : : char *tag;
5019 : :
5020 : : /* Do nothing if not dumping schema */
5021 [ + + ]: 298 : if (!dopt->dumpSchema)
5022 : 42 : return;
5023 : :
5024 : 256 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5025 : :
5026 : 256 : query = createPQExpBuffer();
5027 : :
5028 : 256 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5029 : 256 : fmtId(pubinfo->dobj.name));
5030 : 256 : appendPQExpBuffer(query, " %s",
5031 : 256 : fmtQualifiedDumpable(tbinfo));
5032 : :
5033 [ + + ]: 256 : if (pubrinfo->pubrattrs)
5034 : 81 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5035 : :
5036 [ + + ]: 256 : if (pubrinfo->pubrelqual)
5037 : : {
5038 : : /*
5039 : : * It's necessary to add parentheses around the expression because
5040 : : * pg_get_expr won't supply the parentheses for things like WHERE
5041 : : * TRUE.
5042 : : */
5043 : 114 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5044 : : }
5045 : 256 : appendPQExpBufferStr(query, ";\n");
5046 : :
5047 : : /*
5048 : : * There is no point in creating a drop query as the drop is done by table
5049 : : * drop. (If you think to change this, see also _printTocEntry().)
5050 : : * Although this object doesn't really have ownership as such, set the
5051 : : * owner field anyway to ensure that the command is run by the correct
5052 : : * role at restore time.
5053 : : */
5054 [ + - ]: 256 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5055 : 256 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5056 : 256 : ARCHIVE_OPTS(.tag = tag,
5057 : : .namespace = tbinfo->dobj.namespace->dobj.name,
5058 : : .owner = pubinfo->rolname,
5059 : : .description = "PUBLICATION TABLE",
5060 : : .section = SECTION_POST_DATA,
5061 : : .createStmt = query->data));
5062 : :
5063 : : /* These objects can't currently have comments or seclabels */
5064 : :
5065 : 256 : pfree(tag);
5066 : 256 : destroyPQExpBuffer(query);
5067 : : }
5068 : :
5069 : : /*
5070 : : * Is the currently connected user a superuser?
5071 : : */
5072 : : static bool
5073 : 190 : is_superuser(Archive *fout)
5074 : : {
5075 : 190 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5076 : : const char *val;
5077 : :
5078 : 190 : val = PQparameterStatus(AH->connection, "is_superuser");
5079 : :
5080 [ + - + + ]: 190 : if (val && strcmp(val, "on") == 0)
5081 : 187 : return true;
5082 : :
5083 : 3 : return false;
5084 : : }
5085 : :
5086 : : /*
5087 : : * Set the given value to restrict_nonsystem_relation_kind value. Since
5088 : : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5089 : : * the setting query is effective only where available.
5090 : : */
5091 : : static void
5092 : 225 : set_restrict_relation_kind(Archive *AH, const char *value)
5093 : : {
5094 : 225 : PQExpBuffer query = createPQExpBuffer();
5095 : : PGresult *res;
5096 : :
5097 : 225 : appendPQExpBuffer(query,
5098 : : "SELECT set_config(name, '%s', false) "
5099 : : "FROM pg_settings "
5100 : : "WHERE name = 'restrict_nonsystem_relation_kind'",
5101 : : value);
5102 : 225 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5103 : :
5104 : 225 : PQclear(res);
5105 : 225 : destroyPQExpBuffer(query);
5106 : 225 : }
5107 : :
5108 : : /*
5109 : : * getSubscriptions
5110 : : * get information about subscriptions
5111 : : */
5112 : : void
5113 : 191 : getSubscriptions(Archive *fout)
5114 : : {
5115 : 191 : DumpOptions *dopt = fout->dopt;
5116 : : PQExpBuffer query;
5117 : : PGresult *res;
5118 : : SubscriptionInfo *subinfo;
5119 : : int i_tableoid;
5120 : : int i_oid;
5121 : : int i_subname;
5122 : : int i_subowner;
5123 : : int i_subbinary;
5124 : : int i_substream;
5125 : : int i_subtwophasestate;
5126 : : int i_subdisableonerr;
5127 : : int i_subpasswordrequired;
5128 : : int i_subrunasowner;
5129 : : int i_subservername;
5130 : : int i_subconninfo;
5131 : : int i_subslotname;
5132 : : int i_subsynccommit;
5133 : : int i_subwalrcvtimeout;
5134 : : int i_subpublications;
5135 : : int i_suborigin;
5136 : : int i_suboriginremotelsn;
5137 : : int i_subenabled;
5138 : : int i_subfailover;
5139 : : int i_subretaindeadtuples;
5140 : : int i_submaxretention;
5141 : : int i,
5142 : : ntups;
5143 : :
5144 [ + + ]: 191 : if (dopt->no_subscriptions)
5145 : 1 : return;
5146 : :
5147 [ + + ]: 190 : if (!is_superuser(fout))
5148 : : {
5149 : : int n;
5150 : :
5151 : 3 : res = ExecuteSqlQuery(fout,
5152 : : "SELECT count(*) FROM pg_subscription "
5153 : : "WHERE subdbid = (SELECT oid FROM pg_database"
5154 : : " WHERE datname = current_database())",
5155 : : PGRES_TUPLES_OK);
5156 : 3 : n = atoi(PQgetvalue(res, 0, 0));
5157 [ + + ]: 3 : if (n > 0)
5158 : 2 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5159 : 3 : PQclear(res);
5160 : 3 : return;
5161 : : }
5162 : :
5163 : 187 : query = createPQExpBuffer();
5164 : :
5165 : : /* Get the subscriptions in current database. */
5166 : 187 : appendPQExpBufferStr(query,
5167 : : "SELECT s.tableoid, s.oid, s.subname,\n"
5168 : : " s.subowner,\n"
5169 : : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5170 : : " s.subpublications,\n");
5171 : :
5172 [ + - ]: 187 : if (fout->remoteVersion >= 140000)
5173 : 187 : appendPQExpBufferStr(query, " s.subbinary,\n");
5174 : : else
5175 : 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5176 : :
5177 [ + - ]: 187 : if (fout->remoteVersion >= 140000)
5178 : 187 : appendPQExpBufferStr(query, " s.substream,\n");
5179 : : else
5180 : 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5181 : :
5182 [ + - ]: 187 : if (fout->remoteVersion >= 150000)
5183 : 187 : appendPQExpBufferStr(query,
5184 : : " s.subtwophasestate,\n"
5185 : : " s.subdisableonerr,\n");
5186 : : else
5187 : 0 : appendPQExpBuffer(query,
5188 : : " '%c' AS subtwophasestate,\n"
5189 : : " false AS subdisableonerr,\n",
5190 : : LOGICALREP_TWOPHASE_STATE_DISABLED);
5191 : :
5192 [ + - ]: 187 : if (fout->remoteVersion >= 160000)
5193 : 187 : appendPQExpBufferStr(query,
5194 : : " s.subpasswordrequired,\n"
5195 : : " s.subrunasowner,\n"
5196 : : " s.suborigin,\n");
5197 : : else
5198 : 0 : appendPQExpBuffer(query,
5199 : : " 't' AS subpasswordrequired,\n"
5200 : : " 't' AS subrunasowner,\n"
5201 : : " '%s' AS suborigin,\n",
5202 : : LOGICALREP_ORIGIN_ANY);
5203 : :
5204 [ + + + - ]: 187 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5205 : 40 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5206 : : " s.subenabled,\n");
5207 : : else
5208 : 147 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5209 : : " false AS subenabled,\n");
5210 : :
5211 [ + - ]: 187 : if (fout->remoteVersion >= 170000)
5212 : 187 : appendPQExpBufferStr(query,
5213 : : " s.subfailover,\n");
5214 : : else
5215 : 0 : appendPQExpBufferStr(query,
5216 : : " false AS subfailover,\n");
5217 : :
5218 [ + - ]: 187 : if (fout->remoteVersion >= 190000)
5219 : 187 : appendPQExpBufferStr(query,
5220 : : " s.subretaindeadtuples,\n");
5221 : : else
5222 : 0 : appendPQExpBufferStr(query,
5223 : : " false AS subretaindeadtuples,\n");
5224 : :
5225 [ + - ]: 187 : if (fout->remoteVersion >= 190000)
5226 : 187 : appendPQExpBufferStr(query,
5227 : : " s.submaxretention,\n");
5228 : : else
5229 : 0 : appendPQExpBufferStr(query, " 0 AS submaxretention,\n");
5230 : :
5231 [ + - ]: 187 : if (fout->remoteVersion >= 190000)
5232 : 187 : appendPQExpBufferStr(query,
5233 : : " s.subwalrcvtimeout,\n");
5234 : : else
5235 : 0 : appendPQExpBufferStr(query,
5236 : : " '-1' AS subwalrcvtimeout,\n");
5237 : :
5238 [ + - ]: 187 : if (fout->remoteVersion >= 190000)
5239 : 187 : appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5240 : : else
5241 : 0 : appendPQExpBufferStr(query, " NULL AS subservername\n");
5242 : :
5243 : 187 : appendPQExpBufferStr(query,
5244 : : "FROM pg_subscription s\n");
5245 : :
5246 [ + - ]: 187 : if (fout->remoteVersion >= 190000)
5247 : 187 : appendPQExpBufferStr(query,
5248 : : "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5249 : : " ON fs.oid = s.subserver \n");
5250 : :
5251 [ + + + - ]: 187 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5252 : 40 : appendPQExpBufferStr(query,
5253 : : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5254 : : " ON o.external_id = 'pg_' || s.oid::text \n");
5255 : :
5256 : 187 : appendPQExpBufferStr(query,
5257 : : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5258 : : " WHERE datname = current_database())");
5259 : :
5260 : 187 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5261 : :
5262 : 187 : ntups = PQntuples(res);
5263 : :
5264 : : /*
5265 : : * Get subscription fields. We don't include subskiplsn in the dump as
5266 : : * after restoring the dump this value may no longer be relevant.
5267 : : */
5268 : 187 : i_tableoid = PQfnumber(res, "tableoid");
5269 : 187 : i_oid = PQfnumber(res, "oid");
5270 : 187 : i_subname = PQfnumber(res, "subname");
5271 : 187 : i_subowner = PQfnumber(res, "subowner");
5272 : 187 : i_subenabled = PQfnumber(res, "subenabled");
5273 : 187 : i_subbinary = PQfnumber(res, "subbinary");
5274 : 187 : i_substream = PQfnumber(res, "substream");
5275 : 187 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5276 : 187 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5277 : 187 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5278 : 187 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5279 : 187 : i_subfailover = PQfnumber(res, "subfailover");
5280 : 187 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5281 : 187 : i_submaxretention = PQfnumber(res, "submaxretention");
5282 : 187 : i_subservername = PQfnumber(res, "subservername");
5283 : 187 : i_subconninfo = PQfnumber(res, "subconninfo");
5284 : 187 : i_subslotname = PQfnumber(res, "subslotname");
5285 : 187 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5286 : 187 : i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5287 : 187 : i_subpublications = PQfnumber(res, "subpublications");
5288 : 187 : i_suborigin = PQfnumber(res, "suborigin");
5289 : 187 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5290 : :
5291 : 187 : subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5292 : :
5293 [ + + ]: 324 : for (i = 0; i < ntups; i++)
5294 : : {
5295 : 137 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5296 : 137 : subinfo[i].dobj.catId.tableoid =
5297 : 137 : atooid(PQgetvalue(res, i, i_tableoid));
5298 : 137 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5299 : 137 : AssignDumpId(&subinfo[i].dobj);
5300 : 137 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5301 : 137 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5302 : :
5303 : 137 : subinfo[i].subenabled =
5304 : 137 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5305 [ + - ]: 137 : if (PQgetisnull(res, i, i_subservername))
5306 : 137 : subinfo[i].subservername = NULL;
5307 : : else
5308 : 0 : subinfo[i].subservername = pg_strdup(PQgetvalue(res, i, i_subservername));
5309 : 137 : subinfo[i].subbinary =
5310 : 137 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5311 : 137 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5312 : 137 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5313 : 137 : subinfo[i].subdisableonerr =
5314 : 137 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5315 : 137 : subinfo[i].subpasswordrequired =
5316 : 137 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5317 : 137 : subinfo[i].subrunasowner =
5318 : 137 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5319 : 137 : subinfo[i].subfailover =
5320 : 137 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5321 : 137 : subinfo[i].subretaindeadtuples =
5322 : 137 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5323 : 137 : subinfo[i].submaxretention =
5324 : 137 : atoi(PQgetvalue(res, i, i_submaxretention));
5325 [ - + ]: 137 : if (PQgetisnull(res, i, i_subconninfo))
5326 : 0 : subinfo[i].subconninfo = NULL;
5327 : : else
5328 : 137 : subinfo[i].subconninfo =
5329 : 137 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5330 [ - + ]: 137 : if (PQgetisnull(res, i, i_subslotname))
5331 : 0 : subinfo[i].subslotname = NULL;
5332 : : else
5333 : 137 : subinfo[i].subslotname =
5334 : 137 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5335 : 274 : subinfo[i].subsynccommit =
5336 : 137 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5337 : 274 : subinfo[i].subwalrcvtimeout =
5338 : 137 : pg_strdup(PQgetvalue(res, i, i_subwalrcvtimeout));
5339 : 274 : subinfo[i].subpublications =
5340 : 137 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5341 : 137 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5342 [ + + ]: 137 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5343 : 136 : subinfo[i].suboriginremotelsn = NULL;
5344 : : else
5345 : 1 : subinfo[i].suboriginremotelsn =
5346 : 1 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5347 : :
5348 : : /* Decide whether we want to dump it */
5349 : 137 : selectDumpableObject(&(subinfo[i].dobj), fout);
5350 : : }
5351 : 187 : PQclear(res);
5352 : :
5353 : 187 : destroyPQExpBuffer(query);
5354 : : }
5355 : :
5356 : : /*
5357 : : * getSubscriptionRelations
5358 : : * Get information about subscription membership for dumpable relations. This
5359 : : * will be used only in binary-upgrade mode for PG17 or later versions.
5360 : : */
5361 : : void
5362 : 191 : getSubscriptionRelations(Archive *fout)
5363 : : {
5364 : 191 : DumpOptions *dopt = fout->dopt;
5365 : 191 : SubscriptionInfo *subinfo = NULL;
5366 : : SubRelInfo *subrinfo;
5367 : : PGresult *res;
5368 : : int i_srsubid;
5369 : : int i_srrelid;
5370 : : int i_srsubstate;
5371 : : int i_srsublsn;
5372 : : int ntups;
5373 : 191 : Oid last_srsubid = InvalidOid;
5374 : :
5375 [ + + + + ]: 191 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5376 [ - + ]: 40 : fout->remoteVersion < 170000)
5377 : 151 : return;
5378 : :
5379 : 40 : res = ExecuteSqlQuery(fout,
5380 : : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5381 : : "FROM pg_catalog.pg_subscription_rel "
5382 : : "ORDER BY srsubid",
5383 : : PGRES_TUPLES_OK);
5384 : 40 : ntups = PQntuples(res);
5385 [ + + ]: 40 : if (ntups == 0)
5386 : 39 : goto cleanup;
5387 : :
5388 : : /* Get pg_subscription_rel attributes */
5389 : 1 : i_srsubid = PQfnumber(res, "srsubid");
5390 : 1 : i_srrelid = PQfnumber(res, "srrelid");
5391 : 1 : i_srsubstate = PQfnumber(res, "srsubstate");
5392 : 1 : i_srsublsn = PQfnumber(res, "srsublsn");
5393 : :
5394 : 1 : subrinfo = pg_malloc_array(SubRelInfo, ntups);
5395 [ + + ]: 4 : for (int i = 0; i < ntups; i++)
5396 : : {
5397 : 3 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5398 : 3 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5399 : : TableInfo *tblinfo;
5400 : :
5401 : : /*
5402 : : * If we switched to a new subscription, check if the subscription
5403 : : * exists.
5404 : : */
5405 [ + + ]: 3 : if (cur_srsubid != last_srsubid)
5406 : : {
5407 : 2 : subinfo = findSubscriptionByOid(cur_srsubid);
5408 [ - + ]: 2 : if (subinfo == NULL)
5409 : 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5410 : :
5411 : 2 : last_srsubid = cur_srsubid;
5412 : : }
5413 : :
5414 : 3 : tblinfo = findTableByOid(relid);
5415 [ - + ]: 3 : if (tblinfo == NULL)
5416 : 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5417 : : relid);
5418 : :
5419 : : /* OK, make a DumpableObject for this relationship */
5420 : 3 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5421 : 3 : subrinfo[i].dobj.catId.tableoid = relid;
5422 : 3 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5423 : 3 : AssignDumpId(&subrinfo[i].dobj);
5424 : 3 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5425 : 3 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5426 : 3 : subrinfo[i].subinfo = subinfo;
5427 : 3 : subrinfo[i].tblinfo = tblinfo;
5428 : 3 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5429 [ + + ]: 3 : if (PQgetisnull(res, i, i_srsublsn))
5430 : 1 : subrinfo[i].srsublsn = NULL;
5431 : : else
5432 : 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5433 : :
5434 : : /* Decide whether we want to dump it */
5435 : 3 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5436 : : }
5437 : :
5438 : 1 : cleanup:
5439 : 40 : PQclear(res);
5440 : : }
5441 : :
5442 : : /*
5443 : : * dumpSubscriptionTable
5444 : : * Dump the definition of the given subscription table mapping. This will be
5445 : : * used only in binary-upgrade mode for PG17 or later versions.
5446 : : */
5447 : : static void
5448 : 3 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5449 : : {
5450 : 3 : DumpOptions *dopt = fout->dopt;
5451 : 3 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5452 : : PQExpBuffer query;
5453 : : char *tag;
5454 : :
5455 : : /* Do nothing if not dumping schema */
5456 [ - + ]: 3 : if (!dopt->dumpSchema)
5457 : 0 : return;
5458 : :
5459 : : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5460 : :
5461 : 3 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5462 : :
5463 : 3 : query = createPQExpBuffer();
5464 : :
5465 [ + - ]: 3 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5466 : : {
5467 : : /*
5468 : : * binary_upgrade_add_sub_rel_state will add the subscription relation
5469 : : * to pg_subscription_rel table. This will be used only in
5470 : : * binary-upgrade mode.
5471 : : */
5472 : 3 : appendPQExpBufferStr(query,
5473 : : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5474 : 3 : appendPQExpBufferStr(query,
5475 : : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5476 : 3 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5477 : 3 : appendPQExpBuffer(query,
5478 : : ", %u, '%c'",
5479 : 3 : subrinfo->tblinfo->dobj.catId.oid,
5480 : 3 : subrinfo->srsubstate);
5481 : :
5482 [ + + + - ]: 3 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5483 : 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5484 : : else
5485 : 1 : appendPQExpBufferStr(query, ", NULL");
5486 : :
5487 : 3 : appendPQExpBufferStr(query, ");\n");
5488 : : }
5489 : :
5490 : : /*
5491 : : * There is no point in creating a drop query as the drop is done by table
5492 : : * drop. (If you think to change this, see also _printTocEntry().)
5493 : : * Although this object doesn't really have ownership as such, set the
5494 : : * owner field anyway to ensure that the command is run by the correct
5495 : : * role at restore time.
5496 : : */
5497 [ + - ]: 3 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5498 : 3 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5499 : 3 : ARCHIVE_OPTS(.tag = tag,
5500 : : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5501 : : .owner = subinfo->rolname,
5502 : : .description = "SUBSCRIPTION TABLE",
5503 : : .section = SECTION_POST_DATA,
5504 : : .createStmt = query->data));
5505 : :
5506 : : /* These objects can't currently have comments or seclabels */
5507 : :
5508 : 3 : pfree(tag);
5509 : 3 : destroyPQExpBuffer(query);
5510 : : }
5511 : :
5512 : : /*
5513 : : * dumpSubscription
5514 : : * dump the definition of the given subscription
5515 : : */
5516 : : static void
5517 : 116 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5518 : : {
5519 : 116 : DumpOptions *dopt = fout->dopt;
5520 : : PQExpBuffer delq;
5521 : : PQExpBuffer query;
5522 : : PQExpBuffer publications;
5523 : : char *qsubname;
5524 : 116 : char **pubnames = NULL;
5525 : 116 : int npubnames = 0;
5526 : : int i;
5527 : :
5528 : : /* Do nothing if not dumping schema */
5529 [ + + ]: 116 : if (!dopt->dumpSchema)
5530 : 18 : return;
5531 : :
5532 : 98 : delq = createPQExpBuffer();
5533 : 98 : query = createPQExpBuffer();
5534 : :
5535 : 98 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5536 : :
5537 : 98 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5538 : : qsubname);
5539 : :
5540 : 98 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5541 : : qsubname);
5542 [ - + ]: 98 : if (subinfo->subservername)
5543 : : {
5544 : 0 : appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5545 : : }
5546 : : else
5547 : : {
5548 : 98 : appendPQExpBufferStr(query, "CONNECTION ");
5549 : 98 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5550 : : }
5551 : :
5552 : : /* Build list of quoted publications and append them to query. */
5553 [ - + ]: 98 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5554 : 0 : pg_fatal("could not parse %s array", "subpublications");
5555 : :
5556 : 98 : publications = createPQExpBuffer();
5557 [ + + ]: 196 : for (i = 0; i < npubnames; i++)
5558 : : {
5559 [ - + ]: 98 : if (i > 0)
5560 : 0 : appendPQExpBufferStr(publications, ", ");
5561 : :
5562 : 98 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5563 : : }
5564 : :
5565 : 98 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5566 [ + - ]: 98 : if (subinfo->subslotname)
5567 : 98 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5568 : : else
5569 : 0 : appendPQExpBufferStr(query, "NONE");
5570 : :
5571 [ - + ]: 98 : if (subinfo->subbinary)
5572 : 0 : appendPQExpBufferStr(query, ", binary = true");
5573 : :
5574 [ + + ]: 98 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5575 : 32 : appendPQExpBufferStr(query, ", streaming = on");
5576 [ + + ]: 66 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5577 : 34 : appendPQExpBufferStr(query, ", streaming = parallel");
5578 : : else
5579 : 32 : appendPQExpBufferStr(query, ", streaming = off");
5580 : :
5581 [ - + ]: 98 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5582 : 0 : appendPQExpBufferStr(query, ", two_phase = on");
5583 : :
5584 [ - + ]: 98 : if (subinfo->subdisableonerr)
5585 : 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5586 : :
5587 [ - + ]: 98 : if (!subinfo->subpasswordrequired)
5588 : 0 : appendPQExpBufferStr(query, ", password_required = false");
5589 : :
5590 [ - + ]: 98 : if (subinfo->subrunasowner)
5591 : 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5592 : :
5593 [ + + ]: 98 : if (subinfo->subfailover)
5594 : 1 : appendPQExpBufferStr(query, ", failover = true");
5595 : :
5596 [ + + ]: 98 : if (subinfo->subretaindeadtuples)
5597 : 1 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5598 : :
5599 [ - + ]: 98 : if (subinfo->submaxretention)
5600 : 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5601 : :
5602 [ - + ]: 98 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5603 : 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5604 : :
5605 [ - + ]: 98 : if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5606 : 0 : appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5607 : :
5608 [ + + ]: 98 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5609 : 32 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5610 : :
5611 : 98 : appendPQExpBufferStr(query, ");\n");
5612 : :
5613 : : /*
5614 : : * In binary-upgrade mode, we allow the replication to continue after the
5615 : : * upgrade.
5616 : : */
5617 [ + + + - ]: 98 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5618 : : {
5619 [ + + ]: 5 : if (subinfo->suboriginremotelsn)
5620 : : {
5621 : : /*
5622 : : * Preserve the remote_lsn for the subscriber's replication
5623 : : * origin. This value is required to start the replication from
5624 : : * the position before the upgrade. This value will be stale if
5625 : : * the publisher gets upgraded before the subscriber node.
5626 : : * However, this shouldn't be a problem as the upgrade of the
5627 : : * publisher ensures that all the transactions were replicated
5628 : : * before upgrading it.
5629 : : */
5630 : 1 : appendPQExpBufferStr(query,
5631 : : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5632 : 1 : appendPQExpBufferStr(query,
5633 : : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5634 : 1 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5635 : 1 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5636 : : }
5637 : :
5638 [ + + ]: 5 : if (subinfo->subenabled)
5639 : : {
5640 : : /*
5641 : : * Enable the subscription to allow the replication to continue
5642 : : * after the upgrade.
5643 : : */
5644 : 1 : appendPQExpBufferStr(query,
5645 : : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5646 : 1 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5647 : : }
5648 : : }
5649 : :
5650 [ + - ]: 98 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5651 : 98 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5652 : 98 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5653 : : .owner = subinfo->rolname,
5654 : : .description = "SUBSCRIPTION",
5655 : : .section = SECTION_POST_DATA,
5656 : : .createStmt = query->data,
5657 : : .dropStmt = delq->data));
5658 : :
5659 [ + + ]: 98 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5660 : 32 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5661 : 32 : NULL, subinfo->rolname,
5662 : 32 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5663 : :
5664 [ - + ]: 98 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5665 : 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5666 : 0 : NULL, subinfo->rolname,
5667 : 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5668 : :
5669 : 98 : destroyPQExpBuffer(publications);
5670 : 98 : free(pubnames);
5671 : :
5672 : 98 : destroyPQExpBuffer(delq);
5673 : 98 : destroyPQExpBuffer(query);
5674 : 98 : pg_free(qsubname);
5675 : : }
5676 : :
5677 : : /*
5678 : : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5679 : : * the object needs.
5680 : : */
5681 : : static void
5682 : 5411 : append_depends_on_extension(Archive *fout,
5683 : : PQExpBuffer create,
5684 : : const DumpableObject *dobj,
5685 : : const char *catalog,
5686 : : const char *keyword,
5687 : : const char *objname)
5688 : : {
5689 [ + + ]: 5411 : if (dobj->depends_on_ext)
5690 : : {
5691 : : char *nm;
5692 : : PGresult *res;
5693 : : PQExpBuffer query;
5694 : : int ntups;
5695 : : int i_extname;
5696 : : int i;
5697 : :
5698 : : /* dodge fmtId() non-reentrancy */
5699 : 42 : nm = pg_strdup(objname);
5700 : :
5701 : 42 : query = createPQExpBuffer();
5702 : 42 : appendPQExpBuffer(query,
5703 : : "SELECT e.extname "
5704 : : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5705 : : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5706 : : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5707 : : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5708 : : catalog,
5709 : 42 : dobj->catId.oid);
5710 : 42 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5711 : 42 : ntups = PQntuples(res);
5712 : 42 : i_extname = PQfnumber(res, "extname");
5713 [ + + ]: 84 : for (i = 0; i < ntups; i++)
5714 : : {
5715 : 42 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5716 : : keyword, nm,
5717 : 42 : fmtId(PQgetvalue(res, i, i_extname)));
5718 : : }
5719 : :
5720 : 42 : PQclear(res);
5721 : 42 : destroyPQExpBuffer(query);
5722 : 42 : pg_free(nm);
5723 : : }
5724 : 5411 : }
5725 : :
5726 : : static Oid
5727 : 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5728 : : {
5729 : : /*
5730 : : * If the old version didn't assign an array type, but the new version
5731 : : * does, we must select an unused type OID to assign. This currently only
5732 : : * happens for domains, when upgrading pre-v11 to v11 and up.
5733 : : *
5734 : : * Note: local state here is kind of ugly, but we must have some, since we
5735 : : * mustn't choose the same unused OID more than once.
5736 : : */
5737 : : static Oid next_possible_free_oid = FirstNormalObjectId;
5738 : : PGresult *res;
5739 : : bool is_dup;
5740 : :
5741 : : do
5742 : : {
5743 : 0 : ++next_possible_free_oid;
5744 : 0 : printfPQExpBuffer(upgrade_query,
5745 : : "SELECT EXISTS(SELECT 1 "
5746 : : "FROM pg_catalog.pg_type "
5747 : : "WHERE oid = '%u'::pg_catalog.oid);",
5748 : : next_possible_free_oid);
5749 : 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5750 : 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5751 : 0 : PQclear(res);
5752 [ # # ]: 0 : } while (is_dup);
5753 : :
5754 : 0 : return next_possible_free_oid;
5755 : : }
5756 : :
5757 : : static void
5758 : 1017 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5759 : : PQExpBuffer upgrade_buffer,
5760 : : Oid pg_type_oid,
5761 : : bool force_array_type,
5762 : : bool include_multirange_type)
5763 : : {
5764 : 1017 : PQExpBuffer upgrade_query = createPQExpBuffer();
5765 : : PGresult *res;
5766 : : Oid pg_type_array_oid;
5767 : : Oid pg_type_multirange_oid;
5768 : : Oid pg_type_multirange_array_oid;
5769 : : TypeInfo *tinfo;
5770 : :
5771 : 1017 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5772 : 1017 : appendPQExpBuffer(upgrade_buffer,
5773 : : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5774 : : pg_type_oid);
5775 : :
5776 : 1017 : tinfo = findTypeByOid(pg_type_oid);
5777 [ + - ]: 1017 : if (tinfo)
5778 : 1017 : pg_type_array_oid = tinfo->typarray;
5779 : : else
5780 : 0 : pg_type_array_oid = InvalidOid;
5781 : :
5782 [ + + - + ]: 1017 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5783 : 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5784 : :
5785 [ + + ]: 1017 : if (OidIsValid(pg_type_array_oid))
5786 : : {
5787 : 1015 : appendPQExpBufferStr(upgrade_buffer,
5788 : : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5789 : 1015 : appendPQExpBuffer(upgrade_buffer,
5790 : : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5791 : : pg_type_array_oid);
5792 : : }
5793 : :
5794 : : /*
5795 : : * Pre-set the multirange type oid and its own array type oid.
5796 : : */
5797 [ + + ]: 1017 : if (include_multirange_type)
5798 : : {
5799 [ + - ]: 9 : if (fout->remoteVersion >= 140000)
5800 : : {
5801 : 9 : printfPQExpBuffer(upgrade_query,
5802 : : "SELECT t.oid, t.typarray "
5803 : : "FROM pg_catalog.pg_type t "
5804 : : "JOIN pg_catalog.pg_range r "
5805 : : "ON t.oid = r.rngmultitypid "
5806 : : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5807 : : pg_type_oid);
5808 : :
5809 : 9 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5810 : :
5811 : 9 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5812 : 9 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5813 : :
5814 : 9 : PQclear(res);
5815 : : }
5816 : : else
5817 : : {
5818 : 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5819 : 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5820 : : }
5821 : :
5822 : 9 : appendPQExpBufferStr(upgrade_buffer,
5823 : : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5824 : 9 : appendPQExpBuffer(upgrade_buffer,
5825 : : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5826 : : pg_type_multirange_oid);
5827 : 9 : appendPQExpBufferStr(upgrade_buffer,
5828 : : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5829 : 9 : appendPQExpBuffer(upgrade_buffer,
5830 : : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5831 : : pg_type_multirange_array_oid);
5832 : : }
5833 : :
5834 : 1017 : destroyPQExpBuffer(upgrade_query);
5835 : 1017 : }
5836 : :
5837 : : static void
5838 : 952 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5839 : : PQExpBuffer upgrade_buffer,
5840 : : const TableInfo *tbinfo)
5841 : : {
5842 : 952 : Oid pg_type_oid = tbinfo->reltype;
5843 : :
5844 [ + + ]: 952 : if (OidIsValid(pg_type_oid))
5845 : 937 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5846 : : pg_type_oid, false, false);
5847 : 952 : }
5848 : :
5849 : : /*
5850 : : * bsearch() comparator for BinaryUpgradeClassOidItem
5851 : : */
5852 : : static int
5853 : 13815 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5854 : : {
5855 : 13815 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5856 : 13815 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5857 : :
5858 : 13815 : return pg_cmp_u32(v1.oid, v2.oid);
5859 : : }
5860 : :
5861 : : /*
5862 : : * collectBinaryUpgradeClassOids
5863 : : *
5864 : : * Construct a table of pg_class information required for
5865 : : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5866 : : * lookup.
5867 : : */
5868 : : static void
5869 : 40 : collectBinaryUpgradeClassOids(Archive *fout)
5870 : : {
5871 : : PGresult *res;
5872 : : const char *query;
5873 : :
5874 : 40 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5875 : : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5876 : : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5877 : : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5878 : : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5879 : : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5880 : : "ORDER BY c.oid;";
5881 : :
5882 : 40 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5883 : :
5884 : 40 : nbinaryUpgradeClassOids = PQntuples(res);
5885 : 40 : binaryUpgradeClassOids =
5886 : 40 : pg_malloc_array(BinaryUpgradeClassOidItem, nbinaryUpgradeClassOids);
5887 : :
5888 [ + + ]: 20127 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5889 : : {
5890 : 20087 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5891 : 20087 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5892 : 20087 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5893 : 20087 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5894 : 20087 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5895 : 20087 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5896 : 20087 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5897 : : }
5898 : :
5899 : 40 : PQclear(res);
5900 : 40 : }
5901 : :
5902 : : static void
5903 : 1375 : binary_upgrade_set_pg_class_oids(Archive *fout,
5904 : : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5905 : : {
5906 : 1375 : BinaryUpgradeClassOidItem key = {0};
5907 : : BinaryUpgradeClassOidItem *entry;
5908 : :
5909 : : Assert(binaryUpgradeClassOids);
5910 : :
5911 : : /*
5912 : : * Preserve the OID and relfilenumber of the table, table's index, table's
5913 : : * toast table and toast table's index if any.
5914 : : *
5915 : : * One complexity is that the current table definition might not require
5916 : : * the creation of a TOAST table, but the old database might have a TOAST
5917 : : * table that was created earlier, before some wide columns were dropped.
5918 : : * By setting the TOAST oid we force creation of the TOAST heap and index
5919 : : * by the new backend, so we can copy the files during binary upgrade
5920 : : * without worrying about this case.
5921 : : */
5922 : 1375 : key.oid = pg_class_oid;
5923 : 1375 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5924 : : sizeof(BinaryUpgradeClassOidItem),
5925 : : BinaryUpgradeClassOidItemCmp);
5926 : :
5927 : 1375 : appendPQExpBufferStr(upgrade_buffer,
5928 : : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5929 : :
5930 [ + + ]: 1375 : if (entry->relkind != RELKIND_INDEX &&
5931 [ + + ]: 1066 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5932 : : {
5933 : 1036 : appendPQExpBuffer(upgrade_buffer,
5934 : : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5935 : : pg_class_oid);
5936 : :
5937 : : /*
5938 : : * Not every relation has storage. Also, in a pre-v12 database,
5939 : : * partitioned tables have a relfilenumber, which should not be
5940 : : * preserved when upgrading.
5941 : : */
5942 [ + + ]: 1036 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5943 [ + - ]: 849 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5944 : 849 : appendPQExpBuffer(upgrade_buffer,
5945 : : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5946 : : entry->relfilenumber);
5947 : :
5948 : : /*
5949 : : * In a pre-v12 database, partitioned tables might be marked as having
5950 : : * toast tables, but we should ignore them if so.
5951 : : */
5952 [ + + ]: 1036 : if (OidIsValid(entry->toast_oid) &&
5953 [ + - ]: 296 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5954 : : {
5955 : 296 : appendPQExpBuffer(upgrade_buffer,
5956 : : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5957 : : entry->toast_oid);
5958 : 296 : appendPQExpBuffer(upgrade_buffer,
5959 : : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5960 : : entry->toast_relfilenumber);
5961 : :
5962 : : /* every toast table has an index */
5963 : 296 : appendPQExpBuffer(upgrade_buffer,
5964 : : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5965 : : entry->toast_index_oid);
5966 : 296 : appendPQExpBuffer(upgrade_buffer,
5967 : : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5968 : : entry->toast_index_relfilenumber);
5969 : : }
5970 : : }
5971 : : else
5972 : : {
5973 : : /* Preserve the OID and relfilenumber of the index */
5974 : 339 : appendPQExpBuffer(upgrade_buffer,
5975 : : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5976 : : pg_class_oid);
5977 : 339 : appendPQExpBuffer(upgrade_buffer,
5978 : : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5979 : : entry->relfilenumber);
5980 : : }
5981 : :
5982 : 1375 : appendPQExpBufferChar(upgrade_buffer, '\n');
5983 : 1375 : }
5984 : :
5985 : : /*
5986 : : * If the DumpableObject is a member of an extension, add a suitable
5987 : : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5988 : : *
5989 : : * For somewhat historical reasons, objname should already be quoted,
5990 : : * but not objnamespace (if any).
5991 : : */
5992 : : static void
5993 : 1620 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5994 : : const DumpableObject *dobj,
5995 : : const char *objtype,
5996 : : const char *objname,
5997 : : const char *objnamespace)
5998 : : {
5999 : 1620 : DumpableObject *extobj = NULL;
6000 : : int i;
6001 : :
6002 [ + + ]: 1620 : if (!dobj->ext_member)
6003 : 1598 : return;
6004 : :
6005 : : /*
6006 : : * Find the parent extension. We could avoid this search if we wanted to
6007 : : * add a link field to DumpableObject, but the space costs of that would
6008 : : * be considerable. We assume that member objects could only have a
6009 : : * direct dependency on their own extension, not any others.
6010 : : */
6011 [ + - ]: 22 : for (i = 0; i < dobj->nDeps; i++)
6012 : : {
6013 : 22 : extobj = findObjectByDumpId(dobj->dependencies[i]);
6014 [ + - + - ]: 22 : if (extobj && extobj->objType == DO_EXTENSION)
6015 : 22 : break;
6016 : 0 : extobj = NULL;
6017 : : }
6018 [ - + ]: 22 : if (extobj == NULL)
6019 : 0 : pg_fatal("could not find parent extension for %s %s",
6020 : : objtype, objname);
6021 : :
6022 : 22 : appendPQExpBufferStr(upgrade_buffer,
6023 : : "\n-- For binary upgrade, handle extension membership the hard way\n");
6024 : 22 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6025 : 22 : fmtId(extobj->name),
6026 : : objtype);
6027 [ + + + - ]: 22 : if (objnamespace && *objnamespace)
6028 : 19 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
6029 : 22 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6030 : : }
6031 : :
6032 : : /*
6033 : : * getNamespaces:
6034 : : * get information about all namespaces in the system catalogs
6035 : : */
6036 : : void
6037 : 192 : getNamespaces(Archive *fout)
6038 : : {
6039 : : PGresult *res;
6040 : : int ntups;
6041 : : int i;
6042 : : PQExpBuffer query;
6043 : : NamespaceInfo *nsinfo;
6044 : : int i_tableoid;
6045 : : int i_oid;
6046 : : int i_nspname;
6047 : : int i_nspowner;
6048 : : int i_nspacl;
6049 : : int i_acldefault;
6050 : :
6051 : 192 : query = createPQExpBuffer();
6052 : :
6053 : : /*
6054 : : * we fetch all namespaces including system ones, so that every object we
6055 : : * read in can be linked to a containing namespace.
6056 : : */
6057 : 192 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6058 : : "n.nspowner, "
6059 : : "n.nspacl, "
6060 : : "acldefault('n', n.nspowner) AS acldefault "
6061 : : "FROM pg_namespace n");
6062 : :
6063 : 192 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6064 : :
6065 : 192 : ntups = PQntuples(res);
6066 : :
6067 : 192 : nsinfo = pg_malloc_array(NamespaceInfo, ntups);
6068 : :
6069 : 192 : i_tableoid = PQfnumber(res, "tableoid");
6070 : 192 : i_oid = PQfnumber(res, "oid");
6071 : 192 : i_nspname = PQfnumber(res, "nspname");
6072 : 192 : i_nspowner = PQfnumber(res, "nspowner");
6073 : 192 : i_nspacl = PQfnumber(res, "nspacl");
6074 : 192 : i_acldefault = PQfnumber(res, "acldefault");
6075 : :
6076 [ + + ]: 1888 : for (i = 0; i < ntups; i++)
6077 : : {
6078 : : const char *nspowner;
6079 : :
6080 : 1696 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6081 : 1696 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6082 : 1696 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6083 : 1696 : AssignDumpId(&nsinfo[i].dobj);
6084 : 1696 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6085 : 1696 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6086 : 1696 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6087 : 1696 : nsinfo[i].dacl.privtype = 0;
6088 : 1696 : nsinfo[i].dacl.initprivs = NULL;
6089 : 1696 : nspowner = PQgetvalue(res, i, i_nspowner);
6090 : 1696 : nsinfo[i].nspowner = atooid(nspowner);
6091 : 1696 : nsinfo[i].rolname = getRoleName(nspowner);
6092 : :
6093 : : /* Decide whether to dump this namespace */
6094 : 1696 : selectDumpableNamespace(&nsinfo[i], fout);
6095 : :
6096 : : /* Mark whether namespace has an ACL */
6097 [ + + ]: 1696 : if (!PQgetisnull(res, i, i_nspacl))
6098 : 861 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6099 : :
6100 : : /*
6101 : : * We ignore any pg_init_privs.initprivs entry for the public schema
6102 : : * and assume a predetermined default, for several reasons. First,
6103 : : * dropping and recreating the schema removes its pg_init_privs entry,
6104 : : * but an empty destination database starts with this ACL nonetheless.
6105 : : * Second, we support dump/reload of public schema ownership changes.
6106 : : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6107 : : * initprivs continues to reflect the initial owner. Hence,
6108 : : * synthesize the value that nspacl will have after the restore's
6109 : : * ALTER SCHEMA OWNER. Third, this makes the destination database
6110 : : * match the source's ACL, even if the latter was an initdb-default
6111 : : * ACL, which changed in v15. An upgrade pulls in changes to most
6112 : : * system object ACLs that the DBA had not customized. We've made the
6113 : : * public schema depart from that, because changing its ACL so easily
6114 : : * breaks applications.
6115 : : */
6116 [ + + ]: 1696 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6117 : : {
6118 : 188 : PQExpBuffer aclarray = createPQExpBuffer();
6119 : 188 : PQExpBuffer aclitem = createPQExpBuffer();
6120 : :
6121 : : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6122 : 188 : appendPQExpBufferChar(aclarray, '{');
6123 : 188 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6124 : 188 : appendPQExpBufferStr(aclitem, "=UC/");
6125 : 188 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6126 : 188 : appendPGArray(aclarray, aclitem->data);
6127 : 188 : resetPQExpBuffer(aclitem);
6128 : 188 : appendPQExpBufferStr(aclitem, "=U/");
6129 : 188 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6130 : 188 : appendPGArray(aclarray, aclitem->data);
6131 : 188 : appendPQExpBufferChar(aclarray, '}');
6132 : :
6133 : 188 : nsinfo[i].dacl.privtype = 'i';
6134 : 188 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6135 : 188 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6136 : :
6137 : 188 : destroyPQExpBuffer(aclarray);
6138 : 188 : destroyPQExpBuffer(aclitem);
6139 : : }
6140 : : }
6141 : :
6142 : 192 : PQclear(res);
6143 : 192 : destroyPQExpBuffer(query);
6144 : 192 : }
6145 : :
6146 : : /*
6147 : : * findNamespace:
6148 : : * given a namespace OID, look up the info read by getNamespaces
6149 : : */
6150 : : static NamespaceInfo *
6151 : 626220 : findNamespace(Oid nsoid)
6152 : : {
6153 : : NamespaceInfo *nsinfo;
6154 : :
6155 : 626220 : nsinfo = findNamespaceByOid(nsoid);
6156 [ - + ]: 626220 : if (nsinfo == NULL)
6157 : 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6158 : 626220 : return nsinfo;
6159 : : }
6160 : :
6161 : : /*
6162 : : * getExtensions:
6163 : : * read all extensions in the system catalogs and return them in the
6164 : : * ExtensionInfo* structure
6165 : : *
6166 : : * numExtensions is set to the number of extensions read in
6167 : : */
6168 : : ExtensionInfo *
6169 : 192 : getExtensions(Archive *fout, int *numExtensions)
6170 : : {
6171 : 192 : DumpOptions *dopt = fout->dopt;
6172 : : PGresult *res;
6173 : : int ntups;
6174 : : int i;
6175 : : PQExpBuffer query;
6176 : 192 : ExtensionInfo *extinfo = NULL;
6177 : : int i_tableoid;
6178 : : int i_oid;
6179 : : int i_extname;
6180 : : int i_nspname;
6181 : : int i_extrelocatable;
6182 : : int i_extversion;
6183 : : int i_extconfig;
6184 : : int i_extcondition;
6185 : :
6186 : 192 : query = createPQExpBuffer();
6187 : :
6188 : 192 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6189 : : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6190 : : "FROM pg_extension x "
6191 : : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6192 : :
6193 : 192 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6194 : :
6195 : 192 : ntups = PQntuples(res);
6196 [ - + ]: 192 : if (ntups == 0)
6197 : 0 : goto cleanup;
6198 : :
6199 : 192 : extinfo = pg_malloc_array(ExtensionInfo, ntups);
6200 : :
6201 : 192 : i_tableoid = PQfnumber(res, "tableoid");
6202 : 192 : i_oid = PQfnumber(res, "oid");
6203 : 192 : i_extname = PQfnumber(res, "extname");
6204 : 192 : i_nspname = PQfnumber(res, "nspname");
6205 : 192 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6206 : 192 : i_extversion = PQfnumber(res, "extversion");
6207 : 192 : i_extconfig = PQfnumber(res, "extconfig");
6208 : 192 : i_extcondition = PQfnumber(res, "extcondition");
6209 : :
6210 [ + + ]: 415 : for (i = 0; i < ntups; i++)
6211 : : {
6212 : 223 : extinfo[i].dobj.objType = DO_EXTENSION;
6213 : 223 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6214 : 223 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6215 : 223 : AssignDumpId(&extinfo[i].dobj);
6216 : 223 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6217 : 223 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6218 : 223 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6219 : 223 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6220 : 223 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6221 : 223 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6222 : :
6223 : : /* Decide whether we want to dump it */
6224 : 223 : selectDumpableExtension(&(extinfo[i]), dopt);
6225 : : }
6226 : :
6227 : 192 : cleanup:
6228 : 192 : PQclear(res);
6229 : 192 : destroyPQExpBuffer(query);
6230 : :
6231 : 192 : *numExtensions = ntups;
6232 : :
6233 : 192 : return extinfo;
6234 : : }
6235 : :
6236 : : /*
6237 : : * getTypes:
6238 : : * get information about all types in the system catalogs
6239 : : *
6240 : : * NB: this must run after getFuncs() because we assume we can do
6241 : : * findFuncByOid().
6242 : : */
6243 : : void
6244 : 191 : getTypes(Archive *fout)
6245 : : {
6246 : : PGresult *res;
6247 : : int ntups;
6248 : : int i;
6249 : 191 : PQExpBuffer query = createPQExpBuffer();
6250 : : TypeInfo *tyinfo;
6251 : : ShellTypeInfo *stinfo;
6252 : : int i_tableoid;
6253 : : int i_oid;
6254 : : int i_typname;
6255 : : int i_typnamespace;
6256 : : int i_typacl;
6257 : : int i_acldefault;
6258 : : int i_typowner;
6259 : : int i_typelem;
6260 : : int i_typrelid;
6261 : : int i_typrelkind;
6262 : : int i_typtype;
6263 : : int i_typisdefined;
6264 : : int i_isarray;
6265 : : int i_typarray;
6266 : :
6267 : : /*
6268 : : * we include even the built-in types because those may be used as array
6269 : : * elements by user-defined types
6270 : : *
6271 : : * we filter out the built-in types when we dump out the types
6272 : : *
6273 : : * same approach for undefined (shell) types and array types
6274 : : *
6275 : : * Note: as of 8.3 we can reliably detect whether a type is an
6276 : : * auto-generated array type by checking the element type's typarray.
6277 : : * (Before that the test is capable of generating false positives.) We
6278 : : * still check for name beginning with '_', though, so as to avoid the
6279 : : * cost of the subselect probe for all standard types. This would have to
6280 : : * be revisited if the backend ever allows renaming of array types.
6281 : : */
6282 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6283 : : "typnamespace, typacl, "
6284 : : "acldefault('T', typowner) AS acldefault, "
6285 : : "typowner, "
6286 : : "typelem, typrelid, typarray, "
6287 : : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6288 : : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6289 : : "typtype, typisdefined, "
6290 : : "typname[0] = '_' AND typelem != 0 AND "
6291 : : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6292 : : "FROM pg_type");
6293 : :
6294 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6295 : :
6296 : 191 : ntups = PQntuples(res);
6297 : :
6298 : 191 : tyinfo = pg_malloc_array(TypeInfo, ntups);
6299 : :
6300 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6301 : 191 : i_oid = PQfnumber(res, "oid");
6302 : 191 : i_typname = PQfnumber(res, "typname");
6303 : 191 : i_typnamespace = PQfnumber(res, "typnamespace");
6304 : 191 : i_typacl = PQfnumber(res, "typacl");
6305 : 191 : i_acldefault = PQfnumber(res, "acldefault");
6306 : 191 : i_typowner = PQfnumber(res, "typowner");
6307 : 191 : i_typelem = PQfnumber(res, "typelem");
6308 : 191 : i_typrelid = PQfnumber(res, "typrelid");
6309 : 191 : i_typrelkind = PQfnumber(res, "typrelkind");
6310 : 191 : i_typtype = PQfnumber(res, "typtype");
6311 : 191 : i_typisdefined = PQfnumber(res, "typisdefined");
6312 : 191 : i_isarray = PQfnumber(res, "isarray");
6313 : 191 : i_typarray = PQfnumber(res, "typarray");
6314 : :
6315 [ + + ]: 148169 : for (i = 0; i < ntups; i++)
6316 : : {
6317 : 147978 : tyinfo[i].dobj.objType = DO_TYPE;
6318 : 147978 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6319 : 147978 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6320 : 147978 : AssignDumpId(&tyinfo[i].dobj);
6321 : 147978 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6322 : 295956 : tyinfo[i].dobj.namespace =
6323 : 147978 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6324 : 147978 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6325 : 147978 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6326 : 147978 : tyinfo[i].dacl.privtype = 0;
6327 : 147978 : tyinfo[i].dacl.initprivs = NULL;
6328 : 147978 : tyinfo[i].ftypname = NULL; /* may get filled later */
6329 : 147978 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6330 : 147978 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6331 : 147978 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6332 : 147978 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6333 : 147978 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6334 : 147978 : tyinfo[i].shellType = NULL;
6335 : :
6336 [ + + ]: 147978 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6337 : 147923 : tyinfo[i].isDefined = true;
6338 : : else
6339 : 55 : tyinfo[i].isDefined = false;
6340 : :
6341 [ + + ]: 147978 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6342 : 71192 : tyinfo[i].isArray = true;
6343 : : else
6344 : 76786 : tyinfo[i].isArray = false;
6345 : :
6346 : 147978 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6347 : :
6348 [ + + ]: 147978 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6349 : 1291 : tyinfo[i].isMultirange = true;
6350 : : else
6351 : 146687 : tyinfo[i].isMultirange = false;
6352 : :
6353 : : /* Decide whether we want to dump it */
6354 : 147978 : selectDumpableType(&tyinfo[i], fout);
6355 : :
6356 : : /* Mark whether type has an ACL */
6357 [ + + ]: 147978 : if (!PQgetisnull(res, i, i_typacl))
6358 : 217 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6359 : :
6360 : : /*
6361 : : * If it's a domain, fetch info about its constraints, if any
6362 : : */
6363 : 147978 : tyinfo[i].nDomChecks = 0;
6364 : 147978 : tyinfo[i].domChecks = NULL;
6365 : 147978 : tyinfo[i].notnull = NULL;
6366 [ + + ]: 147978 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6367 [ + + ]: 16336 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6368 : 181 : getDomainConstraints(fout, &(tyinfo[i]));
6369 : :
6370 : : /*
6371 : : * If it's a base type, make a DumpableObject representing a shell
6372 : : * definition of the type. We will need to dump that ahead of the I/O
6373 : : * functions for the type. Similarly, range types need a shell
6374 : : * definition in case they have a canonicalize function.
6375 : : *
6376 : : * Note: the shell type doesn't have a catId. You might think it
6377 : : * should copy the base type's catId, but then it might capture the
6378 : : * pg_depend entries for the type, which we don't want.
6379 : : */
6380 [ + + ]: 147978 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6381 [ + + ]: 16336 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6382 [ + + ]: 7950 : tyinfo[i].typtype == TYPTYPE_RANGE))
6383 : : {
6384 : 8521 : stinfo = pg_malloc_object(ShellTypeInfo);
6385 : 8521 : stinfo->dobj.objType = DO_SHELL_TYPE;
6386 : 8521 : stinfo->dobj.catId = nilCatalogId;
6387 : 8521 : AssignDumpId(&stinfo->dobj);
6388 : 8521 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6389 : 8521 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6390 : 8521 : stinfo->baseType = &(tyinfo[i]);
6391 : 8521 : tyinfo[i].shellType = stinfo;
6392 : :
6393 : : /*
6394 : : * Initially mark the shell type as not to be dumped. We'll only
6395 : : * dump it if the I/O or canonicalize functions need to be dumped;
6396 : : * this is taken care of while sorting dependencies.
6397 : : */
6398 : 8521 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6399 : : }
6400 : : }
6401 : :
6402 : 191 : PQclear(res);
6403 : :
6404 : 191 : destroyPQExpBuffer(query);
6405 : 191 : }
6406 : :
6407 : : /*
6408 : : * getOperators:
6409 : : * get information about all operators in the system catalogs
6410 : : */
6411 : : void
6412 : 191 : getOperators(Archive *fout)
6413 : : {
6414 : : PGresult *res;
6415 : : int ntups;
6416 : : int i;
6417 : 191 : PQExpBuffer query = createPQExpBuffer();
6418 : : OprInfo *oprinfo;
6419 : : int i_tableoid;
6420 : : int i_oid;
6421 : : int i_oprname;
6422 : : int i_oprnamespace;
6423 : : int i_oprowner;
6424 : : int i_oprkind;
6425 : : int i_oprleft;
6426 : : int i_oprright;
6427 : : int i_oprcode;
6428 : :
6429 : : /*
6430 : : * find all operators, including builtin operators; we filter out
6431 : : * system-defined operators at dump-out time.
6432 : : */
6433 : :
6434 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6435 : : "oprnamespace, "
6436 : : "oprowner, "
6437 : : "oprkind, "
6438 : : "oprleft, "
6439 : : "oprright, "
6440 : : "oprcode::oid AS oprcode "
6441 : : "FROM pg_operator");
6442 : :
6443 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6444 : :
6445 : 191 : ntups = PQntuples(res);
6446 : :
6447 : 191 : oprinfo = pg_malloc_array(OprInfo, ntups);
6448 : :
6449 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6450 : 191 : i_oid = PQfnumber(res, "oid");
6451 : 191 : i_oprname = PQfnumber(res, "oprname");
6452 : 191 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6453 : 191 : i_oprowner = PQfnumber(res, "oprowner");
6454 : 191 : i_oprkind = PQfnumber(res, "oprkind");
6455 : 191 : i_oprleft = PQfnumber(res, "oprleft");
6456 : 191 : i_oprright = PQfnumber(res, "oprright");
6457 : 191 : i_oprcode = PQfnumber(res, "oprcode");
6458 : :
6459 [ + + ]: 154091 : for (i = 0; i < ntups; i++)
6460 : : {
6461 : 153900 : oprinfo[i].dobj.objType = DO_OPERATOR;
6462 : 153900 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6463 : 153900 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6464 : 153900 : AssignDumpId(&oprinfo[i].dobj);
6465 : 153900 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6466 : 307800 : oprinfo[i].dobj.namespace =
6467 : 153900 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6468 : 153900 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6469 : 153900 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6470 : 153900 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6471 : 153900 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6472 : 153900 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6473 : :
6474 : : /* Decide whether we want to dump it */
6475 : 153900 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6476 : : }
6477 : :
6478 : 191 : PQclear(res);
6479 : :
6480 : 191 : destroyPQExpBuffer(query);
6481 : 191 : }
6482 : :
6483 : : /*
6484 : : * getCollations:
6485 : : * get information about all collations in the system catalogs
6486 : : */
6487 : : void
6488 : 191 : getCollations(Archive *fout)
6489 : : {
6490 : : PGresult *res;
6491 : : int ntups;
6492 : : int i;
6493 : : PQExpBuffer query;
6494 : : CollInfo *collinfo;
6495 : : int i_tableoid;
6496 : : int i_oid;
6497 : : int i_collname;
6498 : : int i_collnamespace;
6499 : : int i_collowner;
6500 : : int i_collencoding;
6501 : :
6502 : 191 : query = createPQExpBuffer();
6503 : :
6504 : : /*
6505 : : * find all collations, including builtin collations; we filter out
6506 : : * system-defined collations at dump-out time.
6507 : : */
6508 : :
6509 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6510 : : "collnamespace, "
6511 : : "collowner, "
6512 : : "collencoding "
6513 : : "FROM pg_collation");
6514 : :
6515 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6516 : :
6517 : 191 : ntups = PQntuples(res);
6518 : :
6519 : 191 : collinfo = pg_malloc_array(CollInfo, ntups);
6520 : :
6521 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6522 : 191 : i_oid = PQfnumber(res, "oid");
6523 : 191 : i_collname = PQfnumber(res, "collname");
6524 : 191 : i_collnamespace = PQfnumber(res, "collnamespace");
6525 : 191 : i_collowner = PQfnumber(res, "collowner");
6526 : 191 : i_collencoding = PQfnumber(res, "collencoding");
6527 : :
6528 [ + + ]: 168388 : for (i = 0; i < ntups; i++)
6529 : : {
6530 : 168197 : collinfo[i].dobj.objType = DO_COLLATION;
6531 : 168197 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6532 : 168197 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6533 : 168197 : AssignDumpId(&collinfo[i].dobj);
6534 : 168197 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6535 : 336394 : collinfo[i].dobj.namespace =
6536 : 168197 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6537 : 168197 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6538 : 168197 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6539 : :
6540 : : /* Decide whether we want to dump it */
6541 : 168197 : selectDumpableObject(&(collinfo[i].dobj), fout);
6542 : : }
6543 : :
6544 : 191 : PQclear(res);
6545 : :
6546 : 191 : destroyPQExpBuffer(query);
6547 : 191 : }
6548 : :
6549 : : /*
6550 : : * getConversions:
6551 : : * get information about all conversions in the system catalogs
6552 : : */
6553 : : void
6554 : 191 : getConversions(Archive *fout)
6555 : : {
6556 : : PGresult *res;
6557 : : int ntups;
6558 : : int i;
6559 : : PQExpBuffer query;
6560 : : ConvInfo *convinfo;
6561 : : int i_tableoid;
6562 : : int i_oid;
6563 : : int i_conname;
6564 : : int i_connamespace;
6565 : : int i_conowner;
6566 : :
6567 : 191 : query = createPQExpBuffer();
6568 : :
6569 : : /*
6570 : : * find all conversions, including builtin conversions; we filter out
6571 : : * system-defined conversions at dump-out time.
6572 : : */
6573 : :
6574 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6575 : : "connamespace, "
6576 : : "conowner "
6577 : : "FROM pg_conversion");
6578 : :
6579 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6580 : :
6581 : 191 : ntups = PQntuples(res);
6582 : :
6583 : 191 : convinfo = pg_malloc_array(ConvInfo, ntups);
6584 : :
6585 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6586 : 191 : i_oid = PQfnumber(res, "oid");
6587 : 191 : i_conname = PQfnumber(res, "conname");
6588 : 191 : i_connamespace = PQfnumber(res, "connamespace");
6589 : 191 : i_conowner = PQfnumber(res, "conowner");
6590 : :
6591 [ + + ]: 18957 : for (i = 0; i < ntups; i++)
6592 : : {
6593 : 18766 : convinfo[i].dobj.objType = DO_CONVERSION;
6594 : 18766 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6595 : 18766 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6596 : 18766 : AssignDumpId(&convinfo[i].dobj);
6597 : 18766 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6598 : 37532 : convinfo[i].dobj.namespace =
6599 : 18766 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6600 : 18766 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6601 : :
6602 : : /* Decide whether we want to dump it */
6603 : 18766 : selectDumpableObject(&(convinfo[i].dobj), fout);
6604 : : }
6605 : :
6606 : 191 : PQclear(res);
6607 : :
6608 : 191 : destroyPQExpBuffer(query);
6609 : 191 : }
6610 : :
6611 : : /*
6612 : : * getAccessMethods:
6613 : : * get information about all user-defined access methods
6614 : : */
6615 : : void
6616 : 191 : getAccessMethods(Archive *fout)
6617 : : {
6618 : : PGresult *res;
6619 : : int ntups;
6620 : : int i;
6621 : : PQExpBuffer query;
6622 : : AccessMethodInfo *aminfo;
6623 : : int i_tableoid;
6624 : : int i_oid;
6625 : : int i_amname;
6626 : : int i_amhandler;
6627 : : int i_amtype;
6628 : :
6629 : 191 : query = createPQExpBuffer();
6630 : :
6631 : : /*
6632 : : * Select all access methods from pg_am table.
6633 : : */
6634 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6635 : 191 : appendPQExpBufferStr(query,
6636 : : "amtype, "
6637 : : "amhandler::pg_catalog.regproc AS amhandler ");
6638 : 191 : appendPQExpBufferStr(query, "FROM pg_am");
6639 : :
6640 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6641 : :
6642 : 191 : ntups = PQntuples(res);
6643 : :
6644 : 191 : aminfo = pg_malloc_array(AccessMethodInfo, ntups);
6645 : :
6646 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6647 : 191 : i_oid = PQfnumber(res, "oid");
6648 : 191 : i_amname = PQfnumber(res, "amname");
6649 : 191 : i_amhandler = PQfnumber(res, "amhandler");
6650 : 191 : i_amtype = PQfnumber(res, "amtype");
6651 : :
6652 [ + + ]: 1656 : for (i = 0; i < ntups; i++)
6653 : : {
6654 : 1465 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6655 : 1465 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6656 : 1465 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6657 : 1465 : AssignDumpId(&aminfo[i].dobj);
6658 : 1465 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6659 : 1465 : aminfo[i].dobj.namespace = NULL;
6660 : 1465 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6661 : 1465 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6662 : :
6663 : : /* Decide whether we want to dump it */
6664 : 1465 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6665 : : }
6666 : :
6667 : 191 : PQclear(res);
6668 : :
6669 : 191 : destroyPQExpBuffer(query);
6670 : 191 : }
6671 : :
6672 : :
6673 : : /*
6674 : : * getOpclasses:
6675 : : * get information about all opclasses in the system catalogs
6676 : : */
6677 : : void
6678 : 191 : getOpclasses(Archive *fout)
6679 : : {
6680 : : PGresult *res;
6681 : : int ntups;
6682 : : int i;
6683 : 191 : PQExpBuffer query = createPQExpBuffer();
6684 : : OpclassInfo *opcinfo;
6685 : : int i_tableoid;
6686 : : int i_oid;
6687 : : int i_opcmethod;
6688 : : int i_opcname;
6689 : : int i_opcnamespace;
6690 : : int i_opcowner;
6691 : :
6692 : : /*
6693 : : * find all opclasses, including builtin opclasses; we filter out
6694 : : * system-defined opclasses at dump-out time.
6695 : : */
6696 : :
6697 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6698 : : "opcnamespace, "
6699 : : "opcowner "
6700 : : "FROM pg_opclass");
6701 : :
6702 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6703 : :
6704 : 191 : ntups = PQntuples(res);
6705 : :
6706 : 191 : opcinfo = pg_malloc_array(OpclassInfo, ntups);
6707 : :
6708 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6709 : 191 : i_oid = PQfnumber(res, "oid");
6710 : 191 : i_opcmethod = PQfnumber(res, "opcmethod");
6711 : 191 : i_opcname = PQfnumber(res, "opcname");
6712 : 191 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6713 : 191 : i_opcowner = PQfnumber(res, "opcowner");
6714 : :
6715 [ + + ]: 34545 : for (i = 0; i < ntups; i++)
6716 : : {
6717 : 34354 : opcinfo[i].dobj.objType = DO_OPCLASS;
6718 : 34354 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6719 : 34354 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6720 : 34354 : AssignDumpId(&opcinfo[i].dobj);
6721 : 34354 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6722 : 68708 : opcinfo[i].dobj.namespace =
6723 : 34354 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6724 : 34354 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6725 : 34354 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6726 : :
6727 : : /* Decide whether we want to dump it */
6728 : 34354 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6729 : : }
6730 : :
6731 : 191 : PQclear(res);
6732 : :
6733 : 191 : destroyPQExpBuffer(query);
6734 : 191 : }
6735 : :
6736 : : /*
6737 : : * getOpfamilies:
6738 : : * get information about all opfamilies in the system catalogs
6739 : : */
6740 : : void
6741 : 191 : getOpfamilies(Archive *fout)
6742 : : {
6743 : : PGresult *res;
6744 : : int ntups;
6745 : : int i;
6746 : : PQExpBuffer query;
6747 : : OpfamilyInfo *opfinfo;
6748 : : int i_tableoid;
6749 : : int i_oid;
6750 : : int i_opfmethod;
6751 : : int i_opfname;
6752 : : int i_opfnamespace;
6753 : : int i_opfowner;
6754 : :
6755 : 191 : query = createPQExpBuffer();
6756 : :
6757 : : /*
6758 : : * find all opfamilies, including builtin opfamilies; we filter out
6759 : : * system-defined opfamilies at dump-out time.
6760 : : */
6761 : :
6762 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6763 : : "opfnamespace, "
6764 : : "opfowner "
6765 : : "FROM pg_opfamily");
6766 : :
6767 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6768 : :
6769 : 191 : ntups = PQntuples(res);
6770 : :
6771 : 191 : opfinfo = pg_malloc_array(OpfamilyInfo, ntups);
6772 : :
6773 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6774 : 191 : i_oid = PQfnumber(res, "oid");
6775 : 191 : i_opfname = PQfnumber(res, "opfname");
6776 : 191 : i_opfmethod = PQfnumber(res, "opfmethod");
6777 : 191 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6778 : 191 : i_opfowner = PQfnumber(res, "opfowner");
6779 : :
6780 [ + + ]: 28604 : for (i = 0; i < ntups; i++)
6781 : : {
6782 : 28413 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6783 : 28413 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6784 : 28413 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6785 : 28413 : AssignDumpId(&opfinfo[i].dobj);
6786 : 28413 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6787 : 56826 : opfinfo[i].dobj.namespace =
6788 : 28413 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6789 : 28413 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6790 : 28413 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6791 : :
6792 : : /* Decide whether we want to dump it */
6793 : 28413 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6794 : : }
6795 : :
6796 : 191 : PQclear(res);
6797 : :
6798 : 191 : destroyPQExpBuffer(query);
6799 : 191 : }
6800 : :
6801 : : /*
6802 : : * getAggregates:
6803 : : * get information about all user-defined aggregates in the system catalogs
6804 : : */
6805 : : void
6806 : 191 : getAggregates(Archive *fout)
6807 : : {
6808 : 191 : DumpOptions *dopt = fout->dopt;
6809 : : PGresult *res;
6810 : : int ntups;
6811 : : int i;
6812 : 191 : PQExpBuffer query = createPQExpBuffer();
6813 : : AggInfo *agginfo;
6814 : : int i_tableoid;
6815 : : int i_oid;
6816 : : int i_aggname;
6817 : : int i_aggnamespace;
6818 : : int i_pronargs;
6819 : : int i_proargtypes;
6820 : : int i_proowner;
6821 : : int i_aggacl;
6822 : : int i_acldefault;
6823 : : const char *agg_check;
6824 : :
6825 : : /*
6826 : : * Find all interesting aggregates. See comment in getFuncs() for the
6827 : : * rationale behind the filtering logic.
6828 : : */
6829 : 382 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6830 [ + - ]: 191 : : "p.proisagg");
6831 : :
6832 : 191 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6833 : : "p.proname AS aggname, "
6834 : : "p.pronamespace AS aggnamespace, "
6835 : : "p.pronargs, p.proargtypes, "
6836 : : "p.proowner, "
6837 : : "p.proacl AS aggacl, "
6838 : : "acldefault('f', p.proowner) AS acldefault "
6839 : : "FROM pg_proc p "
6840 : : "LEFT JOIN pg_init_privs pip ON "
6841 : : "(p.oid = pip.objoid "
6842 : : "AND pip.classoid = 'pg_proc'::regclass "
6843 : : "AND pip.objsubid = 0) "
6844 : : "WHERE %s AND ("
6845 : : "p.pronamespace != "
6846 : : "(SELECT oid FROM pg_namespace "
6847 : : "WHERE nspname = 'pg_catalog') OR "
6848 : : "p.proacl IS DISTINCT FROM pip.initprivs",
6849 : : agg_check);
6850 [ + + ]: 191 : if (dopt->binary_upgrade)
6851 : 40 : appendPQExpBufferStr(query,
6852 : : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6853 : : "classid = 'pg_proc'::regclass AND "
6854 : : "objid = p.oid AND "
6855 : : "refclassid = 'pg_extension'::regclass AND "
6856 : : "deptype = 'e')");
6857 : 191 : appendPQExpBufferChar(query, ')');
6858 : :
6859 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6860 : :
6861 : 191 : ntups = PQntuples(res);
6862 : :
6863 : 191 : agginfo = pg_malloc_array(AggInfo, ntups);
6864 : :
6865 : 191 : i_tableoid = PQfnumber(res, "tableoid");
6866 : 191 : i_oid = PQfnumber(res, "oid");
6867 : 191 : i_aggname = PQfnumber(res, "aggname");
6868 : 191 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6869 : 191 : i_pronargs = PQfnumber(res, "pronargs");
6870 : 191 : i_proargtypes = PQfnumber(res, "proargtypes");
6871 : 191 : i_proowner = PQfnumber(res, "proowner");
6872 : 191 : i_aggacl = PQfnumber(res, "aggacl");
6873 : 191 : i_acldefault = PQfnumber(res, "acldefault");
6874 : :
6875 [ + + ]: 593 : for (i = 0; i < ntups; i++)
6876 : : {
6877 : 402 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6878 : 402 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6879 : 402 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6880 : 402 : AssignDumpId(&agginfo[i].aggfn.dobj);
6881 : 402 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6882 : 804 : agginfo[i].aggfn.dobj.namespace =
6883 : 402 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6884 : 402 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6885 : 402 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6886 : 402 : agginfo[i].aggfn.dacl.privtype = 0;
6887 : 402 : agginfo[i].aggfn.dacl.initprivs = NULL;
6888 : 402 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6889 : 402 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6890 : 402 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6891 : 402 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6892 [ + + ]: 402 : if (agginfo[i].aggfn.nargs == 0)
6893 : 56 : agginfo[i].aggfn.argtypes = NULL;
6894 : : else
6895 : : {
6896 : 346 : agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6897 : 346 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6898 : 346 : agginfo[i].aggfn.argtypes,
6899 : 346 : agginfo[i].aggfn.nargs);
6900 : : }
6901 : 402 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6902 : :
6903 : : /* Decide whether we want to dump it */
6904 : 402 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6905 : :
6906 : : /* Mark whether aggregate has an ACL */
6907 [ + + ]: 402 : if (!PQgetisnull(res, i, i_aggacl))
6908 : 25 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6909 : : }
6910 : :
6911 : 191 : PQclear(res);
6912 : :
6913 : 191 : destroyPQExpBuffer(query);
6914 : 191 : }
6915 : :
6916 : : /*
6917 : : * getFuncs:
6918 : : * get information about all user-defined functions in the system catalogs
6919 : : */
6920 : : void
6921 : 191 : getFuncs(Archive *fout)
6922 : : {
6923 : 191 : DumpOptions *dopt = fout->dopt;
6924 : : PGresult *res;
6925 : : int ntups;
6926 : : int i;
6927 : 191 : PQExpBuffer query = createPQExpBuffer();
6928 : : FuncInfo *finfo;
6929 : : int i_tableoid;
6930 : : int i_oid;
6931 : : int i_proname;
6932 : : int i_pronamespace;
6933 : : int i_proowner;
6934 : : int i_prolang;
6935 : : int i_pronargs;
6936 : : int i_proargtypes;
6937 : : int i_prorettype;
6938 : : int i_proacl;
6939 : : int i_acldefault;
6940 : : const char *not_agg_check;
6941 : :
6942 : : /*
6943 : : * Find all interesting functions. This is a bit complicated:
6944 : : *
6945 : : * 1. Always exclude aggregates; those are handled elsewhere.
6946 : : *
6947 : : * 2. Always exclude functions that are internally dependent on something
6948 : : * else, since presumably those will be created as a result of creating
6949 : : * the something else. This currently acts only to suppress constructor
6950 : : * functions for range types. Note this is OK only because the
6951 : : * constructors don't have any dependencies the range type doesn't have;
6952 : : * otherwise we might not get creation ordering correct.
6953 : : *
6954 : : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6955 : : * they're members of extensions and we are in binary-upgrade mode then
6956 : : * include them, since we want to dump extension members individually in
6957 : : * that mode. Also, if they are used by casts or transforms then we need
6958 : : * to gather the information about them, though they won't be dumped if
6959 : : * they are built-in. Also, include functions in pg_catalog if they have
6960 : : * an ACL different from what's shown in pg_init_privs (so we have to join
6961 : : * to pg_init_privs; annoying).
6962 : : */
6963 : 382 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6964 [ + - ]: 191 : : "NOT p.proisagg");
6965 : :
6966 : 191 : appendPQExpBuffer(query,
6967 : : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6968 : : "p.pronargs, p.proargtypes, p.prorettype, "
6969 : : "p.proacl, "
6970 : : "acldefault('f', p.proowner) AS acldefault, "
6971 : : "p.pronamespace, "
6972 : : "p.proowner "
6973 : : "FROM pg_proc p "
6974 : : "LEFT JOIN pg_init_privs pip ON "
6975 : : "(p.oid = pip.objoid "
6976 : : "AND pip.classoid = 'pg_proc'::regclass "
6977 : : "AND pip.objsubid = 0) "
6978 : : "WHERE %s"
6979 : : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6980 : : "WHERE classid = 'pg_proc'::regclass AND "
6981 : : "objid = p.oid AND deptype = 'i')"
6982 : : "\n AND ("
6983 : : "\n pronamespace != "
6984 : : "(SELECT oid FROM pg_namespace "
6985 : : "WHERE nspname = 'pg_catalog')"
6986 : : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6987 : : "\n WHERE pg_cast.oid > %u "
6988 : : "\n AND p.oid = pg_cast.castfunc)"
6989 : : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6990 : : "\n WHERE pg_transform.oid > %u AND "
6991 : : "\n (p.oid = pg_transform.trffromsql"
6992 : : "\n OR p.oid = pg_transform.trftosql))",
6993 : : not_agg_check,
6994 : : g_last_builtin_oid,
6995 : : g_last_builtin_oid);
6996 [ + + ]: 191 : if (dopt->binary_upgrade)
6997 : 40 : appendPQExpBufferStr(query,
6998 : : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6999 : : "classid = 'pg_proc'::regclass AND "
7000 : : "objid = p.oid AND "
7001 : : "refclassid = 'pg_extension'::regclass AND "
7002 : : "deptype = 'e')");
7003 : 191 : appendPQExpBufferStr(query,
7004 : : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7005 : 191 : appendPQExpBufferChar(query, ')');
7006 : :
7007 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7008 : :
7009 : 191 : ntups = PQntuples(res);
7010 : :
7011 : 191 : finfo = pg_malloc0_array(FuncInfo, ntups);
7012 : :
7013 : 191 : i_tableoid = PQfnumber(res, "tableoid");
7014 : 191 : i_oid = PQfnumber(res, "oid");
7015 : 191 : i_proname = PQfnumber(res, "proname");
7016 : 191 : i_pronamespace = PQfnumber(res, "pronamespace");
7017 : 191 : i_proowner = PQfnumber(res, "proowner");
7018 : 191 : i_prolang = PQfnumber(res, "prolang");
7019 : 191 : i_pronargs = PQfnumber(res, "pronargs");
7020 : 191 : i_proargtypes = PQfnumber(res, "proargtypes");
7021 : 191 : i_prorettype = PQfnumber(res, "prorettype");
7022 : 191 : i_proacl = PQfnumber(res, "proacl");
7023 : 191 : i_acldefault = PQfnumber(res, "acldefault");
7024 : :
7025 [ + + ]: 5144 : for (i = 0; i < ntups; i++)
7026 : : {
7027 : 4953 : finfo[i].dobj.objType = DO_FUNC;
7028 : 4953 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7029 : 4953 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7030 : 4953 : AssignDumpId(&finfo[i].dobj);
7031 : 4953 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7032 : 9906 : finfo[i].dobj.namespace =
7033 : 4953 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7034 : 4953 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7035 : 4953 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7036 : 4953 : finfo[i].dacl.privtype = 0;
7037 : 4953 : finfo[i].dacl.initprivs = NULL;
7038 : 4953 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7039 : 4953 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7040 : 4953 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7041 : 4953 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7042 [ + + ]: 4953 : if (finfo[i].nargs == 0)
7043 : 1121 : finfo[i].argtypes = NULL;
7044 : : else
7045 : : {
7046 : 3832 : finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7047 : 3832 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7048 : 3832 : finfo[i].argtypes, finfo[i].nargs);
7049 : : }
7050 : 4953 : finfo[i].postponed_def = false; /* might get set during sort */
7051 : :
7052 : : /* Decide whether we want to dump it */
7053 : 4953 : selectDumpableObject(&(finfo[i].dobj), fout);
7054 : :
7055 : : /* Mark whether function has an ACL */
7056 [ + + ]: 4953 : if (!PQgetisnull(res, i, i_proacl))
7057 : 146 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7058 : : }
7059 : :
7060 : 191 : PQclear(res);
7061 : :
7062 : 191 : destroyPQExpBuffer(query);
7063 : 191 : }
7064 : :
7065 : : /*
7066 : : * getRelationStatistics
7067 : : * register the statistics object as a dependent of the relation.
7068 : : *
7069 : : * reltuples is passed as a string to avoid complexities in converting from/to
7070 : : * floating point.
7071 : : */
7072 : : static RelStatsInfo *
7073 : 10566 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7074 : : char *reltuples, int32 relallvisible,
7075 : : int32 relallfrozen, char relkind,
7076 : : char **indAttNames, int nindAttNames)
7077 : : {
7078 [ + + ]: 10566 : if (!fout->dopt->dumpStatistics)
7079 : 6580 : return NULL;
7080 : :
7081 [ + + + + ]: 3986 : if ((relkind == RELKIND_RELATION) ||
7082 [ + + ]: 1659 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7083 [ + + ]: 1001 : (relkind == RELKIND_INDEX) ||
7084 [ + + ]: 667 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7085 [ + + ]: 337 : (relkind == RELKIND_MATVIEW ||
7086 : : relkind == RELKIND_FOREIGN_TABLE))
7087 : : {
7088 : 3684 : RelStatsInfo *info = pg_malloc0_object(RelStatsInfo);
7089 : 3684 : DumpableObject *dobj = &info->dobj;
7090 : :
7091 : 3684 : dobj->objType = DO_REL_STATS;
7092 : 3684 : dobj->catId.tableoid = 0;
7093 : 3684 : dobj->catId.oid = 0;
7094 : 3684 : AssignDumpId(dobj);
7095 : 3684 : dobj->dependencies = pg_malloc_object(DumpId);
7096 : 3684 : dobj->dependencies[0] = rel->dumpId;
7097 : 3684 : dobj->nDeps = 1;
7098 : 3684 : dobj->allocDeps = 1;
7099 : 3684 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7100 : 3684 : dobj->name = pg_strdup(rel->name);
7101 : 3684 : dobj->namespace = rel->namespace;
7102 : 3684 : info->relid = rel->catId.oid;
7103 : 3684 : info->relpages = relpages;
7104 : 3684 : info->reltuples = pstrdup(reltuples);
7105 : 3684 : info->relallvisible = relallvisible;
7106 : 3684 : info->relallfrozen = relallfrozen;
7107 : 3684 : info->relkind = relkind;
7108 : 3684 : info->indAttNames = indAttNames;
7109 : 3684 : info->nindAttNames = nindAttNames;
7110 : :
7111 : : /*
7112 : : * Ordinarily, stats go in SECTION_DATA for tables and
7113 : : * SECTION_POST_DATA for indexes.
7114 : : *
7115 : : * However, the section may be updated later for materialized view
7116 : : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7117 : : * the stats, so the stats must be restored after the data. Also, the
7118 : : * materialized view definition may be postponed to SECTION_POST_DATA
7119 : : * (see repairMatViewBoundaryMultiLoop()).
7120 : : */
7121 [ + + - ]: 3684 : switch (info->relkind)
7122 : : {
7123 : 2692 : case RELKIND_RELATION:
7124 : : case RELKIND_PARTITIONED_TABLE:
7125 : : case RELKIND_MATVIEW:
7126 : : case RELKIND_FOREIGN_TABLE:
7127 : 2692 : info->section = SECTION_DATA;
7128 : 2692 : break;
7129 : 992 : case RELKIND_INDEX:
7130 : : case RELKIND_PARTITIONED_INDEX:
7131 : 992 : info->section = SECTION_POST_DATA;
7132 : 992 : break;
7133 : 0 : default:
7134 : 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7135 : : info->relkind);
7136 : : }
7137 : :
7138 : 3684 : return info;
7139 : : }
7140 : 302 : return NULL;
7141 : : }
7142 : :
7143 : : /*
7144 : : * getTables
7145 : : * read all the tables (no indexes) in the system catalogs,
7146 : : * and return them as an array of TableInfo structures
7147 : : *
7148 : : * *numTables is set to the number of tables read in
7149 : : */
7150 : : TableInfo *
7151 : 192 : getTables(Archive *fout, int *numTables)
7152 : : {
7153 : 192 : DumpOptions *dopt = fout->dopt;
7154 : : PGresult *res;
7155 : : int ntups;
7156 : : int i;
7157 : 192 : PQExpBuffer query = createPQExpBuffer();
7158 : : TableInfo *tblinfo;
7159 : : int i_reltableoid;
7160 : : int i_reloid;
7161 : : int i_relname;
7162 : : int i_relnamespace;
7163 : : int i_relkind;
7164 : : int i_reltype;
7165 : : int i_relowner;
7166 : : int i_relchecks;
7167 : : int i_relhasindex;
7168 : : int i_relhasrules;
7169 : : int i_relpages;
7170 : : int i_reltuples;
7171 : : int i_relallvisible;
7172 : : int i_relallfrozen;
7173 : : int i_toastpages;
7174 : : int i_owning_tab;
7175 : : int i_owning_col;
7176 : : int i_reltablespace;
7177 : : int i_relhasoids;
7178 : : int i_relhastriggers;
7179 : : int i_relpersistence;
7180 : : int i_relispopulated;
7181 : : int i_relreplident;
7182 : : int i_relrowsec;
7183 : : int i_relforcerowsec;
7184 : : int i_relfrozenxid;
7185 : : int i_toastfrozenxid;
7186 : : int i_toastoid;
7187 : : int i_relminmxid;
7188 : : int i_toastminmxid;
7189 : : int i_reloptions;
7190 : : int i_checkoption;
7191 : : int i_toastreloptions;
7192 : : int i_reloftype;
7193 : : int i_foreignserver;
7194 : : int i_amname;
7195 : : int i_is_identity_sequence;
7196 : : int i_relacl;
7197 : : int i_acldefault;
7198 : : int i_ispartition;
7199 : :
7200 : : /*
7201 : : * Find all the tables and table-like objects.
7202 : : *
7203 : : * We must fetch all tables in this phase because otherwise we cannot
7204 : : * correctly identify inherited columns, owned sequences, etc.
7205 : : *
7206 : : * We include system catalogs, so that we can work if a user table is
7207 : : * defined to inherit from a system catalog (pretty weird, but...)
7208 : : *
7209 : : * Note: in this phase we should collect only a minimal amount of
7210 : : * information about each table, basically just enough to decide if it is
7211 : : * interesting. In particular, since we do not yet have lock on any user
7212 : : * table, we MUST NOT invoke any server-side data collection functions
7213 : : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7214 : : * wrong answers if any concurrent DDL is happening.
7215 : : */
7216 : :
7217 : 192 : appendPQExpBufferStr(query,
7218 : : "SELECT c.tableoid, c.oid, c.relname, "
7219 : : "c.relnamespace, c.relkind, c.reltype, "
7220 : : "c.relowner, "
7221 : : "c.relchecks, "
7222 : : "c.relhasindex, c.relhasrules, c.relpages, "
7223 : : "c.reltuples, c.relallvisible, ");
7224 : :
7225 [ + - ]: 192 : if (fout->remoteVersion >= 180000)
7226 : 192 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7227 : : else
7228 : 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7229 : :
7230 : 192 : appendPQExpBufferStr(query,
7231 : : "c.relhastriggers, c.relpersistence, "
7232 : : "c.reloftype, "
7233 : : "c.relacl, "
7234 : : "acldefault(CASE"
7235 : : " WHEN c.relkind = " CppAsString2(RELKIND_PROPGRAPH));
7236 : : /* 19beta1 didn't support acldefault('g'), so we'll fix that below */
7237 : 192 : appendPQExpBufferStr(query,
7238 [ + - ]: 192 : fout->remoteVersion >= 200000 ?
7239 : : " THEN 'g'::\"char\"" :
7240 : : " THEN NULL");
7241 : 192 : appendPQExpBufferStr(query,
7242 : : " WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7243 : : " THEN 's'::\"char\""
7244 : : " ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7245 : : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7246 : : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7247 : : "ELSE 0 END AS foreignserver, "
7248 : : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7249 : : "tc.oid AS toid, "
7250 : : "tc.relpages AS toastpages, "
7251 : : "tc.reloptions AS toast_reloptions, "
7252 : : "d.refobjid AS owning_tab, "
7253 : : "d.refobjsubid AS owning_col, "
7254 : : "tsp.spcname AS reltablespace, ");
7255 : :
7256 [ + - ]: 192 : if (fout->remoteVersion >= 120000)
7257 : 192 : appendPQExpBufferStr(query,
7258 : : "false AS relhasoids, ");
7259 : : else
7260 : 0 : appendPQExpBufferStr(query,
7261 : : "c.relhasoids, ");
7262 : :
7263 : 192 : appendPQExpBufferStr(query,
7264 : : "c.relispopulated, ");
7265 : :
7266 : 192 : appendPQExpBufferStr(query,
7267 : : "c.relreplident, ");
7268 : :
7269 : 192 : appendPQExpBufferStr(query,
7270 : : "c.relrowsecurity, c.relforcerowsecurity, ");
7271 : :
7272 : 192 : appendPQExpBufferStr(query,
7273 : : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7274 : :
7275 : 192 : appendPQExpBufferStr(query,
7276 : : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7277 : : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7278 : : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7279 : :
7280 : 192 : appendPQExpBufferStr(query,
7281 : : "am.amname, ");
7282 : :
7283 : 192 : appendPQExpBufferStr(query,
7284 : : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7285 : :
7286 : 192 : appendPQExpBufferStr(query,
7287 : : "c.relispartition AS ispartition ");
7288 : :
7289 : : /*
7290 : : * Left join to pg_depend to pick up dependency info linking sequences to
7291 : : * their owning column, if any (note this dependency is AUTO except for
7292 : : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7293 : : * collect the spcname.
7294 : : */
7295 : 192 : appendPQExpBufferStr(query,
7296 : : "\nFROM pg_class c\n"
7297 : : "LEFT JOIN pg_depend d ON "
7298 : : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7299 : : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7300 : : "d.objsubid = 0 AND "
7301 : : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7302 : : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7303 : :
7304 : : /*
7305 : : * Left join to pg_am to pick up the amname.
7306 : : */
7307 : 192 : appendPQExpBufferStr(query,
7308 : : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7309 : :
7310 : : /*
7311 : : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7312 : : * that versions 10 and 11 have them, but later versions do not, so
7313 : : * emitting them causes the upgrade to fail.
7314 : : */
7315 : 192 : appendPQExpBufferStr(query,
7316 : : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7317 : : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7318 : : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7319 : :
7320 : : /*
7321 : : * Restrict to interesting relkinds (in particular, not indexes). Not all
7322 : : * relkinds are possible in older servers, but it's not worth the trouble
7323 : : * to emit a version-dependent list.
7324 : : *
7325 : : * Composite-type table entries won't be dumped as such, but we have to
7326 : : * make a DumpableObject for them so that we can track dependencies of the
7327 : : * composite type (pg_depend entries for columns of the composite type
7328 : : * link to the pg_class entry not the pg_type entry).
7329 : : */
7330 : 192 : appendPQExpBufferStr(query,
7331 : : "WHERE c.relkind IN ("
7332 : : CppAsString2(RELKIND_RELATION) ", "
7333 : : CppAsString2(RELKIND_SEQUENCE) ", "
7334 : : CppAsString2(RELKIND_VIEW) ", "
7335 : : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7336 : : CppAsString2(RELKIND_MATVIEW) ", "
7337 : : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7338 : : CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
7339 : : CppAsString2(RELKIND_PROPGRAPH) ")\n"
7340 : : "ORDER BY c.oid");
7341 : :
7342 : 192 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7343 : :
7344 : 192 : ntups = PQntuples(res);
7345 : :
7346 : 192 : *numTables = ntups;
7347 : :
7348 : : /*
7349 : : * Extract data from result and lock dumpable tables. We do the locking
7350 : : * before anything else, to minimize the window wherein a table could
7351 : : * disappear under us.
7352 : : *
7353 : : * Note that we have to save info about all tables here, even when dumping
7354 : : * only one, because we don't yet know which tables might be inheritance
7355 : : * ancestors of the target table.
7356 : : */
7357 : 192 : tblinfo = pg_malloc0_array(TableInfo, ntups);
7358 : :
7359 : 192 : i_reltableoid = PQfnumber(res, "tableoid");
7360 : 192 : i_reloid = PQfnumber(res, "oid");
7361 : 192 : i_relname = PQfnumber(res, "relname");
7362 : 192 : i_relnamespace = PQfnumber(res, "relnamespace");
7363 : 192 : i_relkind = PQfnumber(res, "relkind");
7364 : 192 : i_reltype = PQfnumber(res, "reltype");
7365 : 192 : i_relowner = PQfnumber(res, "relowner");
7366 : 192 : i_relchecks = PQfnumber(res, "relchecks");
7367 : 192 : i_relhasindex = PQfnumber(res, "relhasindex");
7368 : 192 : i_relhasrules = PQfnumber(res, "relhasrules");
7369 : 192 : i_relpages = PQfnumber(res, "relpages");
7370 : 192 : i_reltuples = PQfnumber(res, "reltuples");
7371 : 192 : i_relallvisible = PQfnumber(res, "relallvisible");
7372 : 192 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7373 : 192 : i_toastpages = PQfnumber(res, "toastpages");
7374 : 192 : i_owning_tab = PQfnumber(res, "owning_tab");
7375 : 192 : i_owning_col = PQfnumber(res, "owning_col");
7376 : 192 : i_reltablespace = PQfnumber(res, "reltablespace");
7377 : 192 : i_relhasoids = PQfnumber(res, "relhasoids");
7378 : 192 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7379 : 192 : i_relpersistence = PQfnumber(res, "relpersistence");
7380 : 192 : i_relispopulated = PQfnumber(res, "relispopulated");
7381 : 192 : i_relreplident = PQfnumber(res, "relreplident");
7382 : 192 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7383 : 192 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7384 : 192 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7385 : 192 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7386 : 192 : i_toastoid = PQfnumber(res, "toid");
7387 : 192 : i_relminmxid = PQfnumber(res, "relminmxid");
7388 : 192 : i_toastminmxid = PQfnumber(res, "tminmxid");
7389 : 192 : i_reloptions = PQfnumber(res, "reloptions");
7390 : 192 : i_checkoption = PQfnumber(res, "checkoption");
7391 : 192 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7392 : 192 : i_reloftype = PQfnumber(res, "reloftype");
7393 : 192 : i_foreignserver = PQfnumber(res, "foreignserver");
7394 : 192 : i_amname = PQfnumber(res, "amname");
7395 : 192 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7396 : 192 : i_relacl = PQfnumber(res, "relacl");
7397 : 192 : i_acldefault = PQfnumber(res, "acldefault");
7398 : 192 : i_ispartition = PQfnumber(res, "ispartition");
7399 : :
7400 [ + + ]: 192 : if (dopt->lockWaitTimeout)
7401 : : {
7402 : : /*
7403 : : * Arrange to fail instead of waiting forever for a table lock.
7404 : : *
7405 : : * NB: this coding assumes that the only queries issued within the
7406 : : * following loop are LOCK TABLEs; else the timeout may be undesirably
7407 : : * applied to other things too.
7408 : : */
7409 : 2 : resetPQExpBuffer(query);
7410 : 2 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7411 : 2 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7412 : 2 : ExecuteSqlStatement(fout, query->data);
7413 : : }
7414 : :
7415 : 192 : resetPQExpBuffer(query);
7416 : :
7417 [ + + ]: 55480 : for (i = 0; i < ntups; i++)
7418 : : {
7419 : 55288 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7420 : 55288 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7421 : :
7422 : 55288 : tblinfo[i].dobj.objType = DO_TABLE;
7423 : 55288 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7424 : 55288 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7425 : 55288 : AssignDumpId(&tblinfo[i].dobj);
7426 : 55288 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7427 : 110576 : tblinfo[i].dobj.namespace =
7428 : 55288 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7429 : 55288 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7430 : : /* acldefault computed below */
7431 : 55288 : tblinfo[i].dacl.privtype = 0;
7432 : 55288 : tblinfo[i].dacl.initprivs = NULL;
7433 : 55288 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7434 : 55288 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7435 : 55288 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7436 : 55288 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7437 : 55288 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7438 : 55288 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7439 : 55288 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7440 [ + + ]: 55288 : if (PQgetisnull(res, i, i_toastpages))
7441 : 44892 : tblinfo[i].toastpages = 0;
7442 : : else
7443 : 10396 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7444 [ + + ]: 55288 : if (PQgetisnull(res, i, i_owning_tab))
7445 : : {
7446 : 54864 : tblinfo[i].owning_tab = InvalidOid;
7447 : 54864 : tblinfo[i].owning_col = 0;
7448 : : }
7449 : : else
7450 : : {
7451 : 424 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7452 : 424 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7453 : : }
7454 : 55288 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7455 : 55288 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7456 : 55288 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7457 : 55288 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7458 : 55288 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7459 : 55288 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7460 : 55288 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7461 : 55288 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7462 : 55288 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7463 : 55288 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7464 : 55288 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7465 : 55288 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7466 : 55288 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7467 : 55288 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7468 [ + + ]: 55288 : if (PQgetisnull(res, i, i_checkoption))
7469 : 55239 : tblinfo[i].checkoption = NULL;
7470 : : else
7471 : 49 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7472 : 55288 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7473 : 55288 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7474 : 55288 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7475 [ + + ]: 55288 : if (PQgetisnull(res, i, i_amname))
7476 : 33568 : tblinfo[i].amname = NULL;
7477 : : else
7478 : 21720 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7479 : 55288 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7480 : 55288 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7481 : :
7482 [ + + ]: 55288 : if (tblinfo[i].relkind == RELKIND_PROPGRAPH &&
7483 [ - + ]: 147 : !(fout->remoteVersion >= 200000))
7484 : 0 : {
7485 : 0 : PQExpBuffer aclarray = createPQExpBuffer();
7486 : 0 : PQExpBuffer aclitem = createPQExpBuffer();
7487 : :
7488 : : /* Standard ACL as of v19 is {owner=r/owner} */
7489 : 0 : appendPQExpBufferChar(aclarray, '{');
7490 : 0 : quoteAclUserName(aclitem, tblinfo[i].rolname);
7491 : 0 : appendPQExpBufferStr(aclitem, "=r/");
7492 : 0 : quoteAclUserName(aclitem, tblinfo[i].rolname);
7493 : 0 : appendPGArray(aclarray, aclitem->data);
7494 : 0 : appendPQExpBufferChar(aclarray, '}');
7495 : :
7496 : 0 : tblinfo[i].dacl.acldefault = pstrdup(aclarray->data);
7497 : :
7498 : 0 : destroyPQExpBuffer(aclarray);
7499 : 0 : destroyPQExpBuffer(aclitem);
7500 : : }
7501 : : else
7502 : 55288 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7503 : :
7504 : : /* other fields were zeroed above */
7505 : :
7506 : : /*
7507 : : * Decide whether we want to dump this table.
7508 : : */
7509 [ + + ]: 55288 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7510 : 186 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7511 : : else
7512 : 55102 : selectDumpableTable(&tblinfo[i], fout);
7513 : :
7514 : : /*
7515 : : * Now, consider the table "interesting" if we need to dump its
7516 : : * definition, data or its statistics. Later on, we'll skip a lot of
7517 : : * data collection for uninteresting tables.
7518 : : *
7519 : : * Note: the "interesting" flag will also be set by flagInhTables for
7520 : : * parents of interesting tables, so that we collect necessary
7521 : : * inheritance info even when the parents are not themselves being
7522 : : * dumped. This is the main reason why we need an "interesting" flag
7523 : : * that's separate from the components-to-dump bitmask.
7524 : : */
7525 : 55288 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7526 : : (DUMP_COMPONENT_DEFINITION |
7527 : : DUMP_COMPONENT_DATA |
7528 : 55288 : DUMP_COMPONENT_STATISTICS)) != 0;
7529 : :
7530 : 55288 : tblinfo[i].dummy_view = false; /* might get set during sort */
7531 : 55288 : tblinfo[i].postponed_def = false; /* might get set during sort */
7532 : :
7533 : : /* Tables have data */
7534 : 55288 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7535 : :
7536 : : /* Mark whether table has an ACL */
7537 [ + + ]: 55288 : if (!PQgetisnull(res, i, i_relacl))
7538 : 44645 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7539 : 55288 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7540 : :
7541 : : /* Add statistics */
7542 [ + + ]: 55288 : if (tblinfo[i].interesting)
7543 : : {
7544 : : RelStatsInfo *stats;
7545 : :
7546 : 15440 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7547 : 7720 : tblinfo[i].relpages,
7548 : : PQgetvalue(res, i, i_reltuples),
7549 : : relallvisible, relallfrozen,
7550 : 7720 : tblinfo[i].relkind, NULL, 0);
7551 [ + + ]: 7720 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7552 : 425 : tblinfo[i].stats = stats;
7553 : : }
7554 : :
7555 : : /*
7556 : : * Read-lock target tables to make sure they aren't DROPPED or altered
7557 : : * in schema before we get around to dumping them.
7558 : : *
7559 : : * Note that we don't explicitly lock parents of the target tables; we
7560 : : * assume our lock on the child is enough to prevent schema
7561 : : * alterations to parent tables.
7562 : : *
7563 : : * NOTE: it'd be kinda nice to lock other relations too, not only
7564 : : * plain or partitioned tables, but the backend doesn't presently
7565 : : * allow that.
7566 : : *
7567 : : * We only need to lock the table for certain components; see
7568 : : * pg_dump.h
7569 : : */
7570 [ + + ]: 55288 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7571 [ + + ]: 7720 : (tblinfo[i].relkind == RELKIND_RELATION ||
7572 [ + + ]: 2202 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7573 : : {
7574 : : /*
7575 : : * Tables are locked in batches. When dumping from a remote
7576 : : * server this can save a significant amount of time by reducing
7577 : : * the number of round trips.
7578 : : */
7579 [ + + ]: 6153 : if (query->len == 0)
7580 : 127 : appendPQExpBuffer(query, "LOCK TABLE %s",
7581 : 127 : fmtQualifiedDumpable(&tblinfo[i]));
7582 : : else
7583 : : {
7584 : 6026 : appendPQExpBuffer(query, ", %s",
7585 : 6026 : fmtQualifiedDumpable(&tblinfo[i]));
7586 : :
7587 : : /* Arbitrarily end a batch when query length reaches 100K. */
7588 [ - + ]: 6026 : if (query->len >= 100000)
7589 : : {
7590 : : /* Lock another batch of tables. */
7591 : 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7592 : 0 : ExecuteSqlStatement(fout, query->data);
7593 : 0 : resetPQExpBuffer(query);
7594 : : }
7595 : : }
7596 : : }
7597 : : }
7598 : :
7599 [ + + ]: 192 : if (query->len != 0)
7600 : : {
7601 : : /* Lock the tables in the last batch. */
7602 : 127 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7603 : 127 : ExecuteSqlStatement(fout, query->data);
7604 : : }
7605 : :
7606 [ + + ]: 191 : if (dopt->lockWaitTimeout)
7607 : : {
7608 : 2 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7609 : : }
7610 : :
7611 : 191 : PQclear(res);
7612 : :
7613 : 191 : destroyPQExpBuffer(query);
7614 : :
7615 : 191 : return tblinfo;
7616 : : }
7617 : :
7618 : : /*
7619 : : * getOwnedSeqs
7620 : : * identify owned sequences and mark them as dumpable if owning table is
7621 : : *
7622 : : * We used to do this in getTables(), but it's better to do it after the
7623 : : * index used by findTableByOid() has been set up.
7624 : : */
7625 : : void
7626 : 191 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7627 : : {
7628 : : int i;
7629 : :
7630 : : /*
7631 : : * Force sequences that are "owned" by table columns to be dumped whenever
7632 : : * their owning table is being dumped.
7633 : : */
7634 [ + + ]: 55184 : for (i = 0; i < numTables; i++)
7635 : : {
7636 : 54993 : TableInfo *seqinfo = &tblinfo[i];
7637 : : TableInfo *owning_tab;
7638 : :
7639 [ + + ]: 54993 : if (!OidIsValid(seqinfo->owning_tab))
7640 : 54572 : continue; /* not an owned sequence */
7641 : :
7642 : 421 : owning_tab = findTableByOid(seqinfo->owning_tab);
7643 [ - + ]: 421 : if (owning_tab == NULL)
7644 : 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7645 : : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7646 : :
7647 : : /*
7648 : : * For an identity sequence, dump exactly the same components for the
7649 : : * sequence as for the owning table. This is important because we
7650 : : * treat the identity sequence as an integral part of the table. For
7651 : : * example, there is not any DDL command that allows creation of such
7652 : : * a sequence independently of the table.
7653 : : *
7654 : : * For other owned sequences such as serial sequences, we need to dump
7655 : : * the components that are being dumped for the table and any
7656 : : * components that the sequence is explicitly marked with.
7657 : : *
7658 : : * We can't simply use the set of components which are being dumped
7659 : : * for the table as the table might be in an extension (and only the
7660 : : * non-extension components, eg: ACLs if changed, security labels, and
7661 : : * policies, are being dumped) while the sequence is not (and
7662 : : * therefore the definition and other components should also be
7663 : : * dumped).
7664 : : *
7665 : : * If the sequence is part of the extension then it should be properly
7666 : : * marked by checkExtensionMembership() and this will be a no-op as
7667 : : * the table will be equivalently marked.
7668 : : */
7669 [ + + ]: 421 : if (seqinfo->is_identity_sequence)
7670 : 202 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7671 : : else
7672 : 219 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7673 : :
7674 : : /* Make sure that necessary data is available if we're dumping it */
7675 [ + + ]: 421 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7676 : : {
7677 : 325 : seqinfo->interesting = true;
7678 : 325 : owning_tab->interesting = true;
7679 : : }
7680 : : }
7681 : 191 : }
7682 : :
7683 : : /*
7684 : : * getInherits
7685 : : * read all the inheritance information
7686 : : * from the system catalogs return them in the InhInfo* structure
7687 : : *
7688 : : * numInherits is set to the number of pairs read in
7689 : : */
7690 : : InhInfo *
7691 : 191 : getInherits(Archive *fout, int *numInherits)
7692 : : {
7693 : : PGresult *res;
7694 : : int ntups;
7695 : : int i;
7696 : 191 : PQExpBuffer query = createPQExpBuffer();
7697 : : InhInfo *inhinfo;
7698 : :
7699 : : int i_inhrelid;
7700 : : int i_inhparent;
7701 : :
7702 : : /* find all the inheritance information */
7703 : 191 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7704 : :
7705 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7706 : :
7707 : 191 : ntups = PQntuples(res);
7708 : :
7709 : 191 : *numInherits = ntups;
7710 : :
7711 : 191 : inhinfo = pg_malloc_array(InhInfo, ntups);
7712 : :
7713 : 191 : i_inhrelid = PQfnumber(res, "inhrelid");
7714 : 191 : i_inhparent = PQfnumber(res, "inhparent");
7715 : :
7716 [ + + ]: 3819 : for (i = 0; i < ntups; i++)
7717 : : {
7718 : 3628 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7719 : 3628 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7720 : : }
7721 : :
7722 : 191 : PQclear(res);
7723 : :
7724 : 191 : destroyPQExpBuffer(query);
7725 : :
7726 : 191 : return inhinfo;
7727 : : }
7728 : :
7729 : : /*
7730 : : * getPartitioningInfo
7731 : : * get information about partitioning
7732 : : *
7733 : : * For the most part, we only collect partitioning info about tables we
7734 : : * intend to dump. However, this function has to consider all partitioned
7735 : : * tables in the database, because we need to know about parents of partitions
7736 : : * we are going to dump even if the parents themselves won't be dumped.
7737 : : *
7738 : : * Specifically, what we need to know is whether each partitioned table
7739 : : * has an "unsafe" partitioning scheme that requires us to force
7740 : : * load-via-partition-root mode for its children. Currently the only case
7741 : : * for which we force that is hash partitioning on enum columns, since the
7742 : : * hash codes depend on enum value OIDs which won't be replicated across
7743 : : * dump-and-reload. There are other cases in which load-via-partition-root
7744 : : * might be necessary, but we expect users to cope with them.
7745 : : */
7746 : : void
7747 : 191 : getPartitioningInfo(Archive *fout)
7748 : : {
7749 : : PQExpBuffer query;
7750 : : PGresult *res;
7751 : : int ntups;
7752 : :
7753 : : /* hash partitioning didn't exist before v11 */
7754 [ - + ]: 191 : if (fout->remoteVersion < 110000)
7755 : 0 : return;
7756 : : /* needn't bother if not dumping data */
7757 [ + + ]: 191 : if (!fout->dopt->dumpData)
7758 : 45 : return;
7759 : :
7760 : 146 : query = createPQExpBuffer();
7761 : :
7762 : : /*
7763 : : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7764 : : * appears among the partition opclasses. We needn't check partstrat.
7765 : : *
7766 : : * Note that this query may well retrieve info about tables we aren't
7767 : : * going to dump and hence have no lock on. That's okay since we need not
7768 : : * invoke any unsafe server-side functions.
7769 : : */
7770 : 146 : appendPQExpBufferStr(query,
7771 : : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7772 : : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7773 : : "ON c.opcmethod = a.oid\n"
7774 : : "WHERE opcname = 'enum_ops' "
7775 : : "AND opcnamespace = 'pg_catalog'::regnamespace "
7776 : : "AND amname = 'hash') = ANY(partclass)");
7777 : :
7778 : 146 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7779 : :
7780 : 146 : ntups = PQntuples(res);
7781 : :
7782 [ + + ]: 191 : for (int i = 0; i < ntups; i++)
7783 : : {
7784 : 45 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7785 : : TableInfo *tbinfo;
7786 : :
7787 : 45 : tbinfo = findTableByOid(tabrelid);
7788 [ - + ]: 45 : if (tbinfo == NULL)
7789 : 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7790 : : tabrelid);
7791 : 45 : tbinfo->unsafe_partitions = true;
7792 : : }
7793 : :
7794 : 146 : PQclear(res);
7795 : :
7796 : 146 : destroyPQExpBuffer(query);
7797 : : }
7798 : :
7799 : : /*
7800 : : * getIndexes
7801 : : * get information about every index on a dumpable table
7802 : : *
7803 : : * Note: index data is not returned directly to the caller, but it
7804 : : * does get entered into the DumpableObject tables.
7805 : : */
7806 : : void
7807 : 191 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7808 : : {
7809 : 191 : PQExpBuffer query = createPQExpBuffer();
7810 : 191 : PQExpBuffer tbloids = createPQExpBuffer();
7811 : : PGresult *res;
7812 : : int ntups;
7813 : : int curtblindx;
7814 : : IndxInfo *indxinfo;
7815 : : int i_tableoid,
7816 : : i_oid,
7817 : : i_indrelid,
7818 : : i_indexname,
7819 : : i_relpages,
7820 : : i_reltuples,
7821 : : i_relallvisible,
7822 : : i_relallfrozen,
7823 : : i_parentidx,
7824 : : i_indexdef,
7825 : : i_indnkeyatts,
7826 : : i_indnatts,
7827 : : i_indkey,
7828 : : i_indisclustered,
7829 : : i_indisreplident,
7830 : : i_indnullsnotdistinct,
7831 : : i_contype,
7832 : : i_conname,
7833 : : i_condeferrable,
7834 : : i_condeferred,
7835 : : i_conperiod,
7836 : : i_contableoid,
7837 : : i_conoid,
7838 : : i_condef,
7839 : : i_indattnames,
7840 : : i_tablespace,
7841 : : i_indreloptions,
7842 : : i_indstatcols,
7843 : : i_indstatvals;
7844 : :
7845 : : /*
7846 : : * We want to perform just one query against pg_index. However, we
7847 : : * mustn't try to select every row of the catalog and then sort it out on
7848 : : * the client side, because some of the server-side functions we need
7849 : : * would be unsafe to apply to tables we don't have lock on. Hence, we
7850 : : * build an array of the OIDs of tables we care about (and now have lock
7851 : : * on!), and use a WHERE clause to constrain which rows are selected.
7852 : : */
7853 : 191 : appendPQExpBufferChar(tbloids, '{');
7854 [ + + ]: 55184 : for (int i = 0; i < numTables; i++)
7855 : : {
7856 : 54993 : TableInfo *tbinfo = &tblinfo[i];
7857 : :
7858 [ + + ]: 54993 : if (!tbinfo->hasindex)
7859 : 39181 : continue;
7860 : :
7861 : : /*
7862 : : * We can ignore indexes of uninteresting tables.
7863 : : */
7864 [ + + ]: 15812 : if (!tbinfo->interesting)
7865 : 13615 : continue;
7866 : :
7867 : : /* OK, we need info for this table */
7868 [ + + ]: 2197 : if (tbloids->len > 1) /* do we have more than the '{'? */
7869 : 2115 : appendPQExpBufferChar(tbloids, ',');
7870 : 2197 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7871 : : }
7872 : 191 : appendPQExpBufferChar(tbloids, '}');
7873 : :
7874 : 191 : appendPQExpBufferStr(query,
7875 : : "SELECT t.tableoid, t.oid, i.indrelid, "
7876 : : "t.relname AS indexname, "
7877 : : "t.relpages, t.reltuples, t.relallvisible, ");
7878 : :
7879 [ + - ]: 191 : if (fout->remoteVersion >= 180000)
7880 : 191 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7881 : : else
7882 : 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7883 : :
7884 : 191 : appendPQExpBufferStr(query,
7885 : : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7886 : : "i.indkey, i.indisclustered, "
7887 : : "c.contype, c.conname, "
7888 : : "c.condeferrable, c.condeferred, "
7889 : : "c.tableoid AS contableoid, "
7890 : : "c.oid AS conoid, "
7891 : : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7892 : : "CASE WHEN i.indexprs IS NOT NULL THEN "
7893 : : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7894 : : " FROM pg_catalog.pg_attribute "
7895 : : " WHERE attrelid = i.indexrelid) "
7896 : : "ELSE NULL END AS indattnames, "
7897 : : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7898 : : "t.reloptions AS indreloptions, ");
7899 : :
7900 : :
7901 : 191 : appendPQExpBufferStr(query,
7902 : : "i.indisreplident, ");
7903 : :
7904 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
7905 : 191 : appendPQExpBufferStr(query,
7906 : : "inh.inhparent AS parentidx, "
7907 : : "i.indnkeyatts AS indnkeyatts, "
7908 : : "i.indnatts AS indnatts, "
7909 : : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7910 : : " FROM pg_catalog.pg_attribute "
7911 : : " WHERE attrelid = i.indexrelid AND "
7912 : : " attstattarget >= 0) AS indstatcols, "
7913 : : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7914 : : " FROM pg_catalog.pg_attribute "
7915 : : " WHERE attrelid = i.indexrelid AND "
7916 : : " attstattarget >= 0) AS indstatvals, ");
7917 : : else
7918 : 0 : appendPQExpBufferStr(query,
7919 : : "0 AS parentidx, "
7920 : : "i.indnatts AS indnkeyatts, "
7921 : : "i.indnatts AS indnatts, "
7922 : : "'' AS indstatcols, "
7923 : : "'' AS indstatvals, ");
7924 : :
7925 [ + - ]: 191 : if (fout->remoteVersion >= 150000)
7926 : 191 : appendPQExpBufferStr(query,
7927 : : "i.indnullsnotdistinct, ");
7928 : : else
7929 : 0 : appendPQExpBufferStr(query,
7930 : : "false AS indnullsnotdistinct, ");
7931 : :
7932 [ + - ]: 191 : if (fout->remoteVersion >= 180000)
7933 : 191 : appendPQExpBufferStr(query,
7934 : : "c.conperiod ");
7935 : : else
7936 : 0 : appendPQExpBufferStr(query,
7937 : : "NULL AS conperiod ");
7938 : :
7939 : : /*
7940 : : * The point of the messy-looking outer join is to find a constraint that
7941 : : * is related by an internal dependency link to the index. If we find one,
7942 : : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7943 : : * index won't have more than one internal dependency.
7944 : : *
7945 : : * Note: the check on conrelid is redundant, but useful because that
7946 : : * column is indexed while conindid is not.
7947 : : */
7948 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
7949 : : {
7950 : 191 : appendPQExpBuffer(query,
7951 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7952 : : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7953 : : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7954 : : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7955 : : "LEFT JOIN pg_catalog.pg_constraint c "
7956 : : "ON (i.indrelid = c.conrelid AND "
7957 : : "i.indexrelid = c.conindid AND "
7958 : : "c.contype IN ('p','u','x')) "
7959 : : "LEFT JOIN pg_catalog.pg_inherits inh "
7960 : : "ON (inh.inhrelid = indexrelid) "
7961 : : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7962 : : "AND i.indisready "
7963 : : "ORDER BY i.indrelid, indexname",
7964 : : tbloids->data);
7965 : : }
7966 : : else
7967 : : {
7968 : : /*
7969 : : * the test on indisready is necessary in 9.2, and harmless in
7970 : : * earlier/later versions
7971 : : */
7972 : 0 : appendPQExpBuffer(query,
7973 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7974 : : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7975 : : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7976 : : "LEFT JOIN pg_catalog.pg_constraint c "
7977 : : "ON (i.indrelid = c.conrelid AND "
7978 : : "i.indexrelid = c.conindid AND "
7979 : : "c.contype IN ('p','u','x')) "
7980 : : "WHERE i.indisvalid AND i.indisready "
7981 : : "ORDER BY i.indrelid, indexname",
7982 : : tbloids->data);
7983 : : }
7984 : :
7985 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7986 : :
7987 : 191 : ntups = PQntuples(res);
7988 : :
7989 : 191 : i_tableoid = PQfnumber(res, "tableoid");
7990 : 191 : i_oid = PQfnumber(res, "oid");
7991 : 191 : i_indrelid = PQfnumber(res, "indrelid");
7992 : 191 : i_indexname = PQfnumber(res, "indexname");
7993 : 191 : i_relpages = PQfnumber(res, "relpages");
7994 : 191 : i_reltuples = PQfnumber(res, "reltuples");
7995 : 191 : i_relallvisible = PQfnumber(res, "relallvisible");
7996 : 191 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7997 : 191 : i_parentidx = PQfnumber(res, "parentidx");
7998 : 191 : i_indexdef = PQfnumber(res, "indexdef");
7999 : 191 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8000 : 191 : i_indnatts = PQfnumber(res, "indnatts");
8001 : 191 : i_indkey = PQfnumber(res, "indkey");
8002 : 191 : i_indisclustered = PQfnumber(res, "indisclustered");
8003 : 191 : i_indisreplident = PQfnumber(res, "indisreplident");
8004 : 191 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8005 : 191 : i_contype = PQfnumber(res, "contype");
8006 : 191 : i_conname = PQfnumber(res, "conname");
8007 : 191 : i_condeferrable = PQfnumber(res, "condeferrable");
8008 : 191 : i_condeferred = PQfnumber(res, "condeferred");
8009 : 191 : i_conperiod = PQfnumber(res, "conperiod");
8010 : 191 : i_contableoid = PQfnumber(res, "contableoid");
8011 : 191 : i_conoid = PQfnumber(res, "conoid");
8012 : 191 : i_condef = PQfnumber(res, "condef");
8013 : 191 : i_indattnames = PQfnumber(res, "indattnames");
8014 : 191 : i_tablespace = PQfnumber(res, "tablespace");
8015 : 191 : i_indreloptions = PQfnumber(res, "indreloptions");
8016 : 191 : i_indstatcols = PQfnumber(res, "indstatcols");
8017 : 191 : i_indstatvals = PQfnumber(res, "indstatvals");
8018 : :
8019 : 191 : indxinfo = pg_malloc_array(IndxInfo, ntups);
8020 : :
8021 : : /*
8022 : : * Outer loop iterates once per table, not once per row. Incrementing of
8023 : : * j is handled by the inner loop.
8024 : : */
8025 : 191 : curtblindx = -1;
8026 [ + + ]: 2376 : for (int j = 0; j < ntups;)
8027 : : {
8028 : 2185 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8029 : 2185 : TableInfo *tbinfo = NULL;
8030 : : int numinds;
8031 : :
8032 : : /* Count rows for this table */
8033 [ + + ]: 2846 : for (numinds = 1; numinds < ntups - j; numinds++)
8034 [ + + ]: 2764 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8035 : 2103 : break;
8036 : :
8037 : : /*
8038 : : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8039 : : * order.
8040 : : */
8041 [ + - ]: 26239 : while (++curtblindx < numTables)
8042 : : {
8043 : 26239 : tbinfo = &tblinfo[curtblindx];
8044 [ + + ]: 26239 : if (tbinfo->dobj.catId.oid == indrelid)
8045 : 2185 : break;
8046 : : }
8047 [ - + ]: 2185 : if (curtblindx >= numTables)
8048 : 0 : pg_fatal("unrecognized table OID %u", indrelid);
8049 : : /* cross-check that we only got requested tables */
8050 [ + - ]: 2185 : if (!tbinfo->hasindex ||
8051 [ - + ]: 2185 : !tbinfo->interesting)
8052 : 0 : pg_fatal("unexpected index data for table \"%s\"",
8053 : : tbinfo->dobj.name);
8054 : :
8055 : : /* Save data for this table */
8056 : 2185 : tbinfo->indexes = indxinfo + j;
8057 : 2185 : tbinfo->numIndexes = numinds;
8058 : :
8059 [ + + ]: 5031 : for (int c = 0; c < numinds; c++, j++)
8060 : : {
8061 : : char contype;
8062 : : char indexkind;
8063 : 2846 : char **indAttNames = NULL;
8064 : 2846 : int nindAttNames = 0;
8065 : : RelStatsInfo *relstats;
8066 : 2846 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8067 : 2846 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8068 : 2846 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8069 : :
8070 : 2846 : indxinfo[j].dobj.objType = DO_INDEX;
8071 : 2846 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8072 : 2846 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8073 : 2846 : AssignDumpId(&indxinfo[j].dobj);
8074 : 2846 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8075 : 2846 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8076 : 2846 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8077 : 2846 : indxinfo[j].indextable = tbinfo;
8078 : 2846 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8079 : 2846 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8080 : 2846 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8081 : 2846 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8082 : 2846 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8083 : 2846 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8084 : 2846 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8085 : 2846 : indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8086 : 2846 : parseOidArray(PQgetvalue(res, j, i_indkey),
8087 : 2846 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8088 : 2846 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8089 : 2846 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8090 : 2846 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8091 : 2846 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8092 : 2846 : indxinfo[j].partattaches = (SimplePtrList)
8093 : : {
8094 : : NULL, NULL
8095 : : };
8096 : :
8097 [ + + ]: 2846 : if (indxinfo[j].parentidx == 0)
8098 : 2236 : indexkind = RELKIND_INDEX;
8099 : : else
8100 : 610 : indexkind = RELKIND_PARTITIONED_INDEX;
8101 : :
8102 [ + + ]: 2846 : if (!PQgetisnull(res, j, i_indattnames))
8103 : : {
8104 [ - + ]: 162 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8105 : : &indAttNames, &nindAttNames))
8106 : 0 : pg_fatal("could not parse %s array", "indattnames");
8107 : : }
8108 : :
8109 : 2846 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8110 : : PQgetvalue(res, j, i_reltuples),
8111 : : relallvisible, relallfrozen, indexkind,
8112 : : indAttNames, nindAttNames);
8113 : :
8114 : 2846 : contype = *(PQgetvalue(res, j, i_contype));
8115 [ + + + + : 2846 : if (contype == 'p' || contype == 'u' || contype == 'x')
+ + ]
8116 : 1714 : {
8117 : : /*
8118 : : * If we found a constraint matching the index, create an
8119 : : * entry for it.
8120 : : */
8121 : : ConstraintInfo *constrinfo;
8122 : :
8123 : 1714 : constrinfo = pg_malloc_object(ConstraintInfo);
8124 : 1714 : constrinfo->dobj.objType = DO_CONSTRAINT;
8125 : 1714 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8126 : 1714 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8127 : 1714 : AssignDumpId(&constrinfo->dobj);
8128 : 1714 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8129 : 1714 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8130 : 1714 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8131 : 1714 : constrinfo->contable = tbinfo;
8132 : 1714 : constrinfo->condomain = NULL;
8133 : 1714 : constrinfo->contype = contype;
8134 [ + + ]: 1714 : if (contype == 'x')
8135 : 10 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8136 : : else
8137 : 1704 : constrinfo->condef = NULL;
8138 : 1714 : constrinfo->confrelid = InvalidOid;
8139 : 1714 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8140 : 1714 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8141 : 1714 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8142 : 1714 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8143 : 1714 : constrinfo->conislocal = true;
8144 : 1714 : constrinfo->separate = true;
8145 : :
8146 : 1714 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8147 [ + + ]: 1714 : if (relstats != NULL)
8148 : 589 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8149 : : }
8150 : : else
8151 : : {
8152 : : /* Plain secondary index */
8153 : 1132 : indxinfo[j].indexconstraint = 0;
8154 : : }
8155 : : }
8156 : : }
8157 : :
8158 : 191 : PQclear(res);
8159 : :
8160 : 191 : destroyPQExpBuffer(query);
8161 : 191 : destroyPQExpBuffer(tbloids);
8162 : 191 : }
8163 : :
8164 : : /*
8165 : : * getExtendedStatistics
8166 : : * get information about extended-statistics objects.
8167 : : *
8168 : : * Note: extended statistics data is not returned directly to the caller, but
8169 : : * it does get entered into the DumpableObject tables.
8170 : : */
8171 : : void
8172 : 191 : getExtendedStatistics(Archive *fout)
8173 : : {
8174 : : PQExpBuffer query;
8175 : : PGresult *res;
8176 : : StatsExtInfo *statsextinfo;
8177 : : int ntups;
8178 : : int i_tableoid;
8179 : : int i_oid;
8180 : : int i_stxname;
8181 : : int i_stxnamespace;
8182 : : int i_stxowner;
8183 : : int i_stxrelid;
8184 : : int i_stattarget;
8185 : : int i;
8186 : :
8187 : 191 : query = createPQExpBuffer();
8188 : :
8189 [ - + ]: 191 : if (fout->remoteVersion < 130000)
8190 : 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8191 : : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8192 : : "FROM pg_catalog.pg_statistic_ext");
8193 : : else
8194 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8195 : : "stxnamespace, stxowner, stxrelid, stxstattarget "
8196 : : "FROM pg_catalog.pg_statistic_ext");
8197 : :
8198 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8199 : :
8200 : 191 : ntups = PQntuples(res);
8201 : :
8202 : 191 : i_tableoid = PQfnumber(res, "tableoid");
8203 : 191 : i_oid = PQfnumber(res, "oid");
8204 : 191 : i_stxname = PQfnumber(res, "stxname");
8205 : 191 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8206 : 191 : i_stxowner = PQfnumber(res, "stxowner");
8207 : 191 : i_stxrelid = PQfnumber(res, "stxrelid");
8208 : 191 : i_stattarget = PQfnumber(res, "stxstattarget");
8209 : :
8210 : 191 : statsextinfo = pg_malloc_array(StatsExtInfo, ntups);
8211 : :
8212 [ + + ]: 411 : for (i = 0; i < ntups; i++)
8213 : : {
8214 : 220 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8215 : 220 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8216 : 220 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8217 : 220 : AssignDumpId(&statsextinfo[i].dobj);
8218 : 220 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8219 : 440 : statsextinfo[i].dobj.namespace =
8220 : 220 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8221 : 220 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8222 : 440 : statsextinfo[i].stattable =
8223 : 220 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8224 [ + + ]: 220 : if (PQgetisnull(res, i, i_stattarget))
8225 : 172 : statsextinfo[i].stattarget = -1;
8226 : : else
8227 : 48 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8228 : :
8229 : : /* Decide whether we want to dump it */
8230 : 220 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8231 : :
8232 [ + + ]: 220 : if (fout->dopt->dumpStatistics)
8233 : 164 : statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8234 : : }
8235 : :
8236 : 191 : PQclear(res);
8237 : 191 : destroyPQExpBuffer(query);
8238 : 191 : }
8239 : :
8240 : : /*
8241 : : * getConstraints
8242 : : *
8243 : : * Get info about constraints on dumpable tables.
8244 : : *
8245 : : * Currently handles foreign keys only.
8246 : : * Unique and primary key constraints are handled with indexes,
8247 : : * while check constraints are processed in getTableAttrs().
8248 : : */
8249 : : void
8250 : 191 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8251 : : {
8252 : 191 : PQExpBuffer query = createPQExpBuffer();
8253 : 191 : PQExpBuffer tbloids = createPQExpBuffer();
8254 : : PGresult *res;
8255 : : int ntups;
8256 : : int curtblindx;
8257 : 191 : TableInfo *tbinfo = NULL;
8258 : : ConstraintInfo *constrinfo;
8259 : : int i_contableoid,
8260 : : i_conoid,
8261 : : i_conrelid,
8262 : : i_conname,
8263 : : i_confrelid,
8264 : : i_conindid,
8265 : : i_condef;
8266 : :
8267 : : /*
8268 : : * We want to perform just one query against pg_constraint. However, we
8269 : : * mustn't try to select every row of the catalog and then sort it out on
8270 : : * the client side, because some of the server-side functions we need
8271 : : * would be unsafe to apply to tables we don't have lock on. Hence, we
8272 : : * build an array of the OIDs of tables we care about (and now have lock
8273 : : * on!), and use a WHERE clause to constrain which rows are selected.
8274 : : */
8275 : 191 : appendPQExpBufferChar(tbloids, '{');
8276 [ + + ]: 55184 : for (int i = 0; i < numTables; i++)
8277 : : {
8278 : 54993 : TableInfo *tinfo = &tblinfo[i];
8279 : :
8280 [ + + ]: 54993 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8281 : 47328 : continue;
8282 : :
8283 : : /* OK, we need info for this table */
8284 [ + + ]: 7665 : if (tbloids->len > 1) /* do we have more than the '{'? */
8285 : 7537 : appendPQExpBufferChar(tbloids, ',');
8286 : 7665 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8287 : : }
8288 : 191 : appendPQExpBufferChar(tbloids, '}');
8289 : :
8290 : 191 : appendPQExpBufferStr(query,
8291 : : "SELECT c.tableoid, c.oid, "
8292 : : "conrelid, conname, confrelid, ");
8293 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
8294 : 191 : appendPQExpBufferStr(query, "conindid, ");
8295 : : else
8296 : 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8297 : 191 : appendPQExpBuffer(query,
8298 : : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8299 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8300 : : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8301 : : "WHERE contype = 'f' ",
8302 : : tbloids->data);
8303 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
8304 : 191 : appendPQExpBufferStr(query,
8305 : : "AND conparentid = 0 ");
8306 : 191 : appendPQExpBufferStr(query,
8307 : : "ORDER BY conrelid, conname");
8308 : :
8309 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8310 : :
8311 : 191 : ntups = PQntuples(res);
8312 : :
8313 : 191 : i_contableoid = PQfnumber(res, "tableoid");
8314 : 191 : i_conoid = PQfnumber(res, "oid");
8315 : 191 : i_conrelid = PQfnumber(res, "conrelid");
8316 : 191 : i_conname = PQfnumber(res, "conname");
8317 : 191 : i_confrelid = PQfnumber(res, "confrelid");
8318 : 191 : i_conindid = PQfnumber(res, "conindid");
8319 : 191 : i_condef = PQfnumber(res, "condef");
8320 : :
8321 : 191 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8322 : :
8323 : 191 : curtblindx = -1;
8324 [ + + ]: 428 : for (int j = 0; j < ntups; j++)
8325 : : {
8326 : 237 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8327 : : TableInfo *reftable;
8328 : :
8329 : : /*
8330 : : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8331 : : * order.
8332 : : */
8333 [ + + + + ]: 237 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8334 : : {
8335 [ + - ]: 16524 : while (++curtblindx < numTables)
8336 : : {
8337 : 16524 : tbinfo = &tblinfo[curtblindx];
8338 [ + + ]: 16524 : if (tbinfo->dobj.catId.oid == conrelid)
8339 : 197 : break;
8340 : : }
8341 [ - + ]: 197 : if (curtblindx >= numTables)
8342 : 0 : pg_fatal("unrecognized table OID %u", conrelid);
8343 : : }
8344 : :
8345 : 237 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8346 : 237 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8347 : 237 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8348 : 237 : AssignDumpId(&constrinfo[j].dobj);
8349 : 237 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8350 : 237 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8351 : 237 : constrinfo[j].contable = tbinfo;
8352 : 237 : constrinfo[j].condomain = NULL;
8353 : 237 : constrinfo[j].contype = 'f';
8354 : 237 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8355 : 237 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8356 : 237 : constrinfo[j].conindex = 0;
8357 : 237 : constrinfo[j].condeferrable = false;
8358 : 237 : constrinfo[j].condeferred = false;
8359 : 237 : constrinfo[j].conislocal = true;
8360 : 237 : constrinfo[j].separate = true;
8361 : :
8362 : : /*
8363 : : * Restoring an FK that points to a partitioned table requires that
8364 : : * all partition indexes have been attached beforehand. Ensure that
8365 : : * happens by making the constraint depend on each index partition
8366 : : * attach object.
8367 : : */
8368 : 237 : reftable = findTableByOid(constrinfo[j].confrelid);
8369 [ + - + + ]: 237 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8370 : : {
8371 : 30 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8372 : :
8373 [ + - ]: 30 : if (indexOid != InvalidOid)
8374 : : {
8375 [ + - ]: 30 : for (int k = 0; k < reftable->numIndexes; k++)
8376 : : {
8377 : : IndxInfo *refidx;
8378 : :
8379 : : /* not our index? */
8380 [ - + ]: 30 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8381 : 0 : continue;
8382 : :
8383 : 30 : refidx = &reftable->indexes[k];
8384 : 30 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8385 : 30 : break;
8386 : : }
8387 : : }
8388 : : }
8389 : : }
8390 : :
8391 : 191 : PQclear(res);
8392 : :
8393 : 191 : destroyPQExpBuffer(query);
8394 : 191 : destroyPQExpBuffer(tbloids);
8395 : 191 : }
8396 : :
8397 : : /*
8398 : : * addConstrChildIdxDeps
8399 : : *
8400 : : * Recursive subroutine for getConstraints
8401 : : *
8402 : : * Given an object representing a foreign key constraint and an index on the
8403 : : * partitioned table it references, mark the constraint object as dependent
8404 : : * on the DO_INDEX_ATTACH object of each index partition, recursively
8405 : : * drilling down to their partitions if any. This ensures that the FK is not
8406 : : * restored until the index is fully marked valid.
8407 : : */
8408 : : static void
8409 : 55 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8410 : : {
8411 : : SimplePtrListCell *cell;
8412 : :
8413 : : Assert(dobj->objType == DO_FK_CONSTRAINT);
8414 : :
8415 [ + + ]: 185 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8416 : : {
8417 : 130 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8418 : :
8419 : 130 : addObjectDependency(dobj, attach->dobj.dumpId);
8420 : :
8421 [ + + ]: 130 : if (attach->partitionIdx->partattaches.head != NULL)
8422 : 25 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8423 : : }
8424 : 55 : }
8425 : :
8426 : : /*
8427 : : * getDomainConstraints
8428 : : *
8429 : : * Get info about constraints on a domain.
8430 : : */
8431 : : static void
8432 : 181 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8433 : : {
8434 : : ConstraintInfo *constrinfo;
8435 : 181 : PQExpBuffer query = createPQExpBuffer();
8436 : : PGresult *res;
8437 : : int i_tableoid,
8438 : : i_oid,
8439 : : i_conname,
8440 : : i_consrc,
8441 : : i_convalidated,
8442 : : i_contype;
8443 : : int ntups;
8444 : :
8445 [ + + ]: 181 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8446 : : {
8447 : : /*
8448 : : * Set up query for constraint-specific details. For servers 17 and
8449 : : * up, domains have constraints of type 'n' as well as 'c', otherwise
8450 : : * just the latter.
8451 : : */
8452 : 46 : appendPQExpBuffer(query,
8453 : : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8454 : : "SELECT tableoid, oid, conname, "
8455 : : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8456 : : "convalidated, contype "
8457 : : "FROM pg_catalog.pg_constraint "
8458 : : "WHERE contypid = $1 AND contype IN (%s) "
8459 : : "ORDER BY conname",
8460 [ - + ]: 46 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8461 : :
8462 : 46 : ExecuteSqlStatement(fout, query->data);
8463 : :
8464 : 46 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8465 : : }
8466 : :
8467 : 181 : printfPQExpBuffer(query,
8468 : : "EXECUTE getDomainConstraints('%u')",
8469 : : tyinfo->dobj.catId.oid);
8470 : :
8471 : 181 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8472 : :
8473 : 181 : ntups = PQntuples(res);
8474 : :
8475 : 181 : i_tableoid = PQfnumber(res, "tableoid");
8476 : 181 : i_oid = PQfnumber(res, "oid");
8477 : 181 : i_conname = PQfnumber(res, "conname");
8478 : 181 : i_consrc = PQfnumber(res, "consrc");
8479 : 181 : i_convalidated = PQfnumber(res, "convalidated");
8480 : 181 : i_contype = PQfnumber(res, "contype");
8481 : :
8482 : 181 : constrinfo = pg_malloc_array(ConstraintInfo, ntups);
8483 : 181 : tyinfo->domChecks = constrinfo;
8484 : :
8485 : : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8486 [ + + ]: 373 : for (int i = 0, j = 0; i < ntups; i++)
8487 : : {
8488 : 192 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8489 : 192 : char contype = (PQgetvalue(res, i, i_contype))[0];
8490 : : ConstraintInfo *constraint;
8491 : :
8492 [ + + ]: 192 : if (contype == CONSTRAINT_CHECK)
8493 : : {
8494 : 136 : constraint = &constrinfo[j++];
8495 : 136 : tyinfo->nDomChecks++;
8496 : : }
8497 : : else
8498 : : {
8499 : : Assert(contype == CONSTRAINT_NOTNULL);
8500 : : Assert(tyinfo->notnull == NULL);
8501 : : /* use last item in array for the not-null constraint */
8502 : 56 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8503 : 56 : constraint = tyinfo->notnull;
8504 : : }
8505 : :
8506 : 192 : constraint->dobj.objType = DO_CONSTRAINT;
8507 : 192 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8508 : 192 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8509 : 192 : AssignDumpId(&(constraint->dobj));
8510 : 192 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8511 : 192 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8512 : 192 : constraint->contable = NULL;
8513 : 192 : constraint->condomain = tyinfo;
8514 : 192 : constraint->contype = contype;
8515 : 192 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8516 : 192 : constraint->confrelid = InvalidOid;
8517 : 192 : constraint->conindex = 0;
8518 : 192 : constraint->condeferrable = false;
8519 : 192 : constraint->condeferred = false;
8520 : 192 : constraint->conislocal = true;
8521 : :
8522 : 192 : constraint->separate = !validated;
8523 : :
8524 : : /*
8525 : : * Make the domain depend on the constraint, ensuring it won't be
8526 : : * output till any constraint dependencies are OK. If the constraint
8527 : : * has not been validated, it's going to be dumped after the domain
8528 : : * anyway, so this doesn't matter.
8529 : : */
8530 [ + + ]: 192 : if (validated)
8531 : 187 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8532 : : }
8533 : :
8534 : 181 : PQclear(res);
8535 : :
8536 : 181 : destroyPQExpBuffer(query);
8537 : 181 : }
8538 : :
8539 : : /*
8540 : : * getRules
8541 : : * get basic information about every rule in the system
8542 : : */
8543 : : void
8544 : 191 : getRules(Archive *fout)
8545 : : {
8546 : : PGresult *res;
8547 : : int ntups;
8548 : : int i;
8549 : 191 : PQExpBuffer query = createPQExpBuffer();
8550 : : RuleInfo *ruleinfo;
8551 : : int i_tableoid;
8552 : : int i_oid;
8553 : : int i_rulename;
8554 : : int i_ruletable;
8555 : : int i_ev_type;
8556 : : int i_is_instead;
8557 : : int i_ev_enabled;
8558 : :
8559 : 191 : appendPQExpBufferStr(query, "SELECT "
8560 : : "tableoid, oid, rulename, "
8561 : : "ev_class AS ruletable, ev_type, is_instead, "
8562 : : "ev_enabled "
8563 : : "FROM pg_rewrite "
8564 : : "ORDER BY oid");
8565 : :
8566 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8567 : :
8568 : 191 : ntups = PQntuples(res);
8569 : :
8570 : 191 : ruleinfo = pg_malloc_array(RuleInfo, ntups);
8571 : :
8572 : 191 : i_tableoid = PQfnumber(res, "tableoid");
8573 : 191 : i_oid = PQfnumber(res, "oid");
8574 : 191 : i_rulename = PQfnumber(res, "rulename");
8575 : 191 : i_ruletable = PQfnumber(res, "ruletable");
8576 : 191 : i_ev_type = PQfnumber(res, "ev_type");
8577 : 191 : i_is_instead = PQfnumber(res, "is_instead");
8578 : 191 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8579 : :
8580 [ + + ]: 32907 : for (i = 0; i < ntups; i++)
8581 : : {
8582 : : Oid ruletableoid;
8583 : :
8584 : 32716 : ruleinfo[i].dobj.objType = DO_RULE;
8585 : 32716 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8586 : 32716 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8587 : 32716 : AssignDumpId(&ruleinfo[i].dobj);
8588 : 32716 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8589 : 32716 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8590 : 32716 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8591 [ - + ]: 32716 : if (ruleinfo[i].ruletable == NULL)
8592 : 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8593 : : ruletableoid, ruleinfo[i].dobj.catId.oid);
8594 : 32716 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8595 : 32716 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8596 : 32716 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8597 : 32716 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8598 : 32716 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8599 [ + - ]: 32716 : if (ruleinfo[i].ruletable)
8600 : : {
8601 : : /*
8602 : : * If the table is a view or materialized view, force its ON
8603 : : * SELECT rule to be sorted before the view itself --- this
8604 : : * ensures that any dependencies for the rule affect the table's
8605 : : * positioning. Other rules are forced to appear after their
8606 : : * table.
8607 : : */
8608 [ + + ]: 32716 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8609 [ + + ]: 726 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8610 [ + + + - ]: 32485 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8611 : : {
8612 : 32061 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8613 : 32061 : ruleinfo[i].dobj.dumpId);
8614 : : /* We'll merge the rule into CREATE VIEW, if possible */
8615 : 32061 : ruleinfo[i].separate = false;
8616 : : }
8617 : : else
8618 : : {
8619 : 655 : addObjectDependency(&ruleinfo[i].dobj,
8620 : 655 : ruleinfo[i].ruletable->dobj.dumpId);
8621 : 655 : ruleinfo[i].separate = true;
8622 : : }
8623 : : }
8624 : : else
8625 : 0 : ruleinfo[i].separate = true;
8626 : : }
8627 : :
8628 : 191 : PQclear(res);
8629 : :
8630 : 191 : destroyPQExpBuffer(query);
8631 : 191 : }
8632 : :
8633 : : /*
8634 : : * getTriggers
8635 : : * get information about every trigger on a dumpable table
8636 : : *
8637 : : * Note: trigger data is not returned directly to the caller, but it
8638 : : * does get entered into the DumpableObject tables.
8639 : : */
8640 : : void
8641 : 191 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8642 : : {
8643 : 191 : PQExpBuffer query = createPQExpBuffer();
8644 : 191 : PQExpBuffer tbloids = createPQExpBuffer();
8645 : : PGresult *res;
8646 : : int ntups;
8647 : : int curtblindx;
8648 : : TriggerInfo *tginfo;
8649 : : int i_tableoid,
8650 : : i_oid,
8651 : : i_tgrelid,
8652 : : i_tgname,
8653 : : i_tgenabled,
8654 : : i_tgispartition,
8655 : : i_tgdef;
8656 : :
8657 : : /*
8658 : : * We want to perform just one query against pg_trigger. However, we
8659 : : * mustn't try to select every row of the catalog and then sort it out on
8660 : : * the client side, because some of the server-side functions we need
8661 : : * would be unsafe to apply to tables we don't have lock on. Hence, we
8662 : : * build an array of the OIDs of tables we care about (and now have lock
8663 : : * on!), and use a WHERE clause to constrain which rows are selected.
8664 : : */
8665 : 191 : appendPQExpBufferChar(tbloids, '{');
8666 [ + + ]: 55184 : for (int i = 0; i < numTables; i++)
8667 : : {
8668 : 54993 : TableInfo *tbinfo = &tblinfo[i];
8669 : :
8670 [ + + ]: 54993 : if (!tbinfo->hastriggers ||
8671 [ + + ]: 1260 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8672 : 54033 : continue;
8673 : :
8674 : : /* OK, we need info for this table */
8675 [ + + ]: 960 : if (tbloids->len > 1) /* do we have more than the '{'? */
8676 : 906 : appendPQExpBufferChar(tbloids, ',');
8677 : 960 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8678 : : }
8679 : 191 : appendPQExpBufferChar(tbloids, '}');
8680 : :
8681 [ + - ]: 191 : if (fout->remoteVersion >= 150000)
8682 : : {
8683 : : /*
8684 : : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8685 : : * result in non-forward-compatible dumps of WHEN clauses due to
8686 : : * under-parenthesization.
8687 : : *
8688 : : * NB: We need to see partition triggers in case the tgenabled flag
8689 : : * has been changed from the parent.
8690 : : */
8691 : 191 : appendPQExpBuffer(query,
8692 : : "SELECT t.tgrelid, t.tgname, "
8693 : : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8694 : : "t.tgenabled, t.tableoid, t.oid, "
8695 : : "t.tgparentid <> 0 AS tgispartition\n"
8696 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8697 : : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8698 : : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8699 : : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8700 : : "OR t.tgenabled != u.tgenabled) "
8701 : : "ORDER BY t.tgrelid, t.tgname",
8702 : : tbloids->data);
8703 : : }
8704 [ # # ]: 0 : else if (fout->remoteVersion >= 130000)
8705 : : {
8706 : : /*
8707 : : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8708 : : * result in non-forward-compatible dumps of WHEN clauses due to
8709 : : * under-parenthesization.
8710 : : *
8711 : : * NB: We need to see tgisinternal triggers in partitions, in case the
8712 : : * tgenabled flag has been changed from the parent.
8713 : : */
8714 : 0 : appendPQExpBuffer(query,
8715 : : "SELECT t.tgrelid, t.tgname, "
8716 : : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8717 : : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8718 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8719 : : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8720 : : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8721 : : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8722 : : "ORDER BY t.tgrelid, t.tgname",
8723 : : tbloids->data);
8724 : : }
8725 [ # # ]: 0 : else if (fout->remoteVersion >= 110000)
8726 : : {
8727 : : /*
8728 : : * NB: We need to see tgisinternal triggers in partitions, in case the
8729 : : * tgenabled flag has been changed from the parent. No tgparentid in
8730 : : * version 11-12, so we have to match them via pg_depend.
8731 : : *
8732 : : * See above about pretty=true in pg_get_triggerdef.
8733 : : */
8734 : 0 : appendPQExpBuffer(query,
8735 : : "SELECT t.tgrelid, t.tgname, "
8736 : : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8737 : : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8738 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8739 : : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8740 : : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8741 : : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8742 : : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8743 : : " d.objid = t.oid "
8744 : : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8745 : : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8746 : : "ORDER BY t.tgrelid, t.tgname",
8747 : : tbloids->data);
8748 : : }
8749 : : else
8750 : : {
8751 : : /* See above about pretty=true in pg_get_triggerdef */
8752 : 0 : appendPQExpBuffer(query,
8753 : : "SELECT t.tgrelid, t.tgname, "
8754 : : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8755 : : "t.tgenabled, false as tgispartition, "
8756 : : "t.tableoid, t.oid "
8757 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8758 : : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8759 : : "WHERE NOT tgisinternal "
8760 : : "ORDER BY t.tgrelid, t.tgname",
8761 : : tbloids->data);
8762 : : }
8763 : :
8764 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8765 : :
8766 : 191 : ntups = PQntuples(res);
8767 : :
8768 : 191 : i_tableoid = PQfnumber(res, "tableoid");
8769 : 191 : i_oid = PQfnumber(res, "oid");
8770 : 191 : i_tgrelid = PQfnumber(res, "tgrelid");
8771 : 191 : i_tgname = PQfnumber(res, "tgname");
8772 : 191 : i_tgenabled = PQfnumber(res, "tgenabled");
8773 : 191 : i_tgispartition = PQfnumber(res, "tgispartition");
8774 : 191 : i_tgdef = PQfnumber(res, "tgdef");
8775 : :
8776 : 191 : tginfo = pg_malloc_array(TriggerInfo, ntups);
8777 : :
8778 : : /*
8779 : : * Outer loop iterates once per table, not once per row. Incrementing of
8780 : : * j is handled by the inner loop.
8781 : : */
8782 : 191 : curtblindx = -1;
8783 [ + + ]: 509 : for (int j = 0; j < ntups;)
8784 : : {
8785 : 318 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8786 : 318 : TableInfo *tbinfo = NULL;
8787 : : int numtrigs;
8788 : :
8789 : : /* Count rows for this table */
8790 [ + + ]: 535 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8791 [ + + ]: 481 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8792 : 264 : break;
8793 : :
8794 : : /*
8795 : : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8796 : : * order.
8797 : : */
8798 [ + - ]: 18307 : while (++curtblindx < numTables)
8799 : : {
8800 : 18307 : tbinfo = &tblinfo[curtblindx];
8801 [ + + ]: 18307 : if (tbinfo->dobj.catId.oid == tgrelid)
8802 : 318 : break;
8803 : : }
8804 [ - + ]: 318 : if (curtblindx >= numTables)
8805 : 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8806 : :
8807 : : /* Save data for this table */
8808 : 318 : tbinfo->triggers = tginfo + j;
8809 : 318 : tbinfo->numTriggers = numtrigs;
8810 : :
8811 [ + + ]: 853 : for (int c = 0; c < numtrigs; c++, j++)
8812 : : {
8813 : 535 : tginfo[j].dobj.objType = DO_TRIGGER;
8814 : 535 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8815 : 535 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8816 : 535 : AssignDumpId(&tginfo[j].dobj);
8817 : 535 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8818 : 535 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8819 : 535 : tginfo[j].tgtable = tbinfo;
8820 : 535 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8821 : 535 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8822 : 535 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8823 : : }
8824 : : }
8825 : :
8826 : 191 : PQclear(res);
8827 : :
8828 : 191 : destroyPQExpBuffer(query);
8829 : 191 : destroyPQExpBuffer(tbloids);
8830 : 191 : }
8831 : :
8832 : : /*
8833 : : * getEventTriggers
8834 : : * get information about event triggers
8835 : : */
8836 : : void
8837 : 191 : getEventTriggers(Archive *fout)
8838 : : {
8839 : : int i;
8840 : : PQExpBuffer query;
8841 : : PGresult *res;
8842 : : EventTriggerInfo *evtinfo;
8843 : : int i_tableoid,
8844 : : i_oid,
8845 : : i_evtname,
8846 : : i_evtevent,
8847 : : i_evtowner,
8848 : : i_evttags,
8849 : : i_evtfname,
8850 : : i_evtenabled;
8851 : : int ntups;
8852 : :
8853 : 191 : query = createPQExpBuffer();
8854 : :
8855 : 191 : appendPQExpBufferStr(query,
8856 : : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8857 : : "evtevent, evtowner, "
8858 : : "array_to_string(array("
8859 : : "select quote_literal(x) "
8860 : : " from unnest(evttags) as t(x)), ', ') as evttags, "
8861 : : "e.evtfoid::regproc as evtfname "
8862 : : "FROM pg_event_trigger e "
8863 : : "ORDER BY e.oid");
8864 : :
8865 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8866 : :
8867 : 191 : ntups = PQntuples(res);
8868 : :
8869 : 191 : evtinfo = pg_malloc_array(EventTriggerInfo, ntups);
8870 : :
8871 : 191 : i_tableoid = PQfnumber(res, "tableoid");
8872 : 191 : i_oid = PQfnumber(res, "oid");
8873 : 191 : i_evtname = PQfnumber(res, "evtname");
8874 : 191 : i_evtevent = PQfnumber(res, "evtevent");
8875 : 191 : i_evtowner = PQfnumber(res, "evtowner");
8876 : 191 : i_evttags = PQfnumber(res, "evttags");
8877 : 191 : i_evtfname = PQfnumber(res, "evtfname");
8878 : 191 : i_evtenabled = PQfnumber(res, "evtenabled");
8879 : :
8880 [ + + ]: 246 : for (i = 0; i < ntups; i++)
8881 : : {
8882 : 55 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8883 : 55 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8884 : 55 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8885 : 55 : AssignDumpId(&evtinfo[i].dobj);
8886 : 55 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8887 : 55 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8888 : 55 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8889 : 55 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8890 : 55 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8891 : 55 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8892 : 55 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8893 : :
8894 : : /* Decide whether we want to dump it */
8895 : 55 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8896 : : }
8897 : :
8898 : 191 : PQclear(res);
8899 : :
8900 : 191 : destroyPQExpBuffer(query);
8901 : 191 : }
8902 : :
8903 : : /*
8904 : : * getProcLangs
8905 : : * get basic information about every procedural language in the system
8906 : : *
8907 : : * NB: this must run after getFuncs() because we assume we can do
8908 : : * findFuncByOid().
8909 : : */
8910 : : void
8911 : 191 : getProcLangs(Archive *fout)
8912 : : {
8913 : : PGresult *res;
8914 : : int ntups;
8915 : : int i;
8916 : 191 : PQExpBuffer query = createPQExpBuffer();
8917 : : ProcLangInfo *planginfo;
8918 : : int i_tableoid;
8919 : : int i_oid;
8920 : : int i_lanname;
8921 : : int i_lanpltrusted;
8922 : : int i_lanplcallfoid;
8923 : : int i_laninline;
8924 : : int i_lanvalidator;
8925 : : int i_lanacl;
8926 : : int i_acldefault;
8927 : : int i_lanowner;
8928 : :
8929 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8930 : : "lanname, lanpltrusted, lanplcallfoid, "
8931 : : "laninline, lanvalidator, "
8932 : : "lanacl, "
8933 : : "acldefault('l', lanowner) AS acldefault, "
8934 : : "lanowner "
8935 : : "FROM pg_language "
8936 : : "WHERE lanispl "
8937 : : "ORDER BY oid");
8938 : :
8939 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8940 : :
8941 : 191 : ntups = PQntuples(res);
8942 : :
8943 : 191 : planginfo = pg_malloc_array(ProcLangInfo, ntups);
8944 : :
8945 : 191 : i_tableoid = PQfnumber(res, "tableoid");
8946 : 191 : i_oid = PQfnumber(res, "oid");
8947 : 191 : i_lanname = PQfnumber(res, "lanname");
8948 : 191 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8949 : 191 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8950 : 191 : i_laninline = PQfnumber(res, "laninline");
8951 : 191 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8952 : 191 : i_lanacl = PQfnumber(res, "lanacl");
8953 : 191 : i_acldefault = PQfnumber(res, "acldefault");
8954 : 191 : i_lanowner = PQfnumber(res, "lanowner");
8955 : :
8956 [ + + ]: 430 : for (i = 0; i < ntups; i++)
8957 : : {
8958 : 239 : planginfo[i].dobj.objType = DO_PROCLANG;
8959 : 239 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8960 : 239 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8961 : 239 : AssignDumpId(&planginfo[i].dobj);
8962 : :
8963 : 239 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8964 : 239 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8965 : 239 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8966 : 239 : planginfo[i].dacl.privtype = 0;
8967 : 239 : planginfo[i].dacl.initprivs = NULL;
8968 : 239 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8969 : 239 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8970 : 239 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8971 : 239 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8972 : 239 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8973 : :
8974 : : /* Decide whether we want to dump it */
8975 : 239 : selectDumpableProcLang(&(planginfo[i]), fout);
8976 : :
8977 : : /* Mark whether language has an ACL */
8978 [ + + ]: 239 : if (!PQgetisnull(res, i, i_lanacl))
8979 : 48 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8980 : : }
8981 : :
8982 : 191 : PQclear(res);
8983 : :
8984 : 191 : destroyPQExpBuffer(query);
8985 : 191 : }
8986 : :
8987 : : /*
8988 : : * getCasts
8989 : : * get basic information about most casts in the system
8990 : : *
8991 : : * Skip casts from a range to its multirange, since we'll create those
8992 : : * automatically.
8993 : : */
8994 : : void
8995 : 191 : getCasts(Archive *fout)
8996 : : {
8997 : : PGresult *res;
8998 : : int ntups;
8999 : : int i;
9000 : 191 : PQExpBuffer query = createPQExpBuffer();
9001 : : CastInfo *castinfo;
9002 : : int i_tableoid;
9003 : : int i_oid;
9004 : : int i_castsource;
9005 : : int i_casttarget;
9006 : : int i_castfunc;
9007 : : int i_castcontext;
9008 : : int i_castmethod;
9009 : :
9010 [ + - ]: 191 : if (fout->remoteVersion >= 140000)
9011 : : {
9012 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9013 : : "castsource, casttarget, castfunc, castcontext, "
9014 : : "castmethod "
9015 : : "FROM pg_cast c "
9016 : : "WHERE NOT EXISTS ( "
9017 : : "SELECT 1 FROM pg_range r "
9018 : : "WHERE c.castsource = r.rngtypid "
9019 : : "AND c.casttarget = r.rngmultitypid "
9020 : : ") "
9021 : : "ORDER BY 3,4");
9022 : : }
9023 : : else
9024 : : {
9025 : 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9026 : : "castsource, casttarget, castfunc, castcontext, "
9027 : : "castmethod "
9028 : : "FROM pg_cast ORDER BY 3,4");
9029 : : }
9030 : :
9031 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9032 : :
9033 : 191 : ntups = PQntuples(res);
9034 : :
9035 : 191 : castinfo = pg_malloc_array(CastInfo, ntups);
9036 : :
9037 : 191 : i_tableoid = PQfnumber(res, "tableoid");
9038 : 191 : i_oid = PQfnumber(res, "oid");
9039 : 191 : i_castsource = PQfnumber(res, "castsource");
9040 : 191 : i_casttarget = PQfnumber(res, "casttarget");
9041 : 191 : i_castfunc = PQfnumber(res, "castfunc");
9042 : 191 : i_castcontext = PQfnumber(res, "castcontext");
9043 : 191 : i_castmethod = PQfnumber(res, "castmethod");
9044 : :
9045 [ + + ]: 46694 : for (i = 0; i < ntups; i++)
9046 : : {
9047 : : PQExpBufferData namebuf;
9048 : : TypeInfo *sTypeInfo;
9049 : : TypeInfo *tTypeInfo;
9050 : :
9051 : 46503 : castinfo[i].dobj.objType = DO_CAST;
9052 : 46503 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9053 : 46503 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9054 : 46503 : AssignDumpId(&castinfo[i].dobj);
9055 : 46503 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9056 : 46503 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9057 : 46503 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9058 : 46503 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9059 : 46503 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9060 : :
9061 : : /*
9062 : : * Try to name cast as concatenation of typnames. This is only used
9063 : : * for purposes of sorting. If we fail to find either type, the name
9064 : : * will be an empty string.
9065 : : */
9066 : 46503 : initPQExpBuffer(&namebuf);
9067 : 46503 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9068 : 46503 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9069 [ + - + - ]: 46503 : if (sTypeInfo && tTypeInfo)
9070 : 46503 : appendPQExpBuffer(&namebuf, "%s %s",
9071 : : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9072 : 46503 : castinfo[i].dobj.name = namebuf.data;
9073 : :
9074 : : /* Decide whether we want to dump it */
9075 : 46503 : selectDumpableCast(&(castinfo[i]), fout);
9076 : : }
9077 : :
9078 : 191 : PQclear(res);
9079 : :
9080 : 191 : destroyPQExpBuffer(query);
9081 : 191 : }
9082 : :
9083 : : static char *
9084 : 93 : get_language_name(Archive *fout, Oid langid)
9085 : : {
9086 : : PQExpBuffer query;
9087 : : PGresult *res;
9088 : : char *lanname;
9089 : :
9090 : 93 : query = createPQExpBuffer();
9091 : 93 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9092 : 93 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9093 : 93 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9094 : 93 : destroyPQExpBuffer(query);
9095 : 93 : PQclear(res);
9096 : :
9097 : 93 : return lanname;
9098 : : }
9099 : :
9100 : : /*
9101 : : * getTransforms
9102 : : * get basic information about every transform in the system
9103 : : */
9104 : : void
9105 : 191 : getTransforms(Archive *fout)
9106 : : {
9107 : : PGresult *res;
9108 : : int ntups;
9109 : : int i;
9110 : : PQExpBuffer query;
9111 : : TransformInfo *transforminfo;
9112 : : int i_tableoid;
9113 : : int i_oid;
9114 : : int i_trftype;
9115 : : int i_trflang;
9116 : : int i_trffromsql;
9117 : : int i_trftosql;
9118 : :
9119 : 191 : query = createPQExpBuffer();
9120 : :
9121 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9122 : : "trftype, trflang, trffromsql::oid, trftosql::oid "
9123 : : "FROM pg_transform "
9124 : : "ORDER BY 3,4");
9125 : :
9126 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9127 : :
9128 : 191 : ntups = PQntuples(res);
9129 : :
9130 : 191 : transforminfo = pg_malloc_array(TransformInfo, ntups);
9131 : :
9132 : 191 : i_tableoid = PQfnumber(res, "tableoid");
9133 : 191 : i_oid = PQfnumber(res, "oid");
9134 : 191 : i_trftype = PQfnumber(res, "trftype");
9135 : 191 : i_trflang = PQfnumber(res, "trflang");
9136 : 191 : i_trffromsql = PQfnumber(res, "trffromsql");
9137 : 191 : i_trftosql = PQfnumber(res, "trftosql");
9138 : :
9139 [ + + ]: 246 : for (i = 0; i < ntups; i++)
9140 : : {
9141 : : PQExpBufferData namebuf;
9142 : : TypeInfo *typeInfo;
9143 : : char *lanname;
9144 : :
9145 : 55 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9146 : 55 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9147 : 55 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9148 : 55 : AssignDumpId(&transforminfo[i].dobj);
9149 : 55 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9150 : 55 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9151 : 55 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9152 : 55 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9153 : :
9154 : : /*
9155 : : * Try to name transform as concatenation of type and language name.
9156 : : * This is only used for purposes of sorting. If we fail to find
9157 : : * either, the name will be an empty string.
9158 : : */
9159 : 55 : initPQExpBuffer(&namebuf);
9160 : 55 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9161 : 55 : lanname = get_language_name(fout, transforminfo[i].trflang);
9162 [ + - + - ]: 55 : if (typeInfo && lanname)
9163 : 55 : appendPQExpBuffer(&namebuf, "%s %s",
9164 : : typeInfo->dobj.name, lanname);
9165 : 55 : transforminfo[i].dobj.name = namebuf.data;
9166 : 55 : free(lanname);
9167 : :
9168 : : /* Decide whether we want to dump it */
9169 : 55 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9170 : : }
9171 : :
9172 : 191 : PQclear(res);
9173 : :
9174 : 191 : destroyPQExpBuffer(query);
9175 : 191 : }
9176 : :
9177 : : /*
9178 : : * getTableAttrs -
9179 : : * for each interesting table, read info about its attributes
9180 : : * (names, types, default values, CHECK constraints, etc)
9181 : : *
9182 : : * modifies tblinfo
9183 : : */
9184 : : void
9185 : 191 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9186 : : {
9187 : 191 : DumpOptions *dopt = fout->dopt;
9188 : 191 : PQExpBuffer q = createPQExpBuffer();
9189 : 191 : PQExpBuffer tbloids = createPQExpBuffer();
9190 : 191 : PQExpBuffer checkoids = createPQExpBuffer();
9191 : 191 : PQExpBuffer invalidnotnulloids = NULL;
9192 : : PGresult *res;
9193 : : int ntups;
9194 : : int curtblindx;
9195 : : int i_attrelid;
9196 : : int i_attnum;
9197 : : int i_attname;
9198 : : int i_atttypname;
9199 : : int i_attstattarget;
9200 : : int i_attstorage;
9201 : : int i_typstorage;
9202 : : int i_attidentity;
9203 : : int i_attgenerated;
9204 : : int i_attisdropped;
9205 : : int i_attlen;
9206 : : int i_attalign;
9207 : : int i_attislocal;
9208 : : int i_notnull_name;
9209 : : int i_notnull_comment;
9210 : : int i_notnull_noinherit;
9211 : : int i_notnull_islocal;
9212 : : int i_notnull_invalidoid;
9213 : : int i_attoptions;
9214 : : int i_attcollation;
9215 : : int i_attcompression;
9216 : : int i_attfdwoptions;
9217 : : int i_attmissingval;
9218 : : int i_atthasdef;
9219 : :
9220 : : /*
9221 : : * We want to perform just one query against pg_attribute, and then just
9222 : : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9223 : : * (for CHECK constraints and for NOT NULL constraints). However, we
9224 : : * mustn't try to select every row of those catalogs and then sort it out
9225 : : * on the client side, because some of the server-side functions we need
9226 : : * would be unsafe to apply to tables we don't have lock on. Hence, we
9227 : : * build an array of the OIDs of tables we care about (and now have lock
9228 : : * on!), and use a WHERE clause to constrain which rows are selected.
9229 : : */
9230 : 191 : appendPQExpBufferChar(tbloids, '{');
9231 : 191 : appendPQExpBufferChar(checkoids, '{');
9232 [ + + ]: 55184 : for (int i = 0; i < numTables; i++)
9233 : : {
9234 : 54993 : TableInfo *tbinfo = &tblinfo[i];
9235 : :
9236 : : /* Don't bother to collect info for sequences */
9237 [ + + ]: 54993 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9238 : 647 : continue;
9239 : :
9240 : : /*
9241 : : * Don't bother with uninteresting tables, either. For binary
9242 : : * upgrades, this is bypassed for pg_largeobject_metadata and
9243 : : * pg_shdepend so that the columns names are collected for the
9244 : : * corresponding COPY commands. Restoring the data for those catalogs
9245 : : * is faster than restoring the equivalent set of large object
9246 : : * commands.
9247 : : */
9248 [ + + ]: 54346 : if (!tbinfo->interesting &&
9249 [ + + ]: 47043 : !(fout->dopt->binary_upgrade &&
9250 [ + + ]: 9458 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9251 [ + + ]: 9418 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9252 : 46963 : continue;
9253 : :
9254 : : /* OK, we need info for this table */
9255 [ + + ]: 7383 : if (tbloids->len > 1) /* do we have more than the '{'? */
9256 : 7234 : appendPQExpBufferChar(tbloids, ',');
9257 : 7383 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9258 : :
9259 [ + + ]: 7383 : if (tbinfo->ncheck > 0)
9260 : : {
9261 : : /* Also make a list of the ones with check constraints */
9262 [ + + ]: 548 : if (checkoids->len > 1) /* do we have more than the '{'? */
9263 : 476 : appendPQExpBufferChar(checkoids, ',');
9264 : 548 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9265 : : }
9266 : : }
9267 : 191 : appendPQExpBufferChar(tbloids, '}');
9268 : 191 : appendPQExpBufferChar(checkoids, '}');
9269 : :
9270 : : /*
9271 : : * Find all the user attributes and their types.
9272 : : *
9273 : : * Since we only want to dump COLLATE clauses for attributes whose
9274 : : * collation is different from their type's default, we use a CASE here to
9275 : : * suppress uninteresting attcollations cheaply.
9276 : : */
9277 : 191 : appendPQExpBufferStr(q,
9278 : : "SELECT\n"
9279 : : "a.attrelid,\n"
9280 : : "a.attnum,\n"
9281 : : "a.attname,\n"
9282 : : "a.attstattarget,\n"
9283 : : "a.attstorage,\n"
9284 : : "t.typstorage,\n"
9285 : : "a.atthasdef,\n"
9286 : : "a.attisdropped,\n"
9287 : : "a.attlen,\n"
9288 : : "a.attalign,\n"
9289 : : "a.attislocal,\n"
9290 : : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9291 : : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9292 : : "CASE WHEN a.attcollation <> t.typcollation "
9293 : : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9294 : : "pg_catalog.array_to_string(ARRAY("
9295 : : "SELECT pg_catalog.quote_ident(option_name) || "
9296 : : "' ' || pg_catalog.quote_literal(option_value) "
9297 : : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9298 : : "ORDER BY option_name"
9299 : : "), E',\n ') AS attfdwoptions,\n");
9300 : :
9301 : : /*
9302 : : * Find out any NOT NULL markings for each column. In 18 and up we read
9303 : : * pg_constraint to obtain the constraint name, and for valid constraints
9304 : : * also pg_description to obtain its comment. notnull_noinherit is set
9305 : : * according to the NO INHERIT property. For versions prior to 18, we
9306 : : * store an empty string as the name when a constraint is marked as
9307 : : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9308 : : * without a name); also, such cases are never NO INHERIT.
9309 : : *
9310 : : * For invalid constraints, we need to store their OIDs for processing
9311 : : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9312 : : * is invalid, and NULL otherwise. Their comments are handled not here
9313 : : * but by collectComments, because they're their own dumpable object.
9314 : : *
9315 : : * We track in notnull_islocal whether the constraint was defined directly
9316 : : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9317 : : * might modify this later.
9318 : : */
9319 [ + - ]: 191 : if (fout->remoteVersion >= 180000)
9320 : 191 : appendPQExpBufferStr(q,
9321 : : "co.conname AS notnull_name,\n"
9322 : : "CASE WHEN co.convalidated THEN pt.description"
9323 : : " ELSE NULL END AS notnull_comment,\n"
9324 : : "CASE WHEN NOT co.convalidated THEN co.oid "
9325 : : "ELSE NULL END AS notnull_invalidoid,\n"
9326 : : "co.connoinherit AS notnull_noinherit,\n"
9327 : : "co.conislocal AS notnull_islocal,\n");
9328 : : else
9329 : 0 : appendPQExpBufferStr(q,
9330 : : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9331 : : "NULL AS notnull_comment,\n"
9332 : : "NULL AS notnull_invalidoid,\n"
9333 : : "false AS notnull_noinherit,\n"
9334 : : "CASE WHEN a.attislocal THEN true\n"
9335 : : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9336 : : " ELSE false\n"
9337 : : "END AS notnull_islocal,\n");
9338 : :
9339 [ + - ]: 191 : if (fout->remoteVersion >= 140000)
9340 : 191 : appendPQExpBufferStr(q,
9341 : : "a.attcompression AS attcompression,\n");
9342 : : else
9343 : 0 : appendPQExpBufferStr(q,
9344 : : "'' AS attcompression,\n");
9345 : :
9346 : 191 : appendPQExpBufferStr(q,
9347 : : "a.attidentity,\n");
9348 : :
9349 [ + - ]: 191 : if (fout->remoteVersion >= 110000)
9350 : 191 : appendPQExpBufferStr(q,
9351 : : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9352 : : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9353 : : else
9354 : 0 : appendPQExpBufferStr(q,
9355 : : "NULL AS attmissingval,\n");
9356 : :
9357 [ + - ]: 191 : if (fout->remoteVersion >= 120000)
9358 : 191 : appendPQExpBufferStr(q,
9359 : : "a.attgenerated\n");
9360 : : else
9361 : 0 : appendPQExpBufferStr(q,
9362 : : "'' AS attgenerated\n");
9363 : :
9364 : : /* need left join to pg_type to not fail on dropped columns ... */
9365 : 191 : appendPQExpBuffer(q,
9366 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9367 : : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9368 : : "LEFT JOIN pg_catalog.pg_type t "
9369 : : "ON (a.atttypid = t.oid)\n",
9370 : : tbloids->data);
9371 : :
9372 : : /*
9373 : : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9374 : : * entries and pg_description to get their comments.
9375 : : */
9376 [ + - ]: 191 : if (fout->remoteVersion >= 180000)
9377 : 191 : appendPQExpBufferStr(q,
9378 : : " LEFT JOIN pg_catalog.pg_constraint co ON "
9379 : : "(a.attrelid = co.conrelid\n"
9380 : : " AND co.contype = 'n' AND "
9381 : : "co.conkey = array[a.attnum])\n"
9382 : : " LEFT JOIN pg_catalog.pg_description pt ON "
9383 : : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9384 : :
9385 : 191 : appendPQExpBufferStr(q,
9386 : : "WHERE a.attnum > 0::pg_catalog.int2\n");
9387 : :
9388 : : /*
9389 : : * For binary upgrades from <v12, be sure to pick up
9390 : : * pg_largeobject_metadata's oid column.
9391 : : */
9392 [ + + - + ]: 191 : if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9393 : 0 : appendPQExpBufferStr(q,
9394 : : "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9395 : : CppAsString2(LargeObjectMetadataRelationId) ")\n");
9396 : :
9397 : 191 : appendPQExpBufferStr(q,
9398 : : "ORDER BY a.attrelid, a.attnum");
9399 : :
9400 : 191 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9401 : :
9402 : 191 : ntups = PQntuples(res);
9403 : :
9404 : 191 : i_attrelid = PQfnumber(res, "attrelid");
9405 : 191 : i_attnum = PQfnumber(res, "attnum");
9406 : 191 : i_attname = PQfnumber(res, "attname");
9407 : 191 : i_atttypname = PQfnumber(res, "atttypname");
9408 : 191 : i_attstattarget = PQfnumber(res, "attstattarget");
9409 : 191 : i_attstorage = PQfnumber(res, "attstorage");
9410 : 191 : i_typstorage = PQfnumber(res, "typstorage");
9411 : 191 : i_attidentity = PQfnumber(res, "attidentity");
9412 : 191 : i_attgenerated = PQfnumber(res, "attgenerated");
9413 : 191 : i_attisdropped = PQfnumber(res, "attisdropped");
9414 : 191 : i_attlen = PQfnumber(res, "attlen");
9415 : 191 : i_attalign = PQfnumber(res, "attalign");
9416 : 191 : i_attislocal = PQfnumber(res, "attislocal");
9417 : 191 : i_notnull_name = PQfnumber(res, "notnull_name");
9418 : 191 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9419 : 191 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9420 : 191 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9421 : 191 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9422 : 191 : i_attoptions = PQfnumber(res, "attoptions");
9423 : 191 : i_attcollation = PQfnumber(res, "attcollation");
9424 : 191 : i_attcompression = PQfnumber(res, "attcompression");
9425 : 191 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9426 : 191 : i_attmissingval = PQfnumber(res, "attmissingval");
9427 : 191 : i_atthasdef = PQfnumber(res, "atthasdef");
9428 : :
9429 : : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9430 : 191 : resetPQExpBuffer(tbloids);
9431 : 191 : appendPQExpBufferChar(tbloids, '{');
9432 : :
9433 : : /*
9434 : : * Outer loop iterates once per table, not once per row. Incrementing of
9435 : : * r is handled by the inner loop.
9436 : : */
9437 : 191 : curtblindx = -1;
9438 [ + + ]: 7316 : for (int r = 0; r < ntups;)
9439 : : {
9440 : 7125 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9441 : 7125 : TableInfo *tbinfo = NULL;
9442 : : int numatts;
9443 : : bool hasdefaults;
9444 : :
9445 : : /* Count rows for this table */
9446 [ + + ]: 26559 : for (numatts = 1; numatts < ntups - r; numatts++)
9447 [ + + ]: 26413 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9448 : 6979 : break;
9449 : :
9450 : : /*
9451 : : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9452 : : * order.
9453 : : */
9454 [ + - ]: 38198 : while (++curtblindx < numTables)
9455 : : {
9456 : 38198 : tbinfo = &tblinfo[curtblindx];
9457 [ + + ]: 38198 : if (tbinfo->dobj.catId.oid == attrelid)
9458 : 7125 : break;
9459 : : }
9460 [ - + ]: 7125 : if (curtblindx >= numTables)
9461 : 0 : pg_fatal("unrecognized table OID %u", attrelid);
9462 : : /* cross-check that we only got requested tables */
9463 [ + - ]: 7125 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9464 [ + + ]: 7125 : (!tbinfo->interesting &&
9465 [ + - ]: 80 : !(fout->dopt->binary_upgrade &&
9466 [ + + ]: 80 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9467 [ - + ]: 40 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9468 : 0 : pg_fatal("unexpected column data for table \"%s\"",
9469 : : tbinfo->dobj.name);
9470 : :
9471 : : /* Save data for this table */
9472 : 7125 : tbinfo->numatts = numatts;
9473 : 7125 : tbinfo->attnames = pg_malloc_array(char *, numatts);
9474 : 7125 : tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9475 : 7125 : tbinfo->attstattarget = pg_malloc_array(int, numatts);
9476 : 7125 : tbinfo->attstorage = pg_malloc_array(char, numatts);
9477 : 7125 : tbinfo->typstorage = pg_malloc_array(char, numatts);
9478 : 7125 : tbinfo->attidentity = pg_malloc_array(char, numatts);
9479 : 7125 : tbinfo->attgenerated = pg_malloc_array(char, numatts);
9480 : 7125 : tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9481 : 7125 : tbinfo->attlen = pg_malloc_array(int, numatts);
9482 : 7125 : tbinfo->attalign = pg_malloc_array(char, numatts);
9483 : 7125 : tbinfo->attislocal = pg_malloc_array(bool, numatts);
9484 : 7125 : tbinfo->attoptions = pg_malloc_array(char *, numatts);
9485 : 7125 : tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9486 : 7125 : tbinfo->attcompression = pg_malloc_array(char, numatts);
9487 : 7125 : tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9488 : 7125 : tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9489 : 7125 : tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9490 : 7125 : tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9491 : 7125 : tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9492 : 7125 : tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9493 : 7125 : tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9494 : 7125 : tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9495 : 7125 : hasdefaults = false;
9496 : :
9497 [ + + ]: 33684 : for (int j = 0; j < numatts; j++, r++)
9498 : : {
9499 [ - + ]: 26559 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9500 [ # # # # ]: 0 : !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9501 [ # # ]: 0 : tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9502 : 0 : pg_fatal("invalid column numbering in table \"%s\"",
9503 : : tbinfo->dobj.name);
9504 : 26559 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9505 : 26559 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9506 [ + + ]: 26559 : if (PQgetisnull(res, r, i_attstattarget))
9507 : 26516 : tbinfo->attstattarget[j] = -1;
9508 : : else
9509 : 43 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9510 : 26559 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9511 : 26559 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9512 : 26559 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9513 : 26559 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9514 [ + + + + ]: 26559 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9515 : 26559 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9516 : 26559 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9517 : 26559 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9518 : 26559 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9519 : :
9520 : : /* Handle not-null constraint name and flags */
9521 : 26559 : determineNotNullFlags(fout, res, r,
9522 : : tbinfo, j,
9523 : : i_notnull_name,
9524 : : i_notnull_comment,
9525 : : i_notnull_invalidoid,
9526 : : i_notnull_noinherit,
9527 : : i_notnull_islocal,
9528 : : &invalidnotnulloids);
9529 : :
9530 : 26559 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9531 [ + + ]: 26559 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9532 : 26559 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9533 : 26559 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9534 : 26559 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9535 : 26559 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9536 : 26559 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9537 : 26559 : tbinfo->attrdefs[j] = NULL; /* fix below */
9538 [ + + ]: 26559 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9539 : 1376 : hasdefaults = true;
9540 : : }
9541 : :
9542 [ + + ]: 7125 : if (hasdefaults)
9543 : : {
9544 : : /* Collect OIDs of interesting tables that have defaults */
9545 [ + + ]: 1026 : if (tbloids->len > 1) /* do we have more than the '{'? */
9546 : 955 : appendPQExpBufferChar(tbloids, ',');
9547 : 1026 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9548 : : }
9549 : : }
9550 : :
9551 : : /* If invalidnotnulloids has any data, finalize it */
9552 [ + + ]: 191 : if (invalidnotnulloids != NULL)
9553 : 46 : appendPQExpBufferChar(invalidnotnulloids, '}');
9554 : :
9555 : 191 : PQclear(res);
9556 : :
9557 : : /*
9558 : : * Now get info about column defaults. This is skipped for a data-only
9559 : : * dump, as it is only needed for table schemas.
9560 : : */
9561 [ + + + + ]: 191 : if (dopt->dumpSchema && tbloids->len > 1)
9562 : : {
9563 : : AttrDefInfo *attrdefs;
9564 : : int numDefaults;
9565 : 62 : TableInfo *tbinfo = NULL;
9566 : :
9567 : 62 : pg_log_info("finding table default expressions");
9568 : :
9569 : 62 : appendPQExpBufferChar(tbloids, '}');
9570 : :
9571 : 62 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9572 : : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9573 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9574 : : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9575 : : "ORDER BY a.adrelid, a.adnum",
9576 : : tbloids->data);
9577 : :
9578 : 62 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9579 : :
9580 : 62 : numDefaults = PQntuples(res);
9581 : 62 : attrdefs = pg_malloc_array(AttrDefInfo, numDefaults);
9582 : :
9583 : 62 : curtblindx = -1;
9584 [ + + ]: 1329 : for (int j = 0; j < numDefaults; j++)
9585 : : {
9586 : 1267 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9587 : 1267 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9588 : 1267 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9589 : 1267 : int adnum = atoi(PQgetvalue(res, j, 3));
9590 : 1267 : char *adsrc = PQgetvalue(res, j, 4);
9591 : :
9592 : : /*
9593 : : * Locate the associated TableInfo; we rely on tblinfo[] being in
9594 : : * OID order.
9595 : : */
9596 [ + + + + ]: 1267 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9597 : : {
9598 [ + - ]: 20961 : while (++curtblindx < numTables)
9599 : : {
9600 : 20961 : tbinfo = &tblinfo[curtblindx];
9601 [ + + ]: 20961 : if (tbinfo->dobj.catId.oid == adrelid)
9602 : 951 : break;
9603 : : }
9604 [ - + ]: 951 : if (curtblindx >= numTables)
9605 : 0 : pg_fatal("unrecognized table OID %u", adrelid);
9606 : : }
9607 : :
9608 [ + - - + ]: 1267 : if (adnum <= 0 || adnum > tbinfo->numatts)
9609 : 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9610 : : adnum, tbinfo->dobj.name);
9611 : :
9612 : : /*
9613 : : * dropped columns shouldn't have defaults, but just in case,
9614 : : * ignore 'em
9615 : : */
9616 [ - + ]: 1267 : if (tbinfo->attisdropped[adnum - 1])
9617 : 0 : continue;
9618 : :
9619 : 1267 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9620 : 1267 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9621 : 1267 : attrdefs[j].dobj.catId.oid = adoid;
9622 : 1267 : AssignDumpId(&attrdefs[j].dobj);
9623 : 1267 : attrdefs[j].adtable = tbinfo;
9624 : 1267 : attrdefs[j].adnum = adnum;
9625 : 1267 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9626 : :
9627 : 1267 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9628 : 1267 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9629 : :
9630 : 1267 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9631 : :
9632 : : /*
9633 : : * Figure out whether the default/generation expression should be
9634 : : * dumped as part of the main CREATE TABLE (or similar) command or
9635 : : * as a separate ALTER TABLE (or similar) command. The preference
9636 : : * is to put it into the CREATE command, but in some cases that's
9637 : : * not possible.
9638 : : */
9639 [ + + ]: 1267 : if (tbinfo->attgenerated[adnum - 1])
9640 : : {
9641 : : /*
9642 : : * Column generation expressions cannot be dumped separately,
9643 : : * because there is no syntax for it. By setting separate to
9644 : : * false here we prevent the "default" from being processed as
9645 : : * its own dumpable object. Later, flagInhAttrs() will mark
9646 : : * it as not to be dumped at all, if possible (that is, if it
9647 : : * can be inherited from a parent).
9648 : : */
9649 : 722 : attrdefs[j].separate = false;
9650 : : }
9651 [ + + ]: 545 : else if (tbinfo->relkind == RELKIND_VIEW)
9652 : : {
9653 : : /*
9654 : : * Defaults on a VIEW must always be dumped as separate ALTER
9655 : : * TABLE commands.
9656 : : */
9657 : 34 : attrdefs[j].separate = true;
9658 : : }
9659 [ + + ]: 511 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9660 : : {
9661 : : /* column will be suppressed, print default separately */
9662 : 4 : attrdefs[j].separate = true;
9663 : : }
9664 : : else
9665 : : {
9666 : 507 : attrdefs[j].separate = false;
9667 : : }
9668 : :
9669 [ + + ]: 1267 : if (!attrdefs[j].separate)
9670 : : {
9671 : : /*
9672 : : * Mark the default as needing to appear before the table, so
9673 : : * that any dependencies it has must be emitted before the
9674 : : * CREATE TABLE. If this is not possible, we'll change to
9675 : : * "separate" mode while sorting dependencies.
9676 : : */
9677 : 1229 : addObjectDependency(&tbinfo->dobj,
9678 : 1229 : attrdefs[j].dobj.dumpId);
9679 : : }
9680 : :
9681 : 1267 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9682 : : }
9683 : :
9684 : 62 : PQclear(res);
9685 : : }
9686 : :
9687 : : /*
9688 : : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9689 : : * data-only dump, as it is only needed for table schemas.
9690 : : */
9691 [ + + + + ]: 191 : if (dopt->dumpSchema && invalidnotnulloids)
9692 : : {
9693 : : ConstraintInfo *constrs;
9694 : : int numConstrs;
9695 : : int i_tableoid;
9696 : : int i_oid;
9697 : : int i_conrelid;
9698 : : int i_conname;
9699 : : int i_consrc;
9700 : : int i_conislocal;
9701 : :
9702 : 39 : pg_log_info("finding invalid not-null constraints");
9703 : :
9704 : 39 : resetPQExpBuffer(q);
9705 : 39 : appendPQExpBuffer(q,
9706 : : "SELECT c.tableoid, c.oid, conrelid, conname, "
9707 : : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9708 : : "conislocal, convalidated "
9709 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9710 : : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9711 : : "ORDER BY c.conrelid, c.conname",
9712 : 39 : invalidnotnulloids->data);
9713 : :
9714 : 39 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9715 : :
9716 : 39 : numConstrs = PQntuples(res);
9717 : 39 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9718 : :
9719 : 39 : i_tableoid = PQfnumber(res, "tableoid");
9720 : 39 : i_oid = PQfnumber(res, "oid");
9721 : 39 : i_conrelid = PQfnumber(res, "conrelid");
9722 : 39 : i_conname = PQfnumber(res, "conname");
9723 : 39 : i_consrc = PQfnumber(res, "consrc");
9724 : 39 : i_conislocal = PQfnumber(res, "conislocal");
9725 : :
9726 : : /* As above, this loop iterates once per table, not once per row */
9727 : 39 : curtblindx = -1;
9728 [ + + ]: 108 : for (int j = 0; j < numConstrs;)
9729 : : {
9730 : 69 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9731 : 69 : TableInfo *tbinfo = NULL;
9732 : : int numcons;
9733 : :
9734 : : /* Count rows for this table */
9735 [ + + ]: 69 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9736 [ + - ]: 30 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9737 : 30 : break;
9738 : :
9739 : : /*
9740 : : * Locate the associated TableInfo; we rely on tblinfo[] being in
9741 : : * OID order.
9742 : : */
9743 [ + - ]: 14309 : while (++curtblindx < numTables)
9744 : : {
9745 : 14309 : tbinfo = &tblinfo[curtblindx];
9746 [ + + ]: 14309 : if (tbinfo->dobj.catId.oid == conrelid)
9747 : 69 : break;
9748 : : }
9749 [ - + ]: 69 : if (curtblindx >= numTables)
9750 : 0 : pg_fatal("unrecognized table OID %u", conrelid);
9751 : :
9752 [ + + ]: 138 : for (int c = 0; c < numcons; c++, j++)
9753 : : {
9754 : 69 : constrs[j].dobj.objType = DO_CONSTRAINT;
9755 : 69 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9756 : 69 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9757 : 69 : AssignDumpId(&constrs[j].dobj);
9758 : 69 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9759 : 69 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9760 : 69 : constrs[j].contable = tbinfo;
9761 : 69 : constrs[j].condomain = NULL;
9762 : 69 : constrs[j].contype = 'n';
9763 : 69 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9764 : 69 : constrs[j].confrelid = InvalidOid;
9765 : 69 : constrs[j].conindex = 0;
9766 : 69 : constrs[j].condeferrable = false;
9767 : 69 : constrs[j].condeferred = false;
9768 : 69 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9769 : :
9770 : : /*
9771 : : * All invalid not-null constraints must be dumped separately,
9772 : : * because CREATE TABLE would not create them as invalid, and
9773 : : * also because they must be created after potentially
9774 : : * violating data has been loaded.
9775 : : */
9776 : 69 : constrs[j].separate = true;
9777 : :
9778 : 69 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9779 : : }
9780 : : }
9781 : 39 : PQclear(res);
9782 : : }
9783 : :
9784 : : /*
9785 : : * Get info about table CHECK constraints. This is skipped for a
9786 : : * data-only dump, as it is only needed for table schemas.
9787 : : */
9788 [ + + + + ]: 191 : if (dopt->dumpSchema && checkoids->len > 2)
9789 : : {
9790 : : ConstraintInfo *constrs;
9791 : : int numConstrs;
9792 : : int i_tableoid;
9793 : : int i_oid;
9794 : : int i_conrelid;
9795 : : int i_conname;
9796 : : int i_consrc;
9797 : : int i_conislocal;
9798 : : int i_convalidated;
9799 : :
9800 : 63 : pg_log_info("finding table check constraints");
9801 : :
9802 : 63 : resetPQExpBuffer(q);
9803 : 63 : appendPQExpBuffer(q,
9804 : : "SELECT c.tableoid, c.oid, conrelid, conname, "
9805 : : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9806 : : "conislocal, convalidated "
9807 : : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9808 : : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9809 : : "WHERE contype = 'c' "
9810 : : "ORDER BY c.conrelid, c.conname",
9811 : : checkoids->data);
9812 : :
9813 : 63 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9814 : :
9815 : 63 : numConstrs = PQntuples(res);
9816 : 63 : constrs = pg_malloc_array(ConstraintInfo, numConstrs);
9817 : :
9818 : 63 : i_tableoid = PQfnumber(res, "tableoid");
9819 : 63 : i_oid = PQfnumber(res, "oid");
9820 : 63 : i_conrelid = PQfnumber(res, "conrelid");
9821 : 63 : i_conname = PQfnumber(res, "conname");
9822 : 63 : i_consrc = PQfnumber(res, "consrc");
9823 : 63 : i_conislocal = PQfnumber(res, "conislocal");
9824 : 63 : i_convalidated = PQfnumber(res, "convalidated");
9825 : :
9826 : : /* As above, this loop iterates once per table, not once per row */
9827 : 63 : curtblindx = -1;
9828 [ + + ]: 556 : for (int j = 0; j < numConstrs;)
9829 : : {
9830 : 493 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9831 : 493 : TableInfo *tbinfo = NULL;
9832 : : int numcons;
9833 : :
9834 : : /* Count rows for this table */
9835 [ + + ]: 632 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9836 [ + + ]: 569 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9837 : 430 : break;
9838 : :
9839 : : /*
9840 : : * Locate the associated TableInfo; we rely on tblinfo[] being in
9841 : : * OID order.
9842 : : */
9843 [ + - ]: 20236 : while (++curtblindx < numTables)
9844 : : {
9845 : 20236 : tbinfo = &tblinfo[curtblindx];
9846 [ + + ]: 20236 : if (tbinfo->dobj.catId.oid == conrelid)
9847 : 493 : break;
9848 : : }
9849 [ - + ]: 493 : if (curtblindx >= numTables)
9850 : 0 : pg_fatal("unrecognized table OID %u", conrelid);
9851 : :
9852 [ - + ]: 493 : if (numcons != tbinfo->ncheck)
9853 : : {
9854 : 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9855 : : "expected %d check constraints on table \"%s\" but found %d",
9856 : : tbinfo->ncheck),
9857 : : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9858 : 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9859 : 0 : exit_nicely(1);
9860 : : }
9861 : :
9862 : 493 : tbinfo->checkexprs = constrs + j;
9863 : :
9864 [ + + ]: 1125 : for (int c = 0; c < numcons; c++, j++)
9865 : : {
9866 : 632 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9867 : :
9868 : 632 : constrs[j].dobj.objType = DO_CONSTRAINT;
9869 : 632 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9870 : 632 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9871 : 632 : AssignDumpId(&constrs[j].dobj);
9872 : 632 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9873 : 632 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9874 : 632 : constrs[j].contable = tbinfo;
9875 : 632 : constrs[j].condomain = NULL;
9876 : 632 : constrs[j].contype = 'c';
9877 : 632 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9878 : 632 : constrs[j].confrelid = InvalidOid;
9879 : 632 : constrs[j].conindex = 0;
9880 : 632 : constrs[j].condeferrable = false;
9881 : 632 : constrs[j].condeferred = false;
9882 : 632 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9883 : :
9884 : : /*
9885 : : * An unvalidated constraint needs to be dumped separately, so
9886 : : * that potentially-violating existing data is loaded before
9887 : : * the constraint.
9888 : : */
9889 : 632 : constrs[j].separate = !validated;
9890 : :
9891 : 632 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9892 : :
9893 : : /*
9894 : : * Mark the constraint as needing to appear before the table
9895 : : * --- this is so that any other dependencies of the
9896 : : * constraint will be emitted before we try to create the
9897 : : * table. If the constraint is to be dumped separately, it
9898 : : * will be dumped after data is loaded anyway, so don't do it.
9899 : : * (There's an automatic dependency in the opposite direction
9900 : : * anyway, so don't need to add one manually here.)
9901 : : */
9902 [ + + ]: 632 : if (!constrs[j].separate)
9903 : 567 : addObjectDependency(&tbinfo->dobj,
9904 : 567 : constrs[j].dobj.dumpId);
9905 : :
9906 : : /*
9907 : : * We will detect later whether the constraint must be split
9908 : : * out from the table definition.
9909 : : */
9910 : : }
9911 : : }
9912 : :
9913 : 63 : PQclear(res);
9914 : : }
9915 : :
9916 : 191 : destroyPQExpBuffer(q);
9917 : 191 : destroyPQExpBuffer(tbloids);
9918 : 191 : destroyPQExpBuffer(checkoids);
9919 : 191 : }
9920 : :
9921 : : /*
9922 : : * Based on the getTableAttrs query's row corresponding to one column, set
9923 : : * the name and flags to handle a not-null constraint for that column in
9924 : : * the tbinfo struct.
9925 : : *
9926 : : * Result row 'r' is for tbinfo's attribute 'j'.
9927 : : *
9928 : : * There are four possibilities:
9929 : : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9930 : : * (the constraint name) remains NULL.
9931 : : * 2) The column has a constraint with no name (this is the case when
9932 : : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9933 : : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9934 : : * 3) The column has an invalid not-null constraint. This must be treated
9935 : : * as a separate object (because it must be created after the table data
9936 : : * is loaded). So we add its OID to invalidnotnulloids for processing
9937 : : * elsewhere and do nothing further with it here. We distinguish this
9938 : : * case because the "notnull_invalidoid" column has been set to a non-NULL
9939 : : * value, which is the constraint OID. Valid constraints have a null OID.
9940 : : * 4) The column has a constraint with a known name; in that case
9941 : : * notnull_constrs carries that name and dumpTableSchema will print
9942 : : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9943 : : * (table_column_not_null) and there's no comment on the constraint,
9944 : : * there's no need to print that name in the dump, so notnull_constrs
9945 : : * is set to the empty string and it behaves as case 2.
9946 : : *
9947 : : * In a child table that inherits from a parent already containing NOT NULL
9948 : : * constraints and the columns in the child don't have their own NOT NULL
9949 : : * declarations, we suppress printing constraints in the child: the
9950 : : * constraints are acquired at the point where the child is attached to the
9951 : : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9952 : : * set not here but in flagInhAttrs. That flag is also used when the
9953 : : * constraint was validated in a child but all its parent have it as NOT
9954 : : * VALID.
9955 : : *
9956 : : * Any of these constraints might have the NO INHERIT bit. If so we set
9957 : : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9958 : : *
9959 : : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9960 : : * to do the right thing in all but the trivial case. However, the downside
9961 : : * of getting it wrong is simply that the name is printed rather than
9962 : : * suppressed, so it's not a big deal.
9963 : : *
9964 : : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9965 : : * constraints are found, it is initialized and filled with the array of
9966 : : * OIDs of such constraints, for later processing.
9967 : : */
9968 : : static void
9969 : 26559 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9970 : : TableInfo *tbinfo, int j,
9971 : : int i_notnull_name,
9972 : : int i_notnull_comment,
9973 : : int i_notnull_invalidoid,
9974 : : int i_notnull_noinherit,
9975 : : int i_notnull_islocal,
9976 : : PQExpBuffer *invalidnotnulloids)
9977 : : {
9978 : 26559 : DumpOptions *dopt = fout->dopt;
9979 : :
9980 : : /*
9981 : : * If this not-null constraint is not valid, list its OID in
9982 : : * invalidnotnulloids and do nothing further. It'll be processed
9983 : : * elsewhere later.
9984 : : *
9985 : : * Because invalid not-null constraints are rare, we don't want to malloc
9986 : : * invalidnotnulloids until we're sure we're going it need it, which
9987 : : * happens here.
9988 : : */
9989 [ + + ]: 26559 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
9990 : : {
9991 : 76 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9992 : :
9993 [ + + ]: 76 : if (*invalidnotnulloids == NULL)
9994 : : {
9995 : 46 : *invalidnotnulloids = createPQExpBuffer();
9996 : 46 : appendPQExpBufferChar(*invalidnotnulloids, '{');
9997 : 46 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
9998 : : }
9999 : : else
10000 : 30 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10001 : :
10002 : : /*
10003 : : * Track when a parent constraint is invalid for the cases where a
10004 : : * child constraint has been validated independenly.
10005 : : */
10006 : 76 : tbinfo->notnull_invalid[j] = true;
10007 : :
10008 : : /* nothing else to do */
10009 : 76 : tbinfo->notnull_constrs[j] = NULL;
10010 : 76 : return;
10011 : : }
10012 : :
10013 : : /*
10014 : : * notnull_noinh is straight from the query result. notnull_islocal also,
10015 : : * though flagInhAttrs may change that one later.
10016 : : */
10017 : 26483 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10018 : 26483 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10019 : 26483 : tbinfo->notnull_invalid[j] = false;
10020 : :
10021 : : /*
10022 : : * Determine a constraint name to use. If the column is not marked not-
10023 : : * null, we set NULL which cues ... to do nothing. An empty string says
10024 : : * to print an unnamed NOT NULL, and anything else is a constraint name to
10025 : : * use.
10026 : : */
10027 [ - + ]: 26483 : if (fout->remoteVersion < 180000)
10028 : : {
10029 : : /*
10030 : : * < 18 doesn't have not-null names, so an unnamed constraint is
10031 : : * sufficient.
10032 : : */
10033 [ # # ]: 0 : if (PQgetisnull(res, r, i_notnull_name))
10034 : 0 : tbinfo->notnull_constrs[j] = NULL;
10035 : : else
10036 : 0 : tbinfo->notnull_constrs[j] = "";
10037 : : }
10038 : : else
10039 : : {
10040 [ + + ]: 26483 : if (PQgetisnull(res, r, i_notnull_name))
10041 : 23512 : tbinfo->notnull_constrs[j] = NULL;
10042 : : else
10043 : : {
10044 : : /*
10045 : : * In binary upgrade of inheritance child tables, must have a
10046 : : * constraint name that we can UPDATE later; same if there's a
10047 : : * comment on the constraint.
10048 : : */
10049 [ + + ]: 2971 : if ((dopt->binary_upgrade &&
10050 [ + + ]: 373 : !tbinfo->ispartition &&
10051 [ + + + + ]: 3235 : !tbinfo->notnull_islocal[j]) ||
10052 : 2950 : !PQgetisnull(res, r, i_notnull_comment))
10053 : : {
10054 : 70 : tbinfo->notnull_constrs[j] =
10055 : 70 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10056 : : }
10057 : : else
10058 : : {
10059 : : char *default_name;
10060 : :
10061 : : /* XXX should match ChooseConstraintName better */
10062 : 2901 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10063 : 2901 : tbinfo->attnames[j]);
10064 [ + + ]: 2901 : if (strcmp(default_name,
10065 : 2901 : PQgetvalue(res, r, i_notnull_name)) == 0)
10066 : 1937 : tbinfo->notnull_constrs[j] = "";
10067 : : else
10068 : : {
10069 : 964 : tbinfo->notnull_constrs[j] =
10070 : 964 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10071 : : }
10072 : 2901 : pfree(default_name);
10073 : : }
10074 : : }
10075 : : }
10076 : : }
10077 : :
10078 : : /*
10079 : : * Test whether a column should be printed as part of table's CREATE TABLE.
10080 : : * Column number is zero-based.
10081 : : *
10082 : : * Normally this is always true, but it's false for dropped columns, as well
10083 : : * as those that were inherited without any local definition. (If we print
10084 : : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10085 : : * For partitions, it's always true, because we want the partitions to be
10086 : : * created independently and ATTACH PARTITION used afterwards.
10087 : : *
10088 : : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10089 : : * attisdropped state later, so as to keep control of the physical column
10090 : : * order.
10091 : : *
10092 : : * This function exists because there are scattered nonobvious places that
10093 : : * must be kept in sync with this decision.
10094 : : */
10095 : : bool
10096 : 42930 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10097 : : {
10098 [ + + ]: 42930 : if (dopt->binary_upgrade)
10099 : 6618 : return true;
10100 [ + + ]: 36312 : if (tbinfo->attisdropped[colno])
10101 : 738 : return false;
10102 [ + + + + ]: 35574 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10103 : : }
10104 : :
10105 : :
10106 : : /*
10107 : : * getTSParsers:
10108 : : * get information about all text search parsers in the system catalogs
10109 : : */
10110 : : void
10111 : 191 : getTSParsers(Archive *fout)
10112 : : {
10113 : : PGresult *res;
10114 : : int ntups;
10115 : : int i;
10116 : : PQExpBuffer query;
10117 : : TSParserInfo *prsinfo;
10118 : : int i_tableoid;
10119 : : int i_oid;
10120 : : int i_prsname;
10121 : : int i_prsnamespace;
10122 : : int i_prsstart;
10123 : : int i_prstoken;
10124 : : int i_prsend;
10125 : : int i_prsheadline;
10126 : : int i_prslextype;
10127 : :
10128 : 191 : query = createPQExpBuffer();
10129 : :
10130 : : /*
10131 : : * find all text search objects, including builtin ones; we filter out
10132 : : * system-defined objects at dump-out time.
10133 : : */
10134 : :
10135 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10136 : : "prsstart::oid, prstoken::oid, "
10137 : : "prsend::oid, prsheadline::oid, prslextype::oid "
10138 : : "FROM pg_ts_parser");
10139 : :
10140 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10141 : :
10142 : 191 : ntups = PQntuples(res);
10143 : :
10144 : 191 : prsinfo = pg_malloc_array(TSParserInfo, ntups);
10145 : :
10146 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10147 : 191 : i_oid = PQfnumber(res, "oid");
10148 : 191 : i_prsname = PQfnumber(res, "prsname");
10149 : 191 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10150 : 191 : i_prsstart = PQfnumber(res, "prsstart");
10151 : 191 : i_prstoken = PQfnumber(res, "prstoken");
10152 : 191 : i_prsend = PQfnumber(res, "prsend");
10153 : 191 : i_prsheadline = PQfnumber(res, "prsheadline");
10154 : 191 : i_prslextype = PQfnumber(res, "prslextype");
10155 : :
10156 [ + + ]: 430 : for (i = 0; i < ntups; i++)
10157 : : {
10158 : 239 : prsinfo[i].dobj.objType = DO_TSPARSER;
10159 : 239 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10160 : 239 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10161 : 239 : AssignDumpId(&prsinfo[i].dobj);
10162 : 239 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10163 : 478 : prsinfo[i].dobj.namespace =
10164 : 239 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10165 : 239 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10166 : 239 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10167 : 239 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10168 : 239 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10169 : 239 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10170 : :
10171 : : /* Decide whether we want to dump it */
10172 : 239 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10173 : : }
10174 : :
10175 : 191 : PQclear(res);
10176 : :
10177 : 191 : destroyPQExpBuffer(query);
10178 : 191 : }
10179 : :
10180 : : /*
10181 : : * getTSDictionaries:
10182 : : * get information about all text search dictionaries in the system catalogs
10183 : : */
10184 : : void
10185 : 191 : getTSDictionaries(Archive *fout)
10186 : : {
10187 : : PGresult *res;
10188 : : int ntups;
10189 : : int i;
10190 : : PQExpBuffer query;
10191 : : TSDictInfo *dictinfo;
10192 : : int i_tableoid;
10193 : : int i_oid;
10194 : : int i_dictname;
10195 : : int i_dictnamespace;
10196 : : int i_dictowner;
10197 : : int i_dicttemplate;
10198 : : int i_dictinitoption;
10199 : :
10200 : 191 : query = createPQExpBuffer();
10201 : :
10202 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10203 : : "dictnamespace, dictowner, "
10204 : : "dicttemplate, dictinitoption "
10205 : : "FROM pg_ts_dict");
10206 : :
10207 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10208 : :
10209 : 191 : ntups = PQntuples(res);
10210 : :
10211 : 191 : dictinfo = pg_malloc_array(TSDictInfo, ntups);
10212 : :
10213 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10214 : 191 : i_oid = PQfnumber(res, "oid");
10215 : 191 : i_dictname = PQfnumber(res, "dictname");
10216 : 191 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10217 : 191 : i_dictowner = PQfnumber(res, "dictowner");
10218 : 191 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10219 : 191 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10220 : :
10221 [ + + ]: 6414 : for (i = 0; i < ntups; i++)
10222 : : {
10223 : 6223 : dictinfo[i].dobj.objType = DO_TSDICT;
10224 : 6223 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10225 : 6223 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10226 : 6223 : AssignDumpId(&dictinfo[i].dobj);
10227 : 6223 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10228 : 12446 : dictinfo[i].dobj.namespace =
10229 : 6223 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10230 : 6223 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10231 : 6223 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10232 [ + + ]: 6223 : if (PQgetisnull(res, i, i_dictinitoption))
10233 : 239 : dictinfo[i].dictinitoption = NULL;
10234 : : else
10235 : 5984 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10236 : :
10237 : : /* Decide whether we want to dump it */
10238 : 6223 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10239 : : }
10240 : :
10241 : 191 : PQclear(res);
10242 : :
10243 : 191 : destroyPQExpBuffer(query);
10244 : 191 : }
10245 : :
10246 : : /*
10247 : : * getTSTemplates:
10248 : : * get information about all text search templates in the system catalogs
10249 : : */
10250 : : void
10251 : 191 : getTSTemplates(Archive *fout)
10252 : : {
10253 : : PGresult *res;
10254 : : int ntups;
10255 : : int i;
10256 : : PQExpBuffer query;
10257 : : TSTemplateInfo *tmplinfo;
10258 : : int i_tableoid;
10259 : : int i_oid;
10260 : : int i_tmplname;
10261 : : int i_tmplnamespace;
10262 : : int i_tmplinit;
10263 : : int i_tmpllexize;
10264 : :
10265 : 191 : query = createPQExpBuffer();
10266 : :
10267 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10268 : : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10269 : : "FROM pg_ts_template");
10270 : :
10271 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10272 : :
10273 : 191 : ntups = PQntuples(res);
10274 : :
10275 : 191 : tmplinfo = pg_malloc_array(TSTemplateInfo, ntups);
10276 : :
10277 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10278 : 191 : i_oid = PQfnumber(res, "oid");
10279 : 191 : i_tmplname = PQfnumber(res, "tmplname");
10280 : 191 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10281 : 191 : i_tmplinit = PQfnumber(res, "tmplinit");
10282 : 191 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10283 : :
10284 [ + + ]: 1194 : for (i = 0; i < ntups; i++)
10285 : : {
10286 : 1003 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10287 : 1003 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10288 : 1003 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10289 : 1003 : AssignDumpId(&tmplinfo[i].dobj);
10290 : 1003 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10291 : 2006 : tmplinfo[i].dobj.namespace =
10292 : 1003 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10293 : 1003 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10294 : 1003 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10295 : :
10296 : : /* Decide whether we want to dump it */
10297 : 1003 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10298 : : }
10299 : :
10300 : 191 : PQclear(res);
10301 : :
10302 : 191 : destroyPQExpBuffer(query);
10303 : 191 : }
10304 : :
10305 : : /*
10306 : : * getTSConfigurations:
10307 : : * get information about all text search configurations
10308 : : */
10309 : : void
10310 : 191 : getTSConfigurations(Archive *fout)
10311 : : {
10312 : : PGresult *res;
10313 : : int ntups;
10314 : : int i;
10315 : : PQExpBuffer query;
10316 : : TSConfigInfo *cfginfo;
10317 : : int i_tableoid;
10318 : : int i_oid;
10319 : : int i_cfgname;
10320 : : int i_cfgnamespace;
10321 : : int i_cfgowner;
10322 : : int i_cfgparser;
10323 : :
10324 : 191 : query = createPQExpBuffer();
10325 : :
10326 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10327 : : "cfgnamespace, cfgowner, cfgparser "
10328 : : "FROM pg_ts_config");
10329 : :
10330 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10331 : :
10332 : 191 : ntups = PQntuples(res);
10333 : :
10334 : 191 : cfginfo = pg_malloc_array(TSConfigInfo, ntups);
10335 : :
10336 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10337 : 191 : i_oid = PQfnumber(res, "oid");
10338 : 191 : i_cfgname = PQfnumber(res, "cfgname");
10339 : 191 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10340 : 191 : i_cfgowner = PQfnumber(res, "cfgowner");
10341 : 191 : i_cfgparser = PQfnumber(res, "cfgparser");
10342 : :
10343 [ + + ]: 6379 : for (i = 0; i < ntups; i++)
10344 : : {
10345 : 6188 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10346 : 6188 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10347 : 6188 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10348 : 6188 : AssignDumpId(&cfginfo[i].dobj);
10349 : 6188 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10350 : 12376 : cfginfo[i].dobj.namespace =
10351 : 6188 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10352 : 6188 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10353 : 6188 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10354 : :
10355 : : /* Decide whether we want to dump it */
10356 : 6188 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10357 : : }
10358 : :
10359 : 191 : PQclear(res);
10360 : :
10361 : 191 : destroyPQExpBuffer(query);
10362 : 191 : }
10363 : :
10364 : : /*
10365 : : * getForeignDataWrappers:
10366 : : * get information about all foreign-data wrappers in the system catalogs
10367 : : */
10368 : : void
10369 : 191 : getForeignDataWrappers(Archive *fout)
10370 : : {
10371 : : PGresult *res;
10372 : : int ntups;
10373 : : int i;
10374 : : PQExpBuffer query;
10375 : : FdwInfo *fdwinfo;
10376 : : int i_tableoid;
10377 : : int i_oid;
10378 : : int i_fdwname;
10379 : : int i_fdwowner;
10380 : : int i_fdwhandler;
10381 : : int i_fdwvalidator;
10382 : : int i_fdwconnection;
10383 : : int i_fdwacl;
10384 : : int i_acldefault;
10385 : : int i_fdwoptions;
10386 : :
10387 : 191 : query = createPQExpBuffer();
10388 : :
10389 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10390 : : "fdwowner, "
10391 : : "fdwhandler::pg_catalog.regproc, "
10392 : : "fdwvalidator::pg_catalog.regproc, ");
10393 : :
10394 [ + - ]: 191 : if (fout->remoteVersion >= 190000)
10395 : 191 : appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10396 : : else
10397 : 0 : appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10398 : :
10399 : 191 : appendPQExpBufferStr(query,
10400 : : "fdwacl, "
10401 : : "acldefault('F', fdwowner) AS acldefault, "
10402 : : "array_to_string(ARRAY("
10403 : : "SELECT quote_ident(option_name) || ' ' || "
10404 : : "quote_literal(option_value) "
10405 : : "FROM pg_options_to_table(fdwoptions) "
10406 : : "ORDER BY option_name"
10407 : : "), E',\n ') AS fdwoptions "
10408 : : "FROM pg_foreign_data_wrapper");
10409 : :
10410 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10411 : :
10412 : 191 : ntups = PQntuples(res);
10413 : :
10414 : 191 : fdwinfo = pg_malloc_array(FdwInfo, ntups);
10415 : :
10416 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10417 : 191 : i_oid = PQfnumber(res, "oid");
10418 : 191 : i_fdwname = PQfnumber(res, "fdwname");
10419 : 191 : i_fdwowner = PQfnumber(res, "fdwowner");
10420 : 191 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10421 : 191 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10422 : 191 : i_fdwconnection = PQfnumber(res, "fdwconnection");
10423 : 191 : i_fdwacl = PQfnumber(res, "fdwacl");
10424 : 191 : i_acldefault = PQfnumber(res, "acldefault");
10425 : 191 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10426 : :
10427 [ + + ]: 265 : for (i = 0; i < ntups; i++)
10428 : : {
10429 : 74 : fdwinfo[i].dobj.objType = DO_FDW;
10430 : 74 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10431 : 74 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10432 : 74 : AssignDumpId(&fdwinfo[i].dobj);
10433 : 74 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10434 : 74 : fdwinfo[i].dobj.namespace = NULL;
10435 : 74 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10436 : 74 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10437 : 74 : fdwinfo[i].dacl.privtype = 0;
10438 : 74 : fdwinfo[i].dacl.initprivs = NULL;
10439 : 74 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10440 : 74 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10441 : 74 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10442 : 74 : fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10443 : 74 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10444 : :
10445 : : /* Decide whether we want to dump it */
10446 : 74 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10447 : :
10448 : : /* Mark whether FDW has an ACL */
10449 [ + + ]: 74 : if (!PQgetisnull(res, i, i_fdwacl))
10450 : 48 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10451 : : }
10452 : :
10453 : 191 : PQclear(res);
10454 : :
10455 : 191 : destroyPQExpBuffer(query);
10456 : 191 : }
10457 : :
10458 : : /*
10459 : : * getForeignServers:
10460 : : * get information about all foreign servers in the system catalogs
10461 : : */
10462 : : void
10463 : 191 : getForeignServers(Archive *fout)
10464 : : {
10465 : : PGresult *res;
10466 : : int ntups;
10467 : : int i;
10468 : : PQExpBuffer query;
10469 : : ForeignServerInfo *srvinfo;
10470 : : int i_tableoid;
10471 : : int i_oid;
10472 : : int i_srvname;
10473 : : int i_srvowner;
10474 : : int i_srvfdw;
10475 : : int i_srvtype;
10476 : : int i_srvversion;
10477 : : int i_srvacl;
10478 : : int i_acldefault;
10479 : : int i_srvoptions;
10480 : :
10481 : 191 : query = createPQExpBuffer();
10482 : :
10483 : 191 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10484 : : "srvowner, "
10485 : : "srvfdw, srvtype, srvversion, srvacl, "
10486 : : "acldefault('S', srvowner) AS acldefault, "
10487 : : "array_to_string(ARRAY("
10488 : : "SELECT quote_ident(option_name) || ' ' || "
10489 : : "quote_literal(option_value) "
10490 : : "FROM pg_options_to_table(srvoptions) "
10491 : : "ORDER BY option_name"
10492 : : "), E',\n ') AS srvoptions "
10493 : : "FROM pg_foreign_server");
10494 : :
10495 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10496 : :
10497 : 191 : ntups = PQntuples(res);
10498 : :
10499 : 191 : srvinfo = pg_malloc_array(ForeignServerInfo, ntups);
10500 : :
10501 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10502 : 191 : i_oid = PQfnumber(res, "oid");
10503 : 191 : i_srvname = PQfnumber(res, "srvname");
10504 : 191 : i_srvowner = PQfnumber(res, "srvowner");
10505 : 191 : i_srvfdw = PQfnumber(res, "srvfdw");
10506 : 191 : i_srvtype = PQfnumber(res, "srvtype");
10507 : 191 : i_srvversion = PQfnumber(res, "srvversion");
10508 : 191 : i_srvacl = PQfnumber(res, "srvacl");
10509 : 191 : i_acldefault = PQfnumber(res, "acldefault");
10510 : 191 : i_srvoptions = PQfnumber(res, "srvoptions");
10511 : :
10512 [ + + ]: 269 : for (i = 0; i < ntups; i++)
10513 : : {
10514 : 78 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10515 : 78 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10516 : 78 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10517 : 78 : AssignDumpId(&srvinfo[i].dobj);
10518 : 78 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10519 : 78 : srvinfo[i].dobj.namespace = NULL;
10520 : 78 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10521 : 78 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10522 : 78 : srvinfo[i].dacl.privtype = 0;
10523 : 78 : srvinfo[i].dacl.initprivs = NULL;
10524 : 78 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10525 : 78 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10526 : 78 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10527 : 78 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10528 : 78 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10529 : :
10530 : : /* Decide whether we want to dump it */
10531 : 78 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10532 : :
10533 : : /* Servers have user mappings */
10534 : 78 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10535 : :
10536 : : /* Mark whether server has an ACL */
10537 [ + + ]: 78 : if (!PQgetisnull(res, i, i_srvacl))
10538 : 48 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10539 : : }
10540 : :
10541 : 191 : PQclear(res);
10542 : :
10543 : 191 : destroyPQExpBuffer(query);
10544 : 191 : }
10545 : :
10546 : : /*
10547 : : * getDefaultACLs:
10548 : : * get information about all default ACL information in the system catalogs
10549 : : */
10550 : : void
10551 : 191 : getDefaultACLs(Archive *fout)
10552 : : {
10553 : 191 : DumpOptions *dopt = fout->dopt;
10554 : : DefaultACLInfo *daclinfo;
10555 : : PQExpBuffer query;
10556 : : PGresult *res;
10557 : : int i_oid;
10558 : : int i_tableoid;
10559 : : int i_defaclrole;
10560 : : int i_defaclnamespace;
10561 : : int i_defaclobjtype;
10562 : : int i_defaclacl;
10563 : : int i_acldefault;
10564 : : int i,
10565 : : ntups;
10566 : :
10567 : 191 : query = createPQExpBuffer();
10568 : :
10569 : : /*
10570 : : * Global entries (with defaclnamespace=0) replace the hard-wired default
10571 : : * ACL for their object type. We should dump them as deltas from the
10572 : : * default ACL, since that will be used as a starting point for
10573 : : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10574 : : * non-global entries can only add privileges not revoke them. We must
10575 : : * dump those as-is (i.e., as deltas from an empty ACL).
10576 : : *
10577 : : * We can use defaclobjtype as the object type for acldefault(), except
10578 : : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10579 : : * 's'.
10580 : : */
10581 : 191 : appendPQExpBufferStr(query,
10582 : : "SELECT oid, tableoid, "
10583 : : "defaclrole, "
10584 : : "defaclnamespace, "
10585 : : "defaclobjtype, "
10586 : : "defaclacl, "
10587 : : "CASE WHEN defaclnamespace = 0 THEN "
10588 : : "acldefault(CASE WHEN defaclobjtype = 'S' "
10589 : : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10590 : : "defaclrole) ELSE '{}' END AS acldefault "
10591 : : "FROM pg_default_acl");
10592 : :
10593 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10594 : :
10595 : 191 : ntups = PQntuples(res);
10596 : :
10597 : 191 : daclinfo = pg_malloc_array(DefaultACLInfo, ntups);
10598 : :
10599 : 191 : i_oid = PQfnumber(res, "oid");
10600 : 191 : i_tableoid = PQfnumber(res, "tableoid");
10601 : 191 : i_defaclrole = PQfnumber(res, "defaclrole");
10602 : 191 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10603 : 191 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10604 : 191 : i_defaclacl = PQfnumber(res, "defaclacl");
10605 : 191 : i_acldefault = PQfnumber(res, "acldefault");
10606 : :
10607 [ + + ]: 397 : for (i = 0; i < ntups; i++)
10608 : : {
10609 : 206 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10610 : :
10611 : 206 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10612 : 206 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10613 : 206 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10614 : 206 : AssignDumpId(&daclinfo[i].dobj);
10615 : : /* cheesy ... is it worth coming up with a better object name? */
10616 : 206 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10617 : :
10618 [ + + ]: 206 : if (nspid != InvalidOid)
10619 : 96 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10620 : : else
10621 : 110 : daclinfo[i].dobj.namespace = NULL;
10622 : :
10623 : 206 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10624 : 206 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10625 : 206 : daclinfo[i].dacl.privtype = 0;
10626 : 206 : daclinfo[i].dacl.initprivs = NULL;
10627 : 206 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10628 : 206 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10629 : :
10630 : : /* Default ACLs are ACLs, of course */
10631 : 206 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10632 : :
10633 : : /* Decide whether we want to dump it */
10634 : 206 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10635 : : }
10636 : :
10637 : 191 : PQclear(res);
10638 : :
10639 : 191 : destroyPQExpBuffer(query);
10640 : 191 : }
10641 : :
10642 : : /*
10643 : : * getRoleName -- look up the name of a role, given its OID
10644 : : *
10645 : : * In current usage, we don't expect failures, so error out for a bad OID.
10646 : : */
10647 : : static const char *
10648 : 628063 : getRoleName(const char *roleoid_str)
10649 : : {
10650 : 628063 : Oid roleoid = atooid(roleoid_str);
10651 : :
10652 : : /*
10653 : : * Do binary search to find the appropriate item.
10654 : : */
10655 [ + - ]: 628063 : if (nrolenames > 0)
10656 : : {
10657 : 628063 : RoleNameItem *low = &rolenames[0];
10658 : 628063 : RoleNameItem *high = &rolenames[nrolenames - 1];
10659 : :
10660 [ + - ]: 2512433 : while (low <= high)
10661 : : {
10662 : 2512433 : RoleNameItem *middle = low + (high - low) / 2;
10663 : :
10664 [ + + ]: 2512433 : if (roleoid < middle->roleoid)
10665 : 1882984 : high = middle - 1;
10666 [ + + ]: 629449 : else if (roleoid > middle->roleoid)
10667 : 1386 : low = middle + 1;
10668 : : else
10669 : 628063 : return middle->rolename; /* found a match */
10670 : : }
10671 : : }
10672 : :
10673 : 0 : pg_fatal("role with OID %u does not exist", roleoid);
10674 : : return NULL; /* keep compiler quiet */
10675 : : }
10676 : :
10677 : : /*
10678 : : * collectRoleNames --
10679 : : *
10680 : : * Construct a table of all known roles.
10681 : : * The table is sorted by OID for speed in lookup.
10682 : : */
10683 : : static void
10684 : 192 : collectRoleNames(Archive *fout)
10685 : : {
10686 : : PGresult *res;
10687 : : const char *query;
10688 : : int i;
10689 : :
10690 : 192 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10691 : :
10692 : 192 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10693 : :
10694 : 192 : nrolenames = PQntuples(res);
10695 : :
10696 : 192 : rolenames = pg_malloc_array(RoleNameItem, nrolenames);
10697 : :
10698 [ + + ]: 3702 : for (i = 0; i < nrolenames; i++)
10699 : : {
10700 : 3510 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10701 : 3510 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10702 : : }
10703 : :
10704 : 192 : PQclear(res);
10705 : 192 : }
10706 : :
10707 : : /*
10708 : : * getAdditionalACLs
10709 : : *
10710 : : * We have now created all the DumpableObjects, and collected the ACL data
10711 : : * that appears in the directly-associated catalog entries. However, there's
10712 : : * more ACL-related info to collect. If any of a table's columns have ACLs,
10713 : : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10714 : : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10715 : : * Also, in versions having the pg_init_privs catalog, read that and load the
10716 : : * information into the relevant DumpableObjects.
10717 : : */
10718 : : static void
10719 : 189 : getAdditionalACLs(Archive *fout)
10720 : : {
10721 : 189 : PQExpBuffer query = createPQExpBuffer();
10722 : : PGresult *res;
10723 : : int ntups,
10724 : : i;
10725 : :
10726 : : /* Check for per-column ACLs */
10727 : 189 : appendPQExpBufferStr(query,
10728 : : "SELECT DISTINCT attrelid FROM pg_attribute "
10729 : : "WHERE attacl IS NOT NULL");
10730 : :
10731 : 189 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10732 : :
10733 : 189 : ntups = PQntuples(res);
10734 [ + + ]: 557 : for (i = 0; i < ntups; i++)
10735 : : {
10736 : 368 : Oid relid = atooid(PQgetvalue(res, i, 0));
10737 : : TableInfo *tblinfo;
10738 : :
10739 : 368 : tblinfo = findTableByOid(relid);
10740 : : /* OK to ignore tables we haven't got a DumpableObject for */
10741 [ + - ]: 368 : if (tblinfo)
10742 : : {
10743 : 368 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10744 : 368 : tblinfo->hascolumnACLs = true;
10745 : : }
10746 : : }
10747 : 189 : PQclear(res);
10748 : :
10749 : : /* Fetch initial-privileges data */
10750 : 189 : printfPQExpBuffer(query,
10751 : : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10752 : : "FROM pg_init_privs");
10753 : :
10754 : 189 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10755 : :
10756 : 189 : ntups = PQntuples(res);
10757 [ + + ]: 48314 : for (i = 0; i < ntups; i++)
10758 : : {
10759 : 48125 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10760 : 48125 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10761 : 48125 : int objsubid = atoi(PQgetvalue(res, i, 2));
10762 : 48125 : char privtype = *(PQgetvalue(res, i, 3));
10763 : 48125 : char *initprivs = PQgetvalue(res, i, 4);
10764 : : CatalogId objId;
10765 : : DumpableObject *dobj;
10766 : :
10767 : 48125 : objId.tableoid = classoid;
10768 : 48125 : objId.oid = objoid;
10769 : 48125 : dobj = findObjectByCatalogId(objId);
10770 : : /* OK to ignore entries we haven't got a DumpableObject for */
10771 [ + + ]: 48125 : if (dobj)
10772 : : {
10773 : : /* Cope with sub-object initprivs */
10774 [ + + ]: 35131 : if (objsubid != 0)
10775 : : {
10776 [ + - ]: 4560 : if (dobj->objType == DO_TABLE)
10777 : : {
10778 : : /* For a column initprivs, set the table's ACL flags */
10779 : 4560 : dobj->components |= DUMP_COMPONENT_ACL;
10780 : 4560 : ((TableInfo *) dobj)->hascolumnACLs = true;
10781 : : }
10782 : : else
10783 : 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10784 : : classoid, objoid, objsubid);
10785 : 4745 : continue;
10786 : : }
10787 : :
10788 : : /*
10789 : : * We ignore any pg_init_privs.initprivs entry for the public
10790 : : * schema, as explained in getNamespaces().
10791 : : */
10792 [ + + ]: 30571 : if (dobj->objType == DO_NAMESPACE &&
10793 [ + + ]: 563 : strcmp(dobj->name, "public") == 0)
10794 : 185 : continue;
10795 : :
10796 : : /* Else it had better be of a type we think has ACLs */
10797 [ + + ]: 30386 : if (dobj->objType == DO_NAMESPACE ||
10798 [ + + ]: 30008 : dobj->objType == DO_TYPE ||
10799 [ + + ]: 29984 : dobj->objType == DO_FUNC ||
10800 [ + + ]: 29889 : dobj->objType == DO_AGG ||
10801 [ - + ]: 29865 : dobj->objType == DO_TABLE ||
10802 [ # # ]: 0 : dobj->objType == DO_PROCLANG ||
10803 [ # # ]: 0 : dobj->objType == DO_FDW ||
10804 [ # # ]: 0 : dobj->objType == DO_FOREIGN_SERVER)
10805 : 30386 : {
10806 : 30386 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10807 : :
10808 : 30386 : daobj->dacl.privtype = privtype;
10809 : 30386 : daobj->dacl.initprivs = pstrdup(initprivs);
10810 : : }
10811 : : else
10812 : 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10813 : : classoid, objoid, objsubid);
10814 : : }
10815 : : }
10816 : 189 : PQclear(res);
10817 : :
10818 : 189 : destroyPQExpBuffer(query);
10819 : 189 : }
10820 : :
10821 : : /*
10822 : : * dumpCommentExtended --
10823 : : *
10824 : : * This routine is used to dump any comments associated with the
10825 : : * object handed to this routine. The routine takes the object type
10826 : : * and object name (ready to print, except for schema decoration), plus
10827 : : * the namespace and owner of the object (for labeling the ArchiveEntry),
10828 : : * plus catalog ID and subid which are the lookup key for pg_description,
10829 : : * plus the dump ID for the object (for setting a dependency).
10830 : : * If a matching pg_description entry is found, it is dumped.
10831 : : *
10832 : : * Note: in some cases, such as comments for triggers and rules, the "type"
10833 : : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10834 : : * but it doesn't seem worth complicating the API for all callers to make
10835 : : * it cleaner.
10836 : : *
10837 : : * Note: although this routine takes a dumpId for dependency purposes,
10838 : : * that purpose is just to mark the dependency in the emitted dump file
10839 : : * for possible future use by pg_restore. We do NOT use it for determining
10840 : : * ordering of the comment in the dump file, because this routine is called
10841 : : * after dependency sorting occurs. This routine should be called just after
10842 : : * calling ArchiveEntry() for the specified object.
10843 : : */
10844 : : static void
10845 : 6643 : dumpCommentExtended(Archive *fout, const char *type,
10846 : : const char *name, const char *namespace,
10847 : : const char *owner, CatalogId catalogId,
10848 : : int subid, DumpId dumpId,
10849 : : const char *initdb_comment)
10850 : : {
10851 : 6643 : DumpOptions *dopt = fout->dopt;
10852 : : CommentItem *comments;
10853 : : int ncomments;
10854 : :
10855 : : /* do nothing, if --no-comments is supplied */
10856 [ - + ]: 6643 : if (dopt->no_comments)
10857 : 0 : return;
10858 : :
10859 : : /* Comments are schema not data ... except LO comments are data */
10860 [ + + ]: 6643 : if (strcmp(type, "LARGE OBJECT") != 0)
10861 : : {
10862 [ - + ]: 6583 : if (!dopt->dumpSchema)
10863 : 0 : return;
10864 : : }
10865 : : else
10866 : : {
10867 : : /* We do dump LO comments in binary-upgrade mode */
10868 [ + + - + ]: 60 : if (!dopt->dumpData && !dopt->binary_upgrade)
10869 : 0 : return;
10870 : : }
10871 : :
10872 : : /* Search for comments associated with catalogId, using table */
10873 : 6643 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10874 : : &comments);
10875 : :
10876 : : /* Is there one matching the subid? */
10877 [ + + ]: 6643 : while (ncomments > 0)
10878 : : {
10879 [ + - ]: 6596 : if (comments->objsubid == subid)
10880 : 6596 : break;
10881 : 0 : comments++;
10882 : 0 : ncomments--;
10883 : : }
10884 : :
10885 [ + + ]: 6643 : if (initdb_comment != NULL)
10886 : : {
10887 : : static CommentItem empty_comment = {.descr = ""};
10888 : :
10889 : : /*
10890 : : * initdb creates this object with a comment. Skip dumping the
10891 : : * initdb-provided comment, which would complicate matters for
10892 : : * non-superuser use of pg_dump. When the DBA has removed initdb's
10893 : : * comment, replicate that.
10894 : : */
10895 [ + + ]: 117 : if (ncomments == 0)
10896 : : {
10897 : 4 : comments = &empty_comment;
10898 : 4 : ncomments = 1;
10899 : : }
10900 [ + - ]: 113 : else if (strcmp(comments->descr, initdb_comment) == 0)
10901 : 113 : ncomments = 0;
10902 : : }
10903 : :
10904 : : /* If a comment exists, build COMMENT ON statement */
10905 [ + + ]: 6643 : if (ncomments > 0)
10906 : : {
10907 : 6487 : PQExpBuffer query = createPQExpBuffer();
10908 : 6487 : PQExpBuffer tag = createPQExpBuffer();
10909 : :
10910 : 6487 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10911 [ + + + - ]: 6487 : if (namespace && *namespace)
10912 : 6300 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10913 : 6487 : appendPQExpBuffer(query, "%s IS ", name);
10914 : 6487 : appendStringLiteralAH(query, comments->descr, fout);
10915 : 6487 : appendPQExpBufferStr(query, ";\n");
10916 : :
10917 : 6487 : appendPQExpBuffer(tag, "%s %s", type, name);
10918 : :
10919 : : /*
10920 : : * We mark comments as SECTION_NONE because they really belong in the
10921 : : * same section as their parent, whether that is pre-data or
10922 : : * post-data.
10923 : : */
10924 : 6487 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10925 : 6487 : ARCHIVE_OPTS(.tag = tag->data,
10926 : : .namespace = namespace,
10927 : : .owner = owner,
10928 : : .description = "COMMENT",
10929 : : .section = SECTION_NONE,
10930 : : .createStmt = query->data,
10931 : : .deps = &dumpId,
10932 : : .nDeps = 1));
10933 : :
10934 : 6487 : destroyPQExpBuffer(query);
10935 : 6487 : destroyPQExpBuffer(tag);
10936 : : }
10937 : : }
10938 : :
10939 : : /*
10940 : : * dumpComment --
10941 : : *
10942 : : * Typical simplification of the above function.
10943 : : */
10944 : : static inline void
10945 : 6483 : dumpComment(Archive *fout, const char *type,
10946 : : const char *name, const char *namespace,
10947 : : const char *owner, CatalogId catalogId,
10948 : : int subid, DumpId dumpId)
10949 : : {
10950 : 6483 : dumpCommentExtended(fout, type, name, namespace, owner,
10951 : : catalogId, subid, dumpId, NULL);
10952 : 6483 : }
10953 : :
10954 : : /*
10955 : : * appendNamedArgument --
10956 : : *
10957 : : * Convenience routine for constructing parameters of the form:
10958 : : * 'paraname', 'value'::type
10959 : : */
10960 : : static void
10961 : 5825 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10962 : : const char *argtype, const char *argval)
10963 : : {
10964 : 5825 : appendPQExpBufferStr(out, ",\n\t");
10965 : :
10966 : 5825 : appendStringLiteralAH(out, argname, fout);
10967 : 5825 : appendPQExpBufferStr(out, ", ");
10968 : :
10969 : 5825 : appendStringLiteralAH(out, argval, fout);
10970 : 5825 : appendPQExpBuffer(out, "::%s", argtype);
10971 : 5825 : }
10972 : :
10973 : : /*
10974 : : * fetchAttributeStats --
10975 : : *
10976 : : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10977 : : */
10978 : : static PGresult *
10979 : 1104 : fetchAttributeStats(Archive *fout)
10980 : : {
10981 : 1104 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10982 : 1104 : PQExpBuffer relids = createPQExpBuffer();
10983 : 1104 : PQExpBuffer nspnames = createPQExpBuffer();
10984 : 1104 : PQExpBuffer relnames = createPQExpBuffer();
10985 : 1104 : int count = 0;
10986 : 1104 : PGresult *res = NULL;
10987 : : static TocEntry *te;
10988 : : static bool restarted;
10989 : 1104 : int max_rels = MAX_ATTR_STATS_RELS;
10990 : :
10991 : : /* If we're just starting, set our TOC pointer. */
10992 [ + + ]: 1104 : if (!te)
10993 : 65 : te = AH->toc->next;
10994 : :
10995 : : /*
10996 : : * We can't easily avoid a second TOC scan for the tar format because it
10997 : : * writes restore.sql separately, which means we must execute the queries
10998 : : * twice. This feels risky, but there is no known reason it should
10999 : : * generate different output than the first pass. Even if it does, the
11000 : : * worst-case scenario is that restore.sql might have different statistics
11001 : : * data than the archive.
11002 : : */
11003 [ + + + + : 1104 : if (!restarted && te == AH->toc && AH->format == archTar)
+ + ]
11004 : : {
11005 : 1 : te = AH->toc->next;
11006 : 1 : restarted = true;
11007 : : }
11008 : :
11009 : 1104 : appendPQExpBufferChar(relids, '{');
11010 : 1104 : appendPQExpBufferChar(nspnames, '{');
11011 : 1104 : appendPQExpBufferChar(relnames, '{');
11012 : :
11013 : : /*
11014 : : * Scan the TOC for the next set of relevant stats entries. We assume
11015 : : * that statistics are dumped in the order they are listed in the TOC.
11016 : : * This is perhaps not the sturdiest assumption, so we verify it matches
11017 : : * reality in dumpRelationStats_dumper().
11018 : : */
11019 [ + + + + ]: 17285 : for (; te != AH->toc && count < max_rels; te = te->next)
11020 : : {
11021 [ + + ]: 16181 : if ((te->reqs & REQ_STATS) == 0 ||
11022 [ + + ]: 3650 : strcmp(te->desc, "STATISTICS DATA") != 0)
11023 : 12569 : continue;
11024 : :
11025 [ + - ]: 3612 : if (fout->remoteVersion >= 190000)
11026 : : {
11027 : 3612 : const RelStatsInfo *rsinfo = (const RelStatsInfo *) te->defnDumperArg;
11028 : : char relid[32];
11029 : :
11030 : 3612 : sprintf(relid, "%u", rsinfo->relid);
11031 : 3612 : appendPGArray(relids, relid);
11032 : : }
11033 : : else
11034 : : {
11035 : 0 : appendPGArray(nspnames, te->namespace);
11036 : 0 : appendPGArray(relnames, te->tag);
11037 : : }
11038 : :
11039 : 3612 : count++;
11040 : : }
11041 : :
11042 : 1104 : appendPQExpBufferChar(relids, '}');
11043 : 1104 : appendPQExpBufferChar(nspnames, '}');
11044 : 1104 : appendPQExpBufferChar(relnames, '}');
11045 : :
11046 : : /* Execute the query for the next batch of relations. */
11047 [ + + ]: 1104 : if (count > 0)
11048 : : {
11049 : 112 : PQExpBuffer query = createPQExpBuffer();
11050 : :
11051 : 112 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11052 : :
11053 [ + - ]: 112 : if (fout->remoteVersion >= 190000)
11054 : : {
11055 : 112 : appendStringLiteralAH(query, relids->data, fout);
11056 : 112 : appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11057 : : }
11058 : : else
11059 : : {
11060 : 0 : appendStringLiteralAH(query, nspnames->data, fout);
11061 : 0 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11062 : 0 : appendStringLiteralAH(query, relnames->data, fout);
11063 : 0 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11064 : : }
11065 : :
11066 : 112 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11067 : 112 : destroyPQExpBuffer(query);
11068 : : }
11069 : :
11070 : 1104 : destroyPQExpBuffer(relids);
11071 : 1104 : destroyPQExpBuffer(nspnames);
11072 : 1104 : destroyPQExpBuffer(relnames);
11073 : 1104 : return res;
11074 : : }
11075 : :
11076 : : /*
11077 : : * dumpRelationStats_dumper --
11078 : : *
11079 : : * Generate command to import stats into the relation on the new database.
11080 : : * This routine is called by the Archiver when it wants the statistics to be
11081 : : * dumped.
11082 : : */
11083 : : static char *
11084 : 3612 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11085 : : {
11086 : 3612 : const RelStatsInfo *rsinfo = userArg;
11087 : : static PGresult *res;
11088 : : static int rownum;
11089 : : PQExpBuffer query;
11090 : : PQExpBufferData out_data;
11091 : 3612 : PQExpBuffer out = &out_data;
11092 : : int i_schemaname;
11093 : : int i_tablename;
11094 : : int i_attname;
11095 : : int i_inherited;
11096 : : int i_null_frac;
11097 : : int i_avg_width;
11098 : : int i_n_distinct;
11099 : : int i_most_common_vals;
11100 : : int i_most_common_freqs;
11101 : : int i_histogram_bounds;
11102 : : int i_correlation;
11103 : : int i_most_common_elems;
11104 : : int i_most_common_elem_freqs;
11105 : : int i_elem_count_histogram;
11106 : : int i_range_length_histogram;
11107 : : int i_range_empty_frac;
11108 : : int i_range_bounds_histogram;
11109 : : static TocEntry *expected_te;
11110 : :
11111 : : /*
11112 : : * fetchAttributeStats() assumes that the statistics are dumped in the
11113 : : * order they are listed in the TOC. We verify that here for safety.
11114 : : */
11115 [ + + ]: 3612 : if (!expected_te)
11116 : 65 : expected_te = ((ArchiveHandle *) fout)->toc;
11117 : :
11118 : 3612 : expected_te = expected_te->next;
11119 [ + + ]: 14129 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11120 [ + + ]: 3613 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11121 : 10517 : expected_te = expected_te->next;
11122 : :
11123 [ - + ]: 3612 : if (te != expected_te)
11124 : 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11125 : : te->dumpId, te->desc, te->tag,
11126 : : expected_te->dumpId, expected_te->desc, expected_te->tag);
11127 : :
11128 : 3612 : query = createPQExpBuffer();
11129 [ + + ]: 3612 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11130 : : {
11131 [ + - ]: 65 : if (fout->remoteVersion >= 190000)
11132 : 65 : appendPQExpBufferStr(query,
11133 : : "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11134 : : else
11135 : 0 : appendPQExpBufferStr(query,
11136 : : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11137 : :
11138 : 65 : appendPQExpBufferStr(query,
11139 : : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11140 : : "s.null_frac, s.avg_width, s.n_distinct, "
11141 : : "s.most_common_vals, s.most_common_freqs, "
11142 : : "s.histogram_bounds, s.correlation, "
11143 : : "s.most_common_elems, s.most_common_elem_freqs, "
11144 : : "s.elem_count_histogram, ");
11145 : :
11146 [ + - ]: 65 : if (fout->remoteVersion >= 170000)
11147 : 65 : appendPQExpBufferStr(query,
11148 : : "s.range_length_histogram, "
11149 : : "s.range_empty_frac, "
11150 : : "s.range_bounds_histogram ");
11151 : : else
11152 : 0 : appendPQExpBufferStr(query,
11153 : : "NULL AS range_length_histogram,"
11154 : : "NULL AS range_empty_frac,"
11155 : : "NULL AS range_bounds_histogram ");
11156 : :
11157 : : /*
11158 : : * The results must be in the order of the relations supplied in the
11159 : : * parameters to ensure we remain in sync as we walk through the TOC.
11160 : : *
11161 : : * For versions before 19, the redundant filter clause on s.tablename
11162 : : * = ANY(...) seems sufficient to convince the planner to use
11163 : : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11164 : : * In newer versions, pg_stats returns the table OIDs, eliminating the
11165 : : * need for that hack.
11166 : : */
11167 [ + - ]: 65 : if (fout->remoteVersion >= 190000)
11168 : 65 : appendPQExpBufferStr(query,
11169 : : "FROM pg_catalog.pg_stats s "
11170 : : "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11171 : : "ON s.tableid = u.tableid "
11172 : : "ORDER BY u.ord, s.attname, s.inherited");
11173 : : else
11174 : 0 : appendPQExpBufferStr(query,
11175 : : "FROM pg_catalog.pg_stats s "
11176 : : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11177 : : "ON s.schemaname = u.schemaname "
11178 : : "AND s.tablename = u.tablename "
11179 : : "WHERE s.tablename = ANY($2) "
11180 : : "ORDER BY u.ord, s.attname, s.inherited");
11181 : :
11182 : 65 : ExecuteSqlStatement(fout, query->data);
11183 : :
11184 : 65 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11185 : 65 : resetPQExpBuffer(query);
11186 : : }
11187 : :
11188 : 3612 : initPQExpBuffer(out);
11189 : :
11190 : : /* restore relation stats */
11191 : 3612 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11192 : 3612 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11193 : : fout->remoteVersion);
11194 : 3612 : appendPQExpBufferStr(out, "\t'schemaname', ");
11195 : 3612 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11196 : 3612 : appendPQExpBufferStr(out, ",\n");
11197 : 3612 : appendPQExpBufferStr(out, "\t'relname', ");
11198 : 3612 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11199 : 3612 : appendPQExpBufferStr(out, ",\n");
11200 : 3612 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11201 : :
11202 : : /*
11203 : : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11204 : : * the relation is empty, or it could mean that it hadn't yet been
11205 : : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11206 : : * This ambiguity allegedly can cause the planner to choose inefficient
11207 : : * plans after restoring to v18 or newer. To deal with this, let's just
11208 : : * set reltuples to -1 in that case.
11209 : : */
11210 [ - + - - ]: 3612 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11211 : 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11212 : : else
11213 : 3612 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11214 : :
11215 : 3612 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11216 : 3612 : rsinfo->relallvisible);
11217 : :
11218 [ + - ]: 3612 : if (fout->remoteVersion >= 180000)
11219 : 3612 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11220 : :
11221 : 3612 : appendPQExpBufferStr(out, "\n);\n");
11222 : :
11223 : : /* Fetch the next batch of attribute statistics if needed. */
11224 [ + + ]: 3612 : if (rownum >= PQntuples(res))
11225 : : {
11226 : 1104 : PQclear(res);
11227 : 1104 : res = fetchAttributeStats(fout);
11228 : 1104 : rownum = 0;
11229 : : }
11230 : :
11231 : 3612 : i_schemaname = PQfnumber(res, "schemaname");
11232 : 3612 : i_tablename = PQfnumber(res, "tablename");
11233 : 3612 : i_attname = PQfnumber(res, "attname");
11234 : 3612 : i_inherited = PQfnumber(res, "inherited");
11235 : 3612 : i_null_frac = PQfnumber(res, "null_frac");
11236 : 3612 : i_avg_width = PQfnumber(res, "avg_width");
11237 : 3612 : i_n_distinct = PQfnumber(res, "n_distinct");
11238 : 3612 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11239 : 3612 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11240 : 3612 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11241 : 3612 : i_correlation = PQfnumber(res, "correlation");
11242 : 3612 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11243 : 3612 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11244 : 3612 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11245 : 3612 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11246 : 3612 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11247 : 3612 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11248 : :
11249 : : /* restore attribute stats */
11250 [ + + ]: 4451 : for (; rownum < PQntuples(res); rownum++)
11251 : : {
11252 : : const char *attname;
11253 : :
11254 : : /* Stop if the next stat row in our cache isn't for this relation. */
11255 [ + + ]: 3347 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11256 [ + - ]: 839 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11257 : : break;
11258 : :
11259 : 839 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11260 : 839 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11261 : : fout->remoteVersion);
11262 : 839 : appendPQExpBufferStr(out, "\t'schemaname', ");
11263 : 839 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11264 : 839 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11265 : 839 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11266 : :
11267 [ - + ]: 839 : if (PQgetisnull(res, rownum, i_attname))
11268 : 0 : pg_fatal("unexpected null attname");
11269 : 839 : attname = PQgetvalue(res, rownum, i_attname);
11270 : :
11271 : : /*
11272 : : * Indexes look up attname in indAttNames to derive attnum, all others
11273 : : * use attname directly. We must specify attnum for indexes, since
11274 : : * their attnames are not necessarily stable across dump/reload.
11275 : : */
11276 [ + + ]: 839 : if (rsinfo->nindAttNames == 0)
11277 : : {
11278 : 801 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11279 : 801 : appendStringLiteralAH(out, attname, fout);
11280 : : }
11281 : : else
11282 : : {
11283 : 38 : bool found = false;
11284 : :
11285 [ + - ]: 72 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11286 : : {
11287 [ + + ]: 72 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11288 : : {
11289 : 38 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11290 : : i + 1);
11291 : 38 : found = true;
11292 : 38 : break;
11293 : : }
11294 : : }
11295 : :
11296 [ - + ]: 38 : if (!found)
11297 : 0 : pg_fatal("could not find index attname \"%s\"", attname);
11298 : : }
11299 : :
11300 [ + - ]: 839 : if (!PQgetisnull(res, rownum, i_inherited))
11301 : 839 : appendNamedArgument(out, fout, "inherited", "boolean",
11302 : 839 : PQgetvalue(res, rownum, i_inherited));
11303 [ + - ]: 839 : if (!PQgetisnull(res, rownum, i_null_frac))
11304 : 839 : appendNamedArgument(out, fout, "null_frac", "real",
11305 : 839 : PQgetvalue(res, rownum, i_null_frac));
11306 [ + - ]: 839 : if (!PQgetisnull(res, rownum, i_avg_width))
11307 : 839 : appendNamedArgument(out, fout, "avg_width", "integer",
11308 : 839 : PQgetvalue(res, rownum, i_avg_width));
11309 [ + - ]: 839 : if (!PQgetisnull(res, rownum, i_n_distinct))
11310 : 839 : appendNamedArgument(out, fout, "n_distinct", "real",
11311 : 839 : PQgetvalue(res, rownum, i_n_distinct));
11312 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11313 : 418 : appendNamedArgument(out, fout, "most_common_vals", "text",
11314 : 418 : PQgetvalue(res, rownum, i_most_common_vals));
11315 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11316 : 418 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11317 : 418 : PQgetvalue(res, rownum, i_most_common_freqs));
11318 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11319 : 535 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11320 : 535 : PQgetvalue(res, rownum, i_histogram_bounds));
11321 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_correlation))
11322 : 806 : appendNamedArgument(out, fout, "correlation", "real",
11323 : 806 : PQgetvalue(res, rownum, i_correlation));
11324 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11325 : 8 : appendNamedArgument(out, fout, "most_common_elems", "text",
11326 : 8 : PQgetvalue(res, rownum, i_most_common_elems));
11327 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11328 : 8 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11329 : 8 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11330 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11331 : 7 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11332 : 7 : PQgetvalue(res, rownum, i_elem_count_histogram));
11333 [ + - ]: 839 : if (fout->remoteVersion >= 170000)
11334 : : {
11335 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11336 : 3 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11337 : 3 : PQgetvalue(res, rownum, i_range_length_histogram));
11338 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11339 : 3 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11340 : 3 : PQgetvalue(res, rownum, i_range_empty_frac));
11341 [ + + ]: 839 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11342 : 3 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11343 : 3 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11344 : : }
11345 : 839 : appendPQExpBufferStr(out, "\n);\n");
11346 : : }
11347 : :
11348 : 3612 : destroyPQExpBuffer(query);
11349 : 3612 : return out->data;
11350 : : }
11351 : :
11352 : : /*
11353 : : * dumpRelationStats --
11354 : : *
11355 : : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11356 : : * care of gathering the statistics and generating the restore commands when
11357 : : * they are needed.
11358 : : */
11359 : : static void
11360 : 3684 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11361 : : {
11362 : 3684 : const DumpableObject *dobj = &rsinfo->dobj;
11363 : :
11364 : : /* nothing to do if we are not dumping statistics */
11365 [ - + ]: 3684 : if (!fout->dopt->dumpStatistics)
11366 : 0 : return;
11367 : :
11368 : 3684 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11369 : 3684 : ARCHIVE_OPTS(.tag = dobj->name,
11370 : : .namespace = dobj->namespace->dobj.name,
11371 : : .description = "STATISTICS DATA",
11372 : : .section = rsinfo->section,
11373 : : .defnFn = dumpRelationStats_dumper,
11374 : : .defnArg = rsinfo,
11375 : : .deps = dobj->dependencies,
11376 : : .nDeps = dobj->nDeps));
11377 : : }
11378 : :
11379 : : /*
11380 : : * dumpTableComment --
11381 : : *
11382 : : * As above, but dump comments for both the specified table (or view)
11383 : : * and its columns.
11384 : : */
11385 : : static void
11386 : 78 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11387 : : const char *reltypename)
11388 : : {
11389 : 78 : DumpOptions *dopt = fout->dopt;
11390 : : CommentItem *comments;
11391 : : int ncomments;
11392 : : PQExpBuffer query;
11393 : : PQExpBuffer tag;
11394 : :
11395 : : /* do nothing, if --no-comments is supplied */
11396 [ - + ]: 78 : if (dopt->no_comments)
11397 : 0 : return;
11398 : :
11399 : : /* Comments are SCHEMA not data */
11400 [ - + ]: 78 : if (!dopt->dumpSchema)
11401 : 0 : return;
11402 : :
11403 : : /* Search for comments associated with relation, using table */
11404 : 78 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11405 : 78 : tbinfo->dobj.catId.oid,
11406 : : &comments);
11407 : :
11408 : : /* If comments exist, build COMMENT ON statements */
11409 [ - + ]: 78 : if (ncomments <= 0)
11410 : 0 : return;
11411 : :
11412 : 78 : query = createPQExpBuffer();
11413 : 78 : tag = createPQExpBuffer();
11414 : :
11415 [ + + ]: 224 : while (ncomments > 0)
11416 : : {
11417 : 146 : const char *descr = comments->descr;
11418 : 146 : int objsubid = comments->objsubid;
11419 : :
11420 [ + + ]: 146 : if (objsubid == 0)
11421 : : {
11422 : 34 : resetPQExpBuffer(tag);
11423 : 34 : appendPQExpBuffer(tag, "%s %s", reltypename,
11424 : 34 : fmtId(tbinfo->dobj.name));
11425 : :
11426 : 34 : resetPQExpBuffer(query);
11427 : 34 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11428 : 34 : fmtQualifiedDumpable(tbinfo));
11429 : 34 : appendStringLiteralAH(query, descr, fout);
11430 : 34 : appendPQExpBufferStr(query, ";\n");
11431 : :
11432 : 34 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11433 : 34 : ARCHIVE_OPTS(.tag = tag->data,
11434 : : .namespace = tbinfo->dobj.namespace->dobj.name,
11435 : : .owner = tbinfo->rolname,
11436 : : .description = "COMMENT",
11437 : : .section = SECTION_NONE,
11438 : : .createStmt = query->data,
11439 : : .deps = &(tbinfo->dobj.dumpId),
11440 : : .nDeps = 1));
11441 : : }
11442 [ + - + - ]: 112 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11443 : : {
11444 : 112 : resetPQExpBuffer(tag);
11445 : 112 : appendPQExpBuffer(tag, "COLUMN %s.",
11446 : 112 : fmtId(tbinfo->dobj.name));
11447 : 112 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11448 : :
11449 : 112 : resetPQExpBuffer(query);
11450 : 112 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11451 : 112 : fmtQualifiedDumpable(tbinfo));
11452 : 112 : appendPQExpBuffer(query, "%s IS ",
11453 : 112 : fmtId(tbinfo->attnames[objsubid - 1]));
11454 : 112 : appendStringLiteralAH(query, descr, fout);
11455 : 112 : appendPQExpBufferStr(query, ";\n");
11456 : :
11457 : 112 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11458 : 112 : ARCHIVE_OPTS(.tag = tag->data,
11459 : : .namespace = tbinfo->dobj.namespace->dobj.name,
11460 : : .owner = tbinfo->rolname,
11461 : : .description = "COMMENT",
11462 : : .section = SECTION_NONE,
11463 : : .createStmt = query->data,
11464 : : .deps = &(tbinfo->dobj.dumpId),
11465 : : .nDeps = 1));
11466 : : }
11467 : :
11468 : 146 : comments++;
11469 : 146 : ncomments--;
11470 : : }
11471 : :
11472 : 78 : destroyPQExpBuffer(query);
11473 : 78 : destroyPQExpBuffer(tag);
11474 : : }
11475 : :
11476 : : /*
11477 : : * findComments --
11478 : : *
11479 : : * Find the comment(s), if any, associated with the given object. All the
11480 : : * objsubid values associated with the given classoid/objoid are found with
11481 : : * one search.
11482 : : */
11483 : : static int
11484 : 6755 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11485 : : {
11486 : 6755 : CommentItem *middle = NULL;
11487 : : CommentItem *low;
11488 : : CommentItem *high;
11489 : : int nmatch;
11490 : :
11491 : : /*
11492 : : * Do binary search to find some item matching the object.
11493 : : */
11494 : 6755 : low = &comments[0];
11495 : 6755 : high = &comments[ncomments - 1];
11496 [ + + ]: 67655 : while (low <= high)
11497 : : {
11498 : 67608 : middle = low + (high - low) / 2;
11499 : :
11500 [ + + ]: 67608 : if (classoid < middle->classoid)
11501 : 7211 : high = middle - 1;
11502 [ + + ]: 60397 : else if (classoid > middle->classoid)
11503 : 7309 : low = middle + 1;
11504 [ + + ]: 53088 : else if (objoid < middle->objoid)
11505 : 22504 : high = middle - 1;
11506 [ + + ]: 30584 : else if (objoid > middle->objoid)
11507 : 23876 : low = middle + 1;
11508 : : else
11509 : 6708 : break; /* found a match */
11510 : : }
11511 : :
11512 [ + + ]: 6755 : if (low > high) /* no matches */
11513 : : {
11514 : 47 : *items = NULL;
11515 : 47 : return 0;
11516 : : }
11517 : :
11518 : : /*
11519 : : * Now determine how many items match the object. The search loop
11520 : : * invariant still holds: only items between low and high inclusive could
11521 : : * match.
11522 : : */
11523 : 6708 : nmatch = 1;
11524 [ + + ]: 6764 : while (middle > low)
11525 : : {
11526 [ + + ]: 3260 : if (classoid != middle[-1].classoid ||
11527 [ + + ]: 3098 : objoid != middle[-1].objoid)
11528 : : break;
11529 : 56 : middle--;
11530 : 56 : nmatch++;
11531 : : }
11532 : :
11533 : 6708 : *items = middle;
11534 : :
11535 : 6708 : middle += nmatch;
11536 [ + + ]: 6720 : while (middle <= high)
11537 : : {
11538 [ + + ]: 3467 : if (classoid != middle->classoid ||
11539 [ + + ]: 3346 : objoid != middle->objoid)
11540 : : break;
11541 : 12 : middle++;
11542 : 12 : nmatch++;
11543 : : }
11544 : :
11545 : 6708 : return nmatch;
11546 : : }
11547 : :
11548 : : /*
11549 : : * collectComments --
11550 : : *
11551 : : * Construct a table of all comments available for database objects;
11552 : : * also set the has-comment component flag for each relevant object.
11553 : : *
11554 : : * We used to do per-object queries for the comments, but it's much faster
11555 : : * to pull them all over at once, and on most databases the memory cost
11556 : : * isn't high.
11557 : : *
11558 : : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11559 : : */
11560 : : static void
11561 : 191 : collectComments(Archive *fout)
11562 : : {
11563 : : PGresult *res;
11564 : : PQExpBuffer query;
11565 : : int i_description;
11566 : : int i_classoid;
11567 : : int i_objoid;
11568 : : int i_objsubid;
11569 : : int ntups;
11570 : : int i;
11571 : : DumpableObject *dobj;
11572 : :
11573 : 191 : query = createPQExpBuffer();
11574 : :
11575 : 191 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11576 : : "FROM pg_catalog.pg_description "
11577 : : "ORDER BY classoid, objoid, objsubid");
11578 : :
11579 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11580 : :
11581 : : /* Construct lookup table containing OIDs in numeric form */
11582 : :
11583 : 191 : i_description = PQfnumber(res, "description");
11584 : 191 : i_classoid = PQfnumber(res, "classoid");
11585 : 191 : i_objoid = PQfnumber(res, "objoid");
11586 : 191 : i_objsubid = PQfnumber(res, "objsubid");
11587 : :
11588 : 191 : ntups = PQntuples(res);
11589 : :
11590 : 191 : comments = pg_malloc_array(CommentItem, ntups);
11591 : 191 : ncomments = 0;
11592 : 191 : dobj = NULL;
11593 : :
11594 [ + + ]: 1030024 : for (i = 0; i < ntups; i++)
11595 : : {
11596 : : CatalogId objId;
11597 : : int subid;
11598 : :
11599 : 1029833 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11600 : 1029833 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11601 : 1029833 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11602 : :
11603 : : /* We needn't remember comments that don't match any dumpable object */
11604 [ + + ]: 1029833 : if (dobj == NULL ||
11605 [ + + ]: 374621 : dobj->catId.tableoid != objId.tableoid ||
11606 [ + + ]: 372269 : dobj->catId.oid != objId.oid)
11607 : 1029737 : dobj = findObjectByCatalogId(objId);
11608 [ + + ]: 1029833 : if (dobj == NULL)
11609 : 655027 : continue;
11610 : :
11611 : : /*
11612 : : * Comments on columns of composite types are linked to the type's
11613 : : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11614 : : * in the type's own DumpableObject.
11615 : : */
11616 [ + + + - ]: 374806 : if (subid != 0 && dobj->objType == DO_TABLE &&
11617 [ + + ]: 206 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11618 : 48 : {
11619 : : TypeInfo *cTypeInfo;
11620 : :
11621 : 48 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11622 [ + - ]: 48 : if (cTypeInfo)
11623 : 48 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11624 : : }
11625 : : else
11626 : 374758 : dobj->components |= DUMP_COMPONENT_COMMENT;
11627 : :
11628 : 374806 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11629 : 374806 : comments[ncomments].classoid = objId.tableoid;
11630 : 374806 : comments[ncomments].objoid = objId.oid;
11631 : 374806 : comments[ncomments].objsubid = subid;
11632 : 374806 : ncomments++;
11633 : : }
11634 : :
11635 : 191 : PQclear(res);
11636 : 191 : destroyPQExpBuffer(query);
11637 : 191 : }
11638 : :
11639 : : /*
11640 : : * dumpDumpableObject
11641 : : *
11642 : : * This routine and its subsidiaries are responsible for creating
11643 : : * ArchiveEntries (TOC objects) for each object to be dumped.
11644 : : */
11645 : : static void
11646 : 738370 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11647 : : {
11648 : : /*
11649 : : * Clear any dump-request bits for components that don't exist for this
11650 : : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11651 : : * request for every kind of object.)
11652 : : */
11653 : 738370 : dobj->dump &= dobj->components;
11654 : :
11655 : : /* Now, short-circuit if there's nothing to be done here. */
11656 [ + + ]: 738370 : if (dobj->dump == 0)
11657 : 655467 : return;
11658 : :
11659 [ + + + + : 82903 : switch (dobj->objType)
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + - ]
11660 : : {
11661 : 518 : case DO_NAMESPACE:
11662 : 518 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11663 : 518 : break;
11664 : 25 : case DO_EXTENSION:
11665 : 25 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11666 : 25 : break;
11667 : 971 : case DO_TYPE:
11668 : 971 : dumpType(fout, (const TypeInfo *) dobj);
11669 : 971 : break;
11670 : 76 : case DO_SHELL_TYPE:
11671 : 76 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11672 : 76 : break;
11673 : 1917 : case DO_FUNC:
11674 : 1917 : dumpFunc(fout, (const FuncInfo *) dobj);
11675 : 1917 : break;
11676 : 295 : case DO_AGG:
11677 : 295 : dumpAgg(fout, (const AggInfo *) dobj);
11678 : 295 : break;
11679 : 2525 : case DO_OPERATOR:
11680 : 2525 : dumpOpr(fout, (const OprInfo *) dobj);
11681 : 2525 : break;
11682 : 84 : case DO_ACCESS_METHOD:
11683 : 84 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11684 : 84 : break;
11685 : 675 : case DO_OPCLASS:
11686 : 675 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11687 : 675 : break;
11688 : 561 : case DO_OPFAMILY:
11689 : 561 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11690 : 561 : break;
11691 : 2733 : case DO_COLLATION:
11692 : 2733 : dumpCollation(fout, (const CollInfo *) dobj);
11693 : 2733 : break;
11694 : 335 : case DO_CONVERSION:
11695 : 335 : dumpConversion(fout, (const ConvInfo *) dobj);
11696 : 335 : break;
11697 : 34203 : case DO_TABLE:
11698 : 34203 : dumpTable(fout, (const TableInfo *) dobj);
11699 : 34203 : break;
11700 : 1452 : case DO_TABLE_ATTACH:
11701 : 1452 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11702 : 1452 : break;
11703 : 1121 : case DO_ATTRDEF:
11704 : 1121 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11705 : 1121 : break;
11706 : 2837 : case DO_INDEX:
11707 : 2837 : dumpIndex(fout, (const IndxInfo *) dobj);
11708 : 2837 : break;
11709 : 610 : case DO_INDEX_ATTACH:
11710 : 610 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11711 : 610 : break;
11712 : 183 : case DO_STATSEXT:
11713 : 183 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11714 : 183 : dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11715 : 183 : break;
11716 : 363 : case DO_REFRESH_MATVIEW:
11717 : 363 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11718 : 363 : break;
11719 : 1197 : case DO_RULE:
11720 : 1197 : dumpRule(fout, (const RuleInfo *) dobj);
11721 : 1197 : break;
11722 : 535 : case DO_TRIGGER:
11723 : 535 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11724 : 535 : break;
11725 : 44 : case DO_EVENT_TRIGGER:
11726 : 44 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11727 : 44 : break;
11728 : 2563 : case DO_CONSTRAINT:
11729 : 2563 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11730 : 2563 : break;
11731 : 237 : case DO_FK_CONSTRAINT:
11732 : 237 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11733 : 237 : break;
11734 : 87 : case DO_PROCLANG:
11735 : 87 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11736 : 87 : break;
11737 : 69 : case DO_CAST:
11738 : 69 : dumpCast(fout, (const CastInfo *) dobj);
11739 : 69 : break;
11740 : 44 : case DO_TRANSFORM:
11741 : 44 : dumpTransform(fout, (const TransformInfo *) dobj);
11742 : 44 : break;
11743 : 399 : case DO_SEQUENCE_SET:
11744 : 399 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11745 : 399 : break;
11746 : 4605 : case DO_TABLE_DATA:
11747 : 4605 : dumpTableData(fout, (const TableDataInfo *) dobj);
11748 : 4605 : break;
11749 : 15383 : case DO_DUMMY_TYPE:
11750 : : /* table rowtypes and array types are never dumped separately */
11751 : 15383 : break;
11752 : 44 : case DO_TSPARSER:
11753 : 44 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11754 : 44 : break;
11755 : 182 : case DO_TSDICT:
11756 : 182 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11757 : 182 : break;
11758 : 56 : case DO_TSTEMPLATE:
11759 : 56 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11760 : 56 : break;
11761 : 157 : case DO_TSCONFIG:
11762 : 157 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11763 : 157 : break;
11764 : 54 : case DO_FDW:
11765 : 54 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11766 : 54 : break;
11767 : 58 : case DO_FOREIGN_SERVER:
11768 : 58 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11769 : 58 : break;
11770 : 170 : case DO_DEFAULT_ACL:
11771 : 170 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11772 : 170 : break;
11773 : 88 : case DO_LARGE_OBJECT:
11774 : 88 : dumpLO(fout, (const LoInfo *) dobj);
11775 : 88 : break;
11776 : 88 : case DO_LARGE_OBJECT_DATA:
11777 [ + - ]: 88 : if (dobj->dump & DUMP_COMPONENT_DATA)
11778 : : {
11779 : : LoInfo *loinfo;
11780 : : TocEntry *te;
11781 : :
11782 : 88 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11783 [ - + ]: 88 : if (loinfo == NULL)
11784 : 0 : pg_fatal("missing metadata for large objects \"%s\"",
11785 : : dobj->name);
11786 : :
11787 : 88 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11788 : 88 : ARCHIVE_OPTS(.tag = dobj->name,
11789 : : .owner = loinfo->rolname,
11790 : : .description = "BLOBS",
11791 : : .section = SECTION_DATA,
11792 : : .deps = dobj->dependencies,
11793 : : .nDeps = dobj->nDeps,
11794 : : .dumpFn = dumpLOs,
11795 : : .dumpArg = loinfo));
11796 : :
11797 : : /*
11798 : : * Set the TocEntry's dataLength in case we are doing a
11799 : : * parallel dump and want to order dump jobs by table size.
11800 : : * (We need some size estimate for every TocEntry with a
11801 : : * DataDumper function.) We don't currently have any cheap
11802 : : * way to estimate the size of LOs, but fortunately it doesn't
11803 : : * matter too much as long as we get large batches of LOs
11804 : : * processed reasonably early. Assume 8K per blob.
11805 : : */
11806 : 88 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11807 : : }
11808 : 88 : break;
11809 : 357 : case DO_POLICY:
11810 : 357 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11811 : 357 : break;
11812 : 416 : case DO_PUBLICATION:
11813 : 416 : dumpPublication(fout, (const PublicationInfo *) dobj);
11814 : 416 : break;
11815 : 298 : case DO_PUBLICATION_REL:
11816 : 298 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11817 : 298 : break;
11818 : 103 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11819 : 103 : dumpPublicationNamespace(fout,
11820 : : (const PublicationSchemaInfo *) dobj);
11821 : 103 : break;
11822 : 116 : case DO_SUBSCRIPTION:
11823 : 116 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11824 : 116 : break;
11825 : 3 : case DO_SUBSCRIPTION_REL:
11826 : 3 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11827 : 3 : break;
11828 : 3684 : case DO_REL_STATS:
11829 : 3684 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11830 : 3684 : break;
11831 : 382 : case DO_PRE_DATA_BOUNDARY:
11832 : : case DO_POST_DATA_BOUNDARY:
11833 : : /* never dumped, nothing to do */
11834 : 382 : break;
11835 : : }
11836 : : }
11837 : :
11838 : : /*
11839 : : * dumpNamespace
11840 : : * writes out to fout the queries to recreate a user-defined namespace
11841 : : */
11842 : : static void
11843 : 518 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11844 : : {
11845 : 518 : DumpOptions *dopt = fout->dopt;
11846 : : PQExpBuffer q;
11847 : : PQExpBuffer delq;
11848 : : char *qnspname;
11849 : :
11850 : : /* Do nothing if not dumping schema */
11851 [ + + ]: 518 : if (!dopt->dumpSchema)
11852 : 29 : return;
11853 : :
11854 : 489 : q = createPQExpBuffer();
11855 : 489 : delq = createPQExpBuffer();
11856 : :
11857 : 489 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11858 : :
11859 [ + + ]: 489 : if (nspinfo->create)
11860 : : {
11861 : 334 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11862 : 334 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11863 : : }
11864 : : else
11865 : : {
11866 : : /* see selectDumpableNamespace() */
11867 : 155 : appendPQExpBufferStr(delq,
11868 : : "-- *not* dropping schema, since initdb creates it\n");
11869 : 155 : appendPQExpBufferStr(q,
11870 : : "-- *not* creating schema, since initdb creates it\n");
11871 : : }
11872 : :
11873 [ + + ]: 489 : if (dopt->binary_upgrade)
11874 : 102 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11875 : : "SCHEMA", qnspname, NULL);
11876 : :
11877 [ + + ]: 489 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11878 : 211 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11879 : 211 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11880 : : .owner = nspinfo->rolname,
11881 : : .description = "SCHEMA",
11882 : : .section = SECTION_PRE_DATA,
11883 : : .createStmt = q->data,
11884 : : .dropStmt = delq->data));
11885 : :
11886 : : /* Dump Schema Comments and Security Labels */
11887 [ + + ]: 489 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11888 : : {
11889 : 160 : const char *initdb_comment = NULL;
11890 : :
11891 [ + + + + ]: 160 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11892 : 117 : initdb_comment = "standard public schema";
11893 : 160 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11894 : 160 : NULL, nspinfo->rolname,
11895 : 160 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11896 : : initdb_comment);
11897 : : }
11898 : :
11899 [ - + ]: 489 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11900 : 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11901 : 0 : NULL, nspinfo->rolname,
11902 : 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11903 : :
11904 [ + + ]: 489 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11905 : 385 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11906 : : qnspname, NULL, NULL,
11907 : 385 : NULL, nspinfo->rolname, &nspinfo->dacl);
11908 : :
11909 : 489 : pg_free(qnspname);
11910 : :
11911 : 489 : destroyPQExpBuffer(q);
11912 : 489 : destroyPQExpBuffer(delq);
11913 : : }
11914 : :
11915 : : /*
11916 : : * dumpExtension
11917 : : * writes out to fout the queries to recreate an extension
11918 : : */
11919 : : static void
11920 : 25 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11921 : : {
11922 : 25 : DumpOptions *dopt = fout->dopt;
11923 : : PQExpBuffer q;
11924 : : PQExpBuffer delq;
11925 : : char *qextname;
11926 : :
11927 : : /* Do nothing if not dumping schema */
11928 [ + + ]: 25 : if (!dopt->dumpSchema)
11929 : 1 : return;
11930 : :
11931 : 24 : q = createPQExpBuffer();
11932 : 24 : delq = createPQExpBuffer();
11933 : :
11934 : 24 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11935 : :
11936 : 24 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11937 : :
11938 [ + + ]: 24 : if (!dopt->binary_upgrade)
11939 : : {
11940 : : /*
11941 : : * In a regular dump, we simply create the extension, intentionally
11942 : : * not specifying a version, so that the destination installation's
11943 : : * default version is used.
11944 : : *
11945 : : * Use of IF NOT EXISTS here is unlike our behavior for other object
11946 : : * types; but there are various scenarios in which it's convenient to
11947 : : * manually create the desired extension before restoring, so we
11948 : : * prefer to allow it to exist already.
11949 : : */
11950 : 17 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11951 : 17 : qextname, fmtId(extinfo->namespace));
11952 : : }
11953 : : else
11954 : : {
11955 : : /*
11956 : : * In binary-upgrade mode, it's critical to reproduce the state of the
11957 : : * database exactly, so our procedure is to create an empty extension,
11958 : : * restore all the contained objects normally, and add them to the
11959 : : * extension one by one. This function performs just the first of
11960 : : * those steps. binary_upgrade_extension_member() takes care of
11961 : : * adding member objects as they're created.
11962 : : */
11963 : : int i;
11964 : : int n;
11965 : :
11966 : 7 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11967 : :
11968 : : /*
11969 : : * We unconditionally create the extension, so we must drop it if it
11970 : : * exists. This could happen if the user deleted 'plpgsql' and then
11971 : : * readded it, causing its oid to be greater than g_last_builtin_oid.
11972 : : */
11973 : 7 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11974 : :
11975 : 7 : appendPQExpBufferStr(q,
11976 : : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11977 : 7 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11978 : 7 : appendPQExpBufferStr(q, ", ");
11979 : 7 : appendStringLiteralAH(q, extinfo->namespace, fout);
11980 : 7 : appendPQExpBufferStr(q, ", ");
11981 [ + - ]: 7 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11982 : 7 : appendStringLiteralAH(q, extinfo->extversion, fout);
11983 : 7 : appendPQExpBufferStr(q, ", ");
11984 : :
11985 : : /*
11986 : : * Note that we're pushing extconfig (an OID array) back into
11987 : : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11988 : : * preserved in binary upgrade.
11989 : : */
11990 [ + + ]: 7 : if (strlen(extinfo->extconfig) > 2)
11991 : 1 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11992 : : else
11993 : 6 : appendPQExpBufferStr(q, "NULL");
11994 : 7 : appendPQExpBufferStr(q, ", ");
11995 [ + + ]: 7 : if (strlen(extinfo->extcondition) > 2)
11996 : 1 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11997 : : else
11998 : 6 : appendPQExpBufferStr(q, "NULL");
11999 : 7 : appendPQExpBufferStr(q, ", ");
12000 : 7 : appendPQExpBufferStr(q, "ARRAY[");
12001 : 7 : n = 0;
12002 [ + + ]: 14 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12003 : : {
12004 : : DumpableObject *extobj;
12005 : :
12006 : 7 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12007 [ + - - + ]: 7 : if (extobj && extobj->objType == DO_EXTENSION)
12008 : : {
12009 [ # # ]: 0 : if (n++ > 0)
12010 : 0 : appendPQExpBufferChar(q, ',');
12011 : 0 : appendStringLiteralAH(q, extobj->name, fout);
12012 : : }
12013 : : }
12014 : 7 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12015 : 7 : appendPQExpBufferStr(q, ");\n");
12016 : : }
12017 : :
12018 [ + - ]: 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12019 : 24 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12020 : 24 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12021 : : .description = "EXTENSION",
12022 : : .section = SECTION_PRE_DATA,
12023 : : .createStmt = q->data,
12024 : : .dropStmt = delq->data));
12025 : :
12026 : : /* Dump Extension Comments */
12027 [ + - ]: 24 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12028 : 24 : dumpComment(fout, "EXTENSION", qextname,
12029 : : NULL, "",
12030 : 24 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12031 : :
12032 : 24 : pg_free(qextname);
12033 : :
12034 : 24 : destroyPQExpBuffer(q);
12035 : 24 : destroyPQExpBuffer(delq);
12036 : : }
12037 : :
12038 : : /*
12039 : : * dumpType
12040 : : * writes out to fout the queries to recreate a user-defined type
12041 : : */
12042 : : static void
12043 : 971 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12044 : : {
12045 : 971 : DumpOptions *dopt = fout->dopt;
12046 : :
12047 : : /* Do nothing if not dumping schema */
12048 [ + + ]: 971 : if (!dopt->dumpSchema)
12049 : 56 : return;
12050 : :
12051 : : /* Dump out in proper style */
12052 [ + + ]: 915 : if (tyinfo->typtype == TYPTYPE_BASE)
12053 : 285 : dumpBaseType(fout, tyinfo);
12054 [ + + ]: 630 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12055 : 174 : dumpDomain(fout, tyinfo);
12056 [ + + ]: 456 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12057 : 132 : dumpCompositeType(fout, tyinfo);
12058 [ + + ]: 324 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12059 : 89 : dumpEnumType(fout, tyinfo);
12060 [ + + ]: 235 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12061 : 121 : dumpRangeType(fout, tyinfo);
12062 [ + - + + ]: 114 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12063 : 39 : dumpUndefinedType(fout, tyinfo);
12064 : : else
12065 : 75 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12066 : : tyinfo->dobj.name);
12067 : : }
12068 : :
12069 : : /*
12070 : : * dumpEnumType
12071 : : * writes out to fout the queries to recreate a user-defined enum type
12072 : : */
12073 : : static void
12074 : 89 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12075 : : {
12076 : 89 : DumpOptions *dopt = fout->dopt;
12077 : 89 : PQExpBuffer q = createPQExpBuffer();
12078 : 89 : PQExpBuffer delq = createPQExpBuffer();
12079 : 89 : PQExpBuffer query = createPQExpBuffer();
12080 : : PGresult *res;
12081 : : int num,
12082 : : i;
12083 : : Oid enum_oid;
12084 : : char *qtypname;
12085 : : char *qualtypname;
12086 : : char *label;
12087 : : int i_enumlabel;
12088 : : int i_oid;
12089 : :
12090 [ + + ]: 89 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12091 : : {
12092 : : /* Set up query for enum-specific details */
12093 : 42 : appendPQExpBufferStr(query,
12094 : : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12095 : : "SELECT oid, enumlabel "
12096 : : "FROM pg_catalog.pg_enum "
12097 : : "WHERE enumtypid = $1 "
12098 : : "ORDER BY enumsortorder");
12099 : :
12100 : 42 : ExecuteSqlStatement(fout, query->data);
12101 : :
12102 : 42 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12103 : : }
12104 : :
12105 : 89 : printfPQExpBuffer(query,
12106 : : "EXECUTE dumpEnumType('%u')",
12107 : 89 : tyinfo->dobj.catId.oid);
12108 : :
12109 : 89 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12110 : :
12111 : 89 : num = PQntuples(res);
12112 : :
12113 : 89 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12114 : 89 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12115 : :
12116 : : /*
12117 : : * CASCADE shouldn't be required here as for normal types since the I/O
12118 : : * functions are generic and do not get dropped.
12119 : : */
12120 : 89 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12121 : :
12122 [ + + ]: 89 : if (dopt->binary_upgrade)
12123 : 6 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12124 : 6 : tyinfo->dobj.catId.oid,
12125 : : false, false);
12126 : :
12127 : 89 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12128 : : qualtypname);
12129 : :
12130 [ + + ]: 89 : if (!dopt->binary_upgrade)
12131 : : {
12132 : 83 : i_enumlabel = PQfnumber(res, "enumlabel");
12133 : :
12134 : : /* Labels with server-assigned oids */
12135 [ + + ]: 498 : for (i = 0; i < num; i++)
12136 : : {
12137 : 415 : label = PQgetvalue(res, i, i_enumlabel);
12138 [ + + ]: 415 : if (i > 0)
12139 : 332 : appendPQExpBufferChar(q, ',');
12140 : 415 : appendPQExpBufferStr(q, "\n ");
12141 : 415 : appendStringLiteralAH(q, label, fout);
12142 : : }
12143 : : }
12144 : :
12145 : 89 : appendPQExpBufferStr(q, "\n);\n");
12146 : :
12147 [ + + ]: 89 : if (dopt->binary_upgrade)
12148 : : {
12149 : 6 : i_oid = PQfnumber(res, "oid");
12150 : 6 : i_enumlabel = PQfnumber(res, "enumlabel");
12151 : :
12152 : : /* Labels with dump-assigned (preserved) oids */
12153 [ + + ]: 62 : for (i = 0; i < num; i++)
12154 : : {
12155 : 56 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12156 : 56 : label = PQgetvalue(res, i, i_enumlabel);
12157 : :
12158 [ + + ]: 56 : if (i == 0)
12159 : 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12160 : 56 : appendPQExpBuffer(q,
12161 : : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12162 : : enum_oid);
12163 : 56 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12164 : 56 : appendStringLiteralAH(q, label, fout);
12165 : 56 : appendPQExpBufferStr(q, ";\n\n");
12166 : : }
12167 : : }
12168 : :
12169 [ + + ]: 89 : if (dopt->binary_upgrade)
12170 : 6 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12171 : : "TYPE", qtypname,
12172 : 6 : tyinfo->dobj.namespace->dobj.name);
12173 : :
12174 [ + - ]: 89 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12175 : 89 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12176 : 89 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12177 : : .namespace = tyinfo->dobj.namespace->dobj.name,
12178 : : .owner = tyinfo->rolname,
12179 : : .description = "TYPE",
12180 : : .section = SECTION_PRE_DATA,
12181 : : .createStmt = q->data,
12182 : : .dropStmt = delq->data));
12183 : :
12184 : : /* Dump Type Comments and Security Labels */
12185 [ + + ]: 89 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12186 : 34 : dumpComment(fout, "TYPE", qtypname,
12187 : 34 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12188 : 34 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12189 : :
12190 [ - + ]: 89 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12191 : 0 : dumpSecLabel(fout, "TYPE", qtypname,
12192 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12193 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12194 : :
12195 [ + + ]: 89 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12196 : 34 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12197 : : qtypname, NULL,
12198 : 34 : tyinfo->dobj.namespace->dobj.name,
12199 : 34 : NULL, tyinfo->rolname, &tyinfo->dacl);
12200 : :
12201 : 89 : PQclear(res);
12202 : 89 : destroyPQExpBuffer(q);
12203 : 89 : destroyPQExpBuffer(delq);
12204 : 89 : destroyPQExpBuffer(query);
12205 : 89 : pg_free(qtypname);
12206 : 89 : pg_free(qualtypname);
12207 : 89 : }
12208 : :
12209 : : /*
12210 : : * dumpRangeType
12211 : : * writes out to fout the queries to recreate a user-defined range type
12212 : : */
12213 : : static void
12214 : 121 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12215 : : {
12216 : 121 : DumpOptions *dopt = fout->dopt;
12217 : 121 : PQExpBuffer q = createPQExpBuffer();
12218 : 121 : PQExpBuffer delq = createPQExpBuffer();
12219 : 121 : PQExpBuffer query = createPQExpBuffer();
12220 : : PGresult *res;
12221 : : Oid collationOid;
12222 : : char *qtypname;
12223 : : char *qualtypname;
12224 : : char *procname;
12225 : :
12226 [ + + ]: 121 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12227 : : {
12228 : : /* Set up query for range-specific details */
12229 : 42 : appendPQExpBufferStr(query,
12230 : : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12231 : :
12232 : 42 : appendPQExpBufferStr(query,
12233 : : "SELECT ");
12234 : :
12235 [ + - ]: 42 : if (fout->remoteVersion >= 140000)
12236 : 42 : appendPQExpBufferStr(query,
12237 : : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12238 : : else
12239 : 0 : appendPQExpBufferStr(query,
12240 : : "NULL AS rngmultitype, ");
12241 : :
12242 : 42 : appendPQExpBufferStr(query,
12243 : : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12244 : : "opc.opcname AS opcname, "
12245 : : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12246 : : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12247 : : "opc.opcdefault, "
12248 : : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12249 : : " ELSE rngcollation END AS collation, "
12250 : : "rngcanonical, rngsubdiff "
12251 : : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12252 : : " pg_catalog.pg_opclass opc "
12253 : : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12254 : : "rngtypid = $1");
12255 : :
12256 : 42 : ExecuteSqlStatement(fout, query->data);
12257 : :
12258 : 42 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12259 : : }
12260 : :
12261 : 121 : printfPQExpBuffer(query,
12262 : : "EXECUTE dumpRangeType('%u')",
12263 : 121 : tyinfo->dobj.catId.oid);
12264 : :
12265 : 121 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12266 : :
12267 : 121 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12268 : 121 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12269 : :
12270 : : /*
12271 : : * CASCADE shouldn't be required here as for normal types since the I/O
12272 : : * functions are generic and do not get dropped.
12273 : : */
12274 : 121 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12275 : :
12276 [ + + ]: 121 : if (dopt->binary_upgrade)
12277 : 9 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12278 : 9 : tyinfo->dobj.catId.oid,
12279 : : false, true);
12280 : :
12281 : 121 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12282 : : qualtypname);
12283 : :
12284 : 121 : appendPQExpBuffer(q, "\n subtype = %s",
12285 : : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12286 : :
12287 [ + - ]: 121 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12288 : 121 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12289 : : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12290 : :
12291 : : /* print subtype_opclass only if not default for subtype */
12292 [ + + ]: 121 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12293 : : {
12294 : 34 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12295 : 34 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12296 : :
12297 : 34 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12298 : : fmtId(nspname));
12299 : 34 : appendPQExpBufferStr(q, fmtId(opcname));
12300 : : }
12301 : :
12302 : 121 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12303 [ + + ]: 121 : if (OidIsValid(collationOid))
12304 : : {
12305 : 39 : CollInfo *coll = findCollationByOid(collationOid);
12306 : :
12307 [ + - ]: 39 : if (coll)
12308 : 39 : appendPQExpBuffer(q, ",\n collation = %s",
12309 : 39 : fmtQualifiedDumpable(coll));
12310 : : }
12311 : :
12312 : 121 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12313 [ + + ]: 121 : if (strcmp(procname, "-") != 0)
12314 : 9 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12315 : :
12316 : 121 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12317 [ + + ]: 121 : if (strcmp(procname, "-") != 0)
12318 : 23 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12319 : :
12320 : 121 : appendPQExpBufferStr(q, "\n);\n");
12321 : :
12322 [ + + ]: 121 : if (dopt->binary_upgrade)
12323 : 9 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12324 : : "TYPE", qtypname,
12325 : 9 : tyinfo->dobj.namespace->dobj.name);
12326 : :
12327 [ + - ]: 121 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12328 : 121 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12329 : 121 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12330 : : .namespace = tyinfo->dobj.namespace->dobj.name,
12331 : : .owner = tyinfo->rolname,
12332 : : .description = "TYPE",
12333 : : .section = SECTION_PRE_DATA,
12334 : : .createStmt = q->data,
12335 : : .dropStmt = delq->data));
12336 : :
12337 : : /* Dump Type Comments and Security Labels */
12338 [ + + ]: 121 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12339 : 52 : dumpComment(fout, "TYPE", qtypname,
12340 : 52 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12341 : 52 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12342 : :
12343 [ - + ]: 121 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12344 : 0 : dumpSecLabel(fout, "TYPE", qtypname,
12345 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12346 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12347 : :
12348 [ + + ]: 121 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12349 : 34 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12350 : : qtypname, NULL,
12351 : 34 : tyinfo->dobj.namespace->dobj.name,
12352 : 34 : NULL, tyinfo->rolname, &tyinfo->dacl);
12353 : :
12354 : 121 : PQclear(res);
12355 : 121 : destroyPQExpBuffer(q);
12356 : 121 : destroyPQExpBuffer(delq);
12357 : 121 : destroyPQExpBuffer(query);
12358 : 121 : pg_free(qtypname);
12359 : 121 : pg_free(qualtypname);
12360 : 121 : }
12361 : :
12362 : : /*
12363 : : * dumpUndefinedType
12364 : : * writes out to fout the queries to recreate a !typisdefined type
12365 : : *
12366 : : * This is a shell type, but we use different terminology to distinguish
12367 : : * this case from where we have to emit a shell type definition to break
12368 : : * circular dependencies. An undefined type shouldn't ever have anything
12369 : : * depending on it.
12370 : : */
12371 : : static void
12372 : 39 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12373 : : {
12374 : 39 : DumpOptions *dopt = fout->dopt;
12375 : 39 : PQExpBuffer q = createPQExpBuffer();
12376 : 39 : PQExpBuffer delq = createPQExpBuffer();
12377 : : char *qtypname;
12378 : : char *qualtypname;
12379 : :
12380 : 39 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12381 : 39 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12382 : :
12383 : 39 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12384 : :
12385 [ + + ]: 39 : if (dopt->binary_upgrade)
12386 : 2 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12387 : 2 : tyinfo->dobj.catId.oid,
12388 : : false, false);
12389 : :
12390 : 39 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12391 : : qualtypname);
12392 : :
12393 [ + + ]: 39 : if (dopt->binary_upgrade)
12394 : 2 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12395 : : "TYPE", qtypname,
12396 : 2 : tyinfo->dobj.namespace->dobj.name);
12397 : :
12398 [ + - ]: 39 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12399 : 39 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12400 : 39 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12401 : : .namespace = tyinfo->dobj.namespace->dobj.name,
12402 : : .owner = tyinfo->rolname,
12403 : : .description = "TYPE",
12404 : : .section = SECTION_PRE_DATA,
12405 : : .createStmt = q->data,
12406 : : .dropStmt = delq->data));
12407 : :
12408 : : /* Dump Type Comments and Security Labels */
12409 [ + + ]: 39 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12410 : 34 : dumpComment(fout, "TYPE", qtypname,
12411 : 34 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12412 : 34 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12413 : :
12414 [ - + ]: 39 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12415 : 0 : dumpSecLabel(fout, "TYPE", qtypname,
12416 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12417 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12418 : :
12419 [ - + ]: 39 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12420 : 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12421 : : qtypname, NULL,
12422 : 0 : tyinfo->dobj.namespace->dobj.name,
12423 : 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12424 : :
12425 : 39 : destroyPQExpBuffer(q);
12426 : 39 : destroyPQExpBuffer(delq);
12427 : 39 : pg_free(qtypname);
12428 : 39 : pg_free(qualtypname);
12429 : 39 : }
12430 : :
12431 : : /*
12432 : : * dumpBaseType
12433 : : * writes out to fout the queries to recreate a user-defined base type
12434 : : */
12435 : : static void
12436 : 285 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12437 : : {
12438 : 285 : DumpOptions *dopt = fout->dopt;
12439 : 285 : PQExpBuffer q = createPQExpBuffer();
12440 : 285 : PQExpBuffer delq = createPQExpBuffer();
12441 : 285 : PQExpBuffer query = createPQExpBuffer();
12442 : : PGresult *res;
12443 : : char *qtypname;
12444 : : char *qualtypname;
12445 : : char *typlen;
12446 : : char *typinput;
12447 : : char *typoutput;
12448 : : char *typreceive;
12449 : : char *typsend;
12450 : : char *typmodin;
12451 : : char *typmodout;
12452 : : char *typanalyze;
12453 : : char *typsubscript;
12454 : : Oid typreceiveoid;
12455 : : Oid typsendoid;
12456 : : Oid typmodinoid;
12457 : : Oid typmodoutoid;
12458 : : Oid typanalyzeoid;
12459 : : Oid typsubscriptoid;
12460 : : char *typcategory;
12461 : : char *typispreferred;
12462 : : char *typdelim;
12463 : : char *typbyval;
12464 : : char *typalign;
12465 : : char *typstorage;
12466 : : char *typcollatable;
12467 : : char *typdefault;
12468 : 285 : bool typdefault_is_literal = false;
12469 : :
12470 [ + + ]: 285 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12471 : : {
12472 : : /* Set up query for type-specific details */
12473 : 42 : appendPQExpBufferStr(query,
12474 : : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12475 : : "SELECT typlen, "
12476 : : "typinput, typoutput, typreceive, typsend, "
12477 : : "typreceive::pg_catalog.oid AS typreceiveoid, "
12478 : : "typsend::pg_catalog.oid AS typsendoid, "
12479 : : "typanalyze, "
12480 : : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12481 : : "typdelim, typbyval, typalign, typstorage, "
12482 : : "typmodin, typmodout, "
12483 : : "typmodin::pg_catalog.oid AS typmodinoid, "
12484 : : "typmodout::pg_catalog.oid AS typmodoutoid, "
12485 : : "typcategory, typispreferred, "
12486 : : "(typcollation <> 0) AS typcollatable, "
12487 : : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12488 : :
12489 [ + - ]: 42 : if (fout->remoteVersion >= 140000)
12490 : 42 : appendPQExpBufferStr(query,
12491 : : "typsubscript, "
12492 : : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12493 : : else
12494 : 0 : appendPQExpBufferStr(query,
12495 : : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12496 : :
12497 : 42 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12498 : : "WHERE oid = $1");
12499 : :
12500 : 42 : ExecuteSqlStatement(fout, query->data);
12501 : :
12502 : 42 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12503 : : }
12504 : :
12505 : 285 : printfPQExpBuffer(query,
12506 : : "EXECUTE dumpBaseType('%u')",
12507 : 285 : tyinfo->dobj.catId.oid);
12508 : :
12509 : 285 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12510 : :
12511 : 285 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12512 : 285 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12513 : 285 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12514 : 285 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12515 : 285 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12516 : 285 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12517 : 285 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12518 : 285 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12519 : 285 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12520 : 285 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12521 : 285 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12522 : 285 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12523 : 285 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12524 : 285 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12525 : 285 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12526 : 285 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12527 : 285 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12528 : 285 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12529 : 285 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12530 : 285 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12531 : 285 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12532 : 285 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12533 [ - + ]: 285 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12534 : 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12535 [ + + ]: 285 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12536 : : {
12537 : 44 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12538 : 44 : typdefault_is_literal = true; /* it needs quotes */
12539 : : }
12540 : : else
12541 : 241 : typdefault = NULL;
12542 : :
12543 : 285 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12544 : 285 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12545 : :
12546 : : /*
12547 : : * The reason we include CASCADE is that the circular dependency between
12548 : : * the type and its I/O functions makes it impossible to drop the type any
12549 : : * other way.
12550 : : */
12551 : 285 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12552 : :
12553 : : /*
12554 : : * We might already have a shell type, but setting pg_type_oid is
12555 : : * harmless, and in any case we'd better set the array type OID.
12556 : : */
12557 [ + + ]: 285 : if (dopt->binary_upgrade)
12558 : 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12559 : 8 : tyinfo->dobj.catId.oid,
12560 : : false, false);
12561 : :
12562 : 285 : appendPQExpBuffer(q,
12563 : : "CREATE TYPE %s (\n"
12564 : : " INTERNALLENGTH = %s",
12565 : : qualtypname,
12566 [ + + ]: 285 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12567 : :
12568 : : /* regproc result is sufficiently quoted already */
12569 : 285 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12570 : 285 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12571 [ + + ]: 285 : if (OidIsValid(typreceiveoid))
12572 : 210 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12573 [ + + ]: 285 : if (OidIsValid(typsendoid))
12574 : 210 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12575 [ + + ]: 285 : if (OidIsValid(typmodinoid))
12576 : 35 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12577 [ + + ]: 285 : if (OidIsValid(typmodoutoid))
12578 : 35 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12579 [ + + ]: 285 : if (OidIsValid(typanalyzeoid))
12580 : 3 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12581 : :
12582 [ + + ]: 285 : if (strcmp(typcollatable, "t") == 0)
12583 : 30 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12584 : :
12585 [ + + ]: 285 : if (typdefault != NULL)
12586 : : {
12587 : 44 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12588 [ + - ]: 44 : if (typdefault_is_literal)
12589 : 44 : appendStringLiteralAH(q, typdefault, fout);
12590 : : else
12591 : 0 : appendPQExpBufferStr(q, typdefault);
12592 : : }
12593 : :
12594 [ + + ]: 285 : if (OidIsValid(typsubscriptoid))
12595 : 29 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12596 : :
12597 [ + + ]: 285 : if (OidIsValid(tyinfo->typelem))
12598 : 26 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12599 : 26 : getFormattedTypeName(fout, tyinfo->typelem,
12600 : : zeroIsError));
12601 : :
12602 [ + + ]: 285 : if (strcmp(typcategory, "U") != 0)
12603 : : {
12604 : 161 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12605 : 161 : appendStringLiteralAH(q, typcategory, fout);
12606 : : }
12607 : :
12608 [ + + ]: 285 : if (strcmp(typispreferred, "t") == 0)
12609 : 29 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12610 : :
12611 [ + - + + ]: 285 : if (typdelim && strcmp(typdelim, ",") != 0)
12612 : : {
12613 : 3 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12614 : 3 : appendStringLiteralAH(q, typdelim, fout);
12615 : : }
12616 : :
12617 [ + + ]: 285 : if (*typalign == TYPALIGN_CHAR)
12618 : 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12619 [ + + ]: 273 : else if (*typalign == TYPALIGN_SHORT)
12620 : 6 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12621 [ + + ]: 267 : else if (*typalign == TYPALIGN_INT)
12622 : 189 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12623 [ + - ]: 78 : else if (*typalign == TYPALIGN_DOUBLE)
12624 : 78 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12625 : :
12626 [ + + ]: 285 : if (*typstorage == TYPSTORAGE_PLAIN)
12627 : 210 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12628 [ - + ]: 75 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12629 : 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12630 [ + + ]: 75 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12631 : 66 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12632 [ + - ]: 9 : else if (*typstorage == TYPSTORAGE_MAIN)
12633 : 9 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12634 : :
12635 [ + + ]: 285 : if (strcmp(typbyval, "t") == 0)
12636 : 139 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12637 : :
12638 : 285 : appendPQExpBufferStr(q, "\n);\n");
12639 : :
12640 [ + + ]: 285 : if (dopt->binary_upgrade)
12641 : 8 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12642 : : "TYPE", qtypname,
12643 : 8 : tyinfo->dobj.namespace->dobj.name);
12644 : :
12645 [ + - ]: 285 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12646 : 285 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12647 : 285 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12648 : : .namespace = tyinfo->dobj.namespace->dobj.name,
12649 : : .owner = tyinfo->rolname,
12650 : : .description = "TYPE",
12651 : : .section = SECTION_PRE_DATA,
12652 : : .createStmt = q->data,
12653 : : .dropStmt = delq->data));
12654 : :
12655 : : /* Dump Type Comments and Security Labels */
12656 [ + + ]: 285 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12657 : 250 : dumpComment(fout, "TYPE", qtypname,
12658 : 250 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12659 : 250 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12660 : :
12661 [ - + ]: 285 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12662 : 0 : dumpSecLabel(fout, "TYPE", qtypname,
12663 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12664 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12665 : :
12666 [ + + ]: 285 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12667 : 34 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12668 : : qtypname, NULL,
12669 : 34 : tyinfo->dobj.namespace->dobj.name,
12670 : 34 : NULL, tyinfo->rolname, &tyinfo->dacl);
12671 : :
12672 : 285 : PQclear(res);
12673 : 285 : destroyPQExpBuffer(q);
12674 : 285 : destroyPQExpBuffer(delq);
12675 : 285 : destroyPQExpBuffer(query);
12676 : 285 : pg_free(qtypname);
12677 : 285 : pg_free(qualtypname);
12678 : 285 : }
12679 : :
12680 : : /*
12681 : : * dumpDomain
12682 : : * writes out to fout the queries to recreate a user-defined domain
12683 : : */
12684 : : static void
12685 : 174 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12686 : : {
12687 : 174 : DumpOptions *dopt = fout->dopt;
12688 : 174 : PQExpBuffer q = createPQExpBuffer();
12689 : 174 : PQExpBuffer delq = createPQExpBuffer();
12690 : 174 : PQExpBuffer query = createPQExpBuffer();
12691 : : PGresult *res;
12692 : : int i;
12693 : : char *qtypname;
12694 : : char *qualtypname;
12695 : : char *typnotnull;
12696 : : char *typdefn;
12697 : : char *typdefault;
12698 : : Oid typcollation;
12699 : 174 : bool typdefault_is_literal = false;
12700 : :
12701 [ + + ]: 174 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12702 : : {
12703 : : /* Set up query for domain-specific details */
12704 : 39 : appendPQExpBufferStr(query,
12705 : : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12706 : :
12707 : 39 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12708 : : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12709 : : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12710 : : "t.typdefault, "
12711 : : "CASE WHEN t.typcollation <> u.typcollation "
12712 : : "THEN t.typcollation ELSE 0 END AS typcollation "
12713 : : "FROM pg_catalog.pg_type t "
12714 : : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12715 : : "WHERE t.oid = $1");
12716 : :
12717 : 39 : ExecuteSqlStatement(fout, query->data);
12718 : :
12719 : 39 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12720 : : }
12721 : :
12722 : 174 : printfPQExpBuffer(query,
12723 : : "EXECUTE dumpDomain('%u')",
12724 : 174 : tyinfo->dobj.catId.oid);
12725 : :
12726 : 174 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12727 : :
12728 : 174 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12729 : 174 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12730 [ + + ]: 174 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12731 : 39 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12732 [ - + ]: 135 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12733 : : {
12734 : 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12735 : 0 : typdefault_is_literal = true; /* it needs quotes */
12736 : : }
12737 : : else
12738 : 135 : typdefault = NULL;
12739 : 174 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12740 : :
12741 [ + + ]: 174 : if (dopt->binary_upgrade)
12742 : 29 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12743 : 29 : tyinfo->dobj.catId.oid,
12744 : : true, /* force array type */
12745 : : false); /* force multirange type */
12746 : :
12747 : 174 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12748 : 174 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12749 : :
12750 : 174 : appendPQExpBuffer(q,
12751 : : "CREATE DOMAIN %s AS %s",
12752 : : qualtypname,
12753 : : typdefn);
12754 : :
12755 : : /* Print collation only if different from base type's collation */
12756 [ + + ]: 174 : if (OidIsValid(typcollation))
12757 : : {
12758 : : CollInfo *coll;
12759 : :
12760 : 34 : coll = findCollationByOid(typcollation);
12761 [ + - ]: 34 : if (coll)
12762 : 34 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12763 : : }
12764 : :
12765 : : /*
12766 : : * Print a not-null constraint if there's one. In servers older than 17
12767 : : * these don't have names, so just print it unadorned; in newer ones they
12768 : : * do, but most of the time it's going to be the standard generated one,
12769 : : * so omit the name in that case also.
12770 : : */
12771 [ + + ]: 174 : if (typnotnull[0] == 't')
12772 : : {
12773 [ + - - + ]: 49 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12774 : 0 : appendPQExpBufferStr(q, " NOT NULL");
12775 : : else
12776 : : {
12777 : 49 : ConstraintInfo *notnull = tyinfo->notnull;
12778 : :
12779 [ + - ]: 49 : if (!notnull->separate)
12780 : : {
12781 : : char *default_name;
12782 : :
12783 : : /* XXX should match ChooseConstraintName better */
12784 : 49 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12785 : :
12786 [ + + ]: 49 : if (strcmp(default_name, notnull->dobj.name) == 0)
12787 : 15 : appendPQExpBufferStr(q, " NOT NULL");
12788 : : else
12789 : 34 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12790 : 34 : fmtId(notnull->dobj.name), notnull->condef);
12791 : 49 : pfree(default_name);
12792 : : }
12793 : : }
12794 : : }
12795 : :
12796 [ + + ]: 174 : if (typdefault != NULL)
12797 : : {
12798 : 39 : appendPQExpBufferStr(q, " DEFAULT ");
12799 [ - + ]: 39 : if (typdefault_is_literal)
12800 : 0 : appendStringLiteralAH(q, typdefault, fout);
12801 : : else
12802 : 39 : appendPQExpBufferStr(q, typdefault);
12803 : : }
12804 : :
12805 : 174 : PQclear(res);
12806 : :
12807 : : /*
12808 : : * Add any CHECK constraints for the domain
12809 : : */
12810 [ + + ]: 303 : for (i = 0; i < tyinfo->nDomChecks; i++)
12811 : : {
12812 : 129 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12813 : :
12814 [ + + + - ]: 129 : if (!domcheck->separate && domcheck->contype == 'c')
12815 : 124 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12816 : 124 : fmtId(domcheck->dobj.name), domcheck->condef);
12817 : : }
12818 : :
12819 : 174 : appendPQExpBufferStr(q, ";\n");
12820 : :
12821 : 174 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12822 : :
12823 [ + + ]: 174 : if (dopt->binary_upgrade)
12824 : 29 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12825 : : "DOMAIN", qtypname,
12826 : 29 : tyinfo->dobj.namespace->dobj.name);
12827 : :
12828 [ + - ]: 174 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12829 : 174 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12830 : 174 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12831 : : .namespace = tyinfo->dobj.namespace->dobj.name,
12832 : : .owner = tyinfo->rolname,
12833 : : .description = "DOMAIN",
12834 : : .section = SECTION_PRE_DATA,
12835 : : .createStmt = q->data,
12836 : : .dropStmt = delq->data));
12837 : :
12838 : : /* Dump Domain Comments and Security Labels */
12839 [ - + ]: 174 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12840 : 0 : dumpComment(fout, "DOMAIN", qtypname,
12841 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12842 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12843 : :
12844 [ - + ]: 174 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12845 : 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12846 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12847 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12848 : :
12849 [ + + ]: 174 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12850 : 34 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12851 : : qtypname, NULL,
12852 : 34 : tyinfo->dobj.namespace->dobj.name,
12853 : 34 : NULL, tyinfo->rolname, &tyinfo->dacl);
12854 : :
12855 : : /* Dump any per-constraint comments */
12856 [ + + ]: 303 : for (i = 0; i < tyinfo->nDomChecks; i++)
12857 : : {
12858 : 129 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12859 : : PQExpBuffer conprefix;
12860 : :
12861 : : /* but only if the constraint itself was dumped here */
12862 [ + + ]: 129 : if (domcheck->separate)
12863 : 5 : continue;
12864 : :
12865 : 124 : conprefix = createPQExpBuffer();
12866 : 124 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12867 : 124 : fmtId(domcheck->dobj.name));
12868 : :
12869 [ + + ]: 124 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12870 : 34 : dumpComment(fout, conprefix->data, qtypname,
12871 : 34 : tyinfo->dobj.namespace->dobj.name,
12872 : 34 : tyinfo->rolname,
12873 : 34 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12874 : :
12875 : 124 : destroyPQExpBuffer(conprefix);
12876 : : }
12877 : :
12878 : : /*
12879 : : * And a comment on the not-null constraint, if there's one -- but only if
12880 : : * the constraint itself was dumped here
12881 : : */
12882 [ + + + - ]: 174 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12883 : : {
12884 : 49 : PQExpBuffer conprefix = createPQExpBuffer();
12885 : :
12886 : 49 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12887 : 49 : fmtId(tyinfo->notnull->dobj.name));
12888 : :
12889 [ + + ]: 49 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12890 : 34 : dumpComment(fout, conprefix->data, qtypname,
12891 : 34 : tyinfo->dobj.namespace->dobj.name,
12892 : 34 : tyinfo->rolname,
12893 : 34 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12894 : 49 : destroyPQExpBuffer(conprefix);
12895 : : }
12896 : :
12897 : 174 : destroyPQExpBuffer(q);
12898 : 174 : destroyPQExpBuffer(delq);
12899 : 174 : destroyPQExpBuffer(query);
12900 : 174 : pg_free(qtypname);
12901 : 174 : pg_free(qualtypname);
12902 : 174 : }
12903 : :
12904 : : /*
12905 : : * dumpCompositeType
12906 : : * writes out to fout the queries to recreate a user-defined stand-alone
12907 : : * composite type
12908 : : */
12909 : : static void
12910 : 132 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12911 : : {
12912 : 132 : DumpOptions *dopt = fout->dopt;
12913 : 132 : PQExpBuffer q = createPQExpBuffer();
12914 : 132 : PQExpBuffer dropped = createPQExpBuffer();
12915 : 132 : PQExpBuffer delq = createPQExpBuffer();
12916 : 132 : PQExpBuffer query = createPQExpBuffer();
12917 : : PGresult *res;
12918 : : char *qtypname;
12919 : : char *qualtypname;
12920 : : int ntups;
12921 : : int i_attname;
12922 : : int i_atttypdefn;
12923 : : int i_attlen;
12924 : : int i_attalign;
12925 : : int i_attisdropped;
12926 : : int i_attcollation;
12927 : : int i;
12928 : : int actual_atts;
12929 : :
12930 [ + + ]: 132 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12931 : : {
12932 : : /*
12933 : : * Set up query for type-specific details.
12934 : : *
12935 : : * Since we only want to dump COLLATE clauses for attributes whose
12936 : : * collation is different from their type's default, we use a CASE
12937 : : * here to suppress uninteresting attcollations cheaply. atttypid
12938 : : * will be 0 for dropped columns; collation does not matter for those.
12939 : : */
12940 : 57 : appendPQExpBufferStr(query,
12941 : : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12942 : : "SELECT a.attname, a.attnum, "
12943 : : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12944 : : "a.attlen, a.attalign, a.attisdropped, "
12945 : : "CASE WHEN a.attcollation <> at.typcollation "
12946 : : "THEN a.attcollation ELSE 0 END AS attcollation "
12947 : : "FROM pg_catalog.pg_type ct "
12948 : : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12949 : : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12950 : : "WHERE ct.oid = $1 "
12951 : : "ORDER BY a.attnum");
12952 : :
12953 : 57 : ExecuteSqlStatement(fout, query->data);
12954 : :
12955 : 57 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12956 : : }
12957 : :
12958 : 132 : printfPQExpBuffer(query,
12959 : : "EXECUTE dumpCompositeType('%u')",
12960 : 132 : tyinfo->dobj.catId.oid);
12961 : :
12962 : 132 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12963 : :
12964 : 132 : ntups = PQntuples(res);
12965 : :
12966 : 132 : i_attname = PQfnumber(res, "attname");
12967 : 132 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12968 : 132 : i_attlen = PQfnumber(res, "attlen");
12969 : 132 : i_attalign = PQfnumber(res, "attalign");
12970 : 132 : i_attisdropped = PQfnumber(res, "attisdropped");
12971 : 132 : i_attcollation = PQfnumber(res, "attcollation");
12972 : :
12973 [ + + ]: 132 : if (dopt->binary_upgrade)
12974 : : {
12975 : 18 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12976 : 18 : tyinfo->dobj.catId.oid,
12977 : : false, false);
12978 : 18 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12979 : : }
12980 : :
12981 : 132 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12982 : 132 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12983 : :
12984 : 132 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12985 : : qualtypname);
12986 : :
12987 : 132 : actual_atts = 0;
12988 [ + + ]: 418 : for (i = 0; i < ntups; i++)
12989 : : {
12990 : : char *attname;
12991 : : char *atttypdefn;
12992 : : char *attlen;
12993 : : char *attalign;
12994 : : bool attisdropped;
12995 : : Oid attcollation;
12996 : :
12997 : 286 : attname = PQgetvalue(res, i, i_attname);
12998 : 286 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12999 : 286 : attlen = PQgetvalue(res, i, i_attlen);
13000 : 286 : attalign = PQgetvalue(res, i, i_attalign);
13001 : 286 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13002 : 286 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13003 : :
13004 [ + + + + ]: 286 : if (attisdropped && !dopt->binary_upgrade)
13005 : 8 : continue;
13006 : :
13007 : : /* Format properly if not first attr */
13008 [ + + ]: 278 : if (actual_atts++ > 0)
13009 : 146 : appendPQExpBufferChar(q, ',');
13010 : 278 : appendPQExpBufferStr(q, "\n\t");
13011 : :
13012 [ + + ]: 278 : if (!attisdropped)
13013 : : {
13014 : 276 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13015 : :
13016 : : /* Add collation if not default for the column type */
13017 [ - + ]: 276 : if (OidIsValid(attcollation))
13018 : : {
13019 : : CollInfo *coll;
13020 : :
13021 : 0 : coll = findCollationByOid(attcollation);
13022 [ # # ]: 0 : if (coll)
13023 : 0 : appendPQExpBuffer(q, " COLLATE %s",
13024 : 0 : fmtQualifiedDumpable(coll));
13025 : : }
13026 : : }
13027 : : else
13028 : : {
13029 : : /*
13030 : : * This is a dropped attribute and we're in binary_upgrade mode.
13031 : : * Insert a placeholder for it in the CREATE TYPE command, and set
13032 : : * length and alignment with direct UPDATE to the catalogs
13033 : : * afterwards. See similar code in dumpTableSchema().
13034 : : */
13035 : 2 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13036 : :
13037 : : /* stash separately for insertion after the CREATE TYPE */
13038 : 2 : appendPQExpBufferStr(dropped,
13039 : : "\n-- For binary upgrade, recreate dropped column.\n");
13040 : 2 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13041 : : "SET attlen = %s, "
13042 : : "attalign = '%s', attbyval = false\n"
13043 : : "WHERE attname = ", attlen, attalign);
13044 : 2 : appendStringLiteralAH(dropped, attname, fout);
13045 : 2 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13046 : 2 : appendStringLiteralAH(dropped, qualtypname, fout);
13047 : 2 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13048 : :
13049 : 2 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13050 : : qualtypname);
13051 : 2 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13052 : : fmtId(attname));
13053 : : }
13054 : : }
13055 : 132 : appendPQExpBufferStr(q, "\n);\n");
13056 : 132 : appendPQExpBufferStr(q, dropped->data);
13057 : :
13058 : 132 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13059 : :
13060 [ + + ]: 132 : if (dopt->binary_upgrade)
13061 : 18 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13062 : : "TYPE", qtypname,
13063 : 18 : tyinfo->dobj.namespace->dobj.name);
13064 : :
13065 [ + + ]: 132 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13066 : 115 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13067 : 115 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13068 : : .namespace = tyinfo->dobj.namespace->dobj.name,
13069 : : .owner = tyinfo->rolname,
13070 : : .description = "TYPE",
13071 : : .section = SECTION_PRE_DATA,
13072 : : .createStmt = q->data,
13073 : : .dropStmt = delq->data));
13074 : :
13075 : :
13076 : : /* Dump Type Comments and Security Labels */
13077 [ + + ]: 132 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13078 : 34 : dumpComment(fout, "TYPE", qtypname,
13079 : 34 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13080 : 34 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13081 : :
13082 [ - + ]: 132 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13083 : 0 : dumpSecLabel(fout, "TYPE", qtypname,
13084 : 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13085 : 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13086 : :
13087 [ + + ]: 132 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13088 : 18 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13089 : : qtypname, NULL,
13090 : 18 : tyinfo->dobj.namespace->dobj.name,
13091 : 18 : NULL, tyinfo->rolname, &tyinfo->dacl);
13092 : :
13093 : : /* Dump any per-column comments */
13094 [ + + ]: 132 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13095 : 34 : dumpCompositeTypeColComments(fout, tyinfo, res);
13096 : :
13097 : 132 : PQclear(res);
13098 : 132 : destroyPQExpBuffer(q);
13099 : 132 : destroyPQExpBuffer(dropped);
13100 : 132 : destroyPQExpBuffer(delq);
13101 : 132 : destroyPQExpBuffer(query);
13102 : 132 : pg_free(qtypname);
13103 : 132 : pg_free(qualtypname);
13104 : 132 : }
13105 : :
13106 : : /*
13107 : : * dumpCompositeTypeColComments
13108 : : * writes out to fout the queries to recreate comments on the columns of
13109 : : * a user-defined stand-alone composite type.
13110 : : *
13111 : : * The caller has already made a query to collect the names and attnums
13112 : : * of the type's columns, so we just pass that result into here rather
13113 : : * than reading them again.
13114 : : */
13115 : : static void
13116 : 34 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13117 : : PGresult *res)
13118 : : {
13119 : : CommentItem *comments;
13120 : : int ncomments;
13121 : : PQExpBuffer query;
13122 : : PQExpBuffer target;
13123 : : int i;
13124 : : int ntups;
13125 : : int i_attname;
13126 : : int i_attnum;
13127 : : int i_attisdropped;
13128 : :
13129 : : /* do nothing, if --no-comments is supplied */
13130 [ - + ]: 34 : if (fout->dopt->no_comments)
13131 : 0 : return;
13132 : :
13133 : : /* Search for comments associated with type's pg_class OID */
13134 : 34 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13135 : : &comments);
13136 : :
13137 : : /* If no comments exist, we're done */
13138 [ - + ]: 34 : if (ncomments <= 0)
13139 : 0 : return;
13140 : :
13141 : : /* Build COMMENT ON statements */
13142 : 34 : query = createPQExpBuffer();
13143 : 34 : target = createPQExpBuffer();
13144 : :
13145 : 34 : ntups = PQntuples(res);
13146 : 34 : i_attnum = PQfnumber(res, "attnum");
13147 : 34 : i_attname = PQfnumber(res, "attname");
13148 : 34 : i_attisdropped = PQfnumber(res, "attisdropped");
13149 [ + + ]: 68 : while (ncomments > 0)
13150 : : {
13151 : : const char *attname;
13152 : :
13153 : 34 : attname = NULL;
13154 [ + - ]: 34 : for (i = 0; i < ntups; i++)
13155 : : {
13156 [ + - ]: 34 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13157 [ + - ]: 34 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13158 : : {
13159 : 34 : attname = PQgetvalue(res, i, i_attname);
13160 : 34 : break;
13161 : : }
13162 : : }
13163 [ + - ]: 34 : if (attname) /* just in case we don't find it */
13164 : : {
13165 : 34 : const char *descr = comments->descr;
13166 : :
13167 : 34 : resetPQExpBuffer(target);
13168 : 34 : appendPQExpBuffer(target, "COLUMN %s.",
13169 : 34 : fmtId(tyinfo->dobj.name));
13170 : 34 : appendPQExpBufferStr(target, fmtId(attname));
13171 : :
13172 : 34 : resetPQExpBuffer(query);
13173 : 34 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13174 : 34 : fmtQualifiedDumpable(tyinfo));
13175 : 34 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13176 : 34 : appendStringLiteralAH(query, descr, fout);
13177 : 34 : appendPQExpBufferStr(query, ";\n");
13178 : :
13179 : 34 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13180 : 34 : ARCHIVE_OPTS(.tag = target->data,
13181 : : .namespace = tyinfo->dobj.namespace->dobj.name,
13182 : : .owner = tyinfo->rolname,
13183 : : .description = "COMMENT",
13184 : : .section = SECTION_NONE,
13185 : : .createStmt = query->data,
13186 : : .deps = &(tyinfo->dobj.dumpId),
13187 : : .nDeps = 1));
13188 : : }
13189 : :
13190 : 34 : comments++;
13191 : 34 : ncomments--;
13192 : : }
13193 : :
13194 : 34 : destroyPQExpBuffer(query);
13195 : 34 : destroyPQExpBuffer(target);
13196 : : }
13197 : :
13198 : : /*
13199 : : * dumpShellType
13200 : : * writes out to fout the queries to create a shell type
13201 : : *
13202 : : * We dump a shell definition in advance of the I/O functions for the type.
13203 : : */
13204 : : static void
13205 : 76 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13206 : : {
13207 : 76 : DumpOptions *dopt = fout->dopt;
13208 : : PQExpBuffer q;
13209 : :
13210 : : /* Do nothing if not dumping schema */
13211 [ + + ]: 76 : if (!dopt->dumpSchema)
13212 : 7 : return;
13213 : :
13214 : 69 : q = createPQExpBuffer();
13215 : :
13216 : : /*
13217 : : * Note the lack of a DROP command for the shell type; any required DROP
13218 : : * is driven off the base type entry, instead. This interacts with
13219 : : * _printTocEntry()'s use of the presence of a DROP command to decide
13220 : : * whether an entry needs an ALTER OWNER command. We don't want to alter
13221 : : * the shell type's owner immediately on creation; that should happen only
13222 : : * after it's filled in, otherwise the backend complains.
13223 : : */
13224 : :
13225 [ + + ]: 69 : if (dopt->binary_upgrade)
13226 : 8 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13227 : 8 : stinfo->baseType->dobj.catId.oid,
13228 : : false, false);
13229 : :
13230 : 69 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13231 : 69 : fmtQualifiedDumpable(stinfo));
13232 : :
13233 [ + - ]: 69 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13234 : 69 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13235 : 69 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13236 : : .namespace = stinfo->dobj.namespace->dobj.name,
13237 : : .owner = stinfo->baseType->rolname,
13238 : : .description = "SHELL TYPE",
13239 : : .section = SECTION_PRE_DATA,
13240 : : .createStmt = q->data));
13241 : :
13242 : 69 : destroyPQExpBuffer(q);
13243 : : }
13244 : :
13245 : : /*
13246 : : * dumpProcLang
13247 : : * writes out to fout the queries to recreate a user-defined
13248 : : * procedural language
13249 : : */
13250 : : static void
13251 : 87 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13252 : : {
13253 : 87 : DumpOptions *dopt = fout->dopt;
13254 : : PQExpBuffer defqry;
13255 : : PQExpBuffer delqry;
13256 : : bool useParams;
13257 : : char *qlanname;
13258 : : FuncInfo *funcInfo;
13259 : 87 : FuncInfo *inlineInfo = NULL;
13260 : 87 : FuncInfo *validatorInfo = NULL;
13261 : :
13262 : : /* Do nothing if not dumping schema */
13263 [ + + ]: 87 : if (!dopt->dumpSchema)
13264 : 14 : return;
13265 : :
13266 : : /*
13267 : : * Try to find the support function(s). It is not an error if we don't
13268 : : * find them --- if the functions are in the pg_catalog schema, as is
13269 : : * standard in 8.1 and up, then we won't have loaded them. (In this case
13270 : : * we will emit a parameterless CREATE LANGUAGE command, which will
13271 : : * require PL template knowledge in the backend to reload.)
13272 : : */
13273 : :
13274 : 73 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13275 [ + + + + ]: 73 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13276 : 2 : funcInfo = NULL; /* treat not-dumped same as not-found */
13277 : :
13278 [ + + ]: 73 : if (OidIsValid(plang->laninline))
13279 : : {
13280 : 40 : inlineInfo = findFuncByOid(plang->laninline);
13281 [ + + + - ]: 40 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13282 : 1 : inlineInfo = NULL;
13283 : : }
13284 : :
13285 [ + + ]: 73 : if (OidIsValid(plang->lanvalidator))
13286 : : {
13287 : 40 : validatorInfo = findFuncByOid(plang->lanvalidator);
13288 [ + + + - ]: 40 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13289 : 1 : validatorInfo = NULL;
13290 : : }
13291 : :
13292 : : /*
13293 : : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13294 : : * parameters. Otherwise, we'll write a parameterless command, which will
13295 : : * be interpreted as CREATE EXTENSION.
13296 : : */
13297 [ + - ]: 32 : useParams = (funcInfo != NULL &&
13298 [ + + + - : 137 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
+ - ]
13299 [ + - ]: 32 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13300 : :
13301 : 73 : defqry = createPQExpBuffer();
13302 : 73 : delqry = createPQExpBuffer();
13303 : :
13304 : 73 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13305 : :
13306 : 73 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13307 : : qlanname);
13308 : :
13309 [ + + ]: 73 : if (useParams)
13310 : : {
13311 : 32 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13312 [ - + ]: 32 : plang->lanpltrusted ? "TRUSTED " : "",
13313 : : qlanname);
13314 : 32 : appendPQExpBuffer(defqry, " HANDLER %s",
13315 : 32 : fmtQualifiedDumpable(funcInfo));
13316 [ - + ]: 32 : if (OidIsValid(plang->laninline))
13317 : 0 : appendPQExpBuffer(defqry, " INLINE %s",
13318 : 0 : fmtQualifiedDumpable(inlineInfo));
13319 [ - + ]: 32 : if (OidIsValid(plang->lanvalidator))
13320 : 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13321 : 0 : fmtQualifiedDumpable(validatorInfo));
13322 : : }
13323 : : else
13324 : : {
13325 : : /*
13326 : : * If not dumping parameters, then use CREATE OR REPLACE so that the
13327 : : * command will not fail if the language is preinstalled in the target
13328 : : * database.
13329 : : *
13330 : : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13331 : : * EXISTS; perhaps we should emit that instead? But it might just add
13332 : : * confusion.
13333 : : */
13334 : 41 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13335 : : qlanname);
13336 : : }
13337 : 73 : appendPQExpBufferStr(defqry, ";\n");
13338 : :
13339 [ + + ]: 73 : if (dopt->binary_upgrade)
13340 : 2 : binary_upgrade_extension_member(defqry, &plang->dobj,
13341 : : "LANGUAGE", qlanname, NULL);
13342 : :
13343 [ + + ]: 73 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13344 : 33 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13345 : 33 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13346 : : .owner = plang->lanowner,
13347 : : .description = "PROCEDURAL LANGUAGE",
13348 : : .section = SECTION_PRE_DATA,
13349 : : .createStmt = defqry->data,
13350 : : .dropStmt = delqry->data,
13351 : : ));
13352 : :
13353 : : /* Dump Proc Lang Comments and Security Labels */
13354 [ - + ]: 73 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13355 : 0 : dumpComment(fout, "LANGUAGE", qlanname,
13356 : 0 : NULL, plang->lanowner,
13357 : 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13358 : :
13359 [ - + ]: 73 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13360 : 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13361 : 0 : NULL, plang->lanowner,
13362 : 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13363 : :
13364 [ + + + - ]: 73 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13365 : 40 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13366 : : qlanname, NULL, NULL,
13367 : 40 : NULL, plang->lanowner, &plang->dacl);
13368 : :
13369 : 73 : pg_free(qlanname);
13370 : :
13371 : 73 : destroyPQExpBuffer(defqry);
13372 : 73 : destroyPQExpBuffer(delqry);
13373 : : }
13374 : :
13375 : : /*
13376 : : * format_function_arguments: generate function name and argument list
13377 : : *
13378 : : * This is used when we can rely on pg_get_function_arguments to format
13379 : : * the argument list. Note, however, that pg_get_function_arguments
13380 : : * does not special-case zero-argument aggregates.
13381 : : */
13382 : : static char *
13383 : 4268 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13384 : : {
13385 : : PQExpBufferData fn;
13386 : :
13387 : 4268 : initPQExpBuffer(&fn);
13388 : 4268 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13389 [ + + + + ]: 4268 : if (is_agg && finfo->nargs == 0)
13390 : 80 : appendPQExpBufferStr(&fn, "(*)");
13391 : : else
13392 : 4188 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13393 : 4268 : return fn.data;
13394 : : }
13395 : :
13396 : : /*
13397 : : * format_function_signature: generate function name and argument list
13398 : : *
13399 : : * Only a minimal list of input argument types is generated; this is
13400 : : * sufficient to reference the function, but not to define it.
13401 : : *
13402 : : * If honor_quotes is false then the function name is never quoted.
13403 : : * This is appropriate for use in TOC tags, but not in SQL commands.
13404 : : */
13405 : : static char *
13406 : 2248 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13407 : : {
13408 : : PQExpBufferData fn;
13409 : : int j;
13410 : :
13411 : 2248 : initPQExpBuffer(&fn);
13412 [ + + ]: 2248 : if (honor_quotes)
13413 : 401 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13414 : : else
13415 : 1847 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13416 [ + + ]: 4135 : for (j = 0; j < finfo->nargs; j++)
13417 : : {
13418 [ + + ]: 1887 : if (j > 0)
13419 : 452 : appendPQExpBufferStr(&fn, ", ");
13420 : :
13421 : 1887 : appendPQExpBufferStr(&fn,
13422 : 1887 : getFormattedTypeName(fout, finfo->argtypes[j],
13423 : : zeroIsError));
13424 : : }
13425 : 2248 : appendPQExpBufferChar(&fn, ')');
13426 : 2248 : return fn.data;
13427 : : }
13428 : :
13429 : :
13430 : : /*
13431 : : * dumpFunc:
13432 : : * dump out one function
13433 : : */
13434 : : static void
13435 : 1917 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13436 : : {
13437 : 1917 : DumpOptions *dopt = fout->dopt;
13438 : : PQExpBuffer query;
13439 : : PQExpBuffer q;
13440 : : PQExpBuffer delqry;
13441 : : PQExpBuffer asPart;
13442 : : PGresult *res;
13443 : : char *funcsig; /* identity signature */
13444 : 1917 : char *funcfullsig = NULL; /* full signature */
13445 : : char *funcsig_tag;
13446 : : char *qual_funcsig;
13447 : : char *proretset;
13448 : : char *prosrc;
13449 : : char *probin;
13450 : : char *prosqlbody;
13451 : : char *funcargs;
13452 : : char *funciargs;
13453 : : char *funcresult;
13454 : : char *protrftypes;
13455 : : char *prokind;
13456 : : char *provolatile;
13457 : : char *proisstrict;
13458 : : char *prosecdef;
13459 : : char *proleakproof;
13460 : : char *proconfig;
13461 : : char *procost;
13462 : : char *prorows;
13463 : : char *prosupport;
13464 : : char *proparallel;
13465 : : char *lanname;
13466 : 1917 : char **configitems = NULL;
13467 : 1917 : int nconfigitems = 0;
13468 : : const char *keyword;
13469 : :
13470 : : /* Do nothing if not dumping schema */
13471 [ + + ]: 1917 : if (!dopt->dumpSchema)
13472 : 70 : return;
13473 : :
13474 : 1847 : query = createPQExpBuffer();
13475 : 1847 : q = createPQExpBuffer();
13476 : 1847 : delqry = createPQExpBuffer();
13477 : 1847 : asPart = createPQExpBuffer();
13478 : :
13479 [ + + ]: 1847 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13480 : : {
13481 : : /* Set up query for function-specific details */
13482 : 69 : appendPQExpBufferStr(query,
13483 : : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13484 : :
13485 : 69 : appendPQExpBufferStr(query,
13486 : : "SELECT\n"
13487 : : "proretset,\n"
13488 : : "prosrc,\n"
13489 : : "probin,\n"
13490 : : "provolatile,\n"
13491 : : "proisstrict,\n"
13492 : : "prosecdef,\n"
13493 : : "lanname,\n"
13494 : : "proconfig,\n"
13495 : : "procost,\n"
13496 : : "prorows,\n"
13497 : : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13498 : : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13499 : : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13500 : : "proleakproof,\n");
13501 : :
13502 : 69 : appendPQExpBufferStr(query,
13503 : : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13504 : :
13505 : 69 : appendPQExpBufferStr(query,
13506 : : "proparallel,\n");
13507 : :
13508 [ + - ]: 69 : if (fout->remoteVersion >= 110000)
13509 : 69 : appendPQExpBufferStr(query,
13510 : : "prokind,\n");
13511 : : else
13512 : 0 : appendPQExpBufferStr(query,
13513 : : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13514 : :
13515 [ + - ]: 69 : if (fout->remoteVersion >= 120000)
13516 : 69 : appendPQExpBufferStr(query,
13517 : : "prosupport,\n");
13518 : : else
13519 : 0 : appendPQExpBufferStr(query,
13520 : : "'-' AS prosupport,\n");
13521 : :
13522 [ + - ]: 69 : if (fout->remoteVersion >= 140000)
13523 : 69 : appendPQExpBufferStr(query,
13524 : : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13525 : : else
13526 : 0 : appendPQExpBufferStr(query,
13527 : : "NULL AS prosqlbody\n");
13528 : :
13529 : 69 : appendPQExpBufferStr(query,
13530 : : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13531 : : "WHERE p.oid = $1 "
13532 : : "AND l.oid = p.prolang");
13533 : :
13534 : 69 : ExecuteSqlStatement(fout, query->data);
13535 : :
13536 : 69 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13537 : : }
13538 : :
13539 : 1847 : printfPQExpBuffer(query,
13540 : : "EXECUTE dumpFunc('%u')",
13541 : 1847 : finfo->dobj.catId.oid);
13542 : :
13543 : 1847 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13544 : :
13545 : 1847 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13546 [ + + ]: 1847 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13547 : : {
13548 : 1797 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13549 : 1797 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13550 : 1797 : prosqlbody = NULL;
13551 : : }
13552 : : else
13553 : : {
13554 : 50 : prosrc = NULL;
13555 : 50 : probin = NULL;
13556 : 50 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13557 : : }
13558 : 1847 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13559 : 1847 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13560 : 1847 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13561 : 1847 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13562 : 1847 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13563 : 1847 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13564 : 1847 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13565 : 1847 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13566 : 1847 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13567 : 1847 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13568 : 1847 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13569 : 1847 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13570 : 1847 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13571 : 1847 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13572 : 1847 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13573 : :
13574 : : /*
13575 : : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13576 : : * is used.
13577 : : */
13578 [ + + ]: 1847 : if (prosqlbody)
13579 : : {
13580 : 50 : appendPQExpBufferStr(asPart, prosqlbody);
13581 : : }
13582 [ + + ]: 1797 : else if (probin[0] != '\0')
13583 : : {
13584 : 160 : appendPQExpBufferStr(asPart, "AS ");
13585 : 160 : appendStringLiteralAH(asPart, probin, fout);
13586 [ + - ]: 160 : if (prosrc[0] != '\0')
13587 : : {
13588 : 160 : appendPQExpBufferStr(asPart, ", ");
13589 : :
13590 : : /*
13591 : : * where we have bin, use dollar quoting if allowed and src
13592 : : * contains quote or backslash; else use regular quoting.
13593 : : */
13594 [ + - ]: 160 : if (dopt->disable_dollar_quoting ||
13595 [ + - + - ]: 160 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13596 : 160 : appendStringLiteralAH(asPart, prosrc, fout);
13597 : : else
13598 : 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13599 : : }
13600 : : }
13601 : : else
13602 : : {
13603 : 1637 : appendPQExpBufferStr(asPart, "AS ");
13604 : : /* with no bin, dollar quote src unconditionally if allowed */
13605 [ - + ]: 1637 : if (dopt->disable_dollar_quoting)
13606 : 0 : appendStringLiteralAH(asPart, prosrc, fout);
13607 : : else
13608 : 1637 : appendStringLiteralDQ(asPart, prosrc, NULL);
13609 : : }
13610 : :
13611 [ + + ]: 1847 : if (*proconfig)
13612 : : {
13613 [ - + ]: 15 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13614 : 0 : pg_fatal("could not parse %s array", "proconfig");
13615 : : }
13616 : : else
13617 : : {
13618 : 1832 : configitems = NULL;
13619 : 1832 : nconfigitems = 0;
13620 : : }
13621 : :
13622 : 1847 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13623 : 1847 : funcsig = format_function_arguments(finfo, funciargs, false);
13624 : :
13625 : 1847 : funcsig_tag = format_function_signature(fout, finfo, false);
13626 : :
13627 : 1847 : qual_funcsig = psprintf("%s.%s",
13628 : 1847 : fmtId(finfo->dobj.namespace->dobj.name),
13629 : : funcsig);
13630 : :
13631 [ + + ]: 1847 : if (prokind[0] == PROKIND_PROCEDURE)
13632 : 94 : keyword = "PROCEDURE";
13633 : : else
13634 : 1753 : keyword = "FUNCTION"; /* works for window functions too */
13635 : :
13636 : 1847 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13637 : : keyword, qual_funcsig);
13638 : :
13639 [ + - ]: 3694 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13640 : : keyword,
13641 : 1847 : fmtId(finfo->dobj.namespace->dobj.name),
13642 : : funcfullsig ? funcfullsig :
13643 : : funcsig);
13644 : :
13645 [ + + ]: 1847 : if (prokind[0] == PROKIND_PROCEDURE)
13646 : : /* no result type to output */ ;
13647 [ + - ]: 1753 : else if (funcresult)
13648 : 1753 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13649 : : else
13650 : 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13651 [ # # ]: 0 : (proretset[0] == 't') ? "SETOF " : "",
13652 : 0 : getFormattedTypeName(fout, finfo->prorettype,
13653 : : zeroIsError));
13654 : :
13655 : 1847 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13656 : :
13657 [ - + ]: 1847 : if (*protrftypes)
13658 : : {
13659 : 0 : Oid *typeids = pg_malloc_array(Oid, FUNC_MAX_ARGS);
13660 : : int i;
13661 : :
13662 : 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13663 : 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13664 [ # # ]: 0 : for (i = 0; typeids[i]; i++)
13665 : : {
13666 [ # # ]: 0 : if (i != 0)
13667 : 0 : appendPQExpBufferStr(q, ", ");
13668 : 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13669 : 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13670 : : }
13671 : :
13672 : 0 : pg_free(typeids);
13673 : : }
13674 : :
13675 [ + + ]: 1847 : if (prokind[0] == PROKIND_WINDOW)
13676 : 5 : appendPQExpBufferStr(q, " WINDOW");
13677 : :
13678 [ + + ]: 1847 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13679 : : {
13680 [ + + ]: 355 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13681 : 334 : appendPQExpBufferStr(q, " IMMUTABLE");
13682 [ + - ]: 21 : else if (provolatile[0] == PROVOLATILE_STABLE)
13683 : 21 : appendPQExpBufferStr(q, " STABLE");
13684 [ # # ]: 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13685 : 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13686 : : finfo->dobj.name);
13687 : : }
13688 : :
13689 [ + + ]: 1847 : if (proisstrict[0] == 't')
13690 : 364 : appendPQExpBufferStr(q, " STRICT");
13691 : :
13692 [ - + ]: 1847 : if (prosecdef[0] == 't')
13693 : 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13694 : :
13695 [ + + ]: 1847 : if (proleakproof[0] == 't')
13696 : 10 : appendPQExpBufferStr(q, " LEAKPROOF");
13697 : :
13698 : : /*
13699 : : * COST and ROWS are emitted only if present and not default, so as not to
13700 : : * break backwards-compatibility of the dump without need. Keep this code
13701 : : * in sync with the defaults in functioncmds.c.
13702 : : */
13703 [ + - ]: 1847 : if (strcmp(procost, "0") != 0)
13704 : : {
13705 [ + + + + ]: 1847 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13706 : : {
13707 : : /* default cost is 1 */
13708 [ - + ]: 397 : if (strcmp(procost, "1") != 0)
13709 : 0 : appendPQExpBuffer(q, " COST %s", procost);
13710 : : }
13711 : : else
13712 : : {
13713 : : /* default cost is 100 */
13714 [ + + ]: 1450 : if (strcmp(procost, "100") != 0)
13715 : 11 : appendPQExpBuffer(q, " COST %s", procost);
13716 : : }
13717 : : }
13718 [ + + ]: 1847 : if (proretset[0] == 't' &&
13719 [ + - - + ]: 194 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13720 : 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13721 : :
13722 [ + + ]: 1847 : if (strcmp(prosupport, "-") != 0)
13723 : : {
13724 : : /* We rely on regprocout to provide quoting and qualification */
13725 : 44 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13726 : : }
13727 : :
13728 [ + + ]: 1847 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13729 : : {
13730 [ + + ]: 120 : if (proparallel[0] == PROPARALLEL_SAFE)
13731 : 115 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13732 [ + - ]: 5 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13733 : 5 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13734 [ # # ]: 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13735 : 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13736 : : finfo->dobj.name);
13737 : : }
13738 : :
13739 [ + + ]: 1887 : for (int i = 0; i < nconfigitems; i++)
13740 : : {
13741 : : /* we feel free to scribble on configitems[] here */
13742 : 40 : char *configitem = configitems[i];
13743 : : char *pos;
13744 : :
13745 : 40 : pos = strchr(configitem, '=');
13746 [ - + ]: 40 : if (pos == NULL)
13747 : 0 : continue;
13748 : 40 : *pos++ = '\0';
13749 : 40 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13750 : :
13751 : : /*
13752 : : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13753 : : * by flatten_set_variable_args() before they were put into the
13754 : : * proconfig array. However, because the quoting rules used there
13755 : : * aren't exactly like SQL's, we have to break the list value apart
13756 : : * and then quote the elements as string literals. (The elements may
13757 : : * be double-quoted as-is, but we can't just feed them to the SQL
13758 : : * parser; it would do the wrong thing with elements that are
13759 : : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13760 : : * case for empty lists.
13761 : : *
13762 : : * Variables that are not so marked should just be emitted as simple
13763 : : * string literals. If the variable is not known to
13764 : : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13765 : : * to use GUC_LIST_QUOTE for extension variables.
13766 : : */
13767 [ + + ]: 40 : if (variable_is_guc_list_quote(configitem))
13768 : : {
13769 : : char **namelist;
13770 : : char **nameptr;
13771 : :
13772 : : /* Parse string into list of identifiers */
13773 : : /* this shouldn't fail really */
13774 [ + - ]: 15 : if (SplitGUCList(pos, ',', &namelist))
13775 : : {
13776 : : /* Special case: represent an empty list as NULL */
13777 [ + + ]: 15 : if (*namelist == NULL)
13778 : 5 : appendPQExpBufferStr(q, "NULL");
13779 [ + + ]: 40 : for (nameptr = namelist; *nameptr; nameptr++)
13780 : : {
13781 [ + + ]: 25 : if (nameptr != namelist)
13782 : 15 : appendPQExpBufferStr(q, ", ");
13783 : 25 : appendStringLiteralAH(q, *nameptr, fout);
13784 : : }
13785 : : }
13786 : 15 : pg_free(namelist);
13787 : : }
13788 : : else
13789 : 25 : appendStringLiteralAH(q, pos, fout);
13790 : : }
13791 : :
13792 : 1847 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13793 : :
13794 : 1847 : append_depends_on_extension(fout, q, &finfo->dobj,
13795 : : "pg_catalog.pg_proc", keyword,
13796 : : qual_funcsig);
13797 : :
13798 [ + + ]: 1847 : if (dopt->binary_upgrade)
13799 : 308 : binary_upgrade_extension_member(q, &finfo->dobj,
13800 : : keyword, funcsig,
13801 : 308 : finfo->dobj.namespace->dobj.name);
13802 : :
13803 [ + + ]: 1847 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13804 : 1747 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13805 [ + + ]: 1747 : ARCHIVE_OPTS(.tag = funcsig_tag,
13806 : : .namespace = finfo->dobj.namespace->dobj.name,
13807 : : .owner = finfo->rolname,
13808 : : .description = keyword,
13809 : : .section = finfo->postponed_def ?
13810 : : SECTION_POST_DATA : SECTION_PRE_DATA,
13811 : : .createStmt = q->data,
13812 : : .dropStmt = delqry->data));
13813 : :
13814 : : /* Dump Function Comments and Security Labels */
13815 [ + + ]: 1847 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13816 : 9 : dumpComment(fout, keyword, funcsig,
13817 : 9 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13818 : 9 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13819 : :
13820 [ - + ]: 1847 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13821 : 0 : dumpSecLabel(fout, keyword, funcsig,
13822 : 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13823 : 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13824 : :
13825 [ + + ]: 1847 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13826 : 104 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13827 : : funcsig, NULL,
13828 : 104 : finfo->dobj.namespace->dobj.name,
13829 : 104 : NULL, finfo->rolname, &finfo->dacl);
13830 : :
13831 : 1847 : PQclear(res);
13832 : :
13833 : 1847 : destroyPQExpBuffer(query);
13834 : 1847 : destroyPQExpBuffer(q);
13835 : 1847 : destroyPQExpBuffer(delqry);
13836 : 1847 : destroyPQExpBuffer(asPart);
13837 : 1847 : free(funcsig);
13838 : 1847 : free(funcfullsig);
13839 : 1847 : free(funcsig_tag);
13840 : 1847 : pfree(qual_funcsig);
13841 : 1847 : free(configitems);
13842 : : }
13843 : :
13844 : :
13845 : : /*
13846 : : * Dump a user-defined cast
13847 : : */
13848 : : static void
13849 : 69 : dumpCast(Archive *fout, const CastInfo *cast)
13850 : : {
13851 : 69 : DumpOptions *dopt = fout->dopt;
13852 : : PQExpBuffer defqry;
13853 : : PQExpBuffer delqry;
13854 : : PQExpBuffer labelq;
13855 : : PQExpBuffer castargs;
13856 : 69 : FuncInfo *funcInfo = NULL;
13857 : : const char *sourceType;
13858 : : const char *targetType;
13859 : :
13860 : : /* Do nothing if not dumping schema */
13861 [ + + ]: 69 : if (!dopt->dumpSchema)
13862 : 6 : return;
13863 : :
13864 : : /* Cannot dump if we don't have the cast function's info */
13865 [ + + ]: 63 : if (OidIsValid(cast->castfunc))
13866 : : {
13867 : 38 : funcInfo = findFuncByOid(cast->castfunc);
13868 [ - + ]: 38 : if (funcInfo == NULL)
13869 : 0 : pg_fatal("could not find function definition for function with OID %u",
13870 : : cast->castfunc);
13871 : : }
13872 : :
13873 : 63 : defqry = createPQExpBuffer();
13874 : 63 : delqry = createPQExpBuffer();
13875 : 63 : labelq = createPQExpBuffer();
13876 : 63 : castargs = createPQExpBuffer();
13877 : :
13878 : 63 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13879 : 63 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13880 : 63 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13881 : : sourceType, targetType);
13882 : :
13883 : 63 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13884 : : sourceType, targetType);
13885 : :
13886 [ + - + - ]: 63 : switch (cast->castmethod)
13887 : : {
13888 : 25 : case COERCION_METHOD_BINARY:
13889 : 25 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13890 : 25 : break;
13891 : 0 : case COERCION_METHOD_INOUT:
13892 : 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13893 : 0 : break;
13894 : 38 : case COERCION_METHOD_FUNCTION:
13895 [ + - ]: 38 : if (funcInfo)
13896 : : {
13897 : 38 : char *fsig = format_function_signature(fout, funcInfo, true);
13898 : :
13899 : : /*
13900 : : * Always qualify the function name (format_function_signature
13901 : : * won't qualify it).
13902 : : */
13903 : 38 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13904 : 38 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13905 : 38 : free(fsig);
13906 : : }
13907 : : else
13908 : 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13909 : 38 : break;
13910 : 0 : default:
13911 : 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13912 : : }
13913 : :
13914 [ + + ]: 63 : if (cast->castcontext == 'a')
13915 : 33 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13916 [ + + ]: 30 : else if (cast->castcontext == 'i')
13917 : 10 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13918 : 63 : appendPQExpBufferStr(defqry, ";\n");
13919 : :
13920 : 63 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13921 : : sourceType, targetType);
13922 : :
13923 : 63 : appendPQExpBuffer(castargs, "(%s AS %s)",
13924 : : sourceType, targetType);
13925 : :
13926 [ + + ]: 63 : if (dopt->binary_upgrade)
13927 : 7 : binary_upgrade_extension_member(defqry, &cast->dobj,
13928 : 7 : "CAST", castargs->data, NULL);
13929 : :
13930 [ + - ]: 63 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13931 : 63 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13932 : 63 : ARCHIVE_OPTS(.tag = labelq->data,
13933 : : .description = "CAST",
13934 : : .section = SECTION_PRE_DATA,
13935 : : .createStmt = defqry->data,
13936 : : .dropStmt = delqry->data));
13937 : :
13938 : : /* Dump Cast Comments */
13939 [ - + ]: 63 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13940 : 0 : dumpComment(fout, "CAST", castargs->data,
13941 : : NULL, "",
13942 : 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13943 : :
13944 : 63 : destroyPQExpBuffer(defqry);
13945 : 63 : destroyPQExpBuffer(delqry);
13946 : 63 : destroyPQExpBuffer(labelq);
13947 : 63 : destroyPQExpBuffer(castargs);
13948 : : }
13949 : :
13950 : : /*
13951 : : * Dump a transform
13952 : : */
13953 : : static void
13954 : 44 : dumpTransform(Archive *fout, const TransformInfo *transform)
13955 : : {
13956 : 44 : DumpOptions *dopt = fout->dopt;
13957 : : PQExpBuffer defqry;
13958 : : PQExpBuffer delqry;
13959 : : PQExpBuffer labelq;
13960 : : PQExpBuffer transformargs;
13961 : 44 : FuncInfo *fromsqlFuncInfo = NULL;
13962 : 44 : FuncInfo *tosqlFuncInfo = NULL;
13963 : : char *lanname;
13964 : : const char *transformType;
13965 : :
13966 : : /* Do nothing if not dumping schema */
13967 [ + + ]: 44 : if (!dopt->dumpSchema)
13968 : 6 : return;
13969 : :
13970 : : /* Cannot dump if we don't have the transform functions' info */
13971 [ + - ]: 38 : if (OidIsValid(transform->trffromsql))
13972 : : {
13973 : 38 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13974 [ - + ]: 38 : if (fromsqlFuncInfo == NULL)
13975 : 0 : pg_fatal("could not find function definition for function with OID %u",
13976 : : transform->trffromsql);
13977 : : }
13978 [ + - ]: 38 : if (OidIsValid(transform->trftosql))
13979 : : {
13980 : 38 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13981 [ - + ]: 38 : if (tosqlFuncInfo == NULL)
13982 : 0 : pg_fatal("could not find function definition for function with OID %u",
13983 : : transform->trftosql);
13984 : : }
13985 : :
13986 : 38 : defqry = createPQExpBuffer();
13987 : 38 : delqry = createPQExpBuffer();
13988 : 38 : labelq = createPQExpBuffer();
13989 : 38 : transformargs = createPQExpBuffer();
13990 : :
13991 : 38 : lanname = get_language_name(fout, transform->trflang);
13992 : 38 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13993 : :
13994 : 38 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13995 : : transformType, lanname);
13996 : :
13997 : 38 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13998 : : transformType, lanname);
13999 : :
14000 [ - + - - ]: 38 : if (!transform->trffromsql && !transform->trftosql)
14001 : 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14002 : :
14003 [ + - ]: 38 : if (transform->trffromsql)
14004 : : {
14005 [ + - ]: 38 : if (fromsqlFuncInfo)
14006 : : {
14007 : 38 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14008 : :
14009 : : /*
14010 : : * Always qualify the function name (format_function_signature
14011 : : * won't qualify it).
14012 : : */
14013 : 38 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14014 : 38 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14015 : 38 : free(fsig);
14016 : : }
14017 : : else
14018 : 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14019 : : }
14020 : :
14021 [ + - ]: 38 : if (transform->trftosql)
14022 : : {
14023 [ + - ]: 38 : if (transform->trffromsql)
14024 : 38 : appendPQExpBufferStr(defqry, ", ");
14025 : :
14026 [ + - ]: 38 : if (tosqlFuncInfo)
14027 : : {
14028 : 38 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14029 : :
14030 : : /*
14031 : : * Always qualify the function name (format_function_signature
14032 : : * won't qualify it).
14033 : : */
14034 : 38 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14035 : 38 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14036 : 38 : free(fsig);
14037 : : }
14038 : : else
14039 : 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14040 : : }
14041 : :
14042 : 38 : appendPQExpBufferStr(defqry, ");\n");
14043 : :
14044 : 38 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14045 : : transformType, lanname);
14046 : :
14047 : 38 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14048 : : transformType, lanname);
14049 : :
14050 [ + + ]: 38 : if (dopt->binary_upgrade)
14051 : 2 : binary_upgrade_extension_member(defqry, &transform->dobj,
14052 : 2 : "TRANSFORM", transformargs->data, NULL);
14053 : :
14054 [ + - ]: 38 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14055 : 38 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14056 : 38 : ARCHIVE_OPTS(.tag = labelq->data,
14057 : : .description = "TRANSFORM",
14058 : : .section = SECTION_PRE_DATA,
14059 : : .createStmt = defqry->data,
14060 : : .dropStmt = delqry->data,
14061 : : .deps = transform->dobj.dependencies,
14062 : : .nDeps = transform->dobj.nDeps));
14063 : :
14064 : : /* Dump Transform Comments */
14065 [ - + ]: 38 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14066 : 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14067 : : NULL, "",
14068 : 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14069 : :
14070 : 38 : free(lanname);
14071 : 38 : destroyPQExpBuffer(defqry);
14072 : 38 : destroyPQExpBuffer(delqry);
14073 : 38 : destroyPQExpBuffer(labelq);
14074 : 38 : destroyPQExpBuffer(transformargs);
14075 : : }
14076 : :
14077 : :
14078 : : /*
14079 : : * dumpOpr
14080 : : * write out a single operator definition
14081 : : */
14082 : : static void
14083 : 2525 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14084 : : {
14085 : 2525 : DumpOptions *dopt = fout->dopt;
14086 : : PQExpBuffer query;
14087 : : PQExpBuffer q;
14088 : : PQExpBuffer delq;
14089 : : PQExpBuffer oprid;
14090 : : PQExpBuffer details;
14091 : : PGresult *res;
14092 : : int i_oprkind;
14093 : : int i_oprcode;
14094 : : int i_oprleft;
14095 : : int i_oprright;
14096 : : int i_oprcom;
14097 : : int i_oprnegate;
14098 : : int i_oprrest;
14099 : : int i_oprjoin;
14100 : : int i_oprcanmerge;
14101 : : int i_oprcanhash;
14102 : : char *oprkind;
14103 : : char *oprcode;
14104 : : char *oprleft;
14105 : : char *oprright;
14106 : : char *oprcom;
14107 : : char *oprnegate;
14108 : : char *oprrest;
14109 : : char *oprjoin;
14110 : : char *oprcanmerge;
14111 : : char *oprcanhash;
14112 : : char *oprregproc;
14113 : : char *oprref;
14114 : :
14115 : : /* Do nothing if not dumping schema */
14116 [ + + ]: 2525 : if (!dopt->dumpSchema)
14117 : 7 : return;
14118 : :
14119 : : /*
14120 : : * some operators are invalid because they were the result of user
14121 : : * defining operators before commutators exist
14122 : : */
14123 [ + + ]: 2518 : if (!OidIsValid(oprinfo->oprcode))
14124 : 14 : return;
14125 : :
14126 : 2504 : query = createPQExpBuffer();
14127 : 2504 : q = createPQExpBuffer();
14128 : 2504 : delq = createPQExpBuffer();
14129 : 2504 : oprid = createPQExpBuffer();
14130 : 2504 : details = createPQExpBuffer();
14131 : :
14132 [ + + ]: 2504 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14133 : : {
14134 : : /* Set up query for operator-specific details */
14135 : 42 : appendPQExpBufferStr(query,
14136 : : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14137 : : "SELECT oprkind, "
14138 : : "oprcode::pg_catalog.regprocedure, "
14139 : : "oprleft::pg_catalog.regtype, "
14140 : : "oprright::pg_catalog.regtype, "
14141 : : "oprcom, "
14142 : : "oprnegate, "
14143 : : "oprrest::pg_catalog.regprocedure, "
14144 : : "oprjoin::pg_catalog.regprocedure, "
14145 : : "oprcanmerge, oprcanhash "
14146 : : "FROM pg_catalog.pg_operator "
14147 : : "WHERE oid = $1");
14148 : :
14149 : 42 : ExecuteSqlStatement(fout, query->data);
14150 : :
14151 : 42 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14152 : : }
14153 : :
14154 : 2504 : printfPQExpBuffer(query,
14155 : : "EXECUTE dumpOpr('%u')",
14156 : 2504 : oprinfo->dobj.catId.oid);
14157 : :
14158 : 2504 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14159 : :
14160 : 2504 : i_oprkind = PQfnumber(res, "oprkind");
14161 : 2504 : i_oprcode = PQfnumber(res, "oprcode");
14162 : 2504 : i_oprleft = PQfnumber(res, "oprleft");
14163 : 2504 : i_oprright = PQfnumber(res, "oprright");
14164 : 2504 : i_oprcom = PQfnumber(res, "oprcom");
14165 : 2504 : i_oprnegate = PQfnumber(res, "oprnegate");
14166 : 2504 : i_oprrest = PQfnumber(res, "oprrest");
14167 : 2504 : i_oprjoin = PQfnumber(res, "oprjoin");
14168 : 2504 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14169 : 2504 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14170 : :
14171 : 2504 : oprkind = PQgetvalue(res, 0, i_oprkind);
14172 : 2504 : oprcode = PQgetvalue(res, 0, i_oprcode);
14173 : 2504 : oprleft = PQgetvalue(res, 0, i_oprleft);
14174 : 2504 : oprright = PQgetvalue(res, 0, i_oprright);
14175 : 2504 : oprcom = PQgetvalue(res, 0, i_oprcom);
14176 : 2504 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14177 : 2504 : oprrest = PQgetvalue(res, 0, i_oprrest);
14178 : 2504 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14179 : 2504 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14180 : 2504 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14181 : :
14182 : : /* In PG14 upwards postfix operator support does not exist anymore. */
14183 [ - + ]: 2504 : if (strcmp(oprkind, "r") == 0)
14184 : 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14185 : : oprcode);
14186 : :
14187 : 2504 : oprregproc = convertRegProcReference(oprcode);
14188 [ + - ]: 2504 : if (oprregproc)
14189 : : {
14190 : 2504 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14191 : 2504 : free(oprregproc);
14192 : : }
14193 : :
14194 : 2504 : appendPQExpBuffer(oprid, "%s (",
14195 : 2504 : oprinfo->dobj.name);
14196 : :
14197 : : /*
14198 : : * right unary means there's a left arg and left unary means there's a
14199 : : * right arg. (Although the "r" case is dead code for PG14 and later,
14200 : : * continue to support it in case we're dumping from an old server.)
14201 : : */
14202 [ + - ]: 2504 : if (strcmp(oprkind, "r") == 0 ||
14203 [ + + ]: 2504 : strcmp(oprkind, "b") == 0)
14204 : : {
14205 : 2361 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14206 : 2361 : appendPQExpBufferStr(oprid, oprleft);
14207 : : }
14208 : : else
14209 : 143 : appendPQExpBufferStr(oprid, "NONE");
14210 : :
14211 [ + + ]: 2504 : if (strcmp(oprkind, "l") == 0 ||
14212 [ + - ]: 2361 : strcmp(oprkind, "b") == 0)
14213 : : {
14214 : 2504 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14215 : 2504 : appendPQExpBuffer(oprid, ", %s)", oprright);
14216 : : }
14217 : : else
14218 : 0 : appendPQExpBufferStr(oprid, ", NONE)");
14219 : :
14220 : 2504 : oprref = getFormattedOperatorName(oprcom);
14221 [ + + ]: 2504 : if (oprref)
14222 : : {
14223 : 1679 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14224 : 1679 : free(oprref);
14225 : : }
14226 : :
14227 : 2504 : oprref = getFormattedOperatorName(oprnegate);
14228 [ + + ]: 2504 : if (oprref)
14229 : : {
14230 : 1181 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14231 : 1181 : free(oprref);
14232 : : }
14233 : :
14234 [ + + ]: 2504 : if (strcmp(oprcanmerge, "t") == 0)
14235 : 188 : appendPQExpBufferStr(details, ",\n MERGES");
14236 : :
14237 [ + + ]: 2504 : if (strcmp(oprcanhash, "t") == 0)
14238 : 141 : appendPQExpBufferStr(details, ",\n HASHES");
14239 : :
14240 : 2504 : oprregproc = convertRegProcReference(oprrest);
14241 [ + + ]: 2504 : if (oprregproc)
14242 : : {
14243 : 1532 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14244 : 1532 : free(oprregproc);
14245 : : }
14246 : :
14247 : 2504 : oprregproc = convertRegProcReference(oprjoin);
14248 [ + + ]: 2504 : if (oprregproc)
14249 : : {
14250 : 1532 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14251 : 1532 : free(oprregproc);
14252 : : }
14253 : :
14254 : 2504 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14255 : 2504 : fmtId(oprinfo->dobj.namespace->dobj.name),
14256 : : oprid->data);
14257 : :
14258 : 2504 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14259 : 2504 : fmtId(oprinfo->dobj.namespace->dobj.name),
14260 : 2504 : oprinfo->dobj.name, details->data);
14261 : :
14262 [ + + ]: 2504 : if (dopt->binary_upgrade)
14263 : 12 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14264 : 12 : "OPERATOR", oprid->data,
14265 : 12 : oprinfo->dobj.namespace->dobj.name);
14266 : :
14267 [ + - ]: 2504 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14268 : 2504 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14269 : 2504 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14270 : : .namespace = oprinfo->dobj.namespace->dobj.name,
14271 : : .owner = oprinfo->rolname,
14272 : : .description = "OPERATOR",
14273 : : .section = SECTION_PRE_DATA,
14274 : : .createStmt = q->data,
14275 : : .dropStmt = delq->data));
14276 : :
14277 : : /* Dump Operator Comments */
14278 [ + + ]: 2504 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14279 : 2415 : dumpComment(fout, "OPERATOR", oprid->data,
14280 : 2415 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14281 : 2415 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14282 : :
14283 : 2504 : PQclear(res);
14284 : :
14285 : 2504 : destroyPQExpBuffer(query);
14286 : 2504 : destroyPQExpBuffer(q);
14287 : 2504 : destroyPQExpBuffer(delq);
14288 : 2504 : destroyPQExpBuffer(oprid);
14289 : 2504 : destroyPQExpBuffer(details);
14290 : : }
14291 : :
14292 : : /*
14293 : : * Convert a function reference obtained from pg_operator
14294 : : *
14295 : : * Returns allocated string of what to print, or NULL if function references
14296 : : * is InvalidOid. Returned string is expected to be free'd by the caller.
14297 : : *
14298 : : * The input is a REGPROCEDURE display; we have to strip the argument-types
14299 : : * part.
14300 : : */
14301 : : static char *
14302 : 7512 : convertRegProcReference(const char *proc)
14303 : : {
14304 : : char *name;
14305 : : char *paren;
14306 : : bool inquote;
14307 : :
14308 : : /* In all cases "-" means a null reference */
14309 [ + + ]: 7512 : if (strcmp(proc, "-") == 0)
14310 : 1944 : return NULL;
14311 : :
14312 : 5568 : name = pg_strdup(proc);
14313 : : /* find non-double-quoted left paren */
14314 : 5568 : inquote = false;
14315 [ + - ]: 67010 : for (paren = name; *paren; paren++)
14316 : : {
14317 [ + + + - ]: 67010 : if (*paren == '(' && !inquote)
14318 : : {
14319 : 5568 : *paren = '\0';
14320 : 5568 : break;
14321 : : }
14322 [ + + ]: 61442 : if (*paren == '"')
14323 : 50 : inquote = !inquote;
14324 : : }
14325 : 5568 : return name;
14326 : : }
14327 : :
14328 : : /*
14329 : : * getFormattedOperatorName - retrieve the operator name for the
14330 : : * given operator OID (presented in string form).
14331 : : *
14332 : : * Returns an allocated string, or NULL if the given OID is invalid.
14333 : : * Caller is responsible for free'ing result string.
14334 : : *
14335 : : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14336 : : * useful in commands where the operator's argument types can be inferred from
14337 : : * context. We always schema-qualify the name, though. The predecessor to
14338 : : * this code tried to skip the schema qualification if possible, but that led
14339 : : * to wrong results in corner cases, such as if an operator and its negator
14340 : : * are in different schemas.
14341 : : */
14342 : : static char *
14343 : 5295 : getFormattedOperatorName(const char *oproid)
14344 : : {
14345 : : OprInfo *oprInfo;
14346 : :
14347 : : /* In all cases "0" means a null reference */
14348 [ + + ]: 5295 : if (strcmp(oproid, "0") == 0)
14349 : 2435 : return NULL;
14350 : :
14351 : 2860 : oprInfo = findOprByOid(atooid(oproid));
14352 [ - + ]: 2860 : if (oprInfo == NULL)
14353 : : {
14354 : 0 : pg_log_warning("could not find operator with OID %s",
14355 : : oproid);
14356 : 0 : return NULL;
14357 : : }
14358 : :
14359 : 2860 : return psprintf("OPERATOR(%s.%s)",
14360 : 2860 : fmtId(oprInfo->dobj.namespace->dobj.name),
14361 : : oprInfo->dobj.name);
14362 : : }
14363 : :
14364 : : /*
14365 : : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14366 : : *
14367 : : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14368 : : * argument lists of these functions are predetermined. Note that the
14369 : : * caller should ensure we are in the proper schema, because the results
14370 : : * are search path dependent!
14371 : : */
14372 : : static char *
14373 : 215 : convertTSFunction(Archive *fout, Oid funcOid)
14374 : : {
14375 : : char *result;
14376 : : char query[128];
14377 : : PGresult *res;
14378 : :
14379 : 215 : snprintf(query, sizeof(query),
14380 : : "SELECT '%u'::pg_catalog.regproc", funcOid);
14381 : 215 : res = ExecuteSqlQueryForSingleRow(fout, query);
14382 : :
14383 : 215 : result = pg_strdup(PQgetvalue(res, 0, 0));
14384 : :
14385 : 215 : PQclear(res);
14386 : :
14387 : 215 : return result;
14388 : : }
14389 : :
14390 : : /*
14391 : : * dumpAccessMethod
14392 : : * write out a single access method definition
14393 : : */
14394 : : static void
14395 : 84 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14396 : : {
14397 : 84 : DumpOptions *dopt = fout->dopt;
14398 : : PQExpBuffer q;
14399 : : PQExpBuffer delq;
14400 : : char *qamname;
14401 : :
14402 : : /* Do nothing if not dumping schema */
14403 [ + + ]: 84 : if (!dopt->dumpSchema)
14404 : 12 : return;
14405 : :
14406 : 72 : q = createPQExpBuffer();
14407 : 72 : delq = createPQExpBuffer();
14408 : :
14409 : 72 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14410 : :
14411 : 72 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14412 : :
14413 [ + + - ]: 72 : switch (aminfo->amtype)
14414 : : {
14415 : 34 : case AMTYPE_INDEX:
14416 : 34 : appendPQExpBufferStr(q, "TYPE INDEX ");
14417 : 34 : break;
14418 : 38 : case AMTYPE_TABLE:
14419 : 38 : appendPQExpBufferStr(q, "TYPE TABLE ");
14420 : 38 : break;
14421 : 0 : default:
14422 : 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14423 : : aminfo->amtype, qamname);
14424 : 0 : destroyPQExpBuffer(q);
14425 : 0 : destroyPQExpBuffer(delq);
14426 : 0 : pg_free(qamname);
14427 : 0 : return;
14428 : : }
14429 : :
14430 : 72 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14431 : :
14432 : 72 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14433 : : qamname);
14434 : :
14435 [ + + ]: 72 : if (dopt->binary_upgrade)
14436 : 4 : binary_upgrade_extension_member(q, &aminfo->dobj,
14437 : : "ACCESS METHOD", qamname, NULL);
14438 : :
14439 [ + - ]: 72 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14440 : 72 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14441 : 72 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14442 : : .description = "ACCESS METHOD",
14443 : : .section = SECTION_PRE_DATA,
14444 : : .createStmt = q->data,
14445 : : .dropStmt = delq->data));
14446 : :
14447 : : /* Dump Access Method Comments */
14448 [ - + ]: 72 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14449 : 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14450 : : NULL, "",
14451 : 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14452 : :
14453 : 72 : destroyPQExpBuffer(q);
14454 : 72 : destroyPQExpBuffer(delq);
14455 : 72 : pg_free(qamname);
14456 : : }
14457 : :
14458 : : /*
14459 : : * dumpOpclass
14460 : : * write out a single operator class definition
14461 : : */
14462 : : static void
14463 : 675 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14464 : : {
14465 : 675 : DumpOptions *dopt = fout->dopt;
14466 : : PQExpBuffer query;
14467 : : PQExpBuffer q;
14468 : : PQExpBuffer delq;
14469 : : PQExpBuffer nameusing;
14470 : : PGresult *res;
14471 : : int ntups;
14472 : : int i_opcintype;
14473 : : int i_opckeytype;
14474 : : int i_opcdefault;
14475 : : int i_opcfamily;
14476 : : int i_opcfamilyname;
14477 : : int i_opcfamilynsp;
14478 : : int i_amname;
14479 : : int i_amopstrategy;
14480 : : int i_amopopr;
14481 : : int i_sortfamily;
14482 : : int i_sortfamilynsp;
14483 : : int i_amprocnum;
14484 : : int i_amproc;
14485 : : int i_amproclefttype;
14486 : : int i_amprocrighttype;
14487 : : char *opcintype;
14488 : : char *opckeytype;
14489 : : char *opcdefault;
14490 : : char *opcfamily;
14491 : : char *opcfamilyname;
14492 : : char *opcfamilynsp;
14493 : : char *amname;
14494 : : char *amopstrategy;
14495 : : char *amopopr;
14496 : : char *sortfamily;
14497 : : char *sortfamilynsp;
14498 : : char *amprocnum;
14499 : : char *amproc;
14500 : : char *amproclefttype;
14501 : : char *amprocrighttype;
14502 : : bool needComma;
14503 : : int i;
14504 : :
14505 : : /* Do nothing if not dumping schema */
14506 [ + + ]: 675 : if (!dopt->dumpSchema)
14507 : 21 : return;
14508 : :
14509 : 654 : query = createPQExpBuffer();
14510 : 654 : q = createPQExpBuffer();
14511 : 654 : delq = createPQExpBuffer();
14512 : 654 : nameusing = createPQExpBuffer();
14513 : :
14514 : : /* Get additional fields from the pg_opclass row */
14515 : 654 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14516 : : "opckeytype::pg_catalog.regtype, "
14517 : : "opcdefault, opcfamily, "
14518 : : "opfname AS opcfamilyname, "
14519 : : "nspname AS opcfamilynsp, "
14520 : : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14521 : : "FROM pg_catalog.pg_opclass c "
14522 : : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14523 : : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14524 : : "WHERE c.oid = '%u'::pg_catalog.oid",
14525 : 654 : opcinfo->dobj.catId.oid);
14526 : :
14527 : 654 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14528 : :
14529 : 654 : i_opcintype = PQfnumber(res, "opcintype");
14530 : 654 : i_opckeytype = PQfnumber(res, "opckeytype");
14531 : 654 : i_opcdefault = PQfnumber(res, "opcdefault");
14532 : 654 : i_opcfamily = PQfnumber(res, "opcfamily");
14533 : 654 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14534 : 654 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14535 : 654 : i_amname = PQfnumber(res, "amname");
14536 : :
14537 : : /* opcintype may still be needed after we PQclear res */
14538 : 654 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14539 : 654 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14540 : 654 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14541 : : /* opcfamily will still be needed after we PQclear res */
14542 : 654 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14543 : 654 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14544 : 654 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14545 : : /* amname will still be needed after we PQclear res */
14546 : 654 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14547 : :
14548 : 654 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14549 : 654 : fmtQualifiedDumpable(opcinfo));
14550 : 654 : appendPQExpBuffer(delq, " USING %s;\n",
14551 : : fmtId(amname));
14552 : :
14553 : : /* Build the fixed portion of the CREATE command */
14554 : 654 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14555 : 654 : fmtQualifiedDumpable(opcinfo));
14556 [ + + ]: 654 : if (strcmp(opcdefault, "t") == 0)
14557 : 366 : appendPQExpBufferStr(q, "DEFAULT ");
14558 : 654 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14559 : : opcintype,
14560 : : fmtId(amname));
14561 [ + - ]: 654 : if (strlen(opcfamilyname) > 0)
14562 : : {
14563 : 654 : appendPQExpBufferStr(q, " FAMILY ");
14564 : 654 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14565 : 654 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14566 : : }
14567 : 654 : appendPQExpBufferStr(q, " AS\n ");
14568 : :
14569 : 654 : needComma = false;
14570 : :
14571 [ + + ]: 654 : if (strcmp(opckeytype, "-") != 0)
14572 : : {
14573 : 252 : appendPQExpBuffer(q, "STORAGE %s",
14574 : : opckeytype);
14575 : 252 : needComma = true;
14576 : : }
14577 : :
14578 : 654 : PQclear(res);
14579 : :
14580 : : /*
14581 : : * Now fetch and print the OPERATOR entries (pg_amop rows).
14582 : : *
14583 : : * Print only those opfamily members that are tied to the opclass by
14584 : : * pg_depend entries.
14585 : : */
14586 : 654 : resetPQExpBuffer(query);
14587 : 654 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14588 : : "amopopr::pg_catalog.regoperator, "
14589 : : "opfname AS sortfamily, "
14590 : : "nspname AS sortfamilynsp "
14591 : : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14592 : : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14593 : : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14594 : : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14595 : : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14596 : : "AND refobjid = '%u'::pg_catalog.oid "
14597 : : "AND amopfamily = '%s'::pg_catalog.oid "
14598 : : "ORDER BY amopstrategy",
14599 : 654 : opcinfo->dobj.catId.oid,
14600 : : opcfamily);
14601 : :
14602 : 654 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14603 : :
14604 : 654 : ntups = PQntuples(res);
14605 : :
14606 : 654 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14607 : 654 : i_amopopr = PQfnumber(res, "amopopr");
14608 : 654 : i_sortfamily = PQfnumber(res, "sortfamily");
14609 : 654 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14610 : :
14611 [ + + ]: 868 : for (i = 0; i < ntups; i++)
14612 : : {
14613 : 214 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14614 : 214 : amopopr = PQgetvalue(res, i, i_amopopr);
14615 : 214 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14616 : 214 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14617 : :
14618 [ + + ]: 214 : if (needComma)
14619 : 136 : appendPQExpBufferStr(q, " ,\n ");
14620 : :
14621 : 214 : appendPQExpBuffer(q, "OPERATOR %s %s",
14622 : : amopstrategy, amopopr);
14623 : :
14624 [ - + ]: 214 : if (strlen(sortfamily) > 0)
14625 : : {
14626 : 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14627 : 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14628 : 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14629 : : }
14630 : :
14631 : 214 : needComma = true;
14632 : : }
14633 : :
14634 : 654 : PQclear(res);
14635 : :
14636 : : /*
14637 : : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14638 : : *
14639 : : * Print only those opfamily members that are tied to the opclass by
14640 : : * pg_depend entries.
14641 : : *
14642 : : * We print the amproclefttype/amprocrighttype even though in most cases
14643 : : * the backend could deduce the right values, because of the corner case
14644 : : * of a btree sort support function for a cross-type comparison.
14645 : : */
14646 : 654 : resetPQExpBuffer(query);
14647 : :
14648 : 654 : appendPQExpBuffer(query, "SELECT amprocnum, "
14649 : : "amproc::pg_catalog.regprocedure, "
14650 : : "amproclefttype::pg_catalog.regtype, "
14651 : : "amprocrighttype::pg_catalog.regtype "
14652 : : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14653 : : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14654 : : "AND refobjid = '%u'::pg_catalog.oid "
14655 : : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14656 : : "AND objid = ap.oid "
14657 : : "ORDER BY amprocnum",
14658 : 654 : opcinfo->dobj.catId.oid);
14659 : :
14660 : 654 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14661 : :
14662 : 654 : ntups = PQntuples(res);
14663 : :
14664 : 654 : i_amprocnum = PQfnumber(res, "amprocnum");
14665 : 654 : i_amproc = PQfnumber(res, "amproc");
14666 : 654 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14667 : 654 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14668 : :
14669 [ + + ]: 688 : for (i = 0; i < ntups; i++)
14670 : : {
14671 : 34 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14672 : 34 : amproc = PQgetvalue(res, i, i_amproc);
14673 : 34 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14674 : 34 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14675 : :
14676 [ + - ]: 34 : if (needComma)
14677 : 34 : appendPQExpBufferStr(q, " ,\n ");
14678 : :
14679 : 34 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14680 : :
14681 [ + - + - ]: 34 : if (*amproclefttype && *amprocrighttype)
14682 : 34 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14683 : :
14684 : 34 : appendPQExpBuffer(q, " %s", amproc);
14685 : :
14686 : 34 : needComma = true;
14687 : : }
14688 : :
14689 : 654 : PQclear(res);
14690 : :
14691 : : /*
14692 : : * If needComma is still false it means we haven't added anything after
14693 : : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14694 : : * clause with the same datatype. This isn't sanctioned by the
14695 : : * documentation, but actually DefineOpClass will treat it as a no-op.
14696 : : */
14697 [ + + ]: 654 : if (!needComma)
14698 : 324 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14699 : :
14700 : 654 : appendPQExpBufferStr(q, ";\n");
14701 : :
14702 : 654 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14703 : 654 : appendPQExpBuffer(nameusing, " USING %s",
14704 : : fmtId(amname));
14705 : :
14706 [ + + ]: 654 : if (dopt->binary_upgrade)
14707 : 6 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14708 : 6 : "OPERATOR CLASS", nameusing->data,
14709 : 6 : opcinfo->dobj.namespace->dobj.name);
14710 : :
14711 [ + - ]: 654 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14712 : 654 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14713 : 654 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14714 : : .namespace = opcinfo->dobj.namespace->dobj.name,
14715 : : .owner = opcinfo->rolname,
14716 : : .description = "OPERATOR CLASS",
14717 : : .section = SECTION_PRE_DATA,
14718 : : .createStmt = q->data,
14719 : : .dropStmt = delq->data));
14720 : :
14721 : : /* Dump Operator Class Comments */
14722 [ - + ]: 654 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14723 : 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14724 : 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14725 : 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14726 : :
14727 : 654 : pg_free(opcintype);
14728 : 654 : pg_free(opcfamily);
14729 : 654 : pg_free(amname);
14730 : 654 : destroyPQExpBuffer(query);
14731 : 654 : destroyPQExpBuffer(q);
14732 : 654 : destroyPQExpBuffer(delq);
14733 : 654 : destroyPQExpBuffer(nameusing);
14734 : : }
14735 : :
14736 : : /*
14737 : : * dumpOpfamily
14738 : : * write out a single operator family definition
14739 : : *
14740 : : * Note: this also dumps any "loose" operator members that aren't bound to a
14741 : : * specific opclass within the opfamily.
14742 : : */
14743 : : static void
14744 : 561 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14745 : : {
14746 : 561 : DumpOptions *dopt = fout->dopt;
14747 : : PQExpBuffer query;
14748 : : PQExpBuffer q;
14749 : : PQExpBuffer delq;
14750 : : PQExpBuffer nameusing;
14751 : : PGresult *res;
14752 : : PGresult *res_ops;
14753 : : PGresult *res_procs;
14754 : : int ntups;
14755 : : int i_amname;
14756 : : int i_amopstrategy;
14757 : : int i_amopopr;
14758 : : int i_sortfamily;
14759 : : int i_sortfamilynsp;
14760 : : int i_amprocnum;
14761 : : int i_amproc;
14762 : : int i_amproclefttype;
14763 : : int i_amprocrighttype;
14764 : : char *amname;
14765 : : char *amopstrategy;
14766 : : char *amopopr;
14767 : : char *sortfamily;
14768 : : char *sortfamilynsp;
14769 : : char *amprocnum;
14770 : : char *amproc;
14771 : : char *amproclefttype;
14772 : : char *amprocrighttype;
14773 : : bool needComma;
14774 : : int i;
14775 : :
14776 : : /* Do nothing if not dumping schema */
14777 [ + + ]: 561 : if (!dopt->dumpSchema)
14778 : 14 : return;
14779 : :
14780 : 547 : query = createPQExpBuffer();
14781 : 547 : q = createPQExpBuffer();
14782 : 547 : delq = createPQExpBuffer();
14783 : 547 : nameusing = createPQExpBuffer();
14784 : :
14785 : : /*
14786 : : * Fetch only those opfamily members that are tied directly to the
14787 : : * opfamily by pg_depend entries.
14788 : : */
14789 : 547 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14790 : : "amopopr::pg_catalog.regoperator, "
14791 : : "opfname AS sortfamily, "
14792 : : "nspname AS sortfamilynsp "
14793 : : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14794 : : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14795 : : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14796 : : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14797 : : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14798 : : "AND refobjid = '%u'::pg_catalog.oid "
14799 : : "AND amopfamily = '%u'::pg_catalog.oid "
14800 : : "ORDER BY amopstrategy",
14801 : 547 : opfinfo->dobj.catId.oid,
14802 : 547 : opfinfo->dobj.catId.oid);
14803 : :
14804 : 547 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14805 : :
14806 : 547 : resetPQExpBuffer(query);
14807 : :
14808 : 547 : appendPQExpBuffer(query, "SELECT amprocnum, "
14809 : : "amproc::pg_catalog.regprocedure, "
14810 : : "amproclefttype::pg_catalog.regtype, "
14811 : : "amprocrighttype::pg_catalog.regtype "
14812 : : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14813 : : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14814 : : "AND refobjid = '%u'::pg_catalog.oid "
14815 : : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14816 : : "AND objid = ap.oid "
14817 : : "ORDER BY amprocnum",
14818 : 547 : opfinfo->dobj.catId.oid);
14819 : :
14820 : 547 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14821 : :
14822 : : /* Get additional fields from the pg_opfamily row */
14823 : 547 : resetPQExpBuffer(query);
14824 : :
14825 : 547 : appendPQExpBuffer(query, "SELECT "
14826 : : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14827 : : "FROM pg_catalog.pg_opfamily "
14828 : : "WHERE oid = '%u'::pg_catalog.oid",
14829 : 547 : opfinfo->dobj.catId.oid);
14830 : :
14831 : 547 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14832 : :
14833 : 547 : i_amname = PQfnumber(res, "amname");
14834 : :
14835 : : /* amname will still be needed after we PQclear res */
14836 : 547 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14837 : :
14838 : 547 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14839 : 547 : fmtQualifiedDumpable(opfinfo));
14840 : 547 : appendPQExpBuffer(delq, " USING %s;\n",
14841 : : fmtId(amname));
14842 : :
14843 : : /* Build the fixed portion of the CREATE command */
14844 : 547 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14845 : 547 : fmtQualifiedDumpable(opfinfo));
14846 : 547 : appendPQExpBuffer(q, " USING %s;\n",
14847 : : fmtId(amname));
14848 : :
14849 : 547 : PQclear(res);
14850 : :
14851 : : /* Do we need an ALTER to add loose members? */
14852 [ + + + + ]: 547 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14853 : : {
14854 : 49 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14855 : 49 : fmtQualifiedDumpable(opfinfo));
14856 : 49 : appendPQExpBuffer(q, " USING %s ADD\n ",
14857 : : fmtId(amname));
14858 : :
14859 : 49 : needComma = false;
14860 : :
14861 : : /*
14862 : : * Now fetch and print the OPERATOR entries (pg_amop rows).
14863 : : */
14864 : 49 : ntups = PQntuples(res_ops);
14865 : :
14866 : 49 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14867 : 49 : i_amopopr = PQfnumber(res_ops, "amopopr");
14868 : 49 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14869 : 49 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14870 : :
14871 [ + + ]: 219 : for (i = 0; i < ntups; i++)
14872 : : {
14873 : 170 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14874 : 170 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14875 : 170 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14876 : 170 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14877 : :
14878 [ + + ]: 170 : if (needComma)
14879 : 136 : appendPQExpBufferStr(q, " ,\n ");
14880 : :
14881 : 170 : appendPQExpBuffer(q, "OPERATOR %s %s",
14882 : : amopstrategy, amopopr);
14883 : :
14884 [ - + ]: 170 : if (strlen(sortfamily) > 0)
14885 : : {
14886 : 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14887 : 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14888 : 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14889 : : }
14890 : :
14891 : 170 : needComma = true;
14892 : : }
14893 : :
14894 : : /*
14895 : : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14896 : : */
14897 : 49 : ntups = PQntuples(res_procs);
14898 : :
14899 : 49 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14900 : 49 : i_amproc = PQfnumber(res_procs, "amproc");
14901 : 49 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14902 : 49 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14903 : :
14904 [ + + ]: 234 : for (i = 0; i < ntups; i++)
14905 : : {
14906 : 185 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14907 : 185 : amproc = PQgetvalue(res_procs, i, i_amproc);
14908 : 185 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14909 : 185 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14910 : :
14911 [ + + ]: 185 : if (needComma)
14912 : 170 : appendPQExpBufferStr(q, " ,\n ");
14913 : :
14914 : 185 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14915 : : amprocnum, amproclefttype, amprocrighttype,
14916 : : amproc);
14917 : :
14918 : 185 : needComma = true;
14919 : : }
14920 : :
14921 : 49 : appendPQExpBufferStr(q, ";\n");
14922 : : }
14923 : :
14924 : 547 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14925 : 547 : appendPQExpBuffer(nameusing, " USING %s",
14926 : : fmtId(amname));
14927 : :
14928 [ + + ]: 547 : if (dopt->binary_upgrade)
14929 : 9 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14930 : 9 : "OPERATOR FAMILY", nameusing->data,
14931 : 9 : opfinfo->dobj.namespace->dobj.name);
14932 : :
14933 [ + - ]: 547 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14934 : 547 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14935 : 547 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14936 : : .namespace = opfinfo->dobj.namespace->dobj.name,
14937 : : .owner = opfinfo->rolname,
14938 : : .description = "OPERATOR FAMILY",
14939 : : .section = SECTION_PRE_DATA,
14940 : : .createStmt = q->data,
14941 : : .dropStmt = delq->data));
14942 : :
14943 : : /* Dump Operator Family Comments */
14944 [ - + ]: 547 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14945 : 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14946 : 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14947 : 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14948 : :
14949 : 547 : pg_free(amname);
14950 : 547 : PQclear(res_ops);
14951 : 547 : PQclear(res_procs);
14952 : 547 : destroyPQExpBuffer(query);
14953 : 547 : destroyPQExpBuffer(q);
14954 : 547 : destroyPQExpBuffer(delq);
14955 : 547 : destroyPQExpBuffer(nameusing);
14956 : : }
14957 : :
14958 : : /*
14959 : : * dumpCollation
14960 : : * write out a single collation definition
14961 : : */
14962 : : static void
14963 : 2733 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14964 : : {
14965 : 2733 : DumpOptions *dopt = fout->dopt;
14966 : : PQExpBuffer query;
14967 : : PQExpBuffer q;
14968 : : PQExpBuffer delq;
14969 : : char *qcollname;
14970 : : PGresult *res;
14971 : : int i_collprovider;
14972 : : int i_collisdeterministic;
14973 : : int i_collcollate;
14974 : : int i_collctype;
14975 : : int i_colllocale;
14976 : : int i_collicurules;
14977 : : const char *collprovider;
14978 : : const char *collcollate;
14979 : : const char *collctype;
14980 : : const char *colllocale;
14981 : : const char *collicurules;
14982 : :
14983 : : /* Do nothing if not dumping schema */
14984 [ + + ]: 2733 : if (!dopt->dumpSchema)
14985 : 12 : return;
14986 : :
14987 : 2721 : query = createPQExpBuffer();
14988 : 2721 : q = createPQExpBuffer();
14989 : 2721 : delq = createPQExpBuffer();
14990 : :
14991 : 2721 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14992 : :
14993 : : /* Get collation-specific details */
14994 : 2721 : appendPQExpBufferStr(query, "SELECT ");
14995 : :
14996 : 2721 : appendPQExpBufferStr(query,
14997 : : "collprovider, "
14998 : : "collversion, ");
14999 : :
15000 [ + - ]: 2721 : if (fout->remoteVersion >= 120000)
15001 : 2721 : appendPQExpBufferStr(query,
15002 : : "collisdeterministic, ");
15003 : : else
15004 : 0 : appendPQExpBufferStr(query,
15005 : : "true AS collisdeterministic, ");
15006 : :
15007 [ + - ]: 2721 : if (fout->remoteVersion >= 170000)
15008 : 2721 : appendPQExpBufferStr(query,
15009 : : "colllocale, ");
15010 [ # # ]: 0 : else if (fout->remoteVersion >= 150000)
15011 : 0 : appendPQExpBufferStr(query,
15012 : : "colliculocale AS colllocale, ");
15013 : : else
15014 : 0 : appendPQExpBufferStr(query,
15015 : : "NULL AS colllocale, ");
15016 : :
15017 [ + - ]: 2721 : if (fout->remoteVersion >= 160000)
15018 : 2721 : appendPQExpBufferStr(query,
15019 : : "collicurules, ");
15020 : : else
15021 : 0 : appendPQExpBufferStr(query,
15022 : : "NULL AS collicurules, ");
15023 : :
15024 : 2721 : appendPQExpBuffer(query,
15025 : : "collcollate, "
15026 : : "collctype "
15027 : : "FROM pg_catalog.pg_collation c "
15028 : : "WHERE c.oid = '%u'::pg_catalog.oid",
15029 : 2721 : collinfo->dobj.catId.oid);
15030 : :
15031 : 2721 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15032 : :
15033 : 2721 : i_collprovider = PQfnumber(res, "collprovider");
15034 : 2721 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15035 : 2721 : i_collcollate = PQfnumber(res, "collcollate");
15036 : 2721 : i_collctype = PQfnumber(res, "collctype");
15037 : 2721 : i_colllocale = PQfnumber(res, "colllocale");
15038 : 2721 : i_collicurules = PQfnumber(res, "collicurules");
15039 : :
15040 : 2721 : collprovider = PQgetvalue(res, 0, i_collprovider);
15041 : :
15042 [ + + ]: 2721 : if (!PQgetisnull(res, 0, i_collcollate))
15043 : 48 : collcollate = PQgetvalue(res, 0, i_collcollate);
15044 : : else
15045 : 2673 : collcollate = NULL;
15046 : :
15047 [ + + ]: 2721 : if (!PQgetisnull(res, 0, i_collctype))
15048 : 48 : collctype = PQgetvalue(res, 0, i_collctype);
15049 : : else
15050 : 2673 : collctype = NULL;
15051 : :
15052 : : /*
15053 : : * Before version 15, collcollate and collctype were of type NAME and
15054 : : * non-nullable. Treat empty strings as NULL for consistency.
15055 : : */
15056 [ - + ]: 2721 : if (fout->remoteVersion < 150000)
15057 : : {
15058 [ # # ]: 0 : if (collcollate[0] == '\0')
15059 : 0 : collcollate = NULL;
15060 [ # # ]: 0 : if (collctype[0] == '\0')
15061 : 0 : collctype = NULL;
15062 : : }
15063 : :
15064 [ + + ]: 2721 : if (!PQgetisnull(res, 0, i_colllocale))
15065 : 2670 : colllocale = PQgetvalue(res, 0, i_colllocale);
15066 : : else
15067 : 51 : colllocale = NULL;
15068 : :
15069 [ - + ]: 2721 : if (!PQgetisnull(res, 0, i_collicurules))
15070 : 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15071 : : else
15072 : 2721 : collicurules = NULL;
15073 : :
15074 : 2721 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15075 : 2721 : fmtQualifiedDumpable(collinfo));
15076 : :
15077 : 2721 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15078 : 2721 : fmtQualifiedDumpable(collinfo));
15079 : :
15080 : 2721 : appendPQExpBufferStr(q, "provider = ");
15081 [ + + ]: 2721 : if (collprovider[0] == 'b')
15082 : 19 : appendPQExpBufferStr(q, "builtin");
15083 [ + + ]: 2702 : else if (collprovider[0] == 'c')
15084 : 48 : appendPQExpBufferStr(q, "libc");
15085 [ + + ]: 2654 : else if (collprovider[0] == 'i')
15086 : 2651 : appendPQExpBufferStr(q, "icu");
15087 [ + - ]: 3 : else if (collprovider[0] == 'd')
15088 : : /* to allow dumping pg_catalog; not accepted on input */
15089 : 3 : appendPQExpBufferStr(q, "default");
15090 : : else
15091 : 0 : pg_fatal("unrecognized collation provider: %s",
15092 : : collprovider);
15093 : :
15094 [ - + ]: 2721 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15095 : 0 : appendPQExpBufferStr(q, ", deterministic = false");
15096 : :
15097 [ + + ]: 2721 : if (collprovider[0] == 'd')
15098 : : {
15099 [ + - + - : 3 : if (collcollate || collctype || colllocale || collicurules)
+ - - + ]
15100 : 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15101 : :
15102 : : /* no locale -- the default collation cannot be reloaded anyway */
15103 : : }
15104 [ + + ]: 2718 : else if (collprovider[0] == 'b')
15105 : : {
15106 [ + - + - : 19 : if (collcollate || collctype || !colllocale || collicurules)
+ - - + ]
15107 : 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15108 : :
15109 : 19 : appendPQExpBufferStr(q, ", locale = ");
15110 [ + - ]: 19 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15111 : : fout);
15112 : : }
15113 [ + + ]: 2699 : else if (collprovider[0] == 'i')
15114 : : {
15115 [ + - ]: 2651 : if (fout->remoteVersion >= 150000)
15116 : : {
15117 [ + - + - : 2651 : if (collcollate || collctype || !colllocale)
- + ]
15118 : 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15119 : :
15120 : 2651 : appendPQExpBufferStr(q, ", locale = ");
15121 [ + - ]: 2651 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15122 : : fout);
15123 : : }
15124 : : else
15125 : : {
15126 [ # # # # : 0 : if (!collcollate || !collctype || colllocale ||
# # ]
15127 [ # # ]: 0 : strcmp(collcollate, collctype) != 0)
15128 : 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15129 : :
15130 : 0 : appendPQExpBufferStr(q, ", locale = ");
15131 [ # # ]: 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15132 : : }
15133 : :
15134 [ - + ]: 2651 : if (collicurules)
15135 : : {
15136 : 0 : appendPQExpBufferStr(q, ", rules = ");
15137 [ # # ]: 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15138 : : }
15139 : : }
15140 [ + - ]: 48 : else if (collprovider[0] == 'c')
15141 : : {
15142 [ + - + - : 48 : if (colllocale || collicurules || !collcollate || !collctype)
+ - - + ]
15143 : 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15144 : :
15145 [ + - + - : 48 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
+ - ]
15146 : : {
15147 : 48 : appendPQExpBufferStr(q, ", locale = ");
15148 [ + - ]: 48 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15149 : : }
15150 : : else
15151 : : {
15152 : 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15153 [ # # ]: 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15154 : 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15155 [ # # ]: 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15156 : : }
15157 : : }
15158 : : else
15159 : 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15160 : :
15161 : : /*
15162 : : * For binary upgrade, carry over the collation version. For normal
15163 : : * dump/restore, omit the version, so that it is computed upon restore.
15164 : : */
15165 [ + + ]: 2721 : if (dopt->binary_upgrade)
15166 : : {
15167 : : int i_collversion;
15168 : :
15169 : 5 : i_collversion = PQfnumber(res, "collversion");
15170 [ + + ]: 5 : if (!PQgetisnull(res, 0, i_collversion))
15171 : : {
15172 : 4 : appendPQExpBufferStr(q, ", version = ");
15173 : 4 : appendStringLiteralAH(q,
15174 : : PQgetvalue(res, 0, i_collversion),
15175 : : fout);
15176 : : }
15177 : : }
15178 : :
15179 : 2721 : appendPQExpBufferStr(q, ");\n");
15180 : :
15181 [ + + ]: 2721 : if (dopt->binary_upgrade)
15182 : 5 : binary_upgrade_extension_member(q, &collinfo->dobj,
15183 : : "COLLATION", qcollname,
15184 : 5 : collinfo->dobj.namespace->dobj.name);
15185 : :
15186 [ + - ]: 2721 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15187 : 2721 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15188 : 2721 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15189 : : .namespace = collinfo->dobj.namespace->dobj.name,
15190 : : .owner = collinfo->rolname,
15191 : : .description = "COLLATION",
15192 : : .section = SECTION_PRE_DATA,
15193 : : .createStmt = q->data,
15194 : : .dropStmt = delq->data));
15195 : :
15196 : : /* Dump Collation Comments */
15197 [ + + ]: 2721 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15198 : 2613 : dumpComment(fout, "COLLATION", qcollname,
15199 : 2613 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15200 : 2613 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15201 : :
15202 : 2721 : PQclear(res);
15203 : :
15204 : 2721 : destroyPQExpBuffer(query);
15205 : 2721 : destroyPQExpBuffer(q);
15206 : 2721 : destroyPQExpBuffer(delq);
15207 : 2721 : pg_free(qcollname);
15208 : : }
15209 : :
15210 : : /*
15211 : : * dumpConversion
15212 : : * write out a single conversion definition
15213 : : */
15214 : : static void
15215 : 335 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15216 : : {
15217 : 335 : DumpOptions *dopt = fout->dopt;
15218 : : PQExpBuffer query;
15219 : : PQExpBuffer q;
15220 : : PQExpBuffer delq;
15221 : : char *qconvname;
15222 : : PGresult *res;
15223 : : int i_conforencoding;
15224 : : int i_contoencoding;
15225 : : int i_conproc;
15226 : : int i_condefault;
15227 : : const char *conforencoding;
15228 : : const char *contoencoding;
15229 : : const char *conproc;
15230 : : bool condefault;
15231 : :
15232 : : /* Do nothing if not dumping schema */
15233 [ + + ]: 335 : if (!dopt->dumpSchema)
15234 : 7 : return;
15235 : :
15236 : 328 : query = createPQExpBuffer();
15237 : 328 : q = createPQExpBuffer();
15238 : 328 : delq = createPQExpBuffer();
15239 : :
15240 : 328 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15241 : :
15242 : : /* Get conversion-specific details */
15243 : 328 : appendPQExpBuffer(query, "SELECT "
15244 : : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15245 : : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15246 : : "conproc, condefault "
15247 : : "FROM pg_catalog.pg_conversion c "
15248 : : "WHERE c.oid = '%u'::pg_catalog.oid",
15249 : 328 : convinfo->dobj.catId.oid);
15250 : :
15251 : 328 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15252 : :
15253 : 328 : i_conforencoding = PQfnumber(res, "conforencoding");
15254 : 328 : i_contoencoding = PQfnumber(res, "contoencoding");
15255 : 328 : i_conproc = PQfnumber(res, "conproc");
15256 : 328 : i_condefault = PQfnumber(res, "condefault");
15257 : :
15258 : 328 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15259 : 328 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15260 : 328 : conproc = PQgetvalue(res, 0, i_conproc);
15261 : 328 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15262 : :
15263 : 328 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15264 : 328 : fmtQualifiedDumpable(convinfo));
15265 : :
15266 [ + - ]: 328 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15267 : : (condefault) ? "DEFAULT " : "",
15268 : 328 : fmtQualifiedDumpable(convinfo));
15269 : 328 : appendStringLiteralAH(q, conforencoding, fout);
15270 : 328 : appendPQExpBufferStr(q, " TO ");
15271 : 328 : appendStringLiteralAH(q, contoencoding, fout);
15272 : : /* regproc output is already sufficiently quoted */
15273 : 328 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15274 : :
15275 [ + + ]: 328 : if (dopt->binary_upgrade)
15276 : 1 : binary_upgrade_extension_member(q, &convinfo->dobj,
15277 : : "CONVERSION", qconvname,
15278 : 1 : convinfo->dobj.namespace->dobj.name);
15279 : :
15280 [ + - ]: 328 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15281 : 328 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15282 : 328 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15283 : : .namespace = convinfo->dobj.namespace->dobj.name,
15284 : : .owner = convinfo->rolname,
15285 : : .description = "CONVERSION",
15286 : : .section = SECTION_PRE_DATA,
15287 : : .createStmt = q->data,
15288 : : .dropStmt = delq->data));
15289 : :
15290 : : /* Dump Conversion Comments */
15291 [ + - ]: 328 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15292 : 328 : dumpComment(fout, "CONVERSION", qconvname,
15293 : 328 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15294 : 328 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15295 : :
15296 : 328 : PQclear(res);
15297 : :
15298 : 328 : destroyPQExpBuffer(query);
15299 : 328 : destroyPQExpBuffer(q);
15300 : 328 : destroyPQExpBuffer(delq);
15301 : 328 : pg_free(qconvname);
15302 : : }
15303 : :
15304 : : /*
15305 : : * format_aggregate_signature: generate aggregate name and argument list
15306 : : *
15307 : : * The argument type names are qualified if needed. The aggregate name
15308 : : * is never qualified.
15309 : : */
15310 : : static char *
15311 : 287 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15312 : : {
15313 : : PQExpBufferData buf;
15314 : : int j;
15315 : :
15316 : 287 : initPQExpBuffer(&buf);
15317 [ - + ]: 287 : if (honor_quotes)
15318 : 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15319 : : else
15320 : 287 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15321 : :
15322 [ + + ]: 287 : if (agginfo->aggfn.nargs == 0)
15323 : 40 : appendPQExpBufferStr(&buf, "(*)");
15324 : : else
15325 : : {
15326 : 247 : appendPQExpBufferChar(&buf, '(');
15327 [ + + ]: 539 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15328 [ + + ]: 292 : appendPQExpBuffer(&buf, "%s%s",
15329 : : (j > 0) ? ", " : "",
15330 : : getFormattedTypeName(fout,
15331 : 292 : agginfo->aggfn.argtypes[j],
15332 : : zeroIsError));
15333 : 247 : appendPQExpBufferChar(&buf, ')');
15334 : : }
15335 : 287 : return buf.data;
15336 : : }
15337 : :
15338 : : /*
15339 : : * dumpAgg
15340 : : * write out a single aggregate definition
15341 : : */
15342 : : static void
15343 : 295 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15344 : : {
15345 : 295 : DumpOptions *dopt = fout->dopt;
15346 : : PQExpBuffer query;
15347 : : PQExpBuffer q;
15348 : : PQExpBuffer delq;
15349 : : PQExpBuffer details;
15350 : : char *aggsig; /* identity signature */
15351 : 295 : char *aggfullsig = NULL; /* full signature */
15352 : : char *aggsig_tag;
15353 : : PGresult *res;
15354 : : int i_agginitval;
15355 : : int i_aggminitval;
15356 : : const char *aggtransfn;
15357 : : const char *aggfinalfn;
15358 : : const char *aggcombinefn;
15359 : : const char *aggserialfn;
15360 : : const char *aggdeserialfn;
15361 : : const char *aggmtransfn;
15362 : : const char *aggminvtransfn;
15363 : : const char *aggmfinalfn;
15364 : : bool aggfinalextra;
15365 : : bool aggmfinalextra;
15366 : : char aggfinalmodify;
15367 : : char aggmfinalmodify;
15368 : : const char *aggsortop;
15369 : : char *aggsortconvop;
15370 : : char aggkind;
15371 : : const char *aggtranstype;
15372 : : const char *aggtransspace;
15373 : : const char *aggmtranstype;
15374 : : const char *aggmtransspace;
15375 : : const char *agginitval;
15376 : : const char *aggminitval;
15377 : : const char *proparallel;
15378 : : char defaultfinalmodify;
15379 : :
15380 : : /* Do nothing if not dumping schema */
15381 [ + + ]: 295 : if (!dopt->dumpSchema)
15382 : 8 : return;
15383 : :
15384 : 287 : query = createPQExpBuffer();
15385 : 287 : q = createPQExpBuffer();
15386 : 287 : delq = createPQExpBuffer();
15387 : 287 : details = createPQExpBuffer();
15388 : :
15389 [ + + ]: 287 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15390 : : {
15391 : : /* Set up query for aggregate-specific details */
15392 : 57 : appendPQExpBufferStr(query,
15393 : : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15394 : :
15395 : 57 : appendPQExpBufferStr(query,
15396 : : "SELECT "
15397 : : "aggtransfn,\n"
15398 : : "aggfinalfn,\n"
15399 : : "aggtranstype::pg_catalog.regtype,\n"
15400 : : "agginitval,\n"
15401 : : "aggsortop,\n"
15402 : : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15403 : : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15404 : :
15405 : 57 : appendPQExpBufferStr(query,
15406 : : "aggkind,\n"
15407 : : "aggmtransfn,\n"
15408 : : "aggminvtransfn,\n"
15409 : : "aggmfinalfn,\n"
15410 : : "aggmtranstype::pg_catalog.regtype,\n"
15411 : : "aggfinalextra,\n"
15412 : : "aggmfinalextra,\n"
15413 : : "aggtransspace,\n"
15414 : : "aggmtransspace,\n"
15415 : : "aggminitval,\n");
15416 : :
15417 : 57 : appendPQExpBufferStr(query,
15418 : : "aggcombinefn,\n"
15419 : : "aggserialfn,\n"
15420 : : "aggdeserialfn,\n"
15421 : : "proparallel,\n");
15422 : :
15423 [ + - ]: 57 : if (fout->remoteVersion >= 110000)
15424 : 57 : appendPQExpBufferStr(query,
15425 : : "aggfinalmodify,\n"
15426 : : "aggmfinalmodify\n");
15427 : : else
15428 : 0 : appendPQExpBufferStr(query,
15429 : : "'0' AS aggfinalmodify,\n"
15430 : : "'0' AS aggmfinalmodify\n");
15431 : :
15432 : 57 : appendPQExpBufferStr(query,
15433 : : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15434 : : "WHERE a.aggfnoid = p.oid "
15435 : : "AND p.oid = $1");
15436 : :
15437 : 57 : ExecuteSqlStatement(fout, query->data);
15438 : :
15439 : 57 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15440 : : }
15441 : :
15442 : 287 : printfPQExpBuffer(query,
15443 : : "EXECUTE dumpAgg('%u')",
15444 : 287 : agginfo->aggfn.dobj.catId.oid);
15445 : :
15446 : 287 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15447 : :
15448 : 287 : i_agginitval = PQfnumber(res, "agginitval");
15449 : 287 : i_aggminitval = PQfnumber(res, "aggminitval");
15450 : :
15451 : 287 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15452 : 287 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15453 : 287 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15454 : 287 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15455 : 287 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15456 : 287 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15457 : 287 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15458 : 287 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15459 : 287 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15460 : 287 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15461 : 287 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15462 : 287 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15463 : 287 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15464 : 287 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15465 : 287 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15466 : 287 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15467 : 287 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15468 : 287 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15469 : 287 : agginitval = PQgetvalue(res, 0, i_agginitval);
15470 : 287 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15471 : 287 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15472 : :
15473 : : {
15474 : : char *funcargs;
15475 : : char *funciargs;
15476 : :
15477 : 287 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15478 : 287 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15479 : 287 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15480 : 287 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15481 : : }
15482 : :
15483 : 287 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15484 : :
15485 : : /* identify default modify flag for aggkind (must match DefineAggregate) */
15486 [ + + ]: 287 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15487 : : /* replace omitted flags for old versions */
15488 [ - + ]: 287 : if (aggfinalmodify == '0')
15489 : 0 : aggfinalmodify = defaultfinalmodify;
15490 [ - + ]: 287 : if (aggmfinalmodify == '0')
15491 : 0 : aggmfinalmodify = defaultfinalmodify;
15492 : :
15493 : : /* regproc and regtype output is already sufficiently quoted */
15494 : 287 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15495 : : aggtransfn, aggtranstype);
15496 : :
15497 [ + + ]: 287 : if (strcmp(aggtransspace, "0") != 0)
15498 : : {
15499 : 5 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15500 : : aggtransspace);
15501 : : }
15502 : :
15503 [ + + ]: 287 : if (!PQgetisnull(res, 0, i_agginitval))
15504 : : {
15505 : 209 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15506 : 209 : appendStringLiteralAH(details, agginitval, fout);
15507 : : }
15508 : :
15509 [ + + ]: 287 : if (strcmp(aggfinalfn, "-") != 0)
15510 : : {
15511 : 134 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15512 : : aggfinalfn);
15513 [ + + ]: 134 : if (aggfinalextra)
15514 : 10 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15515 [ + + ]: 134 : if (aggfinalmodify != defaultfinalmodify)
15516 : : {
15517 [ - + - - ]: 34 : switch (aggfinalmodify)
15518 : : {
15519 : 0 : case AGGMODIFY_READ_ONLY:
15520 : 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15521 : 0 : break;
15522 : 34 : case AGGMODIFY_SHAREABLE:
15523 : 34 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15524 : 34 : break;
15525 : 0 : case AGGMODIFY_READ_WRITE:
15526 : 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15527 : 0 : break;
15528 : 0 : default:
15529 : 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15530 : : agginfo->aggfn.dobj.name);
15531 : : break;
15532 : : }
15533 : : }
15534 : : }
15535 : :
15536 [ - + ]: 287 : if (strcmp(aggcombinefn, "-") != 0)
15537 : 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15538 : :
15539 [ - + ]: 287 : if (strcmp(aggserialfn, "-") != 0)
15540 : 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15541 : :
15542 [ - + ]: 287 : if (strcmp(aggdeserialfn, "-") != 0)
15543 : 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15544 : :
15545 [ + + ]: 287 : if (strcmp(aggmtransfn, "-") != 0)
15546 : : {
15547 : 30 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15548 : : aggmtransfn,
15549 : : aggminvtransfn,
15550 : : aggmtranstype);
15551 : : }
15552 : :
15553 [ - + ]: 287 : if (strcmp(aggmtransspace, "0") != 0)
15554 : : {
15555 : 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15556 : : aggmtransspace);
15557 : : }
15558 : :
15559 [ + + ]: 287 : if (!PQgetisnull(res, 0, i_aggminitval))
15560 : : {
15561 : 10 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15562 : 10 : appendStringLiteralAH(details, aggminitval, fout);
15563 : : }
15564 : :
15565 [ - + ]: 287 : if (strcmp(aggmfinalfn, "-") != 0)
15566 : : {
15567 : 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15568 : : aggmfinalfn);
15569 [ # # ]: 0 : if (aggmfinalextra)
15570 : 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15571 [ # # ]: 0 : if (aggmfinalmodify != defaultfinalmodify)
15572 : : {
15573 [ # # # # ]: 0 : switch (aggmfinalmodify)
15574 : : {
15575 : 0 : case AGGMODIFY_READ_ONLY:
15576 : 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15577 : 0 : break;
15578 : 0 : case AGGMODIFY_SHAREABLE:
15579 : 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15580 : 0 : break;
15581 : 0 : case AGGMODIFY_READ_WRITE:
15582 : 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15583 : 0 : break;
15584 : 0 : default:
15585 : 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15586 : : agginfo->aggfn.dobj.name);
15587 : : break;
15588 : : }
15589 : : }
15590 : : }
15591 : :
15592 : 287 : aggsortconvop = getFormattedOperatorName(aggsortop);
15593 [ - + ]: 287 : if (aggsortconvop)
15594 : : {
15595 : 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15596 : : aggsortconvop);
15597 : 0 : free(aggsortconvop);
15598 : : }
15599 : :
15600 [ + + ]: 287 : if (aggkind == AGGKIND_HYPOTHETICAL)
15601 : 5 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15602 : :
15603 [ + + ]: 287 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15604 : : {
15605 [ + - ]: 5 : if (proparallel[0] == PROPARALLEL_SAFE)
15606 : 5 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15607 [ # # ]: 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15608 : 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15609 [ # # ]: 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15610 : 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15611 : : agginfo->aggfn.dobj.name);
15612 : : }
15613 : :
15614 : 287 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15615 : 287 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15616 : : aggsig);
15617 : :
15618 [ + - ]: 574 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15619 : 287 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15620 : : aggfullsig ? aggfullsig : aggsig, details->data);
15621 : :
15622 [ + + ]: 287 : if (dopt->binary_upgrade)
15623 : 49 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15624 : : "AGGREGATE", aggsig,
15625 : 49 : agginfo->aggfn.dobj.namespace->dobj.name);
15626 : :
15627 [ + + ]: 287 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15628 : 270 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15629 : 270 : agginfo->aggfn.dobj.dumpId,
15630 : 270 : ARCHIVE_OPTS(.tag = aggsig_tag,
15631 : : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15632 : : .owner = agginfo->aggfn.rolname,
15633 : : .description = "AGGREGATE",
15634 : : .section = SECTION_PRE_DATA,
15635 : : .createStmt = q->data,
15636 : : .dropStmt = delq->data));
15637 : :
15638 : : /* Dump Aggregate Comments */
15639 [ + + ]: 287 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15640 : 10 : dumpComment(fout, "AGGREGATE", aggsig,
15641 : 10 : agginfo->aggfn.dobj.namespace->dobj.name,
15642 : 10 : agginfo->aggfn.rolname,
15643 : 10 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15644 : :
15645 [ - + ]: 287 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15646 : 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15647 : 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15648 : 0 : agginfo->aggfn.rolname,
15649 : 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15650 : :
15651 : : /*
15652 : : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15653 : : * command look like a function's GRANT; in particular this affects the
15654 : : * syntax for zero-argument aggregates and ordered-set aggregates.
15655 : : */
15656 : 287 : free(aggsig);
15657 : :
15658 : 287 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15659 : :
15660 [ + + ]: 287 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15661 : 18 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15662 : : "FUNCTION", aggsig, NULL,
15663 : 18 : agginfo->aggfn.dobj.namespace->dobj.name,
15664 : 18 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15665 : :
15666 : 287 : free(aggsig);
15667 : 287 : free(aggfullsig);
15668 : 287 : free(aggsig_tag);
15669 : :
15670 : 287 : PQclear(res);
15671 : :
15672 : 287 : destroyPQExpBuffer(query);
15673 : 287 : destroyPQExpBuffer(q);
15674 : 287 : destroyPQExpBuffer(delq);
15675 : 287 : destroyPQExpBuffer(details);
15676 : : }
15677 : :
15678 : : /*
15679 : : * dumpTSParser
15680 : : * write out a single text search parser
15681 : : */
15682 : : static void
15683 : 44 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15684 : : {
15685 : 44 : DumpOptions *dopt = fout->dopt;
15686 : : PQExpBuffer q;
15687 : : PQExpBuffer delq;
15688 : : char *qprsname;
15689 : :
15690 : : /* Do nothing if not dumping schema */
15691 [ + + ]: 44 : if (!dopt->dumpSchema)
15692 : 7 : return;
15693 : :
15694 : 37 : q = createPQExpBuffer();
15695 : 37 : delq = createPQExpBuffer();
15696 : :
15697 : 37 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15698 : :
15699 : 37 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15700 : 37 : fmtQualifiedDumpable(prsinfo));
15701 : :
15702 : 37 : appendPQExpBuffer(q, " START = %s,\n",
15703 : 37 : convertTSFunction(fout, prsinfo->prsstart));
15704 : 37 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15705 : 37 : convertTSFunction(fout, prsinfo->prstoken));
15706 : 37 : appendPQExpBuffer(q, " END = %s,\n",
15707 : 37 : convertTSFunction(fout, prsinfo->prsend));
15708 [ + + ]: 37 : if (prsinfo->prsheadline != InvalidOid)
15709 : 3 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15710 : 3 : convertTSFunction(fout, prsinfo->prsheadline));
15711 : 37 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15712 : 37 : convertTSFunction(fout, prsinfo->prslextype));
15713 : :
15714 : 37 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15715 : 37 : fmtQualifiedDumpable(prsinfo));
15716 : :
15717 [ + + ]: 37 : if (dopt->binary_upgrade)
15718 : 1 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15719 : : "TEXT SEARCH PARSER", qprsname,
15720 : 1 : prsinfo->dobj.namespace->dobj.name);
15721 : :
15722 [ + - ]: 37 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15723 : 37 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15724 : 37 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15725 : : .namespace = prsinfo->dobj.namespace->dobj.name,
15726 : : .description = "TEXT SEARCH PARSER",
15727 : : .section = SECTION_PRE_DATA,
15728 : : .createStmt = q->data,
15729 : : .dropStmt = delq->data));
15730 : :
15731 : : /* Dump Parser Comments */
15732 [ + - ]: 37 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15733 : 37 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15734 : 37 : prsinfo->dobj.namespace->dobj.name, "",
15735 : 37 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15736 : :
15737 : 37 : destroyPQExpBuffer(q);
15738 : 37 : destroyPQExpBuffer(delq);
15739 : 37 : pg_free(qprsname);
15740 : : }
15741 : :
15742 : : /*
15743 : : * dumpTSDictionary
15744 : : * write out a single text search dictionary
15745 : : */
15746 : : static void
15747 : 182 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15748 : : {
15749 : 182 : DumpOptions *dopt = fout->dopt;
15750 : : PQExpBuffer q;
15751 : : PQExpBuffer delq;
15752 : : PQExpBuffer query;
15753 : : char *qdictname;
15754 : : PGresult *res;
15755 : : char *nspname;
15756 : : char *tmplname;
15757 : :
15758 : : /* Do nothing if not dumping schema */
15759 [ + + ]: 182 : if (!dopt->dumpSchema)
15760 : 7 : return;
15761 : :
15762 : 175 : q = createPQExpBuffer();
15763 : 175 : delq = createPQExpBuffer();
15764 : 175 : query = createPQExpBuffer();
15765 : :
15766 : 175 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15767 : :
15768 : : /* Fetch name and namespace of the dictionary's template */
15769 : 175 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15770 : : "FROM pg_ts_template p, pg_namespace n "
15771 : : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15772 : 175 : dictinfo->dicttemplate);
15773 : 175 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15774 : 175 : nspname = PQgetvalue(res, 0, 0);
15775 : 175 : tmplname = PQgetvalue(res, 0, 1);
15776 : :
15777 : 175 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15778 : 175 : fmtQualifiedDumpable(dictinfo));
15779 : :
15780 : 175 : appendPQExpBufferStr(q, " TEMPLATE = ");
15781 : 175 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15782 : 175 : appendPQExpBufferStr(q, fmtId(tmplname));
15783 : :
15784 : 175 : PQclear(res);
15785 : :
15786 : : /* the dictinitoption can be dumped straight into the command */
15787 [ + + ]: 175 : if (dictinfo->dictinitoption)
15788 : 138 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15789 : :
15790 : 175 : appendPQExpBufferStr(q, " );\n");
15791 : :
15792 : 175 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15793 : 175 : fmtQualifiedDumpable(dictinfo));
15794 : :
15795 [ + + ]: 175 : if (dopt->binary_upgrade)
15796 : 10 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15797 : : "TEXT SEARCH DICTIONARY", qdictname,
15798 : 10 : dictinfo->dobj.namespace->dobj.name);
15799 : :
15800 [ + - ]: 175 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15801 : 175 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15802 : 175 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15803 : : .namespace = dictinfo->dobj.namespace->dobj.name,
15804 : : .owner = dictinfo->rolname,
15805 : : .description = "TEXT SEARCH DICTIONARY",
15806 : : .section = SECTION_PRE_DATA,
15807 : : .createStmt = q->data,
15808 : : .dropStmt = delq->data));
15809 : :
15810 : : /* Dump Dictionary Comments */
15811 [ + + ]: 175 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15812 : 130 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15813 : 130 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15814 : 130 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15815 : :
15816 : 175 : destroyPQExpBuffer(q);
15817 : 175 : destroyPQExpBuffer(delq);
15818 : 175 : destroyPQExpBuffer(query);
15819 : 175 : pg_free(qdictname);
15820 : : }
15821 : :
15822 : : /*
15823 : : * dumpTSTemplate
15824 : : * write out a single text search template
15825 : : */
15826 : : static void
15827 : 56 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15828 : : {
15829 : 56 : DumpOptions *dopt = fout->dopt;
15830 : : PQExpBuffer q;
15831 : : PQExpBuffer delq;
15832 : : char *qtmplname;
15833 : :
15834 : : /* Do nothing if not dumping schema */
15835 [ + + ]: 56 : if (!dopt->dumpSchema)
15836 : 7 : return;
15837 : :
15838 : 49 : q = createPQExpBuffer();
15839 : 49 : delq = createPQExpBuffer();
15840 : :
15841 : 49 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15842 : :
15843 : 49 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15844 : 49 : fmtQualifiedDumpable(tmplinfo));
15845 : :
15846 [ + + ]: 49 : if (tmplinfo->tmplinit != InvalidOid)
15847 : 15 : appendPQExpBuffer(q, " INIT = %s,\n",
15848 : 15 : convertTSFunction(fout, tmplinfo->tmplinit));
15849 : 49 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15850 : 49 : convertTSFunction(fout, tmplinfo->tmpllexize));
15851 : :
15852 : 49 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15853 : 49 : fmtQualifiedDumpable(tmplinfo));
15854 : :
15855 [ + + ]: 49 : if (dopt->binary_upgrade)
15856 : 1 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15857 : : "TEXT SEARCH TEMPLATE", qtmplname,
15858 : 1 : tmplinfo->dobj.namespace->dobj.name);
15859 : :
15860 [ + - ]: 49 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15861 : 49 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15862 : 49 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15863 : : .namespace = tmplinfo->dobj.namespace->dobj.name,
15864 : : .description = "TEXT SEARCH TEMPLATE",
15865 : : .section = SECTION_PRE_DATA,
15866 : : .createStmt = q->data,
15867 : : .dropStmt = delq->data));
15868 : :
15869 : : /* Dump Template Comments */
15870 [ + - ]: 49 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15871 : 49 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15872 : 49 : tmplinfo->dobj.namespace->dobj.name, "",
15873 : 49 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15874 : :
15875 : 49 : destroyPQExpBuffer(q);
15876 : 49 : destroyPQExpBuffer(delq);
15877 : 49 : pg_free(qtmplname);
15878 : : }
15879 : :
15880 : : /*
15881 : : * dumpTSConfig
15882 : : * write out a single text search configuration
15883 : : */
15884 : : static void
15885 : 157 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15886 : : {
15887 : 157 : DumpOptions *dopt = fout->dopt;
15888 : : PQExpBuffer q;
15889 : : PQExpBuffer delq;
15890 : : PQExpBuffer query;
15891 : : char *qcfgname;
15892 : : PGresult *res;
15893 : : char *nspname;
15894 : : char *prsname;
15895 : : int ntups,
15896 : : i;
15897 : : int i_tokenname;
15898 : : int i_dictname;
15899 : :
15900 : : /* Do nothing if not dumping schema */
15901 [ + + ]: 157 : if (!dopt->dumpSchema)
15902 : 7 : return;
15903 : :
15904 : 150 : q = createPQExpBuffer();
15905 : 150 : delq = createPQExpBuffer();
15906 : 150 : query = createPQExpBuffer();
15907 : :
15908 : 150 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15909 : :
15910 : : /* Fetch name and namespace of the config's parser */
15911 : 150 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15912 : : "FROM pg_ts_parser p, pg_namespace n "
15913 : : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15914 : 150 : cfginfo->cfgparser);
15915 : 150 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15916 : 150 : nspname = PQgetvalue(res, 0, 0);
15917 : 150 : prsname = PQgetvalue(res, 0, 1);
15918 : :
15919 : 150 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15920 : 150 : fmtQualifiedDumpable(cfginfo));
15921 : :
15922 : 150 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15923 : 150 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15924 : :
15925 : 150 : PQclear(res);
15926 : :
15927 : 150 : resetPQExpBuffer(query);
15928 : 150 : appendPQExpBuffer(query,
15929 : : "SELECT\n"
15930 : : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15931 : : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15932 : : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15933 : : "FROM pg_catalog.pg_ts_config_map AS m\n"
15934 : : "WHERE m.mapcfg = '%u'\n"
15935 : : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15936 : 150 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15937 : :
15938 : 150 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15939 : 150 : ntups = PQntuples(res);
15940 : :
15941 : 150 : i_tokenname = PQfnumber(res, "tokenname");
15942 : 150 : i_dictname = PQfnumber(res, "dictname");
15943 : :
15944 [ + + ]: 3135 : for (i = 0; i < ntups; i++)
15945 : : {
15946 : 2985 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15947 : 2985 : char *dictname = PQgetvalue(res, i, i_dictname);
15948 : :
15949 [ + + ]: 2985 : if (i == 0 ||
15950 [ + + ]: 2835 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15951 : : {
15952 : : /* starting a new token type, so start a new command */
15953 [ + + ]: 2850 : if (i > 0)
15954 : 2700 : appendPQExpBufferStr(q, ";\n");
15955 : 2850 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15956 : 2850 : fmtQualifiedDumpable(cfginfo));
15957 : : /* tokenname needs quoting, dictname does NOT */
15958 : 2850 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15959 : : fmtId(tokenname), dictname);
15960 : : }
15961 : : else
15962 : 135 : appendPQExpBuffer(q, ", %s", dictname);
15963 : : }
15964 : :
15965 [ + - ]: 150 : if (ntups > 0)
15966 : 150 : appendPQExpBufferStr(q, ";\n");
15967 : :
15968 : 150 : PQclear(res);
15969 : :
15970 : 150 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15971 : 150 : fmtQualifiedDumpable(cfginfo));
15972 : :
15973 [ + + ]: 150 : if (dopt->binary_upgrade)
15974 : 5 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15975 : : "TEXT SEARCH CONFIGURATION", qcfgname,
15976 : 5 : cfginfo->dobj.namespace->dobj.name);
15977 : :
15978 [ + - ]: 150 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15979 : 150 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15980 : 150 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15981 : : .namespace = cfginfo->dobj.namespace->dobj.name,
15982 : : .owner = cfginfo->rolname,
15983 : : .description = "TEXT SEARCH CONFIGURATION",
15984 : : .section = SECTION_PRE_DATA,
15985 : : .createStmt = q->data,
15986 : : .dropStmt = delq->data));
15987 : :
15988 : : /* Dump Configuration Comments */
15989 [ + + ]: 150 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15990 : 130 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15991 : 130 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15992 : 130 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15993 : :
15994 : 150 : destroyPQExpBuffer(q);
15995 : 150 : destroyPQExpBuffer(delq);
15996 : 150 : destroyPQExpBuffer(query);
15997 : 150 : pg_free(qcfgname);
15998 : : }
15999 : :
16000 : : /*
16001 : : * dumpForeignDataWrapper
16002 : : * write out a single foreign-data wrapper definition
16003 : : */
16004 : : static void
16005 : 54 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16006 : : {
16007 : 54 : DumpOptions *dopt = fout->dopt;
16008 : : PQExpBuffer q;
16009 : : PQExpBuffer delq;
16010 : : char *qfdwname;
16011 : :
16012 : : /* Do nothing if not dumping schema */
16013 [ + + ]: 54 : if (!dopt->dumpSchema)
16014 : 7 : return;
16015 : :
16016 : 47 : q = createPQExpBuffer();
16017 : 47 : delq = createPQExpBuffer();
16018 : :
16019 : 47 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16020 : :
16021 : 47 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16022 : : qfdwname);
16023 : :
16024 [ - + ]: 47 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16025 : 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16026 : :
16027 [ - + ]: 47 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16028 : 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16029 : :
16030 [ - + ]: 47 : if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16031 : 0 : appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16032 : :
16033 [ - + ]: 47 : if (strlen(fdwinfo->fdwoptions) > 0)
16034 : 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16035 : :
16036 : 47 : appendPQExpBufferStr(q, ";\n");
16037 : :
16038 : 47 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16039 : : qfdwname);
16040 : :
16041 [ + + ]: 47 : if (dopt->binary_upgrade)
16042 : 2 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16043 : : "FOREIGN DATA WRAPPER", qfdwname,
16044 : : NULL);
16045 : :
16046 [ + - ]: 47 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16047 : 47 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16048 : 47 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16049 : : .owner = fdwinfo->rolname,
16050 : : .description = "FOREIGN DATA WRAPPER",
16051 : : .section = SECTION_PRE_DATA,
16052 : : .createStmt = q->data,
16053 : : .dropStmt = delq->data));
16054 : :
16055 : : /* Dump Foreign Data Wrapper Comments */
16056 [ - + ]: 47 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16057 : 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16058 : 0 : NULL, fdwinfo->rolname,
16059 : 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16060 : :
16061 : : /* Handle the ACL */
16062 [ + + ]: 47 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16063 : 33 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16064 : : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16065 : 33 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16066 : :
16067 : 47 : pg_free(qfdwname);
16068 : :
16069 : 47 : destroyPQExpBuffer(q);
16070 : 47 : destroyPQExpBuffer(delq);
16071 : : }
16072 : :
16073 : : /*
16074 : : * dumpForeignServer
16075 : : * write out a foreign server definition
16076 : : */
16077 : : static void
16078 : 58 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16079 : : {
16080 : 58 : DumpOptions *dopt = fout->dopt;
16081 : : PQExpBuffer q;
16082 : : PQExpBuffer delq;
16083 : : PQExpBuffer query;
16084 : : PGresult *res;
16085 : : char *qsrvname;
16086 : : char *fdwname;
16087 : :
16088 : : /* Do nothing if not dumping schema */
16089 [ + + ]: 58 : if (!dopt->dumpSchema)
16090 : 9 : return;
16091 : :
16092 : 49 : q = createPQExpBuffer();
16093 : 49 : delq = createPQExpBuffer();
16094 : 49 : query = createPQExpBuffer();
16095 : :
16096 : 49 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16097 : :
16098 : : /* look up the foreign-data wrapper */
16099 : 49 : appendPQExpBuffer(query, "SELECT fdwname "
16100 : : "FROM pg_foreign_data_wrapper w "
16101 : : "WHERE w.oid = '%u'",
16102 : 49 : srvinfo->srvfdw);
16103 : 49 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16104 : 49 : fdwname = PQgetvalue(res, 0, 0);
16105 : :
16106 : 49 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16107 [ + - - + ]: 49 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16108 : : {
16109 : 0 : appendPQExpBufferStr(q, " TYPE ");
16110 : 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16111 : : }
16112 [ + - - + ]: 49 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16113 : : {
16114 : 0 : appendPQExpBufferStr(q, " VERSION ");
16115 : 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16116 : : }
16117 : :
16118 : 49 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16119 : 49 : appendPQExpBufferStr(q, fmtId(fdwname));
16120 : :
16121 [ + - - + ]: 49 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16122 : 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16123 : :
16124 : 49 : appendPQExpBufferStr(q, ";\n");
16125 : :
16126 : 49 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16127 : : qsrvname);
16128 : :
16129 [ + + ]: 49 : if (dopt->binary_upgrade)
16130 : 2 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16131 : : "SERVER", qsrvname, NULL);
16132 : :
16133 [ + - ]: 49 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16134 : 49 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16135 : 49 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16136 : : .owner = srvinfo->rolname,
16137 : : .description = "SERVER",
16138 : : .section = SECTION_PRE_DATA,
16139 : : .createStmt = q->data,
16140 : : .dropStmt = delq->data));
16141 : :
16142 : : /* Dump Foreign Server Comments */
16143 [ - + ]: 49 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16144 : 0 : dumpComment(fout, "SERVER", qsrvname,
16145 : 0 : NULL, srvinfo->rolname,
16146 : 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16147 : :
16148 : : /* Handle the ACL */
16149 [ + + ]: 49 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16150 : 33 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16151 : : "FOREIGN SERVER", qsrvname, NULL, NULL,
16152 : 33 : NULL, srvinfo->rolname, &srvinfo->dacl);
16153 : :
16154 : : /* Dump user mappings */
16155 [ + - ]: 49 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16156 : 49 : dumpUserMappings(fout,
16157 : 49 : srvinfo->dobj.name, NULL,
16158 : 49 : srvinfo->rolname,
16159 : 49 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16160 : :
16161 : 49 : PQclear(res);
16162 : :
16163 : 49 : pg_free(qsrvname);
16164 : :
16165 : 49 : destroyPQExpBuffer(q);
16166 : 49 : destroyPQExpBuffer(delq);
16167 : 49 : destroyPQExpBuffer(query);
16168 : : }
16169 : :
16170 : : /*
16171 : : * dumpUserMappings
16172 : : *
16173 : : * This routine is used to dump any user mappings associated with the
16174 : : * server handed to this routine. Should be called after ArchiveEntry()
16175 : : * for the server.
16176 : : */
16177 : : static void
16178 : 49 : dumpUserMappings(Archive *fout,
16179 : : const char *servername, const char *namespace,
16180 : : const char *owner,
16181 : : CatalogId catalogId, DumpId dumpId)
16182 : : {
16183 : : PQExpBuffer q;
16184 : : PQExpBuffer delq;
16185 : : PQExpBuffer query;
16186 : : PQExpBuffer tag;
16187 : : PGresult *res;
16188 : : int ntups;
16189 : : int i_usename;
16190 : : int i_umoptions;
16191 : : int i;
16192 : :
16193 : 49 : q = createPQExpBuffer();
16194 : 49 : tag = createPQExpBuffer();
16195 : 49 : delq = createPQExpBuffer();
16196 : 49 : query = createPQExpBuffer();
16197 : :
16198 : : /*
16199 : : * We read from the publicly accessible view pg_user_mappings, so as not
16200 : : * to fail if run by a non-superuser. Note that the view will show
16201 : : * umoptions as null if the user hasn't got privileges for the associated
16202 : : * server; this means that pg_dump will dump such a mapping, but with no
16203 : : * OPTIONS clause. A possible alternative is to skip such mappings
16204 : : * altogether, but it's not clear that that's an improvement.
16205 : : */
16206 : 49 : appendPQExpBuffer(query,
16207 : : "SELECT usename, "
16208 : : "array_to_string(ARRAY("
16209 : : "SELECT quote_ident(option_name) || ' ' || "
16210 : : "quote_literal(option_value) "
16211 : : "FROM pg_options_to_table(umoptions) "
16212 : : "ORDER BY option_name"
16213 : : "), E',\n ') AS umoptions "
16214 : : "FROM pg_user_mappings "
16215 : : "WHERE srvid = '%u' "
16216 : : "ORDER BY usename",
16217 : : catalogId.oid);
16218 : :
16219 : 49 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16220 : :
16221 : 49 : ntups = PQntuples(res);
16222 : 49 : i_usename = PQfnumber(res, "usename");
16223 : 49 : i_umoptions = PQfnumber(res, "umoptions");
16224 : :
16225 [ + + ]: 82 : for (i = 0; i < ntups; i++)
16226 : : {
16227 : : char *usename;
16228 : : char *umoptions;
16229 : :
16230 : 33 : usename = PQgetvalue(res, i, i_usename);
16231 : 33 : umoptions = PQgetvalue(res, i, i_umoptions);
16232 : :
16233 : 33 : resetPQExpBuffer(q);
16234 : 33 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16235 : 33 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16236 : :
16237 [ + - - + ]: 33 : if (umoptions && strlen(umoptions) > 0)
16238 : 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16239 : :
16240 : 33 : appendPQExpBufferStr(q, ";\n");
16241 : :
16242 : 33 : resetPQExpBuffer(delq);
16243 : 33 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16244 : 33 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16245 : :
16246 : 33 : resetPQExpBuffer(tag);
16247 : 33 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16248 : : usename, servername);
16249 : :
16250 : 33 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16251 : 33 : ARCHIVE_OPTS(.tag = tag->data,
16252 : : .namespace = namespace,
16253 : : .owner = owner,
16254 : : .description = "USER MAPPING",
16255 : : .section = SECTION_PRE_DATA,
16256 : : .createStmt = q->data,
16257 : : .dropStmt = delq->data));
16258 : : }
16259 : :
16260 : 49 : PQclear(res);
16261 : :
16262 : 49 : destroyPQExpBuffer(query);
16263 : 49 : destroyPQExpBuffer(delq);
16264 : 49 : destroyPQExpBuffer(tag);
16265 : 49 : destroyPQExpBuffer(q);
16266 : 49 : }
16267 : :
16268 : : /*
16269 : : * Write out default privileges information
16270 : : */
16271 : : static void
16272 : 170 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16273 : : {
16274 : 170 : DumpOptions *dopt = fout->dopt;
16275 : : PQExpBuffer q;
16276 : : PQExpBuffer tag;
16277 : : const char *type;
16278 : :
16279 : : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16280 [ + + + + ]: 170 : if (!dopt->dumpSchema || dopt->aclsSkip)
16281 : 30 : return;
16282 : :
16283 : 140 : q = createPQExpBuffer();
16284 : 140 : tag = createPQExpBuffer();
16285 : :
16286 [ + - + + : 140 : switch (daclinfo->defaclobjtype)
- - - ]
16287 : : {
16288 : 65 : case DEFACLOBJ_RELATION:
16289 : 65 : type = "TABLES";
16290 : 65 : break;
16291 : 0 : case DEFACLOBJ_SEQUENCE:
16292 : 0 : type = "SEQUENCES";
16293 : 0 : break;
16294 : 65 : case DEFACLOBJ_FUNCTION:
16295 : 65 : type = "FUNCTIONS";
16296 : 65 : break;
16297 : 10 : case DEFACLOBJ_TYPE:
16298 : 10 : type = "TYPES";
16299 : 10 : break;
16300 : 0 : case DEFACLOBJ_NAMESPACE:
16301 : 0 : type = "SCHEMAS";
16302 : 0 : break;
16303 : 0 : case DEFACLOBJ_LARGEOBJECT:
16304 : 0 : type = "LARGE OBJECTS";
16305 : 0 : break;
16306 : 0 : default:
16307 : : /* shouldn't get here */
16308 : 0 : pg_fatal("unrecognized object type in default privileges: %d",
16309 : : (int) daclinfo->defaclobjtype);
16310 : : type = ""; /* keep compiler quiet */
16311 : : }
16312 : :
16313 : 140 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16314 : :
16315 : : /* build the actual command(s) for this tuple */
16316 [ - + ]: 140 : if (!buildDefaultACLCommands(type,
16317 : 140 : daclinfo->dobj.namespace != NULL ?
16318 : 66 : daclinfo->dobj.namespace->dobj.name : NULL,
16319 : 140 : daclinfo->dacl.acl,
16320 : 140 : daclinfo->dacl.acldefault,
16321 [ + + ]: 140 : daclinfo->defaclrole,
16322 : : fout->remoteVersion,
16323 : : q))
16324 : 0 : pg_fatal("could not parse default ACL list (%s)",
16325 : : daclinfo->dacl.acl);
16326 : :
16327 [ + - ]: 140 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16328 : 140 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16329 [ + + ]: 140 : ARCHIVE_OPTS(.tag = tag->data,
16330 : : .namespace = daclinfo->dobj.namespace ?
16331 : : daclinfo->dobj.namespace->dobj.name : NULL,
16332 : : .owner = daclinfo->defaclrole,
16333 : : .description = "DEFAULT ACL",
16334 : : .section = SECTION_POST_DATA,
16335 : : .createStmt = q->data));
16336 : :
16337 : 140 : destroyPQExpBuffer(tag);
16338 : 140 : destroyPQExpBuffer(q);
16339 : : }
16340 : :
16341 : : /*----------
16342 : : * Write out grant/revoke information
16343 : : *
16344 : : * 'objDumpId' is the dump ID of the underlying object.
16345 : : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16346 : : * or InvalidDumpId if there is no need for a second dependency.
16347 : : * 'type' must be one of
16348 : : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16349 : : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16350 : : * 'name' is the formatted name of the object. Must be quoted etc. already.
16351 : : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16352 : : * (Currently we assume that subname is only provided for table columns.)
16353 : : * 'nspname' is the namespace the object is in (NULL if none).
16354 : : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16355 : : * to use the default for the object type.
16356 : : * 'owner' is the owner, NULL if there is no owner (for languages).
16357 : : * 'dacl' is the DumpableAcl struct for the object.
16358 : : *
16359 : : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16360 : : * no ACL entry was created.
16361 : : *----------
16362 : : */
16363 : : static DumpId
16364 : 31974 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16365 : : const char *type, const char *name, const char *subname,
16366 : : const char *nspname, const char *tag, const char *owner,
16367 : : const DumpableAcl *dacl)
16368 : : {
16369 : 31974 : DumpId aclDumpId = InvalidDumpId;
16370 : 31974 : DumpOptions *dopt = fout->dopt;
16371 : 31974 : const char *acls = dacl->acl;
16372 : 31974 : const char *acldefault = dacl->acldefault;
16373 : 31974 : char privtype = dacl->privtype;
16374 : 31974 : const char *initprivs = dacl->initprivs;
16375 : : const char *baseacls;
16376 : : PQExpBuffer sql;
16377 : :
16378 : : /* Do nothing if ACL dump is not enabled */
16379 [ + + ]: 31974 : if (dopt->aclsSkip)
16380 : 349 : return InvalidDumpId;
16381 : :
16382 : : /* --data-only skips ACLs *except* large object ACLs */
16383 [ + + + + ]: 31625 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16384 : 1 : return InvalidDumpId;
16385 : :
16386 : 31624 : sql = createPQExpBuffer();
16387 : :
16388 : : /*
16389 : : * In binary upgrade mode, we don't run an extension's script but instead
16390 : : * dump out the objects independently and then recreate them. To preserve
16391 : : * any initial privileges which were set on extension objects, we need to
16392 : : * compute the set of GRANT and REVOKE commands necessary to get from the
16393 : : * default privileges of an object to its initial privileges as recorded
16394 : : * in pg_init_privs.
16395 : : *
16396 : : * At restore time, we apply these commands after having called
16397 : : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16398 : : * copy the results into pg_init_privs. This is how we preserve the
16399 : : * contents of that catalog across binary upgrades.
16400 : : */
16401 [ + + + + : 31624 : if (dopt->binary_upgrade && privtype == 'e' &&
+ - ]
16402 [ + - ]: 13 : initprivs && *initprivs != '\0')
16403 : : {
16404 : 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16405 [ - + ]: 13 : if (!buildACLCommands(name, subname, nspname, type,
16406 : : initprivs, acldefault, owner,
16407 : : "", fout->remoteVersion, sql))
16408 : 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16409 : : initprivs, acldefault, name, type);
16410 : 13 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16411 : : }
16412 : :
16413 : : /*
16414 : : * Now figure the GRANT and REVOKE commands needed to get to the object's
16415 : : * actual current ACL, starting from the initprivs if given, else from the
16416 : : * object-type-specific default. Also, while buildACLCommands will assume
16417 : : * that a NULL/empty acls string means it needn't do anything, what that
16418 : : * actually represents is the object-type-specific default; so we need to
16419 : : * substitute the acldefault string to get the right results in that case.
16420 : : */
16421 [ + + + + ]: 31624 : if (initprivs && *initprivs != '\0')
16422 : : {
16423 : 29711 : baseacls = initprivs;
16424 [ + - + + ]: 29711 : if (acls == NULL || *acls == '\0')
16425 : 17 : acls = acldefault;
16426 : : }
16427 : : else
16428 : 1913 : baseacls = acldefault;
16429 : :
16430 [ - + ]: 31624 : if (!buildACLCommands(name, subname, nspname, type,
16431 : : acls, baseacls, owner,
16432 : : "", fout->remoteVersion, sql))
16433 : 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16434 : : acls, baseacls, name, type);
16435 : :
16436 [ + + ]: 31624 : if (sql->len > 0)
16437 : : {
16438 : 1971 : PQExpBuffer tagbuf = createPQExpBuffer();
16439 : : DumpId aclDeps[2];
16440 : 1971 : int nDeps = 0;
16441 : :
16442 [ - + ]: 1971 : if (tag)
16443 : 0 : appendPQExpBufferStr(tagbuf, tag);
16444 [ + + ]: 1971 : else if (subname)
16445 : 1111 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16446 : : else
16447 : 860 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16448 : :
16449 : 1971 : aclDeps[nDeps++] = objDumpId;
16450 [ + + ]: 1971 : if (altDumpId != InvalidDumpId)
16451 : 1025 : aclDeps[nDeps++] = altDumpId;
16452 : :
16453 : 1971 : aclDumpId = createDumpId();
16454 : :
16455 : 1971 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16456 : 1971 : ARCHIVE_OPTS(.tag = tagbuf->data,
16457 : : .namespace = nspname,
16458 : : .owner = owner,
16459 : : .description = "ACL",
16460 : : .section = SECTION_NONE,
16461 : : .createStmt = sql->data,
16462 : : .deps = aclDeps,
16463 : : .nDeps = nDeps));
16464 : :
16465 : 1971 : destroyPQExpBuffer(tagbuf);
16466 : : }
16467 : :
16468 : 31624 : destroyPQExpBuffer(sql);
16469 : :
16470 : 31624 : return aclDumpId;
16471 : : }
16472 : :
16473 : : /*
16474 : : * dumpSecLabel
16475 : : *
16476 : : * This routine is used to dump any security labels associated with the
16477 : : * object handed to this routine. The routine takes the object type
16478 : : * and object name (ready to print, except for schema decoration), plus
16479 : : * the namespace and owner of the object (for labeling the ArchiveEntry),
16480 : : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16481 : : * plus the dump ID for the object (for setting a dependency).
16482 : : * If a matching pg_seclabel entry is found, it is dumped.
16483 : : *
16484 : : * Note: although this routine takes a dumpId for dependency purposes,
16485 : : * that purpose is just to mark the dependency in the emitted dump file
16486 : : * for possible future use by pg_restore. We do NOT use it for determining
16487 : : * ordering of the label in the dump file, because this routine is called
16488 : : * after dependency sorting occurs. This routine should be called just after
16489 : : * calling ArchiveEntry() for the specified object.
16490 : : */
16491 : : static void
16492 : 10 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16493 : : const char *namespace, const char *owner,
16494 : : CatalogId catalogId, int subid, DumpId dumpId)
16495 : : {
16496 : 10 : DumpOptions *dopt = fout->dopt;
16497 : : SecLabelItem *labels;
16498 : : int nlabels;
16499 : : int i;
16500 : : PQExpBuffer query;
16501 : :
16502 : : /* do nothing, if --no-security-labels is supplied */
16503 [ - + ]: 10 : if (dopt->no_security_labels)
16504 : 0 : return;
16505 : :
16506 : : /*
16507 : : * Security labels are schema not data ... except large object labels are
16508 : : * data
16509 : : */
16510 [ - + ]: 10 : if (strcmp(type, "LARGE OBJECT") != 0)
16511 : : {
16512 [ # # ]: 0 : if (!dopt->dumpSchema)
16513 : 0 : return;
16514 : : }
16515 : : else
16516 : : {
16517 : : /* We do dump large object security labels in binary-upgrade mode */
16518 [ + - - + ]: 10 : if (!dopt->dumpData && !dopt->binary_upgrade)
16519 : 0 : return;
16520 : : }
16521 : :
16522 : : /* Search for security labels associated with catalogId, using table */
16523 : 10 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16524 : :
16525 : 10 : query = createPQExpBuffer();
16526 : :
16527 [ + + ]: 15 : for (i = 0; i < nlabels; i++)
16528 : : {
16529 : : /*
16530 : : * Ignore label entries for which the subid doesn't match.
16531 : : */
16532 [ - + ]: 5 : if (labels[i].objsubid != subid)
16533 : 0 : continue;
16534 : :
16535 : 5 : appendPQExpBuffer(query,
16536 : : "SECURITY LABEL FOR %s ON %s ",
16537 : 5 : fmtId(labels[i].provider), type);
16538 [ - + - - ]: 5 : if (namespace && *namespace)
16539 : 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16540 : 5 : appendPQExpBuffer(query, "%s IS ", name);
16541 : 5 : appendStringLiteralAH(query, labels[i].label, fout);
16542 : 5 : appendPQExpBufferStr(query, ";\n");
16543 : : }
16544 : :
16545 [ + + ]: 10 : if (query->len > 0)
16546 : : {
16547 : 5 : PQExpBuffer tag = createPQExpBuffer();
16548 : :
16549 : 5 : appendPQExpBuffer(tag, "%s %s", type, name);
16550 : 5 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16551 : 5 : ARCHIVE_OPTS(.tag = tag->data,
16552 : : .namespace = namespace,
16553 : : .owner = owner,
16554 : : .description = "SECURITY LABEL",
16555 : : .section = SECTION_NONE,
16556 : : .createStmt = query->data,
16557 : : .deps = &dumpId,
16558 : : .nDeps = 1));
16559 : 5 : destroyPQExpBuffer(tag);
16560 : : }
16561 : :
16562 : 10 : destroyPQExpBuffer(query);
16563 : : }
16564 : :
16565 : : /*
16566 : : * dumpTableSecLabel
16567 : : *
16568 : : * As above, but dump security label for both the specified table (or view)
16569 : : * and its columns.
16570 : : */
16571 : : static void
16572 : 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16573 : : {
16574 : 0 : DumpOptions *dopt = fout->dopt;
16575 : : SecLabelItem *labels;
16576 : : int nlabels;
16577 : : int i;
16578 : : PQExpBuffer query;
16579 : : PQExpBuffer target;
16580 : :
16581 : : /* do nothing, if --no-security-labels is supplied */
16582 [ # # ]: 0 : if (dopt->no_security_labels)
16583 : 0 : return;
16584 : :
16585 : : /* SecLabel are SCHEMA not data */
16586 [ # # ]: 0 : if (!dopt->dumpSchema)
16587 : 0 : return;
16588 : :
16589 : : /* Search for comments associated with relation, using table */
16590 : 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16591 : 0 : tbinfo->dobj.catId.oid,
16592 : : &labels);
16593 : :
16594 : : /* If security labels exist, build SECURITY LABEL statements */
16595 [ # # ]: 0 : if (nlabels <= 0)
16596 : 0 : return;
16597 : :
16598 : 0 : query = createPQExpBuffer();
16599 : 0 : target = createPQExpBuffer();
16600 : :
16601 [ # # ]: 0 : for (i = 0; i < nlabels; i++)
16602 : : {
16603 : : const char *colname;
16604 : 0 : const char *provider = labels[i].provider;
16605 : 0 : const char *label = labels[i].label;
16606 : 0 : int objsubid = labels[i].objsubid;
16607 : :
16608 : 0 : resetPQExpBuffer(target);
16609 [ # # ]: 0 : if (objsubid == 0)
16610 : : {
16611 : 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16612 : 0 : fmtQualifiedDumpable(tbinfo));
16613 : : }
16614 : : else
16615 : : {
16616 : 0 : colname = getAttrName(objsubid, tbinfo);
16617 : : /* first fmtXXX result must be consumed before calling again */
16618 : 0 : appendPQExpBuffer(target, "COLUMN %s",
16619 : 0 : fmtQualifiedDumpable(tbinfo));
16620 : 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16621 : : }
16622 : 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16623 : : fmtId(provider), target->data);
16624 : 0 : appendStringLiteralAH(query, label, fout);
16625 : 0 : appendPQExpBufferStr(query, ";\n");
16626 : : }
16627 [ # # ]: 0 : if (query->len > 0)
16628 : : {
16629 : 0 : resetPQExpBuffer(target);
16630 : 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16631 : 0 : fmtId(tbinfo->dobj.name));
16632 : 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16633 : 0 : ARCHIVE_OPTS(.tag = target->data,
16634 : : .namespace = tbinfo->dobj.namespace->dobj.name,
16635 : : .owner = tbinfo->rolname,
16636 : : .description = "SECURITY LABEL",
16637 : : .section = SECTION_NONE,
16638 : : .createStmt = query->data,
16639 : : .deps = &(tbinfo->dobj.dumpId),
16640 : : .nDeps = 1));
16641 : : }
16642 : 0 : destroyPQExpBuffer(query);
16643 : 0 : destroyPQExpBuffer(target);
16644 : : }
16645 : :
16646 : : /*
16647 : : * findSecLabels
16648 : : *
16649 : : * Find the security label(s), if any, associated with the given object.
16650 : : * All the objsubid values associated with the given classoid/objoid are
16651 : : * found with one search.
16652 : : */
16653 : : static int
16654 : 10 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16655 : : {
16656 : 10 : SecLabelItem *middle = NULL;
16657 : : SecLabelItem *low;
16658 : : SecLabelItem *high;
16659 : : int nmatch;
16660 : :
16661 [ - + ]: 10 : if (nseclabels <= 0) /* no labels, so no match is possible */
16662 : : {
16663 : 0 : *items = NULL;
16664 : 0 : return 0;
16665 : : }
16666 : :
16667 : : /*
16668 : : * Do binary search to find some item matching the object.
16669 : : */
16670 : 10 : low = &seclabels[0];
16671 : 10 : high = &seclabels[nseclabels - 1];
16672 [ + + ]: 15 : while (low <= high)
16673 : : {
16674 : 10 : middle = low + (high - low) / 2;
16675 : :
16676 [ - + ]: 10 : if (classoid < middle->classoid)
16677 : 0 : high = middle - 1;
16678 [ - + ]: 10 : else if (classoid > middle->classoid)
16679 : 0 : low = middle + 1;
16680 [ + + ]: 10 : else if (objoid < middle->objoid)
16681 : 5 : high = middle - 1;
16682 [ - + ]: 5 : else if (objoid > middle->objoid)
16683 : 0 : low = middle + 1;
16684 : : else
16685 : 5 : break; /* found a match */
16686 : : }
16687 : :
16688 [ + + ]: 10 : if (low > high) /* no matches */
16689 : : {
16690 : 5 : *items = NULL;
16691 : 5 : return 0;
16692 : : }
16693 : :
16694 : : /*
16695 : : * Now determine how many items match the object. The search loop
16696 : : * invariant still holds: only items between low and high inclusive could
16697 : : * match.
16698 : : */
16699 : 5 : nmatch = 1;
16700 [ - + ]: 5 : while (middle > low)
16701 : : {
16702 [ # # ]: 0 : if (classoid != middle[-1].classoid ||
16703 [ # # ]: 0 : objoid != middle[-1].objoid)
16704 : : break;
16705 : 0 : middle--;
16706 : 0 : nmatch++;
16707 : : }
16708 : :
16709 : 5 : *items = middle;
16710 : :
16711 : 5 : middle += nmatch;
16712 [ - + ]: 5 : while (middle <= high)
16713 : : {
16714 [ # # ]: 0 : if (classoid != middle->classoid ||
16715 [ # # ]: 0 : objoid != middle->objoid)
16716 : : break;
16717 : 0 : middle++;
16718 : 0 : nmatch++;
16719 : : }
16720 : :
16721 : 5 : return nmatch;
16722 : : }
16723 : :
16724 : : /*
16725 : : * collectSecLabels
16726 : : *
16727 : : * Construct a table of all security labels available for database objects;
16728 : : * also set the has-seclabel component flag for each relevant object.
16729 : : *
16730 : : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16731 : : */
16732 : : static void
16733 : 191 : collectSecLabels(Archive *fout)
16734 : : {
16735 : : PGresult *res;
16736 : : PQExpBuffer query;
16737 : : int i_label;
16738 : : int i_provider;
16739 : : int i_classoid;
16740 : : int i_objoid;
16741 : : int i_objsubid;
16742 : : int ntups;
16743 : : int i;
16744 : : DumpableObject *dobj;
16745 : :
16746 : 191 : query = createPQExpBuffer();
16747 : :
16748 : 191 : appendPQExpBufferStr(query,
16749 : : "SELECT label, provider, classoid, objoid, objsubid "
16750 : : "FROM pg_catalog.pg_seclabels "
16751 : : "ORDER BY classoid, objoid, objsubid");
16752 : :
16753 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16754 : :
16755 : : /* Construct lookup table containing OIDs in numeric form */
16756 : 191 : i_label = PQfnumber(res, "label");
16757 : 191 : i_provider = PQfnumber(res, "provider");
16758 : 191 : i_classoid = PQfnumber(res, "classoid");
16759 : 191 : i_objoid = PQfnumber(res, "objoid");
16760 : 191 : i_objsubid = PQfnumber(res, "objsubid");
16761 : :
16762 : 191 : ntups = PQntuples(res);
16763 : :
16764 : 191 : seclabels = pg_malloc_array(SecLabelItem, ntups);
16765 : 191 : nseclabels = 0;
16766 : 191 : dobj = NULL;
16767 : :
16768 [ + + ]: 196 : for (i = 0; i < ntups; i++)
16769 : : {
16770 : : CatalogId objId;
16771 : : int subid;
16772 : :
16773 : 5 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16774 : 5 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16775 : 5 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16776 : :
16777 : : /* We needn't remember labels that don't match any dumpable object */
16778 [ - + ]: 5 : if (dobj == NULL ||
16779 [ # # ]: 0 : dobj->catId.tableoid != objId.tableoid ||
16780 [ # # ]: 0 : dobj->catId.oid != objId.oid)
16781 : 5 : dobj = findObjectByCatalogId(objId);
16782 [ - + ]: 5 : if (dobj == NULL)
16783 : 0 : continue;
16784 : :
16785 : : /*
16786 : : * Labels on columns of composite types are linked to the type's
16787 : : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16788 : : * in the type's own DumpableObject.
16789 : : */
16790 [ - + - - ]: 5 : if (subid != 0 && dobj->objType == DO_TABLE &&
16791 [ # # ]: 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16792 : 0 : {
16793 : : TypeInfo *cTypeInfo;
16794 : :
16795 : 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16796 [ # # ]: 0 : if (cTypeInfo)
16797 : 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16798 : : }
16799 : : else
16800 : 5 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16801 : :
16802 : 5 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16803 : 5 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16804 : 5 : seclabels[nseclabels].classoid = objId.tableoid;
16805 : 5 : seclabels[nseclabels].objoid = objId.oid;
16806 : 5 : seclabels[nseclabels].objsubid = subid;
16807 : 5 : nseclabels++;
16808 : : }
16809 : :
16810 : 191 : PQclear(res);
16811 : 191 : destroyPQExpBuffer(query);
16812 : 191 : }
16813 : :
16814 : : /*
16815 : : * dumpTable
16816 : : * write out to fout the declarations (not data) of a user-defined table
16817 : : */
16818 : : static void
16819 : 34203 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16820 : : {
16821 : 34203 : DumpOptions *dopt = fout->dopt;
16822 : 34203 : DumpId tableAclDumpId = InvalidDumpId;
16823 : : char *namecopy;
16824 : :
16825 : : /* Do nothing if not dumping schema */
16826 [ + + ]: 34203 : if (!dopt->dumpSchema)
16827 : 1671 : return;
16828 : :
16829 [ + + ]: 32532 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16830 : : {
16831 [ + + ]: 7259 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16832 : 381 : dumpSequence(fout, tbinfo);
16833 : : else
16834 : 6878 : dumpTableSchema(fout, tbinfo);
16835 : : }
16836 : :
16837 : : /* Handle the ACL here */
16838 : 32532 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16839 [ + + ]: 32532 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16840 : : {
16841 : : const char *objtype;
16842 : :
16843 [ + + + ]: 26105 : switch (tbinfo->relkind)
16844 : : {
16845 : 90 : case RELKIND_SEQUENCE:
16846 : 90 : objtype = "SEQUENCE";
16847 : 90 : break;
16848 : 39 : case RELKIND_PROPGRAPH:
16849 : 39 : objtype = "PROPERTY GRAPH";
16850 : 39 : break;
16851 : 25976 : default:
16852 : 25976 : objtype = "TABLE";
16853 : 25976 : break;
16854 : : }
16855 : :
16856 : : tableAclDumpId =
16857 : 26105 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16858 : : objtype, namecopy, NULL,
16859 : 26105 : tbinfo->dobj.namespace->dobj.name,
16860 : 26105 : NULL, tbinfo->rolname, &tbinfo->dacl);
16861 : : }
16862 : :
16863 : : /*
16864 : : * Handle column ACLs, if any. Note: we pull these with a separate query
16865 : : * rather than trying to fetch them during getTableAttrs, so that we won't
16866 : : * miss ACLs on system columns. Doing it this way also allows us to dump
16867 : : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16868 : : */
16869 [ + + + + ]: 32532 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16870 : : {
16871 : 291 : PQExpBuffer query = createPQExpBuffer();
16872 : : PGresult *res;
16873 : : int i;
16874 : :
16875 [ + + ]: 291 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16876 : : {
16877 : : /* Set up query for column ACLs */
16878 : 164 : appendPQExpBufferStr(query,
16879 : : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16880 : :
16881 : : /*
16882 : : * In principle we should call acldefault('c', relowner) to get
16883 : : * the default ACL for a column. However, we don't currently
16884 : : * store the numeric OID of the relowner in TableInfo. We could
16885 : : * convert the owner name using regrole, but that creates a risk
16886 : : * of failure due to concurrent role renames. Given that the
16887 : : * default ACL for columns is empty and is likely to stay that
16888 : : * way, it's not worth extra cycles and risk to avoid hard-wiring
16889 : : * that knowledge here.
16890 : : */
16891 : 164 : appendPQExpBufferStr(query,
16892 : : "SELECT at.attname, "
16893 : : "at.attacl, "
16894 : : "'{}' AS acldefault, "
16895 : : "pip.privtype, pip.initprivs "
16896 : : "FROM pg_catalog.pg_attribute at "
16897 : : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16898 : : "(at.attrelid = pip.objoid "
16899 : : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16900 : : "AND at.attnum = pip.objsubid) "
16901 : : "WHERE at.attrelid = $1 AND "
16902 : : "NOT at.attisdropped "
16903 : : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16904 : : "ORDER BY at.attnum");
16905 : :
16906 : 164 : ExecuteSqlStatement(fout, query->data);
16907 : :
16908 : 164 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16909 : : }
16910 : :
16911 : 291 : printfPQExpBuffer(query,
16912 : : "EXECUTE getColumnACLs('%u')",
16913 : 291 : tbinfo->dobj.catId.oid);
16914 : :
16915 : 291 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16916 : :
16917 [ + + ]: 5266 : for (i = 0; i < PQntuples(res); i++)
16918 : : {
16919 : 4975 : char *attname = PQgetvalue(res, i, 0);
16920 : 4975 : char *attacl = PQgetvalue(res, i, 1);
16921 : 4975 : char *acldefault = PQgetvalue(res, i, 2);
16922 : 4975 : char privtype = *(PQgetvalue(res, i, 3));
16923 : 4975 : char *initprivs = PQgetvalue(res, i, 4);
16924 : : DumpableAcl coldacl;
16925 : : char *attnamecopy;
16926 : :
16927 : 4975 : coldacl.acl = attacl;
16928 : 4975 : coldacl.acldefault = acldefault;
16929 : 4975 : coldacl.privtype = privtype;
16930 : 4975 : coldacl.initprivs = initprivs;
16931 : 4975 : attnamecopy = pg_strdup(fmtId(attname));
16932 : :
16933 : : /*
16934 : : * Column's GRANT type is always TABLE. Each column ACL depends
16935 : : * on the table-level ACL, since we can restore column ACLs in
16936 : : * parallel but the table-level ACL has to be done first.
16937 : : */
16938 : 4975 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16939 : : "TABLE", namecopy, attnamecopy,
16940 : 4975 : tbinfo->dobj.namespace->dobj.name,
16941 : 4975 : NULL, tbinfo->rolname, &coldacl);
16942 : 4975 : pg_free(attnamecopy);
16943 : : }
16944 : 291 : PQclear(res);
16945 : 291 : destroyPQExpBuffer(query);
16946 : : }
16947 : :
16948 : 32532 : pg_free(namecopy);
16949 : : }
16950 : :
16951 : : /*
16952 : : * Create the AS clause for a view or materialized view. The semicolon is
16953 : : * stripped because a materialized view must add a WITH NO DATA clause.
16954 : : *
16955 : : * This returns a new buffer which must be freed by the caller.
16956 : : */
16957 : : static PQExpBuffer
16958 : 926 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16959 : : {
16960 : 926 : PQExpBuffer query = createPQExpBuffer();
16961 : 926 : PQExpBuffer result = createPQExpBuffer();
16962 : : PGresult *res;
16963 : : int len;
16964 : :
16965 : : /* Fetch the view definition */
16966 : 926 : appendPQExpBuffer(query,
16967 : : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16968 : 926 : tbinfo->dobj.catId.oid);
16969 : :
16970 : 926 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16971 : :
16972 [ - + ]: 926 : if (PQntuples(res) != 1)
16973 : : {
16974 [ # # ]: 0 : if (PQntuples(res) < 1)
16975 : 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16976 : : tbinfo->dobj.name);
16977 : : else
16978 : 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16979 : : tbinfo->dobj.name);
16980 : : }
16981 : :
16982 : 926 : len = PQgetlength(res, 0, 0);
16983 : :
16984 [ - + ]: 926 : if (len == 0)
16985 : 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16986 : : tbinfo->dobj.name);
16987 : :
16988 : : /* Strip off the trailing semicolon so that other things may follow. */
16989 : : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16990 : 926 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16991 : :
16992 : 926 : PQclear(res);
16993 : 926 : destroyPQExpBuffer(query);
16994 : :
16995 : 926 : return result;
16996 : : }
16997 : :
16998 : : /*
16999 : : * Create a dummy AS clause for a view. This is used when the real view
17000 : : * definition has to be postponed because of circular dependencies.
17001 : : * We must duplicate the view's external properties -- column names and types
17002 : : * (including collation) -- so that it works for subsequent references.
17003 : : *
17004 : : * This returns a new buffer which must be freed by the caller.
17005 : : */
17006 : : static PQExpBuffer
17007 : 20 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17008 : : {
17009 : 20 : PQExpBuffer result = createPQExpBuffer();
17010 : : int j;
17011 : :
17012 : 20 : appendPQExpBufferStr(result, "SELECT");
17013 : :
17014 [ + + ]: 40 : for (j = 0; j < tbinfo->numatts; j++)
17015 : : {
17016 [ + + ]: 20 : if (j > 0)
17017 : 10 : appendPQExpBufferChar(result, ',');
17018 : 20 : appendPQExpBufferStr(result, "\n ");
17019 : :
17020 : 20 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17021 : :
17022 : : /*
17023 : : * Must add collation if not default for the type, because CREATE OR
17024 : : * REPLACE VIEW won't change it
17025 : : */
17026 [ - + ]: 20 : if (OidIsValid(tbinfo->attcollation[j]))
17027 : : {
17028 : : CollInfo *coll;
17029 : :
17030 : 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17031 [ # # ]: 0 : if (coll)
17032 : 0 : appendPQExpBuffer(result, " COLLATE %s",
17033 : 0 : fmtQualifiedDumpable(coll));
17034 : : }
17035 : :
17036 : 20 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17037 : : }
17038 : :
17039 : 20 : return result;
17040 : : }
17041 : :
17042 : : /*
17043 : : * dumpTableSchema
17044 : : * write the declaration (not data) of one user-defined table or view
17045 : : */
17046 : : static void
17047 : 6878 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17048 : : {
17049 : 6878 : DumpOptions *dopt = fout->dopt;
17050 : 6878 : PQExpBuffer q = createPQExpBuffer();
17051 : 6878 : PQExpBuffer delq = createPQExpBuffer();
17052 : 6878 : PQExpBuffer extra = createPQExpBuffer();
17053 : : char *qrelname;
17054 : : char *qualrelname;
17055 : : int numParents;
17056 : : TableInfo **parents;
17057 : : int actual_atts; /* number of attrs in this CREATE statement */
17058 : : const char *reltypename;
17059 : : char *storage;
17060 : : int j,
17061 : : k;
17062 : :
17063 : : /* We had better have loaded per-column details about this table */
17064 : : Assert(tbinfo->interesting);
17065 : :
17066 : 6878 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17067 : 6878 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17068 : :
17069 [ - + ]: 6878 : if (tbinfo->hasoids)
17070 : 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17071 : : qrelname);
17072 : :
17073 [ + + ]: 6878 : if (dopt->binary_upgrade)
17074 : 952 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17075 : :
17076 : : /* Is it a table or a view? */
17077 [ + + ]: 6878 : if (tbinfo->relkind == RELKIND_VIEW)
17078 : : {
17079 : : PQExpBuffer result;
17080 : :
17081 : : /*
17082 : : * Note: keep this code in sync with the is_view case in dumpRule()
17083 : : */
17084 : :
17085 : 573 : reltypename = "VIEW";
17086 : :
17087 [ + + ]: 573 : if (dopt->binary_upgrade)
17088 : 56 : binary_upgrade_set_pg_class_oids(fout, q,
17089 : 56 : tbinfo->dobj.catId.oid);
17090 : :
17091 : 573 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17092 : :
17093 [ + + ]: 573 : if (tbinfo->dummy_view)
17094 : 10 : result = createDummyViewAsClause(fout, tbinfo);
17095 : : else
17096 : : {
17097 [ + + ]: 563 : if (nonemptyReloptions(tbinfo->reloptions))
17098 : : {
17099 : 63 : appendPQExpBufferStr(q, " WITH (");
17100 : 63 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17101 : 63 : appendPQExpBufferChar(q, ')');
17102 : : }
17103 : 563 : result = createViewAsClause(fout, tbinfo);
17104 : : }
17105 : 573 : appendPQExpBuffer(q, " AS\n%s", result->data);
17106 : 573 : destroyPQExpBuffer(result);
17107 : :
17108 [ + + + - ]: 573 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17109 : 34 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17110 : 573 : appendPQExpBufferStr(q, ";\n");
17111 : : }
17112 [ + + ]: 6305 : else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17113 : : {
17114 : 104 : PQExpBuffer query = createPQExpBuffer();
17115 : : PGresult *res;
17116 : : int len;
17117 : :
17118 : 104 : reltypename = "PROPERTY GRAPH";
17119 : :
17120 [ + + ]: 104 : if (dopt->binary_upgrade)
17121 : 15 : binary_upgrade_set_pg_class_oids(fout, q,
17122 : 15 : tbinfo->dobj.catId.oid);
17123 : :
17124 : 104 : appendPQExpBuffer(query,
17125 : : "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17126 : 104 : tbinfo->dobj.catId.oid);
17127 : :
17128 : 104 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17129 : :
17130 [ - + ]: 104 : if (PQntuples(res) != 1)
17131 : : {
17132 [ # # ]: 0 : if (PQntuples(res) < 1)
17133 : 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17134 : : tbinfo->dobj.name);
17135 : : else
17136 : 0 : pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17137 : : tbinfo->dobj.name);
17138 : : }
17139 : :
17140 : 104 : len = PQgetlength(res, 0, 0);
17141 : :
17142 [ - + ]: 104 : if (len == 0)
17143 : 0 : pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17144 : : tbinfo->dobj.name);
17145 : :
17146 : 104 : appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17147 : :
17148 : 104 : PQclear(res);
17149 : 104 : destroyPQExpBuffer(query);
17150 : :
17151 : 104 : appendPQExpBufferStr(q, ";\n");
17152 : : }
17153 : : else
17154 : : {
17155 : 6201 : char *partkeydef = NULL;
17156 : 6201 : char *ftoptions = NULL;
17157 : 6201 : char *srvname = NULL;
17158 : 6201 : const char *foreign = "";
17159 : :
17160 : : /*
17161 : : * Set reltypename, and collect any relkind-specific data that we
17162 : : * didn't fetch during getTables().
17163 : : */
17164 [ + + + + ]: 6201 : switch (tbinfo->relkind)
17165 : : {
17166 : 604 : case RELKIND_PARTITIONED_TABLE:
17167 : : {
17168 : 604 : PQExpBuffer query = createPQExpBuffer();
17169 : : PGresult *res;
17170 : :
17171 : 604 : reltypename = "TABLE";
17172 : :
17173 : : /* retrieve partition key definition */
17174 : 604 : appendPQExpBuffer(query,
17175 : : "SELECT pg_get_partkeydef('%u')",
17176 : 604 : tbinfo->dobj.catId.oid);
17177 : 604 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17178 : 604 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17179 : 604 : PQclear(res);
17180 : 604 : destroyPQExpBuffer(query);
17181 : 604 : break;
17182 : : }
17183 : 36 : case RELKIND_FOREIGN_TABLE:
17184 : : {
17185 : 36 : PQExpBuffer query = createPQExpBuffer();
17186 : : PGresult *res;
17187 : : int i_srvname;
17188 : : int i_ftoptions;
17189 : :
17190 : 36 : reltypename = "FOREIGN TABLE";
17191 : :
17192 : : /* retrieve name of foreign server and generic options */
17193 : 36 : appendPQExpBuffer(query,
17194 : : "SELECT fs.srvname, "
17195 : : "pg_catalog.array_to_string(ARRAY("
17196 : : "SELECT pg_catalog.quote_ident(option_name) || "
17197 : : "' ' || pg_catalog.quote_literal(option_value) "
17198 : : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17199 : : "ORDER BY option_name"
17200 : : "), E',\n ') AS ftoptions "
17201 : : "FROM pg_catalog.pg_foreign_table ft "
17202 : : "JOIN pg_catalog.pg_foreign_server fs "
17203 : : "ON (fs.oid = ft.ftserver) "
17204 : : "WHERE ft.ftrelid = '%u'",
17205 : 36 : tbinfo->dobj.catId.oid);
17206 : 36 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17207 : 36 : i_srvname = PQfnumber(res, "srvname");
17208 : 36 : i_ftoptions = PQfnumber(res, "ftoptions");
17209 : 36 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17210 : 36 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17211 : 36 : PQclear(res);
17212 : 36 : destroyPQExpBuffer(query);
17213 : :
17214 : 36 : foreign = "FOREIGN ";
17215 : 36 : break;
17216 : : }
17217 : 353 : case RELKIND_MATVIEW:
17218 : 353 : reltypename = "MATERIALIZED VIEW";
17219 : 353 : break;
17220 : 5208 : default:
17221 : 5208 : reltypename = "TABLE";
17222 : 5208 : break;
17223 : : }
17224 : :
17225 : 6201 : numParents = tbinfo->numParents;
17226 : 6201 : parents = tbinfo->parents;
17227 : :
17228 [ + + ]: 6201 : if (dopt->binary_upgrade)
17229 : 881 : binary_upgrade_set_pg_class_oids(fout, q,
17230 : 881 : tbinfo->dobj.catId.oid);
17231 : :
17232 : : /*
17233 : : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17234 : : * ignore it when dumping if it was set in this case.
17235 : : */
17236 : 6201 : appendPQExpBuffer(q, "CREATE %s%s %s",
17237 [ + + ]: 6201 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17238 [ + - ]: 20 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17239 : : "UNLOGGED " : "",
17240 : : reltypename,
17241 : : qualrelname);
17242 : :
17243 : : /*
17244 : : * Attach to type, if reloftype; except in case of a binary upgrade,
17245 : : * we dump the table normally and attach it to the type afterward.
17246 : : */
17247 [ + + + + ]: 6201 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17248 : 24 : appendPQExpBuffer(q, " OF %s",
17249 : 24 : getFormattedTypeName(fout, tbinfo->reloftype,
17250 : : zeroIsError));
17251 : :
17252 [ + + ]: 6201 : if (tbinfo->relkind != RELKIND_MATVIEW)
17253 : : {
17254 : : /* Dump the attributes */
17255 : 5848 : actual_atts = 0;
17256 [ + + ]: 27092 : for (j = 0; j < tbinfo->numatts; j++)
17257 : : {
17258 : : /*
17259 : : * Normally, dump if it's locally defined in this table, and
17260 : : * not dropped. But for binary upgrade, we'll dump all the
17261 : : * columns, and then fix up the dropped and nonlocal cases
17262 : : * below.
17263 : : */
17264 [ + + ]: 21244 : if (shouldPrintColumn(dopt, tbinfo, j))
17265 : : {
17266 : : bool print_default;
17267 : : bool print_notnull;
17268 : :
17269 : : /*
17270 : : * Default value --- suppress if to be printed separately
17271 : : * or not at all.
17272 : : */
17273 : 41370 : print_default = (tbinfo->attrdefs[j] != NULL &&
17274 [ + + + + ]: 21202 : tbinfo->attrdefs[j]->dobj.dump &&
17275 [ + + ]: 1083 : !tbinfo->attrdefs[j]->separate);
17276 : :
17277 : : /*
17278 : : * Not Null constraint --- print it if it is locally
17279 : : * defined, or if binary upgrade. (In the latter case, we
17280 : : * reset conislocal below.)
17281 : : */
17282 [ + + ]: 22624 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17283 [ + + ]: 2505 : (tbinfo->notnull_islocal[j] ||
17284 [ + + ]: 675 : dopt->binary_upgrade ||
17285 [ + + ]: 581 : tbinfo->ispartition));
17286 : :
17287 : : /*
17288 : : * Skip column if fully defined by reloftype, except in
17289 : : * binary upgrade
17290 : : */
17291 [ + + ]: 20119 : if (OidIsValid(tbinfo->reloftype) &&
17292 [ + + + + ]: 50 : !print_default && !print_notnull &&
17293 [ + + ]: 30 : !dopt->binary_upgrade)
17294 : 24 : continue;
17295 : :
17296 : : /* Format properly if not first attr */
17297 [ + + ]: 20095 : if (actual_atts == 0)
17298 : 5461 : appendPQExpBufferStr(q, " (");
17299 : : else
17300 : 14634 : appendPQExpBufferChar(q, ',');
17301 : 20095 : appendPQExpBufferStr(q, "\n ");
17302 : 20095 : actual_atts++;
17303 : :
17304 : : /* Attribute name */
17305 : 20095 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17306 : :
17307 [ + + ]: 20095 : if (tbinfo->attisdropped[j])
17308 : : {
17309 : : /*
17310 : : * ALTER TABLE DROP COLUMN clears
17311 : : * pg_attribute.atttypid, so we will not have gotten a
17312 : : * valid type name; insert INTEGER as a stopgap. We'll
17313 : : * clean things up later.
17314 : : */
17315 : 85 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17316 : : /* and skip to the next column */
17317 : 85 : continue;
17318 : : }
17319 : :
17320 : : /*
17321 : : * Attribute type; print it except when creating a typed
17322 : : * table ('OF type_name'), but in binary-upgrade mode,
17323 : : * print it in that case too.
17324 : : */
17325 [ + + + + ]: 20010 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17326 : : {
17327 : 19994 : appendPQExpBuffer(q, " %s",
17328 : 19994 : tbinfo->atttypnames[j]);
17329 : : }
17330 : :
17331 [ + + ]: 20010 : if (print_default)
17332 : : {
17333 [ + + ]: 949 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17334 : 328 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17335 : 328 : tbinfo->attrdefs[j]->adef_expr);
17336 [ + + ]: 621 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17337 : 230 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17338 : 230 : tbinfo->attrdefs[j]->adef_expr);
17339 : : else
17340 : 391 : appendPQExpBuffer(q, " DEFAULT %s",
17341 : 391 : tbinfo->attrdefs[j]->adef_expr);
17342 : : }
17343 : :
17344 [ + + ]: 20010 : if (print_notnull)
17345 : : {
17346 [ + + ]: 2472 : if (tbinfo->notnull_constrs[j][0] == '\0')
17347 : 1769 : appendPQExpBufferStr(q, " NOT NULL");
17348 : : else
17349 : 703 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17350 : 703 : fmtId(tbinfo->notnull_constrs[j]));
17351 : :
17352 [ + + ]: 2472 : if (tbinfo->notnull_noinh[j])
17353 : 35 : appendPQExpBufferStr(q, " NO INHERIT");
17354 : : }
17355 : :
17356 : : /* Add collation if not default for the type */
17357 [ + + ]: 20010 : if (OidIsValid(tbinfo->attcollation[j]))
17358 : : {
17359 : : CollInfo *coll;
17360 : :
17361 : 216 : coll = findCollationByOid(tbinfo->attcollation[j]);
17362 [ + - ]: 216 : if (coll)
17363 : 216 : appendPQExpBuffer(q, " COLLATE %s",
17364 : 216 : fmtQualifiedDumpable(coll));
17365 : : }
17366 : : }
17367 : :
17368 : : /*
17369 : : * On the other hand, if we choose not to print a column
17370 : : * (likely because it is created by inheritance), but the
17371 : : * column has a locally-defined not-null constraint, we need
17372 : : * to dump the constraint as a standalone object.
17373 : : *
17374 : : * This syntax isn't SQL-conforming, but if you wanted
17375 : : * standard output you wouldn't be creating non-standard
17376 : : * objects to begin with.
17377 : : */
17378 [ + + ]: 21135 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17379 [ + + ]: 1125 : !tbinfo->attisdropped[j] &&
17380 [ + + ]: 756 : tbinfo->notnull_constrs[j] != NULL &&
17381 [ + + ]: 216 : tbinfo->notnull_islocal[j])
17382 : : {
17383 : : /* Format properly if not first attr */
17384 [ + + ]: 94 : if (actual_atts == 0)
17385 : 90 : appendPQExpBufferStr(q, " (");
17386 : : else
17387 : 4 : appendPQExpBufferChar(q, ',');
17388 : 94 : appendPQExpBufferStr(q, "\n ");
17389 : 94 : actual_atts++;
17390 : :
17391 [ + + ]: 94 : if (tbinfo->notnull_constrs[j][0] == '\0')
17392 : 8 : appendPQExpBuffer(q, "NOT NULL %s",
17393 : 8 : fmtId(tbinfo->attnames[j]));
17394 : : else
17395 : 172 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17396 : 86 : tbinfo->notnull_constrs[j],
17397 : 86 : fmtId(tbinfo->attnames[j]));
17398 : :
17399 [ + + ]: 94 : if (tbinfo->notnull_noinh[j])
17400 : 33 : appendPQExpBufferStr(q, " NO INHERIT");
17401 : : }
17402 : : }
17403 : :
17404 : : /*
17405 : : * Add non-inherited CHECK constraints, if any.
17406 : : *
17407 : : * For partitions, we need to include check constraints even if
17408 : : * they're not defined locally, because the ALTER TABLE ATTACH
17409 : : * PARTITION that we'll emit later expects the constraint to be
17410 : : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17411 : : */
17412 [ + + ]: 6441 : for (j = 0; j < tbinfo->ncheck; j++)
17413 : : {
17414 : 593 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17415 : :
17416 [ + + ]: 593 : if (constr->separate ||
17417 [ + + + + ]: 523 : (!constr->conislocal && !tbinfo->ispartition))
17418 : 109 : continue;
17419 : :
17420 [ + + ]: 484 : if (actual_atts == 0)
17421 : 16 : appendPQExpBufferStr(q, " (\n ");
17422 : : else
17423 : 468 : appendPQExpBufferStr(q, ",\n ");
17424 : :
17425 : 484 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17426 : 484 : fmtId(constr->dobj.name));
17427 : 484 : appendPQExpBufferStr(q, constr->condef);
17428 : :
17429 : 484 : actual_atts++;
17430 : : }
17431 : :
17432 [ + + ]: 5848 : if (actual_atts)
17433 : 5567 : appendPQExpBufferStr(q, "\n)");
17434 [ + + - + ]: 281 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17435 : : {
17436 : : /*
17437 : : * No attributes? we must have a parenthesized attribute list,
17438 : : * even though empty, when not using the OF TYPE syntax.
17439 : : */
17440 : 269 : appendPQExpBufferStr(q, " (\n)");
17441 : : }
17442 : :
17443 : : /*
17444 : : * Emit the INHERITS clause (not for partitions), except in
17445 : : * binary-upgrade mode.
17446 : : */
17447 [ + + + + ]: 5848 : if (numParents > 0 && !tbinfo->ispartition &&
17448 [ + + ]: 555 : !dopt->binary_upgrade)
17449 : : {
17450 : 486 : appendPQExpBufferStr(q, "\nINHERITS (");
17451 [ + + ]: 1045 : for (k = 0; k < numParents; k++)
17452 : : {
17453 : 559 : TableInfo *parentRel = parents[k];
17454 : :
17455 [ + + ]: 559 : if (k > 0)
17456 : 73 : appendPQExpBufferStr(q, ", ");
17457 : 559 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17458 : : }
17459 : 486 : appendPQExpBufferChar(q, ')');
17460 : : }
17461 : :
17462 [ + + ]: 5848 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17463 : 604 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17464 : :
17465 [ + + ]: 5848 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17466 : 36 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17467 : : }
17468 : :
17469 [ + + - + ]: 12247 : if (nonemptyReloptions(tbinfo->reloptions) ||
17470 : 6046 : nonemptyReloptions(tbinfo->toast_reloptions))
17471 : : {
17472 : 155 : bool addcomma = false;
17473 : :
17474 : 155 : appendPQExpBufferStr(q, "\nWITH (");
17475 [ + - ]: 155 : if (nonemptyReloptions(tbinfo->reloptions))
17476 : : {
17477 : 155 : addcomma = true;
17478 : 155 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17479 : : }
17480 [ + + ]: 155 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17481 : : {
17482 [ + - ]: 5 : if (addcomma)
17483 : 5 : appendPQExpBufferStr(q, ", ");
17484 : 5 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17485 : : fout);
17486 : : }
17487 : 155 : appendPQExpBufferChar(q, ')');
17488 : : }
17489 : :
17490 : : /* Dump generic options if any */
17491 [ + + + + ]: 6201 : if (ftoptions && ftoptions[0])
17492 : 34 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17493 : :
17494 : : /*
17495 : : * For materialized views, create the AS clause just like a view. At
17496 : : * this point, we always mark the view as not populated.
17497 : : */
17498 [ + + ]: 6201 : if (tbinfo->relkind == RELKIND_MATVIEW)
17499 : : {
17500 : : PQExpBuffer result;
17501 : :
17502 : 353 : result = createViewAsClause(fout, tbinfo);
17503 : 353 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17504 : : result->data);
17505 : 353 : destroyPQExpBuffer(result);
17506 : : }
17507 : : else
17508 : 5848 : appendPQExpBufferStr(q, ";\n");
17509 : :
17510 : : /* Materialized views can depend on extensions */
17511 [ + + ]: 6201 : if (tbinfo->relkind == RELKIND_MATVIEW)
17512 : 353 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17513 : : "pg_catalog.pg_class",
17514 : : "MATERIALIZED VIEW",
17515 : : qualrelname);
17516 : :
17517 : : /*
17518 : : * in binary upgrade mode, update the catalog with any missing values
17519 : : * that might be present.
17520 : : */
17521 [ + + ]: 6201 : if (dopt->binary_upgrade)
17522 : : {
17523 [ + + ]: 4221 : for (j = 0; j < tbinfo->numatts; j++)
17524 : : {
17525 [ + + ]: 3340 : if (tbinfo->attmissingval[j][0] != '\0')
17526 : : {
17527 : 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17528 : 4 : appendPQExpBufferStr(q,
17529 : : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17530 : 4 : appendStringLiteralAH(q, qualrelname, fout);
17531 : 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17532 : 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17533 : 4 : appendPQExpBufferChar(q, ',');
17534 : 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17535 : 4 : appendPQExpBufferStr(q, ");\n\n");
17536 : : }
17537 : : }
17538 : : }
17539 : :
17540 : : /*
17541 : : * To create binary-compatible heap files, we have to ensure the same
17542 : : * physical column order, including dropped columns, as in the
17543 : : * original. Therefore, we create dropped columns above and drop them
17544 : : * here, also updating their attlen/attalign values so that the
17545 : : * dropped column can be skipped properly. (We do not bother with
17546 : : * restoring the original attbyval setting.) Also, inheritance
17547 : : * relationships are set up by doing ALTER TABLE INHERIT rather than
17548 : : * using an INHERITS clause --- the latter would possibly mess up the
17549 : : * column order. That also means we have to take care about setting
17550 : : * attislocal correctly, plus fix up any inherited CHECK constraints.
17551 : : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17552 : : *
17553 : : * We process foreign and partitioned tables here, even though they
17554 : : * lack heap storage, because they can participate in inheritance
17555 : : * relationships and we want this stuff to be consistent across the
17556 : : * inheritance tree. We can exclude indexes, toast tables, sequences
17557 : : * and matviews, even though they have storage, because we don't
17558 : : * support altering or dropping columns in them, nor can they be part
17559 : : * of inheritance trees.
17560 : : */
17561 [ + + ]: 6201 : if (dopt->binary_upgrade &&
17562 [ + + ]: 881 : (tbinfo->relkind == RELKIND_RELATION ||
17563 [ + + ]: 115 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17564 [ + + ]: 114 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17565 : : {
17566 : : bool firstitem;
17567 : : bool firstitem_extra;
17568 : :
17569 : : /*
17570 : : * Drop any dropped columns. Merge the pg_attribute manipulations
17571 : : * into a single SQL command, so that we don't cause repeated
17572 : : * relcache flushes on the target table. Otherwise we risk O(N^2)
17573 : : * relcache bloat while dropping N columns.
17574 : : */
17575 : 864 : resetPQExpBuffer(extra);
17576 : 864 : firstitem = true;
17577 [ + + ]: 4183 : for (j = 0; j < tbinfo->numatts; j++)
17578 : : {
17579 [ + + ]: 3319 : if (tbinfo->attisdropped[j])
17580 : : {
17581 [ + + ]: 85 : if (firstitem)
17582 : : {
17583 : 39 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17584 : : "UPDATE pg_catalog.pg_attribute\n"
17585 : : "SET attlen = v.dlen, "
17586 : : "attalign = v.dalign, "
17587 : : "attbyval = false\n"
17588 : : "FROM (VALUES ");
17589 : 39 : firstitem = false;
17590 : : }
17591 : : else
17592 : 46 : appendPQExpBufferStr(q, ",\n ");
17593 : 85 : appendPQExpBufferChar(q, '(');
17594 : 85 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17595 : 85 : appendPQExpBuffer(q, ", %d, '%c')",
17596 : 85 : tbinfo->attlen[j],
17597 : 85 : tbinfo->attalign[j]);
17598 : : /* The ALTER ... DROP COLUMN commands must come after */
17599 : 85 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17600 : : foreign, qualrelname);
17601 : 85 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17602 : 85 : fmtId(tbinfo->attnames[j]));
17603 : : }
17604 : : }
17605 [ + + ]: 864 : if (!firstitem)
17606 : : {
17607 : 39 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17608 : : "WHERE attrelid = ");
17609 : 39 : appendStringLiteralAH(q, qualrelname, fout);
17610 : 39 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17611 : : " AND attname = v.dname;\n");
17612 : : /* Now we can issue the actual DROP COLUMN commands */
17613 : 39 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17614 : : }
17615 : :
17616 : : /*
17617 : : * Fix up inherited columns. As above, do the pg_attribute
17618 : : * manipulations in a single SQL command.
17619 : : */
17620 : 864 : firstitem = true;
17621 [ + + ]: 4183 : for (j = 0; j < tbinfo->numatts; j++)
17622 : : {
17623 [ + + ]: 3319 : if (!tbinfo->attisdropped[j] &&
17624 [ + + ]: 3234 : !tbinfo->attislocal[j])
17625 : : {
17626 [ + + ]: 650 : if (firstitem)
17627 : : {
17628 : 282 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17629 : 282 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17630 : : "SET attislocal = false\n"
17631 : : "WHERE attrelid = ");
17632 : 282 : appendStringLiteralAH(q, qualrelname, fout);
17633 : 282 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17634 : : " AND attname IN (");
17635 : 282 : firstitem = false;
17636 : : }
17637 : : else
17638 : 368 : appendPQExpBufferStr(q, ", ");
17639 : 650 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17640 : : }
17641 : : }
17642 [ + + ]: 864 : if (!firstitem)
17643 : 282 : appendPQExpBufferStr(q, ");\n");
17644 : :
17645 : : /*
17646 : : * Fix up not-null constraints that come from inheritance. As
17647 : : * above, do the pg_constraint manipulations in a single SQL
17648 : : * command. (Actually, two in special cases, if we're doing an
17649 : : * upgrade from < 18).
17650 : : */
17651 : 864 : firstitem = true;
17652 : 864 : firstitem_extra = true;
17653 : 864 : resetPQExpBuffer(extra);
17654 [ + + ]: 4183 : for (j = 0; j < tbinfo->numatts; j++)
17655 : : {
17656 : : /*
17657 : : * If a not-null constraint comes from inheritance, reset
17658 : : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17659 : : * below. Special hack: in versions < 18, columns with no
17660 : : * local definition need their constraint to be matched by
17661 : : * column number in conkeys instead of by constraint name,
17662 : : * because the latter is not available. (We distinguish the
17663 : : * case because the constraint name is the empty string.)
17664 : : */
17665 [ + + ]: 3319 : if (tbinfo->notnull_constrs[j] != NULL &&
17666 [ + + ]: 333 : !tbinfo->notnull_islocal[j])
17667 : : {
17668 [ + + ]: 94 : if (tbinfo->notnull_constrs[j][0] != '\0')
17669 : : {
17670 [ + + ]: 81 : if (firstitem)
17671 : : {
17672 : 69 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17673 : : "SET conislocal = false\n"
17674 : : "WHERE contype = 'n' AND conrelid = ");
17675 : 69 : appendStringLiteralAH(q, qualrelname, fout);
17676 : 69 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17677 : : "conname IN (");
17678 : 69 : firstitem = false;
17679 : : }
17680 : : else
17681 : 12 : appendPQExpBufferStr(q, ", ");
17682 : 81 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17683 : : }
17684 : : else
17685 : : {
17686 [ + - ]: 13 : if (firstitem_extra)
17687 : : {
17688 : 13 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17689 : : "SET conislocal = false\n"
17690 : : "WHERE contype = 'n' AND conrelid = ");
17691 : 13 : appendStringLiteralAH(extra, qualrelname, fout);
17692 : 13 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17693 : : "conkey IN (");
17694 : 13 : firstitem_extra = false;
17695 : : }
17696 : : else
17697 : 0 : appendPQExpBufferStr(extra, ", ");
17698 : 13 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17699 : : }
17700 : : }
17701 : : }
17702 [ + + ]: 864 : if (!firstitem)
17703 : 69 : appendPQExpBufferStr(q, ");\n");
17704 [ + + ]: 864 : if (!firstitem_extra)
17705 : 13 : appendPQExpBufferStr(extra, ");\n");
17706 : :
17707 [ + + ]: 864 : if (extra->len > 0)
17708 : 13 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17709 : :
17710 : : /*
17711 : : * Add inherited CHECK constraints, if any.
17712 : : *
17713 : : * For partitions, they were already dumped, and conislocal
17714 : : * doesn't need fixing.
17715 : : *
17716 : : * As above, issue only one direct manipulation of pg_constraint.
17717 : : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17718 : : * commands into one as well, refrain for now due to concern about
17719 : : * possible backend memory bloat if there are many such
17720 : : * constraints.
17721 : : */
17722 : 864 : resetPQExpBuffer(extra);
17723 : 864 : firstitem = true;
17724 [ + + ]: 926 : for (k = 0; k < tbinfo->ncheck; k++)
17725 : : {
17726 : 62 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17727 : :
17728 [ + + + + : 62 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
+ + ]
17729 : 60 : continue;
17730 : :
17731 [ + - ]: 2 : if (firstitem)
17732 : 2 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17733 : 2 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17734 : : foreign, qualrelname,
17735 : 2 : fmtId(constr->dobj.name),
17736 : : constr->condef);
17737 : : /* Update pg_constraint after all the ALTER TABLEs */
17738 [ + - ]: 2 : if (firstitem)
17739 : : {
17740 : 2 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17741 : : "SET conislocal = false\n"
17742 : : "WHERE contype = 'c' AND conrelid = ");
17743 : 2 : appendStringLiteralAH(extra, qualrelname, fout);
17744 : 2 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17745 : 2 : appendPQExpBufferStr(extra, " AND conname IN (");
17746 : 2 : firstitem = false;
17747 : : }
17748 : : else
17749 : 0 : appendPQExpBufferStr(extra, ", ");
17750 : 2 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17751 : : }
17752 [ + + ]: 864 : if (!firstitem)
17753 : : {
17754 : 2 : appendPQExpBufferStr(extra, ");\n");
17755 : 2 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17756 : : }
17757 : :
17758 [ + + + + ]: 864 : if (numParents > 0 && !tbinfo->ispartition)
17759 : : {
17760 : 69 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17761 [ + + ]: 149 : for (k = 0; k < numParents; k++)
17762 : : {
17763 : 80 : TableInfo *parentRel = parents[k];
17764 : :
17765 : 80 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17766 : : qualrelname,
17767 : 80 : fmtQualifiedDumpable(parentRel));
17768 : : }
17769 : : }
17770 : :
17771 [ + + ]: 864 : if (OidIsValid(tbinfo->reloftype))
17772 : : {
17773 : 6 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17774 : 6 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17775 : : qualrelname,
17776 : 6 : getFormattedTypeName(fout, tbinfo->reloftype,
17777 : : zeroIsError));
17778 : : }
17779 : : }
17780 : :
17781 : : /*
17782 : : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17783 : : * relminmxid of all vacuumable relations. (While vacuum.c processes
17784 : : * TOAST tables semi-independently, here we see them only as children
17785 : : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17786 : : * child toast table is handled below.)
17787 : : */
17788 [ + + ]: 6201 : if (dopt->binary_upgrade &&
17789 [ + + ]: 881 : (tbinfo->relkind == RELKIND_RELATION ||
17790 [ + + ]: 115 : tbinfo->relkind == RELKIND_MATVIEW))
17791 : : {
17792 : 783 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17793 : 783 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17794 : : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17795 : : "WHERE oid = ",
17796 : 783 : tbinfo->frozenxid, tbinfo->minmxid);
17797 : 783 : appendStringLiteralAH(q, qualrelname, fout);
17798 : 783 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17799 : :
17800 [ + + ]: 783 : if (tbinfo->toast_oid)
17801 : : {
17802 : : /*
17803 : : * The toast table will have the same OID at restore, so we
17804 : : * can safely target it by OID.
17805 : : */
17806 : 296 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17807 : 296 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17808 : : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17809 : : "WHERE oid = '%u';\n",
17810 : 296 : tbinfo->toast_frozenxid,
17811 : 296 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17812 : : }
17813 : : }
17814 : :
17815 : : /*
17816 : : * In binary_upgrade mode, restore matviews' populated status by
17817 : : * poking pg_class directly. This is pretty ugly, but we can't use
17818 : : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17819 : : * matview is not populated even though this matview is; in any case,
17820 : : * we want to transfer the matview's heap storage, not run REFRESH.
17821 : : */
17822 [ + + + + ]: 6201 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17823 [ + + ]: 17 : tbinfo->relispopulated)
17824 : : {
17825 : 15 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17826 : 15 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17827 : : "SET relispopulated = 't'\n"
17828 : : "WHERE oid = ");
17829 : 15 : appendStringLiteralAH(q, qualrelname, fout);
17830 : 15 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17831 : : }
17832 : :
17833 : : /*
17834 : : * Dump additional per-column properties that we can't handle in the
17835 : : * main CREATE TABLE command.
17836 : : */
17837 [ + + ]: 27876 : for (j = 0; j < tbinfo->numatts; j++)
17838 : : {
17839 : : /* None of this applies to dropped columns */
17840 [ + + ]: 21675 : if (tbinfo->attisdropped[j])
17841 : 454 : continue;
17842 : :
17843 : : /*
17844 : : * Dump per-column statistics information. We only issue an ALTER
17845 : : * TABLE statement if the attstattarget entry for this column is
17846 : : * not the default value.
17847 : : */
17848 [ + + ]: 21221 : if (tbinfo->attstattarget[j] >= 0)
17849 : 34 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17850 : : foreign, qualrelname,
17851 : 34 : fmtId(tbinfo->attnames[j]),
17852 : 34 : tbinfo->attstattarget[j]);
17853 : :
17854 : : /*
17855 : : * Dump per-column storage information. The statement is only
17856 : : * dumped if the storage has been changed from the type's default.
17857 : : */
17858 [ + + ]: 21221 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17859 : : {
17860 [ + + - + : 83 : switch (tbinfo->attstorage[j])
- ]
17861 : : {
17862 : 10 : case TYPSTORAGE_PLAIN:
17863 : 10 : storage = "PLAIN";
17864 : 10 : break;
17865 : 39 : case TYPSTORAGE_EXTERNAL:
17866 : 39 : storage = "EXTERNAL";
17867 : 39 : break;
17868 : 0 : case TYPSTORAGE_EXTENDED:
17869 : 0 : storage = "EXTENDED";
17870 : 0 : break;
17871 : 34 : case TYPSTORAGE_MAIN:
17872 : 34 : storage = "MAIN";
17873 : 34 : break;
17874 : 0 : default:
17875 : 0 : storage = NULL;
17876 : : }
17877 : :
17878 : : /*
17879 : : * Only dump the statement if it's a storage type we recognize
17880 : : */
17881 [ + - ]: 83 : if (storage != NULL)
17882 : 83 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17883 : : foreign, qualrelname,
17884 : 83 : fmtId(tbinfo->attnames[j]),
17885 : : storage);
17886 : : }
17887 : :
17888 : : /*
17889 : : * Dump per-column compression, if it's been set.
17890 : : */
17891 [ + + ]: 21221 : if (!dopt->no_toast_compression)
17892 : : {
17893 : : const char *cmname;
17894 : :
17895 [ + + + ]: 21121 : switch (tbinfo->attcompression[j])
17896 : : {
17897 : 73 : case 'p':
17898 : 73 : cmname = "pglz";
17899 : 73 : break;
17900 : 39 : case 'l':
17901 : 39 : cmname = "lz4";
17902 : 39 : break;
17903 : 21009 : default:
17904 : 21009 : cmname = NULL;
17905 : 21009 : break;
17906 : : }
17907 : :
17908 [ + + ]: 21121 : if (cmname != NULL)
17909 : 112 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17910 : : foreign, qualrelname,
17911 : 112 : fmtId(tbinfo->attnames[j]),
17912 : : cmname);
17913 : : }
17914 : :
17915 : : /*
17916 : : * Dump per-column attributes.
17917 : : */
17918 [ + + ]: 21221 : if (tbinfo->attoptions[j][0] != '\0')
17919 : 34 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17920 : : foreign, qualrelname,
17921 : 34 : fmtId(tbinfo->attnames[j]),
17922 : 34 : tbinfo->attoptions[j]);
17923 : :
17924 : : /*
17925 : : * Dump per-column fdw options.
17926 : : */
17927 [ + + ]: 21221 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17928 [ + + ]: 36 : tbinfo->attfdwoptions[j][0] != '\0')
17929 : 34 : appendPQExpBuffer(q,
17930 : : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17931 : : " %s\n"
17932 : : ");\n",
17933 : : qualrelname,
17934 : 34 : fmtId(tbinfo->attnames[j]),
17935 : 34 : tbinfo->attfdwoptions[j]);
17936 : : } /* end loop over columns */
17937 : :
17938 : 6201 : pg_free(partkeydef);
17939 : 6201 : pg_free(ftoptions);
17940 : 6201 : pg_free(srvname);
17941 : : }
17942 : :
17943 : : /*
17944 : : * dump properties we only have ALTER TABLE syntax for
17945 : : */
17946 [ + + ]: 6878 : if ((tbinfo->relkind == RELKIND_RELATION ||
17947 [ + + ]: 1670 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17948 [ + + ]: 1066 : tbinfo->relkind == RELKIND_MATVIEW) &&
17949 [ + + ]: 6165 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17950 : : {
17951 [ + - ]: 207 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17952 : : {
17953 : : /* nothing to do, will be set when the index is dumped */
17954 : : }
17955 [ + - ]: 207 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17956 : : {
17957 : 207 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17958 : : qualrelname);
17959 : : }
17960 [ # # ]: 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17961 : : {
17962 : 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17963 : : qualrelname);
17964 : : }
17965 : : }
17966 : :
17967 [ + + ]: 6878 : if (tbinfo->forcerowsec)
17968 : 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17969 : : qualrelname);
17970 : :
17971 : 6878 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17972 : :
17973 [ + + ]: 6878 : if (dopt->binary_upgrade)
17974 : 952 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17975 : : reltypename, qrelname,
17976 : 952 : tbinfo->dobj.namespace->dobj.name);
17977 : :
17978 [ + - ]: 6878 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17979 : : {
17980 : 6878 : char *tablespace = NULL;
17981 : 6878 : char *tableam = NULL;
17982 : :
17983 : : /*
17984 : : * _selectTablespace() relies on tablespace-enabled objects in the
17985 : : * default tablespace to have a tablespace of "" (empty string) versus
17986 : : * non-tablespace-enabled objects to have a tablespace of NULL.
17987 : : * getTables() sets tbinfo->reltablespace to "" for the default
17988 : : * tablespace (not NULL).
17989 : : */
17990 [ + + + - : 6878 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
+ - + - +
+ + + - +
+ - ]
17991 : 6165 : tablespace = tbinfo->reltablespace;
17992 : :
17993 [ + + + - : 6878 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
+ + ]
17994 [ + + ]: 1317 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17995 : 6165 : tableam = tbinfo->amname;
17996 : :
17997 : 6878 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17998 [ + + ]: 6878 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17999 : : .namespace = tbinfo->dobj.namespace->dobj.name,
18000 : : .tablespace = tablespace,
18001 : : .tableam = tableam,
18002 : : .relkind = tbinfo->relkind,
18003 : : .owner = tbinfo->rolname,
18004 : : .description = reltypename,
18005 : : .section = tbinfo->postponed_def ?
18006 : : SECTION_POST_DATA : SECTION_PRE_DATA,
18007 : : .createStmt = q->data,
18008 : : .dropStmt = delq->data));
18009 : : }
18010 : :
18011 : : /* Dump Table Comments */
18012 [ + + ]: 6878 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18013 : 78 : dumpTableComment(fout, tbinfo, reltypename);
18014 : :
18015 : : /* Dump Table Security Labels */
18016 [ - + ]: 6878 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18017 : 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18018 : :
18019 : : /*
18020 : : * Dump comments for not-null constraints that aren't to be dumped
18021 : : * separately (those are processed by collectComments/dumpComment).
18022 : : */
18023 [ + - + - ]: 6878 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18024 [ + - ]: 6878 : fout->remoteVersion >= 180000)
18025 : : {
18026 : 6878 : PQExpBuffer comment = NULL;
18027 : 6878 : PQExpBuffer tag = NULL;
18028 : :
18029 [ + + ]: 32259 : for (j = 0; j < tbinfo->numatts; j++)
18030 : : {
18031 [ + + ]: 25381 : if (tbinfo->notnull_constrs[j] != NULL &&
18032 [ + + ]: 2721 : tbinfo->notnull_comment[j] != NULL)
18033 : : {
18034 [ + - ]: 44 : if (comment == NULL)
18035 : : {
18036 : 44 : comment = createPQExpBuffer();
18037 : 44 : tag = createPQExpBuffer();
18038 : : }
18039 : : else
18040 : : {
18041 : 0 : resetPQExpBuffer(comment);
18042 : 0 : resetPQExpBuffer(tag);
18043 : : }
18044 : :
18045 : 44 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18046 : 44 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18047 : 44 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18048 : 44 : appendPQExpBufferStr(comment, ";\n");
18049 : :
18050 : 44 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18051 : 44 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18052 : :
18053 : 44 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18054 : 44 : ARCHIVE_OPTS(.tag = tag->data,
18055 : : .namespace = tbinfo->dobj.namespace->dobj.name,
18056 : : .owner = tbinfo->rolname,
18057 : : .description = "COMMENT",
18058 : : .section = SECTION_NONE,
18059 : : .createStmt = comment->data,
18060 : : .deps = &(tbinfo->dobj.dumpId),
18061 : : .nDeps = 1));
18062 : : }
18063 : : }
18064 : :
18065 : 6878 : destroyPQExpBuffer(comment);
18066 : 6878 : destroyPQExpBuffer(tag);
18067 : : }
18068 : :
18069 : : /* Dump comments on inlined table constraints */
18070 [ + + ]: 7471 : for (j = 0; j < tbinfo->ncheck; j++)
18071 : : {
18072 : 593 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18073 : :
18074 [ + + + + ]: 593 : if (constr->separate || !constr->conislocal)
18075 : 254 : continue;
18076 : :
18077 [ + + ]: 339 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18078 : 39 : dumpTableConstraintComment(fout, constr);
18079 : : }
18080 : :
18081 : 6878 : destroyPQExpBuffer(q);
18082 : 6878 : destroyPQExpBuffer(delq);
18083 : 6878 : destroyPQExpBuffer(extra);
18084 : 6878 : pg_free(qrelname);
18085 : 6878 : pg_free(qualrelname);
18086 : 6878 : }
18087 : :
18088 : : /*
18089 : : * dumpTableAttach
18090 : : * write to fout the commands to attach a child partition
18091 : : *
18092 : : * Child partitions are always made by creating them separately
18093 : : * and then using ATTACH PARTITION, rather than using
18094 : : * CREATE TABLE ... PARTITION OF. This is important for preserving
18095 : : * any possible discrepancy in column layout, to allow assigning the
18096 : : * correct tablespace if different, and so that it's possible to restore
18097 : : * a partition without restoring its parent. (You'll get an error from
18098 : : * the ATTACH PARTITION command, but that can be ignored, or skipped
18099 : : * using "pg_restore -L" if you prefer.) The last point motivates
18100 : : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18101 : : * rather than emitting it within the child partition's ArchiveEntry.
18102 : : */
18103 : : static void
18104 : 1452 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18105 : : {
18106 : 1452 : DumpOptions *dopt = fout->dopt;
18107 : : PQExpBuffer q;
18108 : : PGresult *res;
18109 : : char *partbound;
18110 : :
18111 : : /* Do nothing if not dumping schema */
18112 [ + + ]: 1452 : if (!dopt->dumpSchema)
18113 : 57 : return;
18114 : :
18115 : 1395 : q = createPQExpBuffer();
18116 : :
18117 [ + + ]: 1395 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18118 : : {
18119 : : /* Set up query for partbound details */
18120 : 45 : appendPQExpBufferStr(q,
18121 : : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18122 : :
18123 : 45 : appendPQExpBufferStr(q,
18124 : : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18125 : : "FROM pg_class c "
18126 : : "WHERE c.oid = $1");
18127 : :
18128 : 45 : ExecuteSqlStatement(fout, q->data);
18129 : :
18130 : 45 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18131 : : }
18132 : :
18133 : 1395 : printfPQExpBuffer(q,
18134 : : "EXECUTE dumpTableAttach('%u')",
18135 : 1395 : attachinfo->partitionTbl->dobj.catId.oid);
18136 : :
18137 : 1395 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18138 : 1395 : partbound = PQgetvalue(res, 0, 0);
18139 : :
18140 : : /* Perform ALTER TABLE on the parent */
18141 : 1395 : printfPQExpBuffer(q,
18142 : : "ALTER TABLE ONLY %s ",
18143 : 1395 : fmtQualifiedDumpable(attachinfo->parentTbl));
18144 : 1395 : appendPQExpBuffer(q,
18145 : : "ATTACH PARTITION %s %s;\n",
18146 : 1395 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18147 : : partbound);
18148 : :
18149 : : /*
18150 : : * There is no point in creating a drop query as the drop is done by table
18151 : : * drop. (If you think to change this, see also _printTocEntry().)
18152 : : * Although this object doesn't really have ownership as such, set the
18153 : : * owner field anyway to ensure that the command is run by the correct
18154 : : * role at restore time.
18155 : : */
18156 : 1395 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18157 : 1395 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18158 : : .namespace = attachinfo->dobj.namespace->dobj.name,
18159 : : .owner = attachinfo->partitionTbl->rolname,
18160 : : .description = "TABLE ATTACH",
18161 : : .section = SECTION_PRE_DATA,
18162 : : .createStmt = q->data));
18163 : :
18164 : 1395 : PQclear(res);
18165 : 1395 : destroyPQExpBuffer(q);
18166 : : }
18167 : :
18168 : : /*
18169 : : * dumpAttrDef --- dump an attribute's default-value declaration
18170 : : */
18171 : : static void
18172 : 1121 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18173 : : {
18174 : 1121 : DumpOptions *dopt = fout->dopt;
18175 : 1121 : TableInfo *tbinfo = adinfo->adtable;
18176 : 1121 : int adnum = adinfo->adnum;
18177 : : PQExpBuffer q;
18178 : : PQExpBuffer delq;
18179 : : char *qualrelname;
18180 : : char *tag;
18181 : : char *foreign;
18182 : :
18183 : : /* Do nothing if not dumping schema */
18184 [ - + ]: 1121 : if (!dopt->dumpSchema)
18185 : 0 : return;
18186 : :
18187 : : /* Skip if not "separate"; it was dumped in the table's definition */
18188 [ + + ]: 1121 : if (!adinfo->separate)
18189 : 949 : return;
18190 : :
18191 : 172 : q = createPQExpBuffer();
18192 : 172 : delq = createPQExpBuffer();
18193 : :
18194 : 172 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18195 : :
18196 [ - + ]: 172 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18197 : :
18198 : 172 : appendPQExpBuffer(q,
18199 : : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18200 : 172 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18201 : 172 : adinfo->adef_expr);
18202 : :
18203 : 172 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18204 : : foreign, qualrelname,
18205 : 172 : fmtId(tbinfo->attnames[adnum - 1]));
18206 : :
18207 : 172 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18208 : :
18209 [ + - ]: 172 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18210 : 172 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18211 : 172 : ARCHIVE_OPTS(.tag = tag,
18212 : : .namespace = tbinfo->dobj.namespace->dobj.name,
18213 : : .owner = tbinfo->rolname,
18214 : : .description = "DEFAULT",
18215 : : .section = SECTION_PRE_DATA,
18216 : : .createStmt = q->data,
18217 : : .dropStmt = delq->data));
18218 : :
18219 : 172 : pfree(tag);
18220 : 172 : destroyPQExpBuffer(q);
18221 : 172 : destroyPQExpBuffer(delq);
18222 : 172 : pg_free(qualrelname);
18223 : : }
18224 : :
18225 : : /*
18226 : : * getAttrName: extract the correct name for an attribute
18227 : : *
18228 : : * The array tblInfo->attnames[] only provides names of user attributes;
18229 : : * if a system attribute number is supplied, we have to fake it.
18230 : : * We also do a little bit of bounds checking for safety's sake.
18231 : : */
18232 : : static const char *
18233 : 2288 : getAttrName(int attrnum, const TableInfo *tblInfo)
18234 : : {
18235 [ + - + - ]: 2288 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18236 : 2288 : return tblInfo->attnames[attrnum - 1];
18237 [ # # # # : 0 : switch (attrnum)
# # # ]
18238 : : {
18239 : 0 : case SelfItemPointerAttributeNumber:
18240 : 0 : return "ctid";
18241 : 0 : case MinTransactionIdAttributeNumber:
18242 : 0 : return "xmin";
18243 : 0 : case MinCommandIdAttributeNumber:
18244 : 0 : return "cmin";
18245 : 0 : case MaxTransactionIdAttributeNumber:
18246 : 0 : return "xmax";
18247 : 0 : case MaxCommandIdAttributeNumber:
18248 : 0 : return "cmax";
18249 : 0 : case TableOidAttributeNumber:
18250 : 0 : return "tableoid";
18251 : : }
18252 : 0 : pg_fatal("invalid column number %d for table \"%s\"",
18253 : : attrnum, tblInfo->dobj.name);
18254 : : return NULL; /* keep compiler quiet */
18255 : : }
18256 : :
18257 : : /*
18258 : : * dumpIndex
18259 : : * write out to fout a user-defined index
18260 : : */
18261 : : static void
18262 : 2837 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18263 : : {
18264 : 2837 : DumpOptions *dopt = fout->dopt;
18265 : 2837 : TableInfo *tbinfo = indxinfo->indextable;
18266 : 2837 : bool is_constraint = (indxinfo->indexconstraint != 0);
18267 : : PQExpBuffer q;
18268 : : PQExpBuffer delq;
18269 : : char *qindxname;
18270 : : char *qqindxname;
18271 : :
18272 : : /* Do nothing if not dumping schema */
18273 [ + + ]: 2837 : if (!dopt->dumpSchema)
18274 : 128 : return;
18275 : :
18276 : 2709 : q = createPQExpBuffer();
18277 : 2709 : delq = createPQExpBuffer();
18278 : :
18279 : 2709 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18280 : 2709 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18281 : :
18282 : : /*
18283 : : * If there's an associated constraint, don't dump the index per se, but
18284 : : * do dump any comment for it. (This is safe because dependency ordering
18285 : : * will have ensured the constraint is emitted first.) Note that the
18286 : : * emitted comment has to be shown as depending on the constraint, not the
18287 : : * index, in such cases.
18288 : : */
18289 [ + + ]: 2709 : if (!is_constraint)
18290 : : {
18291 : 1082 : char *indstatcols = indxinfo->indstatcols;
18292 : 1082 : char *indstatvals = indxinfo->indstatvals;
18293 : 1082 : char **indstatcolsarray = NULL;
18294 : 1082 : char **indstatvalsarray = NULL;
18295 : 1082 : int nstatcols = 0;
18296 : 1082 : int nstatvals = 0;
18297 : :
18298 [ + + ]: 1082 : if (dopt->binary_upgrade)
18299 : 162 : binary_upgrade_set_pg_class_oids(fout, q,
18300 : 162 : indxinfo->dobj.catId.oid);
18301 : :
18302 : : /* Plain secondary index */
18303 : 1082 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18304 : :
18305 : : /*
18306 : : * Append ALTER TABLE commands as needed to set properties that we
18307 : : * only have ALTER TABLE syntax for. Keep this in sync with the
18308 : : * similar code in dumpConstraint!
18309 : : */
18310 : :
18311 : : /* If the index is clustered, we need to record that. */
18312 [ + + ]: 1082 : if (indxinfo->indisclustered)
18313 : : {
18314 : 5 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18315 : 5 : fmtQualifiedDumpable(tbinfo));
18316 : : /* index name is not qualified in this syntax */
18317 : 5 : appendPQExpBuffer(q, " ON %s;\n",
18318 : : qindxname);
18319 : : }
18320 : :
18321 : : /*
18322 : : * If the index has any statistics on some of its columns, generate
18323 : : * the associated ALTER INDEX queries.
18324 : : */
18325 [ + + - + ]: 1082 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18326 : : {
18327 : : int j;
18328 : :
18329 [ - + ]: 34 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18330 : 0 : pg_fatal("could not parse index statistic columns");
18331 [ - + ]: 34 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18332 : 0 : pg_fatal("could not parse index statistic values");
18333 [ - + ]: 34 : if (nstatcols != nstatvals)
18334 : 0 : pg_fatal("mismatched number of columns and values for index statistics");
18335 : :
18336 [ + + ]: 102 : for (j = 0; j < nstatcols; j++)
18337 : : {
18338 : 68 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18339 : :
18340 : : /*
18341 : : * Note that this is a column number, so no quotes should be
18342 : : * used.
18343 : : */
18344 : 68 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18345 : 68 : indstatcolsarray[j]);
18346 : 68 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18347 : 68 : indstatvalsarray[j]);
18348 : : }
18349 : : }
18350 : :
18351 : : /* Indexes can depend on extensions */
18352 : 1082 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18353 : : "pg_catalog.pg_class",
18354 : : "INDEX", qqindxname);
18355 : :
18356 : : /* If the index defines identity, we need to record that. */
18357 [ - + ]: 1082 : if (indxinfo->indisreplident)
18358 : : {
18359 : 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18360 : 0 : fmtQualifiedDumpable(tbinfo));
18361 : : /* index name is not qualified in this syntax */
18362 : 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18363 : : qindxname);
18364 : : }
18365 : :
18366 : : /*
18367 : : * If this index is a member of a partitioned index, the backend will
18368 : : * not allow us to drop it separately, so don't try. It will go away
18369 : : * automatically when we drop either the index's table or the
18370 : : * partitioned index. (If, in a selective restore with --clean, we
18371 : : * drop neither of those, then this index will not be dropped either.
18372 : : * But that's fine, and even if you think it's not, the backend won't
18373 : : * let us do differently.)
18374 : : */
18375 [ + + ]: 1082 : if (indxinfo->parentidx == 0)
18376 : 892 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18377 : :
18378 [ + - ]: 1082 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18379 : 1082 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18380 : 1082 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18381 : : .namespace = tbinfo->dobj.namespace->dobj.name,
18382 : : .tablespace = indxinfo->tablespace,
18383 : : .owner = tbinfo->rolname,
18384 : : .description = "INDEX",
18385 : : .section = SECTION_POST_DATA,
18386 : : .createStmt = q->data,
18387 : : .dropStmt = delq->data));
18388 : :
18389 : 1082 : free(indstatcolsarray);
18390 : 1082 : free(indstatvalsarray);
18391 : : }
18392 : :
18393 : : /* Dump Index Comments */
18394 [ + + ]: 2709 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18395 [ + + ]: 15 : dumpComment(fout, "INDEX", qindxname,
18396 : 15 : tbinfo->dobj.namespace->dobj.name,
18397 : : tbinfo->rolname,
18398 : : indxinfo->dobj.catId, 0,
18399 : : is_constraint ? indxinfo->indexconstraint :
18400 : : indxinfo->dobj.dumpId);
18401 : :
18402 : 2709 : destroyPQExpBuffer(q);
18403 : 2709 : destroyPQExpBuffer(delq);
18404 : 2709 : pg_free(qindxname);
18405 : 2709 : pg_free(qqindxname);
18406 : : }
18407 : :
18408 : : /*
18409 : : * dumpIndexAttach
18410 : : * write out to fout a partitioned-index attachment clause
18411 : : */
18412 : : static void
18413 : 610 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18414 : : {
18415 : : /* Do nothing if not dumping schema */
18416 [ + + ]: 610 : if (!fout->dopt->dumpSchema)
18417 : 48 : return;
18418 : :
18419 [ + - ]: 562 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18420 : : {
18421 : 562 : PQExpBuffer q = createPQExpBuffer();
18422 : :
18423 : 562 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18424 : 562 : fmtQualifiedDumpable(attachinfo->parentIdx));
18425 : 562 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18426 : 562 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18427 : :
18428 : : /*
18429 : : * There is no need for a dropStmt since the drop is done implicitly
18430 : : * when we drop either the index's table or the partitioned index.
18431 : : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18432 : : * there's no way to do it anyway. (If you think to change this,
18433 : : * consider also what to do with --if-exists.)
18434 : : *
18435 : : * Although this object doesn't really have ownership as such, set the
18436 : : * owner field anyway to ensure that the command is run by the correct
18437 : : * role at restore time.
18438 : : */
18439 : 562 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18440 : 562 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18441 : : .namespace = attachinfo->dobj.namespace->dobj.name,
18442 : : .owner = attachinfo->parentIdx->indextable->rolname,
18443 : : .description = "INDEX ATTACH",
18444 : : .section = SECTION_POST_DATA,
18445 : : .createStmt = q->data));
18446 : :
18447 : 562 : destroyPQExpBuffer(q);
18448 : : }
18449 : : }
18450 : :
18451 : : /*
18452 : : * dumpStatisticsExt
18453 : : * write out to fout an extended statistics object
18454 : : */
18455 : : static void
18456 : 183 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18457 : : {
18458 : 183 : DumpOptions *dopt = fout->dopt;
18459 : : PQExpBuffer q;
18460 : : PQExpBuffer delq;
18461 : : PQExpBuffer query;
18462 : : char *qstatsextname;
18463 : : PGresult *res;
18464 : : char *stxdef;
18465 : :
18466 : : /* Do nothing if not dumping schema */
18467 [ + + ]: 183 : if (!dopt->dumpSchema)
18468 : 28 : return;
18469 : :
18470 : 155 : q = createPQExpBuffer();
18471 : 155 : delq = createPQExpBuffer();
18472 : 155 : query = createPQExpBuffer();
18473 : :
18474 : 155 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18475 : :
18476 : 155 : appendPQExpBuffer(query, "SELECT "
18477 : : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18478 : 155 : statsextinfo->dobj.catId.oid);
18479 : :
18480 : 155 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18481 : :
18482 : 155 : stxdef = PQgetvalue(res, 0, 0);
18483 : :
18484 : : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18485 : 155 : appendPQExpBuffer(q, "%s;\n", stxdef);
18486 : :
18487 : : /*
18488 : : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18489 : : * for this statistics object is not the default value.
18490 : : */
18491 [ + + ]: 155 : if (statsextinfo->stattarget >= 0)
18492 : : {
18493 : 34 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18494 : 34 : fmtQualifiedDumpable(statsextinfo));
18495 : 34 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18496 : 34 : statsextinfo->stattarget);
18497 : : }
18498 : :
18499 : 155 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18500 : 155 : fmtQualifiedDumpable(statsextinfo));
18501 : :
18502 [ + - ]: 155 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18503 : 155 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18504 : 155 : statsextinfo->dobj.dumpId,
18505 : 155 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18506 : : .namespace = statsextinfo->dobj.namespace->dobj.name,
18507 : : .owner = statsextinfo->rolname,
18508 : : .description = "STATISTICS",
18509 : : .section = SECTION_POST_DATA,
18510 : : .createStmt = q->data,
18511 : : .dropStmt = delq->data));
18512 : :
18513 : : /* Dump Statistics Comments */
18514 [ - + ]: 155 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18515 : 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18516 : 0 : statsextinfo->dobj.namespace->dobj.name,
18517 : 0 : statsextinfo->rolname,
18518 : : statsextinfo->dobj.catId, 0,
18519 : 0 : statsextinfo->dobj.dumpId);
18520 : :
18521 : 155 : PQclear(res);
18522 : 155 : destroyPQExpBuffer(q);
18523 : 155 : destroyPQExpBuffer(delq);
18524 : 155 : destroyPQExpBuffer(query);
18525 : 155 : pg_free(qstatsextname);
18526 : : }
18527 : :
18528 : : /*
18529 : : * dumpStatisticsExtStats
18530 : : * write out to fout the stats for an extended statistics object
18531 : : */
18532 : : static void
18533 : 183 : dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
18534 : : {
18535 : 183 : DumpOptions *dopt = fout->dopt;
18536 : : PQExpBuffer query;
18537 : : PGresult *res;
18538 : : int nstats;
18539 : :
18540 : : /* Do nothing if not dumping statistics */
18541 [ + + ]: 183 : if (!dopt->dumpStatistics)
18542 : 40 : return;
18543 : :
18544 [ + + ]: 143 : if (!fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS])
18545 : : {
18546 : 36 : PQExpBuffer pq = createPQExpBuffer();
18547 : :
18548 : : /*---------
18549 : : * Set up query for details about extended statistics objects.
18550 : : *
18551 : : * The query depends on the backend version:
18552 : : * - In v19 and newer versions, query directly the pg_stats_ext*
18553 : : * catalogs.
18554 : : * - In v18 and older versions, ndistinct and dependencies have a
18555 : : * different format that needs translation.
18556 : : * - In v14 and older versions, inherited does not exist.
18557 : : * - In v11 and older versions, there is no pg_stats_ext, hence
18558 : : * the logic joins pg_statistic_ext and pg_namespace.
18559 : : *---------
18560 : : */
18561 : :
18562 : 36 : appendPQExpBufferStr(pq,
18563 : : "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18564 : : "SELECT ");
18565 : :
18566 : : /*
18567 : : * Versions 15 and newer have inherited stats.
18568 : : *
18569 : : * Create this column in all versions because we need to order by it
18570 : : * later.
18571 : : */
18572 [ + - ]: 36 : if (fout->remoteVersion >= 150000)
18573 : 36 : appendPQExpBufferStr(pq, "e.inherited, ");
18574 : : else
18575 : 0 : appendPQExpBufferStr(pq, "false AS inherited, ");
18576 : :
18577 : : /*--------
18578 : : * The ndistinct and dependencies formats changed in v19, so
18579 : : * everything before that needs to be translated.
18580 : : *
18581 : : * The ndistinct translation converts this kind of data:
18582 : : * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18583 : : *
18584 : : * to this:
18585 : : * [ {"attributes": [3,4], "ndistinct": 11},
18586 : : * {"attributes": [3,6], "ndistinct": 11},
18587 : : * {"attributes": [4,6], "ndistinct": 11},
18588 : : * {"attributes": [3,4,6], "ndistinct": 11} ]
18589 : : *
18590 : : * The dependencies translation converts this kind of data:
18591 : : * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18592 : : * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18593 : : * "3, 6 => 4": 1.000000}
18594 : : *
18595 : : * to this:
18596 : : * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18597 : : * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18598 : : * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18599 : : * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18600 : : * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18601 : : *--------
18602 : : */
18603 [ + - ]: 36 : if (fout->remoteVersion >= 190000)
18604 : 36 : appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18605 : : else
18606 : 0 : appendPQExpBufferStr(pq,
18607 : : "( "
18608 : : "SELECT json_agg( "
18609 : : " json_build_object( "
18610 : : " '" PG_NDISTINCT_KEY_ATTRIBUTES "', "
18611 : : " string_to_array(kv.key, ', ')::integer[], "
18612 : : " '" PG_NDISTINCT_KEY_NDISTINCT "', "
18613 : : " kv.value::bigint )) "
18614 : : "FROM json_each_text(e.n_distinct::text::json) AS kv"
18615 : : ") AS n_distinct, "
18616 : : "( "
18617 : : "SELECT json_agg( "
18618 : : " json_build_object( "
18619 : : " '" PG_DEPENDENCIES_KEY_ATTRIBUTES "', "
18620 : : " string_to_array( "
18621 : : " split_part(kv.key, ' => ', 1), "
18622 : : " ', ')::integer[], "
18623 : : " '" PG_DEPENDENCIES_KEY_DEPENDENCY "', "
18624 : : " split_part(kv.key, ' => ', 2)::integer, "
18625 : : " '" PG_DEPENDENCIES_KEY_DEGREE "', "
18626 : : " kv.value::double precision )) "
18627 : : "FROM json_each_text(e.dependencies::text::json) AS kv "
18628 : : ") AS dependencies, ");
18629 : :
18630 : : /* MCV was introduced v13 */
18631 [ + - ]: 36 : if (fout->remoteVersion >= 130000)
18632 : 36 : appendPQExpBufferStr(pq,
18633 : : "e.most_common_vals, e.most_common_freqs, "
18634 : : "e.most_common_base_freqs, ");
18635 : : else
18636 : 0 : appendPQExpBufferStr(pq,
18637 : : "NULL AS most_common_vals, NULL AS most_common_freqs, "
18638 : : "NULL AS most_common_base_freqs, ");
18639 : :
18640 : : /* Expressions were introduced in v14 */
18641 [ + - ]: 36 : if (fout->remoteVersion >= 140000)
18642 : : {
18643 : : /*
18644 : : * There is no ordering column in pg_stats_ext_exprs. However, we
18645 : : * can rely on the unnesting of pg_statistic_ext_data.stxdexpr to
18646 : : * maintain the desired order of expression elements.
18647 : : */
18648 : 36 : appendPQExpBufferStr(pq,
18649 : : "( "
18650 : : "SELECT jsonb_pretty(jsonb_agg("
18651 : : "nullif(j.obj, '{}'::jsonb))) "
18652 : : "FROM pg_stats_ext_exprs AS ee "
18653 : : "CROSS JOIN LATERAL jsonb_strip_nulls("
18654 : : " jsonb_build_object( "
18655 : : " 'null_frac', ee.null_frac::text, "
18656 : : " 'avg_width', ee.avg_width::text, "
18657 : : " 'n_distinct', ee.n_distinct::text, "
18658 : : " 'most_common_vals', ee.most_common_vals::text, "
18659 : : " 'most_common_freqs', ee.most_common_freqs::text, "
18660 : : " 'histogram_bounds', ee.histogram_bounds::text, "
18661 : : " 'correlation', ee.correlation::text, "
18662 : : " 'most_common_elems', ee.most_common_elems::text, "
18663 : : " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18664 : : " 'elem_count_histogram', ee.elem_count_histogram::text");
18665 : :
18666 : : /* These three have been added to pg_stats_ext_exprs in v19. */
18667 [ + - ]: 36 : if (fout->remoteVersion >= 190000)
18668 : 36 : appendPQExpBufferStr(pq,
18669 : : ", "
18670 : : " 'range_length_histogram', ee.range_length_histogram::text, "
18671 : : " 'range_empty_frac', ee.range_empty_frac::text, "
18672 : : " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18673 : :
18674 : 36 : appendPQExpBufferStr(pq,
18675 : : " )) AS j(obj)"
18676 : : "WHERE ee.statistics_schemaname = $1 "
18677 : : "AND ee.statistics_name = $2 ");
18678 : : /* Inherited expressions introduced in v15 */
18679 [ + - ]: 36 : if (fout->remoteVersion >= 150000)
18680 : 36 : appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18681 : :
18682 : 36 : appendPQExpBufferStr(pq, ") AS exprs ");
18683 : : }
18684 : : else
18685 : 0 : appendPQExpBufferStr(pq, "NULL AS exprs ");
18686 : :
18687 : : /* pg_stats_ext introduced in v12 */
18688 [ + - ]: 36 : if (fout->remoteVersion >= 120000)
18689 : 36 : appendPQExpBufferStr(pq,
18690 : : "FROM pg_catalog.pg_stats_ext AS e "
18691 : : "WHERE e.statistics_schemaname = $1 "
18692 : : "AND e.statistics_name = $2 ");
18693 : : else
18694 : 0 : appendPQExpBufferStr(pq,
18695 : : "FROM ( "
18696 : : "SELECT s.stxndistinct AS n_distinct, "
18697 : : " s.stxdependencies AS dependencies "
18698 : : "FROM pg_catalog.pg_statistic_ext AS s "
18699 : : "JOIN pg_catalog.pg_namespace AS n "
18700 : : "ON n.oid = s.stxnamespace "
18701 : : "WHERE n.nspname = $1 "
18702 : : "AND s.stxname = $2 "
18703 : : ") AS e ");
18704 : :
18705 : : /* we always have an inherited column, but it may be a constant */
18706 : 36 : appendPQExpBufferStr(pq, "ORDER BY inherited");
18707 : :
18708 : 36 : ExecuteSqlStatement(fout, pq->data);
18709 : :
18710 : 36 : fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS] = true;
18711 : :
18712 : 36 : destroyPQExpBuffer(pq);
18713 : : }
18714 : :
18715 : 143 : query = createPQExpBuffer();
18716 : :
18717 : 143 : appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18718 : 143 : appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18719 : 143 : appendPQExpBufferStr(query, "::pg_catalog.name, ");
18720 : 143 : appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18721 : 143 : appendPQExpBufferStr(query, "::pg_catalog.name)");
18722 : :
18723 : 143 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18724 : :
18725 : 143 : destroyPQExpBuffer(query);
18726 : :
18727 : 143 : nstats = PQntuples(res);
18728 : :
18729 [ + + ]: 143 : if (nstats > 0)
18730 : : {
18731 : 39 : PQExpBuffer out = createPQExpBuffer();
18732 : :
18733 : 39 : int i_inherited = PQfnumber(res, "inherited");
18734 : 39 : int i_ndistinct = PQfnumber(res, "n_distinct");
18735 : 39 : int i_dependencies = PQfnumber(res, "dependencies");
18736 : 39 : int i_mcv = PQfnumber(res, "most_common_vals");
18737 : 39 : int i_mcf = PQfnumber(res, "most_common_freqs");
18738 : 39 : int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18739 : 39 : int i_exprs = PQfnumber(res, "exprs");
18740 : :
18741 [ + + ]: 78 : for (int i = 0; i < nstats; i++)
18742 : : {
18743 : 39 : TableInfo *tbinfo = statsextinfo->stattable;
18744 : :
18745 [ - + ]: 39 : if (PQgetisnull(res, i, i_inherited))
18746 : 0 : pg_fatal("inherited cannot be NULL");
18747 : :
18748 : 39 : appendPQExpBufferStr(out,
18749 : : "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18750 : 39 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18751 : : fout->remoteVersion);
18752 : :
18753 : : /* Relation information */
18754 : 39 : appendPQExpBufferStr(out, "\t'schemaname', ");
18755 : 39 : appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18756 : 39 : appendPQExpBufferStr(out, ",\n\t'relname', ");
18757 : 39 : appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18758 : :
18759 : : /* Extended statistics information */
18760 : 39 : appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18761 : 39 : appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18762 : 39 : appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18763 : 39 : appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18764 : 39 : appendNamedArgument(out, fout, "inherited", "boolean",
18765 : 39 : PQgetvalue(res, i, i_inherited));
18766 : :
18767 [ + + ]: 39 : if (!PQgetisnull(res, i, i_ndistinct))
18768 : 35 : appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18769 : 35 : PQgetvalue(res, i, i_ndistinct));
18770 : :
18771 [ + + ]: 39 : if (!PQgetisnull(res, i, i_dependencies))
18772 : 36 : appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18773 : 36 : PQgetvalue(res, i, i_dependencies));
18774 : :
18775 [ + + ]: 39 : if (!PQgetisnull(res, i, i_mcv))
18776 : 38 : appendNamedArgument(out, fout, "most_common_vals", "text[]",
18777 : 38 : PQgetvalue(res, i, i_mcv));
18778 : :
18779 [ + + ]: 39 : if (!PQgetisnull(res, i, i_mcf))
18780 : 38 : appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
18781 : 38 : PQgetvalue(res, i, i_mcf));
18782 : :
18783 [ + + ]: 39 : if (!PQgetisnull(res, i, i_mcbf))
18784 : 38 : appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
18785 : 38 : PQgetvalue(res, i, i_mcbf));
18786 : :
18787 [ + + ]: 39 : if (!PQgetisnull(res, i, i_exprs))
18788 : 36 : appendNamedArgument(out, fout, "exprs", "jsonb",
18789 : 36 : PQgetvalue(res, i, i_exprs));
18790 : :
18791 : 39 : appendPQExpBufferStr(out, "\n);\n");
18792 : : }
18793 : :
18794 : 39 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18795 : 39 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18796 : : .namespace = statsextinfo->dobj.namespace->dobj.name,
18797 : : .owner = statsextinfo->rolname,
18798 : : .description = "EXTENDED STATISTICS DATA",
18799 : : .section = SECTION_POST_DATA,
18800 : : .createStmt = out->data,
18801 : : .deps = &statsextinfo->dobj.dumpId,
18802 : : .nDeps = 1));
18803 : 39 : destroyPQExpBuffer(out);
18804 : : }
18805 : 143 : PQclear(res);
18806 : : }
18807 : :
18808 : : /*
18809 : : * dumpConstraint
18810 : : * write out to fout a user-defined constraint
18811 : : */
18812 : : static void
18813 : 2800 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18814 : : {
18815 : 2800 : DumpOptions *dopt = fout->dopt;
18816 : 2800 : TableInfo *tbinfo = coninfo->contable;
18817 : : PQExpBuffer q;
18818 : : PQExpBuffer delq;
18819 : 2800 : char *tag = NULL;
18820 : : char *foreign;
18821 : :
18822 : : /* Do nothing if not dumping schema */
18823 [ + + ]: 2800 : if (!dopt->dumpSchema)
18824 : 110 : return;
18825 : :
18826 : 2690 : q = createPQExpBuffer();
18827 : 2690 : delq = createPQExpBuffer();
18828 : :
18829 : 5202 : foreign = tbinfo &&
18830 [ + + - + ]: 2690 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18831 : :
18832 [ + + ]: 2690 : if (coninfo->contype == 'p' ||
18833 [ + + ]: 1319 : coninfo->contype == 'u' ||
18834 [ + + ]: 1073 : coninfo->contype == 'x')
18835 : 1627 : {
18836 : : /* Index-related constraint */
18837 : : IndxInfo *indxinfo;
18838 : : int k;
18839 : :
18840 : 1627 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18841 : :
18842 [ - + ]: 1627 : if (indxinfo == NULL)
18843 : 0 : pg_fatal("missing index for constraint \"%s\"",
18844 : : coninfo->dobj.name);
18845 : :
18846 [ + + ]: 1627 : if (dopt->binary_upgrade)
18847 : 177 : binary_upgrade_set_pg_class_oids(fout, q,
18848 : : indxinfo->dobj.catId.oid);
18849 : :
18850 : 1627 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18851 : 1627 : fmtQualifiedDumpable(tbinfo));
18852 : 1627 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18853 : 1627 : fmtId(coninfo->dobj.name));
18854 : :
18855 [ + + ]: 1627 : if (coninfo->condef)
18856 : : {
18857 : : /* pg_get_constraintdef should have provided everything */
18858 : 10 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18859 : : }
18860 : : else
18861 : : {
18862 : 1617 : appendPQExpBufferStr(q,
18863 [ + + ]: 1617 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18864 : :
18865 : : /*
18866 : : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18867 : : * indexes. Being able to create this was fixed, but we need to
18868 : : * make the index distinct in order to be able to restore the
18869 : : * dump.
18870 : : */
18871 [ - + - - ]: 1617 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18872 : 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18873 : 1617 : appendPQExpBufferStr(q, " (");
18874 [ + + ]: 3865 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18875 : : {
18876 : 2248 : int indkey = (int) indxinfo->indkeys[k];
18877 : : const char *attname;
18878 : :
18879 [ - + ]: 2248 : if (indkey == InvalidAttrNumber)
18880 : 0 : break;
18881 : 2248 : attname = getAttrName(indkey, tbinfo);
18882 : :
18883 [ + + ]: 2248 : appendPQExpBuffer(q, "%s%s",
18884 : : (k == 0) ? "" : ", ",
18885 : : fmtId(attname));
18886 : : }
18887 [ + + ]: 1617 : if (coninfo->conperiod)
18888 : 113 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18889 : :
18890 [ + + ]: 1617 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18891 : 20 : appendPQExpBufferStr(q, ") INCLUDE (");
18892 : :
18893 [ + + ]: 1657 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18894 : : {
18895 : 40 : int indkey = (int) indxinfo->indkeys[k];
18896 : : const char *attname;
18897 : :
18898 [ - + ]: 40 : if (indkey == InvalidAttrNumber)
18899 : 0 : break;
18900 : 40 : attname = getAttrName(indkey, tbinfo);
18901 : :
18902 : 80 : appendPQExpBuffer(q, "%s%s",
18903 [ + + ]: 40 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18904 : : fmtId(attname));
18905 : : }
18906 : :
18907 : 1617 : appendPQExpBufferChar(q, ')');
18908 : :
18909 [ - + ]: 1617 : if (nonemptyReloptions(indxinfo->indreloptions))
18910 : : {
18911 : 0 : appendPQExpBufferStr(q, " WITH (");
18912 : 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18913 : 0 : appendPQExpBufferChar(q, ')');
18914 : : }
18915 : :
18916 [ + + ]: 1617 : if (coninfo->condeferrable)
18917 : : {
18918 : 25 : appendPQExpBufferStr(q, " DEFERRABLE");
18919 [ + + ]: 25 : if (coninfo->condeferred)
18920 : 15 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18921 : : }
18922 : :
18923 : 1617 : appendPQExpBufferStr(q, ";\n");
18924 : : }
18925 : :
18926 : : /*
18927 : : * Append ALTER TABLE commands as needed to set properties that we
18928 : : * only have ALTER TABLE syntax for. Keep this in sync with the
18929 : : * similar code in dumpIndex!
18930 : : */
18931 : :
18932 : : /* If the index is clustered, we need to record that. */
18933 [ + + ]: 1627 : if (indxinfo->indisclustered)
18934 : : {
18935 : 34 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18936 : 34 : fmtQualifiedDumpable(tbinfo));
18937 : : /* index name is not qualified in this syntax */
18938 : 34 : appendPQExpBuffer(q, " ON %s;\n",
18939 : 34 : fmtId(indxinfo->dobj.name));
18940 : : }
18941 : :
18942 : : /* If the index defines identity, we need to record that. */
18943 [ - + ]: 1627 : if (indxinfo->indisreplident)
18944 : : {
18945 : 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18946 : 0 : fmtQualifiedDumpable(tbinfo));
18947 : : /* index name is not qualified in this syntax */
18948 : 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18949 : 0 : fmtId(indxinfo->dobj.name));
18950 : : }
18951 : :
18952 : : /* Indexes can depend on extensions */
18953 : 1627 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18954 : : "pg_catalog.pg_class", "INDEX",
18955 : 1627 : fmtQualifiedDumpable(indxinfo));
18956 : :
18957 : 1627 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18958 : 1627 : fmtQualifiedDumpable(tbinfo));
18959 : 1627 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18960 : 1627 : fmtId(coninfo->dobj.name));
18961 : :
18962 : 1627 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18963 : :
18964 [ + - ]: 1627 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18965 : 1627 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18966 : 1627 : ARCHIVE_OPTS(.tag = tag,
18967 : : .namespace = tbinfo->dobj.namespace->dobj.name,
18968 : : .tablespace = indxinfo->tablespace,
18969 : : .owner = tbinfo->rolname,
18970 : : .description = "CONSTRAINT",
18971 : : .section = SECTION_POST_DATA,
18972 : : .createStmt = q->data,
18973 : : .dropStmt = delq->data));
18974 : : }
18975 [ + + ]: 1063 : else if (coninfo->contype == 'f')
18976 : : {
18977 : : char *only;
18978 : :
18979 : : /*
18980 : : * Foreign keys on partitioned tables are always declared as
18981 : : * inheriting to partitions; for all other cases, emit them as
18982 : : * applying ONLY directly to the named table, because that's how they
18983 : : * work for regular inherited tables.
18984 : : */
18985 [ + + ]: 223 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18986 : :
18987 : : /*
18988 : : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18989 : : * current table data is not processed
18990 : : */
18991 : 223 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18992 : 223 : only, fmtQualifiedDumpable(tbinfo));
18993 : 223 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18994 : 223 : fmtId(coninfo->dobj.name),
18995 : 223 : coninfo->condef);
18996 : :
18997 : 223 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18998 : 223 : only, fmtQualifiedDumpable(tbinfo));
18999 : 223 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19000 : 223 : fmtId(coninfo->dobj.name));
19001 : :
19002 : 223 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19003 : :
19004 [ + - ]: 223 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19005 : 223 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19006 : 223 : ARCHIVE_OPTS(.tag = tag,
19007 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19008 : : .owner = tbinfo->rolname,
19009 : : .description = "FK CONSTRAINT",
19010 : : .section = SECTION_POST_DATA,
19011 : : .createStmt = q->data,
19012 : : .dropStmt = delq->data));
19013 : : }
19014 [ + + + - : 840 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
+ + ]
19015 : : {
19016 : : /* CHECK or invalid not-null constraint on a table */
19017 : :
19018 : : /* Ignore if not to be dumped separately, or if it was inherited */
19019 [ + + + + ]: 662 : if (coninfo->separate && coninfo->conislocal)
19020 : : {
19021 : : const char *keyword;
19022 : :
19023 [ + + ]: 109 : if (coninfo->contype == 'c')
19024 : 45 : keyword = "CHECK CONSTRAINT";
19025 : : else
19026 : 64 : keyword = "CONSTRAINT";
19027 : :
19028 : : /* not ONLY since we want it to propagate to children */
19029 : 109 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19030 : 109 : fmtQualifiedDumpable(tbinfo));
19031 : 109 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19032 : 109 : fmtId(coninfo->dobj.name),
19033 : 109 : coninfo->condef);
19034 : :
19035 : 109 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19036 : 109 : fmtQualifiedDumpable(tbinfo));
19037 : 109 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19038 : 109 : fmtId(coninfo->dobj.name));
19039 : :
19040 : 109 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19041 : :
19042 [ + - ]: 109 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19043 : 109 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19044 : 109 : ARCHIVE_OPTS(.tag = tag,
19045 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19046 : : .owner = tbinfo->rolname,
19047 : : .description = keyword,
19048 : : .section = SECTION_POST_DATA,
19049 : : .createStmt = q->data,
19050 : : .dropStmt = delq->data));
19051 : : }
19052 : : }
19053 [ + - ]: 178 : else if (tbinfo == NULL)
19054 : : {
19055 : : /* CHECK, NOT NULL constraint on a domain */
19056 : 178 : TypeInfo *tyinfo = coninfo->condomain;
19057 : :
19058 : : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19059 : :
19060 : : /* Ignore if not to be dumped separately */
19061 [ + + ]: 178 : if (coninfo->separate)
19062 : : {
19063 : : const char *keyword;
19064 : :
19065 [ + - ]: 5 : if (coninfo->contype == 'c')
19066 : 5 : keyword = "CHECK CONSTRAINT";
19067 : : else
19068 : 0 : keyword = "CONSTRAINT";
19069 : :
19070 : 5 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19071 : 5 : fmtQualifiedDumpable(tyinfo));
19072 : 5 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19073 : 5 : fmtId(coninfo->dobj.name),
19074 : 5 : coninfo->condef);
19075 : :
19076 : 5 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19077 : 5 : fmtQualifiedDumpable(tyinfo));
19078 : 5 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19079 : 5 : fmtId(coninfo->dobj.name));
19080 : :
19081 : 5 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19082 : :
19083 [ + - ]: 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19084 : 5 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19085 : 5 : ARCHIVE_OPTS(.tag = tag,
19086 : : .namespace = tyinfo->dobj.namespace->dobj.name,
19087 : : .owner = tyinfo->rolname,
19088 : : .description = keyword,
19089 : : .section = SECTION_POST_DATA,
19090 : : .createStmt = q->data,
19091 : : .dropStmt = delq->data));
19092 : :
19093 [ + - ]: 5 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19094 : : {
19095 : 5 : PQExpBuffer conprefix = createPQExpBuffer();
19096 : 5 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19097 : :
19098 : 5 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19099 : 5 : fmtId(coninfo->dobj.name));
19100 : :
19101 : 5 : dumpComment(fout, conprefix->data, qtypname,
19102 : 5 : tyinfo->dobj.namespace->dobj.name,
19103 : : tyinfo->rolname,
19104 : 5 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19105 : 5 : destroyPQExpBuffer(conprefix);
19106 : 5 : pg_free(qtypname);
19107 : : }
19108 : : }
19109 : : }
19110 : : else
19111 : : {
19112 : 0 : pg_fatal("unrecognized constraint type: %c",
19113 : : coninfo->contype);
19114 : : }
19115 : :
19116 : : /* Dump Constraint Comments --- only works for table constraints */
19117 [ + + + + ]: 2690 : if (tbinfo && coninfo->separate &&
19118 [ + + ]: 1989 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19119 : 49 : dumpTableConstraintComment(fout, coninfo);
19120 : :
19121 : 2690 : pfree(tag);
19122 : 2690 : destroyPQExpBuffer(q);
19123 : 2690 : destroyPQExpBuffer(delq);
19124 : : }
19125 : :
19126 : : /*
19127 : : * dumpTableConstraintComment --- dump a constraint's comment if any
19128 : : *
19129 : : * This is split out because we need the function in two different places
19130 : : * depending on whether the constraint is dumped as part of CREATE TABLE
19131 : : * or as a separate ALTER command.
19132 : : */
19133 : : static void
19134 : 88 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
19135 : : {
19136 : 88 : TableInfo *tbinfo = coninfo->contable;
19137 : 88 : PQExpBuffer conprefix = createPQExpBuffer();
19138 : : char *qtabname;
19139 : :
19140 : 88 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19141 : :
19142 : 88 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19143 : 88 : fmtId(coninfo->dobj.name));
19144 : :
19145 [ + - ]: 88 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19146 : 88 : dumpComment(fout, conprefix->data, qtabname,
19147 : 88 : tbinfo->dobj.namespace->dobj.name,
19148 : : tbinfo->rolname,
19149 : : coninfo->dobj.catId, 0,
19150 [ + + ]: 88 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19151 : :
19152 : 88 : destroyPQExpBuffer(conprefix);
19153 : 88 : pg_free(qtabname);
19154 : 88 : }
19155 : :
19156 : : static inline SeqType
19157 : 647 : parse_sequence_type(const char *name)
19158 : : {
19159 [ + - ]: 1443 : for (size_t i = 0; i < lengthof(SeqTypeNames); i++)
19160 : : {
19161 [ + + ]: 1443 : if (strcmp(SeqTypeNames[i], name) == 0)
19162 : 647 : return (SeqType) i;
19163 : : }
19164 : :
19165 : 0 : pg_fatal("unrecognized sequence type: %s", name);
19166 : : return (SeqType) 0; /* keep compiler quiet */
19167 : : }
19168 : :
19169 : : /*
19170 : : * bsearch() comparator for SequenceItem
19171 : : */
19172 : : static int
19173 : 2976 : SequenceItemCmp(const void *p1, const void *p2)
19174 : : {
19175 : 2976 : SequenceItem v1 = *((const SequenceItem *) p1);
19176 : 2976 : SequenceItem v2 = *((const SequenceItem *) p2);
19177 : :
19178 : 2976 : return pg_cmp_u32(v1.oid, v2.oid);
19179 : : }
19180 : :
19181 : : /*
19182 : : * collectSequences
19183 : : *
19184 : : * Construct a table of sequence information. This table is sorted by OID for
19185 : : * speed in lookup.
19186 : : */
19187 : : static void
19188 : 191 : collectSequences(Archive *fout)
19189 : : {
19190 : : PGresult *res;
19191 : : const char *query;
19192 : :
19193 : : /*
19194 : : * Since version 18, we can gather the sequence data in this query with
19195 : : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19196 : : */
19197 [ + - ]: 191 : if (fout->remoteVersion < 180000 ||
19198 [ + + + + ]: 191 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
19199 : 9 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19200 : : "seqstart, seqincrement, "
19201 : : "seqmax, seqmin, "
19202 : : "seqcache, seqcycle, "
19203 : : "NULL, 'f' "
19204 : : "FROM pg_catalog.pg_sequence "
19205 : : "ORDER BY seqrelid";
19206 : : else
19207 : 182 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19208 : : "seqstart, seqincrement, "
19209 : : "seqmax, seqmin, "
19210 : : "seqcache, seqcycle, "
19211 : : "last_value, is_called "
19212 : : "FROM pg_catalog.pg_sequence, "
19213 : : "pg_get_sequence_data(seqrelid) "
19214 : : "ORDER BY seqrelid;";
19215 : :
19216 : 191 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19217 : :
19218 : 191 : nsequences = PQntuples(res);
19219 : 191 : sequences = pg_malloc_array(SequenceItem, nsequences);
19220 : :
19221 [ + + ]: 838 : for (int i = 0; i < nsequences; i++)
19222 : : {
19223 : 647 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19224 : 647 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
19225 : 647 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19226 : 647 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19227 : 647 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19228 : 647 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19229 : 647 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19230 : 647 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19231 : 647 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19232 : 647 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19233 [ + + - + ]: 647 : sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19234 : : }
19235 : :
19236 : 191 : PQclear(res);
19237 : 191 : }
19238 : :
19239 : : /*
19240 : : * dumpSequence
19241 : : * write the declaration (not data) of one user-defined sequence
19242 : : */
19243 : : static void
19244 : 381 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
19245 : : {
19246 : 381 : DumpOptions *dopt = fout->dopt;
19247 : : SequenceItem *seq;
19248 : : bool is_ascending;
19249 : : int64 default_minv,
19250 : : default_maxv;
19251 : 381 : PQExpBuffer query = createPQExpBuffer();
19252 : 381 : PQExpBuffer delqry = createPQExpBuffer();
19253 : : char *qseqname;
19254 : 381 : TableInfo *owning_tab = NULL;
19255 : 381 : SequenceItem key = {0};
19256 : :
19257 : 381 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19258 : :
19259 : : /*
19260 : : * The sequence information is gathered in a sorted table before any calls
19261 : : * to dumpSequence(). See collectSequences() for more information.
19262 : : */
19263 : : Assert(sequences);
19264 : :
19265 : 381 : key.oid = tbinfo->dobj.catId.oid;
19266 : 381 : seq = bsearch(&key, sequences, nsequences,
19267 : : sizeof(SequenceItem), SequenceItemCmp);
19268 : :
19269 : : /* Calculate default limits for a sequence of this type */
19270 : 381 : is_ascending = (seq->incby >= 0);
19271 [ + + ]: 381 : if (seq->seqtype == SEQTYPE_SMALLINT)
19272 : : {
19273 [ + + ]: 25 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19274 [ + + ]: 25 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19275 : : }
19276 [ + + ]: 356 : else if (seq->seqtype == SEQTYPE_INTEGER)
19277 : : {
19278 [ + + ]: 290 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19279 [ + + ]: 290 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19280 : : }
19281 [ + - ]: 66 : else if (seq->seqtype == SEQTYPE_BIGINT)
19282 : : {
19283 [ + + ]: 66 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19284 [ + + ]: 66 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19285 : : }
19286 : : else
19287 : : {
19288 : 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19289 : : default_minv = default_maxv = 0; /* keep compiler quiet */
19290 : : }
19291 : :
19292 : : /*
19293 : : * Identity sequences are not to be dropped separately.
19294 : : */
19295 [ + + ]: 381 : if (!tbinfo->is_identity_sequence)
19296 : : {
19297 : 237 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19298 : 237 : fmtQualifiedDumpable(tbinfo));
19299 : : }
19300 : :
19301 : 381 : resetPQExpBuffer(query);
19302 : :
19303 [ + + ]: 381 : if (dopt->binary_upgrade)
19304 : : {
19305 : 66 : binary_upgrade_set_pg_class_oids(fout, query,
19306 : 66 : tbinfo->dobj.catId.oid);
19307 : :
19308 : : /*
19309 : : * In older PG versions a sequence will have a pg_type entry, but v14
19310 : : * and up don't use that, so don't attempt to preserve the type OID.
19311 : : */
19312 : : }
19313 : :
19314 [ + + ]: 381 : if (tbinfo->is_identity_sequence)
19315 : : {
19316 : 144 : owning_tab = findTableByOid(tbinfo->owning_tab);
19317 : :
19318 : 144 : appendPQExpBuffer(query,
19319 : : "ALTER TABLE %s ",
19320 : 144 : fmtQualifiedDumpable(owning_tab));
19321 : 144 : appendPQExpBuffer(query,
19322 : : "ALTER COLUMN %s ADD GENERATED ",
19323 : 144 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19324 [ + + ]: 144 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19325 : 104 : appendPQExpBufferStr(query, "ALWAYS");
19326 [ + - ]: 40 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19327 : 40 : appendPQExpBufferStr(query, "BY DEFAULT");
19328 : 144 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19329 : 144 : fmtQualifiedDumpable(tbinfo));
19330 : :
19331 : : /*
19332 : : * Emit persistence option only if it's different from the owning
19333 : : * table's. This avoids using this new syntax unnecessarily.
19334 : : */
19335 [ + + ]: 144 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19336 : 10 : appendPQExpBuffer(query, " %s\n",
19337 [ + + ]: 10 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19338 : : "UNLOGGED" : "LOGGED");
19339 : : }
19340 : : else
19341 : : {
19342 : 237 : appendPQExpBuffer(query,
19343 : : "CREATE %sSEQUENCE %s\n",
19344 [ + + ]: 237 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19345 : : "UNLOGGED " : "",
19346 : 237 : fmtQualifiedDumpable(tbinfo));
19347 : :
19348 [ + + ]: 237 : if (seq->seqtype != SEQTYPE_BIGINT)
19349 : 186 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19350 : : }
19351 : :
19352 : 381 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19353 : :
19354 : 381 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19355 : :
19356 [ + + ]: 381 : if (seq->minv != default_minv)
19357 : 15 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19358 : : else
19359 : 366 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19360 : :
19361 [ + + ]: 381 : if (seq->maxv != default_maxv)
19362 : 15 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19363 : : else
19364 : 366 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19365 : :
19366 : 381 : appendPQExpBuffer(query,
19367 : : " CACHE " INT64_FORMAT "%s",
19368 [ + + ]: 381 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19369 : :
19370 [ + + ]: 381 : if (tbinfo->is_identity_sequence)
19371 : 144 : appendPQExpBufferStr(query, "\n);\n");
19372 : : else
19373 : 237 : appendPQExpBufferStr(query, ";\n");
19374 : :
19375 : : /* binary_upgrade: no need to clear TOAST table oid */
19376 : :
19377 [ + + ]: 381 : if (dopt->binary_upgrade)
19378 : 66 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19379 : : "SEQUENCE", qseqname,
19380 : 66 : tbinfo->dobj.namespace->dobj.name);
19381 : :
19382 [ + - ]: 381 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19383 : 381 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19384 : 381 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19385 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19386 : : .owner = tbinfo->rolname,
19387 : : .description = "SEQUENCE",
19388 : : .section = SECTION_PRE_DATA,
19389 : : .createStmt = query->data,
19390 : : .dropStmt = delqry->data));
19391 : :
19392 : : /*
19393 : : * If the sequence is owned by a table column, emit the ALTER for it as a
19394 : : * separate TOC entry immediately following the sequence's own entry. It's
19395 : : * OK to do this rather than using full sorting logic, because the
19396 : : * dependency that tells us it's owned will have forced the table to be
19397 : : * created first. We can't just include the ALTER in the TOC entry
19398 : : * because it will fail if we haven't reassigned the sequence owner to
19399 : : * match the table's owner.
19400 : : *
19401 : : * We need not schema-qualify the table reference because both sequence
19402 : : * and table must be in the same schema.
19403 : : */
19404 [ + + + + ]: 381 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19405 : : {
19406 : 141 : owning_tab = findTableByOid(tbinfo->owning_tab);
19407 : :
19408 [ - + ]: 141 : if (owning_tab == NULL)
19409 : 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19410 : : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19411 : :
19412 [ + + ]: 141 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19413 : : {
19414 : 139 : resetPQExpBuffer(query);
19415 : 139 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19416 : 139 : fmtQualifiedDumpable(tbinfo));
19417 : 139 : appendPQExpBuffer(query, " OWNED BY %s",
19418 : 139 : fmtQualifiedDumpable(owning_tab));
19419 : 139 : appendPQExpBuffer(query, ".%s;\n",
19420 : 139 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19421 : :
19422 [ + - ]: 139 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19423 : 139 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19424 : 139 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19425 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19426 : : .owner = tbinfo->rolname,
19427 : : .description = "SEQUENCE OWNED BY",
19428 : : .section = SECTION_PRE_DATA,
19429 : : .createStmt = query->data,
19430 : : .deps = &(tbinfo->dobj.dumpId),
19431 : : .nDeps = 1));
19432 : : }
19433 : : }
19434 : :
19435 : : /* Dump Sequence Comments and Security Labels */
19436 [ - + ]: 381 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19437 : 0 : dumpComment(fout, "SEQUENCE", qseqname,
19438 : 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19439 : 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19440 : :
19441 [ - + ]: 381 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19442 : 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19443 : 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19444 : 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19445 : :
19446 : 381 : destroyPQExpBuffer(query);
19447 : 381 : destroyPQExpBuffer(delqry);
19448 : 381 : pg_free(qseqname);
19449 : 381 : }
19450 : :
19451 : : /*
19452 : : * dumpSequenceData
19453 : : * write the data of one user-defined sequence
19454 : : */
19455 : : static void
19456 : 399 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19457 : : {
19458 : 399 : TableInfo *tbinfo = tdinfo->tdtable;
19459 : : int64 last;
19460 : : bool called;
19461 : : PQExpBuffer query;
19462 : :
19463 : : /* needn't bother if not dumping sequence data */
19464 [ + + + + ]: 399 : if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19465 : 1 : return;
19466 : :
19467 : 398 : query = createPQExpBuffer();
19468 : :
19469 : : /*
19470 : : * For versions >= 18, the sequence information is gathered in the sorted
19471 : : * array before any calls to dumpSequenceData(). See collectSequences()
19472 : : * for more information.
19473 : : *
19474 : : * For older versions, we have to query the sequence relations
19475 : : * individually.
19476 : : */
19477 [ - + ]: 398 : if (fout->remoteVersion < 180000)
19478 : : {
19479 : : PGresult *res;
19480 : :
19481 : 0 : appendPQExpBuffer(query,
19482 : : "SELECT last_value, is_called FROM %s",
19483 : 0 : fmtQualifiedDumpable(tbinfo));
19484 : :
19485 : 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19486 : :
19487 [ # # ]: 0 : if (PQntuples(res) != 1)
19488 : 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19489 : : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19490 : : PQntuples(res)),
19491 : : tbinfo->dobj.name, PQntuples(res));
19492 : :
19493 : 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19494 : 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19495 : :
19496 : 0 : PQclear(res);
19497 : : }
19498 : : else
19499 : : {
19500 : 398 : SequenceItem key = {0};
19501 : : SequenceItem *entry;
19502 : :
19503 : : Assert(sequences);
19504 : : Assert(tbinfo->dobj.catId.oid);
19505 : :
19506 : 398 : key.oid = tbinfo->dobj.catId.oid;
19507 : 398 : entry = bsearch(&key, sequences, nsequences,
19508 : : sizeof(SequenceItem), SequenceItemCmp);
19509 : :
19510 [ - + ]: 398 : if (entry->null_seqtuple)
19511 : 0 : pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19512 : : "SELECT privilege on the sequence or the sequence may "
19513 : : "have been concurrently dropped",
19514 : : tbinfo->dobj.name);
19515 : :
19516 : 398 : last = entry->last_value;
19517 : 398 : called = entry->is_called;
19518 : : }
19519 : :
19520 : 398 : resetPQExpBuffer(query);
19521 : 398 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19522 : 398 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19523 [ + + ]: 398 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19524 : : last, (called ? "true" : "false"));
19525 : :
19526 [ + - ]: 398 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19527 : 398 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19528 : 398 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19529 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19530 : : .owner = tbinfo->rolname,
19531 : : .description = "SEQUENCE SET",
19532 : : .section = SECTION_DATA,
19533 : : .createStmt = query->data,
19534 : : .deps = &(tbinfo->dobj.dumpId),
19535 : : .nDeps = 1));
19536 : :
19537 : 398 : destroyPQExpBuffer(query);
19538 : : }
19539 : :
19540 : : /*
19541 : : * dumpTrigger
19542 : : * write the declaration of one user-defined table trigger
19543 : : */
19544 : : static void
19545 : 535 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19546 : : {
19547 : 535 : DumpOptions *dopt = fout->dopt;
19548 : 535 : TableInfo *tbinfo = tginfo->tgtable;
19549 : : PQExpBuffer query;
19550 : : PQExpBuffer delqry;
19551 : : PQExpBuffer trigprefix;
19552 : : PQExpBuffer trigidentity;
19553 : : char *qtabname;
19554 : : char *tag;
19555 : :
19556 : : /* Do nothing if not dumping schema */
19557 [ + + ]: 535 : if (!dopt->dumpSchema)
19558 : 33 : return;
19559 : :
19560 : 502 : query = createPQExpBuffer();
19561 : 502 : delqry = createPQExpBuffer();
19562 : 502 : trigprefix = createPQExpBuffer();
19563 : 502 : trigidentity = createPQExpBuffer();
19564 : :
19565 : 502 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19566 : :
19567 : 502 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19568 : 502 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19569 : :
19570 : 502 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19571 : 502 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19572 : :
19573 : : /* Triggers can depend on extensions */
19574 : 502 : append_depends_on_extension(fout, query, &tginfo->dobj,
19575 : : "pg_catalog.pg_trigger", "TRIGGER",
19576 : 502 : trigidentity->data);
19577 : :
19578 [ + + ]: 502 : if (tginfo->tgispartition)
19579 : : {
19580 : : Assert(tbinfo->ispartition);
19581 : :
19582 : : /*
19583 : : * Partition triggers only appear here because their 'tgenabled' flag
19584 : : * differs from its parent's. The trigger is created already, so
19585 : : * remove the CREATE and replace it with an ALTER. (Clear out the
19586 : : * DROP query too, so that pg_dump --create does not cause errors.)
19587 : : */
19588 : 115 : resetPQExpBuffer(query);
19589 : 115 : resetPQExpBuffer(delqry);
19590 : 115 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19591 [ - + ]: 115 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19592 : 115 : fmtQualifiedDumpable(tbinfo));
19593 [ + - + + : 115 : switch (tginfo->tgenabled)
- ]
19594 : : {
19595 : 40 : case 'f':
19596 : : case 'D':
19597 : 40 : appendPQExpBufferStr(query, "DISABLE");
19598 : 40 : break;
19599 : 0 : case 't':
19600 : : case 'O':
19601 : 0 : appendPQExpBufferStr(query, "ENABLE");
19602 : 0 : break;
19603 : 35 : case 'R':
19604 : 35 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19605 : 35 : break;
19606 : 40 : case 'A':
19607 : 40 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19608 : 40 : break;
19609 : : }
19610 : 115 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19611 : 115 : fmtId(tginfo->dobj.name));
19612 : : }
19613 [ + - - + ]: 387 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19614 : : {
19615 : 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19616 [ # # ]: 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19617 : 0 : fmtQualifiedDumpable(tbinfo));
19618 [ # # # # ]: 0 : switch (tginfo->tgenabled)
19619 : : {
19620 : 0 : case 'D':
19621 : : case 'f':
19622 : 0 : appendPQExpBufferStr(query, "DISABLE");
19623 : 0 : break;
19624 : 0 : case 'A':
19625 : 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19626 : 0 : break;
19627 : 0 : case 'R':
19628 : 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19629 : 0 : break;
19630 : 0 : default:
19631 : 0 : appendPQExpBufferStr(query, "ENABLE");
19632 : 0 : break;
19633 : : }
19634 : 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19635 : 0 : fmtId(tginfo->dobj.name));
19636 : : }
19637 : :
19638 : 502 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19639 : 502 : fmtId(tginfo->dobj.name));
19640 : :
19641 : 502 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19642 : :
19643 [ + - ]: 502 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19644 : 502 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19645 : 502 : ARCHIVE_OPTS(.tag = tag,
19646 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19647 : : .owner = tbinfo->rolname,
19648 : : .description = "TRIGGER",
19649 : : .section = SECTION_POST_DATA,
19650 : : .createStmt = query->data,
19651 : : .dropStmt = delqry->data));
19652 : :
19653 [ - + ]: 502 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19654 : 0 : dumpComment(fout, trigprefix->data, qtabname,
19655 : 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19656 : 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19657 : :
19658 : 502 : pfree(tag);
19659 : 502 : destroyPQExpBuffer(query);
19660 : 502 : destroyPQExpBuffer(delqry);
19661 : 502 : destroyPQExpBuffer(trigprefix);
19662 : 502 : destroyPQExpBuffer(trigidentity);
19663 : 502 : pg_free(qtabname);
19664 : : }
19665 : :
19666 : : /*
19667 : : * dumpEventTrigger
19668 : : * write the declaration of one user-defined event trigger
19669 : : */
19670 : : static void
19671 : 44 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19672 : : {
19673 : 44 : DumpOptions *dopt = fout->dopt;
19674 : : PQExpBuffer query;
19675 : : PQExpBuffer delqry;
19676 : : char *qevtname;
19677 : :
19678 : : /* Do nothing if not dumping schema */
19679 [ + + ]: 44 : if (!dopt->dumpSchema)
19680 : 6 : return;
19681 : :
19682 : 38 : query = createPQExpBuffer();
19683 : 38 : delqry = createPQExpBuffer();
19684 : :
19685 : 38 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19686 : :
19687 : 38 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19688 : 38 : appendPQExpBufferStr(query, qevtname);
19689 : 38 : appendPQExpBufferStr(query, " ON ");
19690 : 38 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19691 : :
19692 [ + + ]: 38 : if (strcmp("", evtinfo->evttags) != 0)
19693 : : {
19694 : 5 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19695 : 5 : appendPQExpBufferStr(query, evtinfo->evttags);
19696 : 5 : appendPQExpBufferChar(query, ')');
19697 : : }
19698 : :
19699 : 38 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19700 : 38 : appendPQExpBufferStr(query, evtinfo->evtfname);
19701 : 38 : appendPQExpBufferStr(query, "();\n");
19702 : :
19703 [ - + ]: 38 : if (evtinfo->evtenabled != 'O')
19704 : : {
19705 : 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19706 : : qevtname);
19707 [ # # # # ]: 0 : switch (evtinfo->evtenabled)
19708 : : {
19709 : 0 : case 'D':
19710 : 0 : appendPQExpBufferStr(query, "DISABLE");
19711 : 0 : break;
19712 : 0 : case 'A':
19713 : 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19714 : 0 : break;
19715 : 0 : case 'R':
19716 : 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19717 : 0 : break;
19718 : 0 : default:
19719 : 0 : appendPQExpBufferStr(query, "ENABLE");
19720 : 0 : break;
19721 : : }
19722 : 0 : appendPQExpBufferStr(query, ";\n");
19723 : : }
19724 : :
19725 : 38 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19726 : : qevtname);
19727 : :
19728 [ + + ]: 38 : if (dopt->binary_upgrade)
19729 : 2 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19730 : : "EVENT TRIGGER", qevtname, NULL);
19731 : :
19732 [ + - ]: 38 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19733 : 38 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19734 : 38 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19735 : : .owner = evtinfo->evtowner,
19736 : : .description = "EVENT TRIGGER",
19737 : : .section = SECTION_POST_DATA,
19738 : : .createStmt = query->data,
19739 : : .dropStmt = delqry->data));
19740 : :
19741 [ - + ]: 38 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19742 : 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19743 : 0 : NULL, evtinfo->evtowner,
19744 : 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19745 : :
19746 [ - + ]: 38 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19747 : 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19748 : 0 : NULL, evtinfo->evtowner,
19749 : 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19750 : :
19751 : 38 : destroyPQExpBuffer(query);
19752 : 38 : destroyPQExpBuffer(delqry);
19753 : 38 : pg_free(qevtname);
19754 : : }
19755 : :
19756 : : /*
19757 : : * dumpRule
19758 : : * Dump a rule
19759 : : */
19760 : : static void
19761 : 1197 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19762 : : {
19763 : 1197 : DumpOptions *dopt = fout->dopt;
19764 : 1197 : TableInfo *tbinfo = rinfo->ruletable;
19765 : : bool is_view;
19766 : : PQExpBuffer query;
19767 : : PQExpBuffer cmd;
19768 : : PQExpBuffer delcmd;
19769 : : PQExpBuffer ruleprefix;
19770 : : char *qtabname;
19771 : : PGresult *res;
19772 : : char *tag;
19773 : :
19774 : : /* Do nothing if not dumping schema */
19775 [ + + ]: 1197 : if (!dopt->dumpSchema)
19776 : 70 : return;
19777 : :
19778 : : /*
19779 : : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19780 : : * we do not want to dump it as a separate object.
19781 : : */
19782 [ + + ]: 1127 : if (!rinfo->separate)
19783 : 916 : return;
19784 : :
19785 : : /*
19786 : : * If it's an ON SELECT rule, we want to print it as a view definition,
19787 : : * instead of a rule.
19788 : : */
19789 [ + + + - ]: 211 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19790 : :
19791 : 211 : query = createPQExpBuffer();
19792 : 211 : cmd = createPQExpBuffer();
19793 : 211 : delcmd = createPQExpBuffer();
19794 : 211 : ruleprefix = createPQExpBuffer();
19795 : :
19796 : 211 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19797 : :
19798 [ + + ]: 211 : if (is_view)
19799 : : {
19800 : : PQExpBuffer result;
19801 : :
19802 : : /*
19803 : : * We need OR REPLACE here because we'll be replacing a dummy view.
19804 : : * Otherwise this should look largely like the regular view dump code.
19805 : : */
19806 : 10 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19807 : 10 : fmtQualifiedDumpable(tbinfo));
19808 [ - + ]: 10 : if (nonemptyReloptions(tbinfo->reloptions))
19809 : : {
19810 : 0 : appendPQExpBufferStr(cmd, " WITH (");
19811 : 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19812 : 0 : appendPQExpBufferChar(cmd, ')');
19813 : : }
19814 : 10 : result = createViewAsClause(fout, tbinfo);
19815 : 10 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19816 : 10 : destroyPQExpBuffer(result);
19817 [ - + ]: 10 : if (tbinfo->checkoption != NULL)
19818 : 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19819 : : tbinfo->checkoption);
19820 : 10 : appendPQExpBufferStr(cmd, ";\n");
19821 : : }
19822 : : else
19823 : : {
19824 : : /* In the rule case, just print pg_get_ruledef's result verbatim */
19825 : 201 : appendPQExpBuffer(query,
19826 : : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19827 : 201 : rinfo->dobj.catId.oid);
19828 : :
19829 : 201 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19830 : :
19831 [ - + ]: 201 : if (PQntuples(res) != 1)
19832 : 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19833 : : rinfo->dobj.name, tbinfo->dobj.name);
19834 : :
19835 : 201 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19836 : :
19837 : 201 : PQclear(res);
19838 : : }
19839 : :
19840 : : /*
19841 : : * Add the command to alter the rules replication firing semantics if it
19842 : : * differs from the default.
19843 : : */
19844 [ + + ]: 211 : if (rinfo->ev_enabled != 'O')
19845 : : {
19846 : 15 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19847 [ - - + - ]: 15 : switch (rinfo->ev_enabled)
19848 : : {
19849 : 0 : case 'A':
19850 : 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19851 : 0 : fmtId(rinfo->dobj.name));
19852 : 0 : break;
19853 : 0 : case 'R':
19854 : 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19855 : 0 : fmtId(rinfo->dobj.name));
19856 : 0 : break;
19857 : 15 : case 'D':
19858 : 15 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19859 : 15 : fmtId(rinfo->dobj.name));
19860 : 15 : break;
19861 : : }
19862 : : }
19863 : :
19864 [ + + ]: 211 : if (is_view)
19865 : : {
19866 : : /*
19867 : : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19868 : : * REPLACE VIEW to replace the rule with something with minimal
19869 : : * dependencies.
19870 : : */
19871 : : PQExpBuffer result;
19872 : :
19873 : 10 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19874 : 10 : fmtQualifiedDumpable(tbinfo));
19875 : 10 : result = createDummyViewAsClause(fout, tbinfo);
19876 : 10 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19877 : 10 : destroyPQExpBuffer(result);
19878 : : }
19879 : : else
19880 : : {
19881 : 201 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19882 : 201 : fmtId(rinfo->dobj.name));
19883 : 201 : appendPQExpBuffer(delcmd, "ON %s;\n",
19884 : 201 : fmtQualifiedDumpable(tbinfo));
19885 : : }
19886 : :
19887 : 211 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19888 : 211 : fmtId(rinfo->dobj.name));
19889 : :
19890 : 211 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19891 : :
19892 [ + - ]: 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19893 : 211 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19894 : 211 : ARCHIVE_OPTS(.tag = tag,
19895 : : .namespace = tbinfo->dobj.namespace->dobj.name,
19896 : : .owner = tbinfo->rolname,
19897 : : .description = "RULE",
19898 : : .section = SECTION_POST_DATA,
19899 : : .createStmt = cmd->data,
19900 : : .dropStmt = delcmd->data));
19901 : :
19902 : : /* Dump rule comments */
19903 [ - + ]: 211 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19904 : 0 : dumpComment(fout, ruleprefix->data, qtabname,
19905 : 0 : tbinfo->dobj.namespace->dobj.name,
19906 : : tbinfo->rolname,
19907 : 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19908 : :
19909 : 211 : pfree(tag);
19910 : 211 : destroyPQExpBuffer(query);
19911 : 211 : destroyPQExpBuffer(cmd);
19912 : 211 : destroyPQExpBuffer(delcmd);
19913 : 211 : destroyPQExpBuffer(ruleprefix);
19914 : 211 : pg_free(qtabname);
19915 : : }
19916 : :
19917 : : /*
19918 : : * getExtensionMembership --- obtain extension membership data
19919 : : *
19920 : : * We need to identify objects that are extension members as soon as they're
19921 : : * loaded, so that we can correctly determine whether they need to be dumped.
19922 : : * Generally speaking, extension member objects will get marked as *not* to
19923 : : * be dumped, as they will be recreated by the single CREATE EXTENSION
19924 : : * command. However, in binary upgrade mode we still need to dump the members
19925 : : * individually.
19926 : : */
19927 : : void
19928 : 192 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19929 : : int numExtensions)
19930 : : {
19931 : : PQExpBuffer query;
19932 : : PGresult *res;
19933 : : int ntups,
19934 : : i;
19935 : : int i_classid,
19936 : : i_objid,
19937 : : i_refobjid;
19938 : : ExtensionInfo *ext;
19939 : :
19940 : : /* Nothing to do if no extensions */
19941 [ - + ]: 192 : if (numExtensions == 0)
19942 : 0 : return;
19943 : :
19944 : 192 : query = createPQExpBuffer();
19945 : :
19946 : : /* refclassid constraint is redundant but may speed the search */
19947 : 192 : appendPQExpBufferStr(query, "SELECT "
19948 : : "classid, objid, refobjid "
19949 : : "FROM pg_depend "
19950 : : "WHERE refclassid = 'pg_extension'::regclass "
19951 : : "AND deptype = 'e' "
19952 : : "ORDER BY 3");
19953 : :
19954 : 192 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19955 : :
19956 : 192 : ntups = PQntuples(res);
19957 : :
19958 : 192 : i_classid = PQfnumber(res, "classid");
19959 : 192 : i_objid = PQfnumber(res, "objid");
19960 : 192 : i_refobjid = PQfnumber(res, "refobjid");
19961 : :
19962 : : /*
19963 : : * Since we ordered the SELECT by referenced ID, we can expect that
19964 : : * multiple entries for the same extension will appear together; this
19965 : : * saves on searches.
19966 : : */
19967 : 192 : ext = NULL;
19968 : :
19969 [ + + ]: 1566 : for (i = 0; i < ntups; i++)
19970 : : {
19971 : : CatalogId objId;
19972 : : Oid extId;
19973 : :
19974 : 1374 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19975 : 1374 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19976 : 1374 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19977 : :
19978 [ + + ]: 1374 : if (ext == NULL ||
19979 [ + + ]: 1182 : ext->dobj.catId.oid != extId)
19980 : 223 : ext = findExtensionByOid(extId);
19981 : :
19982 [ - + ]: 1374 : if (ext == NULL)
19983 : : {
19984 : : /* shouldn't happen */
19985 : 0 : pg_log_warning("could not find referenced extension %u", extId);
19986 : 0 : continue;
19987 : : }
19988 : :
19989 : 1374 : recordExtensionMembership(objId, ext);
19990 : : }
19991 : :
19992 : 192 : PQclear(res);
19993 : :
19994 : 192 : destroyPQExpBuffer(query);
19995 : : }
19996 : :
19997 : : /*
19998 : : * processExtensionTables --- deal with extension configuration tables
19999 : : *
20000 : : * There are two parts to this process:
20001 : : *
20002 : : * 1. Identify and create dump records for extension configuration tables.
20003 : : *
20004 : : * Extensions can mark tables as "configuration", which means that the user
20005 : : * is able and expected to modify those tables after the extension has been
20006 : : * loaded. For these tables, we dump out only the data- the structure is
20007 : : * expected to be handled at CREATE EXTENSION time, including any indexes or
20008 : : * foreign keys, which brings us to-
20009 : : *
20010 : : * 2. Record FK dependencies between configuration tables.
20011 : : *
20012 : : * Due to the FKs being created at CREATE EXTENSION time and therefore before
20013 : : * the data is loaded, we have to work out what the best order for reloading
20014 : : * the data is, to avoid FK violations when the tables are restored. This is
20015 : : * not perfect- we can't handle circular dependencies and if any exist they
20016 : : * will cause an invalid dump to be produced (though at least all of the data
20017 : : * is included for a user to manually restore). This is currently documented
20018 : : * but perhaps we can provide a better solution in the future.
20019 : : */
20020 : : void
20021 : 191 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
20022 : : int numExtensions)
20023 : : {
20024 : 191 : DumpOptions *dopt = fout->dopt;
20025 : : PQExpBuffer query;
20026 : : PGresult *res;
20027 : : int ntups,
20028 : : i;
20029 : : int i_conrelid,
20030 : : i_confrelid;
20031 : :
20032 : : /* Nothing to do if no extensions */
20033 [ - + ]: 191 : if (numExtensions == 0)
20034 : 0 : return;
20035 : :
20036 : : /*
20037 : : * Identify extension configuration tables and create TableDataInfo
20038 : : * objects for them, ensuring their data will be dumped even though the
20039 : : * tables themselves won't be.
20040 : : *
20041 : : * Note that we create TableDataInfo objects even in schema-only mode, ie,
20042 : : * user data in a configuration table is treated like schema data. This
20043 : : * seems appropriate since system data in a config table would get
20044 : : * reloaded by CREATE EXTENSION. If the extension is not listed in the
20045 : : * list of extensions to be included, none of its data is dumped.
20046 : : */
20047 [ + + ]: 413 : for (i = 0; i < numExtensions; i++)
20048 : : {
20049 : 222 : ExtensionInfo *curext = &(extinfo[i]);
20050 : 222 : char *extconfig = curext->extconfig;
20051 : 222 : char *extcondition = curext->extcondition;
20052 : 222 : char **extconfigarray = NULL;
20053 : 222 : char **extconditionarray = NULL;
20054 : 222 : int nconfigitems = 0;
20055 : 222 : int nconditionitems = 0;
20056 : :
20057 : : /*
20058 : : * Check if this extension is listed as to include in the dump. If
20059 : : * not, any table data associated with it is discarded.
20060 : : */
20061 [ + + ]: 222 : if (extension_include_oids.head != NULL &&
20062 [ + + ]: 8 : !simple_oid_list_member(&extension_include_oids,
20063 : : curext->dobj.catId.oid))
20064 : 6 : continue;
20065 : :
20066 : : /*
20067 : : * Check if this extension is listed as to exclude in the dump. If
20068 : : * yes, any table data associated with it is discarded.
20069 : : */
20070 [ + + + + ]: 222 : if (extension_exclude_oids.head != NULL &&
20071 : 4 : simple_oid_list_member(&extension_exclude_oids,
20072 : : curext->dobj.catId.oid))
20073 : 2 : continue;
20074 : :
20075 [ + + - + ]: 216 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20076 : : {
20077 : : int j;
20078 : :
20079 [ - + ]: 20 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20080 : 0 : pg_fatal("could not parse %s array", "extconfig");
20081 [ - + ]: 20 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20082 : 0 : pg_fatal("could not parse %s array", "extcondition");
20083 [ - + ]: 20 : if (nconfigitems != nconditionitems)
20084 : 0 : pg_fatal("mismatched number of configurations and conditions for extension");
20085 : :
20086 [ + + ]: 60 : for (j = 0; j < nconfigitems; j++)
20087 : : {
20088 : : TableInfo *configtbl;
20089 : 40 : Oid configtbloid = atooid(extconfigarray[j]);
20090 : 40 : bool dumpobj =
20091 : 40 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20092 : :
20093 : 40 : configtbl = findTableByOid(configtbloid);
20094 [ - + ]: 40 : if (configtbl == NULL)
20095 : 0 : continue;
20096 : :
20097 : : /*
20098 : : * Tables of not-to-be-dumped extensions shouldn't be dumped
20099 : : * unless the table or its schema is explicitly included
20100 : : */
20101 [ + + ]: 40 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20102 : : {
20103 : : /* check table explicitly requested */
20104 [ - + - - ]: 2 : if (table_include_oids.head != NULL &&
20105 : 0 : simple_oid_list_member(&table_include_oids,
20106 : : configtbloid))
20107 : 0 : dumpobj = true;
20108 : :
20109 : : /* check table's schema explicitly requested */
20110 [ + - ]: 2 : if (configtbl->dobj.namespace->dobj.dump &
20111 : : DUMP_COMPONENT_DATA)
20112 : 2 : dumpobj = true;
20113 : : }
20114 : :
20115 : : /* check table excluded by an exclusion switch */
20116 [ + + + + ]: 44 : if (table_exclude_oids.head != NULL &&
20117 : 4 : simple_oid_list_member(&table_exclude_oids,
20118 : : configtbloid))
20119 : 1 : dumpobj = false;
20120 : :
20121 : : /* check schema excluded by an exclusion switch */
20122 [ - + ]: 40 : if (simple_oid_list_member(&schema_exclude_oids,
20123 : 40 : configtbl->dobj.namespace->dobj.catId.oid))
20124 : 0 : dumpobj = false;
20125 : :
20126 [ + + ]: 40 : if (dumpobj)
20127 : : {
20128 : 39 : makeTableDataInfo(dopt, configtbl);
20129 [ + - ]: 39 : if (configtbl->dataObj != NULL)
20130 : : {
20131 [ - + ]: 39 : if (strlen(extconditionarray[j]) > 0)
20132 : 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20133 : : }
20134 : : }
20135 : : }
20136 : : }
20137 [ + + ]: 216 : if (extconfigarray)
20138 : 20 : free(extconfigarray);
20139 [ + + ]: 216 : if (extconditionarray)
20140 : 20 : free(extconditionarray);
20141 : : }
20142 : :
20143 : : /*
20144 : : * Now that all the TableDataInfo objects have been created for all the
20145 : : * extensions, check their FK dependencies and register them to try and
20146 : : * dump the data out in an order that they can be restored in.
20147 : : *
20148 : : * Note that this is not a problem for user tables as their FKs are
20149 : : * recreated after the data has been loaded.
20150 : : */
20151 : :
20152 : 191 : query = createPQExpBuffer();
20153 : :
20154 : 191 : printfPQExpBuffer(query,
20155 : : "SELECT conrelid, confrelid "
20156 : : "FROM pg_constraint "
20157 : : "JOIN pg_depend ON (objid = confrelid) "
20158 : : "WHERE contype = 'f' "
20159 : : "AND refclassid = 'pg_extension'::regclass "
20160 : : "AND classid = 'pg_class'::regclass;");
20161 : :
20162 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20163 : 191 : ntups = PQntuples(res);
20164 : :
20165 : 191 : i_conrelid = PQfnumber(res, "conrelid");
20166 : 191 : i_confrelid = PQfnumber(res, "confrelid");
20167 : :
20168 : : /* Now get the dependencies and register them */
20169 [ - + ]: 191 : for (i = 0; i < ntups; i++)
20170 : : {
20171 : : Oid conrelid,
20172 : : confrelid;
20173 : : TableInfo *reftable,
20174 : : *contable;
20175 : :
20176 : 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20177 : 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20178 : 0 : contable = findTableByOid(conrelid);
20179 : 0 : reftable = findTableByOid(confrelid);
20180 : :
20181 [ # # ]: 0 : if (reftable == NULL ||
20182 [ # # # # ]: 0 : reftable->dataObj == NULL ||
20183 : 0 : contable == NULL ||
20184 [ # # ]: 0 : contable->dataObj == NULL)
20185 : 0 : continue;
20186 : :
20187 : : /*
20188 : : * Make referencing TABLE_DATA object depend on the referenced table's
20189 : : * TABLE_DATA object.
20190 : : */
20191 : 0 : addObjectDependency(&contable->dataObj->dobj,
20192 : 0 : reftable->dataObj->dobj.dumpId);
20193 : : }
20194 : 191 : PQclear(res);
20195 : 191 : destroyPQExpBuffer(query);
20196 : : }
20197 : :
20198 : : /*
20199 : : * getDependencies --- obtain available dependency data
20200 : : */
20201 : : static void
20202 : 191 : getDependencies(Archive *fout)
20203 : : {
20204 : : PQExpBuffer query;
20205 : : PGresult *res;
20206 : : int ntups,
20207 : : i;
20208 : : int i_classid,
20209 : : i_objid,
20210 : : i_refclassid,
20211 : : i_refobjid,
20212 : : i_deptype;
20213 : : DumpableObject *dobj,
20214 : : *refdobj;
20215 : :
20216 : 191 : pg_log_info("reading dependency data");
20217 : :
20218 : 191 : query = createPQExpBuffer();
20219 : :
20220 : : /*
20221 : : * Messy query to collect the dependency data we need. Note that we
20222 : : * ignore the sub-object column, so that dependencies of or on a column
20223 : : * look the same as dependencies of or on a whole table.
20224 : : *
20225 : : * PIN dependencies aren't interesting, and EXTENSION dependencies were
20226 : : * already processed by getExtensionMembership.
20227 : : */
20228 : 191 : appendPQExpBufferStr(query, "SELECT "
20229 : : "classid, objid, refclassid, refobjid, deptype "
20230 : : "FROM pg_depend "
20231 : : "WHERE deptype != 'p' AND deptype != 'e'\n");
20232 : :
20233 : : /*
20234 : : * Since we don't treat pg_amop entries as separate DumpableObjects, we
20235 : : * have to translate their dependencies into dependencies of their parent
20236 : : * opfamily. Ignore internal dependencies though, as those will point to
20237 : : * their parent opclass, which we needn't consider here (and if we did,
20238 : : * it'd just result in circular dependencies). Also, "loose" opfamily
20239 : : * entries will have dependencies on their parent opfamily, which we
20240 : : * should drop since they'd likewise become useless self-dependencies.
20241 : : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20242 : : */
20243 : 191 : appendPQExpBufferStr(query, "UNION ALL\n"
20244 : : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20245 : : "FROM pg_depend d, pg_amop o "
20246 : : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20247 : : "classid = 'pg_amop'::regclass AND objid = o.oid "
20248 : : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20249 : :
20250 : : /* Likewise for pg_amproc entries */
20251 : 191 : appendPQExpBufferStr(query, "UNION ALL\n"
20252 : : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20253 : : "FROM pg_depend d, pg_amproc p "
20254 : : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20255 : : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20256 : : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20257 : :
20258 : : /*
20259 : : * Translate dependencies of pg_propgraph_element entries into
20260 : : * dependencies of their parent pg_class entry.
20261 : : */
20262 [ + - ]: 191 : if (fout->remoteVersion >= 190000)
20263 : 191 : appendPQExpBufferStr(query, "UNION ALL\n"
20264 : : "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20265 : : "FROM pg_depend d, pg_propgraph_element pge "
20266 : : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20267 : : "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20268 : :
20269 : : /* Sort the output for efficiency below */
20270 : 191 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20271 : :
20272 : 191 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20273 : :
20274 : 191 : ntups = PQntuples(res);
20275 : :
20276 : 191 : i_classid = PQfnumber(res, "classid");
20277 : 191 : i_objid = PQfnumber(res, "objid");
20278 : 191 : i_refclassid = PQfnumber(res, "refclassid");
20279 : 191 : i_refobjid = PQfnumber(res, "refobjid");
20280 : 191 : i_deptype = PQfnumber(res, "deptype");
20281 : :
20282 : : /*
20283 : : * Since we ordered the SELECT by referencing ID, we can expect that
20284 : : * multiple entries for the same object will appear together; this saves
20285 : : * on searches.
20286 : : */
20287 : 191 : dobj = NULL;
20288 : :
20289 [ + + ]: 462120 : for (i = 0; i < ntups; i++)
20290 : : {
20291 : : CatalogId objId;
20292 : : CatalogId refobjId;
20293 : : char deptype;
20294 : :
20295 : 461929 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20296 : 461929 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20297 : 461929 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20298 : 461929 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20299 : 461929 : deptype = *(PQgetvalue(res, i, i_deptype));
20300 : :
20301 [ + + ]: 461929 : if (dobj == NULL ||
20302 [ + + ]: 426743 : dobj->catId.tableoid != objId.tableoid ||
20303 [ + + ]: 424638 : dobj->catId.oid != objId.oid)
20304 : 204449 : dobj = findObjectByCatalogId(objId);
20305 : :
20306 : : /*
20307 : : * Failure to find objects mentioned in pg_depend is not unexpected,
20308 : : * since for example we don't collect info about TOAST tables.
20309 : : */
20310 [ + + ]: 461929 : if (dobj == NULL)
20311 : : {
20312 : : #ifdef NOT_USED
20313 : : pg_log_warning("no referencing object %u %u",
20314 : : objId.tableoid, objId.oid);
20315 : : #endif
20316 : 36331 : continue;
20317 : : }
20318 : :
20319 : 426927 : refdobj = findObjectByCatalogId(refobjId);
20320 : :
20321 [ + + ]: 426927 : if (refdobj == NULL)
20322 : : {
20323 : : #ifdef NOT_USED
20324 : : pg_log_warning("no referenced object %u %u",
20325 : : refobjId.tableoid, refobjId.oid);
20326 : : #endif
20327 : 1329 : continue;
20328 : : }
20329 : :
20330 : : /*
20331 : : * For 'x' dependencies, mark the object for later; we still add the
20332 : : * normal dependency, for possible ordering purposes. Currently
20333 : : * pg_dump_sort.c knows to put extensions ahead of all object types
20334 : : * that could possibly depend on them, but this is safer.
20335 : : */
20336 [ + + ]: 425598 : if (deptype == 'x')
20337 : 44 : dobj->depends_on_ext = true;
20338 : :
20339 : : /*
20340 : : * Ordinarily, table rowtypes have implicit dependencies on their
20341 : : * tables. However, for a composite type the implicit dependency goes
20342 : : * the other way in pg_depend; which is the right thing for DROP but
20343 : : * it doesn't produce the dependency ordering we need. So in that one
20344 : : * case, we reverse the direction of the dependency.
20345 : : */
20346 [ + + ]: 425598 : if (deptype == 'i' &&
20347 [ + + ]: 119155 : dobj->objType == DO_TABLE &&
20348 [ + + ]: 1304 : refdobj->objType == DO_TYPE)
20349 : 185 : addObjectDependency(refdobj, dobj->dumpId);
20350 : : else
20351 : : /* normal case */
20352 : 425413 : addObjectDependency(dobj, refdobj->dumpId);
20353 : : }
20354 : :
20355 : 191 : PQclear(res);
20356 : :
20357 : 191 : destroyPQExpBuffer(query);
20358 : 191 : }
20359 : :
20360 : :
20361 : : /*
20362 : : * createBoundaryObjects - create dummy DumpableObjects to represent
20363 : : * dump section boundaries.
20364 : : */
20365 : : static DumpableObject *
20366 : 191 : createBoundaryObjects(void)
20367 : : {
20368 : : DumpableObject *dobjs;
20369 : :
20370 : 191 : dobjs = pg_malloc_array(DumpableObject, 2);
20371 : :
20372 : 191 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20373 : 191 : dobjs[0].catId = nilCatalogId;
20374 : 191 : AssignDumpId(dobjs + 0);
20375 : 191 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20376 : :
20377 : 191 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20378 : 191 : dobjs[1].catId = nilCatalogId;
20379 : 191 : AssignDumpId(dobjs + 1);
20380 : 191 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20381 : :
20382 : 191 : return dobjs;
20383 : : }
20384 : :
20385 : : /*
20386 : : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20387 : : * section boundaries.
20388 : : */
20389 : : static void
20390 : 191 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20391 : : DumpableObject *boundaryObjs)
20392 : : {
20393 : 191 : DumpableObject *preDataBound = boundaryObjs + 0;
20394 : 191 : DumpableObject *postDataBound = boundaryObjs + 1;
20395 : : int i;
20396 : :
20397 [ + + ]: 738561 : for (i = 0; i < numObjs; i++)
20398 : : {
20399 : 738370 : DumpableObject *dobj = dobjs[i];
20400 : :
20401 : : /*
20402 : : * The classification of object types here must match the SECTION_xxx
20403 : : * values assigned during subsequent ArchiveEntry calls!
20404 : : */
20405 [ + + + + : 738370 : switch (dobj->objType)
+ + + +
- ]
20406 : : {
20407 : 687214 : case DO_NAMESPACE:
20408 : : case DO_EXTENSION:
20409 : : case DO_TYPE:
20410 : : case DO_SHELL_TYPE:
20411 : : case DO_FUNC:
20412 : : case DO_AGG:
20413 : : case DO_OPERATOR:
20414 : : case DO_ACCESS_METHOD:
20415 : : case DO_OPCLASS:
20416 : : case DO_OPFAMILY:
20417 : : case DO_COLLATION:
20418 : : case DO_CONVERSION:
20419 : : case DO_TABLE:
20420 : : case DO_TABLE_ATTACH:
20421 : : case DO_ATTRDEF:
20422 : : case DO_PROCLANG:
20423 : : case DO_CAST:
20424 : : case DO_DUMMY_TYPE:
20425 : : case DO_TSPARSER:
20426 : : case DO_TSDICT:
20427 : : case DO_TSTEMPLATE:
20428 : : case DO_TSCONFIG:
20429 : : case DO_FDW:
20430 : : case DO_FOREIGN_SERVER:
20431 : : case DO_TRANSFORM:
20432 : : /* Pre-data objects: must come before the pre-data boundary */
20433 : 687214 : addObjectDependency(preDataBound, dobj->dumpId);
20434 : 687214 : break;
20435 : 5180 : case DO_TABLE_DATA:
20436 : : case DO_SEQUENCE_SET:
20437 : : case DO_LARGE_OBJECT:
20438 : : case DO_LARGE_OBJECT_DATA:
20439 : : /* Data objects: must come between the boundaries */
20440 : 5180 : addObjectDependency(dobj, preDataBound->dumpId);
20441 : 5180 : addObjectDependency(postDataBound, dobj->dumpId);
20442 : 5180 : break;
20443 : 6350 : case DO_INDEX:
20444 : : case DO_INDEX_ATTACH:
20445 : : case DO_STATSEXT:
20446 : : case DO_REFRESH_MATVIEW:
20447 : : case DO_TRIGGER:
20448 : : case DO_EVENT_TRIGGER:
20449 : : case DO_DEFAULT_ACL:
20450 : : case DO_POLICY:
20451 : : case DO_PUBLICATION:
20452 : : case DO_PUBLICATION_REL:
20453 : : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20454 : : case DO_SUBSCRIPTION:
20455 : : case DO_SUBSCRIPTION_REL:
20456 : : /* Post-data objects: must come after the post-data boundary */
20457 : 6350 : addObjectDependency(dobj, postDataBound->dumpId);
20458 : 6350 : break;
20459 : 32716 : case DO_RULE:
20460 : : /* Rules are post-data, but only if dumped separately */
20461 [ + + ]: 32716 : if (((RuleInfo *) dobj)->separate)
20462 : 655 : addObjectDependency(dobj, postDataBound->dumpId);
20463 : 32716 : break;
20464 : 2844 : case DO_CONSTRAINT:
20465 : : case DO_FK_CONSTRAINT:
20466 : : /* Constraints are post-data, but only if dumped separately */
20467 [ + + ]: 2844 : if (((ConstraintInfo *) dobj)->separate)
20468 : 2090 : addObjectDependency(dobj, postDataBound->dumpId);
20469 : 2844 : break;
20470 : 191 : case DO_PRE_DATA_BOUNDARY:
20471 : : /* nothing to do */
20472 : 191 : break;
20473 : 191 : case DO_POST_DATA_BOUNDARY:
20474 : : /* must come after the pre-data boundary */
20475 : 191 : addObjectDependency(dobj, preDataBound->dumpId);
20476 : 191 : break;
20477 : 3684 : case DO_REL_STATS:
20478 : : /* stats section varies by parent object type, DATA or POST */
20479 [ + + ]: 3684 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20480 : : {
20481 : 2406 : addObjectDependency(dobj, preDataBound->dumpId);
20482 : 2406 : addObjectDependency(postDataBound, dobj->dumpId);
20483 : : }
20484 : : else
20485 : 1278 : addObjectDependency(dobj, postDataBound->dumpId);
20486 : 3684 : break;
20487 : : }
20488 : : }
20489 : 191 : }
20490 : :
20491 : :
20492 : : /*
20493 : : * BuildArchiveDependencies - create dependency data for archive TOC entries
20494 : : *
20495 : : * The raw dependency data obtained by getDependencies() is not terribly
20496 : : * useful in an archive dump, because in many cases there are dependency
20497 : : * chains linking through objects that don't appear explicitly in the dump.
20498 : : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20499 : : * will depend on other objects --- but the rule will not appear as a separate
20500 : : * object in the dump. We need to adjust the view's dependencies to include
20501 : : * whatever the rule depends on that is included in the dump.
20502 : : *
20503 : : * Just to make things more complicated, there are also "special" dependencies
20504 : : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20505 : : * not rearrange because pg_restore knows that TABLE DATA only depends on
20506 : : * its table. In these cases we must leave the dependencies strictly as-is
20507 : : * even if they refer to not-to-be-dumped objects.
20508 : : *
20509 : : * To handle this, the convention is that "special" dependencies are created
20510 : : * during ArchiveEntry calls, and an archive TOC item that has any such
20511 : : * entries will not be touched here. Otherwise, we recursively search the
20512 : : * DumpableObject data structures to build the correct dependencies for each
20513 : : * archive TOC item.
20514 : : */
20515 : : static void
20516 : 63 : BuildArchiveDependencies(Archive *fout)
20517 : : {
20518 : 63 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20519 : : TocEntry *te;
20520 : :
20521 : : /* Scan all TOC entries in the archive */
20522 [ + + ]: 7867 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20523 : : {
20524 : : DumpableObject *dobj;
20525 : : DumpId *dependencies;
20526 : : int nDeps;
20527 : : int allocDeps;
20528 : :
20529 : : /* No need to process entries that will not be dumped */
20530 [ + + ]: 7804 : if (te->reqs == 0)
20531 : 3875 : continue;
20532 : : /* Ignore entries that already have "special" dependencies */
20533 [ + + ]: 7796 : if (te->nDeps > 0)
20534 : 3375 : continue;
20535 : : /* Otherwise, look up the item's original DumpableObject, if any */
20536 : 4421 : dobj = findObjectByDumpId(te->dumpId);
20537 [ + + ]: 4421 : if (dobj == NULL)
20538 : 378 : continue;
20539 : : /* No work if it has no dependencies */
20540 [ + + ]: 4043 : if (dobj->nDeps <= 0)
20541 : 114 : continue;
20542 : : /* Set up work array */
20543 : 3929 : allocDeps = 64;
20544 : 3929 : dependencies = pg_malloc_array(DumpId, allocDeps);
20545 : 3929 : nDeps = 0;
20546 : : /* Recursively find all dumpable dependencies */
20547 : 3929 : findDumpableDependencies(AH, dobj,
20548 : : &dependencies, &nDeps, &allocDeps);
20549 : : /* And save 'em ... */
20550 [ + + ]: 3929 : if (nDeps > 0)
20551 : : {
20552 : 2993 : dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20553 : 2993 : te->dependencies = dependencies;
20554 : 2993 : te->nDeps = nDeps;
20555 : : }
20556 : : else
20557 : 936 : pg_free(dependencies);
20558 : : }
20559 : 63 : }
20560 : :
20561 : : /* Recursive search subroutine for BuildArchiveDependencies */
20562 : : static void
20563 : 9351 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20564 : : DumpId **dependencies, int *nDeps, int *allocDeps)
20565 : : {
20566 : : int i;
20567 : :
20568 : : /*
20569 : : * Ignore section boundary objects: if we search through them, we'll
20570 : : * report lots of bogus dependencies.
20571 : : */
20572 [ + + ]: 9351 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20573 [ + + ]: 9330 : dobj->objType == DO_POST_DATA_BOUNDARY)
20574 : 1677 : return;
20575 : :
20576 [ + + ]: 19501 : for (i = 0; i < dobj->nDeps; i++)
20577 : : {
20578 : 11827 : DumpId depid = dobj->dependencies[i];
20579 : :
20580 [ + + ]: 11827 : if (TocIDRequired(AH, depid) != 0)
20581 : : {
20582 : : /* Object will be dumped, so just reference it as a dependency */
20583 [ - + ]: 6405 : if (*nDeps >= *allocDeps)
20584 : : {
20585 : 0 : *allocDeps *= 2;
20586 : 0 : *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20587 : : }
20588 : 6405 : (*dependencies)[*nDeps] = depid;
20589 : 6405 : (*nDeps)++;
20590 : : }
20591 : : else
20592 : : {
20593 : : /*
20594 : : * Object will not be dumped, so recursively consider its deps. We
20595 : : * rely on the assumption that sortDumpableObjects already broke
20596 : : * any dependency loops, else we might recurse infinitely.
20597 : : */
20598 : 5422 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20599 : :
20600 [ + - ]: 5422 : if (otherdobj)
20601 : 5422 : findDumpableDependencies(AH, otherdobj,
20602 : : dependencies, nDeps, allocDeps);
20603 : : }
20604 : : }
20605 : : }
20606 : :
20607 : :
20608 : : /*
20609 : : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20610 : : * given type OID.
20611 : : *
20612 : : * This does not guarantee to schema-qualify the output, so it should not
20613 : : * be used to create the target object name for CREATE or ALTER commands.
20614 : : *
20615 : : * Note that the result is cached and must not be freed by the caller.
20616 : : */
20617 : : static const char *
20618 : 2399 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20619 : : {
20620 : : TypeInfo *typeInfo;
20621 : : char *result;
20622 : : PQExpBuffer query;
20623 : : PGresult *res;
20624 : :
20625 [ - + ]: 2399 : if (oid == 0)
20626 : : {
20627 [ # # ]: 0 : if ((opts & zeroAsStar) != 0)
20628 : 0 : return "*";
20629 [ # # ]: 0 : else if ((opts & zeroAsNone) != 0)
20630 : 0 : return "NONE";
20631 : : }
20632 : :
20633 : : /* see if we have the result cached in the type's TypeInfo record */
20634 : 2399 : typeInfo = findTypeByOid(oid);
20635 [ + - + + ]: 2399 : if (typeInfo && typeInfo->ftypname)
20636 : 1908 : return typeInfo->ftypname;
20637 : :
20638 : 491 : query = createPQExpBuffer();
20639 : 491 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20640 : : oid);
20641 : :
20642 : 491 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20643 : :
20644 : : /* result of format_type is already quoted */
20645 : 491 : result = pg_strdup(PQgetvalue(res, 0, 0));
20646 : :
20647 : 491 : PQclear(res);
20648 : 491 : destroyPQExpBuffer(query);
20649 : :
20650 : : /*
20651 : : * Cache the result for re-use in later requests, if possible. If we
20652 : : * don't have a TypeInfo for the type, the string will be leaked once the
20653 : : * caller is done with it ... but that case really should not happen, so
20654 : : * leaking if it does seems acceptable.
20655 : : */
20656 [ + - ]: 491 : if (typeInfo)
20657 : 491 : typeInfo->ftypname = result;
20658 : :
20659 : 491 : return result;
20660 : : }
20661 : :
20662 : : /*
20663 : : * Return a column list clause for the given relation.
20664 : : *
20665 : : * Special case: if there are no undropped columns in the relation, return
20666 : : * "", not an invalid "()" column list.
20667 : : */
20668 : : static const char *
20669 : 8950 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20670 : : {
20671 : 8950 : int numatts = ti->numatts;
20672 : 8950 : char **attnames = ti->attnames;
20673 : 8950 : bool *attisdropped = ti->attisdropped;
20674 : 8950 : char *attgenerated = ti->attgenerated;
20675 : : bool needComma;
20676 : : int i;
20677 : :
20678 : 8950 : appendPQExpBufferChar(buffer, '(');
20679 : 8950 : needComma = false;
20680 [ + + ]: 42938 : for (i = 0; i < numatts; i++)
20681 : : {
20682 [ + + ]: 33988 : if (attisdropped[i])
20683 : 610 : continue;
20684 [ + + ]: 33378 : if (attgenerated[i])
20685 : 1200 : continue;
20686 [ + + ]: 32178 : if (needComma)
20687 : 23464 : appendPQExpBufferStr(buffer, ", ");
20688 : 32178 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20689 : 32178 : needComma = true;
20690 : : }
20691 : :
20692 [ + + ]: 8950 : if (!needComma)
20693 : 236 : return ""; /* no undropped columns */
20694 : :
20695 : 8714 : appendPQExpBufferChar(buffer, ')');
20696 : 8714 : return buffer->data;
20697 : : }
20698 : :
20699 : : /*
20700 : : * Check if a reloptions array is nonempty.
20701 : : */
20702 : : static bool
20703 : 14747 : nonemptyReloptions(const char *reloptions)
20704 : : {
20705 : : /* Don't want to print it if it's just "{}" */
20706 [ + - + + ]: 14747 : return (reloptions != NULL && strlen(reloptions) > 2);
20707 : : }
20708 : :
20709 : : /*
20710 : : * Format a reloptions array and append it to the given buffer.
20711 : : *
20712 : : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20713 : : */
20714 : : static void
20715 : 223 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20716 : : const char *prefix, Archive *fout)
20717 : : {
20718 : : bool res;
20719 : :
20720 : 223 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20721 : 223 : fout->std_strings);
20722 [ - + ]: 223 : if (!res)
20723 : 0 : pg_log_warning("could not parse %s array", "reloptions");
20724 : 223 : }
20725 : :
20726 : : /*
20727 : : * read_dump_filters - retrieve object identifier patterns from file
20728 : : *
20729 : : * Parse the specified filter file for include and exclude patterns, and add
20730 : : * them to the relevant lists. If the filename is "-" then filters will be
20731 : : * read from STDIN rather than a file.
20732 : : */
20733 : : static void
20734 : 26 : read_dump_filters(const char *filename, DumpOptions *dopt)
20735 : : {
20736 : : FilterStateData fstate;
20737 : : char *objname;
20738 : : FilterCommandType comtype;
20739 : : FilterObjectType objtype;
20740 : :
20741 : 26 : filter_init(&fstate, filename, exit_nicely);
20742 : :
20743 [ + + ]: 84 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20744 : : {
20745 [ + + ]: 33 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20746 : : {
20747 [ - - + + : 17 : switch (objtype)
+ + + - ]
20748 : : {
20749 : 0 : case FILTER_OBJECT_TYPE_NONE:
20750 : 0 : break;
20751 : 0 : case FILTER_OBJECT_TYPE_DATABASE:
20752 : : case FILTER_OBJECT_TYPE_FUNCTION:
20753 : : case FILTER_OBJECT_TYPE_INDEX:
20754 : : case FILTER_OBJECT_TYPE_TABLE_DATA:
20755 : : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20756 : : case FILTER_OBJECT_TYPE_TRIGGER:
20757 : 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20758 : : "include",
20759 : : filter_object_type_name(objtype));
20760 : 0 : exit_nicely(1);
20761 : : break; /* unreachable */
20762 : :
20763 : 1 : case FILTER_OBJECT_TYPE_EXTENSION:
20764 : 1 : simple_string_list_append(&extension_include_patterns, objname);
20765 : 1 : break;
20766 : 1 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20767 : 1 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20768 : 1 : break;
20769 : 1 : case FILTER_OBJECT_TYPE_SCHEMA:
20770 : 1 : simple_string_list_append(&schema_include_patterns, objname);
20771 : 1 : dopt->include_everything = false;
20772 : 1 : break;
20773 : 13 : case FILTER_OBJECT_TYPE_TABLE:
20774 : 13 : simple_string_list_append(&table_include_patterns, objname);
20775 : 13 : dopt->include_everything = false;
20776 : 13 : break;
20777 : 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20778 : 1 : simple_string_list_append(&table_include_patterns_and_children,
20779 : : objname);
20780 : 1 : dopt->include_everything = false;
20781 : 1 : break;
20782 : : }
20783 : : }
20784 [ + + ]: 16 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20785 : : {
20786 [ - + + + : 9 : switch (objtype)
+ + + +
- ]
20787 : : {
20788 : 0 : case FILTER_OBJECT_TYPE_NONE:
20789 : 0 : break;
20790 : 1 : case FILTER_OBJECT_TYPE_DATABASE:
20791 : : case FILTER_OBJECT_TYPE_FUNCTION:
20792 : : case FILTER_OBJECT_TYPE_INDEX:
20793 : : case FILTER_OBJECT_TYPE_TRIGGER:
20794 : : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20795 : 1 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20796 : : "exclude",
20797 : : filter_object_type_name(objtype));
20798 : 1 : exit_nicely(1);
20799 : : break;
20800 : :
20801 : 1 : case FILTER_OBJECT_TYPE_EXTENSION:
20802 : 1 : simple_string_list_append(&extension_exclude_patterns, objname);
20803 : 1 : break;
20804 : 1 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20805 : 1 : simple_string_list_append(&tabledata_exclude_patterns,
20806 : : objname);
20807 : 1 : break;
20808 : 1 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20809 : 1 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20810 : : objname);
20811 : 1 : break;
20812 : 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20813 : 2 : simple_string_list_append(&schema_exclude_patterns, objname);
20814 : 2 : break;
20815 : 2 : case FILTER_OBJECT_TYPE_TABLE:
20816 : 2 : simple_string_list_append(&table_exclude_patterns, objname);
20817 : 2 : break;
20818 : 1 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20819 : 1 : simple_string_list_append(&table_exclude_patterns_and_children,
20820 : : objname);
20821 : 1 : break;
20822 : : }
20823 : : }
20824 : : else
20825 : : {
20826 : : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20827 : : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20828 : : }
20829 : :
20830 [ + + ]: 32 : if (objname)
20831 : 25 : free(objname);
20832 : : }
20833 : :
20834 : 22 : filter_free(&fstate);
20835 : 22 : }
|